/** * Wi-Fi chat-plugin. Only works in a room with id 'wifi' * Handles giveaways in the formats: question, lottery, gts * Written by bumbadadabum, based on the original plugin as written by Codelegend, SilverTactic, DanielCranham **/ 'use strict'; Punishments.roomPunishmentTypes.set('GIVEAWAYBAN', 'banned from giveaways'); const BAN_DURATION = 7 * 24 * 60 * 60 * 1000; function toPokemonId(str) { return str.toLowerCase().replace(/é/g, 'e').replace(/[^a-z0-9 /]/g, ''); } class Giveaway { constructor(host, giver, room, ot, tid, fc, prize) { if (room.gaNumber) { room.gaNumber++; } else { room.gaNumber = 1; } this.host = host; this.giver = giver; this.room = room; this.ot = ot; this.tid = tid; this.fc = `${fc.substr(0, 4)}-${fc.substr(4, 4)}-${fc.substr(8, 4)}`; this.prize = prize; this.phase = 'pending'; this.joined = {}; this.sprite = Giveaway.getSprite(prize); } send(content) { this.room.add(`|uhtml|giveaway${this.room.gaNumber}${this.phase}|
It's giveaway time!
` + `Giveaway started by ${Chat.escapeHTML(this.host.name)}
` + `| ${this.sprite} Giver: ${this.giver} ${Chat.parseText(this.prize)}OT: ${Chat.escapeHTML(this.ot)}, TID: ${this.tid} | ` +
`${rightSide} |
Note: Please do not join if you don't have a 3DS, a copy of Pokémon Sun/Moon, or are currently unable to receive the prize.
`; } } class QuestionGiveaway extends Giveaway { constructor(host, giver, room, ot, tid, fc, prize, question, answers) { super(host, giver, room, ot, tid, fc, prize); this.type = 'question'; this.question = question; this.answers = QuestionGiveaway.sanitizeAnswers(answers); this.answered = {}; // userid: number of guesses this.send(this.generateWindow('The question will be displayed in one minute! Use /ga to answer.')); this.timer = setTimeout(() => this.start(), 1000 * 60); } generateQuestion() { return this.generateWindow(`Giveaway Question: ${this.question}
use /ga to guess.
`); } start() { this.changeUhtml('The giveaway has started! Scroll down to see the question.
'); this.phase = 'started'; this.send(this.generateQuestion()); this.timer = setTimeout(() => this.end(), 1000 * 60 * 5); } guessAnswer(user, guess) { if (this.phase !== 'started') return user.sendTo(this.room, "The giveaway has not started yet."); if (this.checkJoined(user) && Object.values(this.joined).indexOf(user.userid) < 0) return user.sendTo(this.room, "You have already joined the giveaway."); if (this.checkExcluded(user)) return user.sendTo(this.room, "You are disallowed from entering the giveaway."); if (!this.answered[user.userid]) this.answered[user.userid] = 0; if (this.answered[user.userid] >= 3) return user.sendTo(this.room, "You have already guessed three times. You cannot guess anymore in this giveaway."); let sanitized = toId(guess); for (let i = 0; i < this.answers.length; i++) { if (toId(this.answers[i]) === sanitized) { this.winner = user; this.clearTimer(); return this.end(); } } this.joined[user.latestIp] = user.userid; this.answered[user.userid]++; if (this.answered[user.userid] >= 3) { user.sendTo(this.room, `Your guess '${guess}' is wrong. You have used up all of your guesses. Better luck next time!`); } else { user.sendTo(this.room, `Your guess '${guess}' is wrong. Try again!`); } } change(key, value, user) { if (user.userid !== this.host.userid) return user.sendTo(this.room, "Only the host can edit the giveaway."); if (this.phase !== 'pending') return user.sendTo(this.room, "You cannot change the question or answer once the giveaway has started."); if (key === 'question') { this.question = value; return user.sendTo(this.room, `The question has been changed to ${value}.`); } let ans = QuestionGiveaway.sanitizeAnswers(value); if (!ans.length) return user.sendTo(this.room, "You must specify at least one answer and it must not contain any special characters."); this.answers = ans; user.sendTo(this.room, `The answer${Chat.plural(ans, "s have", "has")} been changed to ${ans.join(', ')}.`); } end(force) { if (force) { this.clearTimer(); this.changeUhtml('The giveaway was forcibly ended.
'); this.room.send("The giveaway was forcibly ended."); } else { if (!this.winner) { this.changeUhtml('The giveaway was forcibly ended.
'); this.room.send("The giveaway has been forcibly ended as no one has answered the question."); } else { this.changeUhtml('The giveaway has ended! Scroll down to see the answer.
'); this.phase = 'ended'; this.clearTimer(); this.room.modlog(`${this.winner.name} won ${this.giver.name}'s giveaway for a "${this.prize}" (OT: ${this.ot} TID: ${this.tid} FC: ${this.fc})`); this.send(this.generateWindow(`${Chat.escapeHTML(this.winner.name)} won the giveaway! Congratulations!
` + `${this.question}
Correct answer${Chat.plural(this.answers)}: ${this.answers.join(', ')}
The giveaway was forcibly ended.
'); delete this.room.giveaway; return this.room.send("The giveaway has been forcibly ended as there are not enough participants."); } this.winners = []; while (this.winners.length < this.maxwinners) { let winner = Users(userlist.splice(Math.floor(Math.random() * userlist.length), 1)[0]); if (!winner) continue; this.winners.push(winner); } this.end(); } end(force) { if (force) { this.clearTimer(); this.changeUhtml('The giveaway was forcibly ended.
'); this.room.send("The giveaway was forcibly ended."); } else { this.changeUhtml(`The giveaway has ended! Scroll down to see the winner${Chat.plural(this.winners)}.
`); this.phase = 'ended'; let winnerNames = this.winners.map(winner => winner.name).join(', '); this.room.modlog(`${winnerNames} won ${this.giver.name}'s giveaway for "${this.prize}" (OT: ${this.ot} TID: ${this.tid} FC: ${this.fc})`); this.send(this.generateWindow(`Lottery Draw
${Object.keys(this.joined).length} users joined the giveaway.
Our lucky winner${Chat.plural(this.winners)}: ${Chat.escapeHTML(winnerNames)}! Congratulations!
There is a GTS giveaway going on!
` + `Hosted by: ${Chat.escapeHTML(this.giver.name)} | Left: ${this.left}
` + `| Last winners: ${this.sent.join(' ')} | ` : '') +
`${this.sprite} | ${Chat.parseText(this.summary)} | ` + `${rightSide} |
More Pokémon have been deposited than there are prizes in this giveaway and new deposits will not be accepted. If you have already deposited a Pokémon, please be patient, and do not withdraw your Pokémon.
`); this.changeUhtml(this.generateWindow()); } end(force) { if (force) { this.clearTimer(); this.changeUhtml('The GTS giveaway was forcibly ended.
'); this.room.send("The GTS giveaway was forcibly ended."); } else { this.clearTimer(); this.changeUhtml(`The GTS giveaway has finished.
`); this.room.modlog(`${this.giver.name} has finished their GTS giveaway for "${this.summary}"`); this.send(`The GTS giveaway for a "${Chat.escapeHTML(this.lookfor)}" has finished.
`); } delete this.room.gtsga; } // This currently doesn't match some of the edge cases the other pokemon matching function does account for (such as Type: Null). However, this should never be used as a fodder mon anyway, so I don't see a huge need to implement it. static linkify(text) { let parsed = text.toLowerCase().replace(/é/g, 'e'); for (let i in Dex.data.Pokedex) { let id = i; if (!Dex.data.Pokedex[i].baseSpecies && (Dex.data.Pokedex[i].species.includes(' '))) { id = toPokemonId(Dex.data.Pokedex[i].species); } let regexp = new RegExp(`\\b${id}\\b`, 'ig'); let res = regexp.exec(parsed); if (res) { let num = Dex.data.Pokedex[i].num < 100 ? (Dex.data.Pokedex[i].num < 10 ? `00${Dex.data.Pokedex[i].num}` : `0${Dex.data.Pokedex[i].num}`) : Dex.data.Pokedex[i].num; return `${text.slice(0, res.index)}${text.slice(res.index, res.index + res[0].length)}${text.slice(res.index + res[0].length)}`; } } return text; } } let commands = { // question giveaway. quiz: 'question', qg: 'question', question: function (target, room, user) { if (room.id !== 'wifi' || !target) return false; if (room.giveaway) return this.errorReply("There is already a giveaway going on!"); let [giver, ot, tid, fc, prize, question, ...answers] = target.split(target.includes('|') ? '|' : ',').map(param => param.trim()); if (!(giver && ot && tid && fc && prize && question && answers.length)) return this.errorReply("Invalid arguments specified - /question giver, ot, tid, fc, prize, question, answer(s)"); tid = toId(tid); if (isNaN(tid) || tid.length < 5 || tid.length > 6) return this.errorReply("Invalid TID"); fc = toId(fc); if (!parseInt(fc) || fc.length !== 12) return this.errorReply("Invalid FC"); let targetUser = Users(giver); if (!targetUser || !targetUser.connected) return this.errorReply(`User '${giver}' is not online.`); if (!user.can('warn', null, room) && !(user.can('broadcast', null, room) && user === targetUser)) return this.errorReply("/qg - Access denied."); if (!targetUser.autoconfirmed) return this.errorReply(`User '${targetUser.name}' needs to be autoconfirmed to give something away.`); if (Giveaway.checkBanned(room, targetUser)) return this.errorReply(`User '${targetUser.name}' is giveaway banned.`); room.giveaway = new QuestionGiveaway(user, targetUser, room, ot, tid, fc, prize, question, answers); this.privateModCommand(`(${user.name} started a question giveaway for ${targetUser.name})`); }, changeanswer: 'changequestion', changequestion: function (target, room, user, conn, cmd) { if (room.id !== 'wifi') return false; if (!room.giveaway) return this.errorReply("There is no giveaway going on at the moment."); if (room.giveaway.type !== 'question') return this.errorReply("This is not a question giveaway."); target = target.trim(); if (!target) return this.errorReply("You must include a question or an answer."); room.giveaway.change(cmd.substr(6), target, user); }, showanswer: 'viewanswer', viewanswer: function (target, room, user) { if (room.id !== 'wifi') return false; let giveaway = room.giveaway; if (!giveaway) return this.errorReply("There is no giveaway going on at the moment."); if (giveaway.type !== 'question') return this.errorReply("This is not a question giveaway."); if (user.userid !== giveaway.host.userid && user.userid !== giveaway.giver.userid) return; this.sendReply(`The giveaway question is ${giveaway.question}.\n` + `The answer${Chat.plural(giveaway.answers, 's are', ' is')} ${giveaway.answers.join(', ')}.`); }, guessanswer: 'guess', guess: function (target, room, user) { if (room.id !== 'wifi') return this.errorReply("This command can only be used in the Wi-Fi room."); if (!this.canTalk()) return; if (!room.giveaway) return this.errorReply("There is no giveaway going on at the moment."); if (room.giveaway.type !== 'question') return this.errorReply("This is not a question giveaway."); room.giveaway.guessAnswer(user, target); }, // lottery giveaway. lg: 'lottery', lotto: 'lottery', lottery: function (target, room, user) { if (room.id !== 'wifi' || !target) return false; if (room.giveaway) return this.errorReply("There is already a giveaway going on!"); let [giver, ot, tid, fc, prize, winners] = target.split(target.includes('|') ? '|' : ',').map(param => param.trim()); if (!(giver && ot && tid && fc && prize)) return this.errorReply("Invalid arguments specified - /lottery giver, ot, tid, fc, prize, winners"); tid = toId(tid); if (isNaN(tid) || tid.length < 5 || tid.length > 6) return this.errorReply("Invalid TID"); fc = toId(fc); if (!parseInt(fc) || fc.length !== 12) return this.errorReply("Invalid FC"); let targetUser = Users(giver); if (!targetUser || !targetUser.connected) return this.errorReply(`User '${giver}' is not online.`); if (!user.can('warn', null, room) && !(user.can('broadcast', null, room) && user === targetUser)) return this.errorReply("/lg - Access denied."); if (!targetUser.autoconfirmed) return this.errorReply(`User '${targetUser.name}' needs to be autoconfirmed to give something away.`); if (Giveaway.checkBanned(room, targetUser)) return this.errorReply(`User '${targetUser.name}' is giveaway banned.`); let numWinners = 1; if (winners) { numWinners = parseInt(winners); if (isNaN(numWinners) || numWinners < 1 || numWinners > 10) return this.errorReply("The lottery giveaway can have a minimum of 1 and a maximum of 10 winners."); } room.giveaway = new LotteryGiveaway(user, targetUser, room, ot, tid, fc, prize, numWinners); this.privateModCommand(`(${user.name} started a lottery giveaway for ${targetUser.name})`); }, leavelotto: 'join', leavelottery: 'join', leave: 'join', joinlotto: 'join', joinlottery: 'join', join: function (target, room, user, conn, cmd) { if (room.id !== 'wifi') return this.errorReply("This command can only be used in the Wi-Fi room."); if (!this.canTalk()) return; let giveaway = room.giveaway; if (!giveaway) return this.errorReply("There is no giveaway going on at the moment."); if (giveaway.type !== 'lottery') return this.errorReply("This is not a lottery giveaway."); switch (cmd) { case 'joinlottery': case 'join': case 'joinlotto': giveaway.addUser(user); break; case 'leavelottery': case 'leave': case 'leavelotto': giveaway.removeUser(user); break; } }, // gts commands gts: { new: 'start', start: function (target, room, user) { if (room.id !== 'wifi' || !target) return false; if (room.gtsga) return this.errorReply("There is already a GTS giveaway going on!"); let [giver, amount, summary, deposit, lookfor] = target.split(target.includes('|') ? '|' : ',').map(param => param.trim()); if (!(giver && amount && summary && deposit && lookfor)) return this.errorReply("Invalid arguments specified - /gts start giver, amount, summary, deposit, lookfor"); amount = parseInt(amount); if (!amount || amount < 30 || amount > 100) return this.errorReply("Please enter a valid amount. For a GTS giveaway, you need to give away at least 30 mons, and no more than 100."); let targetUser = Users(giver); if (!targetUser || !targetUser.connected) return this.errorReply(`User '${giver}' is not online.`); if (!this.can('warn', null, room)) return this.errorReply("Permission denied."); if (!targetUser.autoconfirmed) return this.errorReply(`User '${targetUser.name}' needs to be autoconfirmed to host a giveaway.`); if (Giveaway.checkBanned(room, targetUser)) return this.errorReply(`User '${targetUser.name}' is giveaway banned.`); room.gtsga = new GtsGiveaway(room, targetUser, amount, summary, deposit, lookfor); this.privateModCommand(`(${user.name} started a GTS giveaway for ${targetUser.name})`); }, left: function (target, room, user) { if (room.id !== 'wifi') return false; if (!room.gtsga) return this.errorReply("There is no GTS giveaway going on!"); if (!user.can('warn', null, room) && user !== room.gtsga.giver) return this.errorReply("Only the host or a staff member can update GTS giveaways."); if (!target) { if (!this.runBroadcast()) return; let output = `The GTS giveaway from ${room.gtsga.giver} has ${room.gtsga.left} Pokémon remaining!`; if (room.gtsga.sent.length) output += `Last winners: ${room.gtsga.sent.join(', ')}`; return this.sendReply(output); } let newamount = parseInt(target); if (isNaN(newamount)) return this.errorReply("Please enter a valid amount."); if (newamount > room.gtsga.left) return this.errorReply("The new amount must be lower than the old amount."); room.gtsga.updateLeft(newamount); }, sent: function (target, room, user) { if (room.id !== 'wifi') return false; if (!room.gtsga) return this.errorReply("There is no GTS giveaway going on!"); if (!user.can('warn', null, room) && user !== room.gtsga.giver) return this.errorReply("Only the host or a staff member can update GTS giveaways."); if (!target || target.length > 12) return this.errorReply("Please enter a valid IGN."); room.gtsga.updateSent(target); }, full: function (target, room, user) { if (room.id !== 'wifi') return false; if (!room.gtsga) return this.errorReply("There is no GTS giveaway going on!"); if (!user.can('warn', null, room) && user !== room.gtsga.giver) return this.errorReply("Only the host or a staff member can update GTS giveaways."); if (room.gtsga.noDeposits) return this.errorReply("The GTS giveaway was already set to not accept deposits."); room.gtsga.stopDeposits(); }, end: function (target, room, user) { if (room.id !== 'wifi') return this.errorReply("This command can only be used in the Wi-Fi room."); if (!room.gtsga) return this.errorReply("There is no GTS giveaway going on at the moment."); if (!this.can('warn', null, room)) return false; if (target && target.length > 300) { return this.errorReply("The reason is too long. It cannot exceed 300 characters."); } room.gtsga.end(true); if (target) target = `: ${target}`; this.privateModCommand(`(The giveaway was forcibly ended by ${user.name}${target})`); }, }, // general. ban: function (target, room, user) { if (!target) return false; if (room.id !== 'wifi') return this.errorReply("This command can only be used in the Wi-Fi room."); if (!this.can('warn', null, room)) return false; target = this.splitTarget(target); let targetUser = this.targetUser; if (!targetUser) return this.errorReply(`User '${this.targetUsername}' not found.`); if (target.length > 300) { return this.errorReply("The reason is too long. It cannot exceed 300 characters."); } if (Giveaway.checkBanned(room, targetUser)) return this.errorReply(`User '${this.targetUsername}' is already banned from entering giveaways.`); Giveaway.ban(room, targetUser, target); if (room.giveaway) room.giveaway.kickUser(targetUser); if (target) target = ` (${target})`; this.privateModCommand(`(${targetUser.name} was banned from entering giveaways by ${user.name}.${target})`); }, unban: function (target, room, user) { if (!target) return false; if (room.id !== 'wifi') return this.errorReply("This command can only be used in the Wi-Fi room."); if (!this.can('warn', null, room)) return false; this.splitTarget(target); let targetUser = this.targetUser; if (!targetUser) return this.errorReply(`User '${this.targetUsername}' not found.`); if (!Giveaway.checkBanned(room, targetUser)) return this.errorReply(`User '${this.targetUsername}' isn't banned from entering giveaways.`); Giveaway.unban(room, targetUser); this.privateModCommand(`${targetUser.name} was unbanned from entering giveaways by ${user.name}.`); }, stop: 'end', end: function (target, room, user) { if (room.id !== 'wifi') return this.errorReply("This command can only be used in the Wi-Fi room."); if (!room.giveaway) return this.errorReply("There is no giveaway going on at the moment."); if (!this.can('warn', null, room) && user.userid !== room.giveaway.host.userid) return false; if (target && target.length > 300) { return this.errorReply("The reason is too long. It cannot exceed 300 characters."); } room.giveaway.end(true); if (target) target = `: ${target}`; this.privateModCommand(`(The giveaway was forcibly ended by ${user.name}${target})`); }, rm: 'remind', remind: function (target, room, user) { if (room.id !== 'wifi') return this.errorReply("This command can only be used in the Wi-Fi room."); let giveaway = room.giveaway; if (!giveaway) return this.errorReply("There is no giveaway going on at the moment."); if (!this.runBroadcast()) return; if (giveaway.type === 'question') { if (giveaway.phase !== 'started') return this.errorReply("The giveaway has not started yet."); room.giveaway.send(room.giveaway.generateQuestion()); } else { room.giveaway.display(); } }, '': 'help', help: function (target, room, user) { if (room.id !== 'wifi') return this.errorReply("This command can only be used in the Wi-Fi room."); let reply = ''; switch (target) { case 'staff': if (!this.can('broadcast', null, room)) return; reply = 'Staff commands: