From 1e4952f1b871adaa7ba722b383bf1576d4794b76 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Sun, 12 Jan 2025 14:31:32 +1300 Subject: [PATCH] Add anti-timeout system to reactions interaction --- src/discord/interactions/reactions.js | 45 +++++++++++++--------- src/discord/interactions/reactions.test.js | 34 +++++++++++----- 2 files changed, 51 insertions(+), 28 deletions(-) diff --git a/src/discord/interactions/reactions.js b/src/discord/interactions/reactions.js index 1a5a9ca..13dbd69 100644 --- a/src/discord/interactions/reactions.js +++ b/src/discord/interactions/reactions.js @@ -2,6 +2,8 @@ const DiscordTypes = require("discord-api-types/v10") const {discord, sync, select, from} = require("../../passthrough") +const {id: botID} = require("../../../addbot") +const {InteractionMethods} = require("snowtransfer") /** @type {import("../../matrix/api")} */ const api = sync.require("../../matrix/api") @@ -11,21 +13,28 @@ const utils = sync.require("../../m2d/converters/utils") /** * @param {DiscordTypes.APIMessageApplicationCommandGuildInteraction} interaction * @param {{api: typeof api}} di - * @returns {Promise} + * @returns {AsyncGenerator<{[k in keyof InteractionMethods]?: Parameters[2]}>} */ -async function _interact({data}, {api}) { +async function* _interact({data}, {api}) { const row = from("event_message").join("message_channel", "message_id").join("channel_room", "channel_id") .select("event_id", "room_id").where({message_id: data.target_id}).get() if (!row) { - return { + return yield {createInteractionResponse: { type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource, data: { content: "This message hasn't been bridged to Matrix.", flags: DiscordTypes.MessageFlags.Ephemeral } - } + }} } + yield {createInteractionResponse: { + type: DiscordTypes.InteractionResponseType.DeferredChannelMessageWithSource, + data: { + flags: DiscordTypes.MessageFlags.Ephemeral + } + }} + const reactions = await api.getFullRelations(row.room_id, row.event_id, "m.annotation") /** @type {Map} */ @@ -40,29 +49,27 @@ async function _interact({data}, {api}) { } if (inverted.size === 0) { - return { - type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource, - data: { - content: "Nobody from Matrix reacted to this message.", - flags: DiscordTypes.MessageFlags.Ephemeral - } - } + return yield {editOriginalInteractionResponse: { + content: "Nobody from Matrix reacted to this message.", + }} } - return { - type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource, - data: { - content: [...inverted.entries()].map(([key, value]) => `${key} ā®ž ${value.join(" ⬩ ")}`).join("\n"), - flags: DiscordTypes.MessageFlags.Ephemeral - } - } + return yield {editOriginalInteractionResponse: { + content: [...inverted.entries()].map(([key, value]) => `${key} ā®ž ${value.join(" ⬩ ")}`).join("\n"), + }} } /* c8 ignore start */ /** @param {DiscordTypes.APIMessageApplicationCommandGuildInteraction} interaction */ async function interact(interaction) { - await discord.snow.interaction.createInteractionResponse(interaction.id, interaction.token, await _interact(interaction, {api})) + for await (const response of _interact(interaction, {api})) { + if (response.createInteractionResponse) { + await discord.snow.interaction.createInteractionResponse(interaction.id, interaction.token, response.createInteractionResponse) + } else if (response.editOriginalInteractionResponse) { + await discord.snow.interaction.editOriginalInteractionResponse(botID, interaction.token, response.editOriginalInteractionResponse) + } + } } module.exports.interact = interact diff --git a/src/discord/interactions/reactions.test.js b/src/discord/interactions/reactions.test.js index 6e29f57..50ddeca 100644 --- a/src/discord/interactions/reactions.test.js +++ b/src/discord/interactions/reactions.test.js @@ -1,17 +1,31 @@ const {test} = require("supertape") const {_interact} = require("./reactions") +/** + * @template T + * @param {AsyncIterable} ai + * @returns {Promise} + */ +async function fromAsync(ai) { + const result = [] + for await (const value of ai) { + result.push(value) + } + return result +} + test("reactions: checks if message is bridged", async t => { - const msg = await _interact({ + const msgs = await fromAsync(_interact({ data: { target_id: "0" } - }, {}) - t.equal(msg.data.content, "This message hasn't been bridged to Matrix.") + }, {})) + t.equal(msgs.length, 1) + t.equal(msgs[0].createInteractionResponse.data.content, "This message hasn't been bridged to Matrix.") }) test("reactions: different response if nobody reacted", async t => { - const msg = await _interact({ + const msgs = await fromAsync(_interact({ data: { target_id: "1126786462646550579" } @@ -23,13 +37,14 @@ test("reactions: different response if nobody reacted", async t => { return [] } } - }) - t.equal(msg.data.content, "Nobody from Matrix reacted to this message.") + })) + t.equal(msgs.length, 2) + t.equal(msgs[1].editOriginalInteractionResponse.content, "Nobody from Matrix reacted to this message.") }) test("reactions: shows reactions if there are some, ignoring discord users", async t => { let called = 1 - const msg = await _interact({ + const msgs = await fromAsync(_interact({ data: { target_id: "1126786462646550579" } @@ -73,9 +88,10 @@ test("reactions: shows reactions if there are some, ignoring discord users", asy }] } } - }) + })) + t.equal(msgs.length, 2) t.equal( - msg.data.content, + msgs[1].editOriginalInteractionResponse.content, "🐈 ā®ž cadence [they] ⬩ @rnl:cadence.moe" + "\nšŸˆā€ā¬› ā®ž cadence [they]" )