Improvements to command broadcast API

- Method `this.runBroadcast()` has been split from `this.canBroadcast()`.
It's now the only method handling command-usage and intended as the main API
for synchronous commands. Async commands will execute both separately.
Note that the `suppressMessage` parameter is now passed to `runBroadcast`.

- The semantics of `broadcasting` have been similarly split.
`this,broadcastMessage` will be set to a truthy value when `this.canBroadcast` is run.
`this,broadcasting` will only be set to `true` once `this.runBroadcast` is executed.
This commit is contained in:
Ivo Julca
2016-04-02 15:45:54 -05:00
parent 99c9258575
commit 10bb83bf3c
16 changed files with 119 additions and 96 deletions

View File

@@ -17,7 +17,7 @@ This is called an alias: it makes it so `/ip` does the same thing as
But to actually define a command, it's a function:
avatars: function (target, room, user) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
this.sendReplyBox('You can <button name="avatars">change ' +
'your avatar</button> by clicking on it in the <button ' +
'name="openOptions"><i class="icon-cog"></i> Options' +
@@ -129,7 +129,7 @@ Commands have access to the following functions:
if (!this.can('potd')) return false;
`this.canBroadcast(checkOnly)`
`this.runBroadcast()`
* Signifies that a message can be broadcast, as long as the user has
permission to. This will check to see if the user used `!command`
instead of `/command`. If so, it will check to see if the user has
@@ -137,14 +137,13 @@ Commands have access to the following functions:
not. Otherwise, it will add the message to the room, and turn on the
flag `this.broadcasting`, so that `this.sendReply` and `this.sendReplyBox`
will broadcast to the room instead of just the user that used the
command. If checkOnly is set to true, it returns without putting anything
in the room.
command.
Should usually be near the top of the command, like:
if (!this.canBroadcast()) return false;
`this.canBroadcast(checkOnly, suppressMessage)`
`this.runBroadcast(suppressMessage)`
* Functionally the same as `this.canBroadcast()`. However, it will look as
if the user had written the text `suppressMessage`.

View File

@@ -68,16 +68,16 @@ exports.commands = {
ds: 'dexsearch',
dsearch: 'dexsearch',
dexsearch: function (target, room, user, connection, cmd, message) {
if (!this.canBroadcast(true)) return;
if (!this.canBroadcast()) return;
if (!target) return this.parse('/help dexsearch');
return runSearch({
target: target,
cmd: 'dexsearch',
canAll: (!this.broadcasting || room.isPersonal),
message: (this.broadcasting ? "" : message),
canAll: (!this.broadcastMessage || room.isPersonal),
message: (this.broadcastMessage ? "" : message),
}).then(response => {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
if (response.reply) {
this.sendReplyBox(response.reply);
} else if (response.dt) {
@@ -101,7 +101,7 @@ exports.commands = {
rollpokemon: 'randompokemon',
randpoke: 'randompokemon',
randompokemon: function (target, room, user, connection, cmd, message) {
if (!this.canBroadcast(true)) return;
if (!this.canBroadcast()) return;
let targets = target.split(",");
let targetsBuffer = [];
let qty;
@@ -122,11 +122,11 @@ exports.commands = {
return runSearch({
target: targetsBuffer.join(","),
cmd: 'randpoke',
canAll: (!this.broadcasting || room.isPersonal),
message: (this.broadcasting ? "" : message),
canAll: (!this.broadcastMessage || room.isPersonal),
message: (this.broadcastMessage ? "" : message),
}).then(response => {
if (response.reply) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
this.sendReplyBox(response.reply);
} else if (response.dt) {
CommandParser.commands.data.call(this, response.dt, room, user, connection, 'dt');
@@ -141,16 +141,16 @@ exports.commands = {
ms: 'movesearch',
msearch: 'movesearch',
movesearch: function (target, room, user, connection, cmd, message) {
if (!this.canBroadcast(true)) return;
if (!this.canBroadcast()) return;
if (!target) return this.parse('/help movesearch');
return runSearch({
target: target,
cmd: 'movesearch',
canAll: (!this.broadcasting || room.isPersonal),
message: (this.broadcasting ? "" : message),
canAll: (!this.broadcastMessage || room.isPersonal),
message: (this.broadcastMessage ? "" : message),
}).then(response => {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
if (response.reply) {
this.sendReplyBox(response.reply);
} else if (response.dt) {
@@ -171,16 +171,16 @@ exports.commands = {
isearch: 'itemsearch',
itemsearch: function (target, room, user, connection, cmd, message) {
if (!this.canBroadcast(true)) return;
if (!this.canBroadcast()) return;
if (!target) return this.parse('/help itemsearch');
return runSearch({
target: target,
cmd: 'itemsearch',
canAll: (!this.broadcasting || room.isPersonal),
message: (this.broadcasting ? "" : message),
canAll: (!this.broadcastMessage || room.isPersonal),
message: (this.broadcastMessage ? "" : message),
}).then(response => {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
if (response.reply) {
this.sendReplyBox(response.reply);
} else if (response.dt) {
@@ -204,7 +204,7 @@ exports.commands = {
dpplearn: 'learn',
bw2learn: 'learn',
learn: function (target, room, user, connection, cmd, message) {
if (!this.canBroadcast(true)) return;
if (!this.canBroadcast()) return;
if (!target) return this.parse('/help learn');
return runSearch({
@@ -212,7 +212,7 @@ exports.commands = {
cmd: 'learn',
message: cmd,
}).then(response => {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
if (response.reply) {
this.sendReplyBox(response.reply);
} else if (response.error) {

View File

@@ -274,7 +274,7 @@ exports.commands = {
display: function (target, room, user) {
if (!room.game || room.game.title !== 'Hangman') return this.errorReply("There is no game of hangman running in this room.");
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
room.update();
room.game.display(user, this.broadcasting);

View File

@@ -22,7 +22,7 @@ exports.commands = {
alts: 'whois',
whoare: 'whois',
whois: function (target, room, user, connection, cmd) {
if (room.id === 'staff' && !this.canBroadcast()) return;
if (room.id === 'staff' && !this.runBroadcast()) return;
let targetUser = this.targetUserOrSelf(target, user.group === ' ');
if (!targetUser) {
return this.errorReply("User " + this.targetUsername + " not found.");
@@ -214,7 +214,7 @@ exports.commands = {
dex: 'data',
pokedex: 'data',
data: function (target, room, user, connection, cmd) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
let buffer = '';
let targetId = toId(target);
@@ -377,7 +377,7 @@ exports.commands = {
resist: 'weakness',
weakness: function (target, room, user) {
if (!target) return this.parse('/help weakness');
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
target = target.trim();
let targets = target.split(/ ?[,\/ ] ?/);
@@ -475,7 +475,7 @@ exports.commands = {
}
}
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
let factor = 0;
if (Tools.getImmunity(source, defender) || source.ignoreImmunity && (source.ignoreImmunity === true || source.ignoreImmunity[source.type])) {
@@ -500,7 +500,7 @@ exports.commands = {
cover: 'coverage',
coverage: function (target, room, user) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
if (!target) return this.parse("/help coverage");
let targets = target.split(/[,+]/);
@@ -679,7 +679,7 @@ exports.commands = {
statcalc: function (target, room, user) {
if (!target) return this.parse("/help statcalc");
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
let targets = target.split(' ');
@@ -879,7 +879,7 @@ exports.commands = {
*********************************************************/
uptime: function (target, room, user) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
let uptime = process.uptime();
let uptimeText;
if (uptime > 24 * 60 * 60) {
@@ -894,7 +894,7 @@ exports.commands = {
},
groups: function (target, room, user) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
this.sendReplyBox(
"+ <b>Voice</b> - They can use ! commands like !groups, and talk during moderated chat<br />" +
"% <b>Driver</b> - The above, and they can mute. Global % can also lock users and check for alts<br />" +
@@ -911,7 +911,7 @@ exports.commands = {
repository: 'opensource',
git: 'opensource',
opensource: function (target, room, user) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
this.sendReplyBox(
"Pok&eacute;mon Showdown is open source:<br />" +
"- Language: JavaScript (Node.js)<br />" +
@@ -924,23 +924,23 @@ exports.commands = {
"!opensource - Show everyone that information. Requires: + % @ # & ~"],
staff: function (target, room, user) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
this.sendReplyBox("<a href=\"https://www.smogon.com/sim/staff_list\">Pok&eacute;mon Showdown Staff List</a>");
},
forums: function (target, room, user) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
this.sendReplyBox("<a href=\"https://www.smogon.com/forums/forums/pok%C3%A9mon-showdown.209\">Pok&eacute;mon Showdown Forums</a>");
},
suggestions: function (target, room, user) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
this.sendReplyBox("<a href=\"https://www.smogon.com/forums/threads/3534365/\">Make a suggestion for Pok&eacute;mon Showdown</a>");
},
bugreport: 'bugs',
bugs: function (target, room, user) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
if (room.battle) {
this.sendReplyBox("<center><button name=\"saveReplay\"><i class=\"fa fa-upload\"></i> Save Replay</button> &mdash; <a href=\"https://www.smogon.com/forums/threads/3520646/\">Questions</a> &mdash; <a href=\"https://www.smogon.com/forums/threads/3469932/\">Bug Reports</a></center>");
} else {
@@ -953,7 +953,7 @@ exports.commands = {
},
avatars: function (target, room, user) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
this.sendReplyBox("You can <button name=\"avatars\">change your avatar</button> by clicking on it in the <button name=\"openOptions\"><i class=\"fa fa-cog\"></i> Options</button> menu in the upper right. Custom avatars are only obtainable by staff.");
},
avatarshelp: ["/avatars - Explains how to change avatars.",
@@ -961,7 +961,7 @@ exports.commands = {
introduction: 'intro',
intro: function (target, room, user) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
this.sendReplyBox(
"New to competitive Pok&eacute;mon?<br />" +
"- <a href=\"https://www.smogon.com/sim/ps_guide\">Beginner's Guide to Pok&eacute;mon Showdown</a><br />" +
@@ -976,7 +976,7 @@ exports.commands = {
mentoring: 'smogintro',
smogonintro: 'smogintro',
smogintro: function (target, room, user) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
this.sendReplyBox(
"Welcome to Smogon's official simulator! The <a href=\"https://www.smogon.com/forums/forums/264\">Smogon Info / Intro Hub</a> can help you get integrated into the community.<br />" +
"- <a href=\"https://www.smogon.com/forums/threads/3526346\">Useful Smogon Info</a><br />" +
@@ -986,7 +986,7 @@ exports.commands = {
calculator: 'calc',
calc: function (target, room, user) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
this.sendReplyBox(
"Pok&eacute;mon Showdown! damage calculator. (Courtesy of Honko)<br />" +
"- <a href=\"https://pokemonshowdown.com/damagecalc/\">Damage Calculator</a>"
@@ -997,7 +997,7 @@ exports.commands = {
capintro: 'cap',
cap: function (target, room, user) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
this.sendReplyBox(
"An introduction to the Create-A-Pok&eacute;mon project:<br />" +
"- <a href=\"https://www.smogon.com/cap/\">CAP project website and description</a><br />" +
@@ -1010,7 +1010,7 @@ exports.commands = {
"!cap - Show everyone that information. Requires: + % @ # & ~"],
gennext: function (target, room, user) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
this.sendReplyBox(
"NEXT (also called Gen-NEXT) is a mod that makes changes to the game:<br />" +
"- <a href=\"https://github.com/Zarel/Pokemon-Showdown/blob/master/mods/gennext/README.md\">README: overview of NEXT</a><br />" +
@@ -1022,7 +1022,7 @@ exports.commands = {
om: 'othermetas',
othermetas: function (target, room, user) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
target = toId(target);
let buffer = "";
@@ -1065,7 +1065,7 @@ exports.commands = {
tiershelp: 'formathelp',
formatshelp: 'formathelp',
formathelp: function (target, room, user, connection, cmd) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
if (!target) {
return this.sendReplyBox(
"- <a href=\"https://www.smogon.com/tiers/\">Smogon Tiers</a><br />" +
@@ -1128,7 +1128,7 @@ exports.commands = {
roomhelp: function (target, room, user) {
if (room.id === 'lobby' || room.battle) return this.sendReply("This command is too spammy for lobby/battles.");
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
this.sendReplyBox(
"Room drivers (%) can use:<br />" +
"- /warn OR /k <em>username</em>: warn a user and show the Pok&eacute;mon Showdown rules<br />" +
@@ -1173,7 +1173,7 @@ exports.commands = {
restarthelp: function (target, room, user) {
if (room.id === 'lobby' && !this.can('lockdown')) return false;
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
this.sendReplyBox(
"The server is restarting. Things to know:<br />" +
"- We wait a few minutes before restarting so people can finish up their battles<br />" +
@@ -1205,7 +1205,7 @@ exports.commands = {
rule: 'rules',
rules: function (target, room, user) {
if (!target) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
this.sendReplyBox("Please follow the rules:<br />" +
(room.rulesLink ? "- <a href=\"" + Tools.escapeHTML(room.rulesLink) + "\">" + Tools.escapeHTML(room.title) + " room rules</a><br />" : "") +
"- <a href=\"https://pokemonshowdown.com/rules\">" + (room.rulesLink ? "Global rules" : "Rules") + "</a>");
@@ -1229,7 +1229,7 @@ exports.commands = {
"/rules [url] - Change the room rules URL. Requires: # & ~"],
faq: function (target, room, user) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
target = target.toLowerCase();
let buffer = "";
let matched = false;
@@ -1301,7 +1301,7 @@ exports.commands = {
analysis: 'smogdex',
strategy: 'smogdex',
smogdex: function (target, room, user) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
let targets = target.split(',');
let pokemon = Tools.getTemplate(targets[0]);
@@ -1414,7 +1414,7 @@ exports.commands = {
"!analysis [pokemon], [generation] - Shows everyone this link. Requires: + % @ # & ~"],
veekun: function (target, broadcast, user) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
let baseLink = 'http://veekun.com/dex/';
@@ -1484,7 +1484,7 @@ exports.commands = {
"!veekun [pokemon] - Shows everyone this link. Requires: + % @ # & ~"],
register: function () {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
this.sendReplyBox('You will be prompted to register upon winning a rated battle. Alternatively, there is a register button in the <button name="openOptions"><i class="fa fa-cog"></i> Options</button> menu in the upper right.');
},
@@ -1509,7 +1509,7 @@ exports.commands = {
roll: 'dice',
dice: function (target, room, user) {
if (!target || target.match(/[^d\d\s\-\+HL]/i)) return this.parse('/help dice');
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
// ~30 is widely regarded as the sample size required for sum to be a Gaussian distribution.
// This also sets a computation time constraint for safety.
@@ -1602,7 +1602,7 @@ exports.commands = {
pickrandom: function (target, room, user) {
let options = target.split(',');
if (options.length < 2) return this.parse('/help pick');
if (!this.canBroadcast()) return false;
if (!this.runBroadcast()) return false;
const pickedOption = options[Math.floor(Math.random() * options.length)];
return this.sendReplyBox('<em>We randomly picked:</em> ' + Tools.escapeHTML(pickedOption).trim());
},
@@ -1611,7 +1611,7 @@ exports.commands = {
showimage: function (target, room, user) {
if (!target) return this.parse('/help showimage');
if (!this.can('declare', null, room)) return false;
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
if (this.room.isPersonal && !this.user.can('announce')) {
return this.errorReply("Images are not allowed in personal rooms.");
}
@@ -1663,7 +1663,7 @@ exports.commands = {
if (message.charAt(0) === '!') this.broadcasting = true;
} else {
if (!this.can('declare', null, room)) return;
if (!this.canBroadcast(false, '!htmlbox')) return;
if (!this.runBroadcast('!htmlbox')) return;
}
this.sendReplyBox(target);

View File

@@ -487,7 +487,7 @@ function renderGrid(questions, mode) {
let commands = {
help: function () {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
this.sendReplyBox(
"All commands are run under /jeopardy or /jp. For example, /jeopardy viewgrid.<br />" +
@@ -510,7 +510,7 @@ let commands = {
'': 'viewgrid',
viewgrid: function (target, room, user) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
let jeopardy = jeopardies[room.id];
let questions = null;

View File

@@ -238,7 +238,7 @@ exports.commands = {
room.add("The poll timer was turned on: the poll will end in " + timeout + " minute(s).");
return this.privateModCommand("(The poll timer was set to " + timeout + " minute(s) by " + user.name + ".)");
} else {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
if (room.poll.timeout) {
return this.sendReply("The poll timer is on and will end in " + room.poll.timeoutMins + " minute(s).");
} else {
@@ -272,7 +272,7 @@ exports.commands = {
show: 'display',
display: function (target, room, user, connection) {
if (!room.poll) return this.errorReply("There is no poll running in this room.");
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
room.update();
if (this.broadcasting) {

View File

@@ -133,7 +133,7 @@ exports.commands = {
scavengershelp: 'scavengerhelp',
scavengerhelp: function (target, room, user) {
if (room.id !== 'scavengers') return this.errorReply('This command can only be used in the Scavengers room.');
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
this.sendReplyBox(
'<strong>Player commands:</strong><br />' +
'- /scavengers - Join the scavengers room<br />' +

View File

@@ -39,12 +39,12 @@ function wikiaSearch(subdomain, query, callback) {
exports.commands = {
ygo: 'yugioh',
yugioh: function (target, room, user) {
if (!this.canBroadcast(true)) return;
if (!this.canBroadcast()) return;
let subdomain = 'yugioh';
let query = target.trim();
wikiaSearch(subdomain, query, (err, data) => {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
if (err) {
if (err instanceof SyntaxError || err.message === 'Malformed data') {
if (!this.broadcasting) return this.sendReply("Error: Something went wrong in the request: " + err.message);

View File

@@ -14,7 +14,7 @@ exports.commands = {
if (room.id !== 'thehappyplace') return this.errorReply("This command can only be used in The Happy Place.");
if (!room.chatRoomData) return;
if (!target) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
if (!room.chatRoomData.quote) return this.sendReplyBox("The Quote of the Day has not been set.");
return this.sendReplyBox(
"The current <strong>Inspirational Quote of the Day</strong> is:<br />" +

View File

@@ -167,7 +167,7 @@ let commands = {
return this.sendReplyBox(buffer);
}
if (!this.canBroadcast()) return false;
if (!this.runBroadcast()) return false;
if (!artistOfTheDay.nominations.size) return this.sendReplyBox("No nominations have been submitted yet.");
let nominations = Array.from(artistOfTheDay.nominations).sort((a, b) => a[1].localeCompare(b[1]));
@@ -225,7 +225,7 @@ let commands = {
if (room.id !== 'thestudio') return this.errorReply('This command can only be used in The Studio.');
if (!room.chatRoomData) return false;
if (!target) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
if (!room.chatRoomData.artistQuoteOfTheDay) return this.sendReplyBox("The Artist Quote of the Day has not been set.");
return this.sendReplyBox(
"The current <strong>Artist Quote of the Day</strong> is:<br />" +
@@ -256,13 +256,13 @@ let commands = {
'': function (target, room) {
if (room.id !== 'thestudio') return this.errorReply('This command can only be used in The Studio.');
if (!room.chatRoomData || !this.canBroadcast()) return false;
if (!room.chatRoomData || !this.runBroadcast()) return false;
this.sendReplyBox("The Artist of the Day " + (room.chatRoomData.artistOfTheDay ? "is " + room.chatRoomData.artistOfTheDay + "." : "has not been set yet."));
},
help: function (target, room) {
if (room.id !== 'thestudio') return this.errorReply('This command can only be used in The Studio.');
if (!room.chatRoomData || !this.canBroadcast()) return false;
if (!room.chatRoomData || !this.runBroadcast()) return false;
this.sendReply("Use /help aotd to view help for all commands, or /help aotd [command] for help on a specific command.");
},
};

View File

@@ -822,7 +822,7 @@ let commands = {
let buffer = "|raw|<div class=\"ladder\"><table>";
if (!target) {
if (!this.canBroadcast()) return false;
if (!this.runBroadcast()) return false;
let questions = triviaData.questions;
let questionsLen = questions.length;
@@ -882,7 +882,7 @@ let commands = {
'': 'status',
status: function (target, room, user) {
if (room.id !== 'trivia') return this.errorReply('This command can only be used in Trivia.');
if (!this.canBroadcast()) return false;
if (!this.runBroadcast()) return false;
let trivium = trivia[room.id];
if (!trivium) return this.errorReply("There is no trivia game in progress.");
trivium.getStatus(this, user);
@@ -891,7 +891,7 @@ let commands = {
players: function (target, room) {
if (room.id !== 'trivia') return this.errorReply('This command can only be used in Trivia.');
if (!this.canBroadcast()) return false;
if (!this.runBroadcast()) return false;
let trivium = trivia[room.id];
if (!trivium) return this.errorReply("There is no trivia game in progress.");
trivium.getParticipants(this);
@@ -926,7 +926,7 @@ let commands = {
ladder: function (target, room) {
if (room.id !== 'trivia') return this.errorReply('This command can only be used in Trivia.');
if (!this.canBroadcast()) return false;
if (!this.runBroadcast()) return false;
let ladder = triviaData.ladder;
let leaderboard = triviaData.leaderboard;

View File

@@ -361,7 +361,7 @@ let commands = {
if (room.id !== 'wifi') return this.errorReply("This command can only be used in the Wi-Fi room.");
let giveaway = giveaways[room.id];
if (!giveaway) return this.errorReply("There is no giveaway going on at the moment.");
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
if (giveaway.type === 'question') {
if (giveaway.phase !== 'started') return this.errorReply("The giveaway has not started yet.");
this.sendReply("|html|<div class='broadcast-blue'><font size='1'>Question Giveaway started by " + Tools.escapeHTML(giveaway.host.name) + "</font><br/>" +
@@ -390,7 +390,7 @@ let commands = {
case 'game':
case 'giveaway':
case 'user':
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
reply = '<strong>Giveaway participation commands: </strong> (start with /giveaway, except for /ga) <br />' +
'- guess or /ga <em>answer</em> - Guesses the answer for a question giveaway<br />' +
'- viewanswer - Shows the answer in a question giveaway (only to host/giver)<br />' +
@@ -399,7 +399,7 @@ let commands = {
'- leave or leavelottery - Leaves a lottery giveaway<br />';
break;
default:
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
reply = '<b>Wi-Fi room Giveaway help and info</b><br />' +
'- help user - shows list of participation commands<br />' +
'- help staff - shows giveaway staff commands (Requires: % @ # & ~)';

View File

@@ -170,31 +170,54 @@ class CommandContext {
}
return true;
}
canBroadcast(checkOnly, suppressMessage) {
let message = this.canTalk(this.message);
if (!message) return false;
let normalized = message.toLowerCase().replace(/[^a-z0-9\s!,]/g, '');
canBroadcast() {
if (!this.broadcasting && this.cmdToken === BROADCAST_TOKEN) {
if (this.user.broadcasting) {
this.errorReply("You can't broadcast another command too soon.");
return false;
}
let message = this.canTalk(this.message);
if (!message) return false;
if (!this.user.can('broadcast', null, this.room)) {
this.errorReply("You need to be voiced to broadcast this command's information.");
this.errorReply("To see it for yourself, use: /" + message.substr(1));
this.errorReply("To see it for yourself, use: /" + this.message.substr(1));
return false;
}
// broadcast cooldown
if (this.room.lastBroadcast === normalized &&
let broadcastMessage = message.toLowerCase().replace(/[^a-z0-9\s!,]/g, '');
if (this.room.lastBroadcast === this.broadcastMessage &&
this.room.lastBroadcastTime >= Date.now() - BROADCAST_COOLDOWN) {
this.errorReply("You can't broadcast this because it was just broadcast.");
return false;
}
this.broadcasting = true;
this.message = message;
this.broadcastMessage = broadcastMessage;
this.user.broadcasting = true;
}
if (this.broadcasting && !checkOnly) {
this.add('|c|' + this.user.getIdentity(this.room.id) + '|' + (suppressMessage || message));
this.room.lastBroadcast = normalized;
this.room.lastBroadcastTime = Date.now();
return true;
}
runBroadcast(suppressMessage) {
if (this.broadcasting || this.cmdToken !== BROADCAST_TOKEN) {
// Already being broadcast, or the user doesn't intend to broadcast.
return true;
}
if (!this.broadcastMessage) {
// Permission hasn't been checked yet. Do it now.
if (!this.canBroadcast()) return false;
}
this.add('|c|' + this.user.getIdentity(this.room.id) + '|' + (suppressMessage || this.message));
this.room.lastBroadcast = this.broadcastMessage;
this.room.lastBroadcastTime = Date.now();
this.broadcasting = true;
this.user.broadcasting = false;
return true;
}
parse(message, inNamespace, room) {

View File

@@ -25,7 +25,7 @@ const HOURMUTE_LENGTH = 60 * 60 * 1000;
exports.commands = {
version: function (target, room, user) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
this.sendReplyBox("Server version: <b>" + CommandParser.package.version + "</b>");
},
@@ -594,7 +594,7 @@ exports.commands = {
roomdesc: function (target, room, user) {
if (!target) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
if (!room.desc) return this.sendReply("This room does not have a description set.");
this.sendReplyBox("The room description is: " + Tools.escapeHTML(room.desc));
return;
@@ -627,7 +627,7 @@ exports.commands = {
topic: 'roomintro',
roomintro: function (target, room, user) {
if (!target) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
if (!room.introMessage) return this.sendReply("This room does not have an introduction set.");
this.sendReply('|raw|<div class="infobox infobox-limited">' + room.introMessage + '</div>');
if (!this.broadcasting && user.can('declare', null, room)) {
@@ -696,7 +696,7 @@ exports.commands = {
roomalias: function (target, room, user) {
if (!target) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
if (!room.aliases || !room.aliases.length) return this.sendReplyBox("This room does not have any aliases.");
return this.sendReplyBox("This room has the following aliases: " + room.aliases.join(", ") + "");
}
@@ -2241,7 +2241,7 @@ exports.commands = {
if (!user.hasConsoleAccess(connection)) {
return this.errorReply("/eval - Access denied.");
}
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
if (!this.broadcasting) this.sendReply('||>> ' + target);
try {
@@ -2259,7 +2259,7 @@ exports.commands = {
if (!user.hasConsoleAccess(connection)) {
return this.errorReply("/evalbattle - Access denied.");
}
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
if (!room.battle) {
return this.errorReply("/evalbattle - This isn't a battle room.");
}

View File

@@ -806,7 +806,7 @@ let commands = {
}
},
getusers: function (tournament) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
let users = usersToNames(tournament.generator.getUsers(true).sort());
this.sendReplyBox("<strong>" + users.length + " users remain in this tournament:</strong><br />" + Tools.escapeHTML(users.join(", ")));
},
@@ -993,7 +993,7 @@ CommandParser.commands.tournament = function (paramString, room, user) {
if (!params[0]) params = [];
if (cmd === '') {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
this.sendReply('|tournaments|info|' + JSON.stringify(Object.keys(exports.tournaments).filter(tournament => {
tournament = exports.tournaments[tournament];
return !tournament.room.isPrivate && !tournament.room.isPersonal && !tournament.room.staffRoom;
@@ -1112,7 +1112,7 @@ CommandParser.commands.tournament = function (paramString, room, user) {
}
};
CommandParser.commands.tournamenthelp = function (target, room, user) {
if (!this.canBroadcast()) return;
if (!this.runBroadcast()) return;
return this.sendReplyBox(
"- create/new &lt;format>, &lt;type> [, &lt;comma-separated arguments>]: Creates a new tournament in the current room.<br />" +
"- settype &lt;type> [, &lt;comma-separated arguments>]: Modifies the type of tournament after it's been created, but before it has started.<br />" +

View File

@@ -431,6 +431,7 @@ class User {
this.chatQueue = null;
this.chatQueueTimeout = null;
this.lastChatMessage = 0;
this.broadcasting = false;
// for the anti-spamming mechanism
this.lastMessage = '';