{
"version": 3,
"sources": ["../../../../server/chat-plugins/lottery.ts"],
"sourcesContent": ["const LOTTERY_FILE = 'config/chat-plugins/lottery.json';\r\n\r\nimport {FS, Utils} from '../../lib';\r\n\r\nconst lotteriesContents = FS(LOTTERY_FILE).readIfExistsSync();\r\nconst lotteries: {\r\n\t[roomid: string]: {\r\n\t\tmaxWinners: number,\r\n\t\tname: string,\r\n\t\tmarkup: string,\r\n\t\tparticipants: {[ip: string]: string},\r\n\t\twinners: string[],\r\n\t\trunning: boolean,\r\n\t},\r\n} = lotteriesContents ? Object.assign(Object.create(null), JSON.parse(lotteriesContents)) : Object.create(null);\r\n\r\nfunction createLottery(roomid: RoomID, maxWinners: number, name: string, markup: string) {\r\n\tif (lotteries[roomid] && !lotteries[roomid].running) {\r\n\t\tdelete lotteries[roomid];\r\n\t}\r\n\tconst lottery = lotteries[roomid];\r\n\tlotteries[roomid] = {\r\n\t\tmaxWinners, name, markup, participants: lottery?.participants || Object.create(null),\r\n\t\twinners: lottery?.winners || [], running: true,\r\n\t};\r\n\twriteLotteries();\r\n}\r\nfunction writeLotteries() {\r\n\tfor (const roomid of Object.keys(lotteries)) {\r\n\t\tif (!Rooms.get(roomid)) {\r\n\t\t\tdelete lotteries[roomid];\r\n\t\t}\r\n\t}\r\n\tFS(LOTTERY_FILE).writeUpdate(() => JSON.stringify(lotteries));\r\n}\r\nfunction destroyLottery(roomid: RoomID) {\r\n\tdelete lotteries[roomid];\r\n\twriteLotteries();\r\n}\r\nfunction endLottery(roomid: RoomID, winners: string[]) {\r\n\tconst lottery = lotteries[roomid];\r\n\tif (!lottery) return;\r\n\tlottery.winners = winners;\r\n\tlottery.running = false;\r\n\tObject.freeze(lottery);\r\n\twriteLotteries();\r\n}\r\n\r\nfunction isSignedUp(roomid: RoomID, user: User) {\r\n\tconst lottery = lotteries[roomid];\r\n\tif (!lottery) return;\r\n\tconst participants = lottery.participants;\r\n\tconst participantNames = Object.values(participants).map(toID);\r\n\tif (participantNames.includes(user.id)) return true;\r\n\tif (Config.noipchecks) return false;\r\n\treturn !!participants[user.latestIp];\r\n}\r\n\r\nfunction addUserToLottery(roomid: RoomID, user: User) {\r\n\tconst lottery = lotteries[roomid];\r\n\tif (!lottery) return;\r\n\tconst participants = lottery.participants;\r\n\tif (!isSignedUp(roomid, user)) {\r\n\t\tparticipants[user.latestIp] = user.name;\r\n\t\twriteLotteries();\r\n\t\treturn true;\r\n\t}\r\n\treturn false;\r\n}\r\nfunction removeUserFromLottery(roomid: RoomID, user: User) {\r\n\tconst lottery = lotteries[roomid];\r\n\tif (!lottery) return;\r\n\tconst participants = lottery.participants;\r\n\tfor (const [ip, participant] of Object.entries(participants)) {\r\n\t\tif (toID(participant) === user.id || ip === user.latestIp) {\r\n\t\t\tdelete participants[ip];\r\n\t\t\twriteLotteries();\r\n\t\t\treturn true;\r\n\t\t}\r\n\t}\r\n\treturn false;\r\n}\r\nfunction getWinnersInLottery(roomid: RoomID) {\r\n\tconst lottery = lotteries[roomid];\r\n\tif (!lottery) return;\r\n\tconst winners = [];\r\n\tconst participants = Object.values(lottery.participants);\r\n\tfor (let i = 0; i < lottery.maxWinners; i++) {\r\n\t\tconst randomIdx = participants.length * Math.random() << 0;\r\n\t\tconst winner = participants[randomIdx];\r\n\t\twinners.push(winner);\r\n\t\tparticipants.splice(randomIdx, 1);\r\n\t}\r\n\treturn winners;\r\n}\r\n\r\nexport const commands: Chat.ChatCommands = {\r\n\tlottery: {\r\n\t\t''(target, room) {\r\n\t\t\troom = this.requireRoom();\r\n\t\t\tconst lottery = lotteries[room.roomid];\r\n\t\t\tif (!lottery) {\r\n\t\t\t\treturn this.errorReply(\"This room doesn't have a lottery running.\");\r\n\t\t\t}\r\n\t\t\treturn this.parse(`/join view-lottery-${room.roomid}`);\r\n\t\t},\r\n\t\tedit: 'create',\r\n\t\tcreate(target, room, user, connection, cmd) {\r\n\t\t\troom = this.requireRoom();\r\n\t\t\tthis.checkCan('declare', null, room);\r\n\t\t\tif (room.battle || !room.persist) {\r\n\t\t\t\treturn this.errorReply('This room does not support the creation of lotteries.');\r\n\t\t\t}\r\n\t\t\tconst lottery = lotteries[room.roomid];\r\n\t\t\tconst edited = lottery?.running;\r\n\t\t\tif (cmd === 'edit' && !target && lottery) {\r\n\t\t\t\tthis.sendReply('Source:');\r\n\t\t\t\tconst markup = Utils.html`${lottery.markup}`.replace(/\\n/g, '
');\r\n\t\t\t\treturn this.sendReplyBox(`/lottery edit ${lottery.maxWinners}, ${lottery.name}, ${markup}`);\r\n\t\t\t}\r\n\t\t\tconst [maxWinners, name, markup] = Utils.splitFirst(target, ',', 2).map(val => val.trim());\r\n\t\t\tif (!(maxWinners && name && markup.length)) {\r\n\t\t\t\treturn this.errorReply(\"You're missing a command parameter - see /help lottery for this command's syntax.\");\r\n\t\t\t}\r\n\t\t\tconst maxWinnersNum = parseInt(maxWinners);\r\n\t\t\tthis.checkHTML(markup);\r\n\t\t\tif (isNaN(maxWinnersNum)) {\r\n\t\t\t\treturn this.errorReply(`${maxWinners} is not a valid number.`);\r\n\t\t\t}\r\n\t\t\tif (maxWinnersNum < 1) {\r\n\t\t\t\treturn this.errorReply('The maximum winners should be at least 1.');\r\n\t\t\t}\r\n\t\t\tif (maxWinnersNum > Number.MAX_SAFE_INTEGER) {\r\n\t\t\t\treturn this.errorReply('The maximum winners number is too large, please pick a smaller number.');\r\n\t\t\t}\r\n\t\t\tif (name.length > 50) {\r\n\t\t\t\treturn this.errorReply('Name needs to be under 50 characters.');\r\n\t\t\t}\r\n\t\t\tcreateLottery(room.roomid, maxWinnersNum, name, markup);\r\n\t\t\tthis.sendReply(`The lottery was successfully ${edited ? 'edited' : 'created'}.`);\r\n\t\t\tif (!edited) {\r\n\t\t\t\tthis.add(\r\n\t\t\t\t\tUtils.html`|raw|
This lottery has already ended. The winners are:
';\r\n\t\t\tbuf += '