From 42b331135d2f444db0c49520456d325ab38617e9 Mon Sep 17 00:00:00 2001 From: bbedward Date: Mon, 8 Sep 2025 22:16:17 -0400 Subject: [PATCH 01/13] allow overriding ooye data directory --- scripts/backfill.js | 4 +++- scripts/check-migrate.js | 5 ++--- scripts/migrate-from-old-bridge.js | 3 ++- scripts/remove-old-bridged-users.js | 4 ++-- scripts/save-channel-names-to-db.js | 4 ++-- scripts/save-event-types-to-db.js | 4 ++-- scripts/setup.js | 3 ++- scripts/start-server.js | 4 ++-- scripts/wal.js | 4 ++-- src/db/database.js | 16 ++++++++++++++++ start.js | 4 ++-- 11 files changed, 37 insertions(+), 18 deletions(-) create mode 100644 src/db/database.js diff --git a/scripts/backfill.js b/scripts/backfill.js index 12d9da3..0e0e4c6 100644 --- a/scripts/backfill.js +++ b/scripts/backfill.js @@ -12,6 +12,7 @@ if (!channelID) { const assert = require("assert/strict") const sqlite = require("better-sqlite3") +const path = require("path") const backfill = new sqlite("scripts/backfill.db") backfill.prepare("CREATE TABLE IF NOT EXISTS backfill (channel_id TEXT NOT NULL, message_id INTEGER NOT NULL, PRIMARY KEY (channel_id, message_id))").run() @@ -19,9 +20,10 @@ const HeatSync = require("heatsync") const {reg} = require("../src/matrix/read-registration") const passthrough = require("../src/passthrough") +const {getDatabase} = require("../src/db/database") const sync = new HeatSync({watchFS: false}) -const db = new sqlite("ooye.db") +const db = getDatabase() Object.assign(passthrough, {sync, db}) const DiscordClient = require("../src/d2m/discord-client") diff --git a/scripts/check-migrate.js b/scripts/check-migrate.js index 04a4402..dd31ec6 100755 --- a/scripts/check-migrate.js +++ b/scripts/check-migrate.js @@ -4,10 +4,9 @@ // Trigger the database migration flow and exit after committing. // You can use this to run migrations locally and check the result using sqlitebrowser. -const sqlite = require("better-sqlite3") - const passthrough = require("../src/passthrough") -const db = new sqlite("ooye.db") +const {getDatabase} = require("../src/db/database") +const db = getDatabase() const migrate = require("../src/db/migrate") Object.assign(passthrough, {db}) diff --git a/scripts/migrate-from-old-bridge.js b/scripts/migrate-from-old-bridge.js index 36cf884..5a71ab0 100755 --- a/scripts/migrate-from-old-bridge.js +++ b/scripts/migrate-from-old-bridge.js @@ -16,7 +16,8 @@ const oldAT = reg.old_bridge.as_token const newAT = reg.as_token const oldDB = new sqlite(reg.old_bridge.database) -const db = new sqlite("ooye.db") +const {getDatabase} = require("../src/db/database") +const db = getDatabase() db.exec(`CREATE TABLE IF NOT EXISTS half_shot_migration ( discord_channel TEXT NOT NULL, diff --git a/scripts/remove-old-bridged-users.js b/scripts/remove-old-bridged-users.js index d8910bd..8252893 100644 --- a/scripts/remove-old-bridged-users.js +++ b/scripts/remove-old-bridged-users.js @@ -3,8 +3,8 @@ const HeatSync = require("heatsync") const sync = new HeatSync({watchFS: false}) -const sqlite = require("better-sqlite3") -const db = new sqlite("db/ooye.db") +const {getDatabase} = require("../src/db/database") +const db = getDatabase() const passthrough = require("../src/passthrough") Object.assign(passthrough, {db, sync}) diff --git a/scripts/save-channel-names-to-db.js b/scripts/save-channel-names-to-db.js index 1f36a73..e90e567 100755 --- a/scripts/save-channel-names-to-db.js +++ b/scripts/save-channel-names-to-db.js @@ -1,12 +1,12 @@ #!/usr/bin/env node // @ts-check -const sqlite = require("better-sqlite3") const HeatSync = require("heatsync") const {reg} = require("../src/matrix/read-registration") const passthrough = require("../src/passthrough") -const db = new sqlite("ooye.db") +const {getDatabase} = require("../src/db/database") +const db = getDatabase() const sync = new HeatSync({watchFS: false}) diff --git a/scripts/save-event-types-to-db.js b/scripts/save-event-types-to-db.js index edfbb9c..dd84866 100755 --- a/scripts/save-event-types-to-db.js +++ b/scripts/save-event-types-to-db.js @@ -1,11 +1,11 @@ #!/usr/bin/env node // @ts-check -const sqlite = require("better-sqlite3") const HeatSync = require("heatsync") const passthrough = require("../src/passthrough") -const db = new sqlite("ooye.db") +const {getDatabase} = require("../src/db/database") +const db = getDatabase() const sync = new HeatSync({watchFS: false}) diff --git a/scripts/setup.js b/scripts/setup.js index 6bff293..b980ef1 100644 --- a/scripts/setup.js +++ b/scripts/setup.js @@ -34,7 +34,8 @@ if (fs.existsSync("db")) { } const passthrough = require("../src/passthrough") -const db = new sqlite("ooye.db") +const {getDatabase} = require("../src/db/database") +const db = getDatabase() const migrate = require("../src/db/migrate") const sync = new HeatSync({watchFS: false}) diff --git a/scripts/start-server.js b/scripts/start-server.js index 0d4753a..86e11ca 100755 --- a/scripts/start-server.js +++ b/scripts/start-server.js @@ -4,13 +4,13 @@ const {createServer} = require("http") const EventEmitter = require("events") const {createApp, createRouter, toNodeListener} = require("h3") -const sqlite = require("better-sqlite3") const migrate = require("../src/db/migrate") const HeatSync = require("heatsync") const {reg} = require("../src/matrix/read-registration") const passthrough = require("../src/passthrough") -const db = new sqlite("ooye.db") +const {getDatabase} = require("../src/db/database") +const db = getDatabase() const sync = new HeatSync() diff --git a/scripts/wal.js b/scripts/wal.js index 625f2ba..3f8cec9 100755 --- a/scripts/wal.js +++ b/scripts/wal.js @@ -1,7 +1,7 @@ #!/usr/bin/env node // @ts-check -const sqlite = require("better-sqlite3") -const db = new sqlite("ooye.db", {fileMustExist: true}) +const {getDatabase} = require("../src/db/database") +const db = getDatabase({fileMustExist: true}) db.pragma("journal_mode = wal") db.close() diff --git a/src/db/database.js b/src/db/database.js new file mode 100644 index 0000000..861fda8 --- /dev/null +++ b/src/db/database.js @@ -0,0 +1,16 @@ +// @ts-check + +const sqlite = require("better-sqlite3") +const path = require("path") + +/** + * Create a new SQLite database instance + * @param {import("better-sqlite3").Options} [options] - SQLite options + * @returns {import("better-sqlite3").Database} Database instance + */ +function getDatabase(options = {}) { + const dataDir = process.env.OOYE_DATA_DIR || process.cwd() + return new sqlite(path.join(dataDir, "ooye.db"), options) +} + +module.exports = {getDatabase} \ No newline at end of file diff --git a/start.js b/start.js index ca6212b..66fb920 100755 --- a/start.js +++ b/start.js @@ -2,13 +2,13 @@ // @ts-check const fs = require("fs") -const sqlite = require("better-sqlite3") const migrate = require("./src/db/migrate") const HeatSync = require("heatsync") const {reg} = require("./src/matrix/read-registration") const passthrough = require("./src/passthrough") -const db = new sqlite("ooye.db") +const {getDatabase} = require("./src/db/database") +const db = getDatabase() const sync = new HeatSync({watchFunction: fs.watchFile}) From e19703ef03d471f7ce9f2cb441eaf928a9bd72ba Mon Sep 17 00:00:00 2001 From: bbedward Date: Mon, 8 Sep 2025 22:21:13 -0400 Subject: [PATCH 02/13] configurable read_only_room_events_default_power --- src/d2m/actions/create-room.js | 8 +++++--- src/d2m/actions/register-user.js | 2 +- src/matrix/read-registration.js | 3 ++- src/types.d.ts | 1 + 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/d2m/actions/create-room.js b/src/d2m/actions/create-room.js index ff5782d..07c7237 100644 --- a/src/d2m/actions/create-room.js +++ b/src/d2m/actions/create-room.js @@ -40,7 +40,9 @@ const PRIVACY_ENUMS = { const DEFAULT_PRIVACY_LEVEL = 0 -const READ_ONLY_ROOM_EVENTS_DEFAULT_POWER = 50 +function getReadOnlyRoomEventsDefaultPower() { + return reg.ooye.read_only_room_events_default_power ?? 50 +} /** @type {Map>} channel ID -> Promise */ const inflightRoomCreate = new Map() @@ -148,7 +150,7 @@ async function channelToKState(channel, guild, di) { "m.room.join_rules/": join_rules, /** @type {Ty.Event.M_Power_Levels} */ "m.room.power_levels/": { - events_default: everyoneCanSend ? 0 : READ_ONLY_ROOM_EVENTS_DEFAULT_POWER, + events_default: everyoneCanSend ? 0 : getReadOnlyRoomEventsDefaultPower(), events: { "m.reaction": 0, "m.room.redaction": 0 // only affects redactions of own events, required to be able to un-react @@ -559,7 +561,7 @@ async function createAllForGuild(guildID) { } module.exports.DEFAULT_PRIVACY_LEVEL = DEFAULT_PRIVACY_LEVEL -module.exports.READ_ONLY_ROOM_EVENTS_DEFAULT_POWER = READ_ONLY_ROOM_EVENTS_DEFAULT_POWER +module.exports.getReadOnlyRoomEventsDefaultPower = getReadOnlyRoomEventsDefaultPower module.exports.PRIVACY_ENUMS = PRIVACY_ENUMS module.exports.createRoom = createRoom module.exports.ensureRoom = ensureRoom diff --git a/src/d2m/actions/register-user.js b/src/d2m/actions/register-user.js index 674853a..8f2a9b5 100644 --- a/src/d2m/actions/register-user.js +++ b/src/d2m/actions/register-user.js @@ -165,7 +165,7 @@ function memberToPowerLevel(user, member, guild, channel) { /* PL 50 = if room is read-only but the user has been specially allowed to send messages */ const everyoneCanSend = utils.hasPermission(everyonePermissions, DiscordTypes.PermissionFlagsBits.SendMessages) const userCanSend = utils.hasPermission(permissions, DiscordTypes.PermissionFlagsBits.SendMessages) - if (!everyoneCanSend && userCanSend) return createRoom.READ_ONLY_ROOM_EVENTS_DEFAULT_POWER + if (!everyoneCanSend && userCanSend) return createRoom.getReadOnlyRoomEventsDefaultPower() /* PL 20 = Mention Everyone for technical reasons. */ const everyoneCanMentionEveryone = utils.hasPermission(everyonePermissions, DiscordTypes.PermissionFlagsBits.MentionEveryone) const userCanMentionEveryone = utils.hasPermission(permissions, DiscordTypes.PermissionFlagsBits.MentionEveryone) diff --git a/src/matrix/read-registration.js b/src/matrix/read-registration.js index d126851..6c713ba 100644 --- a/src/matrix/read-registration.js +++ b/src/matrix/read-registration.js @@ -57,7 +57,8 @@ function getTemplateRegistration(serverName) { max_file_size: 5000000, content_length_workaround: false, include_user_id_in_mxid: false, - invite: [] + invite: [], + read_only_room_events_default_power: 50 } } } diff --git a/src/types.d.ts b/src/types.d.ts index 37da633..e2f45ae 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -31,6 +31,7 @@ export type AppServiceRegistrationConfig = { discord_origin?: string discord_cdn_origin?: string, web_password: string + read_only_room_events_default_power?: number } old_bridge?: { as_token: string From 0f8fee2d33525e88b15d04bbb9347fcc6ff4f045 Mon Sep 17 00:00:00 2001 From: bbedward Date: Mon, 8 Sep 2025 22:24:42 -0400 Subject: [PATCH 03/13] fix type error --- src/matrix/read-registration.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/matrix/read-registration.js b/src/matrix/read-registration.js index 6c713ba..3e7e358 100644 --- a/src/matrix/read-registration.js +++ b/src/matrix/read-registration.js @@ -58,7 +58,6 @@ function getTemplateRegistration(serverName) { content_length_workaround: false, include_user_id_in_mxid: false, invite: [], - read_only_room_events_default_power: 50 } } } From fd1bf3801b8b72894303c0fbccb40b3058a614ca Mon Sep 17 00:00:00 2001 From: bbedward Date: Mon, 8 Sep 2025 22:33:21 -0400 Subject: [PATCH 04/13] also allow data dir for registration.yaml --- src/matrix/read-registration.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/matrix/read-registration.js b/src/matrix/read-registration.js index 3e7e358..bb93c5c 100644 --- a/src/matrix/read-registration.js +++ b/src/matrix/read-registration.js @@ -5,7 +5,8 @@ const crypto = require("crypto") const assert = require("assert").strict const path = require("path") -const registrationFilePath = path.join(process.cwd(), "registration.yaml") +const dataDir = process.env.OOYE_DATA_DIR || process.cwd() +const registrationFilePath = path.join(dataDir, "registration.yaml") /** @param {import("../types").AppServiceRegistrationConfig} reg */ function checkRegistration(reg) { @@ -57,7 +58,7 @@ function getTemplateRegistration(serverName) { max_file_size: 5000000, content_length_workaround: false, include_user_id_in_mxid: false, - invite: [], + invite: [] } } } From 389150616395f0fd98b45ad0e13ad531e2ae7ff4 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Tue, 7 Oct 2025 00:46:44 -0500 Subject: [PATCH 05/13] Roll back snowtransfer to avoid issue with pins --- package-lock.json | 28 ++++++++++++++++++++-------- package.json | 2 +- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index aa7822f..d82b914 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,7 +35,7 @@ "lru-cache": "^11.0.2", "prettier-bytes": "^1.0.4", "sharp": "^0.33.4", - "snowtransfer": "^0.15.0", + "snowtransfer": "^0.14.2", "stream-mime-type": "^1.0.2", "try-to-catch": "^3.0.1", "uqr": "^0.1.2", @@ -1464,6 +1464,18 @@ "node": ">=22.0.0" } }, + "node_modules/cloudstorm/node_modules/snowtransfer": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/snowtransfer/-/snowtransfer-0.15.0.tgz", + "integrity": "sha512-kEDGKtFiH5nSkHsDZonEUuDx99lUasJoZ7AGrgvE8HzVG59vjvqc//C+pjWj4DuJqTj4Q+Z1L/M/MYNim8F2VA==", + "license": "MIT", + "dependencies": { + "discord-api-types": "^0.38.21" + }, + "engines": { + "node": ">=16.15.0" + } + }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", @@ -2719,12 +2731,12 @@ } }, "node_modules/snowtransfer": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/snowtransfer/-/snowtransfer-0.15.0.tgz", - "integrity": "sha512-kEDGKtFiH5nSkHsDZonEUuDx99lUasJoZ7AGrgvE8HzVG59vjvqc//C+pjWj4DuJqTj4Q+Z1L/M/MYNim8F2VA==", + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/snowtransfer/-/snowtransfer-0.14.2.tgz", + "integrity": "sha512-Fi8OdRmaIgeCj58oVej+tQAoY2I+Xp/6PAYV8X93jE/2E6Anc87SbTbDV6WZXCnuzTQz3gty8JOGz02qI7Qs9A==", "license": "MIT", "dependencies": { - "discord-api-types": "^0.38.21" + "discord-api-types": "^0.38.8" }, "engines": { "node": ">=16.15.0" @@ -3076,9 +3088,9 @@ } }, "node_modules/tar-fs": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz", - "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", "license": "MIT", "dependencies": { "chownr": "^1.1.1", diff --git a/package.json b/package.json index 2fb21f2..f722066 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "lru-cache": "^11.0.2", "prettier-bytes": "^1.0.4", "sharp": "^0.33.4", - "snowtransfer": "^0.15.0", + "snowtransfer": "^0.14.2", "stream-mime-type": "^1.0.2", "try-to-catch": "^3.0.1", "uqr": "^0.1.2", From 7905802825c6dd03b26770fd32f03d1b77b6bf82 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Tue, 7 Oct 2025 00:48:06 -0500 Subject: [PATCH 06/13] Allow customising port in setup --- scripts/setup.js | 23 ++++++++++++++++++----- src/m2d/actions/setup-emojis.js | 3 +-- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/scripts/setup.js b/scripts/setup.js index 6bff293..ecef03d 100644 --- a/scripts/setup.js +++ b/scripts/setup.js @@ -120,16 +120,28 @@ function defineEchoHandler() { /** @type {string} */ // @ts-ignore const serverOrigin = await serverOriginPrompt.run() + console.log("OOYE has its own web server. It needs to be accessible on the public internet.") + console.log("What port would you like OOYE to use? You can connect your reverse proxy to this port later.") + /** @type {{socket: string | number}} */ + const portResponse = await prompt({ + type: "input", + name: "socket", + message: "Web server port", + initial: "6693" + }) + portResponse.socket = +portResponse.socket || portResponse.socket // convert to number if numeric + const app = createApp() app.use(defineEchoHandler()) const server = createServer(toNodeListener(app)) - await server.listen(6693) + await server.listen(portResponse.socket) - console.log("OOYE has its own web server. It needs to be accessible on the public internet.") - console.log("You need to enter a public URL where you will be able to host this web server.") - console.log("OOYE listens on localhost:6693, so you will probably have to set up a reverse proxy.") + console.log("Now you need to enter a public URL that OOYE's web server will live on.") + console.log("Set up your reverse proxy so that this URL accesses OOYE.") console.log("Examples: https://gitdab.com/cadence/out-of-your-element/src/branch/main/docs/get-started.md#appendix") - console.log("Now listening on port 6693. Feel free to send some test requests.") + if (typeof portResponse.socket === "number") { + console.log(`Now listening on http://localhost:${portResponse.socket}. Feel free to send some test requests.`) + } /** @type {{bridge_origin: string}} */ const bridgeOriginResponse = await prompt({ type: "input", @@ -255,6 +267,7 @@ function defineEchoHandler() { reg = { ...template, url: bridgeOriginResponse.bridge_origin, + ...portResponse, ooye: { ...template.ooye, ...bridgeOriginResponse, diff --git a/src/m2d/actions/setup-emojis.js b/src/m2d/actions/setup-emojis.js index ba2c045..1be1d2d 100644 --- a/src/m2d/actions/setup-emojis.js +++ b/src/m2d/actions/setup-emojis.js @@ -5,9 +5,8 @@ const {join} = require("path") const passthrough = require("../../passthrough") -const {id} = require("../../../addbot") - async function setupEmojis() { + const {id} = require("../../../addbot") const {discord, db} = passthrough const emojis = await discord.snow.assets.getAppEmojis(id) for (const name of ["L1", "L2"]) { From 7916f82b55fd81d95abfdea328b5decc75588492 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Tue, 7 Oct 2025 14:09:42 -0500 Subject: [PATCH 07/13] Change thread started message (closes #61) --- src/d2m/converters/thread-to-announcement.js | 3 --- src/d2m/converters/thread-to-announcement.test.js | 10 ---------- 2 files changed, 13 deletions(-) diff --git a/src/d2m/converters/thread-to-announcement.js b/src/d2m/converters/thread-to-announcement.js index 11a067f..98b8f12 100644 --- a/src/d2m/converters/thread-to-announcement.js +++ b/src/d2m/converters/thread-to-announcement.js @@ -32,13 +32,10 @@ async function threadToAnnouncement(parentRoomID, threadRoomID, creatorMxid, thr const template = creatorMxid ? "started a thread:" : "Thread started:" const via = await mxUtils.getViaServersQuery(threadRoomID, di.api) let body = `${template} ${thread.name} https://matrix.to/#/${threadRoomID}?${via.toString()}` - let html = `${template} ${thread.name}` return { msgtype, body, - format: "org.matrix.custom.html", - formatted_body: html, "m.mentions": {}, ...context } diff --git a/src/d2m/converters/thread-to-announcement.test.js b/src/d2m/converters/thread-to-announcement.test.js index 471cd94..3d5d1eb 100644 --- a/src/d2m/converters/thread-to-announcement.test.js +++ b/src/d2m/converters/thread-to-announcement.test.js @@ -55,8 +55,6 @@ test("thread2announcement: no known creator, no branched from event", async t => t.deepEqual(content, { msgtype: "m.text", body: "Thread started: test thread https://matrix.to/#/!thread?via=cadence.moe&via=matrix.org", - format: "org.matrix.custom.html", - formatted_body: `Thread started: test thread`, "m.mentions": {} }) }) @@ -69,8 +67,6 @@ test("thread2announcement: known creator, no branched from event", async t => { t.deepEqual(content, { msgtype: "m.emote", body: "started a thread: test thread https://matrix.to/#/!thread?via=cadence.moe&via=matrix.org", - format: "org.matrix.custom.html", - formatted_body: `started a thread: test thread`, "m.mentions": {} }) }) @@ -95,8 +91,6 @@ test("thread2announcement: no known creator, branched from discord event", async t.deepEqual(content, { msgtype: "m.text", body: "Thread started: test thread https://matrix.to/#/!thread?via=cadence.moe&via=matrix.org", - format: "org.matrix.custom.html", - formatted_body: `Thread started: test thread`, "m.mentions": {}, "m.relates_to": { "m.in_reply_to": { @@ -126,8 +120,6 @@ test("thread2announcement: known creator, branched from discord event", async t t.deepEqual(content, { msgtype: "m.emote", body: "started a thread: test thread https://matrix.to/#/!thread?via=cadence.moe&via=matrix.org", - format: "org.matrix.custom.html", - formatted_body: `started a thread: test thread`, "m.mentions": {}, "m.relates_to": { "m.in_reply_to": { @@ -157,8 +149,6 @@ test("thread2announcement: no known creator, branched from matrix event", async t.deepEqual(content, { msgtype: "m.text", body: "Thread started: test thread https://matrix.to/#/!thread?via=cadence.moe&via=matrix.org", - format: "org.matrix.custom.html", - formatted_body: `Thread started: test thread`, "m.mentions": { user_ids: ["@cadence:cadence.moe"] }, From 5b7433de3260a0504df94f7dcda6a0f0a8ed2884 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Tue, 7 Oct 2025 14:09:50 -0500 Subject: [PATCH 08/13] Make tests time zone independent --- src/d2m/converters/message-to-event.js | 2 +- src/types.d.ts | 1 + test/test.js | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/d2m/converters/message-to-event.js b/src/d2m/converters/message-to-event.js index 1d6288a..f494123 100644 --- a/src/d2m/converters/message-to-event.js +++ b/src/d2m/converters/message-to-event.js @@ -611,7 +611,7 @@ async function messageToEvent(message, guild, options = {}, di) { const event = invite.guild_scheduled_event if (!event) continue // the event ID provided was not valid - const formatter = new Intl.DateTimeFormat("en-NZ", {month: "long", day: "numeric", hour: "numeric", minute: "2-digit", timeZoneName: "shortGeneric"}) // 9 June at 3:00 pm NZT + const formatter = new Intl.DateTimeFormat("en-NZ", {month: "long", day: "numeric", hour: "numeric", minute: "2-digit", timeZoneName: "shortGeneric", timeZone: reg.ooye.time_zone}) // 9 June at 3:00 pm NZT const rep = new mxUtils.MatrixStringBuilder() // Add time diff --git a/src/types.d.ts b/src/types.d.ts index 37da633..27dfddf 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -31,6 +31,7 @@ export type AppServiceRegistrationConfig = { discord_origin?: string discord_cdn_origin?: string, web_password: string + time_zone?: string } old_bridge?: { as_token: string diff --git a/test/test.js b/test/test.js index 3695a84..233fd94 100644 --- a/test/test.js +++ b/test/test.js @@ -25,6 +25,7 @@ reg.namespaces = { aliases: [{regex: "#_ooye_.*:cadence.moe", exclusive: true}] } reg.ooye.bridge_origin = "https://bridge.example.org" +reg.ooye.time_zone = "Pacific/Auckland" const sync = new HeatSync({watchFS: false}) From cffd3c9f2ea0dc1c6be04f28501f4b59ee88caa2 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Fri, 10 Oct 2025 12:26:01 -0600 Subject: [PATCH 09/13] Fix converting discord channel links --- package-lock.json | 8 +-- package.json | 2 +- src/d2m/converters/message-to-event.js | 4 +- src/d2m/converters/message-to-event.test.js | 38 ++++++++++++++ test/data.js | 57 +++++++++++++++++++++ 5 files changed, 102 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index d82b914..fda73e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "AGPL-3.0-or-later", "dependencies": { "@chriscdn/promise-semaphore": "^3.0.1", - "@cloudrac3r/discord-markdown": "^2.6.6", + "@cloudrac3r/discord-markdown": "^2.6.7", "@cloudrac3r/giframe": "^0.4.3", "@cloudrac3r/html-template-tag": "^5.0.1", "@cloudrac3r/in-your-element": "^1.1.1", @@ -225,9 +225,9 @@ } }, "node_modules/@cloudrac3r/discord-markdown": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@cloudrac3r/discord-markdown/-/discord-markdown-2.6.6.tgz", - "integrity": "sha512-4FNO7WmACPvcTrQjeLQLr9WRuP7JDUVUGFrRJvmAjiMs2UlUAsShfSRuU2SCqz3QqmX8vyJ06wy2hkjTTyRtbw==", + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/@cloudrac3r/discord-markdown/-/discord-markdown-2.6.7.tgz", + "integrity": "sha512-bWLmBYWaNEDcQfZHDz4jaAxLKA9161ruEnHo3ms6kfRw8uYku/Uz7U1xTmQ2dQF/q1PiuBvM9I37pLiotlQj8A==", "license": "MIT", "dependencies": { "simple-markdown": "^0.7.3" diff --git a/package.json b/package.json index f722066..85530c1 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ }, "dependencies": { "@chriscdn/promise-semaphore": "^3.0.1", - "@cloudrac3r/discord-markdown": "^2.6.6", + "@cloudrac3r/discord-markdown": "^2.6.7", "@cloudrac3r/giframe": "^0.4.3", "@cloudrac3r/html-template-tag": "^5.0.1", "@cloudrac3r/in-your-element": "^1.1.1", diff --git a/src/d2m/converters/message-to-event.js b/src/d2m/converters/message-to-event.js index f494123..30a20fe 100644 --- a/src/d2m/converters/message-to-event.js +++ b/src/d2m/converters/message-to-event.js @@ -408,13 +408,13 @@ async function messageToEvent(message, guild, options = {}, di) { async function transformParsedVia(parsed) { for (const node of parsed) { - if (node.type === "discordChannel") { + if (node.type === "discordChannel" || node.type === "discordChannelLink") { node.row = select("channel_room", ["room_id", "name", "nick"], {channel_id: node.id}).get() if (node.row?.room_id) { node.via = await getViaServersMemo(node.row.room_id) } } - ;for (const maybeChildNodesArray of [node, node.content, node.items]) { + for (const maybeChildNodesArray of [node, node.content, node.items]) { if (Array.isArray(maybeChildNodesArray)) { await transformParsedVia(maybeChildNodesArray) } diff --git a/src/d2m/converters/message-to-event.test.js b/src/d2m/converters/message-to-event.test.js index fc933e3..ee4ec03 100644 --- a/src/d2m/converters/message-to-event.test.js +++ b/src/d2m/converters/message-to-event.test.js @@ -100,6 +100,44 @@ test("message2event: simple room mention", async t => { t.equal(called, 2, "should call getStateEvent and getJoinedMembers once each") }) +test("message2event: simple room link", async t => { + let called = 0 + const events = await messageToEvent(data.message.simple_room_link, data.guild.general, {}, { + api: { + async getStateEvent(roomID, type, key) { + called++ + t.equal(roomID, "!BnKuBPCvyfOkhcUjEu:cadence.moe") + t.equal(type, "m.room.power_levels") + t.equal(key, "") + return { + users: { + "@_ooye_bot:cadence.moe": 100 + } + } + }, + async getJoinedMembers(roomID) { + called++ + t.equal(roomID, "!BnKuBPCvyfOkhcUjEu:cadence.moe") + return { + joined: { + "@_ooye_bot:cadence.moe": {display_name: null, avatar_url: null}, + "@user:matrix.org": {display_name: null, avatar_url: null} + } + } + } + } + }) + t.deepEqual(events, [{ + $type: "m.room.message", + "m.mentions": {}, + msgtype: "m.text", + body: "#worm-farm", + format: "org.matrix.custom.html", + formatted_body: '#worm-farm' + }]) + t.equal(called, 2, "should call getStateEvent and getJoinedMembers once each") +}) + test("message2event: nicked room mention", async t => { let called = 0 const events = await messageToEvent(data.message.nicked_room_mention, data.guild.general, {}, { diff --git a/test/data.js b/test/data.js index a8ff8a8..e64b9c2 100644 --- a/test/data.js +++ b/test/data.js @@ -1398,6 +1398,63 @@ module.exports = { attachments: [], guild_id: "112760669178241024" }, + simple_room_link: { + type: 0, + tts: false, + timestamp: "2023-07-10T20:04:25.939000+00:00", + referenced_message: null, + pinned: false, + nonce: "1128054139385806848", + mentions: [], + mention_roles: [], + mention_everyone: false, + member: { + roles: [ + "112767366235959296", "118924814567211009", + "204427286542417920", "199995902742626304", + "222168467627835392", "238028326281805825", + "259806643414499328", "265239342648131584", + "271173313575780353", "287733611912757249", + "225744901915148298", "305775031223320577", + "318243902521868288", "348651574924541953", + "349185088157777920", "378402925128712193", + "392141548932038658", "393912152173576203", + "482860581670486028", "495384759074160642", + "638988388740890635", "373336013109461013", + "530220455085473813", "454567553738473472", + "790724320824655873", "1123518980456452097", + "1040735082610167858", "695946570482450442", + "1123460940935991296", "849737964090556488" + ], + premium_since: null, + pending: false, + nick: null, + mute: false, + joined_at: "2015-11-11T09:55:40.321000+00:00", + flags: 0, + deaf: false, + communication_disabled_until: null, + avatar: null + }, + id: "1128054143064494233", + flags: 0, + embeds: [], + edited_timestamp: null, + content: "https://discord.com/channels/112760669178241024/1100319550446252084", + components: [], + channel_id: "266767590641238027", + author: { + username: "kumaccino", + public_flags: 128, + id: "113340068197859328", + global_name: "kumaccino", + discriminator: "0", + avatar_decoration: null, + avatar: "b48302623a12bc7c59a71328f72ccb39" + }, + attachments: [], + guild_id: "112760669178241024" + }, nicked_room_mention: { type: 0, tts: false, From 3de762d42852174c2876845dd4eacdb1698a3647 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Sun, 12 Oct 2025 12:17:20 -0600 Subject: [PATCH 10/13] Fix stickers that don't provide content type --- src/matrix/api.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/matrix/api.js b/src/matrix/api.js index 41af63f..709d70c 100644 --- a/src/matrix/api.js +++ b/src/matrix/api.js @@ -384,7 +384,9 @@ async function getMedia(mxc, init = {}) { }, ...init }) - assert(res.body) + if (init.method !== "HEAD") { + assert(res.body) + } // @ts-ignore return res } From d4f4664c25454ffe50672264737be83afde824ba Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Thu, 23 Oct 2025 23:05:23 +1100 Subject: [PATCH 11/13] Fix retrying m->d message deletions --- src/m2d/actions/redact.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/m2d/actions/redact.js b/src/m2d/actions/redact.js index 1f6cef8..1d3aa67 100644 --- a/src/m2d/actions/redact.js +++ b/src/m2d/actions/redact.js @@ -13,10 +13,12 @@ const utils = sync.require("../converters/utils") */ async function deleteMessage(event) { const rows = from("event_message").join("message_channel", "message_id").select("channel_id", "message_id").where({event_id: event.redacts}).all() + if (!rows.length) return for (const row of rows) { - db.prepare("DELETE FROM message_channel WHERE message_id = ?").run(row.message_id) await discord.snow.channel.deleteMessage(row.channel_id, row.message_id, event.content.reason) + db.prepare("DELETE FROM event_message WHERE message_id = ?").run(row.message_id) } + db.prepare("DELETE FROM message_channel WHERE message_id = ?").run(rows[0].message_id) } /** From 255e166e8cd7ff95f2576ab7b55c46bc93b6c290 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Fri, 31 Oct 2025 16:22:32 +1300 Subject: [PATCH 12/13] Better message when remote emojis unavailable --- src/m2d/actions/emoji-sheet.js | 6 ++++++ src/m2d/converters/event-to-message.js | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/m2d/actions/emoji-sheet.js b/src/m2d/actions/emoji-sheet.js index a63f0b0..ed5ab88 100644 --- a/src/m2d/actions/emoji-sheet.js +++ b/src/m2d/actions/emoji-sheet.js @@ -7,6 +7,8 @@ const {sync} = require("../../passthrough") const emojiSheetConverter = sync.require("../converters/emoji-sheet") /** @type {import("../../matrix/api")} */ const api = sync.require("../../matrix/api") +/** @type {import("../../matrix/mreq")} */ +const mreq = sync.require("../../matrix/mreq") /** * Downloads the emoji from the web and converts to uncompressed PNG data. @@ -19,6 +21,10 @@ async function getAndConvertEmoji(mxc) { // If we were using connection pooling, we would be forced to download the entire GIF. // So we set no agent to ensure we are not connection pooling. const res = await api.getMedia(mxc, {signal: abortController.signal}) + if (res.status !== 200) { + const root = await res.json() + throw new mreq.MatrixServerError(root, {mxc}) + } const readable = stream.Readable.fromWeb(res.body) return emojiSheetConverter.convertImageStream(readable, () => { abortController.abort() diff --git a/src/m2d/converters/event-to-message.js b/src/m2d/converters/event-to-message.js index 3cf08cf..61525e2 100644 --- a/src/m2d/converters/event-to-message.js +++ b/src/m2d/converters/event-to-message.js @@ -11,6 +11,7 @@ const entities = require("entities") const passthrough = require("../../passthrough") const {sync, db, discord, select, from} = passthrough +const {reg} = require("../../matrix/read-registration") /** @type {import("../converters/utils")} */ const mxUtils = sync.require("../converters/utils") /** @type {import("../../discord/utils")} */ @@ -238,7 +239,8 @@ function convertEmoji(mxcUrl, nameForGuess, allowSpriteSheetIndicator, allowLink if (!found) row = null } // Or, if we don't have an emoji right now, we search for the name instead. - if (!row && nameForGuess) { + const isLocalMxc = mxcUrl?.match(/^mxc:\/\/([^/]+)/)?.[1] === reg.ooye.server_name + if (!row && nameForGuess && isLocalMxc) { const nameForGuessLower = nameForGuess.toLowerCase() for (const guild of discord.guilds.values()) { /** @type {{name: string, id: string, animated: number}[]} */ From d95a1143774e69b8619d2e00e2d70c5186b99c8e Mon Sep 17 00:00:00 2001 From: Elliu Date: Sun, 2 Nov 2025 07:50:16 +0000 Subject: [PATCH 13/13] Fix matrix api joinRoom() for remote rooms (#60) When using self-service mode and trying to link with a remote matrix room (room not in the same HS as the bridge user), then we need to add the "via" HSs to join the room with, or else it fails. We get it from the "m.space.child" in the "children_state" of the space hierarchy. Co-authored-by: Cadence Ember Reviewed-on: https://gitdab.com/cadence/out-of-your-element/pulls/60 Co-authored-by: Elliu Co-committed-by: Elliu --- jsconfig.json | 9 ++++ src/d2m/converters/message-to-event.js | 1 + src/matrix/api.js | 13 ++++-- src/matrix/api.test.js | 4 ++ src/types.d.ts | 10 ++++- src/web/routes/link.js | 34 +++++++++++---- src/web/routes/link.test.js | 60 +++++++++++++++++++++++--- test/test.js | 2 + 8 files changed, 115 insertions(+), 18 deletions(-) create mode 100644 jsconfig.json diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..4106061 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "es2024", + "module": "nodenext", + "strict": true, + "noImplicitAny": false, + "useUnknownInCatchVariables": false + } +} diff --git a/src/d2m/converters/message-to-event.js b/src/d2m/converters/message-to-event.js index 30a20fe..93e120e 100644 --- a/src/d2m/converters/message-to-event.js +++ b/src/d2m/converters/message-to-event.js @@ -207,6 +207,7 @@ async function attachmentToEvent(mentions, attachment) { * - alwaysReturnFormattedBody: false - formatted_body will be skipped if it is the same as body because the message is plaintext. if you want the formatted_body to be returned anyway, for example to merge it with another message, then set this to true. * - scanTextForMentions: true - needs to be set to false when converting forwarded messages etc which may be from a different channel that can't be scanned. * @param {{api: import("../../matrix/api"), snow?: import("snowtransfer").SnowTransfer}} di simple-as-nails dependency injection for the matrix API + * @returns {Promise<{$type: string, $sender?: string, [x: string]: any}[]>} */ async function messageToEvent(message, guild, options = {}, di) { const events = [] diff --git a/src/matrix/api.js b/src/matrix/api.js index 709d70c..edffc45 100644 --- a/src/matrix/api.js +++ b/src/matrix/api.js @@ -22,7 +22,11 @@ function path(p, mxid, otherParams = {}) { const u = new URL(p, "http://localhost") if (mxid) u.searchParams.set("user_id", mxid) for (const entry of Object.entries(otherParams)) { - if (entry[1] != undefined) { + if (Array.isArray(entry[1])) { + for (const element of entry[1]) { + u.searchParams.append(entry[0], element) + } + } else if (entry[1] != undefined) { u.searchParams.set(entry[0], entry[1]) } } @@ -62,11 +66,14 @@ async function createRoom(content) { } /** + * @param {string} roomIDOrAlias + * @param {string?} [mxid] + * @param {string[]?} [via] * @returns {Promise} room ID */ -async function joinRoom(roomIDOrAlias, mxid) { +async function joinRoom(roomIDOrAlias, mxid, via) { /** @type {Ty.R.RoomJoined} */ - const root = await mreq.mreq("POST", path(`/client/v3/join/${roomIDOrAlias}`, mxid), {}) + const root = await mreq.mreq("POST", path(`/client/v3/join/${roomIDOrAlias}`, mxid, {via}), {}) return root.room_id } diff --git a/src/matrix/api.test.js b/src/matrix/api.test.js index 82565eb..da92385 100644 --- a/src/matrix/api.test.js +++ b/src/matrix/api.test.js @@ -24,3 +24,7 @@ test("api path: real world mxid", t => { test("api path: extras number works", t => { t.equal(path(`/client/v3/rooms/!example/timestamp_to_event`, null, {ts: 1687324651120}), "/client/v3/rooms/!example/timestamp_to_event?ts=1687324651120") }) + +test("api path: multiple via params", t => { + t.equal(path(`/client/v3/rooms/!example/join`, null, {via: ["cadence.moe", "matrix.org"], ts: 1687324651120}), "/client/v3/rooms/!example/join?via=cadence.moe&via=matrix.org&ts=1687324651120") +}) diff --git a/src/types.d.ts b/src/types.d.ts index 27dfddf..c7cb006 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -149,6 +149,14 @@ export namespace Event { prev_content?: any } + export type StrippedChildStateEvent = { + type: string + state_key: string + sender: string + origin_server_ts: number + content: any + } + export type M_Room_Message = { msgtype: "m.text" | "m.emote" body: string @@ -345,7 +353,7 @@ export namespace R { export type Hierarchy = { avatar_url?: string canonical_alias?: string - children_state: {} + children_state: Event.StrippedChildStateEvent[] guest_can_join: boolean join_rule?: string name?: string diff --git a/src/web/routes/link.js b/src/web/routes/link.js index c5f404e..0afbc49 100644 --- a/src/web/routes/link.js +++ b/src/web/routes/link.js @@ -75,11 +75,15 @@ as.router.post("/api/link-space", defineEventHandler(async event => { const existing = select("guild_space", "guild_id", {}, "WHERE guild_id = ? OR space_id = ?").get(guildID, spaceID) if (existing) throw createError({status: 400, message: "Bad Request", data: `Guild ID ${guildID} or space ID ${spaceID} are already bridged and cannot be reused`}) + const inviteSender = select("invite", "mxid", {mxid: session.data.mxid, room_id: spaceID}).pluck().get() + const inviteSenderServer = inviteSender?.match(/:(.*)/)?.[1] + const via = [inviteSenderServer || ""] + // Check space exists and bridge is joined try { - await api.joinRoom(parsedBody.space_id) + await api.joinRoom(parsedBody.space_id, null, via) } catch (e) { - throw createError({status: 403, message: e.errcode, data: `${e.errcode} - ${e.message}`}) + throw createError({status: 400, message: "Unable To Join", data: `Unable to join the requested Matrix space. Please invite the bridge to the space and try again. (Server said: ${e.errcode} - ${e.message})`}) } // Check bridge has PL 100 @@ -134,19 +138,33 @@ as.router.post("/api/link", defineEventHandler(async event => { if (row) throw createError({status: 400, message: "Bad Request", data: `Channel ID ${row.channel_id} or room ID ${parsedBody.matrix} are already bridged and cannot be reused`}) // Check room is part of the guild's space - let found = false + let foundRoom = false + /** @type {string[]?} */ + let foundVia = null for await (const room of api.generateFullHierarchy(spaceID)) { - if (room.room_id === parsedBody.matrix && !room.room_type) { - found = true - break + // When finding a space during iteration, look at space's children state, because we need a `via` to join the room (when we find it later) + for (const state of room.children_state) { + if (state.type === "m.space.child" && state.state_key === parsedBody.matrix) { + foundVia = state.content.via + } } + + // When finding a room during iteration, see if it was the requested room (to confirm that the room is in the space) + if (room.room_id === parsedBody.matrix && !room.room_type) { + foundRoom = true + } + + if (foundRoom && foundVia) break } - if (!found) throw createError({status: 400, message: "Bad Request", data: "Matrix room needs to be part of the bridged space"}) + if (!foundRoom) throw createError({status: 400, message: "Bad Request", data: "Matrix room needs to be part of the bridged space"}) // Check room exists and bridge is joined try { - await api.joinRoom(parsedBody.matrix) + await api.joinRoom(parsedBody.matrix, null, foundVia) } catch (e) { + if (!foundVia) { + throw createError({status: 400, message: "Unable To Join", data: `Unable to join the requested Matrix room. Please invite the bridge to the room and try again. (Server said: ${e.errcode} - ${e.message})`}) + } throw createError({status: 403, message: e.errcode, data: `${e.errcode} - ${e.message}`}) } diff --git a/src/web/routes/link.test.js b/src/web/routes/link.test.js index 0d8d366..ffe4e5e 100644 --- a/src/web/routes/link.test.js +++ b/src/web/routes/link.test.js @@ -77,7 +77,7 @@ test("web link space: check that OOYE is joined", async t => { } } })) - t.equal(error.data, "M_FORBIDDEN - not allowed to join I guess") + t.equal(error.data, "Unable to join the requested Matrix space. Please invite the bridge to the space and try again. (Server said: M_FORBIDDEN - not allowed to join I guess)") t.equal(called, 1) }) @@ -360,7 +360,7 @@ test("web link room: check that room is part of space (not in hierarchy)", async t.equal(called, 1) }) -test("web link room: check that bridge can join room", async t => { +test("web link room: check that bridge can join room (notices lack of via and asks for invite instead)", async t => { let called = 0 const [error] = await tryToCatch(() => router.test("post", "/api/link", { sessionData: { @@ -381,7 +381,55 @@ test("web link room: check that bridge can join room", async t => { t.equal(spaceID, "!zTMspHVUBhFLLSdmnS:cadence.moe") yield { room_id: "!NDbIqNpJyPvfKRnNcr:cadence.moe", - children_state: {}, + children_state: [], + guest_can_join: false, + num_joined_members: 2 + } + /* c8 ignore next */ + } + } + })) + t.equal(error.data, "Unable to join the requested Matrix room. Please invite the bridge to the room and try again. (Server said: M_FORBIDDEN - not allowed to join I guess)") + t.equal(called, 2) +}) + +test("web link room: check that bridge can join room (uses via for join attempt)", async t => { + let called = 0 + const [error] = await tryToCatch(() => router.test("post", "/api/link", { + sessionData: { + managedGuilds: ["665289423482519565"] + }, + body: { + discord: "665310973967597573", + matrix: "!NDbIqNpJyPvfKRnNcr:cadence.moe", + guild_id: "665289423482519565" + }, + api: { + async joinRoom(roomID, _, via) { + called++ + t.deepEqual(via, ["cadence.moe", "hashi.re"]) + throw new MatrixServerError({errcode: "M_FORBIDDEN", error: "not allowed to join I guess"}) + }, + async *generateFullHierarchy(spaceID) { + called++ + t.equal(spaceID, "!zTMspHVUBhFLLSdmnS:cadence.moe") + yield { + room_id: "!NDbIqNpJyPvfKRnNcr:cadence.moe", + children_state: [], + guest_can_join: false, + num_joined_members: 2 + } + yield { + room_id: "!zTMspHVUBhFLLSdmnS:cadence.moe", + children_state: [{ + type: "m.space.child", + state_key: "!NDbIqNpJyPvfKRnNcr:cadence.moe", + sender: "@elliu:hashi.re", + content: { + via: ["cadence.moe", "hashi.re"] + }, + origin_server_ts: 0 + }], guest_can_join: false, num_joined_members: 2 } @@ -414,7 +462,7 @@ test("web link room: check that bridge has PL 100 in target room (event missing) t.equal(spaceID, "!zTMspHVUBhFLLSdmnS:cadence.moe") yield { room_id: "!NDbIqNpJyPvfKRnNcr:cadence.moe", - children_state: {}, + children_state: [], guest_can_join: false, num_joined_members: 2 } @@ -454,7 +502,7 @@ test("web link room: check that bridge has PL 100 in target room (users default) t.equal(spaceID, "!zTMspHVUBhFLLSdmnS:cadence.moe") yield { room_id: "!NDbIqNpJyPvfKRnNcr:cadence.moe", - children_state: {}, + children_state: [], guest_can_join: false, num_joined_members: 2 } @@ -494,7 +542,7 @@ test("web link room: successfully calls createRoom", async t => { t.equal(spaceID, "!zTMspHVUBhFLLSdmnS:cadence.moe") yield { room_id: "!NDbIqNpJyPvfKRnNcr:cadence.moe", - children_state: {}, + children_state: [], guest_can_join: false, num_joined_members: 2 } diff --git a/test/test.js b/test/test.js index 233fd94..b01f0ce 100644 --- a/test/test.js +++ b/test/test.js @@ -17,6 +17,8 @@ const {reg} = require("../src/matrix/read-registration") reg.ooye.discord_token = "Njg0MjgwMTkyNTUzODQ0NzQ3.Xl3zlw.baby" reg.ooye.server_origin = "https://matrix.cadence.moe" // so that tests will pass even when hard-coded reg.ooye.server_name = "cadence.moe" +reg.ooye.namespace_prefix = "_ooye_" +reg.sender_localpart = "_ooye_bot" reg.id = "baby" reg.as_token = "don't actually take authenticated actions on the server" reg.hs_token = "don't actually take authenticated actions on the server"