2018-02-04 19:09:13 +01:00
// Description:
// Configuration-related functionality
//
// Dependencies:
// "mem-cache": "0.0.5"
//
// Author:
// PombeirP
const { WebClient } = require ( '@slack/client' )
const token = process . env . SLACK _USER _TOKEN || ''
module . exports . build = async ( robot , cache ) => {
const web = new WebClient ( token )
await populateCache ( robot , web , cache )
}
2018-02-06 13:47:21 +01:00
module . exports . getGitHubIdFromSlackId = ( slackUserId , cache ) => {
return cache . get ( getSlackId2GitHubIdCacheKeyName ( slackUserId ) )
2018-02-04 19:09:13 +01:00
}
2018-02-06 13:47:21 +01:00
module . exports . getSlackIdFromGitHubId = ( gitHubId , cache ) => {
return cache . get ( getGitHub2SlackIdCacheKeyName ( gitHubId ) )
2018-02-04 19:09:13 +01:00
}
async function populateCache ( robot , web , cache ) {
2018-02-06 13:47:21 +01:00
robot . log . info ( 'Populating Slack user ID cache...' )
2018-02-04 19:09:13 +01:00
try {
const usersList = await web . users . list ( ) // TODO: This call should be paginated to avoid hitting limits (memory, API): https://api.slack.com/docs/pagination#cursors
const activeUsersList = usersList . members . filter ( u => ! u . deleted && ! u . is _bot && u . id !== 'USLACKBOT' )
let gitHubFieldId = null
let usersMissingGitHubInfo = [ ]
let usersContainingGitHubInfo = [ ]
let rateLimitWait = 10000
let profileFetchPreviousBatchCount = 3
let profileFetchBatchCount = 0
for ( let i = 0 ; i < activeUsersList . length ; ) {
const user = activeUsersList [ i ]
try {
++ profileFetchBatchCount
const { profile } = await web . users . profile . get ( { user : user . id , include _labels : ! gitHubFieldId } )
const username = profile . display _name _normalized || profile . real _name _normalized
if ( ! gitHubFieldId ) {
// Find the field ID for the field with the 'Github ID' label
gitHubFieldId = findGitHubLabelId ( profile )
}
if ( ! gitHubFieldId ) {
2018-02-06 13:47:21 +01:00
robot . log . warn ( ` No GitHub ID field found in @ ${ username } ( ${ user . id } ) profile! ` )
2018-02-04 19:09:13 +01:00
++ i
continue
}
if ( profile . fields && profile . fields [ gitHubFieldId ] ) {
const gitHubUsername = profile . fields [ gitHubFieldId ] . value
2018-02-06 13:47:21 +01:00
robot . log . debug ( ` @ ${ username } ( ${ user . id } ) -> ${ gitHubUsername } ` )
2018-02-04 19:09:13 +01:00
2018-02-06 13:47:21 +01:00
cache . set ( getSlackId2GitHubIdCacheKeyName ( user . id ) , gitHubUsername )
cache . set ( getGitHub2SlackIdCacheKeyName ( gitHubUsername ) , user . id )
2018-02-04 19:09:13 +01:00
usersContainingGitHubInfo = usersContainingGitHubInfo . concat ( username )
} else {
robot . log . warn ( ` @ ${ username } ( ${ user . id } ) has no GitHub ID set ` )
usersMissingGitHubInfo = usersMissingGitHubInfo . concat ( username )
}
++ i
await sleep ( 1500 )
} catch ( e ) {
if ( e . name === 'Error' && e . message === 'ratelimited' ) {
robot . log . trace ( ` Rate-limited, waiting ${ rateLimitWait / 1000 } s... ` )
await sleep ( rateLimitWait )
if ( profileFetchBatchCount < profileFetchPreviousBatchCount ) {
// If we managed to fetch fewer profiles than the last time we got rate-limited, then try increasing the wait period
rateLimitWait += 5000
}
profileFetchPreviousBatchCount = profileFetchBatchCount
profileFetchBatchCount = 0
continue
}
throw e
}
}
2018-02-06 13:47:21 +01:00
robot . log . info ( ` Populated Slack user ID cache with ${ usersContainingGitHubInfo . length } users: ${ usersContainingGitHubInfo . map ( s => '@' + s ) . join ( ', ' ) } ` )
2018-02-04 19:09:13 +01:00
if ( usersMissingGitHubInfo ) {
robot . log . warn ( ` The following ${ usersMissingGitHubInfo . length } Slack users have no GitHub info in their profiles: ${ usersMissingGitHubInfo . map ( s => '@' + s ) . join ( ', ' ) } ` )
}
} catch ( e ) {
2018-02-06 13:47:21 +01:00
robot . log . error ( ` Error while populating Slack user ID cache: ${ e } ` )
2018-02-04 19:09:13 +01:00
}
}
function findGitHubLabelId ( profile ) {
if ( profile . fields ) {
for ( const fieldId in profile . fields ) {
const field = profile . fields [ fieldId ]
if ( field . label === 'Github ID' ) {
return fieldId
}
}
}
return null
}
2018-02-06 13:47:21 +01:00
function getSlackId2GitHubIdCacheKeyName ( slackUserId ) {
return ` Slack- ${ slackUserId } `
2018-02-04 19:09:13 +01:00
}
2018-02-06 13:47:21 +01:00
function getGitHub2SlackIdCacheKeyName ( gitHubUsername ) {
2018-02-04 19:09:13 +01:00
return ` GitHub- ${ gitHubUsername } `
}
function timeout ( ms ) {
return new Promise ( resolve => setTimeout ( resolve , ms ) )
}
async function sleep ( timeoutMs ) {
await timeout ( timeoutMs )
}