@@ -14,18 +14,28 @@ import { PDFDocument } from 'pdf-lib';
1414import { Logger } from 'winston' ;
1515import { applyBonusByUserId } from '../components/coin' ;
1616import { vars } from '../config' ;
17- import { sendKickEmbed } from '../utils/embeds' ;
17+ import { sendKickEmbed , DEFAULT_EMBED_COLOUR } from '../utils/embeds' ;
1818import { convertPdfToPic } from '../utils/pdfToPic' ;
1919import { openDB } from '../components/db' ;
2020import { spawnSync } from 'child_process' ;
21+ import { User } from 'discord.js' ;
22+ import { getCoinEmoji } from '../components/emojis' ;
2123
2224const ANNOUNCEMENTS_CHANNEL_ID : string = vars . ANNOUNCEMENTS_CHANNEL_ID ;
2325const RESUME_CHANNEL_ID : string = vars . RESUME_CHANNEL_ID ;
26+ const COUNTING_CHANNEL_ID : string = vars . COUNTING_CHANNEL_ID ;
2427const IRC_USER_ID : string = vars . IRC_USER_ID ;
2528const PDF_FILE_PATH = 'tmp/resume.pdf' ;
2629const HEIC_FILE_PATH = 'tmp/img.heic' ;
2730const CONVERTED_IMG_PATH = 'tmp/img.jpg' ;
2831
32+ // Variables and constants associated with the counting game
33+ const coinsPerMessage : number = 0.1 ; // Number of coins awarded = coinsPerMessage * highest counting number * messages sent by user
34+ const countingAuthorDelay : number = 1 ; // The minimum number of users that must count for someone to go again
35+ const previousCountingAuthors : Array < User > = [ ] ;
36+ const authorMessageCounts : Map < User , number > = new Map ( ) ;
37+ let currentCountingNumber : number = 1 ;
38+
2939/*
3040 * If honeypot is to exist again, then add HONEYPOT_CHANNEL_ID to the config
3141 * and add a check for a message's channel ID being equal to HONEYPOT_CHANNEL_ID
@@ -93,13 +103,12 @@ const convertResumePdfsIntoImages = async (
93103 message : Message ,
94104) : Promise < Message < boolean > | undefined > => {
95105 const attachment = message . attachments . first ( ) ;
96- const hasAttachment = attachment ;
97106 const isPDF = attachment && attachment . contentType === 'application/pdf' ;
98107 const isImage =
99108 attachment && attachment . contentType && attachment . contentType . startsWith ( 'image' ) ;
100109
101110 // If no resume pdf is provided, nuke message and DM user about why their message got nuked
102- if ( ! ( hasAttachment && ( isPDF || isImage ) ) ) {
111+ if ( ! ( attachment && ( isPDF || isImage ) ) ) {
103112 const user = message . author . id ;
104113 const channel = message . channelId ;
105114
@@ -200,6 +209,74 @@ const convertResumePdfsIntoImages = async (
200209 }
201210} ;
202211
212+ const countingGameLogic = async (
213+ client : Client ,
214+ message : Message ,
215+ ) : Promise < Message < boolean > | undefined > => {
216+
217+ // Check to see if game should end
218+ let reasonForFailure = '' ;
219+ if ( isNaN ( Number ( message . content ) ) ) { // Message was not a number
220+ reasonForFailure = `"${ message . content } " is not a number!` ;
221+ }
222+ else if ( previousCountingAuthors . find ( ( author ) => author === message . author ) ) { // Author is still on cooldown
223+ reasonForFailure = `<@${ message . author . id } > counted too recently!` ;
224+ }
225+ else if ( Number ( message . content ) != currentCountingNumber ) { // Wrong number was sent
226+ reasonForFailure = `${ message . content } is not the next number! The next number was ${ currentCountingNumber } .` ;
227+ }
228+
229+ if ( reasonForFailure ) {
230+ return endCountingGame ( client , message , reasonForFailure ) ;
231+ }
232+
233+ // If checks passed, continue the game
234+ currentCountingNumber ++ ;
235+ message . react ( '✅' ) ;
236+ previousCountingAuthors . unshift ( message . author ) ; // Add current author to list of authors on cooldown
237+ while ( previousCountingAuthors . length > countingAuthorDelay ) {
238+ previousCountingAuthors . pop ( ) ; // Remove last author from cooldown
239+ }
240+ const currentAuthorCount : number | undefined = authorMessageCounts . get ( message . author ) ;
241+ authorMessageCounts . set ( message . author , currentAuthorCount ? currentAuthorCount + 1 : 1 ) ;
242+
243+ return ;
244+ }
245+
246+ const endCountingGame = async (
247+ client : Client ,
248+ message : Message ,
249+ reasonForFailure : string
250+ ) : Promise < Message < boolean > | undefined > => {
251+ const sortedAuthorMessageCounts : Array < [ User , number ] > = Array . from ( authorMessageCounts ) . sort ( ( a , b ) => b [ 1 ] - a [ 1 ] ) ; // Turns map into descending sorted array
252+ const coinsAwarded : Array < string > = [ '**Coins awarded:**' ] ;
253+ sortedAuthorMessageCounts . forEach ( ( pair ) => {
254+ pair [ 1 ] *= coinsPerMessage * currentCountingNumber ; // Changes number of messages sent to number of coins awarded
255+ coinsAwarded . push ( `<@${ pair [ 0 ] . id } > - ${ pair [ 1 ] } ${ getCoinEmoji ( ) } ` ) ;
256+ } ) ;
257+
258+ // ** REMEMBER TO ACTUALLY AWARD COINS
259+
260+ const endGameEmbed = new EmbedBuilder ( )
261+ . setColor ( DEFAULT_EMBED_COLOUR )
262+ . setTitle ( 'Counting Game Over' )
263+ . setDescription ( coinsAwarded . join ( '\n' ) ) ;
264+ endGameEmbed . addFields ( [
265+ {
266+ name : 'Reason for Game Over' ,
267+ value : reasonForFailure ,
268+ } ,
269+ ] ) ;
270+
271+ currentCountingNumber = 1 ;
272+ message . react ( '❌' ) ;
273+ previousCountingAuthors . length = 0 ;
274+ authorMessageCounts . clear ( ) ;
275+
276+ return await message . channel ?. send ( { embeds : [ endGameEmbed ] } ) ;
277+ } ;
278+
279+
203280export const initMessageCreate = async (
204281 client : Client ,
205282 logger : Logger ,
@@ -219,6 +296,10 @@ export const initMessageCreate = async (
219296 await convertResumePdfsIntoImages ( client , message ) ;
220297 }
221298
299+ if ( message . channelId === COUNTING_CHANNEL_ID ) {
300+ await countingGameLogic ( client , message ) ;
301+ }
302+
222303 // Ignore DMs; include announcements, thread, and regular text channels
223304 if ( message . channel . type !== ChannelType . DM ) {
224305 await applyBonusByUserId ( message . author . id ) ;
0 commit comments