rhythia-api 167.0.0 → 169.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.
@@ -10,6 +10,7 @@ export const Schema = {
10
10
  session: z.string(),
11
11
  data: z.object({
12
12
  avatar_url: z.string().optional(),
13
+ profile_image: z.string().optional(),
13
14
  username: z.string().optional(),
14
15
  }),
15
16
  }),
@@ -125,7 +125,7 @@ export async function handler(data: (typeof Schema)["input"]["_type"]) {
125
125
  const countOnline = await supabase
126
126
  .from("profileActivities")
127
127
  .select("*", { count: "exact", head: true })
128
- .eq("last_activity", Date.now() - 1800000);
128
+ .gt("last_activity", Date.now() - 1800000);
129
129
 
130
130
  return NextResponse.json({
131
131
  beatmaps: countBeatmapsQuery.count,
@@ -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(
@@ -87,6 +89,18 @@ export async function handler({
87
89
 
88
90
  const user = (await getUserBySession(session)) as User;
89
91
 
92
+ let { data: leversData } = await supabase
93
+ .from("levers")
94
+ .select("*")
95
+ .eq("id", 1)
96
+ .single();
97
+
98
+ if (leversData && leversData.disable_scores) {
99
+ return NextResponse.json({
100
+ error: "Scores are temporarily disabled",
101
+ });
102
+ }
103
+
90
104
  let { data: userData, error: userError } = await supabase
91
105
  .from("profiles")
92
106
  .select("*")
@@ -172,7 +186,10 @@ export async function handler({
172
186
  multiplierMod *= Math.pow(0.95, data.misses);
173
187
  }
174
188
 
175
- if (beatmaps.starRating) {
189
+ if (
190
+ beatmaps.starRating &&
191
+ Math.abs(beatmaps.starRating - data.virtualStars) < 0.01
192
+ ) {
176
193
  awarded_sp = calculatePerformancePoints(
177
194
  data.speed * beatmaps.starRating * multiplierMod,
178
195
  accurracy
@@ -250,6 +267,28 @@ export async function handler({
250
267
  });
251
268
  console.log("p3");
252
269
 
270
+ if (awarded_sp > 99 && userData.ban == "cool") {
271
+ await postToWebhooks({
272
+ rp: Math.round(awarded_sp * 100) / 100,
273
+ username: userData.username || "",
274
+ userid: userData.id,
275
+ avatar: userData.avatar_url || "",
276
+ mapimage: beatmaps.imageLarge || "",
277
+ spin: data.spin,
278
+ speed: data.speed,
279
+ accuracy: accurracy,
280
+ mapname: beatmaps.title || "",
281
+ mapid: beatmapPages.id || 0,
282
+ misses: data.misses || 0,
283
+ });
284
+ }
285
+
286
+ if (Math.abs((beatmaps.starRating || 0) - data.virtualStars) > 0.01) {
287
+ return NextResponse.json({
288
+ error: "Map mismatch, no RP points were awarded, please report the bug.",
289
+ });
290
+ }
291
+
253
292
  return NextResponse.json({});
254
293
  }
255
294
 
@@ -270,3 +309,112 @@ export function weightCalculate(hashMap: Record<string, number>) {
270
309
  }
271
310
  return totalSp;
272
311
  }
312
+
313
+ const webHookTemplate = {
314
+ content: null,
315
+ embeds: [
316
+ {
317
+ title: "Captain Lou Albano - Do the Mario",
318
+ url: "https://www.rhythia.com/maps/4469",
319
+ color: 9633967,
320
+ fields: [
321
+ {
322
+ name: "Rhythm Points",
323
+ value: "424 RP",
324
+ inline: true,
325
+ },
326
+ {
327
+ name: "Accuracy",
328
+ value: "100%",
329
+ inline: true,
330
+ },
331
+ {
332
+ name: "Speed",
333
+ value: "1.45x",
334
+ inline: true,
335
+ },
336
+ {
337
+ name: "Playstyle",
338
+ value: "Spin",
339
+ inline: true,
340
+ },
341
+ {
342
+ name: "Misses",
343
+ value: "0",
344
+ inline: true,
345
+ },
346
+ ],
347
+ author: {
348
+ name: "cunev",
349
+ url: "https://www.rhythia.com/player/0",
350
+ icon_url:
351
+ "https://static.rhythia.com/user-avatar-1735149648551-a2a8cfbe-af5d-46e8-a19a-be2339c1679a",
352
+ },
353
+ footer: {
354
+ text: "Sun, 22 Dec 2024 22:40:17 GMT",
355
+ },
356
+ thumbnail: {
357
+ url: "https://static.rhythia.com/beatmap-img-1735223264605-eliuka_dj_sharpnel_-_we_luv_lamalarge",
358
+ },
359
+ },
360
+ ],
361
+ attachments: [],
362
+ };
363
+
364
+ export async function postToWebhooks({
365
+ rp,
366
+ username,
367
+ userid,
368
+ avatar,
369
+ mapimage,
370
+ spin,
371
+ speed,
372
+ accuracy,
373
+ mapname,
374
+ mapid,
375
+ misses,
376
+ }: {
377
+ rp: number;
378
+ username: string;
379
+ userid: number;
380
+ avatar: string;
381
+ mapimage: string;
382
+ spin: boolean;
383
+ speed: number;
384
+ accuracy: number;
385
+ mapname: string;
386
+ mapid: number;
387
+ misses: number;
388
+ }) {
389
+ const webHooks = await supabase.from("discordWebhooks").select("*");
390
+
391
+ if (!webHooks.data) return;
392
+
393
+ for (const webhook of webHooks.data) {
394
+ const webhookUrl = webhook.webhook_link;
395
+
396
+ const embed = webHookTemplate.embeds[0];
397
+ embed.title = mapname;
398
+ embed.url = `https://www.rhythia.com/maps/${mapid}`;
399
+ embed.fields[0].value = `${rp} RP`;
400
+ embed.fields[1].value = `${Math.round(accuracy * 10000) / 100}%`;
401
+ embed.fields[2].value = `${speed}x`;
402
+ embed.fields[3].value = spin ? "Spin" : "Lock";
403
+ embed.fields[4].value = `${misses} misses`;
404
+ embed.author.name = username;
405
+ embed.author.url = `https://www.rhythia.com/player/${userid}`;
406
+ embed.author.icon_url = avatar;
407
+ embed.thumbnail.url = mapimage;
408
+ if (mapimage.includes("backfill")) {
409
+ embed.thumbnail.url = "https://www.rhythia.com/unkimg.png";
410
+ }
411
+ embed.footer.text = new Date().toUTCString();
412
+ await fetch(webhookUrl, {
413
+ method: "POST",
414
+ headers: {
415
+ "Content-Type": "application/json",
416
+ },
417
+ body: JSON.stringify(webHookTemplate),
418
+ });
419
+ }
420
+ }
package/index.ts CHANGED
@@ -92,6 +92,7 @@ export const Schema = {
92
92
  session: z.string(),
93
93
  data: z.object({
94
94
  avatar_url: z.string().optional(),
95
+ profile_image: z.string().optional(),
95
96
  username: z.string().optional(),
96
97
  }),
97
98
  }),
@@ -705,6 +706,7 @@ export const Schema = {
705
706
  mods: z.array(z.string()),
706
707
  additionalData: z.any(),
707
708
  spin: z.boolean(),
709
+ virtualStars: z.number(),
708
710
  }),
709
711
  }),
710
712
  output: z.object({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rhythia-api",
3
- "version": "167.0.0",
3
+ "version": "169.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,36 @@ 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
+ }
171
+ levers: {
172
+ Row: {
173
+ disable_scores: boolean
174
+ id: number
175
+ }
176
+ Insert: {
177
+ disable_scores?: boolean
178
+ id?: number
179
+ }
180
+ Update: {
181
+ disable_scores?: boolean
182
+ id?: number
183
+ }
184
+ Relationships: []
185
+ }
156
186
  passkeys: {
157
187
  Row: {
158
188
  email: string
@@ -22,6 +22,17 @@ export async function protectedApi({
22
22
  try {
23
23
  const toParse = await request.json();
24
24
  const data = schema.input.parse(toParse);
25
+
26
+ const dataClone = structuredClone(data);
27
+ if (dataClone) {
28
+ if (dataClone["token"]) {
29
+ dataClone["token"] = "********";
30
+ }
31
+ Object.keys(dataClone).forEach((key) => {
32
+ console.log("KEY: ", key, dataClone[key]);
33
+ });
34
+ }
35
+
25
36
  setActivity(data);
26
37
  if (authorization) {
27
38
  const authorizationResponse = await authorization(data);