2021-04-19 17:29:55 +00:00
const HTTPS = require ( "https" ) ;
const PATH = require ( "path" ) ;
const FS = require ( "fs/promises" ) ;
const CHILD _PROCESS = require ( "child_process" ) ;
function query _git ( ) {
// git shortlog -sn --all | sed -E "s/^\W+[0-9]+\W+//"
return ( async ( ) => {
let users = new Map ( ) ;
let git = new Promise ( ( resolve , reject ) => {
try {
const child = CHILD _PROCESS . spawn ( 'git' , [ 'shortlog' , '-sn' , '--all' ] ) ;
let sout = "" ;
child . stdout . setEncoding ( 'utf8' ) ;
child . stdout . on ( 'data' , ( data ) => {
sout += data ;
} ) ;
let serr = "" ;
child . stderr . setEncoding ( 'utf8' ) ;
child . stderr . on ( 'data' , ( data ) => {
serr += data ;
} ) ;
child . on ( 'close' , ( code ) => {
if ( code != 0 ) {
reject ( [ code , serr ] ) ;
} else {
resolve ( sout ) ;
}
} ) ;
} catch ( e ) {
reject ( e ) ;
}
} ) ;
let result = await git ;
let lines = result . matchAll ( /^[\s]+([0-9]+)[\s]+(.+)$/gim ) ;
for ( let line of lines ) {
let name = line [ 2 ] ;
if ( ! users . has ( name ) ) {
users . set ( name , ` https://github.com/ ${ process . env . GITHUB _REPOSITORY } /graphs/contributors ` )
}
}
return users ;
} ) ( ) ;
}
function query _crowdin ( project _id , project _auth _key ) {
const limit = 100 ;
function _query _page ( page ) {
return new Promise ( ( resolve , reject ) => {
try {
let query = {
protocol : "https:" ,
host : "crowdin.com" ,
path : ` /api/v2/projects/ ${ project _id } /members?limit= ${ limit } &offset= ${ page * limit } ` ,
method : "GET" ,
headers : {
"accept" : "application/json" ,
"authorization" : ` Bearer ${ project _auth _key } `
}
} ;
let req = HTTPS . request ( query , ( res ) => {
let data = "" ;
res . setEncoding ( 'utf8' ) ;
res . on ( 'data' , ( chunk ) => {
data += chunk ;
} ) ;
res . on ( 'end' , ( ) => {
res . data = data ;
resolve ( res ) ;
} ) ;
} ) ;
req . end ( ) ;
} catch ( e ) {
reject ( e ) ;
}
} ) ;
}
return ( async ( ) => {
let page _is _last = false ;
let page = 0 ;
let users = new Map ( ) ;
while ( ! page _is _last ) {
let res = await _query _page ( page ++ ) ;
if ( res . statusCode == 200 ) {
let json = JSON . parse ( res . data ) ;
let count = json . data . length ;
for ( let user of json . data ) {
// Don't credit blocked users for work not done.
if ( user . data . role == 'blocked' ) {
continue ;
}
let key = ( user . data . fullName !== null ) && ( user . data . fullName !== undefined ) && ( user . data . fullName . length > 0 ) ? user . data . fullName : user . data . username ;
users . set ( key , ` https://crowdin.com/profile/ ${ user . data . username } ` ) ;
}
page _is _last = ( count < limit ) ;
} else {
throw new Error ( JSON . parse ( res . data ) ) ;
}
}
return users ;
} ) ( ) ;
}
function query _github _sponsors ( auth _token ) {
const HTTPS = require ( "https" ) ;
let build _query = function ( cursor ) {
if ( cursor === undefined ) {
return ` query {
viewer {
sponsors ( after : null , first : 0 ) {
totalCount
}
}
} ` ;
} else {
return ` query {
viewer {
sponsors ( after : $ { typeof ( cursor ) == "string" ? ` " ${ cursor } " ` : "null" } , first : 100 ) {
nodes {
_ _typename
... on User {
resourcePath
login
name
}
... on Organization {
resourcePath
login
name
}
}
pageInfo {
endCursor
startCursor
}
}
}
} ` ;
}
}
let do _query = function ( schema ) {
return new Promise ( ( resolve , reject ) => {
let text _query = JSON . stringify ( { query : schema } ) ;
let request = HTTPS . request (
"https://api.github.com/graphql" ,
{
method : 'POST' ,
headers : {
'Content-Type' : 'application/json' ,
'Accept' : 'application/json' ,
'Authorization' : ` bearer ${ auth _token } ` ,
'User-Agent' : 'Node.JS'
} ,
} , ( res ) => {
let data = "" ;
res . setEncoding ( 'utf8' ) ;
res . on ( 'data' , ( chunk ) => {
data += chunk ;
} ) ;
res . on ( 'error' , ( ) => {
reject ( ) ;
} ) ;
res . on ( 'end' , ( ) => {
res . data = JSON . parse ( data ) ;
resolve ( res ) ;
} ) ;
} ) ;
request . write ( text _query ) ;
request . end ( ) ;
} )
}
return ( async ( ) => {
let users = new Map ( ) ;
let total = 0 ;
// Find the total amount.
{
let query = build _query ( ) ;
let response = await do _query ( query ) ;
let data = response . data . data ;
total = data . viewer . sponsors . totalCount ;
}
// Index by page, in 100 user increments.
let cursor = null ;
for ( let idx = 0 , edx = total ; idx < edx ; idx += 100 ) {
let query = build _query ( cursor ) ;
let response = await do _query ( query ) ;
let data = response . data . data ;
// Insert data info Map.
for ( let entry of data . viewer . sponsors . nodes ) {
let name = entry . login ;
if ( typeof ( entry . name ) === "string" ) {
name = entry . name ;
}
users . set ( name , ` https://github.com ${ entry . resourcePath } ` ) ;
}
// Update cursor for further queries.
cursor = data . viewer . sponsors . pageInfo . endCursor ;
}
return users ;
} ) ( ) ;
}
( async ( ) => {
let json = { } ;
let markdown = ` # StreamFX Contributors & Supporters
# # Contributors
` ;
// Contributors
{ // - Git
markdown += ` ### Code, Media
Thanks go to the following people , who have either wrangled with code or wrangled with image editors while saving often in the hopes of not losing any changes :
` ;
json . contributor = { } ;
let info = await query _git ( ) ;
let extra = JSON . parse ( await FS . readFile ( PATH . join ( _ _dirname , "patch-contributors-git.json" ) ) ) ;
for ( let key in extra ) {
info . set ( key , extra [ key ] ) ;
}
2021-11-30 13:41:22 +00:00
for ( let key of Array . from ( info . keys ( ) ) . sort ( ( a , b ) => a . localeCompare ( b , undefined , { numeric : true , sensitivity : 'base' } ) ) ) {
2021-04-19 17:29:55 +00:00
let value = info . get ( key ) ;
json . contributor [ key ] = value ;
markdown += ` * [ ${ key } ]( ${ value } ) \n ` ;
}
markdown += "\n" ;
}
{ // - Crowdin
markdown += ` ### Translators
[ StreamFX relies on crowd - sourced translations to be available in your language . ] ( https : //github.com/Xaymar/obs-StreamFX/issues/36) Much thanks go out to all volunteer translators who have taken some time to submit translations on Crowdin.
` ;
json . translator = { } ;
let info = await query _crowdin ( process . env . CROWDIN _PROJECTID , process . env . CROWDIN _TOKEN ) ;
let extra = JSON . parse ( await FS . readFile ( PATH . join ( _ _dirname , "patch-contributors-git.json" ) ) ) ;
for ( let key in extra ) {
info . set ( key , extra [ key ] ) ;
}
2021-11-30 13:41:22 +00:00
for ( let key of Array . from ( info . keys ( ) ) . sort ( ( a , b ) => a . localeCompare ( b , undefined , { numeric : true , sensitivity : 'base' } ) ) ) {
2021-04-19 17:29:55 +00:00
let value = info . get ( key ) ;
json . translator [ key ] = value ;
markdown += ` * [ ${ key } ]( ${ value } ) \n ` ;
}
markdown += "\n" ;
}
// Supporters
markdown += ` ## Supporters
The StreamFX project relies on generous donations from you through [ Patreon ] ( https : //patreon.com/xaymar), [GitHub](https://github.com/sponsors/xaymar) or [PayPal](https://paypal.me/xaymar). Huge thanks go out to the following people for supporting StreamFX:
` ;
json . supporter = { }
{ // - GitHub
markdown += ` ### GitHub Sponsors
` ;
json . supporter . github = { } ;
let info = await query _github _sponsors ( process . env . GITHUB _TOKEN ) ;
let extra = JSON . parse ( await FS . readFile ( PATH . join ( _ _dirname , "patch-supporters-github.json" ) ) ) ;
for ( let key in extra ) {
info . set ( key , extra [ key ] ) ;
}
2021-11-30 13:41:22 +00:00
for ( let key of Array . from ( info . keys ( ) ) . sort ( ( a , b ) => a . localeCompare ( b , undefined , { numeric : true , sensitivity : 'base' } ) ) ) {
2021-04-19 17:29:55 +00:00
let value = info . get ( key ) ;
json . supporter . github [ key ] = value ;
markdown += ` * [ ${ key } ]( ${ value } ) \n ` ;
}
markdown += "\n" ;
}
{ // - Patreon
// TODO: How do we OAuth without OAuth?!
markdown += ` ### Patreon Patrons
` ;
json . supporter . patreon = { } ;
let info = new Map ( ) ;
let extra = JSON . parse ( await FS . readFile ( PATH . join ( _ _dirname , "patch-supporters-patreon.json" ) ) ) ;
for ( let key in extra ) {
info . set ( key , extra [ key ] ) ;
}
2021-11-30 13:41:22 +00:00
for ( let key of Array . from ( info . keys ( ) ) . sort ( ( a , b ) => a . localeCompare ( b , undefined , { numeric : true , sensitivity : 'base' } ) ) ) {
2021-04-19 17:29:55 +00:00
let value = info . get ( key ) ;
json . supporter . patreon [ key ] = value ;
markdown += ` * [ ${ key } ]( ${ value } ) \n ` ;
}
markdown += "\n" ;
}
// Write Markdown file
FS . writeFile ( process . argv [ 2 ] ,
markdown ,
{
encoding : 'utf8'
}
)
// Write JSON file
FS . writeFile ( process . argv [ 3 ] ,
JSON . stringify ( json , undefined , '\t' ) ,
{
encoding : 'utf8'
}
)
} ) ( ) ;