diff --git a/registration.example.yaml b/registration.example.yaml new file mode 100644 index 0000000..d38f5ae --- /dev/null +++ b/registration.example.yaml @@ -0,0 +1,25 @@ +id: de8c56117637cb5d9f4ac216f612dc2adb1de4c09ae8d13553f28c33a28147c7 +hs_token: [a unique 64 character hex string] +as_token: [a unique 64 character hex string] +url: http://localhost:6693 +sender_localpart: _ooye_bot +protocols: + - discord +namespaces: + users: + - exclusive: true + regex: '@_ooye_.*' + aliases: + - exclusive: true + regex: '#_ooye_.*' +rate_limited: false +ooye: + namespace_prefix: _ooye_ + max_file_size: 5000000 + server_name: [the part after the colon in your matrix id, like cadence.moe] + server_origin: [the full protocol and domain of your actual matrix server's location, with no trailing slash, like https://matrix.cadence.moe] + content_length_workaround: false + include_user_id_in_mxid: false + invite: + # uncomment this to auto-invite the named user to newly created spaces and mark them as admin (PL 100) everywhere + # - '@cadence:cadence.moe' diff --git a/scripts/setup.js b/scripts/setup.js index 13c2492..a3902be 100644 --- a/scripts/setup.js +++ b/scripts/setup.js @@ -7,7 +7,6 @@ const sqlite = require("better-sqlite3") const {scheduler} = require("timers/promises") const {isDeepStrictEqual} = require("util") const {createServer} = require("http") -const {join} = require("path") const {prompt} = require("enquirer") const Input = require("enquirer/lib/prompts/input") @@ -209,6 +208,7 @@ async function validateHomeserverOrigin(serverUrlPrompt, url) { url: bridgeOriginResponse.bridge_origin, ooye: { ...template.ooye, + ...serverNameResponse, ...bridgeOriginResponse, server_origin: serverOrigin, ...discordTokenResponse, @@ -335,8 +335,8 @@ async function validateHomeserverOrigin(serverUrlPrompt, url) { } // Upload those emojis to the chosen location db.prepare("REPLACE INTO auto_emoji (name, emoji_id, guild_id) VALUES ('_', '_', ?)").run(guild.id) - await uploadAutoEmoji(discord.snow, guild, "L1", join(__dirname, "../docs/img/L1.png")) - await uploadAutoEmoji(discord.snow, guild, "L2", join(__dirname, "../docs/img/L2.png")) + await uploadAutoEmoji(discord.snow, guild, "L1", "docs/img/L1.png") + await uploadAutoEmoji(discord.snow, guild, "L2", "docs/img/L2.png") } console.log("✅ Emojis are ready...") diff --git a/src/db/orm.test.js b/src/db/orm.test.js index a8f10f4..278723a 100644 --- a/src/db/orm.test.js +++ b/src/db/orm.test.js @@ -58,13 +58,3 @@ test("orm: from: join direction works", t => { const hasNoOwnerInner = from("sim").join("sim_proxy", "user_id", "inner").select("user_id", "proxy_owner_id").where({sim_name: "crunch_god"}).get() t.deepEqual(hasNoOwnerInner, undefined) }) - -test("orm: select unsafe works (to select complex column names that can't be type verified)", t => { - const results = from("member_cache") - .join("member_power", "mxid") - .join("channel_room", "room_id") // only include rooms that are bridged - .and("where member_power.room_id = '*' and member_cache.power_level != member_power.power_level") - .selectUnsafe("mxid", "member_cache.room_id", "member_power.power_level") - .all() - t.equal(results[0].power_level, 100) -}) diff --git a/src/discord/interactions/bridge.js b/src/discord/interactions/bridge.js new file mode 100644 index 0000000..1fbc57e --- /dev/null +++ b/src/discord/interactions/bridge.js @@ -0,0 +1,115 @@ +// @ts-check + +const DiscordTypes = require("discord-api-types/v10") +const Ty = require("../../types") +const {discord, sync, db, select, from, as} = require("../../passthrough") +const assert = require("assert/strict") + +/** @type {import("../../matrix/api")} */ +const api = sync.require("../../matrix/api") + +/** @type {Map>} spaceID -> list of rooms */ +const cache = new Map() +/** @type {Map} roomID -> spaceID */ +const reverseCache = new Map() + +// Manage clearing the cache +sync.addTemporaryListener(as, "type:m.room.name", /** @param {Ty.Event.StateOuter} event */ async event => { + if (event.state_key !== "") return + const roomID = event.room_id + const spaceID = reverseCache.get(roomID) + if (!spaceID) return + const childRooms = await cache.get(spaceID) + if (!childRooms) return + if (event.content.name) { + const found = childRooms.find(r => r.value === roomID) + if (!found) return + found.name = event.content.name + } else { + cache.set(spaceID, Promise.resolve(childRooms.filter(r => r.value !== roomID))) + reverseCache.delete(roomID) + } +}) + +// Manage adding to the cache +async function getCachedHierarchy(spaceID) { + return cache.get(spaceID) || (() => { + const entry = (async () => { + const result = await api.getFullHierarchy(spaceID) + /** @type {{name: string, value: string}[]} */ + const childRooms = [] + for (const room of result) { + if (room.name && !room.name.match(/^\[[⛓️🔊]\]/) && room.room_type !== "m.space") { + childRooms.push({name: room.name, value: room.room_id}) + reverseCache.set(room.room_id, spaceID) + } + } + return childRooms + })() + cache.set(spaceID, entry) + return entry + })() +} + +/** @param {DiscordTypes.APIApplicationCommandAutocompleteGuildInteraction} interaction */ +async function interactAutocomplete({id, token, data, guild_id}) { + const spaceID = select("guild_space", "space_id", {guild_id}).pluck().get() + if (!spaceID) { + return discord.snow.interaction.createInteractionResponse(id, token, { + type: DiscordTypes.InteractionResponseType.ApplicationCommandAutocompleteResult, + data: { + choices: [ + { + name: `Error: This server needs to be bridged somewhere first...`, + value: "baby" + } + ] + } + }) + } + + let rooms = await getCachedHierarchy(spaceID) + // @ts-ignore + rooms = rooms.filter(r => r.name.includes(data.options[0].value)) + + await discord.snow.interaction.createInteractionResponse(id, token, { + type: DiscordTypes.InteractionResponseType.ApplicationCommandAutocompleteResult, + data: { + choices: rooms + } + }) +} + +/** @param {DiscordTypes.APIChatInputApplicationCommandGuildInteraction} interaction */ +async function interactSubmit({id, token, data, guild_id}) { + const spaceID = select("guild_space", "space_id", {guild_id}).pluck().get() + if (!spaceID) { + return discord.snow.interaction.createInteractionResponse(id, token, { + type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource, + data: { + content: "Error: This server needs to be bridged somewhere first...", + flags: DiscordTypes.MessageFlags.Ephemeral + } + }) + } + + return discord.snow.interaction.createInteractionResponse(id, token, { + type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource, + data: { + content: "Valid input. This would do something but it isn't implemented yet.", + flags: DiscordTypes.MessageFlags.Ephemeral + } + }) +} + +/** @param {DiscordTypes.APIGuildInteraction} interaction */ +async function interact(interaction) { + if (interaction.type === DiscordTypes.InteractionType.ApplicationCommandAutocomplete) { + return interactAutocomplete(interaction) + } else if (interaction.type === DiscordTypes.InteractionType.ApplicationCommand) { + // @ts-ignore + return interactSubmit(interaction) + } +} + +module.exports.interact = interact diff --git a/src/discord/interactions/invite.js b/src/discord/interactions/invite.js index bacf465..3b39d9d 100644 --- a/src/discord/interactions/invite.js +++ b/src/discord/interactions/invite.js @@ -14,14 +14,13 @@ const api = sync.require("../../matrix/api") /** * @param {DiscordTypes.APIChatInputApplicationCommandGuildInteraction & {channel: DiscordTypes.APIGuildTextChannel}} interaction - * @param {{api: typeof api}} di * @returns {Promise} */ -async function _interact({data, channel, guild_id}, {api}) { +async function _interact({data, channel, guild_id}) { // Get named MXID /** @type {DiscordTypes.APIApplicationCommandInteractionDataStringOption[] | undefined} */ // @ts-ignore const options = data.options - const input = options?.[0]?.value || "" + const input = options?.[0].value || "" const mxid = input.match(/@([^:]+):([a-z0-9:-]+\.[a-z0-9.:-]+)/)?.[0] if (!mxid) return { type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource, @@ -111,10 +110,9 @@ async function _interact({data, channel, guild_id}, {api}) { /** * @param {DiscordTypes.APIMessageComponentGuildInteraction} interaction - * @param {{api: typeof api}} di * @returns {Promise} */ -async function _interactButton({channel, message}, {api}) { +async function _interactButton({channel, message}) { const mxid = message.content.match(/`(@(?:[^:]+):(?:[a-z0-9:-]+\.[a-z0-9.:-]+))`/)?.[1] assert(mxid) const roomID = select("channel_room", "room_id", {channel_id: channel.id}).pluck().get() @@ -129,16 +127,14 @@ async function _interactButton({channel, message}, {api}) { } } -/* c8 ignore start */ - /** @param {DiscordTypes.APIChatInputApplicationCommandGuildInteraction & {channel: DiscordTypes.APIGuildTextChannel}} interaction */ async function interact(interaction) { - await discord.snow.interaction.createInteractionResponse(interaction.id, interaction.token, await _interact(interaction, {api})) + await discord.snow.interaction.createInteractionResponse(interaction.id, interaction.token, await _interact(interaction)) } /** @param {DiscordTypes.APIMessageComponentGuildInteraction} interaction */ async function interactButton(interaction) { - await discord.snow.interaction.createInteractionResponse(interaction.id, interaction.token, await _interactButton(interaction, {api})) + await discord.snow.interaction.createInteractionResponse(interaction.id, interaction.token, await _interactButton(interaction)) } module.exports.interact = interact diff --git a/src/discord/interactions/invite.test.js b/src/discord/interactions/invite.test.js deleted file mode 100644 index 431ff60..0000000 --- a/src/discord/interactions/invite.test.js +++ /dev/null @@ -1,228 +0,0 @@ -const {test} = require("supertape") -const DiscordTypes = require("discord-api-types/v10") -const {db, discord} = require("../../passthrough") -const {MatrixServerError} = require("../../matrix/mreq") -const {_interact, _interactButton} = require("./invite") - -test("invite: checks for missing matrix ID", async t => { - const msg = await _interact({ - data: { - options: [] - }, - channel: discord.channels.get("0"), - guild_id: "112760669178241024" - }, {}) - t.equal(msg.data.content, "You have to say the Matrix ID of the person you want to invite. Matrix IDs look like this: `@username:example.org`") -}) - -test("invite: checks for invalid matrix ID", async t => { - const msg = await _interact({ - data: { - options: [{ - name: "user", - type: DiscordTypes.ApplicationCommandOptionType.String, - value: "@cadence" - }] - }, - channel: discord.channels.get("0"), - guild_id: "112760669178241024" - }, {}) - t.equal(msg.data.content, "You have to say the Matrix ID of the person you want to invite. Matrix IDs look like this: `@username:example.org`") -}) - -test("invite: checks if channel exists or is autocreatable", async t => { - db.prepare("UPDATE guild_active SET autocreate = 0").run() - const msg = await _interact({ - data: { - options: [{ - name: "user", - type: DiscordTypes.ApplicationCommandOptionType.String, - value: "@cadence:cadence.moe" - }] - }, - channel: discord.channels.get("498323546729086986"), - guild_id: "112760669178241024" - }, {}) - t.equal(msg.data.content, "This channel isn't bridged, so you can't invite Matrix users yet. Try turning on automatic room-creation or link a Matrix room in the website.") - db.prepare("UPDATE guild_active SET autocreate = 1").run() -}) - -test("invite: checks if user is already invited to space", async t => { - let called = 0 - const msg = await _interact({ - data: { - options: [{ - name: "user", - type: DiscordTypes.ApplicationCommandOptionType.String, - value: "@cadence:cadence.moe" - }] - }, - channel: discord.channels.get("112760669178241024"), - guild_id: "112760669178241024" - }, { - api: { - getStateEvent: async (roomID, type, stateKey) => { - called++ - t.equal(roomID, "!jjWAGMeQdNrVZSSfvz:cadence.moe") // space ID - t.equal(type, "m.room.member") - t.equal(stateKey, "@cadence:cadence.moe") - return { - displayname: "cadence", - membership: "invite" - } - } - } - }) - t.equal(msg.data.content, "`@cadence:cadence.moe` already has an invite, which they haven't accepted yet.") - t.equal(called, 1) -}) - -test("invite: invites if user is not in space", async t => { - let called = 0 - const msg = await _interact({ - data: { - options: [{ - name: "user", - type: DiscordTypes.ApplicationCommandOptionType.String, - value: "@cadence:cadence.moe" - }] - }, - channel: discord.channels.get("112760669178241024"), - guild_id: "112760669178241024" - }, { - api: { - getStateEvent: async (roomID, type, stateKey) => { - called++ - t.equal(roomID, "!jjWAGMeQdNrVZSSfvz:cadence.moe") // space ID - t.equal(type, "m.room.member") - t.equal(stateKey, "@cadence:cadence.moe") - throw new MatrixServerError("State event doesn't exist or something") - }, - inviteToRoom: async (roomID, mxid) => { - called++ - t.equal(roomID, "!jjWAGMeQdNrVZSSfvz:cadence.moe") // space ID - t.equal(mxid, "@cadence:cadence.moe") - } - } - }) - t.equal(msg.data.content, "You invited `@cadence:cadence.moe` to the server.") - t.equal(called, 2) -}) - -test("invite: prompts to invite to room (if never joined)", async t => { - let called = 0 - const msg = await _interact({ - data: { - options: [{ - name: "user", - type: DiscordTypes.ApplicationCommandOptionType.String, - value: "@cadence:cadence.moe" - }] - }, - channel: discord.channels.get("112760669178241024"), - guild_id: "112760669178241024" - }, { - api: { - getStateEvent: async (roomID, type, stateKey) => { - called++ - t.equal(type, "m.room.member") - t.equal(stateKey, "@cadence:cadence.moe") - if (roomID === "!jjWAGMeQdNrVZSSfvz:cadence.moe") { // space ID - return { - displayname: "cadence", - membership: "join" - } - } else { - throw new MatrixServerError("State event doesn't exist or something") - } - } - } - }) - t.equal(msg.data.content, "`@cadence:cadence.moe` is already in this server. Would you like to additionally invite them to this specific channel?") - t.equal(called, 2) -}) - -test("invite: prompts to invite to room (if left)", async t => { - let called = 0 - const msg = await _interact({ - data: { - options: [{ - name: "user", - type: DiscordTypes.ApplicationCommandOptionType.String, - value: "@cadence:cadence.moe" - }] - }, - channel: discord.channels.get("112760669178241024"), - guild_id: "112760669178241024" - }, { - api: { - getStateEvent: async (roomID, type, stateKey) => { - called++ - t.equal(type, "m.room.member") - t.equal(stateKey, "@cadence:cadence.moe") - if (roomID === "!jjWAGMeQdNrVZSSfvz:cadence.moe") { // space ID - return { - displayname: "cadence", - membership: "join" - } - } else { - return { - displayname: "cadence", - membership: "leave" - } - } - } - } - }) - t.equal(msg.data.content, "`@cadence:cadence.moe` is already in this server. Would you like to additionally invite them to this specific channel?") - t.equal(called, 2) -}) - -test("invite button: invites to room when button clicked", async t => { - let called = 0 - const msg = await _interactButton({ - channel: discord.channels.get("112760669178241024"), - message: { - content: "`@cadence:cadence.moe` is already in this server. Would you like to additionally invite them to this specific channel?" - } - }, { - api: { - inviteToRoom: async (roomID, mxid) => { - called++ - t.equal(roomID, "!kLRqKKUQXcibIMtOpl:cadence.moe") // room ID - t.equal(mxid, "@cadence:cadence.moe") - } - } - }) - t.equal(msg.data.content, "You invited `@cadence:cadence.moe` to the channel.") - t.equal(called, 1) -}) - -test("invite: no-op if in room and space", async t => { - let called = 0 - const msg = await _interact({ - data: { - options: [{ - name: "user", - type: DiscordTypes.ApplicationCommandOptionType.String, - value: "@cadence:cadence.moe" - }] - }, - channel: discord.channels.get("112760669178241024"), - guild_id: "112760669178241024" - }, { - api: { - getStateEvent: async (roomID, type, stateKey) => { - called++ - t.equal(type, "m.room.member") - t.equal(stateKey, "@cadence:cadence.moe") - return { - displayname: "cadence", - membership: "join" - } - } - } - }) - t.equal(msg.data.content, "`@cadence:cadence.moe` is already in this server and this channel.") - t.equal(called, 2) -}) diff --git a/src/discord/register-interactions.js b/src/discord/register-interactions.js index 7b1e52d..cd9203f 100644 --- a/src/discord/register-interactions.js +++ b/src/discord/register-interactions.js @@ -7,6 +7,7 @@ const {id} = require("../../addbot") const matrixInfo = sync.require("./interactions/matrix-info.js") const invite = sync.require("./interactions/invite.js") const permissions = sync.require("./interactions/permissions.js") +const bridge = sync.require("./interactions/bridge.js") const reactions = sync.require("./interactions/reactions.js") const privacy = sync.require("./interactions/privacy.js") @@ -38,6 +39,20 @@ discord.snow.interaction.bulkOverwriteApplicationCommands(id, [{ name: "user" } ] +}, { + name: "bridge", + contexts: [DiscordTypes.InteractionContextType.Guild], + type: DiscordTypes.ApplicationCommandType.ChatInput, + description: "Start bridging this channel to a Matrix room", + default_member_permissions: String(DiscordTypes.PermissionFlagsBits.ManageChannels), + options: [ + { + type: DiscordTypes.ApplicationCommandOptionType.String, + description: "Destination room to bridge to", + name: "room", + autocomplete: true + } + ] }, { name: "privacy", contexts: [DiscordTypes.InteractionContextType.Guild], @@ -79,6 +94,8 @@ async function dispatchInteraction(interaction) { await permissions.interact(interaction) } else if (interactionId === "permissions_edit") { await permissions.interactEdit(interaction) + } else if (interactionId === "bridge") { + await bridge.interact(interaction) } else if (interactionId === "Reactions") { await reactions.interact(interaction) } else if (interactionId === "privacy") { diff --git a/src/discord/utils.js b/src/discord/utils.js index dea05ae..dc96ff8 100644 --- a/src/discord/utils.js +++ b/src/discord/utils.js @@ -113,7 +113,7 @@ function isWebhookMessage(message) { * @param {Pick} message */ function isEphemeralMessage(message) { - return Boolean(message.flags && (message.flags & DiscordTypes.MessageFlags.Ephemeral)) + return message.flags && (message.flags & DiscordTypes.MessageFlags.Ephemeral) } /** @param {string} snowflake */ diff --git a/src/discord/utils.test.js b/src/discord/utils.test.js index 7900440..7c5f0c8 100644 --- a/src/discord/utils.test.js +++ b/src/discord/utils.test.js @@ -84,67 +84,6 @@ test("getPermissions: channel overwrite to allow role works", t => { t.equal((permissions & want), want) }) -test("getPermissions: channel overwrite to allow user works", t => { - const guildRoles = [ - { - version: 1695412489043, - unicode_emoji: null, - tags: {}, - position: 0, - permissions: "559623605571137", - name: "@everyone", - mentionable: false, - managed: false, - id: "1154868424724463687", - icon: null, - hoist: false, - flags: 0, - color: 0 - }, - { - version: 1695412604262, - unicode_emoji: null, - tags: { bot_id: "466378653216014359" }, - position: 1, - permissions: "536995904", - name: "PluralKit", - mentionable: false, - managed: true, - id: "1154868908336099444", - icon: null, - hoist: false, - flags: 0, - color: 0 - }, - { - version: 1698778936921, - unicode_emoji: null, - tags: {}, - position: 1, - permissions: "536870912", - name: "web hookers", - mentionable: false, - managed: false, - id: "1168988246680801360", - icon: null, - hoist: false, - flags: 0, - color: 0 - } - ] - const userRoles = [] - const userID = "353373325575323648" - const overwrites = [ - { type: 0, id: "1154868908336099444", deny: "0", allow: "1024" }, - { type: 0, id: "1154868424724463687", deny: "1024", allow: "0" }, - { type: 0, id: "1168988246680801360", deny: "0", allow: "1024" }, - { type: 1, id: "353373325575323648", deny: "0", allow: "1024" } - ] - const permissions = utils.getPermissions(userRoles, guildRoles, userID, overwrites) - const want = BigInt(1 << 10 | 1 << 16) - t.equal((permissions & want), want) -}) - test("hasSomePermissions: detects the permission", t => { const userPermissions = DiscordTypes.PermissionFlagsBits.MentionEveryone | DiscordTypes.PermissionFlagsBits.BanMembers const canRemoveMembers = utils.hasSomePermissions(userPermissions, ["KickMembers", "BanMembers"]) @@ -168,15 +107,3 @@ test("hasAllPermissions: doesn't detect not the permissions", t => { const canRemoveMembers = utils.hasAllPermissions(userPermissions, ["KickMembers", "BanMembers"]) t.equal(canRemoveMembers, false) }) - -test("isEphemeralMessage: detects ephemeral message", t => { - t.equal(utils.isEphemeralMessage(data.special_message.ephemeral_message), true) -}) - -test("isEphemeralMessage: doesn't detect normal message", t => { - t.equal(utils.isEphemeralMessage(data.message.simple_plaintext), false) -}) - -test("getPublicUrlForCdn: no-op on non-discord URL", t => { - t.equal(utils.getPublicUrlForCdn("https://cadence.moe"), "https://cadence.moe") -}) diff --git a/src/matrix/power.test.js b/src/matrix/power.test.js new file mode 100644 index 0000000..5423c4f --- /dev/null +++ b/src/matrix/power.test.js @@ -0,0 +1,12 @@ +// @ts-check + +const {test} = require("supertape") +const power = require("./power") + +test("power: get affected rooms", t => { + t.deepEqual(power._getAffectedRooms(), [{ + mxid: "@test_auto_invite:example.org", + power_level: 100, + room_id: "!kLRqKKUQXcibIMtOpl:cadence.moe", + }]) +}) diff --git a/src/matrix/read-registration.js b/src/matrix/read-registration.js index d126851..9fb0535 100644 --- a/src/matrix/read-registration.js +++ b/src/matrix/read-registration.js @@ -9,7 +9,7 @@ const registrationFilePath = path.join(process.cwd(), "registration.yaml") /** @param {import("../types").AppServiceRegistrationConfig} reg */ function checkRegistration(reg) { - reg["ooye"].invite = reg.ooye.invite.filter(mxid => mxid.endsWith(`:${reg.ooye.server_name}`)) // one day I will understand why typescript disagrees with dot notation on this line + reg["ooye"].invite = (reg.ooye.invite || []).filter(mxid => mxid.endsWith(`:${reg.ooye.server_name}`)) // one day I will understand why typescript disagrees with dot notation on this line assert(reg.ooye?.max_file_size) assert(reg.ooye?.namespace_prefix) assert(reg.ooye?.server_name) @@ -19,7 +19,6 @@ function checkRegistration(reg) { assert.match(reg.url, /^https?:/, "url must start with http:// or https://") } -/* c8 ignore next 4 */ /** @param {import("../types").AppServiceRegistrationConfig} reg */ function writeRegistration(reg) { fs.writeFileSync(registrationFilePath, JSON.stringify(reg, null, 2)) @@ -53,7 +52,6 @@ function getTemplateRegistration(serverName) { socket: 6693, ooye: { namespace_prefix, - server_name: serverName, max_file_size: 5000000, content_length_workaround: false, include_user_id_in_mxid: false, @@ -68,8 +66,6 @@ function readRegistration() { try { const content = fs.readFileSync(registrationFilePath, "utf8") result = JSON.parse(content) - result.ooye.invite ||= [] - /* c8 ignore next */ } catch (e) {} return result } diff --git a/src/matrix/read-registration.test.js b/src/matrix/read-registration.test.js index 5fb3b55..80ac09f 100644 --- a/src/matrix/read-registration.test.js +++ b/src/matrix/read-registration.test.js @@ -1,8 +1,5 @@ -// @ts-check - -const tryToCatch = require("try-to-catch") const {test} = require("supertape") -const {reg, checkRegistration, getTemplateRegistration} = require("./read-registration") +const {reg} = require("./read-registration") test("reg: has necessary parameters", t => { const propertiesToCheck = ["sender_localpart", "id", "as_token", "ooye"] @@ -11,19 +8,3 @@ test("reg: has necessary parameters", t => { propertiesToCheck ) }) - -test("check: passes on sample", t => { - checkRegistration(reg) - t.pass("all assertions passed") -}) - -test("check: fails on template as template is missing some required values that are gathered during setup", t => { - let err - try { - // @ts-ignore - checkRegistration(getTemplateRegistration("cadence.moe")) - } catch (e) { - err = e - } - t.ok(err, "one of the assertions failed as expected") -}) diff --git a/src/types.d.ts b/src/types.d.ts index 576bb59..62d9b30 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -55,10 +55,9 @@ export type InitialAppServiceRegistrationConfig = { socket?: string | number, ooye: { namespace_prefix: string - server_name: string - max_file_size: number - content_length_workaround: boolean - invite: string[] + max_file_size: number, + content_length_workaround: boolean, + invite: string[], include_user_id_in_mxid: boolean } } diff --git a/test/data.js b/test/data.js index 9dc2d72..eb2c42a 100644 --- a/test/data.js +++ b/test/data.js @@ -4128,54 +4128,7 @@ module.exports = { guild_id: "112760669178241024" }, position: 0 - }, - ephemeral_message: { - webhook_id: "684280192553844747", - type: 20, - tts: false, - timestamp: "2024-09-29T11:22:04.865000+00:00", - position: 0, - pinned: false, - nonce: "1289910062243905536", - mentions: [], - mention_roles: [], - mention_everyone: false, - interaction_metadata: { - user: {baby: true}, - type: 2, - name: "invite", - id: "1289910063691206717", - command_type: 1, - authorizing_integration_owners: {baby: true} - }, - interaction: { - user: {baby: true}, - type: 2, - name: "invite", - id: "1289910063691206717" - }, - id: "1289910064995504182", - flags: 64, - embeds: [], - edited_timestamp: null, - content: "`@cadence:cadence.moe` is already in this server and this channel.", - components: [], - channel_id: "1100319550446252084", - author: { - username: "Matrix Bridge", - public_flags: 0, - id: "684280192553844747", - global_name: null, - discriminator: "5728", - clan: null, - bot: true, - avatar_decoration_data: null, - avatar: "48ae3c24f2a6ec5c60c41bdabd904018" - }, - attachments: [], - application_id: "684280192553844747" - }, - shard_id: 0 + } }, interaction_message: { thinking_interaction_without_bot_user: { diff --git a/test/ooye-test-data.sql b/test/ooye-test-data.sql index b6f0951..2c23561 100644 --- a/test/ooye-test-data.sql +++ b/test/ooye-test-data.sql @@ -3,9 +3,6 @@ BEGIN TRANSACTION; INSERT INTO guild_space (guild_id, space_id, privacy_level) VALUES ('112760669178241024', '!jjWAGMeQdNrVZSSfvz:cadence.moe', 0); -INSERT INTO guild_active (guild_id, autocreate) VALUES -('112760669178241024', 1); - INSERT INTO channel_room (channel_id, room_id, name, nick, thread_parent, custom_avatar) VALUES ('112760669178241024', '!kLRqKKUQXcibIMtOpl:cadence.moe', 'heave', 'main', NULL, NULL), ('497161350934560778', '!CzvdIdUQXgUjDVKxeU:cadence.moe', 'amanda-spam', NULL, NULL, NULL), diff --git a/test/test.js b/test/test.js index 07146ab..281df29 100644 --- a/test/test.js +++ b/test/test.js @@ -23,8 +23,8 @@ reg.id = "baby" // don't actually take authenticated actions on the server reg.as_token = "baby" reg.hs_token = "baby" reg.ooye.bridge_origin = "https://bridge.example.org" +reg.ooye.invite = [] -/** @type {import("heatsync").default} */ // @ts-ignore const sync = new HeatSync({watchFS: false}) const discord = { @@ -35,7 +35,6 @@ const discord = { id: "684280192553844747" }, channels: new Map([ - [data.channel.general.id, data.channel.general], ["497161350934560778", { guild_id: "497159726455455754" }], @@ -118,6 +117,7 @@ file._actuallyUploadDiscordFileToMxc = function(url, res) { throw new Error(`Not require("../src/matrix/kstate.test") require("../src/matrix/api.test") require("../src/matrix/file.test") + require("../src/matrix/power.test") require("../src/matrix/read-registration.test") require("../src/matrix/txnid.test") require("../src/d2m/actions/create-room.test") @@ -136,5 +136,4 @@ file._actuallyUploadDiscordFileToMxc = function(url, res) { throw new Error(`Not require("../src/m2d/converters/event-to-message.test") require("../src/m2d/converters/utils.test") require("../src/m2d/converters/emoji-sheet.test") - require("../src/discord/interactions/invite.test") })()