121 lines
4.6 KiB
JavaScript
121 lines
4.6 KiB
JavaScript
// @ts-check
|
|
|
|
const Ty = require("../../types")
|
|
const DiscordTypes = require("discord-api-types/v10")
|
|
const {Readable} = require("stream")
|
|
const assert = require("assert").strict
|
|
const crypto = require("crypto")
|
|
const fetch = require("node-fetch").default
|
|
const passthrough = require("../../passthrough")
|
|
const {sync, discord, db, select} = passthrough
|
|
|
|
/** @type {import("./channel-webhook")} */
|
|
const channelWebhook = sync.require("./channel-webhook")
|
|
/** @type {import("../converters/event-to-message")} */
|
|
const eventToMessage = sync.require("../converters/event-to-message")
|
|
/** @type {import("../../matrix/api")}) */
|
|
const api = sync.require("../../matrix/api")
|
|
/** @type {import("../../d2m/actions/register-user")} */
|
|
const registerUser = sync.require("../../d2m/actions/register-user")
|
|
|
|
/**
|
|
* @param {DiscordTypes.RESTPostAPIWebhookWithTokenJSONBody & {files?: {name: string, file: Buffer | Readable}[], pendingFiles?: ({name: string, url: string} | {name: string, url: string, key: string, iv: string} | {name: string, buffer: Buffer | Readable})[]}} message
|
|
* @returns {Promise<DiscordTypes.RESTPostAPIWebhookWithTokenJSONBody & {files?: {name: string, file: Buffer | Readable}[]}>}
|
|
*/
|
|
async function resolvePendingFiles(message) {
|
|
if (!message.pendingFiles) return message
|
|
const files = await Promise.all(message.pendingFiles.map(async p => {
|
|
if ("buffer" in p) {
|
|
return {
|
|
name: p.name,
|
|
file: p.buffer
|
|
}
|
|
}
|
|
if ("key" in p) {
|
|
// Encrypted file
|
|
const d = crypto.createDecipheriv("aes-256-ctr", Buffer.from(p.key, "base64url"), Buffer.from(p.iv, "base64url"))
|
|
// @ts-ignore
|
|
fetch(p.url).then(res => res.body.pipe(d))
|
|
return {
|
|
name: p.name,
|
|
file: d
|
|
}
|
|
} else {
|
|
// Unencrypted file
|
|
/** @type {Readable} */ // @ts-ignore
|
|
const body = await fetch(p.url).then(res => res.body)
|
|
return {
|
|
name: p.name,
|
|
file: body
|
|
}
|
|
}
|
|
}))
|
|
const newMessage = {
|
|
...message,
|
|
files: files.concat(message.files || [])
|
|
}
|
|
delete newMessage.pendingFiles
|
|
return newMessage
|
|
}
|
|
|
|
/** @param {Ty.Event.Outer_M_Room_Message | Ty.Event.Outer_M_Room_Message_File | Ty.Event.Outer_M_Sticker} event */
|
|
async function sendEvent(event) {
|
|
const row = select("channel_room", ["channel_id", "thread_parent"], {room_id: event.room_id}).get()
|
|
if (!row) return // allow the bot to exist in unbridged rooms, just don't do anything with it
|
|
let channelID = row.channel_id
|
|
let threadID = undefined
|
|
if (row.thread_parent) {
|
|
threadID = channelID
|
|
channelID = row.thread_parent // it's the thread's parent... get with the times...
|
|
}
|
|
// @ts-ignore
|
|
const guildID = discord.channels.get(channelID).guild_id
|
|
const guild = discord.guilds.get(guildID)
|
|
assert(guild)
|
|
|
|
// no need to sync the matrix member to the other side. but if I did need to, this is where I'd do it
|
|
|
|
let {messagesToEdit, messagesToSend, messagesToDelete, ensureJoined} = await eventToMessage.eventToMessage(event, guild, {api, snow: discord.snow})
|
|
|
|
messagesToEdit = await Promise.all(messagesToEdit.map(async e => {
|
|
e.message = await resolvePendingFiles(e.message)
|
|
return e
|
|
}))
|
|
messagesToSend = await Promise.all(messagesToSend.map(message => {
|
|
return resolvePendingFiles(message)
|
|
}))
|
|
|
|
let eventPart = 0 // 0 is primary, 1 is supporting
|
|
|
|
/** @type {DiscordTypes.APIMessage[]} */
|
|
const messageResponses = []
|
|
for (const data of messagesToEdit) {
|
|
const messageResponse = await channelWebhook.editMessageWithWebhook(channelID, data.id, data.message, threadID)
|
|
eventPart = 1
|
|
messageResponses.push(messageResponse)
|
|
}
|
|
|
|
for (const id of messagesToDelete) {
|
|
db.prepare("DELETE FROM message_channel WHERE message_id = ?").run(id)
|
|
db.prepare("DELETE FROM event_message WHERE message_id = ?").run(id)
|
|
await channelWebhook.deleteMessageWithWebhook(channelID, id, threadID)
|
|
}
|
|
|
|
for (const message of messagesToSend) {
|
|
const reactionPart = messagesToEdit.length === 0 && message === messagesToSend[messagesToSend.length - 1] ? 0 : 1
|
|
const messageResponse = await channelWebhook.sendMessageWithWebhook(channelID, message, threadID)
|
|
db.prepare("REPLACE INTO message_channel (message_id, channel_id) VALUES (?, ?)").run(messageResponse.id, threadID || channelID)
|
|
db.prepare("INSERT INTO event_message (event_id, event_type, event_subtype, message_id, part, reaction_part, source) VALUES (?, ?, ?, ?, ?, ?, 0)").run(event.event_id, event.type, event.content["msgtype"] || null, messageResponse.id, eventPart, reactionPart) // source 0 = matrix
|
|
|
|
eventPart = 1
|
|
messageResponses.push(messageResponse)
|
|
}
|
|
|
|
for (const user of ensureJoined) {
|
|
registerUser.ensureSimJoined(user, event.room_id)
|
|
}
|
|
|
|
return messageResponses
|
|
}
|
|
|
|
module.exports.sendEvent = sendEvent
|