changing spaces to tabs

This commit is contained in:
Cadence Ember 2023-08-21 21:04:41 +12:00
parent c22f434c1f
commit c8021cadec
9 changed files with 154 additions and 120 deletions

View file

@ -1,2 +1,5 @@
{ {
"editor.insertSpaces": false,
"editor.detectIndentation": false,
"editor.tabSize": 3
} }

View file

@ -15,20 +15,20 @@ const createRoom = sync.require("../actions/create-room")
* @param {import("discord-api-types/v10").GatewayMessageReactionAddDispatchData} data * @param {import("discord-api-types/v10").GatewayMessageReactionAddDispatchData} data
*/ */
async function addReaction(data) { async function addReaction(data) {
const user = data.member?.user const user = data.member?.user
assert.ok(user && user.username) 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 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. if (!parentID) return // Nothing can be done if the parent message was never bridged.
assert.equal(typeof parentID, "string") assert.equal(typeof parentID, "string")
const roomID = await createRoom.ensureRoom(data.channel_id) const roomID = await createRoom.ensureRoom(data.channel_id)
const senderMxid = await registerUser.ensureSimJoined(user, roomID) const senderMxid = await registerUser.ensureSimJoined(user, roomID)
const eventID = await api.sendEvent(roomID, "m.reaction", { const eventID = await api.sendEvent(roomID, "m.reaction", {
"m.relates_to": { "m.relates_to": {
rel_type: "m.annotation", rel_type: "m.annotation",
event_id: parentID, event_id: parentID,
key: data.emoji.name key: data.emoji.name
} }
}, senderMxid) }, senderMxid)
return eventID return eventID
} }

View file

@ -52,10 +52,10 @@ async function withWebhook(channelID, callback) {
* @param {string} [threadID] * @param {string} [threadID]
*/ */
async function sendMessageWithWebhook(channelID, data, threadID) { async function sendMessageWithWebhook(channelID, data, threadID) {
const result = await withWebhook(channelID, async webhook => { 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 discord.snow.webhook.executeWebhook(webhook.id, webhook.token, data, {wait: true, thread_id: threadID, disableEveryone: true})
}) })
return result return result
} }
module.exports.ensureWebhook = ensureWebhook module.exports.ensureWebhook = ensureWebhook

View file

@ -12,7 +12,7 @@ const eventToMessage = sync.require("../converters/event-to-message")
/** @param {import("../../types").Event.Outer<any>} event */ /** @param {import("../../types").Event.Outer<any>} event */
async function sendEvent(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) 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 channelID = row.channel_id
let threadID = undefined 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... 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) const messages = eventToMessage.eventToMessage(event)
assert(Array.isArray(messages)) // sanity assert(Array.isArray(messages)) // sanity
/** @type {DiscordTypes.APIMessage[]} */ /** @type {DiscordTypes.APIMessage[]} */
const messageResponses = [] const messageResponses = []
let eventPart = 0 // 0 is primary, 1 is supporting let eventPart = 0 // 0 is primary, 1 is supporting
for (const message of messages) { 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 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? eventPart = 1 // TODO: use more intelligent algorithm to determine whether primary or supporting?

View file

@ -19,15 +19,15 @@ const makeTxnId = sync.require("./txnid")
* @returns {string} the new endpoint * @returns {string} the new endpoint
*/ */
function path(p, mxid, otherParams = {}) { function path(p, mxid, otherParams = {}) {
if (!mxid) return p if (!mxid) return p
const u = new URL(p, "http://localhost") const u = new URL(p, "http://localhost")
u.searchParams.set("user_id", mxid) u.searchParams.set("user_id", mxid)
for (const entry of Object.entries(otherParams)) { for (const entry of Object.entries(otherParams)) {
if (entry[1] != undefined) { if (entry[1] != undefined) {
u.searchParams.set(entry[0], entry[1]) u.searchParams.set(entry[0], entry[1])
} }
} }
return u.pathname + "?" + u.searchParams.toString() return u.pathname + "?" + u.searchParams.toString()
} }
/** /**
@ -35,40 +35,40 @@ function path(p, mxid, otherParams = {}) {
* @returns {Promise<Ty.R.Registered>} * @returns {Promise<Ty.R.Registered>}
*/ */
function register(username) { function register(username) {
console.log(`[api] register: ${username}`) console.log(`[api] register: ${username}`)
return mreq.mreq("POST", "/client/v3/register", { return mreq.mreq("POST", "/client/v3/register", {
type: "m.login.application_service", type: "m.login.application_service",
username username
}) })
} }
/** /**
* @returns {Promise<string>} room ID * @returns {Promise<string>} room ID
*/ */
async function createRoom(content) { async function createRoom(content) {
console.log(`[api] create room:`, content) console.log(`[api] create room:`, content)
/** @type {Ty.R.RoomCreated} */ /** @type {Ty.R.RoomCreated} */
const root = await mreq.mreq("POST", "/client/v3/createRoom", content) const root = await mreq.mreq("POST", "/client/v3/createRoom", content)
return root.room_id return root.room_id
} }
/** /**
* @returns {Promise<string>} room ID * @returns {Promise<string>} room ID
*/ */
async function joinRoom(roomIDOrAlias, mxid) { async function joinRoom(roomIDOrAlias, mxid) {
/** @type {Ty.R.RoomJoined} */ /** @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))
return root.room_id return root.room_id
} }
async function inviteToRoom(roomID, mxidToInvite, mxid) { async function inviteToRoom(roomID, mxidToInvite, mxid) {
await mreq.mreq("POST", path(`/client/v3/rooms/${roomID}/invite`, mxid), { await mreq.mreq("POST", path(`/client/v3/rooms/${roomID}/invite`, mxid), {
user_id: mxidToInvite user_id: mxidToInvite
}) })
} }
async function leaveRoom(roomID, mxid) { 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 * @template T
*/ */
async function getEvent(roomID, eventID) { async function getEvent(roomID, eventID) {
/** @type {Ty.Event.Outer<T>} */ /** @type {Ty.Event.Outer<T>} */
const root = await mreq.mreq("GET", `/client/v3/rooms/${roomID}/event/${eventID}`) const root = await mreq.mreq("GET", `/client/v3/rooms/${roomID}/event/${eventID}`)
return root return root
} }
/** /**
@ -87,7 +87,17 @@ async function getEvent(roomID, eventID) {
* @returns {Promise<Ty.Event.BaseStateEvent[]>} * @returns {Promise<Ty.Event.BaseStateEvent[]>}
*/ */
function getAllState(roomID) { 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}}>} * @returns {Promise<{joined: {[mxid: string]: Ty.R.RoomMember}}>}
*/ */
function getJoinedMembers(roomID) { 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<string>} event ID * @returns {Promise<string>} event ID
*/ */
async function sendState(roomID, type, stateKey, content, mxid) { async function sendState(roomID, type, stateKey, content, mxid) {
console.log(`[api] state: ${roomID}: ${type}/${stateKey}`) console.log(`[api] state: ${roomID}: ${type}/${stateKey}`)
assert.ok(type) assert.ok(type)
assert.ok(typeof stateKey === "string") assert.ok(typeof stateKey === "string")
/** @type {Ty.R.EventSent} */ /** @type {Ty.R.EventSent} */
const root = await mreq.mreq("PUT", path(`/client/v3/rooms/${roomID}/state/${type}/${stateKey}`, mxid), content) const root = await mreq.mreq("PUT", path(`/client/v3/rooms/${roomID}/state/${type}/${stateKey}`, mxid), content)
return root.event_id 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 * @param {number} [timestamp] timestamp of the newly created event, in unix milliseconds
*/ */
async function sendEvent(roomID, type, content, mxid, timestamp) { async function sendEvent(roomID, type, content, mxid, timestamp) {
console.log(`[api] event ${type} to ${roomID} as ${mxid || "default sim"}`) console.log(`[api] event ${type} to ${roomID} as ${mxid || "default sim"}`)
/** @type {Ty.R.EventSent} */ /** @type {Ty.R.EventSent} */
const root = await mreq.mreq("PUT", path(`/client/v3/rooms/${roomID}/send/${type}/${makeTxnId.makeTxnId()}`, mxid, {ts: timestamp}), content) const root = await mreq.mreq("PUT", path(`/client/v3/rooms/${roomID}/send/${type}/${makeTxnId.makeTxnId()}`, mxid, {ts: timestamp}), content)
return root.event_id return root.event_id
} }
/** /**
* @returns {Promise<string>} room ID * @returns {Promise<string>} room ID
*/ */
async function redactEvent(roomID, eventID, mxid) { async function redactEvent(roomID, eventID, mxid) {
/** @type {Ty.R.EventRedacted} */ /** @type {Ty.R.EventRedacted} */
const root = await mreq.mreq("PUT", path(`/client/v3/rooms/${roomID}/redact/${eventID}/${makeTxnId.makeTxnId()}`, mxid), {}) const root = await mreq.mreq("PUT", path(`/client/v3/rooms/${roomID}/redact/${eventID}/${makeTxnId.makeTxnId()}`, mxid), {})
return root.event_id return root.event_id
} }
async function profileSetDisplayname(mxid, displayname) { async function profileSetDisplayname(mxid, displayname) {
await mreq.mreq("PUT", path(`/client/v3/profile/${mxid}/displayname`, mxid), { await mreq.mreq("PUT", path(`/client/v3/profile/${mxid}/displayname`, mxid), {
displayname displayname
}) })
} }
async function profileSetAvatarUrl(mxid, avatar_url) { async function profileSetAvatarUrl(mxid, avatar_url) {
await mreq.mreq("PUT", path(`/client/v3/profile/${mxid}/avatar_url`, mxid), { await mreq.mreq("PUT", path(`/client/v3/profile/${mxid}/avatar_url`, mxid), {
avatar_url 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 module.exports.path = path
@ -164,3 +194,4 @@ module.exports.sendEvent = sendEvent
module.exports.redactEvent = redactEvent module.exports.redactEvent = redactEvent
module.exports.profileSetDisplayname = profileSetDisplayname module.exports.profileSetDisplayname = profileSetDisplayname
module.exports.profileSetAvatarUrl = profileSetAvatarUrl module.exports.profileSetAvatarUrl = profileSetAvatarUrl
module.exports.setUserPower = setUserPower

View file

@ -2,22 +2,22 @@ const {kstateToState, stateToKState, diffKState, kstateStripConditionals} = requ
const {test} = require("supertape") const {test} = require("supertape")
test("kstate strip: strips false conditions", t => { test("kstate strip: strips false conditions", t => {
t.deepEqual(kstateStripConditionals({ t.deepEqual(kstateStripConditionals({
a: {$if: false, value: 2}, a: {$if: false, value: 2},
b: {value: 4} b: {value: 4}
}), { }), {
b: {value: 4} b: {value: 4}
}) })
}) })
test("kstate strip: keeps true conditions while removing $if", t => { test("kstate strip: keeps true conditions while removing $if", t => {
t.deepEqual(kstateStripConditionals({ t.deepEqual(kstateStripConditionals({
a: {$if: true, value: 2}, a: {$if: true, value: 2},
b: {value: 4} b: {value: 4}
}), { }), {
a: {value: 2}, a: {value: 2},
b: {value: 4} b: {value: 4}
}) })
}) })
test("kstate2state: general", t => { test("kstate2state: general", t => {

View file

@ -2,9 +2,9 @@ const {test} = require("supertape")
const reg = require("./read-registration") const reg = require("./read-registration")
test("reg: has necessary parameters", t => { test("reg: has necessary parameters", t => {
const propertiesToCheck = ["sender_localpart", "id", "as_token", "ooye"] const propertiesToCheck = ["sender_localpart", "id", "as_token", "ooye"]
t.deepEqual( t.deepEqual(
propertiesToCheck.filter(p => p in reg), propertiesToCheck.filter(p => p in reg),
propertiesToCheck propertiesToCheck
) )
}) })

View file

@ -18,10 +18,10 @@ passthrough.discord = discord
;(async () => { ;(async () => {
await discord.cloud.connect() 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)) const f = event => onPacket(discord, event, () => discord.cloud.off("event", f))
discord.cloud.on("event", f) discord.cloud.on("event", f)
})() })()
const expectedGuilds = new Set() const expectedGuilds = new Set()
@ -30,29 +30,29 @@ const prepared = db.prepare("UPDATE channel_room SET name = ? WHERE channel_id =
/** @param {DiscordClient} discord */ /** @param {DiscordClient} discord */
function onPacket(discord, event, unsubscribe) { function onPacket(discord, event, unsubscribe) {
if (event.t === "READY") { if (event.t === "READY") {
for (const obj of event.d.guilds) { for (const obj of event.d.guilds) {
expectedGuilds.add(obj.id) expectedGuilds.add(obj.id)
} }
} else if (event.t === "GUILD_CREATE") { } else if (event.t === "GUILD_CREATE") {
expectedGuilds.delete(event.d.id) expectedGuilds.delete(event.d.id)
// Store the channel. // Store the channel.
for (const channel of event.d.channels || []) { for (const channel of event.d.channels || []) {
prepared.run(channel.name, channel.id) prepared.run(channel.name, channel.id)
} }
// Checked them all? // Checked them all?
if (expectedGuilds.size === 0) { if (expectedGuilds.size === 0) {
discord.cloud.disconnect() discord.cloud.disconnect()
unsubscribe() unsubscribe()
// I don't know why node keeps running. // I don't know why node keeps running.
setTimeout(() => { setTimeout(() => {
console.log("Stopping now.") console.log("Stopping now.")
process.exit() process.exit()
}, 1500).unref() }, 1500).unref()
} }
} }
} }

View file

@ -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 = ?") const preparedUpdate = db.prepare("UPDATE event_message SET event_type = ?, event_subtype = ? WHERE event_id = ?")
;(async () => { ;(async () => {
for (const row of rows) { for (const row of rows) {
if (row.event_type == null) { if (row.event_type == null) {
const event = await api.getEvent(row.room_id, row.event_id) const event = await api.getEvent(row.room_id, row.event_id)
const type = event.type const type = event.type
const subtype = event.content.msgtype || null const subtype = event.content.msgtype || null
preparedUpdate.run(type, subtype, row.event_id) preparedUpdate.run(type, subtype, row.event_id)
console.log(`Updated ${row.event_id} -> ${type} + ${subtype}`) console.log(`Updated ${row.event_id} -> ${type} + ${subtype}`)
} }
} }
})() })()