d->m: Presence
This commit is contained in:
		
							parent
							
								
									f98c30cac3
								
							
						
					
					
						commit
						8ad0117fd2
					
				
					 6 changed files with 70 additions and 1 deletions
				
			
		|  | @ -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 | ||||
|  |  | |||
							
								
								
									
										45
									
								
								src/d2m/actions/set-presence.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/d2m/actions/set-presence.js
									
										
									
									
									
										Normal 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 | ||||
|  | @ -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, | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue