rhythia-api 166.0.0 → 168.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,6 +9,7 @@ export const Schema = {
9
9
  profiles: z.number(),
10
10
  beatmaps: z.number(),
11
11
  scores: z.number(),
12
+ onlineUsers: z.number(),
12
13
  lastBeatmaps: z.array(
13
14
  z.object({
14
15
  id: z.number().nullable().optional(),
@@ -118,12 +119,19 @@ export async function handler(data: (typeof Schema)["input"]["_type"]) {
118
119
 
119
120
  const countScoresQuery = await supabase
120
121
  .from("scores")
121
- .select("*", { count: "exact", head: true });
122
+ .select("id", { count: "exact", head: true });
123
+
124
+ // 30 minutes activity
125
+ const countOnline = await supabase
126
+ .from("profileActivities")
127
+ .select("*", { count: "exact", head: true })
128
+ .gt("last_activity", Date.now() - 1800000);
122
129
 
123
130
  return NextResponse.json({
124
131
  beatmaps: countBeatmapsQuery.count,
125
132
  profiles: countProfilesQuery.count,
126
133
  scores: countScoresQuery.count,
134
+ onlineUsers: countOnline.count,
127
135
  lastBeatmaps: beatmapPage?.map((e) => ({
128
136
  playcount: e.beatmaps?.playcount,
129
137
  created_at: e.created_at,
@@ -40,6 +40,7 @@ export async function handler({
40
40
  .single();
41
41
 
42
42
  if (!userData) return NextResponse.json({ error: "No user." });
43
+ if (userData.ban !== "cool") return NextResponse.json({ error: "Error" });
43
44
 
44
45
  const upserted = await supabase
45
46
  .from("beatmapPageComments")
@@ -21,6 +21,7 @@ export const Schema = {
21
21
  mods: z.array(z.string()),
22
22
  additionalData: z.any(),
23
23
  spin: z.boolean(),
24
+ virtualStars: z.number(),
24
25
  }),
25
26
  }),
26
27
  output: z.object({
@@ -75,6 +76,7 @@ export async function handler({
75
76
  mods: data.mods,
76
77
  additionalData: data.additionalData,
77
78
  spin: data.spin,
79
+ virtualStars: data.virtualStars,
78
80
  })
79
81
  ) {
80
82
  return NextResponse.json(
@@ -172,7 +174,7 @@ export async function handler({
172
174
  multiplierMod *= Math.pow(0.95, data.misses);
173
175
  }
174
176
 
175
- if (beatmaps.starRating) {
177
+ if (beatmaps.starRating && beatmaps.starRating == data.virtualStars) {
176
178
  awarded_sp = calculatePerformancePoints(
177
179
  data.speed * beatmaps.starRating * multiplierMod,
178
180
  accurracy
@@ -250,6 +252,22 @@ export async function handler({
250
252
  });
251
253
  console.log("p3");
252
254
 
255
+ if (awarded_sp > 99) {
256
+ await postToWebhooks({
257
+ rp: Math.round(awarded_sp * 100) / 100,
258
+ username: userData.username || "",
259
+ userid: userData.id,
260
+ avatar: userData.avatar_url || "",
261
+ mapimage: beatmaps.imageLarge || "",
262
+ spin: data.spin,
263
+ speed: data.speed,
264
+ accuracy: accurracy,
265
+ mapname: beatmaps.title || "",
266
+ mapid: beatmapPages.id || 0,
267
+ misses: data.misses || 0,
268
+ });
269
+ }
270
+
253
271
  return NextResponse.json({});
254
272
  }
255
273
 
@@ -270,3 +288,112 @@ export function weightCalculate(hashMap: Record<string, number>) {
270
288
  }
271
289
  return totalSp;
272
290
  }
291
+
292
+ const webHookTemplate = {
293
+ content: null,
294
+ embeds: [
295
+ {
296
+ title: "Captain Lou Albano - Do the Mario",
297
+ url: "https://www.rhythia.com/maps/4469",
298
+ color: 9633967,
299
+ fields: [
300
+ {
301
+ name: "Rhythm Points",
302
+ value: "424 RP",
303
+ inline: true,
304
+ },
305
+ {
306
+ name: "Accuracy",
307
+ value: "100%",
308
+ inline: true,
309
+ },
310
+ {
311
+ name: "Speed",
312
+ value: "1.45x",
313
+ inline: true,
314
+ },
315
+ {
316
+ name: "Playstyle",
317
+ value: "Spin",
318
+ inline: true,
319
+ },
320
+ {
321
+ name: "Misses",
322
+ value: "0",
323
+ inline: true,
324
+ },
325
+ ],
326
+ author: {
327
+ name: "cunev",
328
+ url: "https://www.rhythia.com/player/0",
329
+ icon_url:
330
+ "https://static.rhythia.com/user-avatar-1735149648551-a2a8cfbe-af5d-46e8-a19a-be2339c1679a",
331
+ },
332
+ footer: {
333
+ text: "Sun, 22 Dec 2024 22:40:17 GMT",
334
+ },
335
+ thumbnail: {
336
+ url: "https://static.rhythia.com/beatmap-img-1735223264605-eliuka_dj_sharpnel_-_we_luv_lamalarge",
337
+ },
338
+ },
339
+ ],
340
+ attachments: [],
341
+ };
342
+
343
+ export async function postToWebhooks({
344
+ rp,
345
+ username,
346
+ userid,
347
+ avatar,
348
+ mapimage,
349
+ spin,
350
+ speed,
351
+ accuracy,
352
+ mapname,
353
+ mapid,
354
+ misses,
355
+ }: {
356
+ rp: number;
357
+ username: string;
358
+ userid: number;
359
+ avatar: string;
360
+ mapimage: string;
361
+ spin: boolean;
362
+ speed: number;
363
+ accuracy: number;
364
+ mapname: string;
365
+ mapid: number;
366
+ misses: number;
367
+ }) {
368
+ const webHooks = await supabase.from("discordWebhooks").select("*");
369
+
370
+ if (!webHooks.data) return;
371
+
372
+ for (const webhook of webHooks.data) {
373
+ const webhookUrl = webhook.webhook_link;
374
+
375
+ const embed = webHookTemplate.embeds[0];
376
+ embed.title = mapname;
377
+ embed.url = `https://www.rhythia.com/maps/${mapid}`;
378
+ embed.fields[0].value = `${rp} RP`;
379
+ embed.fields[1].value = `${Math.round(accuracy * 10000) / 100}%`;
380
+ embed.fields[2].value = `${speed}x`;
381
+ embed.fields[3].value = spin ? "Spin" : "Lock";
382
+ embed.fields[4].value = `${misses} misses`;
383
+ embed.author.name = username;
384
+ embed.author.url = `https://www.rhythia.com/player/${userid}`;
385
+ embed.author.icon_url = avatar;
386
+ embed.thumbnail.url = mapimage;
387
+ if (mapimage.includes("backfill")) {
388
+ embed.thumbnail.url = "https://www.rhythia.com/unkimg.png";
389
+ }
390
+ embed.footer.text = new Date().toUTCString();
391
+ await fetch(webhookUrl, {
392
+ method: "POST",
393
+ headers: {
394
+ "Content-Type": "application/json",
395
+ },
396
+ body: JSON.stringify(webHookTemplate),
397
+ });
398
+ }
399
+ }
package/index.ts CHANGED
@@ -445,6 +445,7 @@ export const Schema = {
445
445
  profiles: z.number(),
446
446
  beatmaps: z.number(),
447
447
  scores: z.number(),
448
+ onlineUsers: z.number(),
448
449
  lastBeatmaps: z.array(
449
450
  z.object({
450
451
  id: z.number().nullable().optional(),
@@ -704,6 +705,7 @@ export const Schema = {
704
705
  mods: z.array(z.string()),
705
706
  additionalData: z.any(),
706
707
  spin: z.boolean(),
708
+ virtualStars: z.number(),
707
709
  }),
708
710
  }),
709
711
  output: z.object({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rhythia-api",
3
- "version": "166.0.0",
3
+ "version": "168.0.0",
4
4
  "main": "index.ts",
5
5
  "scripts": {
6
6
  "update": "bun ./scripts/update.ts",
package/types/database.ts CHANGED
@@ -153,6 +153,21 @@ export type Database = {
153
153
  }
154
154
  Relationships: []
155
155
  }
156
+ discordWebhooks: {
157
+ Row: {
158
+ id: number
159
+ webhook_link: string
160
+ }
161
+ Insert: {
162
+ id?: number
163
+ webhook_link?: string
164
+ }
165
+ Update: {
166
+ id?: number
167
+ webhook_link?: string
168
+ }
169
+ Relationships: []
170
+ }
156
171
  passkeys: {
157
172
  Row: {
158
173
  email: string
@@ -22,6 +22,9 @@ export async function protectedApi({
22
22
  try {
23
23
  const toParse = await request.json();
24
24
  const data = schema.input.parse(toParse);
25
+
26
+ console.log({ ...data, session: undefined });
27
+
25
28
  setActivity(data);
26
29
  if (authorization) {
27
30
  const authorizationResponse = await authorization(data);