Files
clawdbot/src/discord/monitor/presence-cache.ts
Kentaro Kuribayashi 3e07bd8b48 feat(discord): add configurable privileged Gateway Intents (GuildPresences, GuildMembers) (#2266)
* feat(discord): add configurable privileged Gateway Intents (GuildPresences, GuildMembers)

Add support for optionally enabling Discord privileged Gateway Intents
via config, starting with GuildPresences and GuildMembers.

When `channels.discord.intents.presence` is set to true:
- GatewayIntents.GuildPresences is added to the gateway connection
- A PresenceUpdateListener caches user presence data in memory
- The member-info action includes user status and activities
  (e.g. Spotify listening activity) from the cache

This enables use cases like:
- Seeing what music a user is currently listening to
- Checking user online/offline/idle/dnd status
- Tracking user activities through the bot API

Both intents require Portal opt-in (Discord Developer Portal →
Privileged Gateway Intents) before they can be used.

Changes:
- config: add `channels.discord.intents.{presence,guildMembers}`
- provider: compute intents dynamically from config
- listeners: add DiscordPresenceListener (extends PresenceUpdateListener)
- presence-cache: simple in-memory Map<userId, GatewayPresenceUpdate>
- discord-actions-guild: include cached presence in member-info response
- schema: add labels and descriptions for new config fields

* fix(test): add PresenceUpdateListener to @buape/carbon mock

* Discord: scope presence cache by account

---------

Co-authored-by: kugutsushi <kugutsushi@clawd>
Co-authored-by: Shadow <hi@shadowing.dev>
2026-01-26 10:39:54 -06:00

53 lines
1.5 KiB
TypeScript

import type { GatewayPresenceUpdate } from "discord-api-types/v10";
/**
* In-memory cache of Discord user presence data.
* Populated by PRESENCE_UPDATE gateway events when the GuildPresences intent is enabled.
*/
const presenceCache = new Map<string, Map<string, GatewayPresenceUpdate>>();
function resolveAccountKey(accountId?: string): string {
return accountId ?? "default";
}
/** Update cached presence for a user. */
export function setPresence(
accountId: string | undefined,
userId: string,
data: GatewayPresenceUpdate,
): void {
const accountKey = resolveAccountKey(accountId);
let accountCache = presenceCache.get(accountKey);
if (!accountCache) {
accountCache = new Map();
presenceCache.set(accountKey, accountCache);
}
accountCache.set(userId, data);
}
/** Get cached presence for a user. Returns undefined if not cached. */
export function getPresence(
accountId: string | undefined,
userId: string,
): GatewayPresenceUpdate | undefined {
return presenceCache.get(resolveAccountKey(accountId))?.get(userId);
}
/** Clear cached presence data. */
export function clearPresences(accountId?: string): void {
if (accountId) {
presenceCache.delete(resolveAccountKey(accountId));
return;
}
presenceCache.clear();
}
/** Get the number of cached presence entries. */
export function presenceCacheSize(): number {
let total = 0;
for (const accountCache of presenceCache.values()) {
total += accountCache.size;
}
return total;
}