forked from cadence/out-of-your-element
New error trace stringifier
This commit is contained in:
parent
22c569c701
commit
dd63ac7d44
4 changed files with 85 additions and 25 deletions
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue