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/parser": "^8.9.0",
"@vercel/og": "^0.6.3",
"discord-webhook-node": "^1.1.8",
"discordx": "^11.12.1",
"elysia": "latest",
"elysia-autoroutes": "^0.5.0",
"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 { cron } from "@elysiajs/cron";
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 ImageController from "./controller/image.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 ScoresController from "./controller/scores.controller";
import LeaderboardController from "./controller/leaderboard.controller";
import { DiscordChannels, initDiscordBot, logToChannel } from "./bot/bot";
import { EmbedBuilder } from "discord.js";
// Load .env file
dotenv.config({
@ -40,6 +42,12 @@ connectScoreSaberWebSocket({
await PlayerService.trackScore(playerScore);
await ScoreService.notifyNumberOne(playerScore);
},
onDisconnect: error => {
logToChannel(
DiscordChannels.backendLogs,
new EmbedBuilder().setDescription(`ScoreSaber websocket disconnected: ${error}`)
);
},
});
export const app = new Elysia();
@ -179,6 +187,9 @@ app.use(swagger());
app.onStart(() => {
console.log("Listening on port http://localhost:8080");
if (isProduction()) {
initDiscordBot();
}
});
app.listen(8080);

@ -10,7 +10,8 @@ import ScoreSaberPlayerScoreToken from "@ssr/common/types/token/scoresaber/score
import { MessageBuilder, Webhook } from "discord-webhook-node";
import { formatPp } from "@ssr/common/utils/number-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 {
/**
@ -43,19 +44,31 @@ export class PlayerService {
// Only notify in production
if (isProduction()) {
const hook = new Webhook({
url: Config.trackedPlayerWebhook,
});
hook.setUsername("Player Tracker");
const embed = new MessageBuilder();
embed.setTitle("New Player Tracked");
embed.addField("Username", playerToken.name, true);
embed.addField("ID", playerToken.id, true);
embed.addField("PP", formatPp(playerToken.pp) + "pp", true);
embed.setDescription(`https://ssr.fascinated.cc/player/${playerToken.id}`);
embed.setThumbnail(playerToken.profilePicture);
embed.setColor("#00ff00");
await hook.send(embed);
logToChannel(
DiscordChannels.trackedPlayerLogs,
new EmbedBuilder()
.setTitle("New Player Tracked")
.setDescription(`https://ssr.fascinated.cc/player/${playerToken.id}`)
.addFields([
{
name: "Username",
value: playerToken.name,
inline: true,
},
{
name: "ID",
value: playerToken.id,
inline: true,
},
{
name: "PP",
value: formatPp(playerToken.pp) + "pp",
inline: true,
},
])
.setThumbnail(playerToken.profilePicture)
.setColor("#00ff00")
);
}
} catch (err) {
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 { formatPp } from "@ssr/common/utils/number-utils";
import { isProduction } from "@ssr/common/utils/utils";
import { Config } from "@ssr/common/config";
import { Metadata } from "@ssr/common/types/metadata";
import { NotFoundError } from "elysia";
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 Score from "@ssr/common/score/score";
import PlayerScoresResponse from "@ssr/common/response/player-scores-response";
import { DiscordChannels, logToChannel } from "../bot/bot";
import { EmbedBuilder } from "discord.js";
export class ScoreService {
/**
@ -45,20 +46,20 @@ export class ScoreService {
return;
}
const hook = new Webhook({
url: Config.numberOneWebhook,
});
hook.setUsername("Number One Feed");
const embed = new MessageBuilder();
embed.setTitle(`${player.name} set a #${score.rank} on ${leaderboard.songName} ${leaderboard.songSubName}`);
embed.setDescription(`
**Player:** https://ssr.fascinated.cc/player/${player.id}
**Leaderboard:** https://ssr.fascinated.cc/leaderboard/${leaderboard.id}
**PP:** ${formatPp(score.pp)}
`);
embed.setThumbnail(leaderboard.coverImage);
embed.setColor("#00ff00");
await hook.send(embed);
logToChannel(
DiscordChannels.numberOneFeed,
new EmbedBuilder()
.setTitle(`${player.name} set a #1 on ${leaderboard.songName} ${leaderboard.songSubName}`)
.setDescription(
`
**Player:** https://ssr.fascinated.cc/player/${player.id}
**Leaderboard:** https://ssr.fascinated.cc/leaderboard/${leaderboard.id}
**PP:** ${formatPp(score.pp)}
`
)
.setThumbnail(leaderboard.coverImage)
.setColor("#00ff00")
);
}
/**

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

@ -5,22 +5,29 @@ type ScoresaberSocket = {
/**
* Invoked when a general message is received.
*
* @param message The received message.
* @param message the received message.
*/
onMessage?: (message: unknown) => void;
/**
* Invoked when a score message is received.
*
* @param score The received score data.
* @param score the received score data.
*/
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.
*/
export function connectScoreSaberWebSocket({ onMessage, onScore }: ScoresaberSocket) {
export function connectScoreSaberWebSocket({ onMessage, onScore, onDisconnect }: ScoresaberSocket) {
let websocket: WebSocket | null = null;
function connectWs() {
@ -35,6 +42,8 @@ export function connectScoreSaberWebSocket({ onMessage, onScore }: ScoresaberSoc
if (websocket) {
websocket.close(); // Close the connection on error
}
onDisconnect && onDisconnect(error);
};
websocket.onclose = () => {