New error trace stringifier
This commit is contained in:
		| @@ -35,6 +35,8 @@ const speedbump = sync.require("./actions/speedbump") | |||||||
| const retrigger = sync.require("./actions/retrigger") | const retrigger = sync.require("./actions/retrigger") | ||||||
| /** @type {import("./actions/set-presence")} */ | /** @type {import("./actions/set-presence")} */ | ||||||
| const setPresence = sync.require("./actions/set-presence") | const setPresence = sync.require("./actions/set-presence") | ||||||
|  | /** @type {import("../m2d/event-dispatcher")} */ | ||||||
|  | const matrixEventDispatcher = sync.require("../m2d/event-dispatcher") | ||||||
|  |  | ||||||
| /** @type {any} */ // @ts-ignore bad types from semaphore | /** @type {any} */ // @ts-ignore bad types from semaphore | ||||||
| const Semaphore = require("@chriscdn/promise-semaphore") | const Semaphore = require("@chriscdn/promise-semaphore") | ||||||
| @@ -66,22 +68,19 @@ module.exports = { | |||||||
| 		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 | ||||||
|  |  | ||||||
| 		let stackLines = null |  | ||||||
| 		if (e.stack) { |  | ||||||
| 			stackLines = e.stack.split("\n") |  | ||||||
| 			let cloudstormLine = stackLines.findIndex(l => l.includes("/node_modules/cloudstorm/")) |  | ||||||
| 			if (cloudstormLine !== -1) { |  | ||||||
| 				stackLines = stackLines.slice(0, cloudstormLine - 2) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		const builder = new mxUtils.MatrixStringBuilder() | 		const builder = new mxUtils.MatrixStringBuilder() | ||||||
| 		builder.addLine("\u26a0 Bridged event from Discord not delivered", "\u26a0 <strong>Bridged event from Discord not delivered</strong>") | 		builder.addLine("\u26a0 Bridged event from Discord not delivered", "\u26a0 <strong>Bridged event from Discord not delivered</strong>") | ||||||
| 		builder.addLine(`Gateway event: ${gatewayMessage.t}`) | 		builder.addLine(`Gateway event: ${gatewayMessage.t}`) | ||||||
| 		builder.addLine(e.toString()) |  | ||||||
| 		if (stackLines) { | 		let errorIntroLine = e.toString() | ||||||
| 			builder.addLine(`Error trace:\n${stackLines.join("\n")}`, `<details><summary>Error trace</summary><pre>${stackLines.join("\n")}</pre></details>`) | 		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>`) | 		builder.addLine("", `<details><summary>Original payload</summary><pre>${util.inspect(gatewayMessage.d, false, 4, false)}</pre></details>`) | ||||||
| 		await api.sendEvent(roomID, "m.room.message", { | 		await api.sendEvent(roomID, "m.room.message", { | ||||||
| 			...builder.get(), | 			...builder.get(), | ||||||
|   | |||||||
| @@ -28,6 +28,58 @@ const {reg} = require("../matrix/read-registration") | |||||||
|  |  | ||||||
| let lastReportedEvent = 0 | let lastReportedEvent = 0 | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * This function is adapted from Evan Kaufman's fantastic work. | ||||||
|  |  * The original function and my adapted function are both MIT licensed. | ||||||
|  |  * @url https://github.com/EvanK/npm-loggable-error/ | ||||||
|  |  * @param {number} [depth] | ||||||
|  |  * @returns {string} | ||||||
|  | */ | ||||||
|  | function stringifyErrorStack(err, depth = 0) { | ||||||
|  | 	let collapsed = " ".repeat(depth); | ||||||
|  | 	if (!(err instanceof Error)) { | ||||||
|  | 		return collapsed + err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// add full stack trace if one exists, otherwise convert to string | ||||||
|  | 	let stackLines = ( err?.stack ?? `${err}` ).replace(/^/gm, " ".repeat(depth)).trim().split("\n") | ||||||
|  | 	let cloudstormLine = stackLines.findIndex(l => l.includes("/node_modules/cloudstorm/")) | ||||||
|  | 	if (cloudstormLine !== -1) { | ||||||
|  | 		stackLines = stackLines.slice(0, cloudstormLine - 2) | ||||||
|  | 	} | ||||||
|  | 	collapsed += stackLines.join("\n") | ||||||
|  |  | ||||||
|  | 	const props = Object.getOwnPropertyNames(err).filter(p => !["message", "stack"].includes(p)) | ||||||
|  |  | ||||||
|  | 	// only break into object notation if we have addtl props to dump | ||||||
|  | 	if (props.length) { | ||||||
|  | 		const dedent = " ".repeat(depth); | ||||||
|  | 		const indent = " ".repeat(depth + 2); | ||||||
|  |  | ||||||
|  | 		collapsed += " {\n"; | ||||||
|  |  | ||||||
|  | 		// loop and print each (indented) prop name | ||||||
|  | 		for (let property of props) { | ||||||
|  | 			collapsed += `${indent}[${property}]: `; | ||||||
|  |  | ||||||
|  | 			// if another error object, stringify it too | ||||||
|  | 			if (err[property] instanceof Error) { | ||||||
|  | 				collapsed += stringifyErrorStack(err[property], depth + 2).trimStart(); | ||||||
|  | 			} | ||||||
|  | 			// otherwise stringify as JSON | ||||||
|  | 			else { | ||||||
|  | 				collapsed += JSON.stringify(err[property]); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			collapsed += "\n"; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		collapsed += `${dedent}}\n`; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return collapsed; | ||||||
|  | } | ||||||
|  |  | ||||||
| function guard(type, fn) { | function guard(type, fn) { | ||||||
| 	return async function(event, ...args) { | 	return async function(event, ...args) { | ||||||
| 		try { | 		try { | ||||||
| @@ -39,7 +91,12 @@ function guard(type, fn) { | |||||||
| 			if (Date.now() - lastReportedEvent < 5000) return | 			if (Date.now() - lastReportedEvent < 5000) return | ||||||
| 			lastReportedEvent = Date.now() | 			lastReportedEvent = Date.now() | ||||||
|  |  | ||||||
| 			const cloudflareErrorTitle = e.toString().match(/<!DOCTYPE html>.*?<title>discord\.com \| ([^<]*)<\/title>/s)?.[1] | 			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) { | 			if (cloudflareErrorTitle) { | ||||||
| 				return api.sendEvent(event.room_id, "m.room.message", { | 				return api.sendEvent(event.room_id, "m.room.message", { | ||||||
| 					msgtype: "m.text", | 					msgtype: "m.text", | ||||||
| @@ -53,16 +110,16 @@ function guard(type, fn) { | |||||||
| 				}) | 				}) | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			let stackLines = e.stack.split("\n") | 			const stack = stringifyErrorStack(e) | ||||||
| 			api.sendEvent(event.room_id, "m.room.message", { | 			api.sendEvent(event.room_id, "m.room.message", { | ||||||
| 				msgtype: "m.text", | 				msgtype: "m.text", | ||||||
| 				body: "\u26a0 Matrix event not delivered to Discord. See formatted content for full details.", | 				body: "\u26a0 Matrix event not delivered to Discord. See formatted content for full details.", | ||||||
| 				format: "org.matrix.custom.html", | 				format: "org.matrix.custom.html", | ||||||
| 				formatted_body: "\u26a0 <strong>Matrix event not delivered to Discord</strong>" | 				formatted_body: "\u26a0 <strong>Matrix event not delivered to Discord</strong>" | ||||||
| 					+ `<br>Event type: ${type}` | 					+ `<br>Event type: ${type}` | ||||||
| 					+ `<br>${e.toString()}` | 					+ `<br>${errorIntroLine}` | ||||||
| 					+ `<br><details><summary>Error trace</summary>` | 					+ `<br><details><summary>Error trace</summary>` | ||||||
| 					+ `<pre>${stackLines.join("\n")}</pre></details>` | 					+ `<pre>${stack}</pre></details>` | ||||||
| 					+ `<details><summary>Original payload</summary>` | 					+ `<details><summary>Original payload</summary>` | ||||||
| 					+ `<pre>${util.inspect(event, false, 4, false)}</pre></details>`, | 					+ `<pre>${util.inspect(event, false, 4, false)}</pre></details>`, | ||||||
| 				"moe.cadence.ooye.error": { | 				"moe.cadence.ooye.error": { | ||||||
| @@ -297,3 +354,5 @@ async event => { | |||||||
| 		db.prepare("UPDATE member_cache SET power_level = ? WHERE room_id = ? AND mxid = ?").run(newPower[mxid] || 0, event.room_id, mxid) | 		db.prepare("UPDATE member_cache SET power_level = ? WHERE room_id = ? AND mxid = ?").run(newPower[mxid] || 0, event.room_id, mxid) | ||||||
| 	} | 	} | ||||||
| })) | })) | ||||||
|  |  | ||||||
|  | module.exports.stringifyErrorStack = stringifyErrorStack | ||||||
|   | |||||||
							
								
								
									
										18
									
								
								test/test.js
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								test/test.js
									
									
									
									
									
								
							| @@ -29,7 +29,7 @@ reg.ooye.bridge_origin = "https://bridge.example.org" | |||||||
| const sync = new HeatSync({watchFS: false}) | const sync = new HeatSync({watchFS: false}) | ||||||
|  |  | ||||||
| const discord = { | const discord = { | ||||||
| 	// @ts-ignore - only ignore guilds, because my data dump is missing random properties | 	// @ts-ignore - ignore guilds, because my data dump is missing random properties | ||||||
| 	guilds: new Map([ | 	guilds: new Map([ | ||||||
| 		[data.guild.general.id, data.guild.general], | 		[data.guild.general.id, data.guild.general], | ||||||
| 		[data.guild.fna.id, data.guild.fna], | 		[data.guild.fna.id, data.guild.fna], | ||||||
| @@ -43,6 +43,7 @@ const discord = { | |||||||
| 	application: { | 	application: { | ||||||
| 		id: "684280192553844747" | 		id: "684280192553844747" | ||||||
| 	}, | 	}, | ||||||
|  | 	// @ts-ignore - ignore channels, because my data dump is missing random properties | ||||||
| 	channels: new Map([ | 	channels: new Map([ | ||||||
| 		[data.channel.general.id, data.channel.general], | 		[data.channel.general.id, data.channel.general], | ||||||
| 		[data.channel.updates.id, data.channel.updates], | 		[data.channel.updates.id, data.channel.updates], | ||||||
| @@ -127,6 +128,13 @@ file._actuallyUploadDiscordFileToMxc = function(url, res) { throw new Error(`Not | |||||||
|  |  | ||||||
| 	require("./addbot.test") | 	require("./addbot.test") | ||||||
| 	require("../src/db/orm.test") | 	require("../src/db/orm.test") | ||||||
|  | 	require("../src/web/server.test") | ||||||
|  | 	require("../src/web/routes/download-discord.test") | ||||||
|  | 	require("../src/web/routes/download-matrix.test") | ||||||
|  | 	require("../src/web/routes/guild.test") | ||||||
|  | 	require("../src/web/routes/guild-settings.test") | ||||||
|  | 	require("../src/web/routes/link.test") | ||||||
|  | 	require("../src/web/routes/log-in-with-matrix.test") | ||||||
| 	require("../src/discord/utils.test") | 	require("../src/discord/utils.test") | ||||||
| 	require("../src/matrix/kstate.test") | 	require("../src/matrix/kstate.test") | ||||||
| 	require("../src/matrix/api.test") | 	require("../src/matrix/api.test") | ||||||
| @@ -147,6 +155,7 @@ file._actuallyUploadDiscordFileToMxc = function(url, res) { throw new Error(`Not | |||||||
| 	require("../src/d2m/converters/remove-reaction.test") | 	require("../src/d2m/converters/remove-reaction.test") | ||||||
| 	require("../src/d2m/converters/thread-to-announcement.test") | 	require("../src/d2m/converters/thread-to-announcement.test") | ||||||
| 	require("../src/d2m/converters/user-to-mxid.test") | 	require("../src/d2m/converters/user-to-mxid.test") | ||||||
|  | 	require("../src/m2d/event-dispatcher.test") | ||||||
| 	require("../src/m2d/converters/diff-pins.test") | 	require("../src/m2d/converters/diff-pins.test") | ||||||
| 	require("../src/m2d/converters/event-to-message.test") | 	require("../src/m2d/converters/event-to-message.test") | ||||||
| 	require("../src/m2d/converters/emoji.test") | 	require("../src/m2d/converters/emoji.test") | ||||||
| @@ -157,11 +166,4 @@ file._actuallyUploadDiscordFileToMxc = function(url, res) { throw new Error(`Not | |||||||
| 	require("../src/discord/interactions/permissions.test") | 	require("../src/discord/interactions/permissions.test") | ||||||
| 	require("../src/discord/interactions/privacy.test") | 	require("../src/discord/interactions/privacy.test") | ||||||
| 	require("../src/discord/interactions/reactions.test") | 	require("../src/discord/interactions/reactions.test") | ||||||
| 	require("../src/web/server.test") |  | ||||||
| 	require("../src/web/routes/download-discord.test") |  | ||||||
| 	require("../src/web/routes/download-matrix.test") |  | ||||||
| 	require("../src/web/routes/guild.test") |  | ||||||
| 	require("../src/web/routes/guild-settings.test") |  | ||||||
| 	require("../src/web/routes/link.test") |  | ||||||
| 	require("../src/web/routes/log-in-with-matrix.test") |  | ||||||
| })() | })() | ||||||
|   | |||||||
| @@ -96,7 +96,7 @@ class Router { | |||||||
|  |  | ||||||
| const router = new Router() | const router = new Router() | ||||||
|  |  | ||||||
| passthrough.as = {router} | passthrough.as = {router, on() {}} | ||||||
|  |  | ||||||
| module.exports.router = router | module.exports.router = router | ||||||
| module.exports.test = test | module.exports.test = test | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Cadence Ember
					Cadence Ember