first commit

This commit is contained in:
creations 2025-01-16 11:34:53 -05:00
commit 57562c5ba6
Signed by: creations
GPG key ID: 8F553AA4320FC711
23 changed files with 1979 additions and 0 deletions

212
src/helpers/discord.bot.ts Normal file
View file

@ -0,0 +1,212 @@
import { Client, Guild, Member, type JSONMember, type Presence, type Uncached } from "oceanic.js";
import { environment } from "../../config/environment";
import { getSteamGameIcon } from "../api/status";
class DiscordBot {
private client: Client;
private watchIds: string[] = environment.discord.watchIds.map((id) => id.toString());
private userPresences: Map<string, Presence>;
private connectedSockets: Set<WebSocket>;
private usersInfo: Map<string, any>;
constructor() {
this.client = new Client({
auth: `Bot ${environment.discord.auth.token}`,
gateway: {
intents: ["GUILDS", "GUILD_PRESENCES"]
}
});
this.userPresences = new Map();
this.usersInfo = new Map();
this.connectedSockets = new Set();
this.watchIds = environment.discord.watchIds.map((id) => id.toString());
}
public async start(): Promise<void> {
try {
await this.client.connect();
await this.setupListeners();
} catch (error) {
console.error("Failed to connect to Discord: ", error);
}
}
public async stop(): Promise<void> {
try {
this.client.disconnect();
} catch (error) {
console.error("Failed to disconnect from Discord: ", error);
}
}
public getClient(): Client {
return this.client;
}
public getUserPresences(): Map<string, Presence> {
return this.userPresences;
}
public getUsersInfo(): Map<string, any> {
return this.usersInfo;
}
public addWebSocket(socket: WebSocket): void {
this.connectedSockets.add(socket);
}
public removeWebSocket(socket: WebSocket): void {
this.connectedSockets.delete(socket);
}
private async broadcastPresenceUpdate(userId: string, presence: Presence): Promise<void> {
const updatedActivities = presence.activities
? await Promise.all(
presence.activities.map(async (activity) => {
if (activity && activity.type === 0 && (!activity.assets || !activity.assets.largeImage)) {
const gameName = activity.name;
// should use what discord uses but i dont care enough to switch it out
const iconUrl = await getSteamGameIcon(gameName);
if (iconUrl) {
return {
...activity,
assets: {
largeImage: iconUrl,
largeText: gameName
}
};
}
}
return activity;
})
)
: [];
const updateMessage = JSON.stringify({
type: "presenceUpdate",
userId,
presence: {
status: presence.status,
activities: updatedActivities
}
});
this.broadcastToWebSockets(updateMessage);
}
private async fetchUsersInfo(): Promise<void> {
const guild = this.client.guilds.get(environment.discord.auth.guildId);
if (!guild) return;
for (const id of this.watchIds) {
try {
const member = await guild.getMember(id);
if (member) {
const memberData = this.serializeMember(member);
this.usersInfo.set(member.user.id, memberData);
const updateMessage = JSON.stringify({
type: "memberUpdate",
user: memberData
});
this.broadcastToWebSockets(updateMessage);
}
} catch (error) {
console.error(`Failed to fetch member with ID ${id}:`, error);
}
}
}
private async fetchUserPresences(): Promise<void> {
const guild = this.client.guilds.get(environment.discord.auth.guildId);
if (!guild) return;
this.watchIds.forEach((id) => {
const member = guild.members.get(id);
if (member && member.presence) {
this.userPresences.set(member.user.id, member.presence);
}
});
}
private async setupListeners(): Promise<void> {
this.client.on("ready", async () => {
const user = this.client.user;
const guild = this.client.guilds.get(environment.discord.auth.guildId);
if (user && guild) {
await this.fetchUserPresences();
await this.fetchUsersInfo();
}
});
this.client.on("presenceUpdate", (guild: Guild | Uncached, member: Member | Uncached, presence: Presence, oldPresence: Presence | null) => {
if (!(member instanceof Member)) return;
const userId = member.user.id.toString();
if (!this.watchIds.includes(userId)) return;
const oldStatus = oldPresence;
const newStatus = presence;
if (oldStatus !== newStatus) {
this.userPresences.set(userId, presence);
this.broadcastPresenceUpdate(userId, presence);
}
});
this.client.on("guildMemberUpdate", (member: Member, oldMember: JSONMember | null) => {
const userId = member.user.id.toString();
if (!this.watchIds.includes(userId)) return;
const memberData = this.serializeMember(member);
this.usersInfo.set(userId, memberData);
const updateMessage = JSON.stringify({
type: "memberUpdate",
user: memberData
});
this.broadcastToWebSockets(updateMessage);
});
}
private serializeMember(member: Member): object {
const user = member.user;
return {
id: user.id,
username: user.username,
discriminator: user.discriminator,
nick: member.nick ?? user.username,
avatar: user.avatar
? `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}`
: `https://cdn.discordapp.com/embed/avatars/${parseInt(user.discriminator) % 5}`,
avatarDecoration: user.avatarDecorationData?.asset
? `https://cdn.discordapp.com/avatar-decoration-presets/${user.avatarDecorationData.asset}`
: null,
roles: member.roles,
joinedAt: member.joinedAt,
premiumSince: member.premiumSince,
user: user
};
}
private broadcastToWebSockets(message: string): void {
for (const socket of this.connectedSockets) {
if (socket.readyState === WebSocket.OPEN) {
socket.send(message);
}
}
}
}
const discordBot = new DiscordBot();
export { discordBot };
export default discordBot;