Files
beatsaber-metrics-tracker/src/services/updateData.ts

223 lines
6.1 KiB
TypeScript
Raw Normal View History

2023-10-27 15:54:11 +01:00
import { Point } from "@influxdata/influxdb-client";
2023-10-27 16:30:30 +01:00
import axios from "axios";
2023-10-27 15:54:11 +01:00
import { w3cwebsocket as WebsocketClient } from "websocket";
import { InfluxWriteAPI } from "..";
import { connectMongo } from "../db/mongo";
import { LeaderboardSchema } from "../db/schemas/leaderboard";
2023-10-30 15:01:08 +00:00
import { Score } from "../db/schemas/score";
import { Headsets } from "../headsets";
import { normalizedRegionName } from "../utils/regionUtils";
2023-10-27 15:54:11 +01:00
async function update() {
2023-10-27 16:30:30 +01:00
const response = await axios.get("https://scoresaber.com/api/players/count");
const count = response.data;
2023-10-27 15:54:11 +01:00
const point = new Point("scoresaber")
.tag("type", "player_count")
.intField("value", parseInt(count))
.timestamp(new Date());
InfluxWriteAPI.writePoint(point);
console.log(`Updated player count to ${count}`);
}
async function connectWebsocket() {
await connectMongo();
const leaderboard = await LeaderboardSchema.findOne({ _id: "scoresaber" });
if (!leaderboard) {
await LeaderboardSchema.create({ _id: "scoresaber", totalPlays: 0 });
}
let totalScores = leaderboard?.totalPlays || 0;
let totalCountries = leaderboard?.countries || {};
let totalHeadsets = leaderboard?.headsets || {};
2023-10-27 15:54:11 +01:00
const socket = new WebsocketClient("wss://scoresaber.com/ws");
socket.onopen = () => {
console.log("Connected to websocket");
};
socket.onclose = () => {
console.log("Disconnected from websocket");
setTimeout(connectWebsocket, 5000);
};
socket.onerror = (error) => {
console.error("Websocket error:", error);
};
2023-10-30 15:01:08 +00:00
socket.onmessage = async (message) => {
2023-10-27 15:54:11 +01:00
const data = message.data;
let json;
try {
json = JSON.parse(data.toString());
} catch (e) {
return;
}
const commandName = json.commandName;
const commandData = json.commandData;
if (commandName == "score") {
2023-10-30 15:01:08 +00:00
const { score, leaderboard } = commandData;
2023-10-27 15:54:11 +01:00
totalScores++;
2023-10-30 15:01:08 +00:00
const {
id,
hmd,
baseScore,
missedNotes,
badCuts,
leaderboardPlayerInfo: player,
pp,
} = score;
const { maxScore, stars, id: leaderboardId } = leaderboard;
2023-10-30 15:49:06 +00:00
await Score.deleteOne({ _id: id }); // Delete the score if it already exists
2023-10-30 15:01:08 +00:00
const data: any = {
_id: id,
player: {
id: player.id,
hmd: hmd,
},
mistakes: {
badCuts: badCuts,
missed: missedNotes,
},
rank: score.rank,
maxCombo: score.maxCombo,
leaderboardId: leaderboardId,
};
if (maxScore) {
data.acc = ((baseScore / maxScore) * 100).toFixed(2);
}
if (pp && stars > 0) {
data.pp = pp;
data.stars = stars;
}
2023-10-30 15:49:06 +00:00
Score.create(data); // Create the score
const countryId =
normalizedRegionName(player.country?.toLowerCase()) || "unknown";
totalCountries[countryId] = (totalCountries[countryId] || 0) + 1;
totalHeadsets[hmd] = (totalHeadsets[hmd] || 0) + 1;
2023-10-30 15:49:06 +00:00
// Update the leaderboard data in the database
await LeaderboardSchema.updateOne(
{ _id: "scoresaber" },
{
$set: {
totalPlays: totalScores,
countries: totalCountries,
headsets: totalHeadsets,
},
}
).exec();
2023-10-30 15:01:08 +00:00
2023-10-30 15:49:06 +00:00
// Write the metrics data to influx
InfluxWriteAPI.writePoint(
new Point("scoresaber")
.tag("type", "score_count")
.intField("value", totalScores)
.timestamp(new Date())
);
InfluxWriteAPI.writePoint(
new Point("scoresaber")
.tag("type", "headsets")
.tag("hmd", Headsets.map((h) => h.name)[hmd])
.intField("value", totalHeadsets[hmd])
.timestamp(new Date())
);
InfluxWriteAPI.writePoint(
new Point("scoresaber")
.tag("type", "countries")
.tag("country", countryId)
.intField("value", totalCountries[countryId])
.timestamp(new Date())
);
2023-10-27 15:54:11 +01:00
}
};
}
2023-10-30 15:01:08 +00:00
async function updateAverages() {
const before = new Date().getTime();
Score.find({}).then((scores) => {
// create an average for all data for all scores
const average = {
acc: 0,
pp: 0,
stars: 0,
mistakes: {
badCuts: 0,
missed: 0,
},
rank: 0,
maxCombo: 0,
};
scores.forEach((score) => {
if (score.acc) {
average.acc += score.acc;
}
if (score.pp) {
average.pp += score.pp;
}
if (score.stars) {
average.stars += score.stars;
}
if (score.mistakes) {
average.mistakes.badCuts += score.mistakes.badCuts;
average.mistakes.missed += score.mistakes.missed;
}
if (score.rank) {
average.rank += score.rank;
}
if (score.maxCombo) {
average.maxCombo += score.maxCombo;
}
});
average.acc /= scores.length;
average.pp /= scores.length;
average.stars /= scores.length;
average.mistakes.badCuts /= scores.length;
average.mistakes.missed /= scores.length;
average.rank /= scores.length;
average.maxCombo /= scores.length;
// create the point and write it to influx
const point = new Point("scoresaber")
.tag("type", "average")
.timestamp(new Date());
2023-10-30 15:03:29 +00:00
if (average.acc) {
point.floatField("acc", average.acc);
}
if (average.pp) {
point.floatField("pp", average.pp);
}
if (average.stars) {
point.floatField("stars", average.stars);
}
if (average.mistakes) {
2023-10-30 15:05:28 +00:00
point.intField(
"badCuts",
isNaN(average.mistakes.badCuts) ? 0 : average.mistakes.badCuts
);
point.intField(
"missed",
isNaN(average.mistakes.missed) ? 0 : average.mistakes.missed
);
2023-10-30 15:03:29 +00:00
}
if (average.rank) {
point.intField("rank", average.rank);
}
if (average.maxCombo) {
point.intField("maxCombo", average.maxCombo);
}
2023-10-30 15:01:08 +00:00
InfluxWriteAPI.writePoint(point);
console.log(`Updated averages in ${new Date().getTime() - before}ms`);
});
}
2023-10-27 15:54:11 +01:00
update();
2023-10-30 15:01:08 +00:00
updateAverages();
2023-10-27 15:54:11 +01:00
connectWebsocket();
setInterval(update, 60_000); // 1 minute
2023-10-30 15:01:08 +00:00
setInterval(updateAverages, 60_000 * 5); // 5 minutes