rhythia-api 236.0.0 → 238.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.
- package/api/executeAdminOperation.ts +296 -1
- package/api/getCollection.ts +26 -23
- package/api/getProfile.ts +9 -7
- package/api/reportProfile.ts +12 -3
- package/index.ts +15 -23
- package/package.json +1 -1
- package/types/database.ts +1356 -1327
- package/utils/profileReportWebhook.ts +180 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
type ProfileReportWebhookProfile = {
|
|
2
|
+
id: number;
|
|
3
|
+
username: string | null;
|
|
4
|
+
computedUsername: string | null;
|
|
5
|
+
avatar_url: string | null;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
function clampText(value: string, maxLength: number) {
|
|
9
|
+
const sanitized = value.replace(
|
|
10
|
+
/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g,
|
|
11
|
+
""
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
if (sanitized.length <= maxLength) {
|
|
15
|
+
return sanitized;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (maxLength <= 3) {
|
|
19
|
+
return sanitized.slice(0, maxLength);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return `${sanitized.slice(0, maxLength - 3)}...`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function getSafeHttpUrl(value: string | null | undefined) {
|
|
26
|
+
if (!value) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const parsed = new URL(value.trim());
|
|
32
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const serialized = parsed.toString();
|
|
37
|
+
if (serialized.length > 2048) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return serialized;
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getDisplayName(profile: ProfileReportWebhookProfile) {
|
|
48
|
+
return (
|
|
49
|
+
profile.username ||
|
|
50
|
+
profile.computedUsername ||
|
|
51
|
+
`User #${profile.id}`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function getPlayerUrl(profileId: number) {
|
|
56
|
+
return `https://www.rhythia.com/player/${profileId}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function postProfileReportWebhook({
|
|
60
|
+
reportId,
|
|
61
|
+
reporter,
|
|
62
|
+
reported,
|
|
63
|
+
description,
|
|
64
|
+
createdAt,
|
|
65
|
+
}: {
|
|
66
|
+
reportId: number;
|
|
67
|
+
reporter: ProfileReportWebhookProfile;
|
|
68
|
+
reported: ProfileReportWebhookProfile;
|
|
69
|
+
description: string;
|
|
70
|
+
createdAt?: string;
|
|
71
|
+
}) {
|
|
72
|
+
const webhookUrl = process.env.DISCORD_REPORT_WEBHOOK_URL;
|
|
73
|
+
if (!webhookUrl) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const reporterName = getDisplayName(reporter);
|
|
79
|
+
const reportedName = getDisplayName(reported);
|
|
80
|
+
const safeReporterAvatarUrl = getSafeHttpUrl(reporter.avatar_url);
|
|
81
|
+
const safeReportedAvatarUrl = getSafeHttpUrl(reported.avatar_url);
|
|
82
|
+
|
|
83
|
+
const embed: Record<string, any> = {
|
|
84
|
+
title: clampText(`Profile Report #${reportId}`, 256),
|
|
85
|
+
description: clampText(description, 4096),
|
|
86
|
+
color: 0xe67e22,
|
|
87
|
+
fields: [
|
|
88
|
+
{
|
|
89
|
+
name: "Reported Player",
|
|
90
|
+
value: clampText(
|
|
91
|
+
`${reportedName} (#${reported.id})\n${getPlayerUrl(reported.id)}`,
|
|
92
|
+
1024
|
|
93
|
+
),
|
|
94
|
+
inline: true,
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: "Reporter",
|
|
98
|
+
value: clampText(
|
|
99
|
+
`${reporterName} (#${reporter.id})\n${getPlayerUrl(reporter.id)}`,
|
|
100
|
+
1024
|
|
101
|
+
),
|
|
102
|
+
inline: true,
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: "Submitted At",
|
|
106
|
+
value: clampText(
|
|
107
|
+
new Date(createdAt || Date.now()).toUTCString(),
|
|
108
|
+
1024
|
|
109
|
+
),
|
|
110
|
+
inline: true,
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
author: {
|
|
114
|
+
name: clampText(reporterName, 256),
|
|
115
|
+
url: getPlayerUrl(reporter.id),
|
|
116
|
+
icon_url: safeReporterAvatarUrl || "https://www.rhythia.com/unkimg.png",
|
|
117
|
+
},
|
|
118
|
+
footer: {
|
|
119
|
+
text: clampText(
|
|
120
|
+
`Reported player: ${reportedName} | Report ID: ${reportId}`,
|
|
121
|
+
2048
|
|
122
|
+
),
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
if (safeReportedAvatarUrl) {
|
|
127
|
+
embed.thumbnail = {
|
|
128
|
+
url: safeReportedAvatarUrl,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const payload = {
|
|
133
|
+
content: "New player report submitted.",
|
|
134
|
+
embeds: [embed],
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
let response = await fetch(webhookUrl, {
|
|
138
|
+
method: "POST",
|
|
139
|
+
headers: {
|
|
140
|
+
"Content-Type": "application/json",
|
|
141
|
+
},
|
|
142
|
+
body: JSON.stringify(payload),
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
if (
|
|
146
|
+
!response.ok &&
|
|
147
|
+
response.status === 400 &&
|
|
148
|
+
payload.embeds?.[0]?.thumbnail?.url
|
|
149
|
+
) {
|
|
150
|
+
const retryPayload = {
|
|
151
|
+
...payload,
|
|
152
|
+
embeds: payload.embeds.map((embed: any) => {
|
|
153
|
+
const clone = { ...embed };
|
|
154
|
+
delete clone.thumbnail;
|
|
155
|
+
return clone;
|
|
156
|
+
}),
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
response = await fetch(webhookUrl, {
|
|
160
|
+
method: "POST",
|
|
161
|
+
headers: {
|
|
162
|
+
"Content-Type": "application/json",
|
|
163
|
+
},
|
|
164
|
+
body: JSON.stringify(retryPayload),
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (!response.ok) {
|
|
169
|
+
const responseBody = await response.text();
|
|
170
|
+
console.log("Discord report webhook failed", {
|
|
171
|
+
reportId,
|
|
172
|
+
status: response.status,
|
|
173
|
+
statusText: response.statusText,
|
|
174
|
+
responseBody: clampText(responseBody || "-", 4000),
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
} catch (error) {
|
|
178
|
+
console.log("Failed to post profile report webhook", error);
|
|
179
|
+
}
|
|
180
|
+
}
|