first commit
This commit is contained in:
commit
57562c5ba6
23 changed files with 1979 additions and 0 deletions
212
src/helpers/discord.bot.ts
Normal file
212
src/helpers/discord.bot.ts
Normal 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;
|
Loading…
Add table
Add a link
Reference in a new issue