d->m: Make PK members appear real
This commit is contained in:
		
							parent
							
								
									988cb9408d
								
							
						
					
					
						commit
						11864f80cf
					
				
					 9 changed files with 215 additions and 23 deletions
				
			
		| 
						 | 
					@ -1,18 +1,32 @@
 | 
				
			||||||
// @ts-check
 | 
					// @ts-check
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const assert = require("assert").strict
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const passthrough = require("../../passthrough")
 | 
					const passthrough = require("../../passthrough")
 | 
				
			||||||
const {sync, db, select} = passthrough
 | 
					const {sync, db, select} = passthrough
 | 
				
			||||||
/** @type {import("../converters/edit-to-changes")} */
 | 
					/** @type {import("../converters/edit-to-changes")} */
 | 
				
			||||||
const editToChanges = sync.require("../converters/edit-to-changes")
 | 
					const editToChanges = sync.require("../converters/edit-to-changes")
 | 
				
			||||||
 | 
					/** @type {import("./register-pk-user")} */
 | 
				
			||||||
 | 
					const registerPkUser = sync.require("./register-pk-user")
 | 
				
			||||||
/** @type {import("../../matrix/api")} */
 | 
					/** @type {import("../../matrix/api")} */
 | 
				
			||||||
const api = sync.require("../../matrix/api")
 | 
					const api = sync.require("../../matrix/api")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @param {import("discord-api-types/v10").GatewayMessageCreateDispatchData} message
 | 
					 * @param {import("discord-api-types/v10").GatewayMessageCreateDispatchData} message
 | 
				
			||||||
 * @param {import("discord-api-types/v10").APIGuild} guild
 | 
					 * @param {import("discord-api-types/v10").APIGuild} guild
 | 
				
			||||||
 | 
					 * @param {{speedbump_id: string, speedbump_webhook_id: string} | null} row data about the webhook which is proxying messages in this channel
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
async function editMessage(message, guild) {
 | 
					async function editMessage(message, guild, row) {
 | 
				
			||||||
	const {roomID, eventsToRedact, eventsToReplace, eventsToSend, senderMxid, promotions} = await editToChanges.editToChanges(message, guild, api)
 | 
						let {roomID, eventsToRedact, eventsToReplace, eventsToSend, senderMxid, promotions} = await editToChanges.editToChanges(message, guild, api)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (row && row.speedbump_webhook_id === message.webhook_id) {
 | 
				
			||||||
 | 
							// Handle the PluralKit public instance
 | 
				
			||||||
 | 
							if (row.speedbump_id === "466378653216014359") {
 | 
				
			||||||
 | 
								const root = await registerPkUser.fetchMessage(message.id)
 | 
				
			||||||
 | 
								assert(root.member)
 | 
				
			||||||
 | 
								senderMxid = await registerPkUser.ensureSimJoined(root.member, roomID)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 1. Replace all the things.
 | 
						// 1. Replace all the things.
 | 
				
			||||||
	for (const {oldID, newContent} of eventsToReplace) {
 | 
						for (const {oldID, newContent} of eventsToReplace) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										139
									
								
								d2m/actions/register-pk-user.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								d2m/actions/register-pk-user.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,139 @@
 | 
				
			||||||
 | 
					// @ts-check
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const assert = require("assert")
 | 
				
			||||||
 | 
					const reg = require("../../matrix/read-registration")
 | 
				
			||||||
 | 
					const Ty = require("../../types")
 | 
				
			||||||
 | 
					const fetch = require("node-fetch").default
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const passthrough = require("../../passthrough")
 | 
				
			||||||
 | 
					const {discord, sync, db, select} = passthrough
 | 
				
			||||||
 | 
					/** @type {import("../../matrix/api")} */
 | 
				
			||||||
 | 
					const api = sync.require("../../matrix/api")
 | 
				
			||||||
 | 
					/** @type {import("../../matrix/file")} */
 | 
				
			||||||
 | 
					const file = sync.require("../../matrix/file")
 | 
				
			||||||
 | 
					/** @type {import("./register-user")} */
 | 
				
			||||||
 | 
					const registerUser = sync.require("./register-user")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * A sim is an account that is being simulated by the bridge to copy events from the other side.
 | 
				
			||||||
 | 
					 * @param {Ty.PkMember} member
 | 
				
			||||||
 | 
					 * @returns mxid
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					async function createSim(member) {
 | 
				
			||||||
 | 
						// Choose sim name
 | 
				
			||||||
 | 
						const simName = "_pk_" + member.id
 | 
				
			||||||
 | 
						const localpart = reg.ooye.namespace_prefix + simName
 | 
				
			||||||
 | 
						const mxid = `@${localpart}:${reg.ooye.server_name}`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Save chosen name in the database forever
 | 
				
			||||||
 | 
						db.prepare("INSERT INTO sim (user_id, sim_name, localpart, mxid) VALUES (?, ?, ?, ?)").run(member.uuid, simName, localpart, mxid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Register matrix user with that name
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
 | 
							await api.register(localpart)
 | 
				
			||||||
 | 
						} catch (e) {
 | 
				
			||||||
 | 
							// If user creation fails, manually undo the database change. Still isn't perfect, but should help.
 | 
				
			||||||
 | 
							// (I would prefer a transaction, but it's not safe to leave transactions open across event loop ticks.)
 | 
				
			||||||
 | 
							db.prepare("DELETE FROM sim WHERE user_id = ?").run(member.uuid)
 | 
				
			||||||
 | 
							throw e
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return mxid
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Ensure a sim is registered for the user.
 | 
				
			||||||
 | 
					 * If there is already a sim, use that one. If there isn't one yet, register a new sim.
 | 
				
			||||||
 | 
					 * @param {Ty.PkMember} member
 | 
				
			||||||
 | 
					 * @returns {Promise<string>} mxid
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					async function ensureSim(member) {
 | 
				
			||||||
 | 
						let mxid = null
 | 
				
			||||||
 | 
						const existing = select("sim", "mxid", {user_id: member.uuid}).pluck().get()
 | 
				
			||||||
 | 
						if (existing) {
 | 
				
			||||||
 | 
							mxid = existing
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							mxid = await createSim(member)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return mxid
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Ensure a sim is registered for the user and is joined to the room.
 | 
				
			||||||
 | 
					 * @param {Ty.PkMember} member
 | 
				
			||||||
 | 
					 * @param {string} roomID
 | 
				
			||||||
 | 
					 * @returns {Promise<string>} mxid
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					async function ensureSimJoined(member, roomID) {
 | 
				
			||||||
 | 
						// Ensure room ID is really an ID, not an alias
 | 
				
			||||||
 | 
						assert.ok(roomID[0] === "!")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Ensure user
 | 
				
			||||||
 | 
						const mxid = await ensureSim(member)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Ensure joined
 | 
				
			||||||
 | 
						const existing = select("sim_member", "mxid", {room_id: roomID, mxid}).pluck().get()
 | 
				
			||||||
 | 
						if (!existing) {
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								await api.inviteToRoom(roomID, mxid)
 | 
				
			||||||
 | 
								await api.joinRoom(roomID, mxid)
 | 
				
			||||||
 | 
							} catch (e) {
 | 
				
			||||||
 | 
								if (e.message.includes("is already in the room.")) {
 | 
				
			||||||
 | 
									// Sweet!
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									throw e
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							db.prepare("INSERT OR IGNORE INTO sim_member (room_id, mxid) VALUES (?, ?)").run(roomID, mxid)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return mxid
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @param {Ty.PkMember} member
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					async function memberToStateContent(member) {
 | 
				
			||||||
 | 
						const displayname = member.display_name || member.name
 | 
				
			||||||
 | 
						const avatar = member.avatar_url || member.webhook_avatar_url
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const content = {
 | 
				
			||||||
 | 
							displayname,
 | 
				
			||||||
 | 
							membership: "join",
 | 
				
			||||||
 | 
							"moe.cadence.ooye.pk_member": member
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (avatar) content.avatar_url = await file.uploadDiscordFileToMxc(avatar)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return content
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Sync profile data for a sim user. This function follows the following process:
 | 
				
			||||||
 | 
					 * 1. Join the sim to the room if needed
 | 
				
			||||||
 | 
					 * 2. Make an object of what the new room member state content would be, including uploading the profile picture if it hasn't been done before
 | 
				
			||||||
 | 
					 * 3. Compare against the previously known state content, which is helpfully stored in the database
 | 
				
			||||||
 | 
					 * 4. If the state content has changed, send it to Matrix and update it in the database for next time
 | 
				
			||||||
 | 
					 * @param {Ty.PkMember} member
 | 
				
			||||||
 | 
					 * @returns {Promise<string>} mxid of the updated sim
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					async function syncUser(member, roomID) {
 | 
				
			||||||
 | 
						const mxid = await ensureSimJoined(member, roomID)
 | 
				
			||||||
 | 
						const content = await memberToStateContent(member)
 | 
				
			||||||
 | 
						const currentHash = registerUser._hashProfileContent(content)
 | 
				
			||||||
 | 
						const existingHash = select("sim_member", "hashed_profile_content", {room_id: roomID, mxid}).safeIntegers().pluck().get()
 | 
				
			||||||
 | 
						// only do the actual sync if the hash has changed since we last looked
 | 
				
			||||||
 | 
						if (existingHash !== currentHash) {
 | 
				
			||||||
 | 
							await api.sendState(roomID, "m.room.member", mxid, content, mxid)
 | 
				
			||||||
 | 
							db.prepare("UPDATE sim_member SET hashed_profile_content = ? WHERE room_id = ? AND mxid = ?").run(currentHash, roomID, mxid)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return mxid
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** @returns {Promise<{member?: Ty.PkMember}>} */
 | 
				
			||||||
 | 
					function fetchMessage(messageID) {
 | 
				
			||||||
 | 
						return fetch(`https://api.pluralkit.me/v2/messages/${messageID}`).then(res => res.json())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports._memberToStateContent = memberToStateContent
 | 
				
			||||||
 | 
					module.exports.ensureSim = ensureSim
 | 
				
			||||||
 | 
					module.exports.ensureSimJoined = ensureSimJoined
 | 
				
			||||||
 | 
					module.exports.syncUser = syncUser
 | 
				
			||||||
 | 
					module.exports.fetchMessage = fetchMessage
 | 
				
			||||||
| 
						 | 
					@ -123,7 +123,7 @@ async function memberToStateContent(user, member, guildID) {
 | 
				
			||||||
	return content
 | 
						return content
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function hashProfileContent(content) {
 | 
					function _hashProfileContent(content) {
 | 
				
			||||||
	const unsignedHash = hasher.h64(`${content.displayname}\u0000${content.avatar_url}`)
 | 
						const unsignedHash = hasher.h64(`${content.displayname}\u0000${content.avatar_url}`)
 | 
				
			||||||
	const signedHash = unsignedHash - 0x8000000000000000n // shifting down to signed 64-bit range
 | 
						const signedHash = unsignedHash - 0x8000000000000000n // shifting down to signed 64-bit range
 | 
				
			||||||
	return signedHash
 | 
						return signedHash
 | 
				
			||||||
| 
						 | 
					@ -142,7 +142,7 @@ function hashProfileContent(content) {
 | 
				
			||||||
async function syncUser(user, member, guildID, roomID) {
 | 
					async function syncUser(user, member, guildID, roomID) {
 | 
				
			||||||
	const mxid = await ensureSimJoined(user, roomID)
 | 
						const mxid = await ensureSimJoined(user, roomID)
 | 
				
			||||||
	const content = await memberToStateContent(user, member, guildID)
 | 
						const content = await memberToStateContent(user, member, guildID)
 | 
				
			||||||
	const currentHash = hashProfileContent(content)
 | 
						const currentHash = _hashProfileContent(content)
 | 
				
			||||||
	const existingHash = select("sim_member", "hashed_profile_content", {room_id: roomID, mxid}).safeIntegers().pluck().get()
 | 
						const existingHash = select("sim_member", "hashed_profile_content", {room_id: roomID, mxid}).safeIntegers().pluck().get()
 | 
				
			||||||
	// only do the actual sync if the hash has changed since we last looked
 | 
						// only do the actual sync if the hash has changed since we last looked
 | 
				
			||||||
	if (existingHash !== currentHash) {
 | 
						if (existingHash !== currentHash) {
 | 
				
			||||||
| 
						 | 
					@ -179,6 +179,7 @@ async function syncAllUsersInRoom(roomID) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports._memberToStateContent = memberToStateContent
 | 
					module.exports._memberToStateContent = memberToStateContent
 | 
				
			||||||
 | 
					module.exports._hashProfileContent = _hashProfileContent
 | 
				
			||||||
module.exports.ensureSim = ensureSim
 | 
					module.exports.ensureSim = ensureSim
 | 
				
			||||||
module.exports.ensureSimJoined = ensureSimJoined
 | 
					module.exports.ensureSimJoined = ensureSimJoined
 | 
				
			||||||
module.exports.syncUser = syncUser
 | 
					module.exports.syncUser = syncUser
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,8 @@ const messageToEvent = sync.require("../converters/message-to-event")
 | 
				
			||||||
const api = sync.require("../../matrix/api")
 | 
					const api = sync.require("../../matrix/api")
 | 
				
			||||||
/** @type {import("./register-user")} */
 | 
					/** @type {import("./register-user")} */
 | 
				
			||||||
const registerUser = sync.require("./register-user")
 | 
					const registerUser = sync.require("./register-user")
 | 
				
			||||||
 | 
					/** @type {import("./register-pk-user")} */
 | 
				
			||||||
 | 
					const registerPkUser = sync.require("./register-pk-user")
 | 
				
			||||||
/** @type {import("../actions/create-room")} */
 | 
					/** @type {import("../actions/create-room")} */
 | 
				
			||||||
const createRoom = sync.require("../actions/create-room")
 | 
					const createRoom = sync.require("../actions/create-room")
 | 
				
			||||||
/** @type {import("../../discord/utils")} */
 | 
					/** @type {import("../../discord/utils")} */
 | 
				
			||||||
| 
						 | 
					@ -18,8 +20,9 @@ const dUtils = sync.require("../../discord/utils")
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @param {import("discord-api-types/v10").GatewayMessageCreateDispatchData} message
 | 
					 * @param {import("discord-api-types/v10").GatewayMessageCreateDispatchData} message
 | 
				
			||||||
 * @param {import("discord-api-types/v10").APIGuild} guild
 | 
					 * @param {import("discord-api-types/v10").APIGuild} guild
 | 
				
			||||||
 | 
					 * @param {{speedbump_id: string, speedbump_webhook_id: string} | null} row data about the webhook which is proxying messages in this channel
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
async function sendMessage(message, guild) {
 | 
					async function sendMessage(message, guild, row) {
 | 
				
			||||||
	const roomID = await createRoom.ensureRoom(message.channel_id)
 | 
						const roomID = await createRoom.ensureRoom(message.channel_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let senderMxid = null
 | 
						let senderMxid = null
 | 
				
			||||||
| 
						 | 
					@ -29,6 +32,13 @@ async function sendMessage(message, guild) {
 | 
				
			||||||
		} else { // well, good enough...
 | 
							} else { // well, good enough...
 | 
				
			||||||
			senderMxid = await registerUser.ensureSimJoined(message.author, roomID)
 | 
								senderMxid = await registerUser.ensureSimJoined(message.author, roomID)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						} else if (row && row.speedbump_webhook_id === message.webhook_id) {
 | 
				
			||||||
 | 
							// Handle the PluralKit public instance
 | 
				
			||||||
 | 
							if (row.speedbump_id === "466378653216014359") {
 | 
				
			||||||
 | 
								const root = await registerPkUser.fetchMessage(message.id)
 | 
				
			||||||
 | 
								assert(root.member) // Member is null if member was deleted. We just got this message, so member surely exists.
 | 
				
			||||||
 | 
								senderMxid = await registerPkUser.syncUser(root.member, roomID)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const events = await messageToEvent.messageToEvent(message, guild, {}, {api})
 | 
						const events = await messageToEvent.messageToEvent(message, guild, {}, {api})
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,8 +22,10 @@ async function updateCache(channelID, speedbumpID, speedbumpChecked) {
 | 
				
			||||||
	const now = Math.floor(Date.now() / 1000)
 | 
						const now = Math.floor(Date.now() / 1000)
 | 
				
			||||||
	if (speedbumpChecked && now - speedbumpChecked < SPEEDBUMP_UPDATE_FREQUENCY) return
 | 
						if (speedbumpChecked && now - speedbumpChecked < SPEEDBUMP_UPDATE_FREQUENCY) return
 | 
				
			||||||
	const webhooks = await discord.snow.webhook.getChannelWebhooks(channelID)
 | 
						const webhooks = await discord.snow.webhook.getChannelWebhooks(channelID)
 | 
				
			||||||
	const found = webhooks.find(b => KNOWN_BOTS.has(b.application_id))?.application_id || null
 | 
						const found = webhooks.find(b => KNOWN_BOTS.has(b.application_id))
 | 
				
			||||||
	db.prepare("UPDATE channel_room SET speedbump_id = ?, speedbump_checked = ? WHERE channel_id = ?").run(found, now, channelID)
 | 
						const foundApplication = found?.application_id
 | 
				
			||||||
 | 
						const foundWebhook = found?.id
 | 
				
			||||||
 | 
						db.prepare("UPDATE channel_room SET speedbump_id = ?, speedbump_webhook_id = ?, speedbump_checked = ? WHERE channel_id = ?").run(foundApplication, foundWebhook, now, channelID)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** @type {Set<string>} set of messageID */
 | 
					/** @type {Set<string>} set of messageID */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -236,22 +236,22 @@ module.exports = {
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	async onMessageCreate(client, message) {
 | 
						async onMessageCreate(client, message) {
 | 
				
			||||||
		if (message.author.username === "Deleted User") return // Nothing we can do for deleted users.
 | 
							if (message.author.username === "Deleted User") return // Nothing we can do for deleted users.
 | 
				
			||||||
		if (message.webhook_id) {
 | 
					 | 
				
			||||||
			const row = select("webhook", "webhook_id", {webhook_id: message.webhook_id}).pluck().get()
 | 
					 | 
				
			||||||
			if (row) return // The message was sent by the bridge's own webhook on discord. We don't want to reflect this back, so just drop it.
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			const speedbumpID = select("channel_room", "speedbump_id", {channel_id: message.channel_id}).pluck().get()
 | 
					 | 
				
			||||||
			if (speedbumpID) {
 | 
					 | 
				
			||||||
				const affected = await speedbump.doSpeedbump(message.id)
 | 
					 | 
				
			||||||
				if (affected) return
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		const channel = client.channels.get(message.channel_id)
 | 
							const channel = client.channels.get(message.channel_id)
 | 
				
			||||||
		if (!channel || !("guild_id" in channel) || !channel.guild_id) return // Nothing we can do in direct messages.
 | 
							if (!channel || !("guild_id" in channel) || !channel.guild_id) return // Nothing we can do in direct messages.
 | 
				
			||||||
		const guild = client.guilds.get(channel.guild_id)
 | 
							const guild = client.guilds.get(channel.guild_id)
 | 
				
			||||||
		assert(guild)
 | 
							assert(guild)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		await sendMessage.sendMessage(message, guild),
 | 
							const row = select("channel_room", ["speedbump_id", "speedbump_webhook_id"], {channel_id: message.channel_id}).get()
 | 
				
			||||||
 | 
							if (message.webhook_id) {
 | 
				
			||||||
 | 
								const row = select("webhook", "webhook_id", {webhook_id: message.webhook_id}).pluck().get()
 | 
				
			||||||
 | 
								if (row) return // The message was sent by the bridge's own webhook on discord. We don't want to reflect this back, so just drop it.
 | 
				
			||||||
 | 
							} else if (row) {
 | 
				
			||||||
 | 
								const affected = await speedbump.doSpeedbump(message.id)
 | 
				
			||||||
 | 
								if (affected) return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// @ts-ignore
 | 
				
			||||||
 | 
							await sendMessage.sendMessage(message, guild, row),
 | 
				
			||||||
		await discordCommandHandler.execute(message, channel, guild)
 | 
							await discordCommandHandler.execute(message, channel, guild)
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -260,13 +260,16 @@ module.exports = {
 | 
				
			||||||
	 * @param {DiscordTypes.GatewayMessageUpdateDispatchData} data
 | 
						 * @param {DiscordTypes.GatewayMessageUpdateDispatchData} data
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	async onMessageUpdate(client, data) {
 | 
						async onMessageUpdate(client, data) {
 | 
				
			||||||
 | 
							const row = select("channel_room", ["speedbump_id", "speedbump_webhook_id"], {channel_id: data.channel_id}).get()
 | 
				
			||||||
		if (data.webhook_id) {
 | 
							if (data.webhook_id) {
 | 
				
			||||||
			const row = select("webhook", "webhook_id", {webhook_id: data.webhook_id}).pluck().get()
 | 
								const row = select("webhook", "webhook_id", {webhook_id: data.webhook_id}).pluck().get()
 | 
				
			||||||
			if (row) {
 | 
								if (row) return // The message was sent by the bridge's own webhook on discord. We don't want to reflect this back, so just drop it.
 | 
				
			||||||
				// The update was sent by the bridge's own webhook on discord. We don't want to reflect this back, so just drop it.
 | 
							} else if (row) {
 | 
				
			||||||
				return
 | 
								// Edits need to go through the speedbump as well. If the message is delayed but the edit isn't, we don't have anything to edit from.
 | 
				
			||||||
			}
 | 
								const affected = await speedbump.doSpeedbump(data.id)
 | 
				
			||||||
 | 
								if (affected) return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Based on looking at data they've sent me over the gateway, this is the best way to check for meaningful changes.
 | 
							// Based on looking at data they've sent me over the gateway, this is the best way to check for meaningful changes.
 | 
				
			||||||
		// If the message content is a string then it includes all interesting fields and is meaningful.
 | 
							// If the message content is a string then it includes all interesting fields and is meaningful.
 | 
				
			||||||
		if (typeof data.content === "string") {
 | 
							if (typeof data.content === "string") {
 | 
				
			||||||
| 
						 | 
					@ -277,7 +280,8 @@ module.exports = {
 | 
				
			||||||
			if (!channel || !("guild_id" in channel) || !channel.guild_id) return // Nothing we can do in direct messages.
 | 
								if (!channel || !("guild_id" in channel) || !channel.guild_id) return // Nothing we can do in direct messages.
 | 
				
			||||||
			const guild = client.guilds.get(channel.guild_id)
 | 
								const guild = client.guilds.get(channel.guild_id)
 | 
				
			||||||
			assert(guild)
 | 
								assert(guild)
 | 
				
			||||||
			await editMessage.editMessage(message, guild)
 | 
								// @ts-ignore
 | 
				
			||||||
 | 
								await editMessage.editMessage(message, guild, row)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
BEGIN TRANSACTION;
 | 
					BEGIN TRANSACTION;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ALTER TABLE channel_room ADD COLUMN speedbump_id TEXT;
 | 
					ALTER TABLE channel_room ADD COLUMN speedbump_id TEXT;
 | 
				
			||||||
 | 
					ALTER TABLE channel_room ADD COLUMN speedbump_webhook_id TEXT;
 | 
				
			||||||
ALTER TABLE channel_room ADD COLUMN speedbump_checked INTEGER;
 | 
					ALTER TABLE channel_room ADD COLUMN speedbump_checked INTEGER;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
COMMIT;
 | 
					COMMIT;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										1
									
								
								db/orm-defs.d.ts
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								db/orm-defs.d.ts
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -8,6 +8,7 @@ export type Models = {
 | 
				
			||||||
		custom_avatar: string | null
 | 
							custom_avatar: string | null
 | 
				
			||||||
		last_bridged_pin_timestamp: number | null
 | 
							last_bridged_pin_timestamp: number | null
 | 
				
			||||||
		speedbump_id: string | null
 | 
							speedbump_id: string | null
 | 
				
			||||||
 | 
							speedbump_webhook_id: string | null
 | 
				
			||||||
		speedbump_checked: number | null
 | 
							speedbump_checked: number | null
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										20
									
								
								types.d.ts
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								types.d.ts
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -34,6 +34,26 @@ export type WebhookCreds = {
 | 
				
			||||||
	token: string
 | 
						token: string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type PkMember = {
 | 
				
			||||||
 | 
						id: string
 | 
				
			||||||
 | 
						uuid: string
 | 
				
			||||||
 | 
						name: string
 | 
				
			||||||
 | 
						display_name: string | null
 | 
				
			||||||
 | 
						color: string | null
 | 
				
			||||||
 | 
						birthday: string | null
 | 
				
			||||||
 | 
						pronouns: string | null
 | 
				
			||||||
 | 
						avatar_url: string | null
 | 
				
			||||||
 | 
						webhook_avatar_url: string | null
 | 
				
			||||||
 | 
						banner: string | null
 | 
				
			||||||
 | 
						description: string | null
 | 
				
			||||||
 | 
						created: string | null
 | 
				
			||||||
 | 
						keep_proxy: boolean
 | 
				
			||||||
 | 
						tts: boolean
 | 
				
			||||||
 | 
						autoproxy_enabled: boolean | null
 | 
				
			||||||
 | 
						message_count: number | null
 | 
				
			||||||
 | 
						last_message_timestamp: string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export namespace Event {
 | 
					export namespace Event {
 | 
				
			||||||
	export type Outer<T> = {
 | 
						export type Outer<T> = {
 | 
				
			||||||
		type: string
 | 
							type: string
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue