a super shitty discord bot but i needed logging
Some checks failed
Deploy Backend / deploy (push) Failing after 1m29s
Deploy Website / deploy (push) Has been cancelled

This commit is contained in:
Lee 2024-10-17 18:29:30 +01:00
parent 373a6355a6
commit a086bebc40
8 changed files with 122 additions and 34 deletions

BIN
bun.lockb

Binary file not shown.

@ -18,7 +18,7 @@
"@typescript-eslint/eslint-plugin": "^8.9.0", "@typescript-eslint/eslint-plugin": "^8.9.0",
"@typescript-eslint/parser": "^8.9.0", "@typescript-eslint/parser": "^8.9.0",
"@vercel/og": "^0.6.3", "@vercel/og": "^0.6.3",
"discord-webhook-node": "^1.1.8", "discordx": "^11.12.1",
"elysia": "latest", "elysia": "latest",
"elysia-autoroutes": "^0.5.0", "elysia-autoroutes": "^0.5.0",
"elysia-decorators": "^1.0.2", "elysia-decorators": "^1.0.2",

@ -0,0 +1,53 @@
import { Client, MetadataStorage } from "discordx";
import { Config } from "@ssr/common/config";
import { ActivityType, EmbedBuilder } from "discord.js";
export enum DiscordChannels {
trackedPlayerLogs = "1295985197262569512",
numberOneFeed = "1295988063817830430",
backendLogs = "1296524935237468250",
}
export const DiscordBot = new Client({
intents: [],
presence: {
status: "online",
activities: [
{
name: "scores...",
type: ActivityType.Watching,
url: "https://ssr.fascinated.cc",
},
],
},
});
DiscordBot.once("ready", () => {
console.log("Discord bot ready!");
});
export function initDiscordBot() {
console.log("Initializing discord bot...");
MetadataStorage.instance.build().then(async () => {
await DiscordBot.login(Config.discordBotToken!).then();
});
}
/**
* Logs the message to a discord channel.
*
* @param channelId the channel id to log to
* @param message the message to log
*/
export function logToChannel(channelId: DiscordChannels, message: EmbedBuilder) {
const channel = DiscordBot.channels.cache.find(c => c.id === channelId);
if (channel == undefined) {
throw new Error(`Channel "${channelId}" not found`);
}
if (!channel.isSendable()) {
throw new Error(`Channel "${channelId}" is not sendable`);
}
channel.send({ embeds: [message] });
}

@ -15,7 +15,7 @@ import PlayerController from "./controller/player.controller";
import { PlayerService } from "./service/player.service"; import { PlayerService } from "./service/player.service";
import { cron } from "@elysiajs/cron"; import { cron } from "@elysiajs/cron";
import { scoresaberService } from "@ssr/common/service/impl/scoresaber"; import { scoresaberService } from "@ssr/common/service/impl/scoresaber";
import { delay } from "@ssr/common/utils/utils"; import { delay, isProduction } from "@ssr/common/utils/utils";
import { connectScoreSaberWebSocket } from "@ssr/common/websocket/scoresaber-websocket"; import { connectScoreSaberWebSocket } from "@ssr/common/websocket/scoresaber-websocket";
import ImageController from "./controller/image.controller"; import ImageController from "./controller/image.controller";
import ReplayController from "./controller/replay.controller"; import ReplayController from "./controller/replay.controller";
@ -24,6 +24,8 @@ import { Config } from "@ssr/common/config";
import { PlayerDocument, PlayerModel } from "@ssr/common/model/player"; import { PlayerDocument, PlayerModel } from "@ssr/common/model/player";
import ScoresController from "./controller/scores.controller"; import ScoresController from "./controller/scores.controller";
import LeaderboardController from "./controller/leaderboard.controller"; import LeaderboardController from "./controller/leaderboard.controller";
import { DiscordChannels, initDiscordBot, logToChannel } from "./bot/bot";
import { EmbedBuilder } from "discord.js";
// Load .env file // Load .env file
dotenv.config({ dotenv.config({
@ -40,6 +42,12 @@ connectScoreSaberWebSocket({
await PlayerService.trackScore(playerScore); await PlayerService.trackScore(playerScore);
await ScoreService.notifyNumberOne(playerScore); await ScoreService.notifyNumberOne(playerScore);
}, },
onDisconnect: error => {
logToChannel(
DiscordChannels.backendLogs,
new EmbedBuilder().setDescription(`ScoreSaber websocket disconnected: ${error}`)
);
},
}); });
export const app = new Elysia(); export const app = new Elysia();
@ -179,6 +187,9 @@ app.use(swagger());
app.onStart(() => { app.onStart(() => {
console.log("Listening on port http://localhost:8080"); console.log("Listening on port http://localhost:8080");
if (isProduction()) {
initDiscordBot();
}
}); });
app.listen(8080); app.listen(8080);

@ -10,7 +10,8 @@ import ScoreSaberPlayerScoreToken from "@ssr/common/types/token/scoresaber/score
import { MessageBuilder, Webhook } from "discord-webhook-node"; import { MessageBuilder, Webhook } from "discord-webhook-node";
import { formatPp } from "@ssr/common/utils/number-utils"; import { formatPp } from "@ssr/common/utils/number-utils";
import { isProduction } from "@ssr/common/utils/utils"; import { isProduction } from "@ssr/common/utils/utils";
import { Config } from "@ssr/common/config"; import { DiscordChannels, logToChannel } from "../bot/bot";
import { EmbedBuilder } from "discord.js";
export class PlayerService { export class PlayerService {
/** /**
@ -43,19 +44,31 @@ export class PlayerService {
// Only notify in production // Only notify in production
if (isProduction()) { if (isProduction()) {
const hook = new Webhook({ logToChannel(
url: Config.trackedPlayerWebhook, DiscordChannels.trackedPlayerLogs,
}); new EmbedBuilder()
hook.setUsername("Player Tracker"); .setTitle("New Player Tracked")
const embed = new MessageBuilder(); .setDescription(`https://ssr.fascinated.cc/player/${playerToken.id}`)
embed.setTitle("New Player Tracked"); .addFields([
embed.addField("Username", playerToken.name, true); {
embed.addField("ID", playerToken.id, true); name: "Username",
embed.addField("PP", formatPp(playerToken.pp) + "pp", true); value: playerToken.name,
embed.setDescription(`https://ssr.fascinated.cc/player/${playerToken.id}`); inline: true,
embed.setThumbnail(playerToken.profilePicture); },
embed.setColor("#00ff00"); {
await hook.send(embed); name: "ID",
value: playerToken.id,
inline: true,
},
{
name: "PP",
value: formatPp(playerToken.pp) + "pp",
inline: true,
},
])
.setThumbnail(playerToken.profilePicture)
.setColor("#00ff00")
);
} }
} catch (err) { } catch (err) {
const message = `Failed to create player document for "${id}"`; const message = `Failed to create player document for "${id}"`;

@ -4,7 +4,6 @@ import ScoreSaberPlayerScoreToken from "@ssr/common/types/token/scoresaber/score
import { MessageBuilder, Webhook } from "discord-webhook-node"; import { MessageBuilder, Webhook } from "discord-webhook-node";
import { formatPp } from "@ssr/common/utils/number-utils"; import { formatPp } from "@ssr/common/utils/number-utils";
import { isProduction } from "@ssr/common/utils/utils"; import { isProduction } from "@ssr/common/utils/utils";
import { Config } from "@ssr/common/config";
import { Metadata } from "@ssr/common/types/metadata"; import { Metadata } from "@ssr/common/types/metadata";
import { NotFoundError } from "elysia"; import { NotFoundError } from "elysia";
import BeatSaverService from "./beatsaver.service"; import BeatSaverService from "./beatsaver.service";
@ -20,6 +19,8 @@ import { PlayerScore } from "@ssr/common/score/player-score";
import LeaderboardScoresResponse from "@ssr/common/response/leaderboard-scores-response"; import LeaderboardScoresResponse from "@ssr/common/response/leaderboard-scores-response";
import Score from "@ssr/common/score/score"; import Score from "@ssr/common/score/score";
import PlayerScoresResponse from "@ssr/common/response/player-scores-response"; import PlayerScoresResponse from "@ssr/common/response/player-scores-response";
import { DiscordChannels, logToChannel } from "../bot/bot";
import { EmbedBuilder } from "discord.js";
export class ScoreService { export class ScoreService {
/** /**
@ -45,20 +46,20 @@ export class ScoreService {
return; return;
} }
const hook = new Webhook({ logToChannel(
url: Config.numberOneWebhook, DiscordChannels.numberOneFeed,
}); new EmbedBuilder()
hook.setUsername("Number One Feed"); .setTitle(`${player.name} set a #1 on ${leaderboard.songName} ${leaderboard.songSubName}`)
const embed = new MessageBuilder(); .setDescription(
embed.setTitle(`${player.name} set a #${score.rank} on ${leaderboard.songName} ${leaderboard.songSubName}`); `
embed.setDescription(` **Player:** https://ssr.fascinated.cc/player/${player.id}
**Player:** https://ssr.fascinated.cc/player/${player.id} **Leaderboard:** https://ssr.fascinated.cc/leaderboard/${leaderboard.id}
**Leaderboard:** https://ssr.fascinated.cc/leaderboard/${leaderboard.id} **PP:** ${formatPp(score.pp)}
**PP:** ${formatPp(score.pp)} `
`); )
embed.setThumbnail(leaderboard.coverImage); .setThumbnail(leaderboard.coverImage)
embed.setColor("#00ff00"); .setColor("#00ff00")
await hook.send(embed); );
} }
/** /**

@ -11,4 +11,5 @@ export const Config = {
trackedPlayerWebhook: process.env.TRACKED_PLAYERS_WEBHOOK, trackedPlayerWebhook: process.env.TRACKED_PLAYERS_WEBHOOK,
numberOneWebhook: process.env.NUMBER_ONE_WEBHOOK, numberOneWebhook: process.env.NUMBER_ONE_WEBHOOK,
mongoUri: process.env.MONGO_URI, mongoUri: process.env.MONGO_URI,
discordBotToken: process.env.DISCORD_BOT_TOKEN,
} as const; } as const;

@ -5,22 +5,29 @@ type ScoresaberSocket = {
/** /**
* Invoked when a general message is received. * Invoked when a general message is received.
* *
* @param message The received message. * @param message the received message.
*/ */
onMessage?: (message: unknown) => void; onMessage?: (message: unknown) => void;
/** /**
* Invoked when a score message is received. * Invoked when a score message is received.
* *
* @param score The received score data. * @param score the received score data.
*/ */
onScore?: (score: ScoreSaberPlayerScoreToken) => void; onScore?: (score: ScoreSaberPlayerScoreToken) => void;
/**
* Invoked when the connection is closed.
*
* @param error the error that caused the connection to close
*/
onDisconnect?: (error: WebSocket.ErrorEvent) => void;
}; };
/** /**
* Connects to the ScoreSaber websocket and handles incoming messages. * Connects to the ScoreSaber websocket and handles incoming messages.
*/ */
export function connectScoreSaberWebSocket({ onMessage, onScore }: ScoresaberSocket) { export function connectScoreSaberWebSocket({ onMessage, onScore, onDisconnect }: ScoresaberSocket) {
let websocket: WebSocket | null = null; let websocket: WebSocket | null = null;
function connectWs() { function connectWs() {
@ -35,6 +42,8 @@ export function connectScoreSaberWebSocket({ onMessage, onScore }: ScoresaberSoc
if (websocket) { if (websocket) {
websocket.close(); // Close the connection on error websocket.close(); // Close the connection on error
} }
onDisconnect && onDisconnect(error);
}; };
websocket.onclose = () => { websocket.onclose = () => {