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