From 5b15f710d7c811c7c1f4f684234b85f87bab10bd 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 +++--- db/ooye.db | Bin 106496 -> 106496 bytes matrix/file.js | 3 ++- test/data.js | 4 +-- 8 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/db/ooye.db b/db/ooye.db index 7d323c81fa70762b9aba35f54f9fda885d6c6bac..5168a0f57405b2bfa3d220e31089a5c75bca3e6c 100644 GIT binary patch delta 1757 zcmb7^%}?8Q9L8}7F#(d0c0xj+X-sTllfwM+b8Ks`iOD zJ8S}3RaI5ca+x-1(u%Tr>)YoKVXMmHfhGN!?aSt$*1S@{rdMj&v*No zY5SRJcMh7_n`g#mMwY8T9_9CKlZNVu1G+h4?HL}b6VpqtK|k9b!6)#&@g>9emZ#9o zu>kai`pNX%ke_CtO=PqHEw0r$xsvX;Hd9K3ZI>BwMcnc^gUVJEaI0Xg9jv=EfoM!n zgi=NqxRv#4Fdp|uwx|q3`zj)%5TfjGb8FIimI=gAr0WezF1eTxQ|kDQ0)(o_*Vu*w z^h?EXcq{3a`~|t{$~#<12kY{+y=EipFpn2h4^Jk@Be) zFZ#TW0`KyslPu~8)ciHkFLZMqvg%B9+M=VIDN3Xu093uN1a~2B=51cXBGOL8K7)mjY-SLjZvg6vC|Qlxui#4#5D0 zq1rau;~*51)giC+XYgNxbP7omDDK2)gdkBAFd3b4jrd(av_ByU3?l%7;7nSBm=_CT zt5@yv7-;%9&7U}#8bpwqD;vv|GNpEeQ2IzIPy0*$iaT~e!EuTpfD@seG+yrs z)YJaV>mH%iOG~kGijOzCY*rLjwNI;8*_oa;?!?Xz>R)$fhaYuL#z*aDS%tS33}$&~ zM|@`E$7e$pKAV5z6+@P#M#(-r`ZQshvQIr9v3V?Zi#YXsXx;s3O11CL%wD#DeQ4kR z)9^N8Ivuh7U^{?MY<1fQwn^(R)~~ExtIs-P`5k^?I<-+a6{4I3ux=HFo4 z{KPykhvED%`dBdy^b2#1<8Vh8T;mXI=z_~SKX^&$Z|jPy7F_H1EnV>c>|YxFv@SS{ z`Bihy>WXWmF*#iN^4C8Mlg;CaUFgF}XnF7OUDNwZMl>|53NKJoUKHzv=J7WVA1OjRRVv}L4GLziVF_R4vU;&epFsw3@pwcV^1783!lhD$SvtJXX3g!U(heC4tNen4i*jN4YUX-lPaJj3JMVk;R&z_e+f>rUZ8UZv9M(UvnZ(G=K>CX ow+fH}JqrPAk)da|ubTm30+&dR0XUaPo&h$KX7@O^ww?h>0h8NdzyJUM 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": {