From 740ddc36d12a222d84f52b913f1f6cf5bd0f664d Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Thu, 29 Jun 2023 00:06:56 +1200 Subject: [PATCH] support d->m stickers --- d2m/actions/create-room.js | 33 +++++++++++++++++++------ d2m/actions/send-message.js | 2 +- d2m/converters/message-to-event.js | 31 +++++++++++++++++++++-- d2m/converters/message-to-event.test.js | 4 +-- d2m/event-dispatcher.js | 8 +++--- matrix/file.js | 3 ++- test/data.js | 4 +-- 7 files changed, 67 insertions(+), 18 deletions(-) diff --git a/d2m/actions/create-room.js b/d2m/actions/create-room.js index c4baa2d..479bb79 100644 --- a/d2m/actions/create-room.js +++ b/d2m/actions/create-room.js @@ -32,12 +32,13 @@ function applyKStateDiffToRoom(roomID, kstate) { } /** - * @param {import("discord-api-types/v10").APIGuildTextChannel} channel - * @param {import("discord-api-types/v10").APIGuild} guild + * @param {DiscordTypes.APIGuildTextChannel} channel + * @param {DiscordTypes.APIGuild} guild */ async function channelToKState(channel, guild) { const spaceID = db.prepare("SELECT space_id FROM guild_space WHERE guild_id = ?").pluck().get(guild.id) assert.ok(typeof spaceID === "string") + const customName = db.prepare("SELECT nick FROM channel_room WHERE channel_id = ?").pluck().get(channel.id) const avatarEventContent = {} if (guild.icon) { @@ -45,9 +46,27 @@ async function channelToKState(channel, guild) { avatarEventContent.url = await file.uploadDiscordFileToMxc(avatarEventContent.discord_path) // TODO: somehow represent future values in kstate (callbacks?), while still allowing for diffing, so test cases don't need to touch the media API } + // TODO: Improve nasty nested ifs + let convertedName, convertedTopic + if (customName) { + convertedName = customName + if (channel.topic) { + convertedTopic = `${channel.name} | ${channel.topic}\n\nChannel ID: ${channel.id}\nGuild ID: ${guild.id}` + } else { + convertedTopic = `${channel.name}\n\nChannel ID: ${channel.id}\nGuild ID: ${guild.id}` + } + } else { + convertedName = channel.name + if (channel.topic) { + convertedTopic = `${channel.topic}\n\nChannel ID: ${channel.id}\nGuild ID: ${guild.id}` + } else { + convertedTopic = `Channel ID: ${channel.id}\nGuild ID: ${guild.id}` + } + } + const channelKState = { - "m.room.name/": {name: channel.name}, - "m.room.topic/": {$if: channel.topic, topic: channel.topic}, + "m.room.name/": {name: convertedName}, + "m.room.topic/": {topic: convertedTopic}, "m.room.avatar/": avatarEventContent, "m.room.guest_access/": {guest_access: "can_join"}, "m.room.history_visibility/": {history_visibility: "invited"}, @@ -69,7 +88,7 @@ async function channelToKState(channel, guild) { /** * Create a bridge room, store the relationship in the database, and add it to the guild's space. - * @param {import("discord-api-types/v10").APIGuildTextChannel} channel + * @param {DiscordTypes.APIGuildTextChannel} channel * @param guild * @param {string} spaceID * @param {any} kstate @@ -96,7 +115,7 @@ async function createRoom(channel, guild, spaceID, kstate) { } /** - * @param {import("discord-api-types/v10").APIGuildChannel} channel + * @param {DiscordTypes.APIGuildChannel} channel */ function channelToGuild(channel) { const guildID = channel.guild_id @@ -129,7 +148,7 @@ function channelToGuild(channel) { * @returns {Promise} room ID */ async function _syncRoom(channelID, shouldActuallySync) { - /** @ts-ignore @type {import("discord-api-types/v10").APIGuildChannel} */ + /** @ts-ignore @type {DiscordTypes.APIGuildChannel} */ const channel = discord.channels.get(channelID) assert.ok(channel) const guild = channelToGuild(channel) diff --git a/d2m/actions/send-message.js b/d2m/actions/send-message.js index 897f7c0..24a825a 100644 --- a/d2m/actions/send-message.js +++ b/d2m/actions/send-message.js @@ -27,7 +27,7 @@ async function sendMessage(message, guild) { await registerUser.syncUser(message.author, message.member, message.guild_id, roomID) } - const events = await messageToEvent.messageToEvent(message) + const events = await messageToEvent.messageToEvent(message, guild) const eventIDs = [] let eventPart = 0 // 0 is primary, 1 is supporting for (const event of events) { diff --git a/d2m/converters/message-to-event.js b/d2m/converters/message-to-event.js index 86be8ac..549d104 100644 --- a/d2m/converters/message-to-event.js +++ b/d2m/converters/message-to-event.js @@ -1,16 +1,18 @@ // @ts-check +const assert = require("assert").strict const markdown = require("discord-markdown") const passthrough = require("../../passthrough") -const { sync, db } = passthrough +const { sync, db, discord } = passthrough /** @type {import("../../matrix/file")} */ const file = sync.require("../../matrix/file") /** * @param {import("discord-api-types/v10").APIMessage} message + * @param {import("discord-api-types/v10").APIGuild} guild */ -async function messageToEvent(message) { +async function messageToEvent(message, guild) { const events = [] // Text content appears first @@ -87,6 +89,31 @@ async function messageToEvent(message) { events.push(...attachmentEvents) // Then stickers + if (message.sticker_items) { + const stickerEvents = await Promise.all(message.sticker_items.map(async stickerItem => { + const format = file.stickerFormat.get(stickerItem.format_type) + if (format?.mime) { + let body = stickerItem.name + const sticker = guild.stickers.find(sticker => sticker.id === stickerItem.id) + if (sticker && sticker.description) body += ` - ${sticker.description}` + return { + $type: "m.sticker", + body, + info: { + mimetype: format.mime + }, + url: await file.uploadDiscordFileToMxc(file.sticker(stickerItem)) + } + } else { + return { + $type: "m.room.message", + msgtype: "m.text", + body: "Unsupported sticker format. Name: " + stickerItem.name + } + } + })) + events.push(...stickerEvents) + } return events } diff --git a/d2m/converters/message-to-event.test.js b/d2m/converters/message-to-event.test.js index 323dc78..c92cd85 100644 --- a/d2m/converters/message-to-event.test.js +++ b/d2m/converters/message-to-event.test.js @@ -4,7 +4,7 @@ const {messageToEvent} = require("./message-to-event") const data = require("../../test/data") test("message2event: stickers", async t => { - const events = await messageToEvent(data.message.sticker) + const events = await messageToEvent(data.message.sticker, data.guild.general) t.deepEqual(events, [{ $type: "m.room.message", msgtype: "m.text", @@ -29,6 +29,6 @@ test("message2event: stickers", async t => { // thumbnail_url // thumbnail_info }, - url: "mxc://" + url: "mxc://cadence.moe/UuUaLwXhkxFRwwWCXipDlBHn" }]) }) diff --git a/d2m/event-dispatcher.js b/d2m/event-dispatcher.js index baf19a8..fbcdca0 100644 --- a/d2m/event-dispatcher.js +++ b/d2m/event-dispatcher.js @@ -1,5 +1,4 @@ -// @ts-check - +const assert = require("assert").strict const {sync} = require("../passthrough") /** @type {import("./actions/send-message")}) */ @@ -15,8 +14,11 @@ module.exports = { * @param {import("discord-api-types/v10").GatewayMessageCreateDispatchData} message */ onMessageCreate(client, message) { + /** @ts-ignore @type {import("discord-api-types/v10").APIGuildChannel} */ + const channel = client.channels.get(message.channel_id) + const guild = client.guilds.get(channel.guild_id) if (message.guild_id !== "112760669178241024") return // TODO: activate on other servers (requires the space creation flow to be done first) - sendMessage.sendMessage(message) + sendMessage.sendMessage(message, guild) }, /** diff --git a/matrix/file.js b/matrix/file.js index a373676..64cd492 100644 --- a/matrix/file.js +++ b/matrix/file.js @@ -83,10 +83,11 @@ function emoji(emojiID, animated) { const stickerFormat = new Map([ [1, {label: "PNG", ext: "png", mime: "image/png"}], [2, {label: "APNG", ext: "png", mime: "image/apng"}], - [3, {label: "LOTTIE", ext: "json", mime: "application/json"}], + [3, {label: "LOTTIE", ext: "json", mime: null}], [4, {label: "GIF", ext: "gif", mime: "image/gif"}] ]) +/** @param {{id: string, format_type: number}} sticker */ function sticker(sticker) { const format = stickerFormat.get(sticker.format_type) if (!format) throw new Error(`No such format ${sticker.format_type} for sticker ${JSON.stringify(sticker)}`) diff --git a/test/data.js b/test/data.js index 6a65b5f..bb70cbe 100644 --- a/test/data.js +++ b/test/data.js @@ -22,8 +22,8 @@ module.exports = { }, room: { general: { - "m.room.name/": {name: "collective-unconscious"}, - "m.room.topic/": {topic: "https://docs.google.com/document/d/blah/edit | I spread, pipe, and whip because it is my will. :headstone:"}, + "m.room.name/": {name: "main"}, + "m.room.topic/": {topic: "collective-unconscious | https://docs.google.com/document/d/blah/edit | I spread, pipe, and whip because it is my will. :headstone:\n\nChannel ID: 112760669178241024\nGuild ID: 112760669178241024"}, "m.room.guest_access/": {guest_access: "can_join"}, "m.room.history_visibility/": {history_visibility: "invited"}, "m.space.parent/!jjWAGMeQdNrVZSSfvz:cadence.moe": {