Rework event dispatchers
This commit is contained in:
		
							parent
							
								
									c1592fcb95
								
							
						
					
					
						commit
						4cb99feeb2
					
				
					 3 changed files with 105 additions and 151 deletions
				
			
		|  | @ -157,59 +157,17 @@ const utils = { | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// Event dispatcher for OOYE bridge operations
 | 		// Event dispatcher for OOYE bridge operations
 | ||||||
| 		if (listen === "full") { | 		if (listen === "full" && message.t) { | ||||||
| 			try { | 			try { | ||||||
| 				if (message.t === "GUILD_UPDATE") { | 				if (message.t === "MESSAGE_REACTION_REMOVE" || message.t === "MESSAGE_REACTION_REMOVE_EMOJI" || message.t === "MESSAGE_REACTION_REMOVE_ALL") { | ||||||
| 					await eventDispatcher.onGuildUpdate(client, message.d) |  | ||||||
| 
 |  | ||||||
| 				} else if (message.t === "GUILD_EMOJIS_UPDATE" || message.t === "GUILD_STICKERS_UPDATE") { |  | ||||||
| 					await eventDispatcher.onExpressionsUpdate(client, message.d) |  | ||||||
| 
 |  | ||||||
| 				} else if (message.t === "CHANNEL_UPDATE") { |  | ||||||
| 					await eventDispatcher.onChannelOrThreadUpdate(client, message.d, false) |  | ||||||
| 
 |  | ||||||
| 				} else if (message.t === "CHANNEL_PINS_UPDATE") { |  | ||||||
| 					await eventDispatcher.onChannelPinsUpdate(client, message.d) |  | ||||||
| 
 |  | ||||||
| 				} else if (message.t === "CHANNEL_DELETE") { |  | ||||||
| 					await eventDispatcher.onChannelDelete(client, message.d) |  | ||||||
| 
 |  | ||||||
| 				} else if (message.t === "THREAD_CREATE") { |  | ||||||
| 					// @ts-ignore
 |  | ||||||
| 					await eventDispatcher.onThreadCreate(client, message.d) |  | ||||||
| 
 |  | ||||||
| 				} else if (message.t === "THREAD_UPDATE") { |  | ||||||
| 					// @ts-ignore
 |  | ||||||
| 					await eventDispatcher.onChannelOrThreadUpdate(client, message.d, true) |  | ||||||
| 
 |  | ||||||
| 				} else if (message.t === "MESSAGE_CREATE") { |  | ||||||
| 					await eventDispatcher.onMessageCreate(client, message.d) |  | ||||||
| 
 |  | ||||||
| 				} else if (message.t === "MESSAGE_UPDATE") { |  | ||||||
| 					await eventDispatcher.onMessageUpdate(client, message.d) |  | ||||||
| 
 |  | ||||||
| 				} else if (message.t === "MESSAGE_DELETE") { |  | ||||||
| 					await eventDispatcher.onMessageDelete(client, message.d) |  | ||||||
| 
 |  | ||||||
| 				} else if (message.t === "MESSAGE_DELETE_BULK") { |  | ||||||
| 					await eventDispatcher.onMessageDeleteBulk(client, message.d) |  | ||||||
| 
 |  | ||||||
| 				} else if (message.t === "TYPING_START") { |  | ||||||
| 					await eventDispatcher.onTypingStart(client, message.d) |  | ||||||
| 
 |  | ||||||
| 				} else if (message.t === "MESSAGE_REACTION_ADD") { |  | ||||||
| 					await eventDispatcher.onReactionAdd(client, message.d) |  | ||||||
| 
 |  | ||||||
| 				} else if (message.t === "MESSAGE_REACTION_REMOVE" || message.t === "MESSAGE_REACTION_REMOVE_EMOJI" || message.t === "MESSAGE_REACTION_REMOVE_ALL") { |  | ||||||
| 					await eventDispatcher.onSomeReactionsRemoved(client, message.d) | 					await eventDispatcher.onSomeReactionsRemoved(client, message.d) | ||||||
| 
 | 
 | ||||||
| 				} else if (message.t === "INTERACTION_CREATE") { | 				} else if (message.t === "INTERACTION_CREATE") { | ||||||
| 					await interactions.dispatchInteraction(message.d) | 					await interactions.dispatchInteraction(message.d) | ||||||
| 
 | 
 | ||||||
| 				} else if (message.t === "PRESENCE_UPDATE") { | 				} else if (message.t in eventDispatcher) { | ||||||
| 					eventDispatcher.onPresenceUpdate(client, message.d) | 					await eventDispatcher[message.t](client, message.d) | ||||||
| 				} | 				} | ||||||
| 
 |  | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				// Let OOYE try to handle errors too
 | 				// Let OOYE try to handle errors too
 | ||||||
| 				await eventDispatcher.onError(client, e, message) | 				await eventDispatcher.onError(client, e, message) | ||||||
|  |  | ||||||
|  | @ -2,7 +2,6 @@ | ||||||
| 
 | 
 | ||||||
| const assert = require("assert").strict | const assert = require("assert").strict | ||||||
| const DiscordTypes = require("discord-api-types/v10") | const DiscordTypes = require("discord-api-types/v10") | ||||||
| const util = require("util") |  | ||||||
| const {sync, db, select, from} = require("../passthrough") | const {sync, db, select, from} = require("../passthrough") | ||||||
| 
 | 
 | ||||||
| /** @type {import("./actions/send-message")}) */ | /** @type {import("./actions/send-message")}) */ | ||||||
|  | @ -27,8 +26,6 @@ const updatePins = sync.require("./actions/update-pins") | ||||||
| const api = sync.require("../matrix/api") | const api = sync.require("../matrix/api") | ||||||
| /** @type {import("../discord/utils")} */ | /** @type {import("../discord/utils")} */ | ||||||
| const dUtils = sync.require("../discord/utils") | const dUtils = sync.require("../discord/utils") | ||||||
| /** @type {import("../m2d/converters/utils")} */ |  | ||||||
| const mxUtils = require("../m2d/converters/utils") |  | ||||||
| /** @type {import("./actions/speedbump")} */ | /** @type {import("./actions/speedbump")} */ | ||||||
| const speedbump = sync.require("./actions/speedbump") | const speedbump = sync.require("./actions/speedbump") | ||||||
| /** @type {import("./actions/retrigger")} */ | /** @type {import("./actions/retrigger")} */ | ||||||
|  | @ -42,8 +39,6 @@ const matrixEventDispatcher = sync.require("../m2d/event-dispatcher") | ||||||
| const Semaphore = require("@chriscdn/promise-semaphore") | const Semaphore = require("@chriscdn/promise-semaphore") | ||||||
| const checkMissedPinsSema = new Semaphore() | const checkMissedPinsSema = new Semaphore() | ||||||
| 
 | 
 | ||||||
| let lastReportedEvent = 0 |  | ||||||
| 
 |  | ||||||
| // Grab Discord events we care about for the bridge, check them, and pass them on
 | // Grab Discord events we care about for the bridge, check them, and pass them on
 | ||||||
| 
 | 
 | ||||||
| module.exports = { | module.exports = { | ||||||
|  | @ -53,45 +48,14 @@ module.exports = { | ||||||
| 	 * @param {import("cloudstorm").IGatewayMessage} gatewayMessage | 	 * @param {import("cloudstorm").IGatewayMessage} gatewayMessage | ||||||
| 	 */ | 	 */ | ||||||
| 	async onError(client, e, gatewayMessage) { | 	async onError(client, e, gatewayMessage) { | ||||||
| 		console.error("hit event-dispatcher's error handler with this exception:") |  | ||||||
| 		console.error(e) // TODO: also log errors into a file or into the database, maybe use a library for this? or just wing it? definitely need to be able to store the formatted event body to load back in later
 |  | ||||||
| 		console.error(`while handling this ${gatewayMessage.t} gateway event:`) |  | ||||||
| 		console.dir(gatewayMessage.d, {depth: null}) |  | ||||||
| 
 |  | ||||||
| 		if (gatewayMessage.t === "TYPING_START") return |  | ||||||
| 
 |  | ||||||
| 		if (Date.now() - lastReportedEvent < 5000) return |  | ||||||
| 		lastReportedEvent = Date.now() |  | ||||||
| 
 |  | ||||||
| 		const channelID = gatewayMessage.d["channel_id"] | 		const channelID = gatewayMessage.d["channel_id"] | ||||||
| 		if (!channelID) return | 		if (!channelID) return | ||||||
| 		const roomID = select("channel_room", "room_id", {channel_id: channelID}).pluck().get() | 		const roomID = select("channel_room", "room_id", {channel_id: channelID}).pluck().get() | ||||||
| 		if (!roomID) return | 		if (!roomID) return | ||||||
| 
 | 
 | ||||||
| 		const builder = new mxUtils.MatrixStringBuilder() | 		if (gatewayMessage.t === "TYPING_START") return | ||||||
| 		builder.addLine("\u26a0 Bridged event from Discord not delivered", "\u26a0 <strong>Bridged event from Discord not delivered</strong>") |  | ||||||
| 		builder.addLine(`Gateway event: ${gatewayMessage.t}`) |  | ||||||
| 
 | 
 | ||||||
| 		let errorIntroLine = e.toString() | 		await matrixEventDispatcher.sendError(roomID, "Discord", gatewayMessage.t, e, gatewayMessage.d) | ||||||
| 		if (e.cause) { |  | ||||||
| 			errorIntroLine += ` (cause: ${e.cause})` |  | ||||||
| 		} |  | ||||||
| 		builder.addLine(errorIntroLine) |  | ||||||
| 
 |  | ||||||
| 		const stack = matrixEventDispatcher.stringifyErrorStack(e) |  | ||||||
| 		builder.addLine(`Error trace:\n${stack}`, `<details><summary>Error trace</summary><pre>${stack}</pre></details>`) |  | ||||||
| 
 |  | ||||||
| 		builder.addLine("", `<details><summary>Original payload</summary><pre>${util.inspect(gatewayMessage.d, false, 4, false)}</pre></details>`) |  | ||||||
| 		await api.sendEvent(roomID, "m.room.message", { |  | ||||||
| 			...builder.get(), |  | ||||||
| 			"moe.cadence.ooye.error": { |  | ||||||
| 				source: "discord", |  | ||||||
| 				payload: gatewayMessage |  | ||||||
| 			}, |  | ||||||
| 			"m.mentions": { |  | ||||||
| 				user_ids: ["@cadence:cadence.moe"] |  | ||||||
| 			} |  | ||||||
| 		}) |  | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
|  | @ -151,7 +115,7 @@ module.exports = { | ||||||
| 					backfill: true, | 					backfill: true, | ||||||
| 					...messages[i] | 					...messages[i] | ||||||
| 				} | 				} | ||||||
| 				await module.exports.onMessageCreate(client, simulatedGatewayDispatchData) | 				await module.exports.MESSAGE_CREATE(client, simulatedGatewayDispatchData) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
|  | @ -198,7 +162,7 @@ module.exports = { | ||||||
| 	 * @param {import("./discord-client")} client | 	 * @param {import("./discord-client")} client | ||||||
| 	 * @param {DiscordTypes.APIThreadChannel} thread | 	 * @param {DiscordTypes.APIThreadChannel} thread | ||||||
| 	 */ | 	 */ | ||||||
| 	async onThreadCreate(client, thread) { | 	async THREAD_CREATE(client, thread) { | ||||||
| 		const channelID = thread.parent_id || undefined | 		const channelID = thread.parent_id || undefined | ||||||
| 		const parentRoomID = select("channel_room", "room_id", {channel_id: channelID}).pluck().get() | 		const parentRoomID = select("channel_room", "room_id", {channel_id: channelID}).pluck().get() | ||||||
| 		if (!parentRoomID) return // Not interested in a thread if we aren't interested in its wider channel (won't autocreate)
 | 		if (!parentRoomID) return // Not interested in a thread if we aren't interested in its wider channel (won't autocreate)
 | ||||||
|  | @ -210,7 +174,7 @@ module.exports = { | ||||||
| 	 * @param {import("./discord-client")} client | 	 * @param {import("./discord-client")} client | ||||||
| 	 * @param {DiscordTypes.GatewayGuildUpdateDispatchData} guild | 	 * @param {DiscordTypes.GatewayGuildUpdateDispatchData} guild | ||||||
| 	 */ | 	 */ | ||||||
| 	async onGuildUpdate(client, guild) { | 	async GUILD_UPDATE(client, guild) { | ||||||
| 		const spaceID = select("guild_space", "space_id", {guild_id: guild.id}).pluck().get() | 		const spaceID = select("guild_space", "space_id", {guild_id: guild.id}).pluck().get() | ||||||
| 		if (!spaceID) return | 		if (!spaceID) return | ||||||
| 		await createSpace.syncSpace(guild) | 		await createSpace.syncSpace(guild) | ||||||
|  | @ -219,19 +183,26 @@ module.exports = { | ||||||
| 	/** | 	/** | ||||||
| 	 * @param {import("./discord-client")} client | 	 * @param {import("./discord-client")} client | ||||||
| 	 * @param {DiscordTypes.GatewayChannelUpdateDispatchData} channelOrThread | 	 * @param {DiscordTypes.GatewayChannelUpdateDispatchData} channelOrThread | ||||||
| 	 * @param {boolean} isThread |  | ||||||
| 	 */ | 	 */ | ||||||
| 	async onChannelOrThreadUpdate(client, channelOrThread, isThread) { | 	async CHANNEL_UPDATE(client, channelOrThread) { | ||||||
| 		const roomID = select("channel_room", "room_id", {channel_id: channelOrThread.id}).pluck().get() | 		const roomID = select("channel_room", "room_id", {channel_id: channelOrThread.id}).pluck().get() | ||||||
| 		if (!roomID) return // No target room to update the data on
 | 		if (!roomID) return // No target room to update the data on
 | ||||||
| 		await createRoom.syncRoom(channelOrThread.id) | 		await createRoom.syncRoom(channelOrThread.id) | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * @param {import("./discord-client")} client | ||||||
|  | 	 * @param {DiscordTypes.GatewayChannelUpdateDispatchData} thread | ||||||
|  | 	 */ | ||||||
|  | 	async THREAD_UPDATE(client, thread) { | ||||||
|  | 		await module.exports.CHANNEL_UPDATE(client, thread) | ||||||
|  | 	}, | ||||||
|  | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * @param {import("./discord-client")} client | 	 * @param {import("./discord-client")} client | ||||||
| 	 * @param {DiscordTypes.GatewayChannelPinsUpdateDispatchData} data | 	 * @param {DiscordTypes.GatewayChannelPinsUpdateDispatchData} data | ||||||
| 	 */ | 	 */ | ||||||
| 	async onChannelPinsUpdate(client, data) { | 	async CHANNEL_PINS_UPDATE(client, data) { | ||||||
| 		const roomID = select("channel_room", "room_id", {channel_id: data.channel_id}).pluck().get() | 		const roomID = select("channel_room", "room_id", {channel_id: data.channel_id}).pluck().get() | ||||||
| 		if (!roomID) return // No target room to update pins in
 | 		if (!roomID) return // No target room to update pins in
 | ||||||
| 		const convertedTimestamp = updatePins.convertTimestamp(data.last_pin_timestamp) | 		const convertedTimestamp = updatePins.convertTimestamp(data.last_pin_timestamp) | ||||||
|  | @ -242,7 +213,7 @@ module.exports = { | ||||||
| 	 * @param {import("./discord-client")} client | 	 * @param {import("./discord-client")} client | ||||||
| 	 * @param {DiscordTypes.GatewayChannelDeleteDispatchData} channel | 	 * @param {DiscordTypes.GatewayChannelDeleteDispatchData} channel | ||||||
| 	 */ | 	 */ | ||||||
| 	async onChannelDelete(client, channel) { | 	async CHANNEL_DELETE(client, channel) { | ||||||
| 		const guildID = channel["guild_id"] | 		const guildID = channel["guild_id"] | ||||||
| 		if (!guildID) return // channel must have been a DM channel or something
 | 		if (!guildID) return // channel must have been a DM channel or something
 | ||||||
| 		const roomID = select("channel_room", "room_id", {channel_id: channel.id}).pluck().get() | 		const roomID = select("channel_room", "room_id", {channel_id: channel.id}).pluck().get() | ||||||
|  | @ -255,7 +226,7 @@ module.exports = { | ||||||
| 	 * @param {import("./discord-client")} client | 	 * @param {import("./discord-client")} client | ||||||
| 	 * @param {DiscordTypes.GatewayMessageCreateDispatchData} message | 	 * @param {DiscordTypes.GatewayMessageCreateDispatchData} message | ||||||
| 	 */ | 	 */ | ||||||
| 	async onMessageCreate(client, message) { | 	async MESSAGE_CREATE(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.
 | ||||||
| 		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.
 | ||||||
|  | @ -285,7 +256,7 @@ module.exports = { | ||||||
| 	 * @param {import("./discord-client")} client | 	 * @param {import("./discord-client")} client | ||||||
| 	 * @param {DiscordTypes.GatewayMessageUpdateDispatchData} data | 	 * @param {DiscordTypes.GatewayMessageUpdateDispatchData} data | ||||||
| 	 */ | 	 */ | ||||||
| 	async onMessageUpdate(client, data) { | 	async MESSAGE_UPDATE(client, data) { | ||||||
| 		// 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.
 | ||||||
| 		// Otherwise, if there are embeds, then the system generated URL preview embeds.
 | 		// Otherwise, if there are embeds, then the system generated URL preview embeds.
 | ||||||
|  | @ -303,7 +274,7 @@ module.exports = { | ||||||
| 		if (affected) return | 		if (affected) return | ||||||
| 
 | 
 | ||||||
| 		// Check that the sending-to room exists, and deal with Eventual Consistency(TM)
 | 		// Check that the sending-to room exists, and deal with Eventual Consistency(TM)
 | ||||||
| 		if (retrigger.eventNotFoundThenRetrigger(data.id, module.exports.onMessageUpdate, client, data)) return | 		if (retrigger.eventNotFoundThenRetrigger(data.id, module.exports.MESSAGE_UPDATE, client, data)) return | ||||||
| 
 | 
 | ||||||
| 		/** @type {DiscordTypes.GatewayMessageCreateDispatchData} */ | 		/** @type {DiscordTypes.GatewayMessageCreateDispatchData} */ | ||||||
| 		// @ts-ignore
 | 		// @ts-ignore
 | ||||||
|  | @ -321,7 +292,7 @@ module.exports = { | ||||||
| 	 * @param {import("./discord-client")} client | 	 * @param {import("./discord-client")} client | ||||||
| 	 * @param {DiscordTypes.GatewayMessageReactionAddDispatchData} data | 	 * @param {DiscordTypes.GatewayMessageReactionAddDispatchData} data | ||||||
| 	 */ | 	 */ | ||||||
| 	async onReactionAdd(client, data) { | 	async MESSAGE_REACTION_ADD(client, data) { | ||||||
| 		if (data.user_id === client.user.id) return // m2d reactions are added by the discord bot user - do not reflect them back to matrix.
 | 		if (data.user_id === client.user.id) return // m2d reactions are added by the discord bot user - do not reflect them back to matrix.
 | ||||||
| 		await addReaction.addReaction(data) | 		await addReaction.addReaction(data) | ||||||
| 	}, | 	}, | ||||||
|  | @ -338,25 +309,25 @@ module.exports = { | ||||||
| 	 * @param {import("./discord-client")} client | 	 * @param {import("./discord-client")} client | ||||||
| 	 * @param {DiscordTypes.GatewayMessageDeleteDispatchData} data | 	 * @param {DiscordTypes.GatewayMessageDeleteDispatchData} data | ||||||
| 	 */ | 	 */ | ||||||
| 	async onMessageDelete(client, data) { | 	async MESSAGE_DELETE(client, data) { | ||||||
| 		speedbump.onMessageDelete(data.id) | 		speedbump.onMessageDelete(data.id) | ||||||
| 		if (retrigger.eventNotFoundThenRetrigger(data.id, module.exports.onMessageDelete, client, data)) return | 		if (retrigger.eventNotFoundThenRetrigger(data.id, module.exports.MESSAGE_DELETE, client, data)) return | ||||||
| 		await deleteMessage.deleteMessage(data) | 		await deleteMessage.deleteMessage(data) | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 		/** | 	/** | ||||||
| 	 * @param {import("./discord-client")} client | 	 * @param {import("./discord-client")} client | ||||||
| 	 * @param {DiscordTypes.GatewayMessageDeleteBulkDispatchData} data | 	 * @param {DiscordTypes.GatewayMessageDeleteBulkDispatchData} data | ||||||
| 	 */ | 	 */ | ||||||
| 		async onMessageDeleteBulk(client, data) { | 	async MESSAGE_DELETE_BULK(client, data) { | ||||||
| 			await deleteMessage.deleteMessageBulk(data) | 		await deleteMessage.deleteMessageBulk(data) | ||||||
| 		}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * @param {import("./discord-client")} client | 	 * @param {import("./discord-client")} client | ||||||
| 	 * @param {DiscordTypes.GatewayTypingStartDispatchData} data | 	 * @param {DiscordTypes.GatewayTypingStartDispatchData} data | ||||||
| 	 */ | 	 */ | ||||||
| 	async onTypingStart(client, data) { | 	async TYPING_START(client, data) { | ||||||
| 		const roomID = select("channel_room", "room_id", {channel_id: data.channel_id}).pluck().get() | 		const roomID = select("channel_room", "room_id", {channel_id: data.channel_id}).pluck().get() | ||||||
| 		if (!roomID) return | 		if (!roomID) return | ||||||
| 		const mxid = from("sim").join("sim_member", "mxid").where({user_id: data.user_id, room_id: roomID}).pluck("mxid").get() | 		const mxid = from("sim").join("sim_member", "mxid").where({user_id: data.user_id, room_id: roomID}).pluck("mxid").get() | ||||||
|  | @ -369,9 +340,17 @@ module.exports = { | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * @param {import("./discord-client")} client | 	 * @param {import("./discord-client")} client | ||||||
| 	 * @param {DiscordTypes.GatewayGuildEmojisUpdateDispatchData | DiscordTypes.GatewayGuildStickersUpdateDispatchData} data | 	 * @param {DiscordTypes.GatewayGuildEmojisUpdateDispatchData} data | ||||||
| 	 */ | 	 */ | ||||||
| 	async onExpressionsUpdate(client, data) { | 	async GUILD_EMOJIS_UPDATE(client, data) { | ||||||
|  | 		await createSpace.syncSpaceExpressions(data, false) | ||||||
|  | 	}, | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * @param {import("./discord-client")} client | ||||||
|  | 	 * @param {DiscordTypes.GatewayGuildStickersUpdateDispatchData} data | ||||||
|  | 	 */ | ||||||
|  | 	async GUILD_STICKERS_UPDATE(client, data) { | ||||||
| 		await createSpace.syncSpaceExpressions(data, false) | 		await createSpace.syncSpaceExpressions(data, false) | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
|  | @ -379,7 +358,7 @@ module.exports = { | ||||||
| 	 * @param {import("./discord-client")} client | 	 * @param {import("./discord-client")} client | ||||||
| 	 * @param {DiscordTypes.GatewayPresenceUpdateDispatchData} data | 	 * @param {DiscordTypes.GatewayPresenceUpdateDispatchData} data | ||||||
| 	 */ | 	 */ | ||||||
| 	onPresenceUpdate(client, data) { | 	PRESENCE_UPDATE(client, data) { | ||||||
| 		const status = data.status | 		const status = data.status | ||||||
| 		if (!status) return | 		if (!status) return | ||||||
| 		setPresence.presenceTracker.incomingPresence(data.user.id, data.guild_id, status) | 		setPresence.presenceTracker.incomingPresence(data.user.id, data.guild_id, status) | ||||||
|  |  | ||||||
|  | @ -42,7 +42,7 @@ function stringifyErrorStack(err, depth = 0) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// add full stack trace if one exists, otherwise convert to string
 | 	// add full stack trace if one exists, otherwise convert to string
 | ||||||
| 	let stackLines = ( err?.stack ?? `${err}` ).replace(/^/gm, " ".repeat(depth)).trim().split("\n") | 	let stackLines = String(err?.stack ?? err).replace(/^/gm, " ".repeat(depth)).trim().split("\n") | ||||||
| 	let cloudstormLine = stackLines.findIndex(l => l.includes("/node_modules/cloudstorm/")) | 	let cloudstormLine = stackLines.findIndex(l => l.includes("/node_modules/cloudstorm/")) | ||||||
| 	if (cloudstormLine !== -1) { | 	if (cloudstormLine !== -1) { | ||||||
| 		stackLines = stackLines.slice(0, cloudstormLine - 2) | 		stackLines = stackLines.slice(0, cloudstormLine - 2) | ||||||
|  | @ -80,56 +80,72 @@ function stringifyErrorStack(err, depth = 0) { | ||||||
| 	return collapsed; | 	return collapsed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * @param {string} roomID | ||||||
|  |  * @param {"Discord" | "Matrix"} source | ||||||
|  |  * @param {any} type | ||||||
|  |  * @param {any} e | ||||||
|  |  * @param {any} payload | ||||||
|  |  */ | ||||||
|  | async function sendError(roomID, source, type, e, payload) { | ||||||
|  | 	console.error(`Error while processing a ${type} ${source} event:`) | ||||||
|  | 	console.error(e) | ||||||
|  | 	console.dir(payload, {depth: null}) | ||||||
|  | 
 | ||||||
|  | 	if (Date.now() - lastReportedEvent < 5000) return null | ||||||
|  | 	lastReportedEvent = Date.now() | ||||||
|  | 
 | ||||||
|  | 	let errorIntroLine = e.toString() | ||||||
|  | 	if (e.cause) { | ||||||
|  | 		errorIntroLine += ` (cause: ${e.cause})` | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	const builder = new utils.MatrixStringBuilder() | ||||||
|  | 
 | ||||||
|  | 	const cloudflareErrorTitle = errorIntroLine.match(/<!DOCTYPE html>.*?<title>discord\.com \| ([^<]*)<\/title>/s)?.[1] | ||||||
|  | 	if (cloudflareErrorTitle) { | ||||||
|  | 		builder.addLine( | ||||||
|  | 			`\u26a0 Matrix event not delivered to Discord. Discord might be down right now. Cloudflare error: ${cloudflareErrorTitle}`, | ||||||
|  | 			`\u26a0 <strong>Matrix event not delivered to Discord</strong><br>Discord might be down right now. Cloudflare error: ${cloudflareErrorTitle}` | ||||||
|  | 		) | ||||||
|  | 	} else { | ||||||
|  | 		// What
 | ||||||
|  | 		const what = source === "Discord" ? "Bridged event from Discord not delivered" : "Matrix event not delivered to Discord" | ||||||
|  | 		builder.addLine(`\u26a0 ${what}`, `\u26a0 <strong>${what}</strong>`) | ||||||
|  | 
 | ||||||
|  | 		// Who
 | ||||||
|  | 		builder.addLine(`Event type: ${type}`) | ||||||
|  | 
 | ||||||
|  | 		// Why
 | ||||||
|  | 		builder.addLine(errorIntroLine) | ||||||
|  | 
 | ||||||
|  | 		// Where
 | ||||||
|  | 		const stack = stringifyErrorStack(e) | ||||||
|  | 		builder.addLine(`Error trace:\n${stack}`, `<details><summary>Error trace</summary><pre>${stack}</pre></details>`) | ||||||
|  | 
 | ||||||
|  | 		// How
 | ||||||
|  | 		builder.addLine("", `<details><summary>Original payload</summary><pre>${util.inspect(payload, false, 4, false)}</pre></details>`) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Send
 | ||||||
|  | 	await api.sendEvent(roomID, "m.room.message", { | ||||||
|  | 		...builder.get(), | ||||||
|  | 		"moe.cadence.ooye.error": { | ||||||
|  | 			source: source.toLowerCase(), | ||||||
|  | 			payload | ||||||
|  | 		}, | ||||||
|  | 		"m.mentions": { | ||||||
|  | 			user_ids: ["@cadence:cadence.moe"] | ||||||
|  | 		} | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| function guard(type, fn) { | function guard(type, fn) { | ||||||
| 	return async function(event, ...args) { | 	return async function(event, ...args) { | ||||||
| 		try { | 		try { | ||||||
| 			return await fn(event, ...args) | 			return await fn(event, ...args) | ||||||
| 		} catch (e) { | 		} catch (e) { | ||||||
| 			console.error(`Exception while processing a ${type} Matrix event:`) | 			await sendError(event.room_id, "Matrix", type, e, event) | ||||||
| 			console.dir(event, {depth: null}) |  | ||||||
| 
 |  | ||||||
| 			if (Date.now() - lastReportedEvent < 5000) return |  | ||||||
| 			lastReportedEvent = Date.now() |  | ||||||
| 
 |  | ||||||
| 			let errorIntroLine = e.toString() |  | ||||||
| 			if (e.cause) { |  | ||||||
| 				errorIntroLine += ` (cause: ${e.cause})` |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			const cloudflareErrorTitle = errorIntroLine.match(/<!DOCTYPE html>.*?<title>discord\.com \| ([^<]*)<\/title>/s)?.[1] |  | ||||||
| 			if (cloudflareErrorTitle) { |  | ||||||
| 				return api.sendEvent(event.room_id, "m.room.message", { |  | ||||||
| 					msgtype: "m.text", |  | ||||||
| 					body: `\u26a0 Matrix event not delivered to Discord. Cloudflare error: ${cloudflareErrorTitle}.`, |  | ||||||
| 					format: "org.matrix.custom.html", |  | ||||||
| 					formatted_body: `\u26a0 <strong>Matrix event not delivered to Discord</strong><br>Cloudflare error: ${cloudflareErrorTitle}`, |  | ||||||
| 					"moe.cadence.ooye.error": { |  | ||||||
| 						source: "matrix", |  | ||||||
| 						payload: event |  | ||||||
| 					} |  | ||||||
| 				}) |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			const stack = stringifyErrorStack(e) |  | ||||||
| 			api.sendEvent(event.room_id, "m.room.message", { |  | ||||||
| 				msgtype: "m.text", |  | ||||||
| 				body: "\u26a0 Matrix event not delivered to Discord. See formatted content for full details.", |  | ||||||
| 				format: "org.matrix.custom.html", |  | ||||||
| 				formatted_body: "\u26a0 <strong>Matrix event not delivered to Discord</strong>" |  | ||||||
| 					+ `<br>Event type: ${type}` |  | ||||||
| 					+ `<br>${errorIntroLine}` |  | ||||||
| 					+ `<br><details><summary>Error trace</summary>` |  | ||||||
| 					+ `<pre>${stack}</pre></details>` |  | ||||||
| 					+ `<details><summary>Original payload</summary>` |  | ||||||
| 					+ `<pre>${util.inspect(event, false, 4, false)}</pre></details>`, |  | ||||||
| 				"moe.cadence.ooye.error": { |  | ||||||
| 					source: "matrix", |  | ||||||
| 					payload: event |  | ||||||
| 				}, |  | ||||||
| 				"m.mentions": { |  | ||||||
| 					user_ids: ["@cadence:cadence.moe"] |  | ||||||
| 				} |  | ||||||
| 			}) |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | @ -356,3 +372,4 @@ async event => { | ||||||
| })) | })) | ||||||
| 
 | 
 | ||||||
| module.exports.stringifyErrorStack = stringifyErrorStack | module.exports.stringifyErrorStack = stringifyErrorStack | ||||||
|  | module.exports.sendError = sendError | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue