Skip to content

Commit

Permalink
Check for slash commands prior to splitting across multiple messages.
Browse files Browse the repository at this point in the history
Along the way this also guards against unintended slash command invocation,
preventing a somewhat obscure security vulnerability.
  • Loading branch information
Rosuav committed Dec 6, 2024
1 parent 539da78 commit c3164ee
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 25 deletions.
15 changes: 9 additions & 6 deletions connection.pike
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,14 @@ class channel(mapping identity) {
//slash commands. TODO: Include the voice at the beginning of the message?
if (cfg->simulate) {cfg->simulate(prefix + msg); return;}

if (G->G->send_chat_command) {
//Attempt to send the message(s) via the Twitch APIs if they have slash commands
//Any that can't be sent that way will be sent the usual way.
string|Concurrent.Future handled = G->G->send_chat_command(this, voice, msg);
if (!stringp(handled)) return; //Can optionally await the Future
msg = handled;
}

//Wrap to 500 characters to fit inside the Twitch limit
array msgs = ({ });
while (sizeof(msg) > 500)
Expand All @@ -791,15 +799,10 @@ class channel(mapping identity) {
if (!pos) pos = 500 - sizeof(prefix);
msgs += ({prefix + String.trim(msg[..pos-1])});
msg = String.trim(msg[pos+1..]);
if (has_prefix(msg, "/")) msg = " " + msg; //Prevent slash commands from being treated as commands
}
msgs += ({prefix + msg});

if (G->G->send_chat_commands) {
//Attempt to send the message(s) via the Twitch APIs if they have slash commands
//Any that can't be sent that way will be sent the usual way.
msgs = G->G->send_chat_commands(this, voice, msgs);
if (!sizeof(msgs)) return;
}
mapping tags = ([]);
if (dest == "/reply") tags->reply_parent_msg_id = target;
if (cfg->callback) {
Expand Down
37 changes: 18 additions & 19 deletions modules/twitch_apis.pike
Original file line number Diff line number Diff line change
Expand Up @@ -335,29 +335,28 @@ __async__ void warn(object channel, string voiceid, string msg, mapping tok) {
));
}

//Send a series of chat slash commands, and return any non-command chat messages for subsequent delivery
array(string) send_chat_commands(object channel, string voiceid, array(string) msgs) {
array rest = ({ });
foreach (msgs, string msg) {
sscanf(msg, "/%[^ ] %s", string cmd, string param);
if (!need_scope[cmd]) {rest += ({msg}); continue;}
mapping tok = G->G->user_credentials[(int)voiceid];
if (!voiceid || voiceid == "0") {
voiceid = (string)G->G->bot_uid;
tok = (["token": G->G->dbsettings->credentials->token,
"scopes": G->G->dbsettings->credentials->scopes || ({"whispers:edit"})]);
}
if (!has_value(tok->scopes, need_scope[cmd])) {
channel->report_error("ERROR", "This command requires " + need_scope[cmd] + " permission", msg);
return 0; //Note that this will still suppress the chat message.
}
this[cmd](channel, voiceid, param || "", tok);
//Process a slash command and return a promise when it will be done, or return any
//non-command chat message for direct delivery.
string|Concurrent.Future send_chat_command(object channel, string voiceid, string msg) {
if (!has_prefix(msg, "/")) return msg;
werror("send_chat_command %O\n", msg);
sscanf(msg, "/%[^ ] %s", string cmd, string param);
if (!need_scope[cmd]) return " " + msg; //Return "/asdf" as " /asdf" so it gets output correctly
mapping tok = G->G->user_credentials[(int)voiceid];
if (!voiceid || voiceid == "0") {
voiceid = (string)G->G->bot_uid;
tok = (["token": G->G->dbsettings->credentials->token,
"scopes": G->G->dbsettings->credentials->scopes || ({"whispers:edit"})]);
}
if (!has_value(tok->scopes, need_scope[cmd])) {
channel->report_error("ERROR", "This command requires " + need_scope[cmd] + " permission", msg);
return Concurrent.resolve(1); //Note that this will still suppress the chat message.
}
return rest;
return this[cmd](channel, voiceid, param || "", tok);
}

protected void create(string name) {
G->G->send_chat_commands = send_chat_commands;
G->G->send_chat_command = send_chat_command;
mapping voice_scopes = ([]), scope_commands = ([]);
mapping slashcommands = (["me": "Describe an action -> msg"]);
foreach (Array.transpose(({indices(this), annotations(this)})), [string key, mixed ann]) {
Expand Down

0 comments on commit c3164ee

Please sign in to comment.