diff --git a/src/db/schemas/beatsaver.ts b/src/db/schemas/beatsaver.ts new file mode 100644 index 0000000..c3e0090 --- /dev/null +++ b/src/db/schemas/beatsaver.ts @@ -0,0 +1,12 @@ +import mongoose from "mongoose"; +const { Schema } = mongoose; + +const beatSaverSchema = new Schema({ + _id: String, + totalCreated: Number, + totalUpdated: Number, + totalDeleted: Number, +}); + +export const BeatSaverSchema = + mongoose.models.BeatSaver || mongoose.model("BeatSaver", beatSaverSchema); diff --git a/src/index.ts b/src/index.ts index 78b8024..b9904ef 100644 --- a/src/index.ts +++ b/src/index.ts @@ -22,5 +22,6 @@ export const InfluxWriteAPI = influxClient.getWriteApi( ); export const InfluxQueryAPI = influxClient.getQueryApi(INFLUXDB_ORG); -require("./services/updateData"); +require("./services/scoresaber"); +require("./services/beatsaver"); require("./api/api"); diff --git a/src/services/beatsaver.ts b/src/services/beatsaver.ts new file mode 100644 index 0000000..0459af1 --- /dev/null +++ b/src/services/beatsaver.ts @@ -0,0 +1,126 @@ +import { Point } from "@influxdata/influxdb-client"; +import cron from "node-cron"; +import { w3cwebsocket as WebsocketClient } from "websocket"; +import { InfluxWriteAPI } from ".."; +import { connectMongo } from "../db/mongo"; +import { BeatSaverSchema } from "../db/schemas/beatsaver"; + +const WEBSOCKET_URL = "wss://ws.beatsaver.com/maps"; + +let totalUpdated: number | undefined; +let totalCreated: number | undefined; +let totalDeleted: number | undefined; + +async function update() { + if (totalCreated == undefined) { + throw new Error("Total created is undefined"); + } + if (totalUpdated == undefined) { + throw new Error("Total updated is undefined"); + } + if (totalDeleted == undefined) { + throw new Error("Total deleted is undefined"); + } + const totalCreatedPoint = new Point("beatsaver") + .tag("type", "maps_created") + .intField("value", totalCreated) + .timestamp(new Date()); + const totalUpdatedPoint = new Point("beatsaver") + .tag("type", "maps_updated") + .intField("value", totalUpdated) + .timestamp(new Date()); + const totalDeletedPoint = new Point("beatsaver") + .tag("type", "maps_deleted") + .intField("value", totalDeleted) + .timestamp(new Date()); + + InfluxWriteAPI.writePoints([ + totalCreatedPoint, + totalUpdatedPoint, + totalDeletedPoint, + ]); +} + +async function connectWebsocket() { + const socket = new WebsocketClient(WEBSOCKET_URL); + await connectMongo(); + let beatSaver = await BeatSaverSchema.findOne({ _id: "beatsaver" }); + if (!beatSaver) { + beatSaver = await BeatSaverSchema.create({ + _id: "beatsaver", + totalUpdated: 0, + totalCreated: 0, + totalDeleted: 0, + }); + } + totalCreated = beatSaver.totalCreated || 0; + totalUpdated = beatSaver.totalUpdated || 0; + totalDeleted = beatSaver.totalDeleted || 0; + + console.log(totalCreated, totalUpdated, totalDeleted); + + socket.onopen = () => { + console.log("Connected to beatsaver websocket"); + }; + + socket.onclose = () => { + console.log("Disconnected from beatsaver websocket"); + setTimeout(connectWebsocket, 5000); + }; + + socket.onerror = (error) => { + console.error("Beatsaver Websocket error:", error); + }; + + socket.onmessage = async (message) => { + const json = JSON.parse(message.data.toString()); + console.log(totalCreated, totalUpdated, totalDeleted); + if (totalCreated == undefined) { + throw new Error("Total created is undefined"); + } + if (totalUpdated == undefined) { + throw new Error("Total updated is undefined"); + } + if (totalDeleted == undefined) { + throw new Error("Total deleted is undefined"); + } + + const { type, msg } = json; + const { id, metadata, uploaded, updatedAt } = msg; + if (!metadata) { + throw new Error("Metadata is undefined for map " + id); + } + const { bpm, duration } = metadata; + // new map + if (type == "MAP_UPDATE" && uploaded == updatedAt) { + totalCreated++; + InfluxWriteAPI.writePoint( + new Point("beatsaver") + .tag("type", "map_create") + .intField("bpm", bpm) + .intField("duration", duration) + ); + } else { + // updated map + totalUpdated++; + } + // deleted map + if (type == "MAP_DELETE") { + totalDeleted++; + } + + BeatSaverSchema.updateOne( + { _id: "beatsaver" }, + { + $set: { + totalUpdated: totalUpdated, + totalCreated: totalCreated, + totalDeleted: totalDeleted, + }, + } + ).exec(); + }; +} + +connectWebsocket(); +cron.schedule("*/5 * * * *", update); diff --git a/src/services/updateData.ts b/src/services/scoresaber.ts similarity index 95% rename from src/services/updateData.ts rename to src/services/scoresaber.ts index 190fec9..cbe7e99 100644 --- a/src/services/updateData.ts +++ b/src/services/scoresaber.ts @@ -43,16 +43,16 @@ async function connectWebsocket() { const socket = new WebsocketClient("wss://scoresaber.com/ws"); socket.onopen = () => { - console.log("Connected to websocket"); + console.log("Connected to scoresaber websocket"); }; socket.onclose = () => { - console.log("Disconnected from websocket"); + console.log("Disconnected from scoresaber websocket"); setTimeout(connectWebsocket, 5000); }; socket.onerror = (error) => { - console.error("Websocket error:", error); + console.error("Scoresaber Websocket error:", error); }; socket.onmessage = async (message) => {