@@ -8,7 +8,13 @@ import { alertUsers } from './officeOpenDM';
8
8
import { vars } from '../config' ;
9
9
import { DEFAULT_EMBED_COLOUR } from '../utils/embeds' ;
10
10
import { 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' ;
12
18
import { getInterviewers } from './interviewer' ;
13
19
import {
14
20
getSuggestionPrintout ,
@@ -24,8 +30,13 @@ const NOTIF_CHANNEL_ID: string = vars.NOTIF_CHANNEL_ID;
24
30
const OFFICE_STATUS_CHANNEL_ID : string = vars . OFFICE_STATUS_CHANNEL_ID ;
25
31
const OFFICE_HOURS_STATUS_API = 'https://csclub.uwaterloo.ca/~webcom/office-status.json' ;
26
32
const 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 ] ;
29
40
30
41
// The last known status of the office
31
42
// false if closed
@@ -141,27 +152,69 @@ export const createCoffeeChatCron = (client: Client): CronJob =>
141
152
// Gives Codey coin role to those on the leaderboard list everyday
142
153
export const assignCodeyRoleForLeaderboard = ( client : Client ) : CronJob =>
143
154
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 ) ) ;
146
155
const guild = client . guilds . resolve ( TARGET_GUILD_ID ) ;
147
156
if ( ! guild ) {
148
157
throw new CodeyUserError ( undefined , 'guild not found' ) ;
149
158
}
159
+
150
160
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 ;
159
173
}
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
+ }
165
183
}
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
+ } ) ;
166
219
} ) ;
167
220
} ) ;
0 commit comments