@@ -2,16 +2,18 @@ import { Temporal } from '@js-temporal/polyfill';
22
33import { PSCommands , PSPointsNonce , PSRoomConfigs } from '@/cache' ;
44import { prefix } from '@/config/ps' ;
5- import { bulkAddPoints } from '@/database/points' ;
5+ import { type BulkPointsDataInput , bulkAddPoints } from '@/database/points' ;
66import { ANNOUNCEMENTS_CHANNEL , ROLES } from '@/discord/constants/servers/petmods' ;
77import { getChannel } from '@/discord/loaders/channels' ;
88import { IS_ENABLED } from '@/enabled' ;
99import { i18n } from '@/i18n' ;
1010import { TimeZone } from '@/ps/handlers/cron/constants' ;
1111import getSecretFunction from '@/secrets/functions' ;
1212import { toId } from '@/tools' ;
13+ import { Username } from '@/utils/components' ;
1314import { Form } from '@/utils/components/ps' ;
1415import { Logger } from '@/utils/logger' ;
16+ import { pluralize } from '@/utils/pluralize' ;
1517import { randomString } from '@/utils/random' ;
1618
1719import type { PSCommandContext } from '@/types/chat' ;
@@ -58,6 +60,16 @@ function inRange(time: Temporal.PlainTime, range: [Temporal.PlainTime, Temporal.
5860 return Temporal . PlainTime . compare ( time , range [ 0 ] ) === - 1 && Temporal . PlainTime . compare ( time , range [ 1 ] ) === 1 ;
5961 }
6062}
63+
64+ function labelPoints ( data : Record < string , number > , pointsType : string ) : Record < string , Record < string , number > > {
65+ // TODO: Add mapValues
66+ return Object . fromEntries ( Object . entries ( data ) . map ( ( [ user , amount ] ) => [ user , { [ pointsType ] : amount } ] ) ) ;
67+ }
68+
69+ function toBulkData ( data : Record < string , Record < string , number > > ) : BulkPointsDataInput {
70+ return Object . fromEntries ( Object . entries ( data ) . map ( ( [ user , points ] ) => [ user , { id : toId ( user ) , name : user , points } ] ) ) ;
71+ }
72+
6173export function tourHandler ( this : Client , roomId : string , line : string , isIntro ?: boolean ) : void {
6274 if ( isIntro ) return ;
6375
@@ -114,109 +126,151 @@ export function tourHandler(this: Client, roomId: string, line: string, isIntro?
114126 if ( e instanceof Error ) Logger . errorLog ( e ) ;
115127 return ;
116128 }
117- if ( roomId === 'hindi' ) {
118- if ( / c a s u a l | i g n o r e | n o ? p o i n t s / i. test ( json . format || '' ) ) return ;
119- // The actual algorithm is secret
120- // Nice try, though
121- const scoringAlgo = getSecretFunction < ( tourBracket : string ) => Record < string , number > | null > (
122- 'hindiTourPointsAlgo' ,
123- ( ) => null
124- ) ;
125- const pointsToAdd = scoringAlgo ( data ) ;
126- if ( ! pointsToAdd ) return ;
127-
128- const pointsType = PSRoomConfigs [ roomId ] ?. points ?. priority [ 0 ] ;
129- if ( ! pointsType ) throw new Error ( `AAAAAA someone ping PartMan for ${ roomId } ` ) ;
130- bulkAddPoints (
131- Object . fromEntries (
132- Object . entries ( pointsToAdd ) . map ( ( [ user , points ] ) => [
133- user ,
134- { id : toId ( user ) , name : user , points : { [ pointsType ] : points } } ,
135- ] )
136- ) ,
137- roomId
138- ) . then ( async res => {
139- if ( ! res ) return ;
140- const lbCommand = PSCommands . leaderboard ;
141- const partialMessage : RecursivePartial < PSMessage > = {
142- type : 'chat' ,
143- target : room ,
144- parent : this ,
145- } ;
146- const $T = i18n ( ) ; // TODO: Use language pref
147- const partialContext : Partial < PSCommandContext > = {
148- args : [ ] ,
149- message : partialMessage as PSMessage ,
150- broadcastHTML : room . sendHTML . bind ( room ) ,
151- $T,
152- } ;
153-
154- lbCommand . run ( partialContext as PSCommandContext ) ;
129+
130+ const showLeaderboard = ( ) => {
131+ const lbCommand = PSCommands . leaderboard ;
132+ const partialMessage : RecursivePartial < PSMessage > = {
133+ type : 'chat' ,
134+ target : room ,
135+ parent : this ,
136+ } ;
137+ const $T = i18n ( ) ; // TODO: Use language pref
138+ const partialContext : Partial < PSCommandContext > = {
139+ args : [ ] ,
140+ message : partialMessage as PSMessage ,
141+ broadcastHTML : room . sendHTML . bind ( room ) ,
142+ $T,
143+ } ;
144+ lbCommand . run ( partialContext as PSCommandContext ) ;
145+ } ;
146+
147+ /** [#1, #2, #3 and #4] */
148+ const getTopFour = ( ) : string [ ] => {
149+ const winners : string [ ] = [ ] ;
150+ const root = json . bracketData . rootNode ;
151+
152+ // add first place
153+ winners . push ( root . team ) ;
154+ // add second place
155+ root . children ?. forEach ( child => {
156+ if ( child . team !== winners [ 0 ] ) winners . push ( child . team ) ;
157+ } ) ;
158+ // add runners-up
159+ root . children ?. forEach ( child => {
160+ if ( Array . isArray ( child . children ) )
161+ child . children . forEach ( kid => {
162+ if ( ! winners . includes ( kid . team ) ) winners . push ( kid . team ) ;
163+ } ) ;
155164 } ) ;
156- }
157- if ( roomId === 'capproject' ) {
158- const currentTime = Temporal . Now . instant ( ) . toZonedDateTimeISO ( TimeZone . AST ) . toPlainTime ( ) ;
159165
160- if (
161- inRange ( currentTime , [ new Temporal . PlainTime ( 11 ) , new Temporal . PlainTime ( 13 ) ] ) ||
162- inRange ( currentTime , [ new Temporal . PlainTime ( 23 ) , new Temporal . PlainTime ( 1 ) ] )
163- ) {
164- if ( json . generator !== 'Single Elimination' ) return ;
166+ return winners ;
167+ } ;
165168
166- const root = json . bracketData . rootNode ;
167- const winners : string [ ] = [ ] ;
168- const pointsToAdd : Record < string , number > = { } ;
169+ switch ( roomId ) {
170+ case 'hindi' : {
171+ if ( / c a s u a l | i g n o r e | n o ? p o i n t s / i. test ( json . format || '' ) ) return ;
172+ // The actual algorithm is secret
173+ // Nice try, though
174+ const scoringAlgo = getSecretFunction < ( tourBracket : string ) => Record < string , number > | null > (
175+ 'hindiTourPointsAlgo' ,
176+ ( ) => null
177+ ) ;
178+ const pointsToAdd = scoringAlgo ( data ) ;
179+ if ( ! pointsToAdd ) return ;
169180
170- // add first place
171- winners . push ( root . team ) ;
172- // add second place
173- root . children ?. forEach ( child => {
174- if ( child . team !== winners [ 0 ] ) winners . push ( child . team ) ;
175- } ) ;
176- // add runners-up
177- root . children ?. forEach ( child => {
178- if ( Array . isArray ( child . children ) )
179- child . children . forEach ( kid => {
180- if ( ! winners . includes ( kid . team ) ) winners . push ( kid . team ) ;
181- } ) ;
181+ const pointsType = PSRoomConfigs [ roomId ] ?. points ?. priority [ 0 ] ;
182+ if ( ! pointsType ) throw new Error ( `AAAAAA someone ping PartMan for ${ roomId } ` ) ;
183+ bulkAddPoints ( toBulkData ( labelPoints ( pointsToAdd , pointsType ) ) , roomId ) . then ( res => {
184+ if ( res ) showLeaderboard ( ) ;
182185 } ) ;
186+ break ;
187+ }
188+
189+ case 'capproject' : {
190+ const currentTime = Temporal . Now . instant ( ) . toZonedDateTimeISO ( TimeZone . AST ) . toPlainTime ( ) ;
183191
184- [ 3 , 2 , 1 , 1 ] . forEach ( ( amt , index ) => {
185- if ( winners [ index ] ) {
186- pointsToAdd [ winners [ index ] ] = amt ;
192+ if (
193+ inRange ( currentTime , [ new Temporal . PlainTime ( 11 ) , new Temporal . PlainTime ( 13 ) ] ) ||
194+ inRange ( currentTime , [ new Temporal . PlainTime ( 23 ) , new Temporal . PlainTime ( 1 ) ] )
195+ ) {
196+ if ( json . generator !== 'Single Elimination' ) return ;
197+
198+ const winners = getTopFour ( ) ;
199+ const pointsToAdd : Record < string , number > = { } ;
200+
201+ [ 3 , 2 , 1 , 1 ] . forEach ( ( amt , index ) => {
202+ if ( winners [ index ] ) {
203+ pointsToAdd [ winners [ index ] ] = amt ;
204+ }
205+ } ) ;
206+
207+ const pointsType = PSRoomConfigs [ roomId ] ?. points ?. types . tournight ;
208+ if ( ! pointsType ) {
209+ room . send ( "Hi for some reason Tour Nights don't exist, someone go poke PartMan" ) ;
210+ Logger . errorLog ( new Error ( `CAP room points: ${ JSON . stringify ( PSRoomConfigs [ roomId ] ) } ` ) ) ;
211+ return ;
187212 }
188- } ) ;
189213
190- const pointsType = PSRoomConfigs . capproject ?. points ?. types . tournight ;
214+ const nonce = randomString ( ) ;
215+ PSPointsNonce [ nonce ] = labelPoints ( pointsToAdd , pointsType . id ) ;
216+
217+ room . sendHTML (
218+ < div className = "infobox" >
219+ < p >
220+ < b > { pointsType . plural } </ b >
221+ { ': ' }
222+ { Object . entries ( pointsToAdd )
223+ . map ( ( [ user , amount ] ) => `+${ amount } ${ user } ` )
224+ . join ( ', ' ) }
225+ </ p >
226+ < p >
227+ < Form value = { `/botmsg ${ this . status . username } ,${ prefix } @${ roomId } addnonce ${ nonce } ` } >
228+ < button > Add Points!</ button >
229+ </ Form >
230+ </ p >
231+ </ div > ,
232+ { rank : '%' }
233+ ) ;
234+ }
235+ break ;
236+ }
237+
238+ case 'petmods' : {
239+ const winners = getTopFour ( ) ;
240+ if ( winners . length < 4 ) {
241+ room . sendHTML ( < div className = "infobox" > Not adding points for this (only { winners . length } players).</ div > , { rank : '%' } ) ;
242+ return ;
243+ }
244+
245+ const roomConfig = PSRoomConfigs [ roomId ] ?. points ;
246+ const pointsType = roomConfig ?. types [ roomConfig ?. priority [ 0 ] ] ;
191247 if ( ! pointsType ) {
192- room . send ( "Hi for some reason Tour Nights don 't exist , someone go poke PartMan" ) ;
193- Logger . errorLog ( new Error ( `CAP room points: ${ JSON . stringify ( PSRoomConfigs . capproject ) } ` ) ) ;
248+ room . send ( "Hi for some reason points aren 't configured properly , someone go poke PartMan" ) ;
249+ Logger . errorLog ( new Error ( `Pet Mods room points: ${ JSON . stringify ( PSRoomConfigs [ roomId ] ) } ` ) ) ;
194250 return ;
195251 }
196252
197- const nonce = randomString ( ) ;
198- // TODO: Add mapValues
199- PSPointsNonce [ nonce ] = Object . fromEntries (
200- Object . entries ( pointsToAdd ) . map ( ( [ user , amount ] ) => [ user , { [ pointsType . id ] : amount } ] )
201- ) ;
253+ const pointsToAdd : Record < string , number > = { } ;
254+ [ 4 , 2 , 1 , 1 ] . forEach ( ( amount , index ) => ( pointsToAdd [ winners [ index ] ] = amount ) ) ;
202255
203- room . sendHTML (
204- < div className = "infobox" >
205- < p >
206- < b > { pointsType . plural } </ b >
207- { ': ' }
208- { Object . entries ( pointsToAdd )
209- . map ( ( [ user , amount ] ) => `+${ amount } ${ user } ` )
210- . join ( ', ' ) }
211- </ p >
212- < p >
213- < Form value = { `/botmsg ${ this . status . username } ,${ prefix } @${ roomId } addnonce ${ nonce } ` } >
214- < button > Add Points!</ button >
215- </ Form >
216- </ p >
217- </ div > ,
218- { rank : '%' }
219- ) ;
256+ bulkAddPoints ( toBulkData ( labelPoints ( pointsToAdd , pointsType . id ) ) , roomId ) . then ( res => {
257+ if ( ! res ) return ;
258+ room . sendHTML (
259+ < div className = "infobox" >
260+ Added points:{ ' ' }
261+ { Object . entries ( pointsToAdd ) . map ( ( [ user , amount ] ) => (
262+ < >
263+ < Username name = { user } /> : { pluralize ( amount , pointsType ) }
264+ </ >
265+ ) ) }
266+ </ div > ,
267+ {
268+ rank : '%' ,
269+ }
270+ ) ;
271+ showLeaderboard ( ) ;
272+ } ) ;
273+ break ;
220274 }
221275 }
222276 }
0 commit comments