Split part and reaction_part

Now, reactions should always end up on the bottom of a message group,
instead of sometimes being in the middle.
This commit is contained in:
Cadence Ember 2023-10-14 22:08:10 +13:00
parent b7f90db20a
commit c24752625d
13 changed files with 121 additions and 71 deletions

View file

@ -21,7 +21,7 @@ async function addReaction(data) {
const user = data.member?.user
assert.ok(user && user.username)
const parentID = select("event_message", "event_id", {message_id: data.message_id, part: 0}).pluck().get() // 0 = primary
const parentID = select("event_message", "event_id", {message_id: data.message_id, reaction_part: 0}).pluck().get()
if (!parentID) return // Nothing can be done if the parent message was never bridged.
assert.equal(typeof parentID, "string")

View file

@ -12,7 +12,7 @@ const api = sync.require("../../matrix/api")
* @param {import("discord-api-types/v10").APIGuild} guild
*/
async function editMessage(message, guild) {
const {roomID, eventsToRedact, eventsToReplace, eventsToSend, senderMxid, promoteEvent, promoteNextEvent} = await editToChanges.editToChanges(message, guild, api)
const {roomID, eventsToRedact, eventsToReplace, eventsToSend, senderMxid, promotions} = await editToChanges.editToChanges(message, guild, api)
// 1. Replace all the things.
for (const {oldID, newContent} of eventsToReplace) {
@ -36,24 +36,30 @@ async function editMessage(message, guild) {
}
// 3. Consistency: Ensure there is exactly one part = 0
let eventPart = 1
if (promoteEvent) {
db.prepare("UPDATE event_message SET part = 0 WHERE event_id = ?").run(promoteEvent)
} else if (promoteNextEvent) {
eventPart = 0
const sendNewEventParts = new Set()
for (const promotion of promotions) {
if ("eventID" in promotion) {
db.prepare(`UPDATE event_message SET ${promotion.column} = 0 WHERE event_id = ?`).run(promotion.eventID)
} else if ("nextEvent" in promotion) {
sendNewEventParts.add(promotion.column)
}
}
// 4. Send all the things.
if (eventsToSend.length) {
db.prepare("REPLACE INTO message_channel (message_id, channel_id) VALUES (?, ?)").run(message.id, message.channel_id)
}
for (const content of eventsToSend) {
const eventType = content.$type
/** @type {Pick<typeof content, Exclude<keyof content, "$type">> & { $type?: string }} */
const contentWithoutType = {...content}
delete contentWithoutType.$type
delete contentWithoutType.$sender
const part = sendNewEventParts.has("part") && eventsToSend[0] === content ? 0 : 1
const reactionPart = sendNewEventParts.has("reaction_part") && eventsToSend[eventsToSend.length - 1] === content ? 0 : 1
const eventID = await api.sendEvent(roomID, eventType, contentWithoutType, senderMxid)
db.prepare("INSERT INTO event_message (event_id, event_type, event_subtype, message_id, part, source) VALUES (?, ?, ?, ?, ?, 1)").run(eventID, eventType, content.msgtype || null, message.id, eventPart) // part 1 = supporting; source 1 = discord
eventPart = 1
db.prepare("INSERT INTO event_message (event_id, event_type, event_subtype, message_id, part, reaction_part, source) VALUES (?, ?, ?, ?, ?, ?, 1)").run(eventID, eventType, content.msgtype || null, message.id, part, reactionPart) // source 1 = discord
}
}

View file

@ -20,7 +20,7 @@ const converter = sync.require("../converters/remove-reaction")
async function removeSomeReactions(data) {
const roomID = select("channel_room", "room_id", {channel_id: data.channel_id}).pluck().get()
if (!roomID) return
const eventIDForMessage = select("event_message", "event_id", {message_id: data.message_id, part: 0}).pluck().get()
const eventIDForMessage = select("event_message", "event_id", {message_id: data.message_id, reaction_part: 0}).pluck().get()
if (!eventIDForMessage) return
/** @type {Ty.Pagination<Ty.Event.Outer<Ty.Event.M_Reaction>>} */

View file

@ -33,12 +33,14 @@ async function sendMessage(message, guild) {
const events = await messageToEvent.messageToEvent(message, guild, {}, {api})
const eventIDs = []
let eventPart = 0 // 0 is primary, 1 is supporting
if (events.length) {
db.prepare("REPLACE INTO message_channel (message_id, channel_id) VALUES (?, ?)").run(message.id, message.channel_id)
if (senderMxid) api.sendTyping(roomID, false, senderMxid)
}
for (const event of events) {
const part = event === events[0] ? 0 : 1
const reactionPart = event === events[events.length - 1] ? 0 : 1
const eventType = event.$type
if ("$sender" in event) senderMxid = event.$sender
/** @type {Pick<typeof event, Exclude<keyof event, "$type" | "$sender">> & { $type?: string, $sender?: string }} */
@ -48,12 +50,14 @@ async function sendMessage(message, guild) {
const useTimestamp = message["backfill"] ? new Date(message.timestamp).getTime() : undefined
const eventID = await api.sendEvent(roomID, eventType, eventWithoutType, senderMxid, useTimestamp)
db.prepare("INSERT INTO event_message (event_id, event_type, event_subtype, message_id, part, source) VALUES (?, ?, ?, ?, ?, 1)").run(eventID, eventType, event.msgtype || null, message.id, eventPart) // source 1 = discord
db.prepare("INSERT INTO event_message (event_id, event_type, event_subtype, message_id, part, reaction_part, source) VALUES (?, ?, ?, ?, ?, ?, 1)").run(eventID, eventType, event.msgtype || null, message.id, part, reactionPart) // source 1 = discord
// The primary event is part = 0 and has the most important and distinct information. It is used to provide reply previews, be pinned, and possibly future uses.
// The first event is chosen to be the primary part because it is usually the message text content and is more likely to be distinct.
// For example, "Reply to 'this meme made me think of you'" is more useful than "Replied to image".
eventPart = 1
// The last event gets reaction_part = 0. Reactions are managed there because reactions are supposed to appear at the bottom.
eventIDs.push(eventID)
}