diff --git a/README.md b/README.md index 28664f64b32770a71ace8997465ffdbfe69bc1e1..934ed5feb3e94fd4f9ff2a0ec639b9b24dc49040 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,6 @@ In a vague order of what is coming up next - [x] Audio/Video content - [ ] Typing notifs (**Not supported, requires syncing**) - [x] User Profiles - - [ ] Reactions - Discord -> Matrix - [x] Text content - [x] Image content @@ -153,7 +152,6 @@ In a vague order of what is coming up next - [x] User Profiles - [x] Presence - [x] Per-guild display names. - - [x] Reactions - [x] Group messages - [ ] Third Party Lookup - [x] Rooms diff --git a/changelog.d/862.feature b/changelog.d/862.feature deleted file mode 100644 index a693baafcfc131dce751aa5750e545c27ccc9aee..0000000000000000000000000000000000000000 --- a/changelog.d/862.feature +++ /dev/null @@ -1 +0,0 @@ -Adds one-way reaction support from Discord -> Matrix. Thanks to @SethFalco! diff --git a/src/bot.ts b/src/bot.ts index 8e0ccfb37c19df2f523d68e0bccf0222b83159f8..f5cd93c9f986288bb54ab3e0fd313b1b57ee02ea 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -263,21 +263,6 @@ export class DiscordBot { await this.channelSync.OnGuildDelete(guild); } catch (err) { log.error("Exception thrown while handling \"guildDelete\" event", err); } }); - client.on("messageReactionAdd", async (reaction, user) => { - try { - await this.OnMessageReactionAdd(reaction, user); - } catch (err) { log.error("Exception thrown while handling \"messageReactionAdd\" event", err); } - }); - client.on("messageReactionRemove", async (reaction, user) => { - try { - await this.OnMessageReactionRemove(reaction, user); - } catch (err) { log.error("Exception thrown while handling \"messageReactionRemove\" event", err); } - }); - client.on("messageReactionRemoveAll", async (message) => { - try { - await this.OnMessageReactionRemoveAll(message); - } catch (err) { log.error("Exception thrown while handling \"messageReactionRemoveAll\" event", err); } - }); // Due to messages often arriving before we get a response from the send call, // messages get delayed from discord. We use Util.DelayedPromise to handle this. @@ -1221,146 +1206,6 @@ export class DiscordBot { await this.OnMessage(newMsg); } - public async OnMessageReactionAdd(reaction: Discord.MessageReaction, user: Discord.User | Discord.PartialUser) { - const message = reaction.message; - const reactionName = reaction.emoji.name; - log.verbose(`Got message reaction add event for ${message.id} with ${reactionName}`); - - let rooms: string[]; - - try { - rooms = await this.channelSync.GetRoomIdsFromChannel(message.channel); - } catch (err) { - log.verbose(`No bridged rooms to forward reaction to. Reaction Event: ${reaction}`); - MetricPeg.get.requestOutcome(message.id, true, "dropped"); - return; - } - - const intent = this.GetIntentFromDiscordMember(user); - await intent.ensureRegistered(); - this.userActivity.updateUserActivity(intent.userId); - - const storeEvent = await this.store.Get(DbEvent, { - discord_id: message.id - }); - - if (!storeEvent?.Result) { - log.verbose(`Received add reaction event for untracked message. Dropping! Reaction Event: ${reaction}`); - return; - } - - while (storeEvent.Next()) { - const matrixIds = storeEvent.MatrixId.split(";"); - - for (const room of rooms) { - const reactionEventId = await intent.underlyingClient.unstableApis.addReactionToEvent( - room, - matrixIds[0], - reaction.emoji.id ? `:${reactionName}:` : reactionName - ); - - const event = new DbEvent(); - event.MatrixId = `${reactionEventId};${room}`; - event.DiscordId = message.id; - event.ChannelId = message.channel.id; - if (message.guild) { - event.GuildId = message.guild.id; - } - - await this.store.Insert(event); - } - } - } - - public async OnMessageReactionRemove(reaction: Discord.MessageReaction, user: Discord.User | Discord.PartialUser) { - const message = reaction.message; - log.verbose(`Got message reaction remove event for ${message.id} with ${reaction.emoji.name}`); - - const intent = this.GetIntentFromDiscordMember(user); - await intent.ensureRegistered(); - this.userActivity.updateUserActivity(intent.userId); - - const storeEvent = await this.store.Get(DbEvent, { - discord_id: message.id, - }); - - if (!storeEvent?.Result) { - log.verbose(`Received remove reaction event for untracked message. Dropping! Reaction Event: ${reaction}`); - return; - } - - while (storeEvent.Next()) { - const [ eventId, roomId ] = storeEvent.MatrixId.split(";"); - const underlyingClient = intent.underlyingClient; - - const { chunk } = await underlyingClient.unstableApis.getRelationsForEvent( - roomId, - eventId, - "m.annotation" - ); - - const event = chunk.find((event) => { - if (event.sender !== intent.userId) { - return false; - } - - return event.content["m.relates_to"].key === reaction.emoji.name; - }); - - if (!event) { - log.verbose(`Received remove reaction event for tracked message where the add reaction event was not bridged. Dropping! Reaction Event: ${reaction}`); - return; - } - - const { room_id, event_id } = event; - - try { - await underlyingClient.redactEvent(room_id, event_id); - } catch (ex) { - log.warn(`Failed to delete ${storeEvent.DiscordId}, retrying as bot`); - try { - await this.bridge.botIntent.underlyingClient.redactEvent(room_id, event_id); - } catch (ex) { - log.warn(`Failed to delete ${event_id}, giving up`); - } - } - } - } - - public async OnMessageReactionRemoveAll(message: Discord.Message | Discord.PartialMessage) { - log.verbose(`Got message reaction remove all event for ${message.id}`); - - const storeEvent = await this.store.Get(DbEvent, { - discord_id: message.id, - }); - - if (!storeEvent?.Result) { - log.verbose(`Received remove all reaction event for untracked message. Dropping! Event: ${message}`); - return; - } - - while (storeEvent.Next()) { - const [ eventId, roomId ] = storeEvent.MatrixId.split(";"); - const underlyingClient = this.bridge.botIntent.underlyingClient; - - const { chunk } = await underlyingClient.unstableApis.getRelationsForEvent( - roomId, - eventId, - "m.annotation" - ); - - const filteredChunk = chunk.filter((event) => event.sender === this.bridge.botUserId); - - await Promise.all(filteredChunk.map(async (event) => { - try { - return await underlyingClient.redactEvent(event.room_id, event.event_id); - } catch (ex) { - log.warn(`Failed to delete ${event.event_id}, giving up`); - } - })); - } - } - private async DeleteDiscordMessage(msg: Discord.Message) { log.info(`Got delete event for ${msg.id}`); const storeEvent = await this.store.Get(DbEvent, {discord_id: msg.id}); diff --git a/src/db/dbdataevent.ts b/src/db/dbdataevent.ts index 5d8fd9e545be5f176005b9830d35ad12433bc855..d3fc58f7c90154017884e63bc015b8f386d41174 100644 --- a/src/db/dbdataevent.ts +++ b/src/db/dbdataevent.ts @@ -19,9 +19,7 @@ import { IDbDataMany } from "./dbdatainterface"; import { ISqlCommandParameters } from "./connector"; export class DbEvent implements IDbDataMany { - /** ID associated with the event in the format "MatrixID:RoomID". */ public MatrixId: string; - /** Discord ID of the relevant message associated with this event. */ public DiscordId: string; public GuildId: string; public ChannelId: string; diff --git a/test/mocks/message.ts b/test/mocks/message.ts index 9393f421dd54c0f8009d189503a01bae889bd34c..bfd7ac554033dd70e5c3092856f9f579ee1c2eba 100644 --- a/test/mocks/message.ts +++ b/test/mocks/message.ts @@ -24,7 +24,7 @@ import { MockCollection } from "./collection"; export class MockMessage { public attachments = new MockCollection<string, any>(); public embeds: any[] = []; - public content: string; + public content = ""; public channel: Discord.TextChannel | undefined; public guild: Discord.Guild | undefined; public author: MockUser; diff --git a/test/mocks/reaction.ts b/test/mocks/reaction.ts deleted file mode 100644 index 7ca85555fa4f7322956ca2f15c2b1cc6d28a05fc..0000000000000000000000000000000000000000 --- a/test/mocks/reaction.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MockTextChannel } from './channel'; -import { MockEmoji } from './emoji'; -import { MockMessage } from './message'; - -/* tslint:disable:no-unused-expression max-file-line-count no-any */ -export class MockReaction { - public message: MockMessage; - public emoji: MockEmoji; - public channel: MockTextChannel; - - constructor(message: MockMessage, emoji: MockEmoji, channel: MockTextChannel) { - this.message = message; - this.emoji = emoji; - this.channel = channel; - } -} diff --git a/test/test_discordbot.ts b/test/test_discordbot.ts index 009157ded564bf3ec1e77a9d964fa8b27e7e495f..e5834d4ed64b58021a78a2efd39536d80b5fca90 100644 --- a/test/test_discordbot.ts +++ b/test/test_discordbot.ts @@ -26,8 +26,6 @@ import { Util } from "../src/util"; import { AppserviceMock } from "./mocks/appservicemock"; import { MockUser } from "./mocks/user"; import { MockTextChannel } from "./mocks/channel"; -import { MockReaction } from './mocks/reaction'; -import { MockEmoji } from './mocks/emoji'; // we are a test file and thus need those /* tslint:disable:no-unused-expression max-file-line-count no-any */ @@ -535,82 +533,4 @@ describe("DiscordBot", () => { expect(expected).to.eq(ITERATIONS); }); }); - describe("OnMessageReactionAdd", () => { - const channel = new MockTextChannel(); - const author = new MockUser("11111"); - const message = new MockMessage(channel, "Hello, World!", author); - const emoji = new MockEmoji("", "🤔"); - const reaction = new MockReaction(message, emoji, channel); - - function getDiscordBot() { - mockBridge.cleanup(); - const discord = new modDiscordBot.DiscordBot( - config, - mockBridge, - {}, - ); - discord.channelSync = { - GetRoomIdsFromChannel: async () => ["!asdf:localhost"], - }; - discord.store = { - Get: async () => { - let storeMockResults = 0; - - return { - Result: true, - MatrixId: "$mAKet_w5WYFCgh1WaHVOvyn9LJLbolFeuELTKVfm0Po;!asdf:localhost", - Next: () => storeMockResults++ === 0 - } - }, - Insert: async () => { }, - }; - discord.userActivity = { - updateUserActivity: () => { } - }; - discord.GetIntentFromDiscordMember = () => { - return mockBridge.getIntent(author.id); - } - return discord; - } - - it("Adds reaction from Discord → Matrix", async () => { - discordBot = getDiscordBot(); - await discordBot.OnMessageReactionAdd(reaction, author); - mockBridge.getIntent(author.id).underlyingClient.unstableApis.wasCalled( - "addReactionToEvent", - true, - "!asdf:localhost", - "$mAKet_w5WYFCgh1WaHVOvyn9LJLbolFeuELTKVfm0Po", - "🤔" - ); - }); - - it("Removes reaction from Discord → Matrix", async () => { - discordBot = getDiscordBot(); - const intent = mockBridge.getIntent(author.id); - - intent.underlyingClient.unstableApis.getRelationsForEvent = async () => { - return { - chunk: [ - { - sender: "11111", - room_id: "!asdf:localhost", - event_id: "$mAKet_w5WYFCgh1WaHVOvyn9LJLbolFeuELTKVfm0Po", - content: { - "m.relates_to": { key: "🤔" } - } - } - ] - } - } - - await discordBot.OnMessageReactionRemove(reaction, author); - intent.underlyingClient.wasCalled( - "redactEvent", - false, - "!asdf:localhost", - "$mAKet_w5WYFCgh1WaHVOvyn9LJLbolFeuELTKVfm0Po", - ); - }); - }); });