Per-guild presence sync settings
On by default for existing and small guilds. Off for new large guilds. Can be toggled either way.
This commit is contained in:
		
							parent
							
								
									69e3d64905
								
							
						
					
					
						commit
						0f435e930e
					
				
					 8 changed files with 56 additions and 7 deletions
				
			
		|  | @ -31,6 +31,8 @@ async function createSpace(guild, kstate) { | |||
| 	const topic = kstate["m.room.topic/"]?.topic || undefined | ||||
| 	assert(name) | ||||
| 
 | ||||
| 	const memberCount = guild["member_count"] ?? guild.approximate_member_count ?? 0 | ||||
| 	const enablePresenceByDefault = +(memberCount < 150) // could increase this later on if it doesn't cause any problems
 | ||||
| 	const globalAdmins = select("member_power", "mxid", {room_id: "*"}).pluck().all() | ||||
| 
 | ||||
| 	const roomID = await createRoom.postApplyPowerLevels(kstate, async kstate => { | ||||
|  | @ -50,7 +52,7 @@ async function createSpace(guild, kstate) { | |||
| 			initial_state: await ks.kstateToState(kstate) | ||||
| 		}) | ||||
| 	}) | ||||
| 	db.prepare("INSERT INTO guild_space (guild_id, space_id) VALUES (?, ?)").run(guild.id, roomID) | ||||
| 	db.prepare("INSERT INTO guild_space (guild_id, space_id, presence) VALUES (?, ?, ?)").run(guild.id, roomID, enablePresenceByDefault) | ||||
| 	return roomID | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -11,11 +11,22 @@ const presenceDelay = 1500 | |||
| /** @type {Map<string, NodeJS.Timeout>} user ID -> cancelable timeout */ | ||||
| const presenceDelayMap = new Map() | ||||
| 
 | ||||
| // Access the list of enabled guilds as needed rather than like multiple times per second when a user changes presence
 | ||||
| /** @type {Set<string>} */ | ||||
| let presenceEnabledGuilds | ||||
| function checkPresenceEnabledGuilds() { | ||||
| 	presenceEnabledGuilds = new Set(select("guild_space", "guild_id", {presence: 1}).pluck().all()) | ||||
| } | ||||
| checkPresenceEnabledGuilds() | ||||
| 
 | ||||
| /** | ||||
|  * @param {string} userID Discord user ID | ||||
|  * @param {string} guildID Discord guild ID that this presence applies to (really, the same presence applies to every single guild, but is delivered separately) | ||||
|  * @param {string} status status field from Discord's PRESENCE_UPDATE event | ||||
|  */ | ||||
| function setPresence(userID, status) { | ||||
| function setPresence(userID, guildID, status) { | ||||
| 	// check if we care about this guild
 | ||||
| 	if (!presenceEnabledGuilds.has(guildID)) return | ||||
| 	// cancel existing timer if one is already set
 | ||||
| 	if (presenceDelayMap.has(userID)) { | ||||
| 		clearTimeout(presenceDelayMap.get(userID)) | ||||
|  | @ -43,3 +54,4 @@ function setPresenceCallback(user_id, status) { | |||
| } | ||||
| 
 | ||||
| module.exports.setPresence = setPresence | ||||
| module.exports.checkPresenceEnabledGuilds = checkPresenceEnabledGuilds | ||||
|  |  | |||
|  | @ -198,7 +198,7 @@ const utils = { | |||
| 					await interactions.dispatchInteraction(message.d) | ||||
| 
 | ||||
| 				} else if (message.t === "PRESENCE_UPDATE") { | ||||
| 					eventDispatcher.onPresenceUpdate(message.d.user.id, message.d.status) | ||||
| 					eventDispatcher.onPresenceUpdate(client, message.d) | ||||
| 				} | ||||
| 
 | ||||
| 			} catch (e) { | ||||
|  |  | |||
|  | @ -374,11 +374,12 @@ module.exports = { | |||
| 	}, | ||||
| 
 | ||||
| 	/** | ||||
| 	 * @param {string} userID | ||||
| 	 * @param {string} [status] | ||||
| 	 * @param {import("./discord-client")} client | ||||
| 	 * @param {DiscordTypes.GatewayPresenceUpdateDispatchData} data | ||||
| 	 */ | ||||
| 	async onPresenceUpdate(userID, status) { | ||||
| 	async onPresenceUpdate(client, data) { | ||||
| 		const status = data.status | ||||
| 		if (!status) return | ||||
| 		setPresence.setPresence(userID, status) | ||||
| 		setPresence.setPresence(data.user.id, data.guild_id, status) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
							
								
								
									
										5
									
								
								src/db/migrations/0020-add-presence-to-guild-space.sql
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/db/migrations/0020-add-presence-to-guild-space.sql
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | |||
| BEGIN TRANSACTION; | ||||
| 
 | ||||
| ALTER TABLE guild_space ADD COLUMN presence INTEGER NOT NULL DEFAULT 1; | ||||
| 
 | ||||
| COMMIT; | ||||
							
								
								
									
										1
									
								
								src/db/orm-defs.d.ts
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								src/db/orm-defs.d.ts
									
										
									
									
										vendored
									
									
								
							|  | @ -33,6 +33,7 @@ export type Models = { | |||
| 		guild_id: string | ||||
| 		space_id: string | ||||
| 		privacy_level: number | ||||
| 		presence: 0 | 1 | ||||
| 	} | ||||
| 
 | ||||
| 	guild_active: { | ||||
|  |  | |||
|  | @ -102,6 +102,17 @@ block body | |||
|       #autocreate-loading | ||||
| 
 | ||||
|   if space_id | ||||
|     h3.mt32.fs-category Presence | ||||
|     .s-card | ||||
|       form.d-flex.ai-center.g8 | ||||
|         label.s-label.fl-grow1(for="presence") | ||||
|           | Show online statuses on Matrix | ||||
|           p.s-description This might cause lag on really big Discord servers. | ||||
|         - let value = !!select("guild_space", "presence", {guild_id}).pluck().get() | ||||
|         input(type="hidden" name="guild_id" value=guild_id) | ||||
|         input.s-toggle-switch.order-last#autocreate(name="presence" type="checkbox" hx-post="/api/presence" hx-indicator="#presence-loading" hx-disabled-elt="this" checked=value autocomplete="off") | ||||
|         #presence-loading | ||||
| 
 | ||||
|     h3.mt32.fs-category Privacy level | ||||
|     .s-card | ||||
|       form(hx-post="/api/privacy-level" hx-trigger="change" hx-indicator="#privacy-level-loading" hx-disabled-elt="input") | ||||
|  |  | |||
|  | @ -8,6 +8,8 @@ const {as, db, sync, select} = require("../../passthrough") | |||
| 
 | ||||
| /** @type {import("../auth")} */ | ||||
| const auth = sync.require("../auth") | ||||
| /** @type {import("../../d2m/actions/set-presence")} */ | ||||
| const setPresence = sync.require("../../d2m/actions/set-presence") | ||||
| 
 | ||||
| /** | ||||
|  * @param {H3Event} event | ||||
|  | @ -25,6 +27,10 @@ const schema = { | |||
| 		guild_id: z.string(), | ||||
| 		autocreate: z.string().optional() | ||||
| 	}), | ||||
| 	presence: z.object({ | ||||
| 		guild_id: z.string(), | ||||
| 		presence: z.string().optional() | ||||
| 	}), | ||||
| 	privacyLevel: z.object({ | ||||
| 		guild_id: z.string(), | ||||
| 		level: z.enum(levels) | ||||
|  | @ -51,6 +57,17 @@ as.router.post("/api/autocreate", defineEventHandler(async event => { | |||
| 	return null // 204
 | ||||
| })) | ||||
| 
 | ||||
| as.router.post("/api/presence", defineEventHandler(async event => { | ||||
| 	const parsedBody = await readValidatedBody(event, schema.presence.parse) | ||||
| 	const managed = await auth.getManagedGuilds(event) | ||||
| 	if (!managed.has(parsedBody.guild_id)) throw createError({status: 403, message: "Forbidden", data: "Can't change settings for a guild you don't have Manage Server permissions in"}) | ||||
| 
 | ||||
| 	db.prepare("UPDATE guild_space SET presence = ? WHERE guild_id = ?").run(+!!parsedBody.presence, parsedBody.guild_id) | ||||
| 	setPresence.checkPresenceEnabledGuilds() | ||||
| 
 | ||||
| 	return null // 204
 | ||||
| })) | ||||
| 
 | ||||
| as.router.post("/api/privacy-level", defineEventHandler(async event => { | ||||
| 	const parsedBody = await readValidatedBody(event, schema.privacyLevel.parse) | ||||
| 	const managed = await auth.getManagedGuilds(event) | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue