xmoj-script 1.1.1 → 1.1.2

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.
@@ -1,1227 +0,0 @@
1
- import { Result, ThrowErrorIfFailed } from "./Result";
2
- import { Database } from "./Database";
3
- import { Output } from "./Output";
4
- import { CheerioAPI, load } from "cheerio";
5
- import * as sqlstring from 'sqlstring';
6
- import CryptoJS from "crypto-js";
7
-
8
- export class Process {
9
- private AdminUserList: Array<string> = ["zhuchenrui2", "shanwenxiao", "shihongxi"];
10
- private CaptchaSecretKey: string;
11
- private GithubImagePAT: string;
12
- private ACCOUNT_ID: string;
13
- private API_TOKEN: string;
14
- private Username: string;
15
- private SessionID: string;
16
- private RemoteIP: string;
17
- private XMOJDatabase: Database;
18
- private logs: { writeDataPoint: (arg0: { blobs: string[]; indexes: string[]; }) => void; };
19
- private RequestData: Request;
20
- private Fetch = async (RequestURL: URL): Promise<Response> => {
21
- Output.Log("Fetch: " + RequestURL.toString());
22
- let Abort = new AbortController();
23
- setTimeout(() => {
24
- Abort.abort();
25
- }, 5000);
26
- let RequestData = new Request(RequestURL, {
27
- headers: {
28
- "Cookie": "PHPSESSID=" + this.SessionID
29
- },
30
- signal: Abort.signal
31
- });
32
- return await fetch(RequestData);
33
- }
34
- public CheckParams = (Data: object, Checklist: object): Result => {
35
- for (let i in Data) {
36
- if (Checklist[i] === undefined) {
37
- return new Result(false, "参数" + i + "未知");
38
- }
39
- const AvailableTypes = ["string", "number", "bigint", "boolean", "symbol", "undefined", "object", "function"];
40
- if (AvailableTypes.indexOf(Checklist[i]) === -1) {
41
- return new Result(false, "参数类型" + Checklist[i] + "未知");
42
- }
43
- if (typeof Data[i] !== Checklist[i]) {
44
- return new Result(false, "参数" + i + "期望类型" + Checklist[i] + "实际类型" + typeof Data[i]);
45
- }
46
- }
47
- for (let i in Checklist) {
48
- if (Data[i] === undefined) {
49
- return new Result(false, "参数" + i + "未找到");
50
- }
51
- }
52
- return new Result(true, "参数检测通过");
53
- }
54
- public CheckToken = async (Data: object): Promise<Result> => {
55
- ThrowErrorIfFailed(this.CheckParams(Data, {
56
- "SessionID": "string",
57
- "Username": "string"
58
- }));
59
- this.SessionID = Data["SessionID"];
60
- this.Username = Data["Username"];
61
- // return new Result(true, "令牌检测跳过");
62
- let HashedToken: string = CryptoJS.SHA3(this.SessionID).toString();
63
- let CurrentSessionData = ThrowErrorIfFailed(await this.XMOJDatabase.Select("phpsessid", ["user_id", "create_time"], {
64
- token: HashedToken
65
- }));
66
- if (CurrentSessionData.toString() !== "") {
67
- if (CurrentSessionData[0]["user_id"] === this.Username &&
68
- CurrentSessionData[0]["create_time"] + 1000 * 60 * 60 * 24 * 7 > new Date().getTime()) {
69
- return new Result(true, "令牌匹配");
70
- }
71
- else {
72
- ThrowErrorIfFailed(await this.XMOJDatabase.Delete("phpsessid", {
73
- token: HashedToken
74
- }));
75
- Output.Log("Session " + this.SessionID + " expired");
76
- }
77
- }
78
-
79
- let SessionUsername: string = await this.Fetch(new URL("http://www.xmoj.tech/template/bs3/profile.php"))
80
- .then((Response) => {
81
- return Response.text();
82
- }).then((Response) => {
83
- let SessionUsername = Response.substring(Response.indexOf("user_id=") + 8);
84
- SessionUsername = SessionUsername.substring(0, SessionUsername.indexOf("'"));
85
- return SessionUsername;
86
- }).catch((Error) => {
87
- Output.Error("Check token failed: " + Error + "\n" +
88
- "PHPSessionID: \"" + this.SessionID + "\"\n" +
89
- "Username : \"" + this.Username + "\"\n");
90
- return "";
91
- });
92
- if (SessionUsername == "") {
93
- Output.Debug("Check token failed: Session invalid\n" +
94
- "PHPSessionID: \"" + this.SessionID + "\"\n");
95
- return new Result(false, "令牌不合法");
96
- }
97
- if (SessionUsername != this.Username) {
98
- Output.Debug("Check token failed: Session and username not match \n" +
99
- "PHPSessionID : \"" + this.SessionID + "\"\n" +
100
- "SessionUsername: \"" + SessionUsername + "\"\n" +
101
- "Username : \"" + this.Username + "\"\n");
102
- return new Result(false, "令牌不匹配");
103
- }
104
- //check if th item already exists in db
105
- if (ThrowErrorIfFailed(await this.XMOJDatabase.GetTableSize("phpsessid", {
106
- token: HashedToken
107
- }))["TableSize"] == 0) {
108
- ThrowErrorIfFailed(await this.XMOJDatabase.Insert("phpsessid", {
109
- token: HashedToken,
110
- user_id: this.Username,
111
- create_time: new Date().getTime()
112
- }));
113
- } else {
114
- Output.Log("token already exists, skipping insert");
115
- }
116
- Output.Log("Record session: " + this.SessionID + " for " + this.Username);
117
- return new Result(true, "令牌匹配");
118
- }
119
- public IfUserExist = async (Username: string): Promise<Result> => {
120
- return await this.Fetch(new URL("http://www.xmoj.tech/userinfo.php?user=" + Username))
121
- .then((Response) => {
122
- return Response.text();
123
- }).then((Response) => {
124
- return new Result(true, "用户检查成功", {
125
- "Exist": Response.indexOf("No such User!") === -1
126
- });
127
- }).catch((Error) => {
128
- Output.Error("Check user exist failed: " + Error + "\n" +
129
- "Username: \"" + Username + "\"\n");
130
- return new Result(false, "用户检查失败");
131
- });
132
- }
133
- public IsAdmin = (): boolean => {
134
- return this.AdminUserList.indexOf(this.Username) !== -1;
135
- }
136
- public VerifyCaptcha = async (CaptchaToken: string): Promise<Result> => {
137
- const ErrorDescriptions: Object = {
138
- "missing-input-secret": "密钥为空",
139
- "invalid-input-secret": "密钥不正确",
140
- "missing-input-response": "验证码令牌为空",
141
- "invalid-input-response": "验证码令牌不正确或已过期",
142
- "invalid-widget-id": "解析出的组件编号不正确",
143
- "invalid-parsed-secret": "解析出的密钥不正确",
144
- "bad-request": "请求格式错误",
145
- "timeout-or-duplicate": "相同验证码已经校验过",
146
- "internal-error": "服务器错误"
147
- };
148
- // return new Result(true, "验证码检测跳过");
149
- if (CaptchaToken === "") {
150
- return new Result(false, "验证码没有完成");
151
- }
152
- let VerifyFormData = new FormData();
153
- VerifyFormData.append("secret", this.CaptchaSecretKey);
154
- VerifyFormData.append("response", CaptchaToken);
155
- VerifyFormData.append("remoteip", this.RemoteIP);
156
- const VerifyResult = await fetch("https://challenges.cloudflare.com/turnstile/v0/siteverify", {
157
- body: JSON.stringify({
158
- secret: this.CaptchaSecretKey,
159
- response: CaptchaToken,
160
- remoteip: this.RemoteIP
161
- }),
162
- headers: {
163
- "Content-Type": "application/json"
164
- },
165
- method: 'POST',
166
- }).then((Response) => {
167
- return Response.json();
168
- });
169
- if (VerifyResult["success"]) {
170
- return new Result(true, "验证码通过");
171
- }
172
- else {
173
- let ErrorString: string = "验证没有通过:";
174
- for (let i = 0; i < VerifyResult["error-codes"].length; i++) {
175
- ErrorString += (ErrorDescriptions[VerifyResult["error-codes"][i]] == null ? VerifyResult["error-codes"][i] : ErrorDescriptions[VerifyResult["error-codes"][i]]) + " ";
176
- }
177
- ErrorString = ErrorString.trimEnd();
178
- return new Result(false, ErrorString);
179
- }
180
- }
181
- public GetProblemScore = async (ProblemID: number): Promise<number> => {
182
- return await this.Fetch(new URL("http://www.xmoj.tech/status.php?user_id=" + this.Username + "&problem_id=" + ProblemID))
183
- .then((Response) => {
184
- return Response.text();
185
- }).then((Response) => {
186
- let ParsedDocument: CheerioAPI = load(Response);
187
- let ResultTable = ParsedDocument("#result-tab");
188
- if (ResultTable.length == 0) {
189
- Output.Error("Get problem score failed: Cannot find table element\n" +
190
- "ProblemID: \"" + ProblemID + "\"\n" +
191
- "Username : \"" + this.Username + "\"\n");
192
- ThrowErrorIfFailed(new Result(false, "获取题目分数失败"));
193
- }
194
- let MaxScore: number = 0;
195
- let ResultTableBody = ResultTable.children().eq(1);
196
- for (let i = 0; i < ResultTableBody.children().length; i++) {
197
- let ResultRow = ResultTableBody.children().eq(i);
198
- if (ResultRow.children().eq(4).text().trim() === "正确") {
199
- return 100;
200
- }
201
- else if (ResultRow.children().eq(4).children().length == 2) {
202
- let ScoreSpan = ResultRow.children().eq(4).children().eq(1);
203
- if (ScoreSpan.length == 0) {
204
- Output.Error("Get problem score failed: Cannot find score span\n" +
205
- "ProblemID: \"" + ProblemID + "\"\n" +
206
- "Username : \"" + this.Username + "\"\n");
207
- ThrowErrorIfFailed(new Result(false, "获取题目分数失败"));
208
- }
209
- let Score: string = ScoreSpan.text().trim();
210
- MaxScore = Math.max(MaxScore, parseInt(Score.substring(0, Score.length - 1)));
211
- }
212
- }
213
- return MaxScore;
214
- }).catch((Error) => {
215
- Output.Error("Get user score failed: " + Error + "\n" +
216
- "ProblemID: \"" + ProblemID + "\"\n" +
217
- "Username : \"" + this.Username + "\"\n");
218
- ThrowErrorIfFailed(new Result(false, "获取题目分数失败"));
219
- return 0;
220
- });
221
- }
222
- private AddBBSMention = async (ToUserID: string, PostID: number): Promise<void> => {
223
- if (ToUserID === this.Username) {
224
- return;
225
- }
226
- if (ThrowErrorIfFailed(await this.XMOJDatabase.GetTableSize("bbs_mention", {
227
- to_user_id: ToUserID,
228
- post_id: PostID
229
- }))["TableSize"] === 0) {
230
- ThrowErrorIfFailed(await this.XMOJDatabase.Insert("bbs_mention", {
231
- to_user_id: ToUserID,
232
- post_id: PostID,
233
- bbs_mention_time: new Date().getTime()
234
- }));
235
- }
236
- else {
237
- ThrowErrorIfFailed(await this.XMOJDatabase.Update("bbs_mention", {
238
- bbs_mention_time: new Date().getTime()
239
- }, {
240
- to_user_id: ToUserID,
241
- post_id: PostID
242
- }));
243
- }
244
- };
245
- private AddMailMention = async (FromUserID: string, ToUserID: string): Promise<void> => {
246
- if (ThrowErrorIfFailed(await this.XMOJDatabase.GetTableSize("short_message_mention", {
247
- from_user_id: FromUserID,
248
- to_user_id: ToUserID
249
- }))["TableSize"] === 0) {
250
- ThrowErrorIfFailed(await this.XMOJDatabase.Insert("short_message_mention", {
251
- from_user_id: FromUserID,
252
- to_user_id: ToUserID,
253
- mail_mention_time: new Date().getTime()
254
- }));
255
- }
256
- else {
257
- ThrowErrorIfFailed(await this.XMOJDatabase.Update("short_message_mention", {
258
- mail_mention_time: new Date().getTime()
259
- }, {
260
- from_user_id: FromUserID,
261
- to_user_id: ToUserID
262
- }));
263
- }
264
- };
265
- private ProcessFunctions = {
266
- NewPost: async (Data: object): Promise<Result> => {
267
- ThrowErrorIfFailed(this.CheckParams(Data, {
268
- "ProblemID": "number",
269
- "Title": "string",
270
- "Content": "string",
271
- "CaptchaSecretKey": "string",
272
- "BoardID": "number"
273
- }));
274
- ThrowErrorIfFailed(await this.VerifyCaptcha(Data["CaptchaSecretKey"]));
275
- if (Data["Title"].trim() === "") {
276
- return new Result(false, "标题不能为空");
277
- }
278
- if (Data["Content"].trim() === "") {
279
- return new Result(false, "内容不能为空");
280
- }
281
- if (!this.IsAdmin && Data["BoardID"] === 0) {
282
- return new Result(false, "没有权限发表公告");
283
- }
284
- if (Data["BoardID"] !== 0 && ThrowErrorIfFailed(await this.XMOJDatabase.GetTableSize("bbs_board", {
285
- board_id: Data["BoardID"]
286
- }))["TableSize"] === 0) {
287
- return new Result(false, "未找到板块");
288
- }
289
- let PostID = ThrowErrorIfFailed(await this.XMOJDatabase.Insert("bbs_post", {
290
- user_id: this.Username,
291
- problem_id: Data["ProblemID"],
292
- title: Data["Title"],
293
- post_time: new Date().getTime(),
294
- board_id: Data["BoardID"]
295
- }))["InsertID"];
296
- let ReplyID = ThrowErrorIfFailed(await this.XMOJDatabase.Insert("bbs_reply", {
297
- user_id: this.Username,
298
- post_id: PostID,
299
- content: Data["Content"],
300
- reply_time: new Date().getTime()
301
- }))["InsertID"];
302
- return new Result(true, "创建讨论成功", {
303
- PostID: PostID,
304
- ReplyID: ReplyID
305
- });
306
- },
307
- NewReply: async (Data: object): Promise<Result> => {
308
- ThrowErrorIfFailed(this.CheckParams(Data, {
309
- "PostID": "number",
310
- "Content": "string",
311
- "CaptchaSecretKey": "string"
312
- }));
313
- ThrowErrorIfFailed(await this.VerifyCaptcha(Data["CaptchaSecretKey"]));
314
-
315
- let Post = ThrowErrorIfFailed(await this.XMOJDatabase.Select("bbs_post", ["title", "user_id"], { post_id: Data["PostID"] }));
316
- if (Post.toString() == "") {
317
- return new Result(false, "未找到讨论");
318
- }
319
- //check if the post is locked
320
- if (ThrowErrorIfFailed(await this.XMOJDatabase.GetTableSize("bbs_lock", {
321
- post_id: Data["PostID"]
322
- }))["TableSize"] === 1 && !this.IsAdmin()) {
323
- return new Result(false, "讨论已被锁定");
324
- }
325
- Data["Content"] = Data["Content"].trim();
326
- if (Data["Content"] === "") {
327
- return new Result(false, "内容不能为空");
328
- }
329
- let MentionPeople = new Array<string>();
330
- for (let Match of String(Data["Content"]).matchAll(/@([a-zA-Z0-9]+)/g)) {
331
- if (ThrowErrorIfFailed(await this.IfUserExist(Match[1]))["Exist"]) {
332
- MentionPeople.push(Match[1]);
333
- }
334
- }
335
- MentionPeople = Array.from(new Set(MentionPeople));
336
- let ReplyID = ThrowErrorIfFailed(await this.XMOJDatabase.Insert("bbs_reply", {
337
- user_id: this.Username,
338
- post_id: Data["PostID"],
339
- content: Data["Content"],
340
- reply_time: new Date().getTime()
341
- }))["InsertID"];
342
-
343
- for (let i in MentionPeople) {
344
- await this.AddBBSMention(MentionPeople[i], Data["PostID"]);
345
- }
346
-
347
- if (Post[0]["user_id"] !== this.Username) {
348
- await this.AddBBSMention(Post[0]["user_id"], Data["PostID"]);
349
- }
350
-
351
- return new Result(true, "创建回复成功", {
352
- ReplyID: ReplyID
353
- });
354
- },
355
- GetPosts: async (Data: object): Promise<Result> => {
356
- ThrowErrorIfFailed(this.CheckParams(Data, {
357
- "ProblemID": "number",
358
- "Page": "number",
359
- "BoardID": "number"
360
- }));
361
- let ResponseData = {
362
- Posts: new Array<Object>,
363
- PageCount: Math.ceil(ThrowErrorIfFailed(await this.XMOJDatabase.GetTableSize("bbs_post"))["TableSize"] / 10)
364
- };
365
- if (ResponseData.PageCount === 0) {
366
- return new Result(true, "获得讨论列表成功", ResponseData);
367
- }
368
- if (Data["Page"] < 1 || Data["Page"] > ResponseData.PageCount) {
369
- return new Result(false, "参数页数不在范围1~" + ResponseData.PageCount + "内");
370
- }
371
- let SearchCondition = {};
372
- if (Data["ProblemID"] !== 0) {
373
- SearchCondition["problem_id"] = Data["ProblemID"];
374
- }
375
- if (Data["BoardID"] !== -1) {
376
- SearchCondition["board_id"] = Data["BoardID"];
377
- }
378
- let Posts = ThrowErrorIfFailed(await this.XMOJDatabase.Select("bbs_post", [], SearchCondition, {
379
- Order: "post_id",
380
- OrderIncreasing: false,
381
- Limit: 15,
382
- Offset: (Data["Page"] - 1) * 15
383
- }));
384
- for (let i in Posts) {
385
- let Post = Posts[i];
386
-
387
- let ReplyCount: number = ThrowErrorIfFailed(await this.XMOJDatabase.GetTableSize("bbs_reply", { post_id: Post["post_id"] }))["TableSize"];
388
- let LastReply = ThrowErrorIfFailed(await this.XMOJDatabase.Select("bbs_reply", ["user_id", "reply_time"], { post_id: Post["post_id"] }, {
389
- Order: "reply_time",
390
- OrderIncreasing: false,
391
- Limit: 1
392
- }));
393
- if (ReplyCount === 0) {
394
- await this.XMOJDatabase.Delete("bbs_post", {
395
- post_id: Post["post_id"]
396
- });
397
- continue;
398
- }
399
-
400
- let LockData = {
401
- Locked: false,
402
- LockPerson: "",
403
- LockTime: 0
404
- };
405
- let Locked = ThrowErrorIfFailed(await this.XMOJDatabase.Select("bbs_lock", [], {
406
- post_id: Post["post_id"]
407
- }));
408
- if (Locked.toString() !== "") {
409
- LockData.Locked = true;
410
- LockData.LockPerson = Locked[0]["lock_person"];
411
- LockData.LockTime = Locked[0]["lock_time"];
412
- }
413
-
414
- ResponseData.Posts.push({
415
- PostID: Post["post_id"],
416
- UserID: Post["user_id"],
417
- ProblemID: Post["problem_id"],
418
- Title: Post["title"],
419
- PostTime: Post["post_time"],
420
- BoardID: Post["board_id"],
421
- BoardName: ThrowErrorIfFailed(await this.XMOJDatabase.Select("bbs_board", ["board_name"], {
422
- board_id: Post["board_id"]
423
- }))[0]["board_name"],
424
- ReplyCount: ReplyCount,
425
- LastReplyUserID: LastReply[0]["user_id"],
426
- LastReplyTime: LastReply[0]["reply_time"],
427
- Lock: LockData
428
- });
429
- }
430
- return new Result(true, "获得讨论列表成功", ResponseData);
431
- },
432
- GetPost: async (Data: object): Promise<Result> => {
433
- ThrowErrorIfFailed(this.CheckParams(Data, {
434
- "PostID": "number",
435
- "Page": "number"
436
- }));
437
- let ResponseData = {
438
- UserID: "",
439
- ProblemID: 0,
440
- Title: "",
441
- BoardID: 0,
442
- BoardName: "",
443
- PostTime: 0,
444
- Reply: new Array<Object>(),
445
- PageCount: 0,
446
- Lock: {
447
- Locked: false,
448
- LockPerson: "",
449
- LockTime: 0
450
- }
451
- };
452
- let Post = ThrowErrorIfFailed(await this.XMOJDatabase.Select("bbs_post", [], {
453
- post_id: Data["PostID"]
454
- }));
455
- if (Post.toString() == "") {
456
- return new Result(false, "未找到讨论");
457
- }
458
- ResponseData.PageCount = Math.ceil(ThrowErrorIfFailed(await this.XMOJDatabase.GetTableSize("bbs_reply", { post_id: Data["PostID"] }))["TableSize"] / 10);
459
- if (ResponseData.PageCount === 0) {
460
- return new Result(true, "获得讨论成功", ResponseData);
461
- }
462
- if (Data["Page"] < 1 || Data["Page"] > ResponseData.PageCount) {
463
- return new Result(false, "参数页数不在范围1~" + ResponseData.PageCount + "内");
464
- }
465
- ResponseData.UserID = Post[0]["user_id"];
466
- ResponseData.ProblemID = Post[0]["problem_id"];
467
- ResponseData.Title = Post[0]["title"];
468
- ResponseData.PostTime = Post[0]["post_time"];
469
- ResponseData.BoardID = Post[0]["board_id"];
470
- ResponseData.BoardName = ThrowErrorIfFailed(await this.XMOJDatabase.Select("bbs_board", ["board_name"], { board_id: Post[0]["board_id"] }))[0]["board_name"];
471
-
472
- let Locked = ThrowErrorIfFailed(await this.XMOJDatabase.Select("bbs_lock", [], {
473
- post_id: Data["PostID"]
474
- }));
475
- if (Locked.toString() !== "") {
476
- ResponseData.Lock.Locked = true;
477
- ResponseData.Lock.LockPerson = Locked[0]["lock_person"];
478
- ResponseData.Lock.LockTime = Locked[0]["lock_time"];
479
- }
480
-
481
- let Reply = ThrowErrorIfFailed(await this.XMOJDatabase.Select("bbs_reply", [], { post_id: Data["PostID"] }, {
482
- Order: "reply_time",
483
- OrderIncreasing: true,
484
- Limit: 10,
485
- Offset: (Data["Page"] - 1) * 10
486
- }));
487
- for (let i in Reply) {
488
- let ReplyItem = Reply[i];
489
- ResponseData.Reply.push({
490
- ReplyID: ReplyItem["reply_id"],
491
- UserID: ReplyItem["user_id"],
492
- Content: ReplyItem["content"],
493
- ReplyTime: ReplyItem["reply_time"],
494
- EditTime: ReplyItem["edit_time"],
495
- EditPerson: ReplyItem["edit_person"]
496
- });
497
- }
498
- return new Result(true, "获得讨论成功", ResponseData);
499
- },
500
- LockPost: async (Data: object): Promise<Result> => {
501
- ThrowErrorIfFailed(this.CheckParams(Data, {
502
- "PostID": "number"
503
- }));
504
- if (ThrowErrorIfFailed(await this.XMOJDatabase.GetTableSize("bbs_post", {
505
- post_id: Data["PostID"]
506
- }))["TableSize"] === 0) {
507
- return new Result(false, "未找到讨论");
508
- }
509
- if (!this.IsAdmin()) {
510
- return new Result(false, "没有权限锁定此讨论");
511
- }
512
- if (ThrowErrorIfFailed(await this.XMOJDatabase.GetTableSize("bbs_lock", {
513
- post_id: Data["PostID"]
514
- }))["TableSize"] === 1) {
515
- return new Result(false, "讨论已经被锁定");
516
- }
517
- ThrowErrorIfFailed(await this.XMOJDatabase.Insert("bbs_lock", {
518
- post_id: Data["PostID"],
519
- lock_person: this.Username,
520
- lock_time: new Date().getTime()
521
- }));
522
- return new Result(true, "讨论锁定成功");
523
- },
524
- UnlockPost: async (Data: object): Promise<Result> => {
525
- ThrowErrorIfFailed(this.CheckParams(Data, {
526
- "PostID": "number"
527
- }));
528
- if (ThrowErrorIfFailed(await this.XMOJDatabase.GetTableSize("bbs_post", {
529
- post_id: Data["PostID"]
530
- }))["TableSize"] === 0) {
531
- return new Result(false, "未找到讨论");
532
- }
533
- if (!this.IsAdmin()) {
534
- return new Result(false, "没有权限解锁此讨论");
535
- }
536
- if (ThrowErrorIfFailed(await this.XMOJDatabase.GetTableSize("bbs_lock", {
537
- post_id: Data["PostID"]
538
- }))["TableSize"] === 0) {
539
- return new Result(false, "讨论已经被解锁");
540
- }
541
- ThrowErrorIfFailed(await this.XMOJDatabase.Delete("bbs_lock", {
542
- post_id: Data["PostID"]
543
- }));
544
- return new Result(true, "讨论解锁成功");
545
- },
546
- EditReply: async (Data: object): Promise<Result> => {
547
- ThrowErrorIfFailed(this.CheckParams(Data, {
548
- "ReplyID": "number",
549
- "Content": "string"
550
- }));
551
- let Reply = ThrowErrorIfFailed(await this.XMOJDatabase.Select("bbs_reply", ["post_id", "user_id"], {
552
- reply_id: Data["ReplyID"]
553
- }));
554
- if (Reply.toString() === "") {
555
- return new Result(false, "未找到回复");
556
- }
557
- if (!this.IsAdmin() && Reply[0]["user_id"] !== this.Username) {
558
- return new Result(false, "没有权限编辑此回复");
559
- }
560
- if (ThrowErrorIfFailed(await this.XMOJDatabase.GetTableSize("bbs_post", {
561
- post_id: Reply[0]["post_id"]
562
- }))["TableSize"] === 0) {
563
- return new Result(false, "未找到讨论");
564
- }
565
-
566
- if (!this.IsAdmin() && ThrowErrorIfFailed(await this.XMOJDatabase.GetTableSize("bbs_lock", {
567
- post_id: Reply[0]["post_id"]
568
- }))["TableSize"] === 1) {
569
- return new Result(false, "讨论已被锁定");
570
- }
571
-
572
- Data["Content"] = Data["Content"].trim();
573
- if (Data["Content"] === "") {
574
- return new Result(false, "内容不能为空");
575
- }
576
- let MentionPeople = new Array<string>();
577
- for (let Match of String(Data["Content"]).matchAll(/@([a-zA-Z0-9]+)/g)) {
578
- if (ThrowErrorIfFailed(await this.IfUserExist(Match[1]))["Exist"]) {
579
- MentionPeople.push(Match[1]);
580
- }
581
- }
582
- await this.XMOJDatabase.Update("bbs_reply", {
583
- content: Data["Content"],
584
- edit_time: new Date().getTime(),
585
- edit_person: this.Username
586
- }, {
587
- reply_id: Data["ReplyID"]
588
- });
589
- for (let i in MentionPeople) {
590
- await this.AddBBSMention(MentionPeople[i], Reply[0]["post_id"]);
591
- }
592
- return new Result(true, "编辑回复成功");
593
- },
594
- DeletePost: async (Data: object, CheckUserID: boolean = true): Promise<Result> => {
595
- ThrowErrorIfFailed(this.CheckParams(Data, {
596
- "PostID": "number"
597
- }));
598
- let Post = ThrowErrorIfFailed(await this.XMOJDatabase.Select("bbs_post", ["user_id"], {
599
- post_id: Data["PostID"]
600
- }));
601
- if (Post.toString() === "") {
602
- return new Result(false, "未找到讨论");
603
- }
604
- if (!this.IsAdmin() && ThrowErrorIfFailed(await this.XMOJDatabase.GetTableSize("bbs_lock", {
605
- post_id: Data["PostID"]
606
- }))["TableSize"] === 1) {
607
- return new Result(false, "讨论已被锁定");
608
- }
609
- if (!this.IsAdmin() && CheckUserID && Post[0]["user_id"] !== this.Username) {
610
- return new Result(false, "没有权限删除此讨论");
611
- }
612
- let Replies = ThrowErrorIfFailed(await this.XMOJDatabase.Select("bbs_reply", ["reply_id"], {
613
- post_id: Data["PostID"]
614
- }));
615
- for (let i in Replies) {
616
- await this.XMOJDatabase.Delete("bbs_reply", {
617
- reply_id: Replies[i]["reply_id"]
618
- });
619
- }
620
- await this.XMOJDatabase.Delete("bbs_post", { post_id: Data["PostID"] });
621
- return new Result(true, "删除讨论成功");
622
- },
623
- DeleteReply: async (Data: object): Promise<Result> => {
624
- ThrowErrorIfFailed(this.CheckParams(Data, {
625
- "ReplyID": "number"
626
- }));
627
- let Reply = ThrowErrorIfFailed(await this.XMOJDatabase.Select("bbs_reply", ["user_id", "post_id"], { reply_id: Data["ReplyID"] }));
628
- if (Reply.toString() === "") {
629
- return new Result(false, "未找到回复");
630
- }
631
- if (!this.IsAdmin() && ThrowErrorIfFailed(await this.XMOJDatabase.GetTableSize("bbs_lock", {
632
- post_id: Reply[0]["post_id"]
633
- }))["TableSize"] === 1) {
634
- return new Result(false, "讨论已被锁定");
635
- }
636
- if (!this.IsAdmin() && Reply[0]["user_id"] !== this.Username) {
637
- return new Result(false, "没有权限删除此回复");
638
- }
639
- if (ThrowErrorIfFailed(await this.XMOJDatabase.GetTableSize("bbs_reply", {
640
- post_id: Reply[0]["post_id"]
641
- }))["TableSize"] === 1) {
642
- await this.ProcessFunctions.DeletePost({ PostID: Reply[0]["post_id"] }, false);
643
- }
644
- await this.XMOJDatabase.Delete("bbs_reply", { reply_id: Data["ReplyID"] });
645
- return new Result(true, "删除回复成功");
646
- },
647
- GetBBSMentionList: async (Data: object): Promise<Result> => {
648
- ThrowErrorIfFailed(this.CheckParams(Data, {}));
649
- let ResponseData = {
650
- MentionList: new Array<Object>()
651
- };
652
- let Mentions = ThrowErrorIfFailed(await this.XMOJDatabase.Select("bbs_mention", ["bbs_mention_id", "post_id", "bbs_mention_time"], {
653
- to_user_id: this.Username
654
- }));
655
- for (let i in Mentions) {
656
- let Mention = Mentions[i];
657
- let Post = ThrowErrorIfFailed(await this.XMOJDatabase.Select("bbs_post", ["user_id", "title"], { post_id: Mention["post_id"] }));
658
- if (Post.toString() === "") {
659
- continue;
660
- }
661
- ResponseData.MentionList.push({
662
- MentionID: Mention["bbs_mention_id"],
663
- PostID: Mention["post_id"],
664
- PostTitle: Post[0]["title"],
665
- MentionTime: Mention["bbs_mention_time"]
666
- });
667
- }
668
- return new Result(true, "获得讨论提及列表成功", ResponseData);
669
- },
670
- GetMailMentionList: async (Data: object): Promise<Result> => {
671
- ThrowErrorIfFailed(this.CheckParams(Data, {}));
672
- let ResponseData = {
673
- MentionList: new Array<Object>()
674
- };
675
- let Mentions = ThrowErrorIfFailed(await this.XMOJDatabase.Select("short_message_mention", ["mail_mention_id", "from_user_id", "mail_mention_time"], {
676
- to_user_id: this.Username
677
- }));
678
- for (let i in Mentions) {
679
- let Mention = Mentions[i];
680
- ResponseData.MentionList.push({
681
- MentionID: Mention["mail_mention_id"],
682
- FromUserID: Mention["from_user_id"],
683
- MentionTime: Mention["mail_mention_time"]
684
- });
685
- }
686
- return new Result(true, "获得短消息提及列表成功", ResponseData);
687
- },
688
- ReadBBSMention: async (Data: object): Promise<Result> => {
689
- ThrowErrorIfFailed(this.CheckParams(Data, {
690
- "MentionID": "number"
691
- }));
692
- let MentionData = ThrowErrorIfFailed(await this.XMOJDatabase.Select("bbs_mention", ["to_user_id"], {
693
- bbs_mention_id: Data["MentionID"]
694
- }));
695
- if (MentionData.toString() === "") {
696
- return new Result(false, "未找到提及");
697
- }
698
- if (MentionData[0]["to_user_id"] !== this.Username) {
699
- return new Result(false, "没有权限阅读此提及");
700
- }
701
- ThrowErrorIfFailed(await this.XMOJDatabase.Delete("bbs_mention", {
702
- bbs_mention_id: Data["MentionID"]
703
- }));
704
- return new Result(true, "阅读讨论提及成功");
705
- },
706
- ReadMailMention: async (Data: object): Promise<Result> => {
707
- ThrowErrorIfFailed(this.CheckParams(Data, {
708
- "MentionID": "number"
709
- }));
710
- let MentionData = ThrowErrorIfFailed(await this.XMOJDatabase.Select("short_message_mention", ["to_user_id"], {
711
- mail_mention_id: Data["MentionID"]
712
- }));
713
- if (MentionData.toString() === "") {
714
- return new Result(false, "未找到提及");
715
- }
716
- if (MentionData[0]["to_user_id"] !== this.Username) {
717
- return new Result(false, "没有权限阅读此提及");
718
- }
719
- ThrowErrorIfFailed(await this.XMOJDatabase.Delete("short_message_mention", {
720
- mail_mention_id: Data["MentionID"]
721
- }));
722
- return new Result(true, "阅读短消息提及成功");
723
- },
724
- GetMailList: async (Data: object): Promise<Result> => {
725
- ThrowErrorIfFailed(this.CheckParams(Data, {}));
726
- let ResponseData = {
727
- MailList: new Array<Object>()
728
- };
729
- let OtherUsernameList = new Array<string>();
730
- let Mails = ThrowErrorIfFailed(await this.XMOJDatabase.Select("short_message", ["message_from"], { message_to: this.Username }, {}, true));
731
- for (let i in Mails) {
732
- OtherUsernameList.push(Mails[i]["message_from"]);
733
- }
734
- Mails = ThrowErrorIfFailed(await this.XMOJDatabase.Select("short_message", ["message_to"], { message_from: this.Username }, {}, true));
735
- for (let i in Mails) {
736
- OtherUsernameList.push(Mails[i]["message_to"]);
737
- }
738
- OtherUsernameList = Array.from(new Set(OtherUsernameList));
739
- for (let i in OtherUsernameList) {
740
- let LastMessageFrom = ThrowErrorIfFailed(await this.XMOJDatabase.Select("short_message", ["content", "send_time"], {
741
- message_from: OtherUsernameList[i],
742
- message_to: this.Username
743
- }, {
744
- Order: "send_time",
745
- OrderIncreasing: false,
746
- Limit: 1
747
- }));
748
- let LastMessageTo = ThrowErrorIfFailed(await this.XMOJDatabase.Select("short_message", ["content", "send_time"], {
749
- message_from: this.Username,
750
- message_to: OtherUsernameList[i]
751
- }, {
752
- Order: "send_time",
753
- OrderIncreasing: false,
754
- Limit: 1
755
- }));
756
- let LastMessage;
757
- if (LastMessageFrom.toString() === "") {
758
- LastMessage = LastMessageTo;
759
- }
760
- else if (LastMessageTo.toString() === "") {
761
- LastMessage = LastMessageFrom;
762
- }
763
- else {
764
- LastMessage = LastMessageFrom[0]["send_time"] > LastMessageTo[0]["send_time"] ? LastMessageFrom : LastMessageTo;
765
- }
766
- let UnreadCount = ThrowErrorIfFailed(await this.XMOJDatabase.GetTableSize("short_message", {
767
- message_from: OtherUsernameList[i],
768
- message_to: this.Username,
769
- is_read: 0
770
- }));
771
- ResponseData.MailList.push({
772
- OtherUser: OtherUsernameList[i],
773
- LastsMessage: LastMessage[0]["content"],
774
- SendTime: LastMessage[0]["send_time"],
775
- UnreadCount: UnreadCount["TableSize"]
776
- });
777
- }
778
- ResponseData.MailList.sort((a, b) => {
779
- return a["SendTime"] < b["SendTime"] ? 1 : -1;
780
- });
781
- return new Result(true, "获得短消息列表成功", ResponseData);
782
- },
783
- SendMail: async (Data: object): Promise<Result> => {
784
- ThrowErrorIfFailed(this.CheckParams(Data, {
785
- "ToUser": "string",
786
- "Content": "string"
787
- }));
788
- if (ThrowErrorIfFailed(await this.IfUserExist(Data["ToUser"]))["Exist"] === false) {
789
- return new Result(false, "未找到用户");
790
- }
791
- if (Data["ToUser"] === this.Username) {
792
- return new Result(false, "无法给自己发送短消息");
793
- }
794
- let MessageID = ThrowErrorIfFailed(await this.XMOJDatabase.Insert("short_message", {
795
- message_from: this.Username,
796
- message_to: Data["ToUser"],
797
- content: Data["Content"],
798
- send_time: new Date().getTime()
799
- }))["InsertID"];
800
- await this.AddMailMention(this.Username, Data["ToUser"]);
801
- return new Result(true, "发送短消息成功", {
802
- MessageID: MessageID
803
- });
804
- },
805
- GetMail: async (Data: object): Promise<Result> => {
806
- ThrowErrorIfFailed(this.CheckParams(Data, {
807
- "OtherUser": "string"
808
- }));
809
- let ResponseData = {
810
- Mail: new Array<Object>()
811
- };
812
- let Mails = ThrowErrorIfFailed(await this.XMOJDatabase.Select("short_message", [], {
813
- message_from: Data["OtherUser"],
814
- message_to: this.Username
815
- }, {
816
- Order: "send_time",
817
- OrderIncreasing: false
818
- }));
819
- for (let i in Mails) {
820
- let Mail = Mails[i];
821
- ResponseData.Mail.push({
822
- MessageID: Mail["message_id"],
823
- FromUser: Mail["message_from"],
824
- ToUser: Mail["message_to"],
825
- Content: Mail["content"],
826
- SendTime: Mail["send_time"],
827
- IsRead: Mail["is_read"]
828
- });
829
- }
830
- Mails = ThrowErrorIfFailed(await this.XMOJDatabase.Select("short_message", [], {
831
- message_from: this.Username,
832
- message_to: Data["OtherUser"]
833
- }, {
834
- Order: "send_time",
835
- OrderIncreasing: false
836
- }));
837
- for (let i in Mails) {
838
- let Mail = Mails[i];
839
- ResponseData.Mail.push({
840
- MessageID: Mail["message_id"],
841
- FromUser: Mail["message_from"],
842
- ToUser: Mail["message_to"],
843
- Content: Mail["content"],
844
- SendTime: Mail["send_time"],
845
- IsRead: Mail["is_read"]
846
- });
847
- }
848
- ResponseData.Mail.sort((a, b) => {
849
- return a["SendTime"] < b["SendTime"] ? 1 : -1;
850
- });
851
- await this.XMOJDatabase.Update("short_message", {
852
- is_read: 1
853
- }, {
854
- message_from: Data["OtherUser"],
855
- message_to: this.Username
856
- });
857
- return new Result(true, "获得短消息成功", ResponseData);
858
- },
859
- UploadStd: async (Data: object): Promise<Result> => {
860
- ThrowErrorIfFailed(this.CheckParams(Data, {
861
- "ProblemID": "number"
862
- }));
863
- let ProblemID = Data["ProblemID"];
864
- if (await this.GetProblemScore(ProblemID) !== 100) {
865
- return new Result(false, "没有权限上传此标程");
866
- }
867
- if (ThrowErrorIfFailed(await this.XMOJDatabase.GetTableSize("std_answer", {
868
- problem_id: ProblemID
869
- }))["TableSize"] !== 0) {
870
- return new Result(true, "此题已经有人上传标程");
871
- }
872
- var StdCode: string = "";
873
- var PageIndex: number = 0;
874
- while (StdCode === "") {
875
- await this.Fetch(new URL("http://www.xmoj.tech/problemstatus.php?id=" + ProblemID + "&page=" + PageIndex))
876
- .then((Response) => {
877
- return Response.text();
878
- }).then(async (Response) => {
879
- if (Response.indexOf("[NEXT]") === -1) {
880
- StdCode = "这道题没有标程(即用户std没有AC这道题)";
881
- return;
882
- }
883
- let ParsedDocument: CheerioAPI = load(Response);
884
- let SubmitTable = ParsedDocument("#problemstatus");
885
- if (SubmitTable.length == 0) {
886
- Output.Error("Get Std code failed: Cannot find submit table\n" +
887
- "ProblemID: \"" + ProblemID + "\"\n" +
888
- "Username : \"" + this.Username + "\"\n");
889
- ThrowErrorIfFailed(new Result(false, "获取标程失败"));
890
- }
891
- let SubmitTableBody = SubmitTable.children().eq(1);
892
- for (let i = 1; i < SubmitTableBody.children().length; i++) {
893
- let SubmitRow = SubmitTableBody.children().eq(i);
894
- if (SubmitRow.children().eq(2).text().trim() === "std") {
895
- let SID: string = SubmitRow.children().eq(1).text();
896
- if (SID.indexOf("(") != -1) {
897
- SID = SID.substring(0, SID.indexOf("("));
898
- }
899
- await this.Fetch(new URL("http://www.xmoj.tech/getsource.php?id=" + SID))
900
- .then((Response) => {
901
- return Response.text();
902
- })
903
- .then((Response) => {
904
- Response = Response.substring(0, Response.indexOf("<!--not cached-->")).trim();
905
- if (Response === "I am sorry, You could not view this code!") {
906
- Output.Error("Get Std code failed: Cannot view code\n" +
907
- "ProblemID: \"" + ProblemID + "\"\n" +
908
- "Username : \"" + this.Username + "\"\n");
909
- ThrowErrorIfFailed(new Result(false, "获取标程失败"));
910
- }
911
- Response = Response.substring(0, Response.indexOf("/**************************************************************")).trim();
912
- StdCode = Response;
913
- });
914
- }
915
- }
916
- }).catch((Error) => {
917
- Output.Error("Get Std code failed: " + Error + "\n" +
918
- "ProblemID: \"" + ProblemID + "\"\n" +
919
- "Username : \"" + this.Username + "\"\n");
920
- ThrowErrorIfFailed(new Result(false, "获取标程失败"));
921
- });
922
- PageIndex++;
923
- }
924
-
925
- ThrowErrorIfFailed(await this.XMOJDatabase.Insert("std_answer", {
926
- problem_id: Data["ProblemID"],
927
- std_code: StdCode
928
- }));
929
- return new Result(true, "标程上传成功");
930
- },
931
- GetStdList: async (Data: object): Promise<Result> => {
932
- ThrowErrorIfFailed(this.CheckParams(Data, {}));
933
- let ResponseData = {
934
- StdList: new Array<number>()
935
- };
936
- let StdList = ThrowErrorIfFailed(await this.XMOJDatabase.Select("std_answer", ["problem_id"]));
937
- for (let i in StdList) {
938
- ResponseData.StdList.push(StdList[i]["problem_id"]);
939
- }
940
- return new Result(true, "获得标程列表成功", ResponseData);
941
- },
942
- GetStd: async (Data: object): Promise<Result> => {
943
- ThrowErrorIfFailed(this.CheckParams(Data, {
944
- "ProblemID": "number"
945
- }));
946
- if (await this.GetProblemScore(Data["ProblemID"]) < 50) {
947
- return new Result(false, "没有权限获取此标程");
948
- }
949
- let Std = ThrowErrorIfFailed(await this.XMOJDatabase.Select("std_answer", ["std_code"], {
950
- problem_id: Data["ProblemID"]
951
- }));
952
- if (Std.toString() === "") {
953
- return new Result(false, "此题还没有人上传标程");
954
- }
955
- return new Result(true, "获得标程成功", {
956
- "StdCode": Std[0]["std_code"]
957
- });
958
- },
959
- NewBadge: async (Data: object): Promise<Result> => {
960
- ThrowErrorIfFailed(this.CheckParams(Data, {
961
- "UserID": "string"
962
- }));
963
- if (!this.IsAdmin()) {
964
- return new Result(false, "没有权限创建此标签");
965
- }
966
- ThrowErrorIfFailed(await this.XMOJDatabase.Insert("badge", {
967
- user_id: Data["UserID"]
968
- }));
969
- return new Result(true, "创建标签成功");
970
- },
971
- EditBadge: async (Data: object): Promise<Result> => {
972
- ThrowErrorIfFailed(this.CheckParams(Data, {
973
- "UserID": "string",
974
- "BackgroundColor": "string",
975
- "Color": "string",
976
- "Content": "string"
977
- }));
978
- if (!this.IsAdmin() && Data["UserID"] !== this.Username) {
979
- return new Result(false, "没有权限编辑此标签");
980
- }
981
- if (ThrowErrorIfFailed(await this.XMOJDatabase.GetTableSize("badge", {
982
- user_id: Data["UserID"]
983
- }))["TableSize"] === 0) {
984
- return new Result(false, "未找到标签");
985
- }
986
- ThrowErrorIfFailed(await this.XMOJDatabase.Update("badge", {
987
- background_color: Data["BackgroundColor"],
988
- color: Data["Color"],
989
- content: Data["Content"]
990
- }, {
991
- user_id: Data["UserID"]
992
- }));
993
- return new Result(true, "编辑标签成功");
994
- },
995
- GetBadge: async (Data: object): Promise<Result> => {
996
- ThrowErrorIfFailed(this.CheckParams(Data, {
997
- "UserID": "string"
998
- }));
999
- let BadgeData = ThrowErrorIfFailed(await this.XMOJDatabase.Select("badge", ["background_color", "color", "content"], {
1000
- user_id: Data["UserID"]
1001
- }));
1002
- if (BadgeData.toString() == "") {
1003
- return new Result(false, "未找到标签");
1004
- }
1005
- return new Result(true, "获得标签成功", {
1006
- Content: BadgeData[0]["content"],
1007
- BackgroundColor: BadgeData[0]["background_color"],
1008
- Color: BadgeData[0]["color"]
1009
- });
1010
- },
1011
- DeleteBadge: async (Data: object): Promise<Result> => {
1012
- ThrowErrorIfFailed(this.CheckParams(Data, {
1013
- "UserID": "string"
1014
- }));
1015
- if (!this.IsAdmin()) {
1016
- return new Result(false, "没有权限删除此标签");
1017
- }
1018
- ThrowErrorIfFailed(await this.XMOJDatabase.Delete("badge", {
1019
- user_id: Data["UserID"]
1020
- }));
1021
- return new Result(true, "删除标签成功");
1022
- },
1023
- GetBoards: async (Data: object): Promise<Result> => {
1024
- ThrowErrorIfFailed(this.CheckParams(Data, {}));
1025
- let Boards: Array<Object> = new Array<Object>();
1026
- let BoardsData = ThrowErrorIfFailed(await this.XMOJDatabase.Select("bbs_board", []));
1027
- for (let i in BoardsData) {
1028
- let Board = BoardsData[i];
1029
- Boards.push({
1030
- BoardID: Board["board_id"],
1031
- BoardName: Board["board_name"]
1032
- });
1033
- }
1034
- return new Result(true, "获得板块列表成功", {
1035
- "Boards": Boards
1036
- });
1037
- },
1038
- UploadImage: async (Data: object): Promise<Result> => {
1039
- const GithubImageRepo = "PythonSmall-Q/XMOJ-Script-Pictures";
1040
- ThrowErrorIfFailed(this.CheckParams(Data, {
1041
- "Image": "string"
1042
- }));
1043
- let Image: String = Data["Image"];
1044
- let ImageID: String = "";
1045
- for (let i = 0; i < 32; i++) {
1046
- ImageID += String.fromCharCode(Math.floor(Math.random() * 26) + 97);
1047
- }
1048
- let ImageData = Image.replace(/^data:image\/\w+;base64,/, "");
1049
- await fetch(new URL("https://api.github.com/repos/" + GithubImageRepo + "/contents/" + ImageID), {
1050
- method: "PUT",
1051
- headers: {
1052
- "Authorization": "Bearer " + this.GithubImagePAT,
1053
- "Content-Type": "application/json",
1054
- "User-Agent": "XMOJ-Script-Server"
1055
- },
1056
- body: JSON.stringify({
1057
- message: `${this.Username} ${new Date().getFullYear()}/${new Date().getMonth() + 1}/${new Date().getDate()} ${new Date().getHours()}:${new Date().getMinutes()}:${new Date().getSeconds()}`,
1058
- content: ImageData
1059
- })
1060
- }).then((Response) => {
1061
- return Response.json();
1062
- }).then((Response) => {
1063
- if (Response["content"]["name"] !== ImageID) {
1064
- Output.Error("Upload image failed\n" +
1065
- "Username: \"" + this.Username + "\"\n" +
1066
- "ImageID : \"" + ImageID + "\"\n" +
1067
- "Response: \"" + JSON.stringify(Response) + "\"\n");
1068
- ThrowErrorIfFailed(new Result(false, "上传图片失败"));
1069
- }
1070
- }).catch((Error) => {
1071
- Output.Error("Upload image failed: " + Error + "\n" +
1072
- "Username: \"" + this.Username + "\"\n" +
1073
- "ImageID : \"" + ImageID + "\"\n");
1074
- ThrowErrorIfFailed(new Result(false, "上传图片失败"));
1075
- });
1076
- return new Result(true, "上传图片成功", {
1077
- ImageID: ImageID
1078
- });
1079
- },
1080
- GetImage: async (Data: object): Promise<Blob> => {
1081
- const GithubImageRepo = "PythonSmall-Q/XMOJ-Script-Pictures";
1082
- ThrowErrorIfFailed(this.CheckParams(Data, {
1083
- "ImageID": "string"
1084
- }));
1085
- return await fetch(new URL("https://api.github.com/repos/" + GithubImageRepo + "/contents/" + Data["ImageID"] + "?1=1"), {
1086
- method: "GET",
1087
- headers: {
1088
- "Authorization": "Bearer " + this.GithubImagePAT,
1089
- "Accept": "application/vnd.github.v3.raw",
1090
- "User-Agent": "XMOJ-Script-Server"
1091
- }
1092
- }).then((Response) => {
1093
- return Response.blob();
1094
- }).catch((Error) => {
1095
- Output.Error("Get image failed: " + Error + "\n" +
1096
- "ImageID : \"" + Data["ImageID"] + "\"\n");
1097
- return new Blob();
1098
- });
1099
- },
1100
- SendData: async (Data: object): Promise<Result> => {
1101
- //instantly return
1102
- return new Result(true, "数据发送成功");
1103
- },
1104
- GetAnalytics: async (Data: object): Promise<Result> => {
1105
- ThrowErrorIfFailed(this.CheckParams(Data, {
1106
- "Username": "string"
1107
- }));
1108
- if (Data["Username"] !== this.Username && !this.IsAdmin()) {
1109
- return new Result(false, "没有权限获取此用户日志");
1110
- }
1111
-
1112
- let sanitizedUsername = sqlstring.escape(Data["Username"]);
1113
- const query = `SELECT index1 AS username, blob1 AS IP, blob2 AS Path, blob3 AS Version, blob4 AS DebugMode, timestamp FROM logdb WHERE index1=${sanitizedUsername} ORDER BY timestamp ASC`;
1114
-
1115
- const API = `https://api.cloudflare.com/client/v4/accounts/${this.ACCOUNT_ID}/analytics_engine/sql`;
1116
- const response = await fetch(API, {
1117
- method: 'POST',
1118
- headers: {
1119
- 'Authorization': `Bearer ${this.API_TOKEN}`,
1120
- },
1121
- body: query,
1122
- });
1123
- const responseJSON = await response.json();
1124
- return new Result(true, "获得统计数据成功", responseJSON);
1125
- },
1126
- LastOnline: async (Data: object): Promise<Result> => {
1127
- ThrowErrorIfFailed(this.CheckParams(Data, {
1128
- "Username": "string"
1129
- }));
1130
- let username = Data["Username"];
1131
- let sanitizedUsername = sqlstring.escape(username);
1132
- const query = `SELECT timestamp FROM logdb WHERE index1=${sanitizedUsername} ORDER BY timestamp DESC LIMIT 1`;
1133
- const API = `https://api.cloudflare.com/client/v4/accounts/${this.ACCOUNT_ID}/analytics_engine/sql`;
1134
- const response = await fetch(API, {
1135
- method: 'POST',
1136
- headers: {
1137
- 'Authorization': `Bearer ${this.API_TOKEN}`,
1138
- },
1139
- body: query,
1140
- });
1141
- const responseJSON = await response.json();
1142
- // parse json and return ["data"][0][timestamp]
1143
- if (responseJSON.data && responseJSON.data.length > 0) {
1144
- let timestamp = responseJSON.data[0].timestamp;
1145
- let unixTime = Date.parse(timestamp);
1146
- return new Result(true, "获得最近登录时间成功", { "logintime": unixTime });
1147
- } else {
1148
- return new Result(false, "获得最近登录时间失败", {});
1149
- }
1150
- }
1151
- };
1152
- constructor(RequestData: Request, Environment) {
1153
- this.XMOJDatabase = new Database(Environment.DB);
1154
- this.logs = Environment.logdb;
1155
- this.CaptchaSecretKey = Environment.CaptchaSecretKey;
1156
- this.GithubImagePAT = Environment.GithubImagePAT;
1157
- this.ACCOUNT_ID = Environment.ACCOUNT_ID;
1158
- this.API_TOKEN = Environment.API_TOKEN;
1159
- this.RequestData = RequestData;
1160
- this.RemoteIP = RequestData.headers.get("CF-Connecting-IP") || "";
1161
- }
1162
- public async Process(): Promise<Response> {
1163
- try {
1164
- let PathName = new URL(this.RequestData.url).pathname;
1165
- PathName = PathName === "/" ? "/index" : PathName;
1166
- PathName = PathName.substring(1);
1167
- if (this.ProcessFunctions[PathName] === undefined) {
1168
- throw new Result(false, "访问的页面不存在");
1169
- }
1170
- if (this.RequestData.method === "GET" && PathName === "GetImage") {
1171
- return new Response(await this.ProcessFunctions[PathName]({
1172
- ImageID: new URL(this.RequestData.url).searchParams.get("ImageID")
1173
- }), {
1174
- headers: {
1175
- "content-type": "image/png"
1176
- }
1177
- });
1178
- }
1179
- if (this.RequestData.method !== "POST") {
1180
- throw new Result(false, "不允许此请求方式");
1181
- }
1182
- if (this.RequestData.headers.get("content-type") !== "application/json") {
1183
- throw new Result(false, "不允许此资源类型");
1184
- }
1185
- let RequestJSON: object;
1186
- try {
1187
- RequestJSON = await this.RequestData.json();
1188
- }
1189
- catch (Error) {
1190
- throw new Result(false, "请求格式有误");
1191
- }
1192
- ThrowErrorIfFailed(this.CheckParams(RequestJSON, {
1193
- "Authentication": "object",
1194
- "Data": "object",
1195
- "Version": "string",
1196
- "DebugMode": "boolean"
1197
- }));
1198
- var TokenFailedCount = 0;
1199
- while (true) {
1200
- if ((await this.CheckToken(RequestJSON["Authentication"])).Data["Success"]) {
1201
- break;
1202
- }
1203
- TokenFailedCount++;
1204
- if (TokenFailedCount >= 2) {
1205
- ThrowErrorIfFailed(await this.CheckToken(RequestJSON["Authentication"]));
1206
- break;
1207
- }
1208
- }
1209
- this.logs.writeDataPoint({
1210
- 'blobs': [this.RemoteIP, PathName, RequestJSON["Version"], RequestJSON["DebugMode"]],
1211
- 'indexes': [this.Username]
1212
- });
1213
- throw await this.ProcessFunctions[PathName](RequestJSON["Data"]);
1214
- }
1215
- catch (ResponseData) {
1216
- if (!(ResponseData instanceof Result)) {
1217
- Output.Error(ResponseData);
1218
- ResponseData = new Result(false, "服务器运行错误:" + String(ResponseData).split("\n")[0]);
1219
- }
1220
- return new Response(JSON.stringify(ResponseData), {
1221
- headers: {
1222
- "content-type": "application/json;charset=UTF-8"
1223
- }
1224
- });
1225
- }
1226
- }
1227
- }