changing spaces to tabs
This commit is contained in:
parent
c22f434c1f
commit
c8021cadec
9 changed files with 154 additions and 120 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -1,2 +1,5 @@
|
|||
{
|
||||
"editor.insertSpaces": false,
|
||||
"editor.detectIndentation": false,
|
||||
"editor.tabSize": 3
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -12,7 +12,7 @@ const eventToMessage = sync.require("../converters/event-to-message")
|
|||
|
||||
/** @param {import("../../types").Event.Outer<any>} 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?
|
||||
|
|
129
matrix/api.js
129
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<Ty.R.Registered>}
|
||||
*/
|
||||
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<string>} 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<string>} 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<T>} */
|
||||
const root = await mreq.mreq("GET", `/client/v3/rooms/${roomID}/event/${eventID}`)
|
||||
return root
|
||||
/** @type {Ty.Event.Outer<T>} */
|
||||
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<Ty.Event.BaseStateEvent[]>}
|
||||
*/
|
||||
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<string>} 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<string>} 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
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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
|
||||
)
|
||||
})
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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}`)
|
||||
}
|
||||
}
|
||||
})()
|
||||
|
|
Loading…
Reference in a new issue