Website Status

This commit is contained in:
2025-11-29 16:40:05 +00:00
parent e02fdf59f4
commit 1b356dd6aa
24 changed files with 1294 additions and 52 deletions
+106
View File
@@ -0,0 +1,106 @@
import { Client, Partials, GatewayIntentBits, ActivityType } from "discord.js";
import { DISCORD_TOKEN, DISCORD_USER_ID, DISCORD_GUILD_ID } from "$env/static/private";
class Bot {
private client: Client;
// Normalize a discord.js Activity object into a simple POJO
private mapActivity(a: any) {
const type = typeof a.type === 'number' ? a.type : (ActivityType[a.type] ?? a.type);
const application_id = a.applicationId ?? a.application_id ?? null;
const rawLarge = a.assets?.largeImage ?? a.assets?.large_image ?? null;
const rawSmall = a.assets?.smallImage ?? a.assets?.small_image ?? null;
const formatAsset = (raw: string | null) => {
if (!raw) return null;
if (raw.startsWith('mp:external/')) {
return `https://media.discordapp.net/${raw.replace(/^mp:/, '')}`;
}
if (/^https?:\/\//.test(raw)) return raw;
if (application_id) return `https://cdn.discordapp.com/app-assets/${application_id}/${raw}.webp`;
return raw;
};
return {
name: a.name ?? null,
type,
details: a.details ?? a.state ?? null,
url: a.url ?? null,
application_id,
assets: {
large_image: formatAsset(rawLarge),
small_image: formatAsset(rawSmall)
},
emoji: a.emoji ? { id: a.emoji.id ?? null, animated: !!a.emoji.animated } : null
};
}
constructor() {
// Need presence and members intents to read user presence/activity
this.client = new Client({
partials: [Partials.User],
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMembers,
GatewayIntentBits.GuildPresences
]
});
this.client.login(DISCORD_TOKEN).catch((error) => {
console.error("Failed to login to Discord:", error);
});
this.client.once("clientReady", async () => {
console.log(`Logged in as ${this.client.user?.tag}`);
});
}
getClient(): Client {
return this.client;
}
/**
* Look up the user's presence specifically in the given guild.
* Returns null if guild/member/presence not available.
*/
async getUserActivityInGuild(guildId: string = DISCORD_GUILD_ID, userId: string = DISCORD_USER_ID) {
try {
// Fetch guild from cache or API
let guild = this.client.guilds.cache.get(guildId) || await this.client.guilds.fetch(guildId).catch(() => null);
if (!guild) return null;
// Fetch member (will populate presence if available)
const member = await guild.members.fetch(userId).catch(() => null);
if (!member) return null;
const presence = member.presence;
const user = member.user;
// Build avatar URL
const avatarHash = user.avatar;
const avatarUrl = avatarHash
? `https://cdn.discordapp.com/avatars/${user.id}/${avatarHash}.${avatarHash.startsWith('a_') ? 'gif' : 'webp'}?size=128`
: `https://cdn.discordapp.com/embed/avatars/${(BigInt(user.id) >> BigInt(22)) % BigInt(6)}.png`;
// Get guild/clan tag if available
const guildTag = (user as any).primaryGuild?.tag ?? null;
const guildTagBadgeImage = `https://cdn.discordapp.com/clan-badges/${(user as any).primaryGuild?.identityGuildId}/${(user as any).primaryGuild?.badge}.png?size=32`;
if (!presence) return { guildId, status: 'offline', activities: [], displayName: member.displayName, avatarUrl, guildTag, guildTagBadgeImage };
const status = presence.status;
const activities = (presence.activities || []).map((a: any) => this.mapActivity(a));
return { guildId, status, activities, displayName: member.displayName, avatarUrl, guildTag, guildTagBadgeImage };
} catch (err) {
return null;
}
}
}
export const bot = new Bot();
// Re-export image helper for convenience
export { fetchUserImages } from "./discord/fetchUserImages";