@@ -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