@@ -8,7 +8,13 @@ import { alertUsers } from './officeOpenDM';
88import { vars } from '../config' ;
99import { DEFAULT_EMBED_COLOUR } from '../utils/embeds' ;
1010import { getMatch , writeHistoricMatches } from '../components/coffeeChat' ;
11- import { adjustCoinBalanceByUserId , BonusType , coinBonusMap , getCoinLeaderboard } from './coin' ;
11+ import {
12+ adjustCoinBalanceByUserId ,
13+ BonusType ,
14+ coinBonusMap ,
15+ getCoinLeaderboard ,
16+ UserCoinEntry ,
17+ } from './coin' ;
1218import { getInterviewers } from './interviewer' ;
1319import {
1420 getSuggestionPrintout ,
@@ -24,8 +30,13 @@ const NOTIF_CHANNEL_ID: string = vars.NOTIF_CHANNEL_ID;
2430const OFFICE_STATUS_CHANNEL_ID : string = vars . OFFICE_STATUS_CHANNEL_ID ;
2531const OFFICE_HOURS_STATUS_API = 'https://csclub.uwaterloo.ca/~webcom/office-status.json' ;
2632const TARGET_GUILD_ID : string = vars . TARGET_GUILD_ID ;
27- const CODEY_COIN_ROLE_ID : string = vars . CODEY_COIN_ROLE_ID ;
28- const NUMBER_USERS_TO_ASSIGN_ROLE = 10 ;
33+ const NUMBER_USERS_TO_ASSIGN_ROLE = 20 ;
34+ const CODEY_COIN_ROLES : string [ ] = [
35+ vars . CODEY_COIN_T5_ROLE_ID ,
36+ vars . CODEY_COIN_T10_ROLE_ID ,
37+ vars . CODEY_COIN_T20_ROLE_ID ,
38+ ] ;
39+ const CODEY_COIN_INTERVALS : number [ ] = [ 5 , 10 , 20 ] ;
2940
3041// The last known status of the office
3142// false if closed
@@ -141,27 +152,69 @@ export const createCoffeeChatCron = (client: Client): CronJob =>
141152// Gives Codey coin role to those on the leaderboard list everyday
142153export const assignCodeyRoleForLeaderboard = ( client : Client ) : CronJob =>
143154 new CronJob ( '0 0 0 */1 * *' , async function ( ) {
144- const leaderboard = await getCoinLeaderboard ( NUMBER_USERS_TO_ASSIGN_ROLE ) ;
145- const leaderboardIds : Set < string > = new Set ( leaderboard . map ( ( entry ) => entry . user_id ) ) ;
146155 const guild = client . guilds . resolve ( TARGET_GUILD_ID ) ;
147156 if ( ! guild ) {
148157 throw new CodeyUserError ( undefined , 'guild not found' ) ;
149158 }
159+
150160 const members = await guild . members . fetch ( ) ;
151- // Removing role from previous members
152- const guildMembersPreviousRole = await loadRoleMembers ( CODEY_COIN_ROLE_ID ) ;
153- const previousIds : Set < string > = new Set ( guildMembersPreviousRole . map ( ( member ) => member . id ) ) ;
154- const roleName : string = await getRoleName ( CODEY_COIN_ROLE_ID ) ;
155-
156- guildMembersPreviousRole . forEach ( async ( member ) => {
157- if ( member && ! leaderboardIds . has ( member . id ) ) {
158- await updateMemberRole ( member , roleName , false ) ;
161+ const leaderboard : UserCoinEntry [ ] = [ ] ;
162+ let fetchAttempts = 0 ;
163+
164+ // Fetch leaderboard until we have enough human members to assign roles to
165+ while ( leaderboard . length < NUMBER_USERS_TO_ASSIGN_ROLE ) {
166+ const leaderboardBuffer = await getCoinLeaderboard (
167+ NUMBER_USERS_TO_ASSIGN_ROLE ,
168+ fetchAttempts * NUMBER_USERS_TO_ASSIGN_ROLE ,
169+ ) ;
170+
171+ if ( leaderboardBuffer . length <= 0 ) {
172+ break ;
159173 }
160- } ) ;
161- leaderboardIds . forEach ( async ( user_id ) => {
162- const memberToUpdate = members . get ( user_id ) ;
163- if ( memberToUpdate && ! previousIds . has ( user_id ) ) {
164- await updateMemberRole ( memberToUpdate , roleName , true ) ;
174+
175+ for ( const entry of leaderboardBuffer ) {
176+ if ( members . get ( entry . user_id ) ?. user ?. bot ) {
177+ continue ;
178+ }
179+ leaderboard . push ( entry ) ;
180+ if ( leaderboard . length >= NUMBER_USERS_TO_ASSIGN_ROLE ) {
181+ break ;
182+ }
165183 }
184+
185+ fetchAttempts ++ ;
186+ }
187+
188+ // Create slices of the leaderboard to assign roles to
189+ const topSlices : string [ ] [ ] = [ ] ;
190+ for ( let i = 0 ; i < CODEY_COIN_INTERVALS . length ; i ++ ) {
191+ topSlices . push (
192+ leaderboard
193+ // Slice the leaderboard into intervals -> {[0,i), [i-1, i), [i-1, end|i)}
194+ . slice ( i === 0 ? 0 : CODEY_COIN_INTERVALS [ i - 1 ] , CODEY_COIN_INTERVALS [ i ] )
195+ . map ( ( entry ) => entry . user_id ) ,
196+ ) ;
197+ }
198+
199+ CODEY_COIN_ROLES . forEach ( async ( roleId , idx ) => {
200+ const roleName : string = await getRoleName ( roleId ) ;
201+ const guildMembersPreviousRole = await loadRoleMembers ( roleId ) ;
202+ const previousIds : Set < string > = new Set ( guildMembersPreviousRole . map ( ( member ) => member . id ) ) ;
203+ const leaderboardIds = new Set ( topSlices [ idx ] ) ;
204+
205+ // Removing role from former members
206+ guildMembersPreviousRole . forEach ( async ( member ) => {
207+ if ( member && ! leaderboardIds . has ( member . id ) ) {
208+ await updateMemberRole ( member , roleName , false ) ;
209+ }
210+ } ) ;
211+
212+ // Adding role to new members
213+ leaderboardIds . forEach ( async ( user_id ) => {
214+ const memberToUpdate = members . get ( user_id ) ;
215+ if ( memberToUpdate && ! previousIds . has ( user_id ) ) {
216+ await updateMemberRole ( memberToUpdate , roleName , true ) ;
217+ }
218+ } ) ;
166219 } ) ;
167220 } ) ;
0 commit comments