begin converting discord attachments and stickers

This commit is contained in:
Cadence Ember 2023-05-12 17:35:37 +12:00
parent 38d7db5071
commit e1d7ced87d
6 changed files with 194 additions and 33 deletions

View file

@ -19,16 +19,31 @@ const createRoom = sync.require("../actions/create-room")
async function sendMessage(message) { async function sendMessage(message) {
assert.ok(message.member) assert.ok(message.member)
const event = messageToEvent.messageToEvent(message)
const roomID = await createRoom.ensureRoom(message.channel_id) const roomID = await createRoom.ensureRoom(message.channel_id)
let senderMxid = null let senderMxid = null
if (!message.webhook_id) { if (!message.webhook_id) {
senderMxid = await registerUser.ensureSimJoined(message.author, roomID) senderMxid = await registerUser.ensureSimJoined(message.author, roomID)
await registerUser.syncUser(message.author, message.member, message.guild_id, roomID) await registerUser.syncUser(message.author, message.member, message.guild_id, roomID)
} }
const eventID = await api.sendEvent(roomID, "m.room.message", event, senderMxid)
db.prepare("INSERT INTO event_message (event_id, message_id, part) VALUES (?, ?, ?)").run(eventID, message.id, 0) // 0 is primary, 1 is supporting const events = await messageToEvent.messageToEvent(message)
return eventID const eventIDs = []
let eventPart = 0 // 0 is primary, 1 is supporting
for (const event of events) {
const eventType = event.$type
/** @type {Pick<typeof event, Exclude<keyof event, "$type">> & { $type?: string }} */
const eventWithoutType = {...event}
delete eventWithoutType.$type
const eventID = await api.sendEvent(roomID, eventType, event, senderMxid)
db.prepare("INSERT INTO event_message (event_id, message_id, part) VALUES (?, ?, ?)").run(eventID, message.id, eventPart)
eventPart = 1 // TODO: use more intelligent algorithm to determine whether primary or supporting
eventIDs.push(eventID)
}
return eventIDs
} }
module.exports.sendMessage = sendMessage module.exports.sendMessage = sendMessage

View file

@ -2,27 +2,93 @@
const markdown = require("discord-markdown") const markdown = require("discord-markdown")
const passthrough = require("../../passthrough")
const { sync, db } = 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").APIMessage} message
* @returns {import("../../types").Event.M_Room_Message}
*/ */
function messageToEvent(message) { async function messageToEvent(message) {
const events = []
// Text content appears first
const body = message.content const body = message.content
const html = markdown.toHTML(body, { const html = markdown.toHTML(body, {
/* discordCallback: { discordCallback: {
user: Function, user: node => {
channel: Function, const mxid = db.prepare("SELECT mxid FROM sim WHERE discord_id = ?").pluck().get(node.id)
role: Function, if (mxid) {
everyone: Function, return "https://matrix.to/#/" + mxid
here: Function } else {
} */ return "@" + node.id
}
},
channel: node => {
const roomID = db.prepare("SELECT room_id FROM channel_room WHERE channel_id = ?").pluck().get(node.id)
if (roomID) {
return "https://matrix.to/#/" + roomID
} else {
return "#" + node.id
}
},
role: node =>
"@&" + node.id,
everyone: node =>
"@room",
here: node =>
"@here"
}
}, null, null) }, null, null)
return { const isPlaintext = body === html
msgtype: "m.text", if (isPlaintext) {
body: body, events.push({
format: "org.matrix.custom.html", $type: "m.room.message",
formatted_body: html msgtype: "m.text",
body: body
})
} else {
events.push({
$type: "m.room.message",
msgtype: "m.text",
body: body,
format: "org.matrix.custom.html",
formatted_body: html
})
} }
// Then attachments
const attachmentEvents = await Promise.all(message.attachments.map(async attachment => {
// TODO: handle large files differently - link them instead of uploading
if (attachment.content_type?.startsWith("image/") && attachment.width && attachment.height) {
return {
$type: "m.room.message",
msgtype: "m.image",
url: await file.uploadDiscordFileToMxc(attachment.url),
external_url: attachment.url,
body: attachment.filename,
// TODO: filename: attachment.filename and then use body as the caption
info: {
mimetype: attachment.content_type,
w: attachment.width,
h: attachment.height,
size: attachment.size
}
}
} else {
return {
$type: "m.room.message",
msgtype: "m.text",
body: "Unsupported attachment:\n" + JSON.stringify(attachment, null, 2)
}
}
}))
events.push(...attachmentEvents)
// Then stickers
return events
} }
module.exports.messageToEvent = messageToEvent module.exports.messageToEvent = messageToEvent

View file

@ -0,0 +1,28 @@
const {test} = require("supertape")
const assert = require("assert")
const {messageToEvent} = require("./message-to-event")
const data = require("../../test/data")
test("message2event: stickers", async t => {
const events = await messageToEvent(data.message.sticker)
t.deepEqual(events, [{
$type: "m.room.message",
msgtype: "m.text",
body: "can have attachments too"
}, {
$type: "m.room.message",
msgtype: "m.image",
url: "mxc://cadence.moe/ZDCNYnkPszxGKgObUIFmvjus",
body: "image.png",
external_url: "https://cdn.discordapp.com/attachments/122155380120748034/1106366167486038016/image.png",
info: {
mimetype: "image/png",
w: 333,
h: 287,
size: 127373,
},
}, {
$type: "m.sticker",
todo: "todo"
}])
})

View file

@ -74,7 +74,14 @@ function memberAvatar(guildID, user, member) {
return `/guilds/${guildID}/users/${user.id}/avatars/${member.avatar}.png?size=${IMAGE_SIZE}` return `/guilds/${guildID}/users/${user.id}/avatars/${member.avatar}.png?size=${IMAGE_SIZE}`
} }
function emoji(emojiID, animated) {
const base = `/emojis/${emojiID}`
if (animated) return base + ".gif"
else return base + ".png"
}
module.exports.guildIcon = guildIcon module.exports.guildIcon = guildIcon
module.exports.userAvatar = userAvatar module.exports.userAvatar = userAvatar
module.exports.memberAvatar = memberAvatar module.exports.memberAvatar = memberAvatar
module.exports.emoji = emoji
module.exports.uploadDiscordFileToMxc = uploadDiscordFileToMxc module.exports.uploadDiscordFileToMxc = uploadDiscordFileToMxc

View file

@ -6,18 +6,18 @@ module.exports = {
channel: { channel: {
general: { general: {
type: 0, type: 0,
topic: 'https://docs.google.com/document/d/blah/edit | I spread, pipe, and whip because it is my will. :headstone:', topic: "https://docs.google.com/document/d/blah/edit | I spread, pipe, and whip because it is my will. :headstone:",
rate_limit_per_user: 0, rate_limit_per_user: 0,
position: 0, position: 0,
permission_overwrites: [], permission_overwrites: [],
parent_id: null, parent_id: null,
nsfw: false, nsfw: false,
name: 'collective-unconscious' , name: "collective-unconscious" ,
last_pin_timestamp: '2023-04-06T09:51:57+00:00', last_pin_timestamp: "2023-04-06T09:51:57+00:00",
last_message_id: '1103832925784514580', last_message_id: "1103832925784514580",
id: '112760669178241024', id: "112760669178241024",
default_thread_rate_limit_per_user: 0, default_thread_rate_limit_per_user: 0,
guild_id: '112760669178241024' guild_id: "112760669178241024"
} }
}, },
room: { room: {
@ -45,41 +45,85 @@ module.exports = {
}, },
guild: { guild: {
general: { general: {
owner_id: '112760500130975744', owner_id: "112760500130975744",
premium_tier: 3, premium_tier: 3,
stickers: [], stickers: [],
max_members: 500000, max_members: 500000,
splash: '86a34ed02524b972918bef810087f8e7', splash: "86a34ed02524b972918bef810087f8e7",
explicit_content_filter: 0, explicit_content_filter: 0,
afk_channel_id: null, afk_channel_id: null,
nsfw_level: 0, nsfw_level: 0,
description: null, description: null,
preferred_locale: 'en-US', preferred_locale: "en-US",
system_channel_id: '112760669178241024', system_channel_id: "112760669178241024",
mfa_level: 0, mfa_level: 0,
/** @type {300} */ /** @type {300} */
afk_timeout: 300, afk_timeout: 300,
id: '112760669178241024', id: "112760669178241024",
icon: 'a_f83622e09ead74f0c5c527fe241f8f8c', icon: "a_f83622e09ead74f0c5c527fe241f8f8c",
emojis: [], emojis: [],
premium_subscription_count: 14, premium_subscription_count: 14,
roles: [], roles: [],
discovery_splash: null, discovery_splash: null,
default_message_notifications: 1, default_message_notifications: 1,
region: 'deprecated', region: "deprecated",
max_video_channel_users: 25, max_video_channel_users: 25,
verification_level: 0, verification_level: 0,
application_id: null, application_id: null,
premium_progress_bar_enabled: false, premium_progress_bar_enabled: false,
banner: 'a_a666ae551605a2d8cda0afd591c0af3a', banner: "a_a666ae551605a2d8cda0afd591c0af3a",
features: [], features: [],
vanity_url_code: null, vanity_url_code: null,
hub_type: null, hub_type: null,
public_updates_channel_id: null, public_updates_channel_id: null,
rules_channel_id: null, rules_channel_id: null,
name: 'Psychonauts 3', name: "Psychonauts 3",
max_stage_video_channel_users: 300, max_stage_video_channel_users: 300,
system_channel_flags: 0|0 system_channel_flags: 0|0
} }
},
message: {
// Display order is text content, attachments, then stickers
sticker: {
id: "1106366167788044450",
type: 0,
content: "can have attachments too",
channel_id: "122155380120748034",
author: {
id: "113340068197859328",
username: "Cookie 🍪",
global_name: null,
display_name: null,
avatar: "b48302623a12bc7c59a71328f72ccb39",
discriminator: "7766",
public_flags: 128,
avatar_decoration: null
},
attachments: [{
id: "1106366167486038016",
filename: "image.png",
size: 127373,
url: "https://cdn.discordapp.com/attachments/122155380120748034/1106366167486038016/image.png",
proxy_url: "https://media.discordapp.net/attachments/122155380120748034/1106366167486038016/image.png",
width: 333,
height: 287,
content_type: "image/png"
}],
embeds: [],
mentions: [],
mention_roles: [],
pinned: false,
mention_everyone: false,
tts: false,
timestamp: "2023-05-11T23:44:09.690000+00:00",
edited_timestamp: null,
flags: 0,
components: [],
sticker_items: [{
id: "1106323941183717586",
format_type: 1,
name: "pomu puff"
}]
}
} }
} }

View file

@ -14,5 +14,6 @@ Object.assign(passthrough, { config, sync, db })
require("../matrix/kstate.test") require("../matrix/kstate.test")
require("../matrix/api.test") require("../matrix/api.test")
require("../matrix/read-registration.test") require("../matrix/read-registration.test")
require("../d2m/converters/message-to-event.test")
require("../d2m/actions/create-room.test") require("../d2m/actions/create-room.test")
require("../d2m/converters/user-to-mxid.test") require("../d2m/converters/user-to-mxid.test")