dna-api 0.2.6 → 0.2.7

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/src/index.ts CHANGED
@@ -1,1644 +1,1736 @@
1
- import * as forge from "node-forge"
2
- //#region const
3
- enum RespCode {
4
- ERROR = -999,
5
- OK_ZERO = 0,
6
- OK_HTTP = 200,
7
- BAD_REQUEST = 400,
8
- SERVER_ERROR = 500,
9
- }
10
-
11
- const DNA_GAME_ID = 268
12
- //#endregion
13
-
14
- /**
15
- * DNA API类,用于与DNA游戏服务器交互
16
- */
17
- export class DNAAPI {
18
- public fetchFn?: typeof fetch
19
- public is_h5 = false
20
- public RSA_PUBLIC_KEY =
21
- "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDGpdbezK+eknQZQzPOjp8mr/dP+QHwk8CRkQh6C6qFnfLH3tiyl0pnt3dePuFDnM1PUXGhCkQ157ePJCQgkDU2+mimDmXh0oLFn9zuWSp+U8uLSLX3t3PpJ8TmNCROfUDWvzdbnShqg7JfDmnrOJz49qd234W84nrfTHbzdqeigQIDAQAB"
22
- public BASE_URL = "https://dnabbs-api.yingxiong.com/"
23
-
24
- /**
25
- * 构造函数
26
- * @param dev_code 设备码
27
- * @param uid 用户ID
28
- * @param token 访问令牌
29
- * @param options 选项
30
- * @param options.fetchFn 自定义fetch函数
31
- * @param options.is_h5 是否为H5端
32
- * @param options.rsa_public_key RSA公钥(base64) 设为空字符串从服务器获取
33
- */
34
- constructor(
35
- public dev_code: string,
36
- public token = "",
37
- options: { fetchFn?: typeof fetch; is_h5?: boolean; rsa_public_key?: string } = {},
38
- ) {
39
- this.fetchFn = options.fetchFn
40
- if (options.is_h5 !== undefined) this.is_h5 = options.is_h5
41
- if (options.rsa_public_key !== undefined) this.RSA_PUBLIC_KEY = options.rsa_public_key
42
- }
43
-
44
- /**
45
- * 获取RSA公钥
46
- * @returns RSA公钥(base64)
47
- */
48
- async getRsaPublicKey() {
49
- if (this.RSA_PUBLIC_KEY) {
50
- return this.RSA_PUBLIC_KEY
51
- }
52
- const res = await this._dna_request<{ key: string }>("config/getRsaPublicKey")
53
-
54
- if (res.is_success && res.data) {
55
- const key = res.data.key
56
- if (typeof key === "string") {
57
- this.RSA_PUBLIC_KEY = key
58
- }
59
- }
60
- return this.RSA_PUBLIC_KEY
61
- }
62
-
63
- /**
64
- * 登录
65
- */
66
- async login(mobile: string, code: string) {
67
- const data = { mobile, code, gameList: DNA_GAME_ID }
68
- const res = await this._dna_request<DNALoginRes>("user/sdkLogin", data, { sign: true, refer: true })
69
- if (res.is_success && res.data) {
70
- const data = res.data
71
- if (typeof data.token === "string") {
72
- this.token = data.token
73
- }
74
- }
75
- return res
76
- }
77
-
78
- /**
79
- * 获取登录日志
80
- */
81
- async loginLog() {
82
- return await this._dna_request<DNALoginRes>("user/login/log")
83
- }
84
-
85
- /**
86
- * 获取角色列表
87
- */
88
- async getRoleList() {
89
- return await this._dna_request<DNARoleListRes>("role/list")
90
- }
91
-
92
- /**
93
- * 获取默认角色
94
- */
95
- async getDefaultRoleForTool() {
96
- const data = { type: 1 }
97
- return await this._dna_request<DNARoleForToolRes>("role/defaultRoleForTool", data, { sign: true, token: true, tokenSig: true })
98
- }
99
-
100
- /**
101
- * 获取角色详情
102
- */
103
- async getCharDetail(char_id: string, char_eid: string, otherUserId?: string) {
104
- const data = { charId: char_id, charEid: char_eid, type: 1, otherUserId } as any
105
- return await this._dna_request<DNACharDetailRes>("role/getCharDetail", data)
106
- }
107
-
108
- /**
109
- * 获取武器详情
110
- */
111
- async getWeaponDetail(weapon_id: string, weapon_eid: string, otherUserId?: string) {
112
- const data = { weaponId: weapon_id, weaponEid: weapon_eid, type: 1, otherUserId }
113
- return await this._dna_request<DNAWeaponDetailRes>("role/getWeaponDetail", data)
114
- }
115
-
116
- /**
117
- * 获取角色简讯
118
- */
119
- async getShortNoteInfo() {
120
- return await this._dna_request<DNARoleShortNoteRes>("role/getShortNoteInfo")
121
- }
122
-
123
- /**
124
- * 检查是否签到
125
- */
126
- async haveSignIn() {
127
- const data = { gameId: DNA_GAME_ID }
128
- return await this._dna_request<DNAHaveSignInRes>("user/haveSignInNew", data)
129
- }
130
-
131
- /**
132
- * 签到日历
133
- */
134
- async signCalendar() {
135
- const data = { gameId: DNA_GAME_ID }
136
- return await this._dna_request<DNACalendarSignRes>("encourage/signin/show", data)
137
- }
138
-
139
- /** ? */
140
- async soulTask() {
141
- return await this._dna_request("role/soul/task")
142
- }
143
-
144
- /**
145
- * 游戏签到
146
- */
147
- async gameSign(day_award_id: number, period: number) {
148
- const data = {
149
- dayAwardId: day_award_id,
150
- periodId: period,
151
- signinType: 1,
152
- }
153
- return await this._dna_request("encourage/signin/signin", data)
154
- }
155
-
156
- /**
157
- * 皎皎角签到
158
- */
159
- async bbsSign() {
160
- const data = { gameId: DNA_GAME_ID }
161
- return await this._dna_request("user/signIn", data)
162
- }
163
-
164
- /**
165
- * 获取任务进度
166
- */
167
- async getTaskProcess() {
168
- const data = { gameId: DNA_GAME_ID }
169
- return await this._dna_request<DNATaskProcessRes>("encourage/level/getTaskProcess", data)
170
- }
171
-
172
- /**
173
- * 获取帖子列表
174
- * @param forumId 论坛ID
175
- * @param pageIndex 页码
176
- * @param pageSize 每页数量
177
- * @param searchType 搜索类型 1:最新 2:热门
178
- * @param timeType 时间类型 0:全部 1:今日 2:本周 3:本月
179
- * @returns 帖子列表
180
- */
181
- async getPostList(forumId: number = 48, pageIndex: number = 1, pageSize: number = 20, searchType: number = 1, timeType: number = 0) {
182
- const data = {
183
- forumId: forumId,
184
- gameId: DNA_GAME_ID,
185
- pageIndex: pageIndex,
186
- pageSize: pageSize,
187
- searchType: searchType, // 1:最新 2:热门
188
- timeType: timeType, // 0:全部 1:今日 2:本周 3:本月
189
- }
190
- return await this._dna_request<DNAPostListRes>("forum/list", data)
191
- }
192
-
193
- /** 管理员锁定帖子 */
194
- async lockPost(post: { postId: number; gameId?: number; gameForumId: number; operateType: number }) {
195
- const data = {
196
- postId: post.postId,
197
- gameId: post.gameId ?? DNA_GAME_ID,
198
- gameForumId: post.gameForumId,
199
- operateType: post.operateType,
200
- }
201
- return await this._dna_request("forum/moderator/postLock", data)
202
- }
203
-
204
- /** 管理员移动帖子 */
205
- async postDownOrUp(post: { postId: number; gameId?: number; gameForumId: number; operateType: number }) {
206
- const data = {
207
- postId: post.postId,
208
- gameId: post.gameId ?? DNA_GAME_ID,
209
- gameForumId: post.gameForumId,
210
- operateType: post.operateType,
211
- }
212
- return await this._dna_request("forum/moderator/postDownOrUp", data)
213
- }
214
-
215
- /** 管理员设精 */
216
- async postElite(post: { postId: number; gameId?: number; gameForumId: number; operateType: number }) {
217
- const data = {
218
- postId: post.postId,
219
- gameId: post.gameId ?? DNA_GAME_ID,
220
- gameForumId: post.gameForumId,
221
- operateType: post.operateType,
222
- }
223
- return await this._dna_request("forum/moderator/postElite", data)
224
- }
225
-
226
- /** 管理员隐藏帖子 */
227
- async postHide(post: { postId: number; gameId?: number; gameForumId: number; operateType: number }) {
228
- const data = {
229
- postId: post.postId,
230
- gameId: post.gameId ?? DNA_GAME_ID,
231
- gameForumId: post.gameForumId,
232
- operateType: post.operateType,
233
- }
234
- return await this._dna_request("forum/moderator/postHide", data)
235
- }
236
-
237
- /** 管理员设置权重 */
238
- async reRank(post: { postId: number; gameId?: number; gameForumId: number }, weight: number) {
239
- const data = {
240
- postId: post.postId,
241
- gameId: post.gameId ?? DNA_GAME_ID,
242
- gameForumId: post.gameForumId,
243
- weight: weight,
244
- }
245
- return await this._dna_request("forum/moderator/reRank", data)
246
- }
247
-
248
- /** 管理员设置强推 */
249
- async strongRecommend(post: { postId: number; gameId?: number; gameForumId: number }, operateType = 1) {
250
- const data = {
251
- postId: post.postId,
252
- gameId: post.gameId ?? DNA_GAME_ID,
253
- gameForumId: post.gameForumId,
254
- operateType: operateType,
255
- }
256
- return await this._dna_request("forum/moderator/setForceRecommend", data)
257
- }
258
-
259
- /** 管理员删帖 */
260
- async adminDelete(post: { postId: number; gameId?: number; gameForumId: number }, content: string, reasonCode: number) {
261
- const data = {
262
- postId: post.postId,
263
- gameId: post.gameId ?? DNA_GAME_ID,
264
- gameForumId: post.gameForumId,
265
- content: content,
266
- reasonCode: reasonCode,
267
- }
268
- return await this._dna_request("forum/moderator/postDelete", data)
269
- }
270
- /** 管理员移动帖子 */
271
- async adminMovePost(
272
- post: { postId: number; gameId?: number; gameForumId: number },
273
- newGameId: number,
274
- newForumId: number,
275
- newTopicIdStr: string,
276
- ) {
277
- const data = {
278
- postId: post.postId,
279
- gameId: post.gameId ?? DNA_GAME_ID,
280
- gameForumId: post.gameForumId,
281
- newGameId: newGameId,
282
- newForumId: newForumId,
283
- newTopicIdStr: newTopicIdStr,
284
- }
285
- return await this._dna_request("forum/moderator/postMove", data)
286
- }
287
- /** ? */
288
- async adminRefreshTime(post: { postId: number; gameId?: number; gameForumId: number }, refresh: number) {
289
- const data = {
290
- postId: post.postId,
291
- gameId: post.gameId ?? DNA_GAME_ID,
292
- gameForumId: post.gameForumId,
293
- refresh: refresh,
294
- }
295
- return await this._dna_request("forum/moderator/setRefresh", data)
296
- }
297
-
298
- /** 黑名单 */
299
- async blockList() {
300
- return await this._dna_request("user/block/list")
301
- }
302
-
303
- /** 拉黑 */
304
- async blockOther(blockPostId: number, blockUserId: string, type: number) {
305
- const data = {
306
- blockPostId: blockPostId,
307
- blockUserId: blockUserId,
308
- type: type,
309
- }
310
- return await this._dna_request("user/block/list", data)
311
- }
312
-
313
- /** ? */
314
- async viewCommunity() {
315
- return await this._dna_request("encourage/level/viewCommunity")
316
- }
317
-
318
- /** ? */
319
- async viewCount() {
320
- return await this._dna_request("forum/viewCount")
321
- }
322
-
323
- /** ? */
324
- async receiveLog(periodId: number, pageIndex: number, pageSize: number) {
325
- const data = {
326
- periodId: periodId,
327
- pageIndex: pageIndex,
328
- pageSize: pageSize,
329
- }
330
- return await this._dna_request("encourage/signin/receiveLog", data)
331
- }
332
-
333
- /** 收藏 */
334
- async collect(postId: number, toUserId: string, operateType = 1) {
335
- const data = {
336
- operateType: operateType,
337
- postId: postId,
338
- toUserId: toUserId,
339
- }
340
- return await this._dna_request("forum/collect", data)
341
- }
342
-
343
- /** 删除评论 */
344
- async commentDelete(
345
- comment: { id: number; gameId: number; gameForumId: number },
346
- entityType: number,
347
- content: string,
348
- reasonCode: number,
349
- ) {
350
- const data = {
351
- id: comment.id,
352
- gameId: comment.gameId,
353
- gameForumId: comment.gameForumId,
354
- entityType: entityType,
355
- content: content,
356
- reasonCode: reasonCode,
357
- }
358
- return await this._dna_request("forum/collect", data)
359
- }
360
-
361
- /** 推荐列表 */
362
- async recommendList(recIndex: number, newIndex: number, size: number, history: number, gameId = DNA_GAME_ID) {
363
- const data = {
364
- gameId: gameId,
365
- recIndex: recIndex,
366
- newIndex: newIndex,
367
- size: size,
368
- history: history,
369
- }
370
- return await this._dna_request("forum/recommend/list", data)
371
- }
372
-
373
- /** 举报 */
374
- async report(
375
- { commentId = 0, postId = 0, replyId = 0 }: { commentId?: number; postId?: number; replyId?: number },
376
- reportReason = 1,
377
- reportType = 1,
378
- ) {
379
- const data = {
380
- commentId: commentId,
381
- postId: postId,
382
- replyId: replyId,
383
- reportReason: reportReason,
384
- reportType: reportType,
385
- }
386
- return await this._dna_request<DNAPostListRes>("forum/recommend/list", data)
387
- }
388
- /** 搜索帖子 */
389
- async searchPost(keyword: number, pageIndex: number, pageSize: number, gameId = DNA_GAME_ID, searchType = 1) {
390
- const data = {
391
- gameId: gameId,
392
- keyword: keyword,
393
- pageIndex: pageIndex,
394
- pageSize: pageSize,
395
- searchType: searchType,
396
- }
397
- return await this._dna_request<DNAPostListRes>("forum/searchPost", data)
398
- }
399
- /** 搜索帖子 */
400
- async searchTopic(keyword: number, pageIndex: number, pageSize = 20, gameId = DNA_GAME_ID) {
401
- const data = {
402
- gameId: gameId,
403
- keyword: keyword,
404
- pageIndex: pageIndex,
405
- pageSize: pageSize,
406
- }
407
- return await this._dna_request<DNAPostListRes>("config/searchTopic", data)
408
- }
409
-
410
- /** 搜索帖子 */
411
- async searchUser(keyword: number, pageIndex: number, pageSize: number) {
412
- const data = {
413
- keyword: keyword,
414
- pageIndex: pageIndex,
415
- pageSize: pageSize,
416
- }
417
- return await this._dna_request<DNAPostListRes>("user/searchUser", data)
418
- }
419
-
420
- /**
421
- * 获取帖子列表
422
- * @param topicId 主题ID
423
- * @param pageIndex 页码
424
- * @param pageSize 每页数量
425
- * @param searchType 搜索类型 1:最新 2:热门
426
- * @param timeType 时间类型 0:全部 1:今日 2:本周 3:本月
427
- * @returns 帖子列表
428
- */
429
- async getPostsByTopic(
430
- topicId: number = 177,
431
- pageIndex: number = 1,
432
- pageSize: number = 20,
433
- searchType: number = 1,
434
- timeType: number = 0,
435
- ) {
436
- const data = {
437
- topicId: topicId,
438
- gameId: DNA_GAME_ID,
439
- pageIndex: pageIndex,
440
- pageSize: pageSize,
441
- searchType: searchType, // 1:最新 2:热门
442
- timeType: timeType, // 0:全部 1:今日 2:本周 3:本月
443
- }
444
- return await this._dna_request<DNAPostListRes>("forum/getPostByTopic", data)
445
- }
446
-
447
- /**
448
- * 获取帖子详情
449
- * @param post_id 帖子ID
450
- * @returns 帖子详情
451
- */
452
- async getPostDetail(post_id: string) {
453
- const data = { postId: post_id }
454
- return await this._dna_request<DNAPostDetailRes>("forum/getPostDetail", data)
455
- }
456
-
457
- /** 关注用户 */
458
- async doFollow(userId: string, unfollow?: boolean) {
459
- const data = {
460
- followUserId: userId,
461
- operateType: unfollow ? 0 : 1,
462
- }
463
- return await this._dna_request("user/followUser", data, { sign: true })
464
- }
465
-
466
- /** 获取关注状态 */
467
- async getFollowState(userId: string) {
468
- const data = {
469
- followUserId: userId,
470
- }
471
- return await this._dna_request("user/isFollow", data)
472
- }
473
-
474
- /**
475
- * 点赞帖子
476
- */
477
- async doLike(post: { gameForumId: string; postId: string; postType: string; userId: string }) {
478
- const data = {
479
- forumId: post.gameForumId,
480
- gameId: DNA_GAME_ID,
481
- likeType: "1",
482
- operateType: "1",
483
- postCommentId: "",
484
- postCommentReplyId: "",
485
- postId: post.postId,
486
- postType: post.postType,
487
- toUserId: post.userId,
488
- }
489
- return await this._dna_request("forum/like", data)
490
- }
491
-
492
- /** 分享帖子 */
493
- async doShare() {
494
- const data = { gameId: DNA_GAME_ID }
495
- return await this._dna_request("encourage/level/shareTask", data)
496
- }
497
-
498
- /** 回复帖子 */
499
- async createComment(post: { userId: string; postId: string; gameForumId: number }, content: string) {
500
- const content_json = JSON.stringify([
501
- {
502
- content,
503
- contentType: "1",
504
- imgHeight: 0,
505
- imgWidth: 0,
506
- url: "",
507
- },
508
- ])
509
- const data = {
510
- postId: post.postId,
511
- forumId: post.gameForumId,
512
- postType: "1",
513
- content: content_json,
514
- }
515
-
516
- return await this._dna_request("forum/comment/createComment", data, { sign: true, refer: true, params: { toUserId: post.userId } })
517
- }
518
-
519
- /** 回复评论 */
520
- async createReply(post: { userId: string; postId: string; postCommentId: string; gameForumId: number }, content: string) {
521
- const content_json = JSON.stringify([
522
- {
523
- content,
524
- contentType: "1",
525
- imgHeight: 0,
526
- imgWidth: 0,
527
- url: "",
528
- },
529
- ])
530
- const data = {
531
- content: content_json,
532
- forumId: post.gameForumId,
533
- postCommentId: post.postCommentId,
534
- postId: post.postId,
535
- postType: "1",
536
- toUserId: post.userId,
537
- }
538
-
539
- return await this._dna_request("forum/comment/createReply", data, { sign: true, refer: true, params: { toUserId: post.userId } })
540
- }
541
-
542
- /** 回复评论的评论 */
543
- async createReplyList(
544
- post: { userId: string; postId: string; postCommentId: string; postCommentReplyId: string; gameForumId: number },
545
- content: string,
546
- ) {
547
- const content_json = JSON.stringify([
548
- {
549
- content,
550
- contentType: "1",
551
- imgHeight: 0,
552
- imgWidth: 0,
553
- url: "",
554
- },
555
- ])
556
- const data = {
557
- content: content_json,
558
- forumId: post.gameForumId,
559
- postCommentId: post.postCommentId,
560
- postCommentReplyId: post.postCommentReplyId,
561
- postId: post.postId,
562
- postType: "1",
563
- toUserId: post.userId,
564
- }
565
- return await this._dna_request("forum/comment/createReply", data, { sign: true, refer: true, params: { toUserId: post.userId } })
566
- }
567
-
568
- /** 删 */
569
- async deletePost(deleteType: number, id: number) {
570
- return await this._dna_request("forum/more/delete", { deleteType, id }, { sign: true, refer: true })
571
- }
572
-
573
- /**
574
- * 获取用户信息
575
- * @returns 用户信息
576
- */
577
- async getOtherMine(userId = "709542994134436647") {
578
- const data = {
579
- otherUserId: userId,
580
- searchType: 1,
581
- type: 2,
582
- }
583
- return await this._dna_request<DNAMineRes>("user/mine", data)
584
- }
585
-
586
- /**
587
- * 获取用户信息
588
- * @returns 用户信息
589
- */
590
- async getMine() {
591
- return await this._dna_request<DNAMineRes>("user/mine")
592
- }
593
-
594
- async getGameConfig() {
595
- const data = { gameId: DNA_GAME_ID }
596
- return await this._dna_request<DNAGameConfigRes[]>("config/getGameConfig", data)
597
- }
598
-
599
- async getHeaders(options?: {
600
- payload?: Record<string, any> | string
601
- exparams?: Record<string, any>
602
- dev_code?: string
603
- refer?: boolean
604
- token?: string
605
- tokenSig?: boolean
606
- }) {
607
- let { payload, exparams, dev_code = this.dev_code, refer, token = this.token, tokenSig } = options || {}
608
-
609
- const CONTENT_TYPE = "application/x-www-form-urlencoded; charset=utf-8"
610
- const iosBaseHeader = {
611
- version: "1.1.3",
612
- source: "ios",
613
- "Content-Type": CONTENT_TYPE,
614
- "User-Agent": "DoubleHelix/4 CFNetwork/3860.100.1 Darwin/25.0.0",
615
- }
616
- const h5BaseHeader = {
617
- version: "3.11.0",
618
- source: "h5",
619
- "Content-Type": CONTENT_TYPE,
620
- "User-Agent":
621
- "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
622
- }
623
- // 默认获取ios头
624
- const headers = { ...(this.is_h5 ? h5BaseHeader : iosBaseHeader) } as Record<string, any>
625
- if (dev_code) {
626
- headers.devCode = dev_code
627
- }
628
- if (refer) {
629
- headers.origin = "https://dnabbs.yingxiong.com"
630
- headers.refer = "https://dnabbs.yingxiong.com/"
631
- }
632
- if (token) {
633
- headers.token = token
634
- }
635
- if (typeof payload === "object") {
636
- const si = build_signature(payload, tokenSig ? token : "")
637
- Object.assign(payload, { sign: si.s, timestamp: si.t })
638
- if (exparams) {
639
- Object.assign(payload, exparams)
640
- }
641
-
642
- const params = new URLSearchParams()
643
- Object.entries(payload).forEach(([key, value]) => {
644
- params.append(key, String(value))
645
- })
646
- payload = params.toString()
647
-
648
- const rk = si.k
649
- const pk = await this.getRsaPublicKey()
650
- const ek = rsa_encrypt(rk, pk)
651
- if (this.is_h5) {
652
- headers.k = ek
653
- } else {
654
- headers.rk = rk
655
- headers.key = ek
656
- }
657
- }
658
- return { headers, payload }
659
- }
660
-
661
- private async _dna_request<T = any>(
662
- url: string,
663
- data?: any,
664
- options?: {
665
- method?: "GET" | "POST"
666
- sign?: boolean
667
- tokenSig?: boolean
668
- token?: boolean
669
- refer?: boolean
670
- params?: Record<string, any>
671
- max_retries?: number
672
- retry_delay?: number
673
- timeout?: number
674
- },
675
- ): Promise<DNAApiResponse<T>> {
676
- let { method = "POST", sign, refer, params, max_retries = 3, retry_delay = 1, timeout = 10000, token, tokenSig } = options || {}
677
- let headers: Record<string, any>
678
- if (sign) {
679
- const { payload: p, headers: h } = await this.getHeaders({
680
- payload: data,
681
- refer,
682
- exparams: params,
683
- token: token ? this.token : undefined,
684
- tokenSig,
685
- })
686
- data = p
687
- headers = h
688
- } else {
689
- const { headers: h } = await this.getHeaders({ token: token ? this.token : undefined })
690
- headers = h
691
- }
692
-
693
- for (let attempt = 0; attempt < max_retries; attempt++) {
694
- try {
695
- let body: string = data
696
- if (data && typeof data === "object") {
697
- const p = new URLSearchParams()
698
- Object.entries(data).forEach(([key, value]) => {
699
- if (value !== undefined) p.append(key, String(value))
700
- })
701
- body = p.toString()
702
- }
703
- const fetchOptions: RequestInit = {
704
- method,
705
- headers,
706
- body,
707
- }
708
-
709
- // 实现超时控制
710
- const controller = new AbortController()
711
- const timeoutId = setTimeout(() => controller.abort(), timeout)
712
-
713
- const initOptions = {
714
- ...fetchOptions,
715
- signal: controller.signal,
716
- }
717
- const response = this.fetchFn
718
- ? await this.fetchFn(`${this.BASE_URL}${url}`, initOptions)
719
- : await fetch(`${this.BASE_URL}${url}`, initOptions)
720
- clearTimeout(timeoutId)
721
-
722
- // 获取响应头的 content-type
723
- const contentType = response.headers.get("content-type") || ""
724
- let raw_res: any
725
-
726
- // 根据 content-type 处理响应数据
727
- if (contentType.includes("text/")) {
728
- const textData = await response.text()
729
- raw_res = {
730
- code: RespCode.ERROR,
731
- data: textData,
732
- }
733
- } else {
734
- raw_res = await response.json()
735
- }
736
-
737
- if (typeof raw_res === "object" && raw_res !== null) {
738
- try {
739
- if (typeof raw_res.data === "string") {
740
- raw_res.data = JSON.parse(raw_res.data)
741
- }
742
- } catch (e) {
743
- // 忽略解析错误
744
- }
745
- }
746
-
747
- return new DNAApiResponse<T>(raw_res)
748
- } catch (e) {
749
- console.error(`请求失败: ${(e as Error).message}`)
750
- if (attempt < max_retries - 1) {
751
- await new Promise((resolve) => setTimeout(resolve, retry_delay * Math.pow(2, attempt)))
752
- }
753
- }
754
- }
755
-
756
- return DNAApiResponse.err("请求服务器失败,已达最大重试次数")
757
- }
758
- }
759
-
760
- enum DNAInstanceMHType {
761
- "角色" = "role",
762
- "武器" = "weapon",
763
- "魔之楔" = "mzx",
764
- "role" = "角色",
765
- "weapon" = "武器",
766
- "mzx" = "魔之楔",
767
- }
768
-
769
- export function getDNAInstanceMHType(key: keyof typeof DNAInstanceMHType) {
770
- return DNAInstanceMHType[key]
771
- }
772
-
773
- //#region 接口定义
774
-
775
- export interface DNAMineRes {
776
- mine: DNAMine
777
- postList: DNAPost[]
778
- hasNext: number
779
- }
780
-
781
- export interface DNAMine {
782
- /** 文章数量 */
783
- articleCount: number
784
- /** 收藏数量 */
785
- collectCount: number
786
- /** 评论数量 */
787
- commentCount: number
788
- /** 粉丝数量 */
789
- fansCount: number
790
- /** 新粉丝数量 */
791
- fansNewCount: number
792
- /** 关注数量 */
793
- followCount: number
794
- /** 性别 */
795
- gender: number
796
- /** 精华数量 */
797
- goldNum: number
798
- /** 头像 */
799
- headUrl: string
800
- /** 是否关注 */
801
- isFollow: number
802
- /** 是否登录用户 */
803
- isLoginUser: number
804
- /** 是否被禁言 */
805
- isMute: number
806
- /** 等级 */
807
- levelTotal: number
808
- /** 点赞数量 */
809
- likeCount: number
810
- /** 手机号 */
811
- mobile: string
812
- /** 管理员列表 */
813
- moderatorVos: any[]
814
- /** 帖子数量 */
815
- postCount: number
816
- /** 注册时间 */
817
- registerTime: string
818
- /** 状态 */
819
- status: number
820
- /** 趋势数量 */
821
- trendCount: number
822
- /** 用户ID */
823
- userId: string
824
- /** 用户名 */
825
- userName: string
826
- }
827
-
828
- export interface DNAGameConfigRes {
829
- /** 游戏所有板块列表 */
830
- gameAllForumList: GameForum[]
831
- /** 游戏ID */
832
- gameId: number
833
- /** 游戏板块图片列表 */
834
- gameForumPictureList: any[]
835
- /** 游戏板块列表 */
836
- gameForumList: GameForum[]
837
- /** 签到按钮图片 */
838
- signBtn: string
839
- sort: number
840
- /** 话题列表 */
841
- topicList: GameTopic[]
842
- /** 背景图片 */
843
- drawBackgroundUrl: string
844
- /** 是否默认游戏 */
845
- gameDefault: number
846
- /** 签到颜色 */
847
- signColor: string
848
- /** 游戏名称 */
849
- gameName: string
850
- /** CM图片2 意味不明 */
851
- drawListUrl: string
852
- /** 英文站点 */
853
- configTab: ConfigTab[]
854
- }
855
-
856
- export interface ConfigTab {
857
- name: string
858
- url: string
859
- }
860
-
861
- export interface GameTopic {
862
- /** 话题背景图片 */
863
- backgroundUrl: string
864
- gameId: number
865
- /** 话题图标 */
866
- topicIconUrl: string
867
- topicId: number
868
- /** 话题名称 */
869
- topicName: string
870
- sort: number
871
- /** 话题描述 */
872
- topicDesc: string
873
- }
874
-
875
- export interface GameForum {
876
- /** 全部=1 普通=3 */
877
- forumDataType: number
878
- /** 固定1 */
879
- forumUiType: number
880
- /** 小红点 */
881
- isTrend: number
882
- /** 夜间模式图标 */
883
- iconWhiteUrl: string
884
- /** 固定1 */
885
- forumType: number
886
- /** 板块名称 */
887
- name: string
888
- forumListShowType: number
889
- /** 图标 */
890
- iconUrl: string
891
- id: number
892
- sort: number
893
- /** 官方 */
894
- isOfficial: number
895
- }
896
-
897
- export interface UserGame {
898
- gameId: number // gameId
899
- gameName: string // gameName
900
- }
901
-
902
- export interface DNALoginRes {
903
- applyCancel?: number // applyCancel
904
- gender?: number // gender
905
- signature?: string // signature
906
- headUrl: string // headUrl
907
- userName: string // userName
908
- userId: string // userId
909
- isOfficial: number // isOfficial
910
- token: string // token
911
- userGameList: UserGame[] // userGameList
912
- isRegister: number // isRegister
913
- status: number // status
914
- /** 是否完成绑定 0: 未绑定, 1: 已绑定 */
915
- isComplete: number
916
- refreshToken: string // refreshToken
917
- }
918
-
919
- export interface DNARoleShowVo {
920
- roleId: string // roleId
921
- headUrl?: string // headUrl
922
- level?: number // level
923
- roleName?: string // roleName
924
- isDefault?: number // isDefault
925
- roleRegisterTime?: string // roleRegisterTime
926
- boundType?: number // boundType
927
- roleBoundId: string // roleBoundId
928
- }
929
-
930
- export interface DNARole {
931
- gameName: string // gameName
932
- showVoList: DNARoleShowVo[] // showVoList
933
- gameId: number // gameId
934
- }
935
-
936
- export interface DNARoleListRes {
937
- roles: DNARole[] // roles
938
- }
939
-
940
- /** 密函 */
941
- export interface DNARoleForToolInstance {
942
- id: number
943
- /** 中文 */
944
- name: string
945
- /** 密函编码 */
946
- code: string
947
- /** 固定0 */
948
- on: number
949
- }
950
-
951
- export interface DNARoleForToolInstanceInfo {
952
- /** 密函列表 */
953
- instances: DNARoleForToolInstance[] // instances
954
- }
955
-
956
- export interface DraftDoingInfo {
957
- draftCompleteNum: number // draftCompleteNum
958
- draftDoingNum: number // draftDoingNum
959
- /** 结束时间 */
960
- endTime: string
961
- /** 产品id */
962
- productId?: number
963
- /** 产品名称 */
964
- productName: string
965
- /** 开始时间 */
966
- startTime: string
967
- }
968
-
969
- export interface DraftInfo {
970
- /** 正在做的锻造 */
971
- draftDoingInfo?: DraftDoingInfo[]
972
- /** 正在做的锻造数量 */
973
- draftDoingNum: number
974
- /** 最大锻造数量 */
975
- draftMaxNum: number
976
- }
977
-
978
- export interface DNARoleShortNoteRes {
979
- /** 迷津进度 */
980
- rougeLikeRewardCount: number
981
- /** 迷津总数 */
982
- rougeLikeRewardTotal: number
983
- /** 备忘手记进度 */
984
- currentTaskProgress: number
985
- /** 备忘手记总数 */
986
- maxDailyTaskProgress: number
987
- /** 梦魇进度 */
988
- hardBossRewardCount: number
989
- /** 梦魇总数 */
990
- hardBossRewardTotal: number
991
- /** 锻造信息 */
992
- draftInfo: DraftInfo
993
- }
994
-
995
- export interface DNARoleWeapon {
996
- weaponId: number
997
- weaponEid: string
998
- /** 武器类型图标 */
999
- elementIcon: string
1000
- /** 武器图标 */
1001
- icon: string
1002
- /** 武器等级 */
1003
- level: number
1004
- /** 武器名称 */
1005
- name: string
1006
- /** 精炼等级 */
1007
- skillLevel: number
1008
- /** 是否解锁 */
1009
- unLocked: boolean
1010
- }
1011
-
1012
- export interface DNARoleChar {
1013
- charId: number
1014
- charEid: string
1015
- /** 元素图标 */
1016
- elementIcon: string
1017
- /** 命座等级 */
1018
- gradeLevel: number
1019
- /** 角色图标 */
1020
- icon: string
1021
- /** 角色等级 */
1022
- level: number
1023
- /** 角色名称 */
1024
- name: string
1025
- /** 是否解锁 */
1026
- unLocked: boolean
1027
- }
1028
-
1029
- export interface DNARoleForToolRes {
1030
- /** 角色信息 */
1031
- roleInfo: DNARoleInfo
1032
- /** 密函 */
1033
- instanceInfo: DNARoleForToolInstanceInfo[]
1034
- }
1035
-
1036
- export interface DNARoleShow {
1037
- /** 角色列表 */
1038
- roleChars: DNARoleChar[]
1039
- /** 武器列表 */
1040
- langRangeWeapons: DNARoleWeapon[]
1041
- /** 武器列表 */
1042
- closeWeapons: DNARoleWeapon[]
1043
- /** 角色头像 */
1044
- headUrl: string
1045
- /** 等级 */
1046
- level: number
1047
- /** 成就列表 */
1048
- params: DNARoleAchievement[]
1049
- /** 角色id */
1050
- roleId: string
1051
- /** 角色名称 */
1052
- roleName: string
1053
- /** 迷津 */
1054
- rougeLikeInfo: DNARougeLikeInfo
1055
- }
1056
-
1057
- export interface DNARoleAchievement {
1058
- paramKey: string // paramKey
1059
- paramValue: string // paramValue
1060
- }
1061
-
1062
- export interface DNARoleInfo {
1063
- /** 深渊信息 */
1064
- abyssInfo: DNAAbyssInfo
1065
- /** 角色信息 */
1066
- roleShow: DNARoleShow
1067
- }
1068
-
1069
- export interface DNARougeLikeInfo {
1070
- /** 最大通过等级 */
1071
- maxPassed: number
1072
- /** 最大通过等级名称 */
1073
- maxPassedName: string
1074
- /** 重置时间 */
1075
- resetTime: string
1076
- /** 奖励数量 */
1077
- rewardCount: number
1078
- /** 奖励总数 */
1079
- rewardTotal: number
1080
- /** 天赋信息 */
1081
- talentInfo: DNARougeLikeTalentInfo[]
1082
- }
1083
-
1084
- export interface DNARougeLikeTalentInfo {
1085
- cur: string
1086
- max: string
1087
- }
1088
-
1089
- export interface DNAAbyssInfo {
1090
- /** 阵容 */
1091
- bestTimeVo1: DNABestTimeVo1
1092
- /** 结束时间 */
1093
- endTime: string
1094
- /** 操作名称 */
1095
- operaName: string
1096
- /** 进度名称 */
1097
- progressName: string
1098
- /** 星级 */
1099
- stars: string
1100
- /** 开始时间 */
1101
- startTime: string
1102
- }
1103
-
1104
- /** 深渊阵容 */
1105
- export interface DNABestTimeVo1 {
1106
- /** 角色图标 */
1107
- charIcon: string
1108
- /** 近战武器图标 */
1109
- closeWeaponIcon: string
1110
- /** 远程武器图标 */
1111
- langRangeWeaponIcon: string
1112
- /** 魔灵图标 */
1113
- petIcon: string
1114
- /** 协战角色图标1 */
1115
- phantomCharIcon1: string
1116
- /** 协战武器图标1 */
1117
- phantomWeaponIcon1: string
1118
- /** 协战角色图标2 */
1119
- phantomCharIcon2: string
1120
- /** 协战武器图标2 */
1121
- phantomWeaponIcon2: string
1122
- }
1123
-
1124
- /** 角色属性 */
1125
- export interface DNACharAttribute {
1126
- /** 技能范围 */
1127
- skillRange: string
1128
- /** 强化值 */
1129
- strongValue: string
1130
- /** 技能威力 */
1131
- skillIntensity: string
1132
- /** 武器精通 */
1133
- weaponTags: string[]
1134
- /** 防御 */
1135
- def: number
1136
- /** 仇恨值 */
1137
- enmityValue: string
1138
- /** 技能效益 */
1139
- skillEfficiency: string
1140
- /** 技能耐久 */
1141
- skillSustain: string
1142
- /** 最大生命值 */
1143
- maxHp: number
1144
- /** 攻击 */
1145
- atk: number
1146
- /** 最大护盾 */
1147
- maxES: number
1148
- /** 最大神志 */
1149
- maxSp: number
1150
- }
1151
-
1152
- /** 角色技能 */
1153
- export interface DNARoleSkill {
1154
- /** 技能id */
1155
- skillId: number
1156
- /** 技能图标 */
1157
- icon: string
1158
- /** 技能等级 */
1159
- level: number
1160
- /** 技能名称 */
1161
- skillName: string
1162
- }
1163
-
1164
- /** 溯源 */
1165
- export interface DNARoleTrace {
1166
- /** 溯源图标 */
1167
- icon: string
1168
- /** 溯源描述 */
1169
- description: string
1170
- }
1171
-
1172
- /** 魔之楔 */
1173
- export interface DNARoleMod {
1174
- /** id 没佩戴为-1 */
1175
- id: number
1176
- /** 图标 */
1177
- icon?: string
1178
- /** 质量 */
1179
- quality?: number
1180
- /** 名称 */
1181
- name?: string
1182
- }
1183
-
1184
- export interface DNACharDetail {
1185
- charId: number
1186
- /** 角色属性 */
1187
- attribute: DNACharAttribute
1188
- /** 角色技能 */
1189
- skills: DNARoleSkill[]
1190
- /** 立绘 */
1191
- paint: string
1192
- /** 角色名称 */
1193
- charName: string
1194
- /** 元素图标 */
1195
- elementIcon: string
1196
- /** 溯源 */
1197
- traces: DNARoleTrace[]
1198
- /** 当前魔之楔 */
1199
- currentVolume: number
1200
- /** 最大魔之楔 */
1201
- sumVolume: number
1202
- /** 角色等级 */
1203
- level: number
1204
- /** 角色头像 */
1205
- icon: string
1206
- /** 溯源等级 0-6 */
1207
- gradeLevel: number
1208
- /** 元素名称 */
1209
- elementName: string
1210
- /** 魔之楔列表 */
1211
- modes: DNARoleMod[]
1212
- }
1213
-
1214
- export interface DNACharDetailRes {
1215
- /** 角色详情 */
1216
- charDetail: DNACharDetail
1217
- }
1218
-
1219
- export interface DNAWeaponDetailRes {
1220
- /** 武器详情 */
1221
- weaponDetail: DNAWeaponDetail
1222
- }
1223
-
1224
- export interface DNAWeaponDetail {
1225
- attribute: DNAWeaponAttribute
1226
- currentVolume: number
1227
- description: string
1228
- elementIcon: string
1229
- elementName: string
1230
- icon: string
1231
- id: number
1232
- level: number
1233
- modes: DNARoleMod[]
1234
- name: string
1235
- skillLevel: number
1236
- sumVolume: number
1237
- }
1238
-
1239
- export interface DNAWeaponAttribute {
1240
- atk: number
1241
- crd: number
1242
- cri: number
1243
- speed: number
1244
- trigger: number
1245
- }
1246
-
1247
- export interface DNADayAward {
1248
- gameId: number // gameId
1249
- periodId: number // periodId
1250
- iconUrl: string // iconUrl
1251
- id: number // id
1252
- dayInPeriod: number // dayInPeriod
1253
- updateTime: number // updateTime
1254
- awardNum: number // awardNum
1255
- thirdProductId: string // thirdProductId
1256
- createTime: number // createTime
1257
- awardName: string // awardName
1258
- }
1259
-
1260
- export interface DNACaSignPeriod {
1261
- gameId: number // gameId
1262
- retryCos: number // retryCos
1263
- endDate: number // endDate
1264
- id: number // id
1265
- startDate: number // startDate
1266
- retryTimes: number // retryTimes
1267
- overDays: number // overDays
1268
- createTime: number // createTime
1269
- name: string // name
1270
- }
1271
-
1272
- export interface DNACaSignRoleInfo {
1273
- headUrl: string // headUrl
1274
- roleId: string // roleId
1275
- roleName: string // roleName
1276
- level: number // level
1277
- roleBoundId: string // roleBoundId
1278
- }
1279
-
1280
- export interface DNAHaveSignInRes {
1281
- /** 已签到天数 */
1282
- totalSignInDay: number
1283
- }
1284
-
1285
- export interface DNACalendarSignRes {
1286
- todaySignin: boolean // todaySignin
1287
- userGoldNum: number // userGoldNum
1288
- dayAward: DNADayAward[] // dayAward
1289
- signinTime: number // signinTime
1290
- period: DNACaSignPeriod // period
1291
- roleInfo: DNACaSignRoleInfo // roleInfo
1292
- }
1293
-
1294
- export interface DNABBSTask {
1295
- /** 备注 */
1296
- remark: string
1297
- /** 完成次数 */
1298
- completeTimes: number
1299
- /** 需要次数 */
1300
- times: number
1301
- /** skipType */
1302
- skipType: number
1303
- /** 获取经验 */
1304
- gainExp: number
1305
- /** 进度 */
1306
- process: number
1307
- /** 获取金币 */
1308
- gainGold: number
1309
- /** 任务标识名 */
1310
- markName?: string
1311
- }
1312
-
1313
- export interface DNATaskProcessRes {
1314
- dailyTask: DNABBSTask[] // dailyTask
1315
- }
1316
-
1317
- export interface DNATopicPostListRes {
1318
- postList: DNAPost[]
1319
- topic: DNATopicDetail
1320
- hasNext: number
1321
- }
1322
-
1323
- export interface DNATopicDetail {
1324
- backgroundUrl: string
1325
- gameId: number
1326
- sort: number
1327
- topicDesc: string
1328
- topicIconUrl: string
1329
- topicId: number
1330
- topicName: string
1331
- }
1332
-
1333
- export interface DNAPost {
1334
- browseCount: string
1335
- collectionCount: number
1336
- commentCount: number
1337
- gameForumId: number
1338
- gameId: number
1339
- gameName: string
1340
- imgContent: DNAPostImgContent[]
1341
- imgCount: number
1342
- isCollect: number
1343
- isCreator: number
1344
- isElite: number
1345
- isFollow: number
1346
- isLike: number
1347
- isLock: number
1348
- isOfficial: number
1349
- isPublisher: number
1350
- likeCount: number
1351
- postContent: string
1352
- postCover: string
1353
- postCoverVo: DNAPostCoverVo
1354
- postId: string
1355
- postStatus: number
1356
- postTitle: string
1357
- postType: number
1358
- showTime: string
1359
- topics: DNATopicShort[]
1360
- userHeadUrl: string
1361
- userId: string
1362
- userLevel: number
1363
- userModeratorIdentity: number
1364
- userName: string
1365
- }
1366
-
1367
- export interface DNATopicShort {
1368
- topicId: number
1369
- topicName: string
1370
- }
1371
-
1372
- export interface DNAPostCoverVo {
1373
- imgHeight: number
1374
- imgWidth: number
1375
- }
1376
-
1377
- export interface DNAPostImgContent {
1378
- imgHeight: number
1379
- imgWidth: number
1380
- url: string
1381
- isAbnormal?: boolean
1382
- }
1383
-
1384
- export interface DNAPostListRes {
1385
- postList: DNAPost[] // posts
1386
- topList: any[]
1387
- hasNext: number
1388
- }
1389
-
1390
- export interface DNAPostDetailRes {
1391
- isHotCount: boolean
1392
- gameId: number
1393
- isFollow: number
1394
- isLike: number
1395
- postDetail: DNAPostDetail
1396
- isCollect: number
1397
- hasNext: number
1398
- comment: DNAComment[]
1399
- }
1400
-
1401
- export interface DNAPostDetail {
1402
- auditStatus: number
1403
- browseCount: string
1404
- checkStatus: number
1405
- collectionCount: number
1406
- commentCount: number
1407
- gameForumId: number
1408
- gameForumVo: DNAGameForumVo
1409
- gameId: number
1410
- gameName: string
1411
- headCodeUrl: string
1412
- id: string
1413
- isCreator: number
1414
- isElite: number
1415
- isForceRecommend: number
1416
- isHide: number
1417
- isLock: number
1418
- isMine: number
1419
- isOfficial: number
1420
- isRecommend: number
1421
- isTop: number
1422
- lastEditorTime?: string
1423
- likeCount: number
1424
- postContent: DNAPostContent[]
1425
- postH5Content: string
1426
- postTime: string
1427
- postTitle: string
1428
- postType: number
1429
- postUserId: string
1430
- refreshHour: number
1431
- score: number
1432
- topics: DNATopicShort[]
1433
- userHeadCode: string
1434
- userLevel: number
1435
- userModeratorIdentity: number
1436
- userName: string
1437
- whiteUrl: any[]
1438
- }
1439
-
1440
- export interface DNAPostContent {
1441
- contentType: PostContentType
1442
- imgHeight: number
1443
- imgWidth: number
1444
- url?: string
1445
- content?: string
1446
- contentVideo?: DNAPostContentVideo
1447
- }
1448
-
1449
- export interface DNAComment {
1450
- checkStatus: number
1451
- commentContent: DNAPostContent[]
1452
- commentId: string
1453
- commentTime: string
1454
- contentTextStatus: number
1455
- floor: number
1456
- isCreator: number
1457
- isLike: number
1458
- isMine: number
1459
- isOfficial: number
1460
- isPublisher: number
1461
- likeCount: number
1462
- replyCount: number
1463
- replyVos: DNAReplyVos[]
1464
- userHeadCode?: string
1465
- userHeadUrl: string
1466
- userId: string
1467
- userLevel: number
1468
- userModeratorIdentity: number
1469
- userName: string
1470
- }
1471
-
1472
- export interface DNAReplyVos {
1473
- checkStatus: number
1474
- contentTextStatus: number
1475
- createTime: number
1476
- isCreator: number
1477
- isLike: number
1478
- isMine: number
1479
- isOfficial: number
1480
- isPublisher: number
1481
- likeCount: number
1482
- postCommentId: string
1483
- postCommentReplayId: string
1484
- replyContent: DNAPostContent[]
1485
- replyContentStr: string
1486
- replyId: string
1487
- replyTime: string
1488
- toUserIsCreator: number
1489
- toUserModeratorIdentity: number
1490
- toUserName: string
1491
- userHeadCode?: string
1492
- userHeadUrl: string
1493
- userId: string
1494
- userLevel: number
1495
- userModeratorIdentity: number
1496
- userName: string
1497
- }
1498
-
1499
- export interface DNAGameForumVo {
1500
- forumDataType: number
1501
- forumListShowType: number
1502
- forumType: number
1503
- forumUiType: number
1504
- iconUrl: string
1505
- iconWhiteUrl: string
1506
- id: number
1507
- isOfficial: number
1508
- isTrend: number
1509
- name: string
1510
- sort: number
1511
- }
1512
-
1513
- export enum PostContentType {
1514
- TEXT = 1,
1515
- IMAGE = 2,
1516
- VIDEO = 5,
1517
- }
1518
-
1519
- export interface DNAPostContentVideo {
1520
- videoUrl: string // videoUrl
1521
- coverUrl?: string // coverUrl
1522
- }
1523
-
1524
- class DNAApiResponse<T = any> {
1525
- code: number = 0
1526
- msg: string = ""
1527
- success: boolean = false
1528
- data?: T
1529
-
1530
- constructor(raw_data: any) {
1531
- this.code = raw_data.code || 0
1532
- this.msg = raw_data.msg || ""
1533
- this.success = raw_data.success || false
1534
- this.data = raw_data.data
1535
- }
1536
-
1537
- // 判断是否成功
1538
- get is_success() {
1539
- return this.success && [RespCode.OK_ZERO, RespCode.OK_HTTP].includes(this.code)
1540
- }
1541
-
1542
- // 错误响应静态方法
1543
- static err<T = undefined>(msg: string, code: number = RespCode.ERROR): DNAApiResponse<T> {
1544
- return new DNAApiResponse<T>({ code, msg, data: undefined, success: false })
1545
- }
1546
- }
1547
- //#endregion
1548
-
1549
- //#region utils
1550
-
1551
- // RSA加密函数
1552
- function rsa_encrypt(text: string, public_key_b64: string): string {
1553
- try {
1554
- // 将base64公钥转换为PEM格式
1555
- const lines: string[] = []
1556
- for (let i = 0; i < public_key_b64.length; i += 64) {
1557
- lines.push(public_key_b64.slice(i, i + 64))
1558
- }
1559
- const pem = `-----BEGIN PUBLIC KEY-----\n${lines.join("\n")}\n-----END PUBLIC KEY-----`
1560
-
1561
- // 导入PEM格式的RSA公钥
1562
- const publicKey = forge.pki.publicKeyFromPem(pem)
1563
-
1564
- // 执行PKCS1_v1_5加密
1565
- const textBytes = forge.util.encodeUtf8(text)
1566
- const encrypted = publicKey.encrypt(textBytes)
1567
-
1568
- return forge.util.encode64(encrypted)
1569
- } catch (e) {
1570
- throw new Error(`[DNA] RSA 加密失败: ${(e as Error).message}`)
1571
- }
1572
- }
1573
-
1574
- // 生成随机字符串
1575
- function rand_str(length: number = 16): string {
1576
- const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
1577
- let result = ""
1578
- for (let i = 0; i < length; i++) {
1579
- result += chars.charAt(Math.floor(Math.random() * chars.length))
1580
- }
1581
- return result
1582
- }
1583
-
1584
- // MD5加密并转换为大写
1585
- function md5_upper(text: string): string {
1586
- const md = forge.md.md5.create()
1587
- md.update(text)
1588
- return md.digest().toHex().toUpperCase()
1589
- }
1590
-
1591
- // 签名哈希函数
1592
- function signature_hash(text: string): string {
1593
- function swap_positions(text: string, positions: number[]): string {
1594
- const chars = text.split("")
1595
- for (let i = 1; i < positions.length; i += 2) {
1596
- const p1 = positions[i - 1]
1597
- const p2 = positions[i]
1598
- if (p1 >= 0 && p1 < chars.length && p2 >= 0 && p2 < chars.length) {
1599
- ;[chars[p1], chars[p2]] = [chars[p2], chars[p1]]
1600
- }
1601
- }
1602
- return chars.join("")
1603
- }
1604
- return swap_positions(md5_upper(text), [1, 13, 5, 17, 7, 23])
1605
- }
1606
-
1607
- // 签名函数
1608
- function sign_fI(data: Record<string, any>, secret: string): string {
1609
- const pairs: string[] = []
1610
- const sortedKeys = Object.keys(data).sort()
1611
- for (const k of sortedKeys) {
1612
- const v = data[k]
1613
- if (v !== null && v !== undefined && v !== "") {
1614
- pairs.push(`${k}=${v}`)
1615
- }
1616
- }
1617
- const qs = pairs.join("&")
1618
- return signature_hash(`${qs}&${secret}`)
1619
- }
1620
-
1621
- // XOR编码函数
1622
- function xor_encode(text: string, key: string): string {
1623
- const encoder = new TextEncoder()
1624
- const tb = encoder.encode(text)
1625
- const kb = encoder.encode(key)
1626
- const out: string[] = []
1627
- for (let i = 0; i < tb.length; i++) {
1628
- const b = tb[i]
1629
- const e = (b & 255) + (kb[i % kb.length] & 255)
1630
- out.push(`@${e}`)
1631
- }
1632
- return out.join("")
1633
- }
1634
-
1635
- // 构建签名
1636
- function build_signature(data: Record<string, any>, token?: string): Record<string, any> {
1637
- const ts = Date.now()
1638
- const sign_data = { ...data, timestamp: ts, token }
1639
- const sec = rand_str(16)
1640
- const sig = sign_fI(sign_data, sec)
1641
- const enc = xor_encode(sig, sec)
1642
- return { s: enc, t: ts, k: sec }
1643
- }
1644
- //#endregion
1
+ import * as forge from "node-forge"
2
+ //#region const
3
+ enum RespCode {
4
+ ERROR = -999,
5
+ OK_ZERO = 0,
6
+ OK_HTTP = 200,
7
+ BAD_REQUEST = 400,
8
+ SERVER_ERROR = 500,
9
+ }
10
+
11
+ const DNA_GAME_ID = 268
12
+ //#endregion
13
+
14
+ /**
15
+ * DNA API类,用于与DNA游戏服务器交互
16
+ */
17
+ export class DNAAPI {
18
+ public fetchFn?: typeof fetch
19
+ public is_h5 = false
20
+ public RSA_PUBLIC_KEY =
21
+ "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDGpdbezK+eknQZQzPOjp8mr/dP+QHwk8CRkQh6C6qFnfLH3tiyl0pnt3dePuFDnM1PUXGhCkQ157ePJCQgkDU2+mimDmXh0oLFn9zuWSp+U8uLSLX3t3PpJ8TmNCROfUDWvzdbnShqg7JfDmnrOJz49qd234W84nrfTHbzdqeigQIDAQAB"
22
+ public BASE_URL = "https://dnabbs-api.yingxiong.com/"
23
+ private uploadKey: string = ""
24
+
25
+ /**
26
+ * 构造函数
27
+ * @param dev_code 设备码
28
+ * @param uid 用户ID
29
+ * @param token 访问令牌
30
+ * @param options 选项
31
+ * @param options.fetchFn 自定义fetch函数
32
+ * @param options.is_h5 是否为H5端
33
+ * @param options.rsa_public_key RSA公钥(base64) 设为空字符串从服务器获取
34
+ */
35
+ constructor(
36
+ public dev_code: string,
37
+ public token = "",
38
+ options: { fetchFn?: typeof fetch; is_h5?: boolean; rsa_public_key?: string } = {},
39
+ ) {
40
+ this.fetchFn = options.fetchFn
41
+ if (options.is_h5 !== undefined) this.is_h5 = options.is_h5
42
+ if (options.rsa_public_key !== undefined) this.RSA_PUBLIC_KEY = options.rsa_public_key
43
+ }
44
+
45
+ /**
46
+ * 获取RSA公钥
47
+ * @returns RSA公钥(base64)
48
+ */
49
+ async getRsaPublicKey() {
50
+ if (this.RSA_PUBLIC_KEY) {
51
+ return this.RSA_PUBLIC_KEY
52
+ }
53
+ const res = await this._dna_request<{ key: string }>("config/getRsaPublicKey")
54
+
55
+ if (res.is_success && res.data) {
56
+ const key = res.data.key
57
+ if (typeof key === "string") {
58
+ this.RSA_PUBLIC_KEY = key
59
+ }
60
+ }
61
+ return this.RSA_PUBLIC_KEY
62
+ }
63
+
64
+ /**
65
+ * 登录
66
+ */
67
+ async login(mobile: string, code: string) {
68
+ const data = { code: code, devCode: this.dev_code, gameList: DNA_GAME_ID, loginType: 1, mobile: mobile }
69
+ const res = await this._dna_request<DNALoginRes>("user/sdkLogin", data, { sign: true })
70
+ if (res.is_success && res.data) {
71
+ const data = res.data
72
+ if (typeof data.token === "string") {
73
+ this.token = data.token
74
+ }
75
+ }
76
+ return res
77
+ }
78
+
79
+ /**
80
+ * 获取短信验证码
81
+ */
82
+ async getSmsCode(mobile: string, vJson: string) {
83
+ const data = { mobile, isCaptcha: 1, vJson }
84
+ return await this._dna_request("user/getSmsCode", data)
85
+ }
86
+
87
+ /**
88
+ * 上传头像
89
+ */
90
+ async uploadHead(file: File) {
91
+ const data = new FormData()
92
+ data.append("parts", file)
93
+ const res = await this._dna_request<string[]>("user/img/uploadHead", data, { sign: true })
94
+ if (res.is_success && res.data) {
95
+ res.data = res.data.map((url) => aesDecryptImageUrl(url, this.uploadKey))
96
+ }
97
+ return res
98
+ }
99
+
100
+ /**
101
+ * 图片上传
102
+ */
103
+ async uploadImage(file: File) {
104
+ const data = new FormData()
105
+ data.append("files", file)
106
+ data.append("type", "post")
107
+ const res = await this._dna_request<string[]>("config/img/upload", data, { sign: true })
108
+ if (res.is_success && res.data) {
109
+ res.data = res.data.map((url) => aesDecryptImageUrl(url, this.uploadKey))
110
+ }
111
+ return res
112
+ }
113
+
114
+ /**
115
+ * 获取登录日志
116
+ */
117
+ async loginLog() {
118
+ return await this._dna_request<DNALoginRes>("user/login/log")
119
+ }
120
+
121
+ /**
122
+ * 获取角色列表
123
+ */
124
+ async getRoleList() {
125
+ return await this._dna_request<DNARoleListRes>("role/list")
126
+ }
127
+
128
+ /**
129
+ * 获取默认角色
130
+ */
131
+ async getDefaultRoleForTool() {
132
+ const data = { type: 1 }
133
+ return await this._dna_request<DNARoleForToolRes>("role/defaultRoleForTool", data, { sign: true, token: true, tokenSig: true })
134
+ }
135
+
136
+ /**
137
+ * 获取角色详情
138
+ */
139
+ async getCharDetail(char_id: string, char_eid: string, otherUserId?: string) {
140
+ const data = { charId: char_id, charEid: char_eid, type: 1, otherUserId } as any
141
+ return await this._dna_request<DNACharDetailRes>("role/getCharDetail", data)
142
+ }
143
+
144
+ /**
145
+ * 获取武器详情
146
+ */
147
+ async getWeaponDetail(weapon_id: string, weapon_eid: string, otherUserId?: string) {
148
+ const data = { weaponId: weapon_id, weaponEid: weapon_eid, type: 1, otherUserId }
149
+ return await this._dna_request<DNAWeaponDetailRes>("role/getWeaponDetail", data)
150
+ }
151
+
152
+ /**
153
+ * 获取角色简讯
154
+ */
155
+ async getShortNoteInfo() {
156
+ return await this._dna_request<DNARoleShortNoteRes>("role/getShortNoteInfo")
157
+ }
158
+
159
+ /**
160
+ * 检查是否签到
161
+ */
162
+ async haveSignIn() {
163
+ const data = { gameId: DNA_GAME_ID }
164
+ return await this._dna_request<DNAHaveSignInRes>("user/haveSignInNew", data)
165
+ }
166
+
167
+ /**
168
+ * 签到日历
169
+ */
170
+ async signCalendar() {
171
+ const data = { gameId: DNA_GAME_ID }
172
+ return await this._dna_request<DNACalendarSignRes>("encourage/signin/show", data)
173
+ }
174
+
175
+ /** ? */
176
+ async soulTask() {
177
+ return await this._dna_request("role/soul/task")
178
+ }
179
+
180
+ /**
181
+ * 游戏签到
182
+ */
183
+ async gameSign(day_award_id: number, period: number) {
184
+ const data = {
185
+ dayAwardId: day_award_id,
186
+ periodId: period,
187
+ signinType: 1,
188
+ }
189
+ return await this._dna_request("encourage/signin/signin", data)
190
+ }
191
+
192
+ /**
193
+ * 皎皎角签到
194
+ */
195
+ async bbsSign() {
196
+ const data = { gameId: DNA_GAME_ID }
197
+ return await this._dna_request("user/signIn", data)
198
+ }
199
+
200
+ /**
201
+ * 获取任务进度
202
+ */
203
+ async getTaskProcess() {
204
+ const data = { gameId: DNA_GAME_ID }
205
+ return await this._dna_request<DNATaskProcessRes>("encourage/level/getTaskProcess", data)
206
+ }
207
+
208
+ /**
209
+ * 获取帖子列表
210
+ * @param forumId 论坛ID
211
+ * @param pageIndex 页码
212
+ * @param pageSize 每页数量
213
+ * @param searchType 搜索类型 1:最新 2:热门
214
+ * @param timeType 时间类型 0:全部 1:今日 2:本周 3:本月
215
+ * @returns 帖子列表
216
+ */
217
+ async getPostList(forumId: number = 48, pageIndex: number = 1, pageSize: number = 20, searchType: number = 1, timeType: number = 0) {
218
+ const data = {
219
+ forumId: forumId,
220
+ gameId: DNA_GAME_ID,
221
+ pageIndex: pageIndex,
222
+ pageSize: pageSize,
223
+ searchType: searchType, // 1:最新 2:热门
224
+ timeType: timeType, // 0:全部 1:今日 2:本周 3:本月
225
+ }
226
+ return await this._dna_request<DNAPostListRes>("forum/list", data)
227
+ }
228
+
229
+ /** 管理员锁定帖子 */
230
+ async lockPost(post: { postId: number; gameId?: number; gameForumId: number; operateType: number }) {
231
+ const data = {
232
+ postId: post.postId,
233
+ gameId: post.gameId ?? DNA_GAME_ID,
234
+ gameForumId: post.gameForumId,
235
+ operateType: post.operateType,
236
+ }
237
+ return await this._dna_request("forum/moderator/postLock", data)
238
+ }
239
+
240
+ /** 管理员移动帖子 */
241
+ async postDownOrUp(post: { postId: number; gameId?: number; gameForumId: number; operateType: number }) {
242
+ const data = {
243
+ postId: post.postId,
244
+ gameId: post.gameId ?? DNA_GAME_ID,
245
+ gameForumId: post.gameForumId,
246
+ operateType: post.operateType,
247
+ }
248
+ return await this._dna_request("forum/moderator/postDownOrUp", data)
249
+ }
250
+
251
+ /** 管理员设精 */
252
+ async postElite(post: { postId: number; gameId?: number; gameForumId: number; operateType: number }) {
253
+ const data = {
254
+ postId: post.postId,
255
+ gameId: post.gameId ?? DNA_GAME_ID,
256
+ gameForumId: post.gameForumId,
257
+ operateType: post.operateType,
258
+ }
259
+ return await this._dna_request("forum/moderator/postElite", data)
260
+ }
261
+
262
+ /** 管理员隐藏帖子 */
263
+ async postHide(post: { postId: number; gameId?: number; gameForumId: number; operateType: number }) {
264
+ const data = {
265
+ postId: post.postId,
266
+ gameId: post.gameId ?? DNA_GAME_ID,
267
+ gameForumId: post.gameForumId,
268
+ operateType: post.operateType,
269
+ }
270
+ return await this._dna_request("forum/moderator/postHide", data)
271
+ }
272
+
273
+ /** 管理员设置权重 */
274
+ async reRank(post: { postId: number; gameId?: number; gameForumId: number }, weight: number) {
275
+ const data = {
276
+ postId: post.postId,
277
+ gameId: post.gameId ?? DNA_GAME_ID,
278
+ gameForumId: post.gameForumId,
279
+ weight: weight,
280
+ }
281
+ return await this._dna_request("forum/moderator/reRank", data)
282
+ }
283
+
284
+ /** 管理员设置强推 */
285
+ async strongRecommend(post: { postId: number; gameId?: number; gameForumId: number }, operateType = 1) {
286
+ const data = {
287
+ postId: post.postId,
288
+ gameId: post.gameId ?? DNA_GAME_ID,
289
+ gameForumId: post.gameForumId,
290
+ operateType: operateType,
291
+ }
292
+ return await this._dna_request("forum/moderator/setForceRecommend", data)
293
+ }
294
+
295
+ /** 管理员删帖 */
296
+ async adminDelete(post: { postId: number; gameId?: number; gameForumId: number }, content: string, reasonCode: number) {
297
+ const data = {
298
+ postId: post.postId,
299
+ gameId: post.gameId ?? DNA_GAME_ID,
300
+ gameForumId: post.gameForumId,
301
+ content: content,
302
+ reasonCode: reasonCode,
303
+ }
304
+ return await this._dna_request("forum/moderator/postDelete", data)
305
+ }
306
+ /** 管理员移动帖子 */
307
+ async adminMovePost(
308
+ post: { postId: number; gameId?: number; gameForumId: number },
309
+ newGameId: number,
310
+ newForumId: number,
311
+ newTopicIdStr: string,
312
+ ) {
313
+ const data = {
314
+ postId: post.postId,
315
+ gameId: post.gameId ?? DNA_GAME_ID,
316
+ gameForumId: post.gameForumId,
317
+ newGameId: newGameId,
318
+ newForumId: newForumId,
319
+ newTopicIdStr: newTopicIdStr,
320
+ }
321
+ return await this._dna_request("forum/moderator/postMove", data)
322
+ }
323
+ /** ? */
324
+ async adminRefreshTime(post: { postId: number; gameId?: number; gameForumId: number }, refresh: number) {
325
+ const data = {
326
+ postId: post.postId,
327
+ gameId: post.gameId ?? DNA_GAME_ID,
328
+ gameForumId: post.gameForumId,
329
+ refresh: refresh,
330
+ }
331
+ return await this._dna_request("forum/moderator/setRefresh", data)
332
+ }
333
+
334
+ /** 黑名单 */
335
+ async blockList() {
336
+ return await this._dna_request("user/block/list")
337
+ }
338
+
339
+ /** 拉黑 */
340
+ async blockOther(blockPostId: number, blockUserId: string, type: number) {
341
+ const data = {
342
+ blockPostId: blockPostId,
343
+ blockUserId: blockUserId,
344
+ type: type,
345
+ }
346
+ return await this._dna_request("user/block/list", data)
347
+ }
348
+
349
+ /** ? */
350
+ async viewCommunity() {
351
+ return await this._dna_request("encourage/level/viewCommunity")
352
+ }
353
+
354
+ /** ? */
355
+ async viewCount() {
356
+ return await this._dna_request("forum/viewCount")
357
+ }
358
+
359
+ /** ? */
360
+ async receiveLog(periodId: number, pageIndex: number, pageSize: number) {
361
+ const data = {
362
+ periodId: periodId,
363
+ pageIndex: pageIndex,
364
+ pageSize: pageSize,
365
+ }
366
+ return await this._dna_request("encourage/signin/receiveLog", data)
367
+ }
368
+
369
+ /** 收藏 */
370
+ async collect(postId: number, toUserId: string, operateType = 1) {
371
+ const data = {
372
+ operateType: operateType,
373
+ postId: postId,
374
+ toUserId: toUserId,
375
+ }
376
+ return await this._dna_request("forum/collect", data)
377
+ }
378
+
379
+ /** 删除评论 */
380
+ async commentDelete(
381
+ comment: { id: number; gameId: number; gameForumId: number },
382
+ entityType: number,
383
+ content: string,
384
+ reasonCode: number,
385
+ ) {
386
+ const data = {
387
+ id: comment.id,
388
+ gameId: comment.gameId,
389
+ gameForumId: comment.gameForumId,
390
+ entityType: entityType,
391
+ content: content,
392
+ reasonCode: reasonCode,
393
+ }
394
+ return await this._dna_request("forum/collect", data)
395
+ }
396
+
397
+ /** 推荐列表 */
398
+ async recommendList(recIndex: number, newIndex: number, size: number, history: number, gameId = DNA_GAME_ID) {
399
+ const data = {
400
+ gameId: gameId,
401
+ recIndex: recIndex,
402
+ newIndex: newIndex,
403
+ size: size,
404
+ history: history,
405
+ }
406
+ return await this._dna_request("forum/recommend/list", data)
407
+ }
408
+
409
+ /** 举报 */
410
+ async report(
411
+ { commentId = 0, postId = 0, replyId = 0 }: { commentId?: number; postId?: number; replyId?: number },
412
+ reportReason = 1,
413
+ reportType = 1,
414
+ ) {
415
+ const data = {
416
+ commentId: commentId,
417
+ postId: postId,
418
+ replyId: replyId,
419
+ reportReason: reportReason,
420
+ reportType: reportType,
421
+ }
422
+ return await this._dna_request<DNAPostListRes>("forum/recommend/list", data)
423
+ }
424
+ /** 搜索帖子 */
425
+ async searchPost(keyword: number, pageIndex: number, pageSize: number, gameId = DNA_GAME_ID, searchType = 1) {
426
+ const data = {
427
+ gameId: gameId,
428
+ keyword: keyword,
429
+ pageIndex: pageIndex,
430
+ pageSize: pageSize,
431
+ searchType: searchType,
432
+ }
433
+ return await this._dna_request<DNAPostListRes>("forum/searchPost", data)
434
+ }
435
+ /** 搜索帖子 */
436
+ async searchTopic(keyword: number, pageIndex: number, pageSize = 20, gameId = DNA_GAME_ID) {
437
+ const data = {
438
+ gameId: gameId,
439
+ keyword: keyword,
440
+ pageIndex: pageIndex,
441
+ pageSize: pageSize,
442
+ }
443
+ return await this._dna_request<DNAPostListRes>("config/searchTopic", data)
444
+ }
445
+
446
+ /** 搜索帖子 */
447
+ async searchUser(keyword: number, pageIndex: number, pageSize: number) {
448
+ const data = {
449
+ keyword: keyword,
450
+ pageIndex: pageIndex,
451
+ pageSize: pageSize,
452
+ }
453
+ return await this._dna_request<DNAPostListRes>("user/searchUser", data)
454
+ }
455
+
456
+ /**
457
+ * 获取帖子列表
458
+ * @param topicId 主题ID
459
+ * @param pageIndex 页码
460
+ * @param pageSize 每页数量
461
+ * @param searchType 搜索类型 1:最新 2:热门
462
+ * @param timeType 时间类型 0:全部 1:今日 2:本周 3:本月
463
+ * @returns 帖子列表
464
+ */
465
+ async getPostsByTopic(
466
+ topicId: number = 177,
467
+ pageIndex: number = 1,
468
+ pageSize: number = 20,
469
+ searchType: number = 1,
470
+ timeType: number = 0,
471
+ ) {
472
+ const data = {
473
+ topicId: topicId,
474
+ gameId: DNA_GAME_ID,
475
+ pageIndex: pageIndex,
476
+ pageSize: pageSize,
477
+ searchType: searchType, // 1:最新 2:热门
478
+ timeType: timeType, // 0:全部 1:今日 2:本周 3:本月
479
+ }
480
+ return await this._dna_request<DNAPostListRes>("forum/getPostByTopic", data)
481
+ }
482
+
483
+ /**
484
+ * 获取帖子详情
485
+ * @param post_id 帖子ID
486
+ * @returns 帖子详情
487
+ */
488
+ async getPostDetail(post_id: string) {
489
+ const data = { postId: post_id }
490
+ return await this._dna_request<DNAPostDetailRes>("forum/getPostDetail", data)
491
+ }
492
+
493
+ /** 关注用户 */
494
+ async doFollow(userId: string, unfollow?: boolean) {
495
+ const data = {
496
+ followUserId: userId,
497
+ operateType: unfollow ? 0 : 1,
498
+ }
499
+ return await this._dna_request("user/followUser", data, { sign: true })
500
+ }
501
+
502
+ /** 获取关注状态 */
503
+ async getFollowState(userId: string) {
504
+ const data = {
505
+ followUserId: userId,
506
+ }
507
+ return await this._dna_request("user/isFollow", data)
508
+ }
509
+
510
+ /**
511
+ * 点赞帖子
512
+ */
513
+ async doLike(post: { gameForumId: string; postId: string; postType: string; userId: string }) {
514
+ const data = {
515
+ forumId: post.gameForumId,
516
+ gameId: DNA_GAME_ID,
517
+ likeType: "1",
518
+ operateType: "1",
519
+ postCommentId: "",
520
+ postCommentReplyId: "",
521
+ postId: post.postId,
522
+ postType: post.postType,
523
+ toUserId: post.userId,
524
+ }
525
+ return await this._dna_request("forum/like", data)
526
+ }
527
+
528
+ /** 分享帖子 */
529
+ async doShare() {
530
+ const data = { gameId: DNA_GAME_ID }
531
+ return await this._dna_request("encourage/level/shareTask", data)
532
+ }
533
+
534
+ /** 回复帖子 */
535
+ async createComment(post: { userId: string; postId: string; gameForumId: number }, content: string) {
536
+ const content_json = JSON.stringify([
537
+ {
538
+ content,
539
+ contentType: "1",
540
+ // imgHeight: 0,
541
+ // imgWidth: 0,
542
+ // url: "",
543
+ },
544
+ ])
545
+ const data = {
546
+ postId: post.postId,
547
+ forumId: post.gameForumId,
548
+ postType: "1",
549
+ content: content_json,
550
+ }
551
+
552
+ return await this._dna_request("forum/comment/createComment", data, {
553
+ sign: true,
554
+ params: { toUserId: post.userId },
555
+ })
556
+ }
557
+
558
+ /** 回复评论 */
559
+ async createReply(post: { userId: string; postId: string; postCommentId: string; gameForumId: number }, content: string) {
560
+ const content_json = JSON.stringify([
561
+ {
562
+ content,
563
+ contentType: "1",
564
+ imgHeight: 0,
565
+ imgWidth: 0,
566
+ url: "",
567
+ },
568
+ ])
569
+ const data = {
570
+ content: content_json,
571
+ forumId: post.gameForumId,
572
+ postCommentId: post.postCommentId,
573
+ postId: post.postId,
574
+ postType: "1",
575
+ toUserId: post.userId,
576
+ }
577
+
578
+ return await this._dna_request("forum/comment/createReply", data, { sign: true, refer: true, params: { toUserId: post.userId } })
579
+ }
580
+
581
+ /** 回复评论的评论 */
582
+ async createReplyList(
583
+ post: { userId: string; postId: string; postCommentId: string; postCommentReplyId: string; gameForumId: number },
584
+ content: string,
585
+ ) {
586
+ const content_json = JSON.stringify([
587
+ {
588
+ content,
589
+ contentType: "1",
590
+ imgHeight: 0,
591
+ imgWidth: 0,
592
+ url: "",
593
+ },
594
+ ])
595
+ const data = {
596
+ content: content_json,
597
+ forumId: post.gameForumId,
598
+ postCommentId: post.postCommentId,
599
+ postCommentReplyId: post.postCommentReplyId,
600
+ postId: post.postId,
601
+ postType: "1",
602
+ toUserId: post.userId,
603
+ }
604
+ return await this._dna_request("forum/comment/createReply", data, { sign: true, refer: true, params: { toUserId: post.userId } })
605
+ }
606
+
607
+ /** */
608
+ async deletePost(deleteType: number, id: number) {
609
+ return await this._dna_request("forum/more/delete", { deleteType, id }, { sign: true, refer: true })
610
+ }
611
+
612
+ /**
613
+ * 获取用户信息
614
+ * @returns 用户信息
615
+ */
616
+ async getOtherMine(userId = "709542994134436647") {
617
+ const data = {
618
+ otherUserId: userId,
619
+ searchType: 1,
620
+ type: 2,
621
+ }
622
+ return await this._dna_request<DNAMineRes>("user/mine", data)
623
+ }
624
+
625
+ /**
626
+ * 获取用户信息
627
+ * @returns 用户信息
628
+ */
629
+ async getMine() {
630
+ return await this._dna_request<DNAMineRes>("user/mine")
631
+ }
632
+
633
+ async getGameConfig() {
634
+ const data = { gameId: DNA_GAME_ID }
635
+ return await this._dna_request<DNAGameConfigRes[]>("config/getGameConfig", data)
636
+ }
637
+
638
+ async getHeaders(options?: {
639
+ payload?: Record<string, any> | string | FormData
640
+ exparams?: Record<string, any>
641
+ dev_code?: string
642
+ refer?: boolean
643
+ token?: string
644
+ tokenSig?: boolean
645
+ }) {
646
+ let { payload, exparams, dev_code = this.dev_code, refer, token = this.token, tokenSig } = options || {}
647
+
648
+ const CONTENT_TYPE = "application/x-www-form-urlencoded; charset=utf-8"
649
+ const iosBaseHeader = {
650
+ version: "1.1.3",
651
+ source: "ios",
652
+ "Content-Type": CONTENT_TYPE,
653
+ "User-Agent": "DoubleHelix/4 CFNetwork/3860.100.1 Darwin/25.0.0",
654
+ }
655
+ const h5BaseHeader = {
656
+ version: "3.11.0",
657
+ source: "h5",
658
+ "Content-Type": CONTENT_TYPE,
659
+ "User-Agent":
660
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
661
+ }
662
+ // 默认获取ios头
663
+ const headers = { ...(this.is_h5 ? h5BaseHeader : iosBaseHeader) } as Record<string, any>
664
+ if (dev_code) {
665
+ headers.devCode = dev_code
666
+ }
667
+ if (refer) {
668
+ headers.origin = "https://dnabbs.yingxiong.com"
669
+ headers.refer = "https://dnabbs.yingxiong.com/"
670
+ }
671
+ if (token) {
672
+ headers.token = token
673
+ }
674
+ if (payload instanceof FormData) {
675
+ const pk = await this.getRsaPublicKey()
676
+ const { signature, key } = build_upload_signature(pk)
677
+ headers.t = signature
678
+ this.uploadKey = key
679
+
680
+ if (exparams) {
681
+ for (const [key, value] of Object.entries(exparams)) {
682
+ payload.append(key, String(value))
683
+ }
684
+ }
685
+
686
+ delete headers["Content-Type"]
687
+ } else if (typeof payload === "object") {
688
+ const si = build_signature(payload, tokenSig ? token : "")
689
+ Object.assign(payload, { sign: si.s, timestamp: si.t })
690
+ if (exparams) {
691
+ Object.assign(payload, exparams)
692
+ }
693
+
694
+ const params = new URLSearchParams()
695
+ Object.entries(payload).forEach(([key, value]) => {
696
+ params.append(key, String(value))
697
+ })
698
+ payload = params.toString()
699
+
700
+ const rk = si.k
701
+ const pk = await this.getRsaPublicKey()
702
+ const ek = rsa_encrypt(rk, pk)
703
+ if (this.is_h5) {
704
+ headers.k = ek
705
+ } else {
706
+ headers.rk = rk
707
+ headers.key = ek
708
+ }
709
+ }
710
+ return { headers, payload }
711
+ }
712
+
713
+ private async _dna_request<T = any>(
714
+ url: string,
715
+ data?: any,
716
+ options?: {
717
+ method?: "GET" | "POST"
718
+ sign?: boolean
719
+ file?: File
720
+ tokenSig?: boolean
721
+ token?: boolean
722
+ refer?: boolean
723
+ params?: Record<string, any>
724
+ max_retries?: number
725
+ retry_delay?: number
726
+ timeout?: number
727
+ },
728
+ ): Promise<DNAApiResponse<T>> {
729
+ let { method = "POST", sign, refer, params, max_retries = 3, retry_delay = 1, timeout = 10000, token, tokenSig } = options || {}
730
+ let headers: Record<string, any>
731
+ if (sign) {
732
+ const { payload: p, headers: h } = await this.getHeaders({
733
+ payload: data,
734
+ refer,
735
+ exparams: params,
736
+ token: token ? this.token : undefined,
737
+ tokenSig,
738
+ })
739
+ data = p
740
+ headers = h
741
+ } else {
742
+ const { headers: h } = await this.getHeaders({ token: token ? this.token : undefined })
743
+ headers = h
744
+ }
745
+
746
+ for (let attempt = 0; attempt < max_retries; attempt++) {
747
+ try {
748
+ let body: string | FormData | undefined = data
749
+ if (data && typeof data === "object" && !(data instanceof FormData)) {
750
+ const p = new URLSearchParams()
751
+ Object.entries(data).forEach(([key, value]) => {
752
+ if (value !== undefined) p.append(key, String(value))
753
+ })
754
+ body = p.toString()
755
+ }
756
+ const fetchOptions: RequestInit = {
757
+ method,
758
+ headers,
759
+ body,
760
+ }
761
+
762
+ // 实现超时控制
763
+ const controller = new AbortController()
764
+ const timeoutId = setTimeout(() => controller.abort(), timeout)
765
+
766
+ const initOptions = {
767
+ ...fetchOptions,
768
+ signal: controller.signal,
769
+ }
770
+ const response = this.fetchFn
771
+ ? await this.fetchFn(`${this.BASE_URL}${url}`, initOptions)
772
+ : await fetch(`${this.BASE_URL}${url}`, initOptions)
773
+ clearTimeout(timeoutId)
774
+
775
+ // 获取响应头的 content-type
776
+ const contentType = response.headers.get("content-type") || ""
777
+ let raw_res: any
778
+
779
+ // 根据 content-type 处理响应数据
780
+ if (contentType.includes("text/")) {
781
+ const textData = await response.text()
782
+ raw_res = {
783
+ code: RespCode.ERROR,
784
+ data: textData,
785
+ }
786
+ } else {
787
+ raw_res = await response.json()
788
+ }
789
+
790
+ if (typeof raw_res === "object" && raw_res !== null) {
791
+ try {
792
+ if (typeof raw_res.data === "string") {
793
+ raw_res.data = JSON.parse(raw_res.data)
794
+ }
795
+ } catch (e) {
796
+ // 忽略解析错误
797
+ }
798
+ }
799
+
800
+ return new DNAApiResponse<T>(raw_res)
801
+ } catch (e) {
802
+ console.error(`请求失败: ${(e as Error).message}`)
803
+ if (attempt < max_retries - 1) {
804
+ await new Promise((resolve) => setTimeout(resolve, retry_delay * Math.pow(2, attempt)))
805
+ }
806
+ }
807
+ }
808
+
809
+ return DNAApiResponse.err("请求服务器失败,已达最大重试次数")
810
+ }
811
+ }
812
+
813
+ enum DNAInstanceMHType {
814
+ "角色" = "role",
815
+ "武器" = "weapon",
816
+ "魔之楔" = "mzx",
817
+ "role" = "角色",
818
+ "weapon" = "武器",
819
+ "mzx" = "魔之楔",
820
+ }
821
+
822
+ export function getDNAInstanceMHType(key: keyof typeof DNAInstanceMHType) {
823
+ return DNAInstanceMHType[key]
824
+ }
825
+
826
+ //#region 接口定义
827
+
828
+ export interface DNAMineRes {
829
+ mine: DNAMine
830
+ postList: DNAPost[]
831
+ hasNext: number
832
+ }
833
+
834
+ export interface DNAMine {
835
+ /** 文章数量 */
836
+ articleCount: number
837
+ /** 收藏数量 */
838
+ collectCount: number
839
+ /** 评论数量 */
840
+ commentCount: number
841
+ /** 粉丝数量 */
842
+ fansCount: number
843
+ /** 新粉丝数量 */
844
+ fansNewCount: number
845
+ /** 关注数量 */
846
+ followCount: number
847
+ /** 性别 */
848
+ gender: number
849
+ /** 精华数量 */
850
+ goldNum: number
851
+ /** 头像 */
852
+ headUrl: string
853
+ /** 是否关注 */
854
+ isFollow: number
855
+ /** 是否登录用户 */
856
+ isLoginUser: number
857
+ /** 是否被禁言 */
858
+ isMute: number
859
+ /** 等级 */
860
+ levelTotal: number
861
+ /** 点赞数量 */
862
+ likeCount: number
863
+ /** 手机号 */
864
+ mobile: string
865
+ /** 管理员列表 */
866
+ moderatorVos: any[]
867
+ /** 帖子数量 */
868
+ postCount: number
869
+ /** 注册时间 */
870
+ registerTime: string
871
+ /** 状态 */
872
+ status: number
873
+ /** 趋势数量 */
874
+ trendCount: number
875
+ /** 用户ID */
876
+ userId: string
877
+ /** 用户名 */
878
+ userName: string
879
+ }
880
+
881
+ export interface DNAGameConfigRes {
882
+ /** 游戏所有板块列表 */
883
+ gameAllForumList: GameForum[]
884
+ /** 游戏ID */
885
+ gameId: number
886
+ /** 游戏板块图片列表 */
887
+ gameForumPictureList: any[]
888
+ /** 游戏板块列表 */
889
+ gameForumList: GameForum[]
890
+ /** 签到按钮图片 */
891
+ signBtn: string
892
+ sort: number
893
+ /** 话题列表 */
894
+ topicList: GameTopic[]
895
+ /** 背景图片 */
896
+ drawBackgroundUrl: string
897
+ /** 是否默认游戏 */
898
+ gameDefault: number
899
+ /** 签到颜色 */
900
+ signColor: string
901
+ /** 游戏名称 */
902
+ gameName: string
903
+ /** CM图片2 意味不明 */
904
+ drawListUrl: string
905
+ /** 英文站点 */
906
+ configTab: ConfigTab[]
907
+ }
908
+
909
+ export interface ConfigTab {
910
+ name: string
911
+ url: string
912
+ }
913
+
914
+ export interface GameTopic {
915
+ /** 话题背景图片 */
916
+ backgroundUrl: string
917
+ gameId: number
918
+ /** 话题图标 */
919
+ topicIconUrl: string
920
+ topicId: number
921
+ /** 话题名称 */
922
+ topicName: string
923
+ sort: number
924
+ /** 话题描述 */
925
+ topicDesc: string
926
+ }
927
+
928
+ export interface GameForum {
929
+ /** 全部=1 普通=3 */
930
+ forumDataType: number
931
+ /** 固定1 */
932
+ forumUiType: number
933
+ /** 小红点 */
934
+ isTrend: number
935
+ /** 夜间模式图标 */
936
+ iconWhiteUrl: string
937
+ /** 固定1 */
938
+ forumType: number
939
+ /** 板块名称 */
940
+ name: string
941
+ forumListShowType: number
942
+ /** 图标 */
943
+ iconUrl: string
944
+ id: number
945
+ sort: number
946
+ /** 官方 */
947
+ isOfficial: number
948
+ }
949
+
950
+ export interface UserGame {
951
+ gameId: number // gameId
952
+ gameName: string // gameName
953
+ }
954
+
955
+ export interface DNALoginRes {
956
+ applyCancel?: number // applyCancel
957
+ gender?: number // gender
958
+ signature?: string // signature
959
+ headUrl: string // headUrl
960
+ userName: string // userName
961
+ userId: string // userId
962
+ isOfficial: number // isOfficial
963
+ token: string // token
964
+ userGameList: UserGame[] // userGameList
965
+ isRegister: number // isRegister
966
+ status: number // status
967
+ /** 是否完成绑定 0: 未绑定, 1: 已绑定 */
968
+ isComplete: number
969
+ refreshToken: string // refreshToken
970
+ }
971
+
972
+ export interface DNARoleShowVo {
973
+ roleId: string // roleId
974
+ headUrl?: string // headUrl
975
+ level?: number // level
976
+ roleName?: string // roleName
977
+ isDefault?: number // isDefault
978
+ roleRegisterTime?: string // roleRegisterTime
979
+ boundType?: number // boundType
980
+ roleBoundId: string // roleBoundId
981
+ }
982
+
983
+ export interface DNARole {
984
+ gameName: string // gameName
985
+ showVoList: DNARoleShowVo[] // showVoList
986
+ gameId: number // gameId
987
+ }
988
+
989
+ export interface DNARoleListRes {
990
+ roles: DNARole[] // roles
991
+ }
992
+
993
+ /** 密函 */
994
+ export interface DNARoleForToolInstance {
995
+ id: number
996
+ /** 中文 */
997
+ name: string
998
+ /** 密函编码 */
999
+ code: string
1000
+ /** 固定0 */
1001
+ on: number
1002
+ }
1003
+
1004
+ export interface DNARoleForToolInstanceInfo {
1005
+ /** 密函列表 */
1006
+ instances: DNARoleForToolInstance[] // instances
1007
+ }
1008
+
1009
+ export interface DraftDoingInfo {
1010
+ draftCompleteNum: number // draftCompleteNum
1011
+ draftDoingNum: number // draftDoingNum
1012
+ /** 结束时间 */
1013
+ endTime: string
1014
+ /** 产品id */
1015
+ productId?: number
1016
+ /** 产品名称 */
1017
+ productName: string
1018
+ /** 开始时间 */
1019
+ startTime: string
1020
+ }
1021
+
1022
+ export interface DraftInfo {
1023
+ /** 正在做的锻造 */
1024
+ draftDoingInfo?: DraftDoingInfo[]
1025
+ /** 正在做的锻造数量 */
1026
+ draftDoingNum: number
1027
+ /** 最大锻造数量 */
1028
+ draftMaxNum: number
1029
+ }
1030
+
1031
+ export interface DNARoleShortNoteRes {
1032
+ /** 迷津进度 */
1033
+ rougeLikeRewardCount: number
1034
+ /** 迷津总数 */
1035
+ rougeLikeRewardTotal: number
1036
+ /** 备忘手记进度 */
1037
+ currentTaskProgress: number
1038
+ /** 备忘手记总数 */
1039
+ maxDailyTaskProgress: number
1040
+ /** 梦魇进度 */
1041
+ hardBossRewardCount: number
1042
+ /** 梦魇总数 */
1043
+ hardBossRewardTotal: number
1044
+ /** 锻造信息 */
1045
+ draftInfo: DraftInfo
1046
+ }
1047
+
1048
+ export interface DNARoleWeapon {
1049
+ weaponId: number
1050
+ weaponEid: string
1051
+ /** 武器类型图标 */
1052
+ elementIcon: string
1053
+ /** 武器图标 */
1054
+ icon: string
1055
+ /** 武器等级 */
1056
+ level: number
1057
+ /** 武器名称 */
1058
+ name: string
1059
+ /** 精炼等级 */
1060
+ skillLevel: number
1061
+ /** 是否解锁 */
1062
+ unLocked: boolean
1063
+ }
1064
+
1065
+ export interface DNARoleChar {
1066
+ charId: number
1067
+ charEid: string
1068
+ /** 元素图标 */
1069
+ elementIcon: string
1070
+ /** 命座等级 */
1071
+ gradeLevel: number
1072
+ /** 角色图标 */
1073
+ icon: string
1074
+ /** 角色等级 */
1075
+ level: number
1076
+ /** 角色名称 */
1077
+ name: string
1078
+ /** 是否解锁 */
1079
+ unLocked: boolean
1080
+ }
1081
+
1082
+ export interface DNARoleForToolRes {
1083
+ /** 角色信息 */
1084
+ roleInfo: DNARoleInfo
1085
+ /** 密函 */
1086
+ instanceInfo: DNARoleForToolInstanceInfo[]
1087
+ }
1088
+
1089
+ export interface DNARoleShow {
1090
+ /** 角色列表 */
1091
+ roleChars: DNARoleChar[]
1092
+ /** 武器列表 */
1093
+ langRangeWeapons: DNARoleWeapon[]
1094
+ /** 武器列表 */
1095
+ closeWeapons: DNARoleWeapon[]
1096
+ /** 角色头像 */
1097
+ headUrl: string
1098
+ /** 等级 */
1099
+ level: number
1100
+ /** 成就列表 */
1101
+ params: DNARoleAchievement[]
1102
+ /** 角色id */
1103
+ roleId: string
1104
+ /** 角色名称 */
1105
+ roleName: string
1106
+ /** 迷津 */
1107
+ rougeLikeInfo: DNARougeLikeInfo
1108
+ }
1109
+
1110
+ export interface DNARoleAchievement {
1111
+ paramKey: string // paramKey
1112
+ paramValue: string // paramValue
1113
+ }
1114
+
1115
+ export interface DNARoleInfo {
1116
+ /** 深渊信息 */
1117
+ abyssInfo: DNAAbyssInfo
1118
+ /** 角色信息 */
1119
+ roleShow: DNARoleShow
1120
+ }
1121
+
1122
+ export interface DNARougeLikeInfo {
1123
+ /** 最大通过等级 */
1124
+ maxPassed: number
1125
+ /** 最大通过等级名称 */
1126
+ maxPassedName: string
1127
+ /** 重置时间 */
1128
+ resetTime: string
1129
+ /** 奖励数量 */
1130
+ rewardCount: number
1131
+ /** 奖励总数 */
1132
+ rewardTotal: number
1133
+ /** 天赋信息 */
1134
+ talentInfo: DNARougeLikeTalentInfo[]
1135
+ }
1136
+
1137
+ export interface DNARougeLikeTalentInfo {
1138
+ cur: string
1139
+ max: string
1140
+ }
1141
+
1142
+ export interface DNAAbyssInfo {
1143
+ /** 阵容 */
1144
+ bestTimeVo1: DNABestTimeVo1
1145
+ /** 结束时间 */
1146
+ endTime: string
1147
+ /** 操作名称 */
1148
+ operaName: string
1149
+ /** 进度名称 */
1150
+ progressName: string
1151
+ /** 星级 */
1152
+ stars: string
1153
+ /** 开始时间 */
1154
+ startTime: string
1155
+ }
1156
+
1157
+ /** 深渊阵容 */
1158
+ export interface DNABestTimeVo1 {
1159
+ /** 角色图标 */
1160
+ charIcon: string
1161
+ /** 近战武器图标 */
1162
+ closeWeaponIcon: string
1163
+ /** 远程武器图标 */
1164
+ langRangeWeaponIcon: string
1165
+ /** 魔灵图标 */
1166
+ petIcon: string
1167
+ /** 协战角色图标1 */
1168
+ phantomCharIcon1: string
1169
+ /** 协战武器图标1 */
1170
+ phantomWeaponIcon1: string
1171
+ /** 协战角色图标2 */
1172
+ phantomCharIcon2: string
1173
+ /** 协战武器图标2 */
1174
+ phantomWeaponIcon2: string
1175
+ }
1176
+
1177
+ /** 角色属性 */
1178
+ export interface DNACharAttribute {
1179
+ /** 技能范围 */
1180
+ skillRange: string
1181
+ /** 强化值 */
1182
+ strongValue: string
1183
+ /** 技能威力 */
1184
+ skillIntensity: string
1185
+ /** 武器精通 */
1186
+ weaponTags: string[]
1187
+ /** 防御 */
1188
+ def: number
1189
+ /** 仇恨值 */
1190
+ enmityValue: string
1191
+ /** 技能效益 */
1192
+ skillEfficiency: string
1193
+ /** 技能耐久 */
1194
+ skillSustain: string
1195
+ /** 最大生命值 */
1196
+ maxHp: number
1197
+ /** 攻击 */
1198
+ atk: number
1199
+ /** 最大护盾 */
1200
+ maxES: number
1201
+ /** 最大神志 */
1202
+ maxSp: number
1203
+ }
1204
+
1205
+ /** 角色技能 */
1206
+ export interface DNARoleSkill {
1207
+ /** 技能id */
1208
+ skillId: number
1209
+ /** 技能图标 */
1210
+ icon: string
1211
+ /** 技能等级 */
1212
+ level: number
1213
+ /** 技能名称 */
1214
+ skillName: string
1215
+ }
1216
+
1217
+ /** 溯源 */
1218
+ export interface DNARoleTrace {
1219
+ /** 溯源图标 */
1220
+ icon: string
1221
+ /** 溯源描述 */
1222
+ description: string
1223
+ }
1224
+
1225
+ /** 魔之楔 */
1226
+ export interface DNARoleMod {
1227
+ /** id 没佩戴为-1 */
1228
+ id: number
1229
+ /** 图标 */
1230
+ icon?: string
1231
+ /** 质量 */
1232
+ quality?: number
1233
+ /** 名称 */
1234
+ name?: string
1235
+ }
1236
+
1237
+ export interface DNACharDetail {
1238
+ charId: number
1239
+ /** 角色属性 */
1240
+ attribute: DNACharAttribute
1241
+ /** 角色技能 */
1242
+ skills: DNARoleSkill[]
1243
+ /** 立绘 */
1244
+ paint: string
1245
+ /** 角色名称 */
1246
+ charName: string
1247
+ /** 元素图标 */
1248
+ elementIcon: string
1249
+ /** 溯源 */
1250
+ traces: DNARoleTrace[]
1251
+ /** 当前魔之楔 */
1252
+ currentVolume: number
1253
+ /** 最大魔之楔 */
1254
+ sumVolume: number
1255
+ /** 角色等级 */
1256
+ level: number
1257
+ /** 角色头像 */
1258
+ icon: string
1259
+ /** 溯源等级 0-6 */
1260
+ gradeLevel: number
1261
+ /** 元素名称 */
1262
+ elementName: string
1263
+ /** 魔之楔列表 */
1264
+ modes: DNARoleMod[]
1265
+ }
1266
+
1267
+ export interface DNACharDetailRes {
1268
+ /** 角色详情 */
1269
+ charDetail: DNACharDetail
1270
+ }
1271
+
1272
+ export interface DNAWeaponDetailRes {
1273
+ /** 武器详情 */
1274
+ weaponDetail: DNAWeaponDetail
1275
+ }
1276
+
1277
+ export interface DNAWeaponDetail {
1278
+ attribute: DNAWeaponAttribute
1279
+ currentVolume: number
1280
+ description: string
1281
+ elementIcon: string
1282
+ elementName: string
1283
+ icon: string
1284
+ id: number
1285
+ level: number
1286
+ modes: DNARoleMod[]
1287
+ name: string
1288
+ skillLevel: number
1289
+ sumVolume: number
1290
+ }
1291
+
1292
+ export interface DNAWeaponAttribute {
1293
+ atk: number
1294
+ crd: number
1295
+ cri: number
1296
+ speed: number
1297
+ trigger: number
1298
+ }
1299
+
1300
+ export interface DNADayAward {
1301
+ gameId: number // gameId
1302
+ periodId: number // periodId
1303
+ iconUrl: string // iconUrl
1304
+ id: number // id
1305
+ dayInPeriod: number // dayInPeriod
1306
+ updateTime: number // updateTime
1307
+ awardNum: number // awardNum
1308
+ thirdProductId: string // thirdProductId
1309
+ createTime: number // createTime
1310
+ awardName: string // awardName
1311
+ }
1312
+
1313
+ export interface DNACaSignPeriod {
1314
+ gameId: number // gameId
1315
+ retryCos: number // retryCos
1316
+ endDate: number // endDate
1317
+ id: number // id
1318
+ startDate: number // startDate
1319
+ retryTimes: number // retryTimes
1320
+ overDays: number // overDays
1321
+ createTime: number // createTime
1322
+ name: string // name
1323
+ }
1324
+
1325
+ export interface DNACaSignRoleInfo {
1326
+ headUrl: string // headUrl
1327
+ roleId: string // roleId
1328
+ roleName: string // roleName
1329
+ level: number // level
1330
+ roleBoundId: string // roleBoundId
1331
+ }
1332
+
1333
+ export interface DNAHaveSignInRes {
1334
+ /** 已签到天数 */
1335
+ totalSignInDay: number
1336
+ }
1337
+
1338
+ export interface DNACalendarSignRes {
1339
+ todaySignin: boolean // todaySignin
1340
+ userGoldNum: number // userGoldNum
1341
+ dayAward: DNADayAward[] // dayAward
1342
+ signinTime: number // signinTime
1343
+ period: DNACaSignPeriod // period
1344
+ roleInfo: DNACaSignRoleInfo // roleInfo
1345
+ }
1346
+
1347
+ export interface DNABBSTask {
1348
+ /** 备注 */
1349
+ remark: string
1350
+ /** 完成次数 */
1351
+ completeTimes: number
1352
+ /** 需要次数 */
1353
+ times: number
1354
+ /** skipType */
1355
+ skipType: number
1356
+ /** 获取经验 */
1357
+ gainExp: number
1358
+ /** 进度 */
1359
+ process: number
1360
+ /** 获取金币 */
1361
+ gainGold: number
1362
+ /** 任务标识名 */
1363
+ markName?: string
1364
+ }
1365
+
1366
+ export interface DNATaskProcessRes {
1367
+ dailyTask: DNABBSTask[] // dailyTask
1368
+ }
1369
+
1370
+ export interface DNATopicPostListRes {
1371
+ postList: DNAPost[]
1372
+ topic: DNATopicDetail
1373
+ hasNext: number
1374
+ }
1375
+
1376
+ export interface DNATopicDetail {
1377
+ backgroundUrl: string
1378
+ gameId: number
1379
+ sort: number
1380
+ topicDesc: string
1381
+ topicIconUrl: string
1382
+ topicId: number
1383
+ topicName: string
1384
+ }
1385
+
1386
+ export interface DNAPost {
1387
+ browseCount: string
1388
+ collectionCount: number
1389
+ commentCount: number
1390
+ gameForumId: number
1391
+ gameId: number
1392
+ gameName: string
1393
+ imgContent: DNAPostImgContent[]
1394
+ imgCount: number
1395
+ isCollect: number
1396
+ isCreator: number
1397
+ isElite: number
1398
+ isFollow: number
1399
+ isLike: number
1400
+ isLock: number
1401
+ isOfficial: number
1402
+ isPublisher: number
1403
+ likeCount: number
1404
+ postContent: string
1405
+ postCover: string
1406
+ postCoverVo: DNAPostCoverVo
1407
+ postId: string
1408
+ postStatus: number
1409
+ postTitle: string
1410
+ postType: number
1411
+ showTime: string
1412
+ topics: DNATopicShort[]
1413
+ userHeadUrl: string
1414
+ userId: string
1415
+ userLevel: number
1416
+ userModeratorIdentity: number
1417
+ userName: string
1418
+ }
1419
+
1420
+ export interface DNATopicShort {
1421
+ topicId: number
1422
+ topicName: string
1423
+ }
1424
+
1425
+ export interface DNAPostCoverVo {
1426
+ imgHeight: number
1427
+ imgWidth: number
1428
+ }
1429
+
1430
+ export interface DNAPostImgContent {
1431
+ imgHeight: number
1432
+ imgWidth: number
1433
+ url: string
1434
+ isAbnormal?: boolean
1435
+ }
1436
+
1437
+ export interface DNAPostListRes {
1438
+ postList: DNAPost[] // posts
1439
+ topList: any[]
1440
+ hasNext: number
1441
+ }
1442
+
1443
+ export interface DNAPostDetailRes {
1444
+ isHotCount: boolean
1445
+ gameId: number
1446
+ isFollow: number
1447
+ isLike: number
1448
+ postDetail: DNAPostDetail
1449
+ isCollect: number
1450
+ hasNext: number
1451
+ comment: DNAComment[]
1452
+ }
1453
+
1454
+ export interface DNAPostDetail {
1455
+ auditStatus: number
1456
+ browseCount: string
1457
+ checkStatus: number
1458
+ collectionCount: number
1459
+ commentCount: number
1460
+ gameForumId: number
1461
+ gameForumVo: DNAGameForumVo
1462
+ gameId: number
1463
+ gameName: string
1464
+ headCodeUrl: string
1465
+ id: string
1466
+ isCreator: number
1467
+ isElite: number
1468
+ isForceRecommend: number
1469
+ isHide: number
1470
+ isLock: number
1471
+ isMine: number
1472
+ isOfficial: number
1473
+ isRecommend: number
1474
+ isTop: number
1475
+ lastEditorTime?: string
1476
+ likeCount: number
1477
+ postContent: DNAPostContent[]
1478
+ postH5Content: string
1479
+ postTime: string
1480
+ postTitle: string
1481
+ postType: number
1482
+ postUserId: string
1483
+ refreshHour: number
1484
+ score: number
1485
+ topics: DNATopicShort[]
1486
+ userHeadCode: string
1487
+ userLevel: number
1488
+ userModeratorIdentity: number
1489
+ userName: string
1490
+ whiteUrl: any[]
1491
+ }
1492
+
1493
+ export interface DNAPostContent {
1494
+ contentType: PostContentType
1495
+ imgHeight: number
1496
+ imgWidth: number
1497
+ url?: string
1498
+ content?: string
1499
+ contentVideo?: DNAPostContentVideo
1500
+ }
1501
+
1502
+ export interface DNAComment {
1503
+ checkStatus: number
1504
+ commentContent: DNAPostContent[]
1505
+ commentId: string
1506
+ commentTime: string
1507
+ contentTextStatus: number
1508
+ floor: number
1509
+ isCreator: number
1510
+ isLike: number
1511
+ isMine: number
1512
+ isOfficial: number
1513
+ isPublisher: number
1514
+ likeCount: number
1515
+ replyCount: number
1516
+ replyVos: DNAReplyVos[]
1517
+ userHeadCode?: string
1518
+ userHeadUrl: string
1519
+ userId: string
1520
+ userLevel: number
1521
+ userModeratorIdentity: number
1522
+ userName: string
1523
+ }
1524
+
1525
+ export interface DNAReplyVos {
1526
+ checkStatus: number
1527
+ contentTextStatus: number
1528
+ createTime: number
1529
+ isCreator: number
1530
+ isLike: number
1531
+ isMine: number
1532
+ isOfficial: number
1533
+ isPublisher: number
1534
+ likeCount: number
1535
+ postCommentId: string
1536
+ postCommentReplayId: string
1537
+ replyContent: DNAPostContent[]
1538
+ replyContentStr: string
1539
+ replyId: string
1540
+ replyTime: string
1541
+ toUserIsCreator: number
1542
+ toUserModeratorIdentity: number
1543
+ toUserName: string
1544
+ userHeadCode?: string
1545
+ userHeadUrl: string
1546
+ userId: string
1547
+ userLevel: number
1548
+ userModeratorIdentity: number
1549
+ userName: string
1550
+ }
1551
+
1552
+ export interface DNAGameForumVo {
1553
+ forumDataType: number
1554
+ forumListShowType: number
1555
+ forumType: number
1556
+ forumUiType: number
1557
+ iconUrl: string
1558
+ iconWhiteUrl: string
1559
+ id: number
1560
+ isOfficial: number
1561
+ isTrend: number
1562
+ name: string
1563
+ sort: number
1564
+ }
1565
+
1566
+ export enum PostContentType {
1567
+ TEXT = 1,
1568
+ IMAGE = 2,
1569
+ VIDEO = 5,
1570
+ }
1571
+
1572
+ export interface DNAPostContentVideo {
1573
+ videoUrl: string // videoUrl
1574
+ coverUrl?: string // coverUrl
1575
+ }
1576
+
1577
+ class DNAApiResponse<T = any> {
1578
+ code: number = 0
1579
+ msg: string = ""
1580
+ success: boolean = false
1581
+ data?: T
1582
+
1583
+ constructor(raw_data: any) {
1584
+ this.code = raw_data.code || 0
1585
+ this.msg = raw_data.msg || ""
1586
+ this.success = raw_data.success || false
1587
+ this.data = raw_data.data
1588
+ }
1589
+
1590
+ // 判断是否成功
1591
+ get is_success() {
1592
+ return this.success && [RespCode.OK_ZERO, RespCode.OK_HTTP].includes(this.code)
1593
+ }
1594
+
1595
+ // 错误响应静态方法
1596
+ static err<T = undefined>(msg: string, code: number = RespCode.ERROR): DNAApiResponse<T> {
1597
+ return new DNAApiResponse<T>({ code, msg, data: undefined, success: false })
1598
+ }
1599
+ }
1600
+ //#endregion
1601
+
1602
+ //#region utils
1603
+
1604
+ // RSA加密函数
1605
+ function rsa_encrypt(text: string, public_key_b64: string): string {
1606
+ try {
1607
+ // 将base64公钥转换为PEM格式
1608
+ const lines: string[] = []
1609
+ for (let i = 0; i < public_key_b64.length; i += 64) {
1610
+ lines.push(public_key_b64.slice(i, i + 64))
1611
+ }
1612
+ const pem = `-----BEGIN PUBLIC KEY-----\n${lines.join("\n")}\n-----END PUBLIC KEY-----`
1613
+
1614
+ // 导入PEM格式的RSA公钥
1615
+ const publicKey = forge.pki.publicKeyFromPem(pem)
1616
+
1617
+ // 执行PKCS1_v1_5加密
1618
+ const textBytes = forge.util.encodeUtf8(text)
1619
+ const encrypted = publicKey.encrypt(textBytes)
1620
+
1621
+ return forge.util.encode64(encrypted)
1622
+ } catch (e) {
1623
+ throw new Error(`[DNA] RSA 加密失败: ${(e as Error).message}`)
1624
+ }
1625
+ }
1626
+
1627
+ // 生成随机字符串
1628
+ function rand_str(length: number = 16): string {
1629
+ const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
1630
+ let result = ""
1631
+ for (let i = 0; i < length; i++) {
1632
+ result += chars.charAt(Math.floor(Math.random() * chars.length))
1633
+ }
1634
+ return result
1635
+ }
1636
+
1637
+ // MD5加密并转换为大写
1638
+ function md5_upper(text: string): string {
1639
+ const md = forge.md.md5.create()
1640
+ md.update(text)
1641
+ return md.digest().toHex().toUpperCase()
1642
+ }
1643
+
1644
+ // 签名哈希函数
1645
+ function signature_hash(text: string): string {
1646
+ function swap_positions(text: string, positions: number[]): string {
1647
+ const chars = text.split("")
1648
+ for (let i = 1; i < positions.length; i += 2) {
1649
+ const p1 = positions[i - 1]
1650
+ const p2 = positions[i]
1651
+ if (p1 >= 0 && p1 < chars.length && p2 >= 0 && p2 < chars.length) {
1652
+ ;[chars[p1], chars[p2]] = [chars[p2], chars[p1]]
1653
+ }
1654
+ }
1655
+ return chars.join("")
1656
+ }
1657
+ return swap_positions(md5_upper(text), [1, 13, 5, 17, 7, 23])
1658
+ }
1659
+
1660
+ // 签名函数
1661
+ function sign_fI(data: Record<string, any>, secret: string): string {
1662
+ const pairs: string[] = []
1663
+ const sortedKeys = Object.keys(data).sort()
1664
+ for (const k of sortedKeys) {
1665
+ const v = data[k]
1666
+ if (v !== null && v !== undefined && v !== "") {
1667
+ pairs.push(`${k}=${v}`)
1668
+ }
1669
+ }
1670
+ const qs = pairs.join("&")
1671
+ return signature_hash(`${qs}&${secret}`)
1672
+ }
1673
+
1674
+ // XOR编码函数
1675
+ function xor_encode(text: string, key: string): string {
1676
+ const encoder = new TextEncoder()
1677
+ const tb = encoder.encode(text)
1678
+ const kb = encoder.encode(key)
1679
+ const out: string[] = []
1680
+ for (let i = 0; i < tb.length; i++) {
1681
+ const b = tb[i]
1682
+ const e = (b & 255) + (kb[i % kb.length] & 255)
1683
+ out.push(`@${e}`)
1684
+ }
1685
+ return out.join("")
1686
+ }
1687
+
1688
+ // 上传图片签名 - 字符交换
1689
+ function swapForUploadImgApp(text: string): string {
1690
+ const chars = text.split("")
1691
+ const swaps = [
1692
+ [3, 23],
1693
+ [11, 32],
1694
+ [22, 42],
1695
+ [25, 48],
1696
+ ]
1697
+ for (const [p1, p2] of swaps) {
1698
+ if (p1 < chars.length && p2 < chars.length) {
1699
+ ;[chars[p1], chars[p2]] = [chars[p2], chars[p1]]
1700
+ }
1701
+ }
1702
+ return chars.join("")
1703
+ }
1704
+
1705
+ // AES 解密图片 URL
1706
+ function aesDecryptImageUrl(encryptedUrl: string, key: string): string {
1707
+ const swapped = swapForUploadImgApp(encryptedUrl)
1708
+
1709
+ const encryptedData = forge.util.decode64(swapped)
1710
+
1711
+ const decipher = forge.cipher.createDecipher("AES-CBC", key)
1712
+ decipher.start({ iv: "A-16-Byte-String" })
1713
+ decipher.update(forge.util.createBuffer(encryptedData))
1714
+ decipher.finish()
1715
+
1716
+ return decipher.output.getBytes()
1717
+ }
1718
+
1719
+ // 构建上传图片签名(返回签名和密钥)
1720
+ function build_upload_signature(public_key: string): { signature: string; key: string } {
1721
+ const key = rand_str(16)
1722
+ const encrypted = rsa_encrypt(key, public_key)
1723
+ const signature = swapForUploadImgApp(encrypted)
1724
+ return { signature, key }
1725
+ }
1726
+
1727
+ // 构建普通签名
1728
+ function build_signature(data: Record<string, any>, token?: string): Record<string, any> {
1729
+ const ts = Date.now()
1730
+ const sign_data = { ...data, timestamp: ts, token }
1731
+ const sec = rand_str(16)
1732
+ const sig = sign_fI(sign_data, sec)
1733
+ const enc = xor_encode(sig, sec)
1734
+ return { s: enc, t: ts, k: sec }
1735
+ }
1736
+ //#endregion