From c8021cadecb5c84b69a206413b4eb100ad9f76f2 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Mon, 21 Aug 2023 21:04:41 +1200 Subject: [PATCH] changing spaces to tabs --- .vscode/settings.json | 3 + d2m/actions/add-reaction.js | 22 ++--- m2d/actions/channel-webhook.js | 8 +- m2d/actions/send-event.js | 10 +-- matrix/api.js | 129 +++++++++++++++++----------- matrix/kstate.test.js | 26 +++--- matrix/read-registration.test.js | 10 +-- scripts/save-channel-names-to-db.js | 48 +++++------ scripts/save-event-types-to-db.js | 18 ++-- 9 files changed, 154 insertions(+), 120 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 2c63c08..9f1e183 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,2 +1,5 @@ { + "editor.insertSpaces": false, + "editor.detectIndentation": false, + "editor.tabSize": 3 } diff --git a/d2m/actions/add-reaction.js b/d2m/actions/add-reaction.js index b46af59..2eab8f0 100644 --- a/d2m/actions/add-reaction.js +++ b/d2m/actions/add-reaction.js @@ -15,20 +15,20 @@ const createRoom = sync.require("../actions/create-room") * @param {import("discord-api-types/v10").GatewayMessageReactionAddDispatchData} data */ async function addReaction(data) { - const user = data.member?.user - assert.ok(user && user.username) - const parentID = db.prepare("SELECT event_id FROM event_message WHERE message_id = ? AND part = 0").pluck().get(data.message_id) // 0 = primary - if (!parentID) return // Nothing can be done if the parent message was never bridged. - assert.equal(typeof parentID, "string") + const user = data.member?.user + assert.ok(user && user.username) + const parentID = db.prepare("SELECT event_id FROM event_message WHERE message_id = ? AND part = 0").pluck().get(data.message_id) // 0 = primary + if (!parentID) return // Nothing can be done if the parent message was never bridged. + assert.equal(typeof parentID, "string") const roomID = await createRoom.ensureRoom(data.channel_id) const senderMxid = await registerUser.ensureSimJoined(user, roomID) const eventID = await api.sendEvent(roomID, "m.reaction", { - "m.relates_to": { - rel_type: "m.annotation", - event_id: parentID, - key: data.emoji.name - } - }, senderMxid) + "m.relates_to": { + rel_type: "m.annotation", + event_id: parentID, + key: data.emoji.name + } + }, senderMxid) return eventID } diff --git a/m2d/actions/channel-webhook.js b/m2d/actions/channel-webhook.js index 6d39da7..b0bc072 100644 --- a/m2d/actions/channel-webhook.js +++ b/m2d/actions/channel-webhook.js @@ -52,10 +52,10 @@ async function withWebhook(channelID, callback) { * @param {string} [threadID] */ async function sendMessageWithWebhook(channelID, data, threadID) { - const result = await withWebhook(channelID, async webhook => { - return discord.snow.webhook.executeWebhook(webhook.id, webhook.token, data, {wait: true, thread_id: threadID, disableEveryone: true}) - }) - return result + const result = await withWebhook(channelID, async webhook => { + return discord.snow.webhook.executeWebhook(webhook.id, webhook.token, data, {wait: true, thread_id: threadID, disableEveryone: true}) + }) + return result } module.exports.ensureWebhook = ensureWebhook diff --git a/m2d/actions/send-event.js b/m2d/actions/send-event.js index 88ba0fd..39eed22 100644 --- a/m2d/actions/send-event.js +++ b/m2d/actions/send-event.js @@ -12,7 +12,7 @@ const eventToMessage = sync.require("../converters/event-to-message") /** @param {import("../../types").Event.Outer} event */ async function sendEvent(event) { - // TODO: we just assume the bridge has already been created + // TODO: we just assume the bridge has already been created const row = db.prepare("SELECT channel_id, thread_parent FROM channel_room WHERE room_id = ?").get(event.room_id) let channelID = row.channel_id let threadID = undefined @@ -21,16 +21,16 @@ async function sendEvent(event) { channelID = row.thread_parent // it's the thread's parent... get with the times... } - // no need to sync the matrix member to the other side. but if I did need to, this is where I'd do it + // no need to sync the matrix member to the other side. but if I did need to, this is where I'd do it const messages = eventToMessage.eventToMessage(event) - assert(Array.isArray(messages)) // sanity + assert(Array.isArray(messages)) // sanity - /** @type {DiscordTypes.APIMessage[]} */ + /** @type {DiscordTypes.APIMessage[]} */ const messageResponses = [] let eventPart = 0 // 0 is primary, 1 is supporting for (const message of messages) { - const messageResponse = await channelWebhook.sendMessageWithWebhook(channelID, message, threadID) + const messageResponse = await channelWebhook.sendMessageWithWebhook(channelID, message, threadID) db.prepare("INSERT INTO event_message (event_id, event_type, event_subtype, message_id, channel_id, part, source) VALUES (?, ?, ?, ?, ?, ?, 0)").run(event.event_id, event.type, event.content.msgtype || null, messageResponse.id, channelID, eventPart) // source 0 = matrix eventPart = 1 // TODO: use more intelligent algorithm to determine whether primary or supporting? diff --git a/matrix/api.js b/matrix/api.js index 81d8a16..b382631 100644 --- a/matrix/api.js +++ b/matrix/api.js @@ -19,15 +19,15 @@ const makeTxnId = sync.require("./txnid") * @returns {string} the new endpoint */ function path(p, mxid, otherParams = {}) { - if (!mxid) return p - const u = new URL(p, "http://localhost") - u.searchParams.set("user_id", mxid) - for (const entry of Object.entries(otherParams)) { - if (entry[1] != undefined) { - u.searchParams.set(entry[0], entry[1]) - } - } - return u.pathname + "?" + u.searchParams.toString() + if (!mxid) return p + const u = new URL(p, "http://localhost") + u.searchParams.set("user_id", mxid) + for (const entry of Object.entries(otherParams)) { + if (entry[1] != undefined) { + u.searchParams.set(entry[0], entry[1]) + } + } + return u.pathname + "?" + u.searchParams.toString() } /** @@ -35,40 +35,40 @@ function path(p, mxid, otherParams = {}) { * @returns {Promise} */ function register(username) { - console.log(`[api] register: ${username}`) - return mreq.mreq("POST", "/client/v3/register", { - type: "m.login.application_service", - username - }) + console.log(`[api] register: ${username}`) + return mreq.mreq("POST", "/client/v3/register", { + type: "m.login.application_service", + username + }) } /** * @returns {Promise} room ID */ async function createRoom(content) { - console.log(`[api] create room:`, content) - /** @type {Ty.R.RoomCreated} */ - const root = await mreq.mreq("POST", "/client/v3/createRoom", content) - return root.room_id + console.log(`[api] create room:`, content) + /** @type {Ty.R.RoomCreated} */ + const root = await mreq.mreq("POST", "/client/v3/createRoom", content) + return root.room_id } /** * @returns {Promise} room ID */ async function joinRoom(roomIDOrAlias, mxid) { - /** @type {Ty.R.RoomJoined} */ - const root = await mreq.mreq("POST", path(`/client/v3/join/${roomIDOrAlias}`, mxid)) - return root.room_id + /** @type {Ty.R.RoomJoined} */ + const root = await mreq.mreq("POST", path(`/client/v3/join/${roomIDOrAlias}`, mxid)) + return root.room_id } async function inviteToRoom(roomID, mxidToInvite, mxid) { - await mreq.mreq("POST", path(`/client/v3/rooms/${roomID}/invite`, mxid), { - user_id: mxidToInvite - }) + await mreq.mreq("POST", path(`/client/v3/rooms/${roomID}/invite`, mxid), { + user_id: mxidToInvite + }) } async function leaveRoom(roomID, mxid) { - await mreq.mreq("POST", path(`/client/v3/rooms/${roomID}/leave`, mxid), {}) + await mreq.mreq("POST", path(`/client/v3/rooms/${roomID}/leave`, mxid), {}) } /** @@ -77,9 +77,9 @@ async function leaveRoom(roomID, mxid) { * @template T */ async function getEvent(roomID, eventID) { - /** @type {Ty.Event.Outer} */ - const root = await mreq.mreq("GET", `/client/v3/rooms/${roomID}/event/${eventID}`) - return root + /** @type {Ty.Event.Outer} */ + const root = await mreq.mreq("GET", `/client/v3/rooms/${roomID}/event/${eventID}`) + return root } /** @@ -87,7 +87,17 @@ async function getEvent(roomID, eventID) { * @returns {Promise} */ function getAllState(roomID) { - return mreq.mreq("GET", `/client/v3/rooms/${roomID}/state`) + return mreq.mreq("GET", `/client/v3/rooms/${roomID}/state`) +} + +/** + * @param {string} roomID + * @param {string} type + * @param {string} key + * @returns the *content* of the state event + */ +function getStateEvent(roomID, type, key) { + return mreq.mreq("GET", `/client/v3/rooms/${roomID}/state/${type}/${key}`) } /** @@ -96,7 +106,7 @@ function getAllState(roomID) { * @returns {Promise<{joined: {[mxid: string]: Ty.R.RoomMember}}>} */ function getJoinedMembers(roomID) { - return mreq.mreq("GET", `/client/v3/rooms/${roomID}/joined_members`) + return mreq.mreq("GET", `/client/v3/rooms/${roomID}/joined_members`) } /** @@ -107,12 +117,12 @@ function getJoinedMembers(roomID) { * @returns {Promise} event ID */ async function sendState(roomID, type, stateKey, content, mxid) { - console.log(`[api] state: ${roomID}: ${type}/${stateKey}`) - assert.ok(type) - assert.ok(typeof stateKey === "string") - /** @type {Ty.R.EventSent} */ - const root = await mreq.mreq("PUT", path(`/client/v3/rooms/${roomID}/state/${type}/${stateKey}`, mxid), content) - return root.event_id + console.log(`[api] state: ${roomID}: ${type}/${stateKey}`) + assert.ok(type) + assert.ok(typeof stateKey === "string") + /** @type {Ty.R.EventSent} */ + const root = await mreq.mreq("PUT", path(`/client/v3/rooms/${roomID}/state/${type}/${stateKey}`, mxid), content) + return root.event_id } /** @@ -123,31 +133,51 @@ async function sendState(roomID, type, stateKey, content, mxid) { * @param {number} [timestamp] timestamp of the newly created event, in unix milliseconds */ async function sendEvent(roomID, type, content, mxid, timestamp) { - console.log(`[api] event ${type} to ${roomID} as ${mxid || "default sim"}`) - /** @type {Ty.R.EventSent} */ - const root = await mreq.mreq("PUT", path(`/client/v3/rooms/${roomID}/send/${type}/${makeTxnId.makeTxnId()}`, mxid, {ts: timestamp}), content) - return root.event_id + console.log(`[api] event ${type} to ${roomID} as ${mxid || "default sim"}`) + /** @type {Ty.R.EventSent} */ + const root = await mreq.mreq("PUT", path(`/client/v3/rooms/${roomID}/send/${type}/${makeTxnId.makeTxnId()}`, mxid, {ts: timestamp}), content) + return root.event_id } /** * @returns {Promise} room ID */ async function redactEvent(roomID, eventID, mxid) { - /** @type {Ty.R.EventRedacted} */ - const root = await mreq.mreq("PUT", path(`/client/v3/rooms/${roomID}/redact/${eventID}/${makeTxnId.makeTxnId()}`, mxid), {}) - return root.event_id + /** @type {Ty.R.EventRedacted} */ + const root = await mreq.mreq("PUT", path(`/client/v3/rooms/${roomID}/redact/${eventID}/${makeTxnId.makeTxnId()}`, mxid), {}) + return root.event_id } async function profileSetDisplayname(mxid, displayname) { - await mreq.mreq("PUT", path(`/client/v3/profile/${mxid}/displayname`, mxid), { - displayname - }) + await mreq.mreq("PUT", path(`/client/v3/profile/${mxid}/displayname`, mxid), { + displayname + }) } async function profileSetAvatarUrl(mxid, avatar_url) { - await mreq.mreq("PUT", path(`/client/v3/profile/${mxid}/avatar_url`, mxid), { - avatar_url - }) + await mreq.mreq("PUT", path(`/client/v3/profile/${mxid}/avatar_url`, mxid), { + avatar_url + }) +} + +/** + * Set a user's power level within a room. + * @param {string} roomID + * @param {string} mxid + * @param {number} power + */ +async function setUserPower(roomID, mxid, power) { + // Yes it's this hard https://github.com/matrix-org/matrix-appservice-bridge/blob/2334b0bae28a285a767fe7244dad59f5a5963037/src/components/intent.ts#L352 + const powerLevels = await getStateEvent(roomID, "m.room.power_levels", "") + const users = powerLevels.users || {} + if (power != null) { + users[mxid] = power + } else { + delete users[mxid] + } + powerLevels.users = users + await sendState(roomID, "m.room.power_levels", "", powerLevels) + return powerLevels } module.exports.path = path @@ -164,3 +194,4 @@ module.exports.sendEvent = sendEvent module.exports.redactEvent = redactEvent module.exports.profileSetDisplayname = profileSetDisplayname module.exports.profileSetAvatarUrl = profileSetAvatarUrl +module.exports.setUserPower = setUserPower diff --git a/matrix/kstate.test.js b/matrix/kstate.test.js index ed59e9d..1541898 100644 --- a/matrix/kstate.test.js +++ b/matrix/kstate.test.js @@ -2,22 +2,22 @@ const {kstateToState, stateToKState, diffKState, kstateStripConditionals} = requ const {test} = require("supertape") test("kstate strip: strips false conditions", t => { - t.deepEqual(kstateStripConditionals({ - a: {$if: false, value: 2}, - b: {value: 4} - }), { - b: {value: 4} - }) + t.deepEqual(kstateStripConditionals({ + a: {$if: false, value: 2}, + b: {value: 4} + }), { + b: {value: 4} + }) }) test("kstate strip: keeps true conditions while removing $if", t => { - t.deepEqual(kstateStripConditionals({ - a: {$if: true, value: 2}, - b: {value: 4} - }), { - a: {value: 2}, - b: {value: 4} - }) + t.deepEqual(kstateStripConditionals({ + a: {$if: true, value: 2}, + b: {value: 4} + }), { + a: {value: 2}, + b: {value: 4} + }) }) test("kstate2state: general", t => { diff --git a/matrix/read-registration.test.js b/matrix/read-registration.test.js index d402cfb..e5123b9 100644 --- a/matrix/read-registration.test.js +++ b/matrix/read-registration.test.js @@ -2,9 +2,9 @@ const {test} = require("supertape") const reg = require("./read-registration") test("reg: has necessary parameters", t => { - const propertiesToCheck = ["sender_localpart", "id", "as_token", "ooye"] - t.deepEqual( - propertiesToCheck.filter(p => p in reg), - propertiesToCheck - ) + const propertiesToCheck = ["sender_localpart", "id", "as_token", "ooye"] + t.deepEqual( + propertiesToCheck.filter(p => p in reg), + propertiesToCheck + ) }) diff --git a/scripts/save-channel-names-to-db.js b/scripts/save-channel-names-to-db.js index a70b1bb..6f5867a 100644 --- a/scripts/save-channel-names-to-db.js +++ b/scripts/save-channel-names-to-db.js @@ -18,10 +18,10 @@ passthrough.discord = discord ;(async () => { await discord.cloud.connect() - console.log("Discord gateway started") + console.log("Discord gateway started") - const f = event => onPacket(discord, event, () => discord.cloud.off("event", f)) - discord.cloud.on("event", f) + const f = event => onPacket(discord, event, () => discord.cloud.off("event", f)) + discord.cloud.on("event", f) })() const expectedGuilds = new Set() @@ -30,29 +30,29 @@ const prepared = db.prepare("UPDATE channel_room SET name = ? WHERE channel_id = /** @param {DiscordClient} discord */ function onPacket(discord, event, unsubscribe) { - if (event.t === "READY") { - for (const obj of event.d.guilds) { - expectedGuilds.add(obj.id) - } + if (event.t === "READY") { + for (const obj of event.d.guilds) { + expectedGuilds.add(obj.id) + } - } else if (event.t === "GUILD_CREATE") { - expectedGuilds.delete(event.d.id) + } else if (event.t === "GUILD_CREATE") { + expectedGuilds.delete(event.d.id) - // Store the channel. - for (const channel of event.d.channels || []) { - prepared.run(channel.name, channel.id) - } + // Store the channel. + for (const channel of event.d.channels || []) { + prepared.run(channel.name, channel.id) + } - // Checked them all? - if (expectedGuilds.size === 0) { - discord.cloud.disconnect() - unsubscribe() + // Checked them all? + if (expectedGuilds.size === 0) { + discord.cloud.disconnect() + unsubscribe() - // I don't know why node keeps running. - setTimeout(() => { - console.log("Stopping now.") - process.exit() - }, 1500).unref() - } - } + // I don't know why node keeps running. + setTimeout(() => { + console.log("Stopping now.") + process.exit() + }, 1500).unref() + } + } } diff --git a/scripts/save-event-types-to-db.js b/scripts/save-event-types-to-db.js index 83f5d2b..547e85c 100644 --- a/scripts/save-event-types-to-db.js +++ b/scripts/save-event-types-to-db.js @@ -18,13 +18,13 @@ const rows = db.prepare("SELECT event_id, room_id, event_type FROM event_message const preparedUpdate = db.prepare("UPDATE event_message SET event_type = ?, event_subtype = ? WHERE event_id = ?") ;(async () => { - for (const row of rows) { - if (row.event_type == null) { - const event = await api.getEvent(row.room_id, row.event_id) - const type = event.type - const subtype = event.content.msgtype || null - preparedUpdate.run(type, subtype, row.event_id) - console.log(`Updated ${row.event_id} -> ${type} + ${subtype}`) - } - } + for (const row of rows) { + if (row.event_type == null) { + const event = await api.getEvent(row.room_id, row.event_id) + const type = event.type + const subtype = event.content.msgtype || null + preparedUpdate.run(type, subtype, row.event_id) + console.log(`Updated ${row.event_id} -> ${type} + ${subtype}`) + } + } })()