d->m: Presence

This commit is contained in:
Cadence Ember
2025-02-10 15:04:34 +13:00
parent f98c30cac3
commit 8ad0117fd2
6 changed files with 70 additions and 1 deletions

View File

@@ -36,6 +36,7 @@ Most features you'd expect in both directions, plus a little extra spice:
* Attachments
* Spoiler attachments
* Embeds
* Presence
* Guild-Space details syncing
* Channel-Room details syncing
* Custom emoji list syncing

View File

@@ -0,0 +1,45 @@
// @ts-check
const passthrough = require("../../passthrough")
const {sync, select} = passthrough
/** @type {import("../../matrix/api")} */
const api = sync.require("../../matrix/api")
// Adding a debounce to all updates because events are issued multiple times, once for each guild.
// Sometimes a status update is even issued twice in a row for the same user+guild, weird!
const presenceDelay = 1500
/** @type {Map<string, NodeJS.Timeout>} user ID -> cancelable timeout */
const presenceDelayMap = new Map()
/**
* @param {string} userID Discord user ID
* @param {string} status status field from Discord's PRESENCE_UPDATE event
*/
function setPresence(userID, status) {
// cancel existing timer if one is already set
if (presenceDelayMap.has(userID)) {
clearTimeout(presenceDelayMap.get(userID))
}
// new timer, which will run if nothing else comes in soon
presenceDelayMap.set(userID, setTimeout(setPresenceCallback, presenceDelay, userID, status).unref())
}
/**
* @param {string} user_id Discord user ID
* @param {string} status status field from Discord's PRESENCE_UPDATE event
*/
function setPresenceCallback(user_id, status) {
presenceDelayMap.delete(user_id)
const mxid = select("sim", "mxid", {user_id}).pluck().get()
if (!mxid) return
const presence =
( status === "online" ? "online"
: status === "offline" ? "offline"
: "unavailable") // idle, dnd, and anything else they dream up in the future
api.setPresence(presence, mxid).catch(e => {
console.error("d->m: Skipping presence update failure:")
console.error(e)
})
}
module.exports.setPresence = setPresence

View File

@@ -28,7 +28,7 @@ class DiscordClient {
intents: [
"DIRECT_MESSAGES", "DIRECT_MESSAGE_REACTIONS", "DIRECT_MESSAGE_TYPING",
"GUILDS", "GUILD_EMOJIS_AND_STICKERS", "GUILD_MESSAGES", "GUILD_MESSAGE_REACTIONS", "GUILD_MESSAGE_TYPING", "GUILD_WEBHOOKS",
"MESSAGE_CONTENT"
"MESSAGE_CONTENT", "GUILD_PRESENCES"
],
ws: {
compress: false,

View File

@@ -196,6 +196,9 @@ const utils = {
} else if (message.t === "INTERACTION_CREATE") {
await interactions.dispatchInteraction(message.d)
} else if (message.t === "PRESENCE_UPDATE") {
eventDispatcher.onPresenceUpdate(message.d.user.id, message.d.status)
}
} catch (e) {

View File

@@ -33,6 +33,8 @@ const mxUtils = require("../m2d/converters/utils")
const speedbump = sync.require("./actions/speedbump")
/** @type {import("./actions/retrigger")} */
const retrigger = sync.require("./actions/retrigger")
/** @type {import("./actions/set-presence")} */
const setPresence = sync.require("./actions/set-presence")
/** @type {any} */ // @ts-ignore bad types from semaphore
const Semaphore = require("@chriscdn/promise-semaphore")
@@ -369,5 +371,14 @@ module.exports = {
*/
async onExpressionsUpdate(client, data) {
await createSpace.syncSpaceExpressions(data, false)
},
/**
* @param {string} userID
* @param {string} [status]
*/
async onPresenceUpdate(userID, status) {
if (!status) return
setPresence.setPresence(userID, status)
}
}

View File

@@ -408,6 +408,14 @@ async function setAccountData(type, content, mxid) {
await mreq.mreq("PUT", `/client/v3/user/${mxid}/account_data/${type}`, content)
}
/**
* @param {"online" | "offline" | "unavailable"} presence
* @param {string} mxid
*/
async function setPresence(presence, mxid) {
await mreq.mreq("PUT", path(`/client/v3/presence/${mxid}/status`, mxid), {presence})
}
module.exports.path = path
module.exports.register = register
module.exports.createRoom = createRoom
@@ -440,3 +448,4 @@ module.exports.ackEvent = ackEvent
module.exports.getAlias = getAlias
module.exports.getAccountData = getAccountData
module.exports.setAccountData = setAccountData
module.exports.setPresence = setPresence