@jeik/dingtalk-connector 0.8.21-fix1
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/CHANGELOG.md +686 -0
- package/LICENSE +21 -0
- package/README.en.md +181 -0
- package/README.md +221 -0
- package/bin/dingtalk-connector.js +858 -0
- package/bin/wizard-config.mjs +110 -0
- package/dist/accounts-BAzdqkAV.mjs +268 -0
- package/dist/accounts-BQptOmgB.mjs +2 -0
- package/dist/chunk-upload-BBQgGtcZ.mjs +193 -0
- package/dist/chunk-upload-DaLXXZH3.mjs +2 -0
- package/dist/common-C8pYKU_y.mjs +2 -0
- package/dist/common-Dt9n6fQN.mjs +101 -0
- package/dist/connection-DHHFFNQJ.mjs +423 -0
- package/dist/entry-bundled.d.mts +16 -0
- package/dist/entry-bundled.mjs +31 -0
- package/dist/game-xiyou-CqHt-6Q1.mjs +4271 -0
- package/dist/gateway-methods-C4tcgI7P.mjs +771 -0
- package/dist/gateway-methods-Ci31A3vg.mjs +2 -0
- package/dist/http-client-CpnJHB89.mjs +2 -0
- package/dist/http-client-DFWZgO1n.mjs +33 -0
- package/dist/index.d.mts +193 -0
- package/dist/index.mjs +45 -0
- package/dist/logger-BmJkQkm1.mjs +2 -0
- package/dist/logger-mZ9OSbmD.mjs +58 -0
- package/dist/media-C_SVin7s.mjs +2 -0
- package/dist/media-cz72EVS3.mjs +509 -0
- package/dist/message-handler-DESzFFDc.mjs +1971 -0
- package/dist/messaging-B6l1sRvX.mjs +1044 -0
- package/dist/runtime-DUgpo5zC.mjs +1422 -0
- package/dist/session-DJ4jYqPv.mjs +114 -0
- package/dist/utils-Bjh4r_qS.mjs +4 -0
- package/dist/utils-CIfI_3Jh.mjs +63 -0
- package/dist/utils-legacy-CALCPP1t.mjs +230 -0
- package/dist/utils-legacy-CFYDBM4r.mjs +3 -0
- package/docs/DEAP_AGENT_GUIDE.en.md +115 -0
- package/docs/DEAP_AGENT_GUIDE.md +115 -0
- package/docs/DINGTALK_MANUAL_SETUP.md +50 -0
- package/docs/MULTI_AGENT_SETUP.md +306 -0
- package/docs/RELEASE_NOTES_V0.7.10.md +40 -0
- package/docs/RELEASE_NOTES_V0.7.2.md +143 -0
- package/docs/RELEASE_NOTES_V0.7.3.md +149 -0
- package/docs/RELEASE_NOTES_V0.7.4.md +206 -0
- package/docs/RELEASE_NOTES_V0.7.5.md +267 -0
- package/docs/RELEASE_NOTES_V0.7.6.md +219 -0
- package/docs/RELEASE_NOTES_V0.7.7.md +122 -0
- package/docs/RELEASE_NOTES_V0.7.8.md +101 -0
- package/docs/RELEASE_NOTES_V0.7.9.md +65 -0
- package/docs/RELEASE_NOTES_V0.8.0.md +53 -0
- package/docs/RELEASE_NOTES_V0.8.1.md +47 -0
- package/docs/RELEASE_NOTES_V0.8.10.md +49 -0
- package/docs/RELEASE_NOTES_V0.8.11.md +51 -0
- package/docs/RELEASE_NOTES_V0.8.12.md +63 -0
- package/docs/RELEASE_NOTES_V0.8.13-beta.0.md +69 -0
- package/docs/RELEASE_NOTES_V0.8.13.md +62 -0
- package/docs/RELEASE_NOTES_V0.8.14.md +86 -0
- package/docs/RELEASE_NOTES_V0.8.16.md +40 -0
- package/docs/RELEASE_NOTES_V0.8.17.md +87 -0
- package/docs/RELEASE_NOTES_V0.8.18.md +64 -0
- package/docs/RELEASE_NOTES_V0.8.19.md +62 -0
- package/docs/RELEASE_NOTES_V0.8.2.md +55 -0
- package/docs/RELEASE_NOTES_V0.8.20.md +49 -0
- package/docs/RELEASE_NOTES_V0.8.3.md +63 -0
- package/docs/RELEASE_NOTES_V0.8.4.md +45 -0
- package/docs/RELEASE_NOTES_V0.8.7.md +49 -0
- package/docs/RELEASE_NOTES_V0.8.8.md +63 -0
- package/docs/RELEASE_NOTES_V0.8.9.md +81 -0
- package/docs/RELEASE_NOTES_v0.7.0.md +142 -0
- package/docs/RELEASE_NOTES_v0.7.1.md +74 -0
- package/docs/TROUBLESHOOTING.md +122 -0
- package/index.ts +77 -0
- package/openclaw.plugin.json +551 -0
- package/package.json +147 -0
- package/skills/dingtalk-channel-rules/SKILL.md +91 -0
- package/skills/dingtalk-troubleshoot/SKILL.md +93 -0
- package/skills/dws-cli/SKILL.md +129 -0
- package/skills/dws-cli/references/error-codes.md +95 -0
- package/skills/dws-cli/references/field-rules.md +105 -0
- package/skills/dws-cli/references/global-reference.md +104 -0
- package/skills/dws-cli/references/intent-guide.md +114 -0
- package/skills/dws-cli/references/products/aitable.md +452 -0
- package/skills/dws-cli/references/products/attendance.md +93 -0
- package/skills/dws-cli/references/products/calendar.md +217 -0
- package/skills/dws-cli/references/products/chat.md +292 -0
- package/skills/dws-cli/references/products/contact.md +108 -0
- package/skills/dws-cli/references/products/ding.md +57 -0
- package/skills/dws-cli/references/products/report.md +162 -0
- package/skills/dws-cli/references/products/simple.md +128 -0
- package/skills/dws-cli/references/products/todo.md +138 -0
- package/skills/dws-cli/references/products/workbench.md +39 -0
- package/skills/dws-cli/references/recovery-guide.md +94 -0
- package/src/channel.ts +588 -0
- package/src/config/accounts.ts +242 -0
- package/src/config/schema.ts +180 -0
- package/src/core/connection.ts +741 -0
- package/src/core/message-handler.ts +1788 -0
- package/src/core/provider.ts +111 -0
- package/src/core/state.ts +54 -0
- package/src/device-auth-config.ts +14 -0
- package/src/device-auth.ts +197 -0
- package/src/directory.ts +95 -0
- package/src/docs.ts +293 -0
- package/src/game-xiyou/achievement-engine.ts +252 -0
- package/src/game-xiyou/bounty-system.ts +315 -0
- package/src/game-xiyou/commands.ts +223 -0
- package/src/game-xiyou/drop-engine.ts +241 -0
- package/src/game-xiyou/encounter-system.ts +135 -0
- package/src/game-xiyou/escape-engine.ts +164 -0
- package/src/game-xiyou/exp-calculator.ts +139 -0
- package/src/game-xiyou/index.ts +479 -0
- package/src/game-xiyou/level-system.ts +91 -0
- package/src/game-xiyou/monster-pool.ts +180 -0
- package/src/game-xiyou/pity-counter.ts +114 -0
- package/src/game-xiyou/random-event-engine.ts +648 -0
- package/src/game-xiyou/renderer.ts +679 -0
- package/src/game-xiyou/storage.ts +218 -0
- package/src/game-xiyou/treasure-system.ts +105 -0
- package/src/game-xiyou/types.ts +582 -0
- package/src/game-xiyou/uid-resolver.ts +49 -0
- package/src/gateway-methods.ts +740 -0
- package/src/onboarding.ts +553 -0
- package/src/policy.ts +32 -0
- package/src/probe.ts +210 -0
- package/src/reply-dispatcher.ts +874 -0
- package/src/runtime.ts +32 -0
- package/src/sdk/helpers.ts +322 -0
- package/src/sdk/types.ts +519 -0
- package/src/secret-input.ts +19 -0
- package/src/services/media/audio.ts +54 -0
- package/src/services/media/chunk-upload.ts +296 -0
- package/src/services/media/common.ts +155 -0
- package/src/services/media/file.ts +75 -0
- package/src/services/media/image.ts +81 -0
- package/src/services/media/index.ts +10 -0
- package/src/services/media/video.ts +162 -0
- package/src/services/media.ts +1143 -0
- package/src/services/messaging/card.ts +604 -0
- package/src/services/messaging/index.ts +18 -0
- package/src/services/messaging/mentions.ts +267 -0
- package/src/services/messaging/send.ts +141 -0
- package/src/services/messaging.ts +1191 -0
- package/src/services/reply-markers.ts +55 -0
- package/src/targets.ts +45 -0
- package/src/types/index.ts +59 -0
- package/src/types/pdf-parse.d.ts +3 -0
- package/src/utils/agent.ts +63 -0
- package/src/utils/async.ts +51 -0
- package/src/utils/constants.ts +27 -0
- package/src/utils/http-client.ts +38 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/logger.ts +78 -0
- package/src/utils/session.ts +147 -0
- package/src/utils/token.ts +93 -0
- package/src/utils/utils-legacy.ts +454 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 悬赏令系统 (v2)
|
|
3
|
+
*
|
|
4
|
+
* 每日刷新 3 张悬赏令(铜/银/金),为日常使用增加目标感和方向性。
|
|
5
|
+
* 使用基于 UID + 日期的种子随机,确保同一用户同一天结果一致。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createHash } from 'crypto';
|
|
9
|
+
import type {
|
|
10
|
+
Bounty, BountyTier, BountyCondition, BountyReward,
|
|
11
|
+
DailyBountyState, BountyHistory, UserProfile, MonsterQuality,
|
|
12
|
+
DropResult,
|
|
13
|
+
} from './types.ts';
|
|
14
|
+
|
|
15
|
+
// ============ 悬赏令模板池 ============
|
|
16
|
+
|
|
17
|
+
interface BountyTemplate {
|
|
18
|
+
id: string;
|
|
19
|
+
tier: BountyTier;
|
|
20
|
+
descriptionTemplate: string;
|
|
21
|
+
reward: BountyReward;
|
|
22
|
+
condition: BountyCondition;
|
|
23
|
+
hasProductPlaceholder?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const BRONZE_POOL: BountyTemplate[] = [
|
|
27
|
+
{ id: 'B001', tier: 'bronze', descriptionTemplate: '成功执行 3 次任意 dws 命令', reward: { exp: 15 }, condition: { type: 'command', count: 3 } },
|
|
28
|
+
{ id: 'B002', tier: 'bronze', descriptionTemplate: '使用 {product} 成功执行 1 次命令', reward: { exp: 10 }, condition: { type: 'command', count: 1 }, hasProductPlaceholder: true },
|
|
29
|
+
{ id: 'B003', tier: 'bronze', descriptionTemplate: '收服 1 只任意妖怪', reward: { exp: 10 }, condition: { type: 'capture', count: 1 } },
|
|
30
|
+
{ id: 'B004', tier: 'bronze', descriptionTemplate: '达成 3 连击', reward: { exp: 20 }, condition: { type: 'combo', count: 3 } },
|
|
31
|
+
{ id: 'B005', tier: 'bronze', descriptionTemplate: '使用 2 种不同产品的命令', reward: { exp: 15 }, condition: { type: 'product_variety', count: 2 } },
|
|
32
|
+
{ id: 'B006', tier: 'bronze', descriptionTemplate: '收服 1 只精良及以上妖怪', reward: { exp: 20 }, condition: { type: 'capture', qualityMin: 'fine', count: 1 } },
|
|
33
|
+
{ id: 'B007', tier: 'bronze', descriptionTemplate: '成功执行 5 次任意 dws 命令', reward: { exp: 25 }, condition: { type: 'command', count: 5 } },
|
|
34
|
+
{ id: 'B008', tier: 'bronze', descriptionTemplate: '收服 2 只任意妖怪(不含逃跑)', reward: { exp: 20 }, condition: { type: 'capture', count: 2 } },
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const SILVER_POOL: BountyTemplate[] = [
|
|
38
|
+
{ id: 'B101', tier: 'silver', descriptionTemplate: '收服 1 只稀有及以上妖怪', reward: { exp: 50 }, condition: { type: 'capture', qualityMin: 'rare', count: 1 } },
|
|
39
|
+
{ id: 'B102', tier: 'silver', descriptionTemplate: '达成 5 连击', reward: { exp: 40 }, condition: { type: 'combo', count: 5 } },
|
|
40
|
+
{ id: 'B103', tier: 'silver', descriptionTemplate: '使用 3 种不同产品的命令', reward: { exp: 35 }, condition: { type: 'product_variety', count: 3 } },
|
|
41
|
+
{ id: 'B104', tier: 'silver', descriptionTemplate: '收服 1 只与 {product} 关联的妖怪', reward: { exp: 30 }, condition: { type: 'capture', count: 1 }, hasProductPlaceholder: true },
|
|
42
|
+
{ id: 'B105', tier: 'silver', descriptionTemplate: '成功执行 10 次任意 dws 命令', reward: { exp: 50 }, condition: { type: 'command', count: 10 } },
|
|
43
|
+
{ id: 'B106', tier: 'silver', descriptionTemplate: '收服 3 只不同品质的妖怪', reward: { exp: 45 }, condition: { type: 'quality_variety', count: 3 } },
|
|
44
|
+
{ id: 'B107', tier: 'silver', descriptionTemplate: '触发 1 次神仙机缘', reward: { exp: 40 }, condition: { type: 'encounter', count: 1 } },
|
|
45
|
+
{ id: 'B108', tier: 'silver', descriptionTemplate: '收服 1 只图鉴中未拥有的新妖怪', reward: { exp: 60 }, condition: { type: 'capture', count: 1, isNew: true } },
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
const GOLD_POOL: BountyTemplate[] = [
|
|
49
|
+
{ id: 'B201', tier: 'gold', descriptionTemplate: '收服 1 只史诗及以上妖怪', reward: { exp: 100, treasureFragment: 'random' }, condition: { type: 'capture', qualityMin: 'epic', count: 1 } },
|
|
50
|
+
{ id: 'B202', tier: 'gold', descriptionTemplate: '达成 10 连击', reward: { exp: 80 }, condition: { type: 'combo', count: 10 } },
|
|
51
|
+
{ id: 'B203', tier: 'gold', descriptionTemplate: '使用 5 种不同产品的命令', reward: { exp: 70 }, condition: { type: 'product_variety', count: 5 } },
|
|
52
|
+
{ id: 'B204', tier: 'gold', descriptionTemplate: '单日收服 5 只不同妖怪', reward: { exp: 100 }, condition: { type: 'capture', count: 5 } },
|
|
53
|
+
{ id: 'B205', tier: 'gold', descriptionTemplate: '收服本周 UP 妖怪', reward: { exp: 120 }, condition: { type: 'capture', count: 1, isUpMonster: true } },
|
|
54
|
+
{ id: 'B206', tier: 'gold', descriptionTemplate: '触发 1 次赐宝机缘', reward: { exp: 80 }, condition: { type: 'encounter', count: 1 } },
|
|
55
|
+
{ id: 'B207', tier: 'gold', descriptionTemplate: '连续成功 15 次不中断', reward: { exp: 100 }, condition: { type: 'combo', count: 15 } },
|
|
56
|
+
{ id: 'B208', tier: 'gold', descriptionTemplate: '收服 2 只稀有及以上妖怪', reward: { exp: 90 }, condition: { type: 'capture', qualityMin: 'rare', count: 2 } },
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
const AVAILABLE_PRODUCTS = [
|
|
60
|
+
'aitable', 'calendar', 'chat', 'contact', 'todo',
|
|
61
|
+
'approval', 'attendance', 'report', 'ding', 'workbench', 'devdoc',
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
// ============ 种子随机 ============
|
|
65
|
+
|
|
66
|
+
function hashString(input: string): number {
|
|
67
|
+
const hash = createHash('sha256').update(input).digest();
|
|
68
|
+
return hash.readUInt32BE(0);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function seededRandom(seed: number): () => number {
|
|
72
|
+
let state = seed;
|
|
73
|
+
return () => {
|
|
74
|
+
state = (state * 1664525 + 1013904223) & 0xFFFFFFFF;
|
|
75
|
+
return (state >>> 0) / 0xFFFFFFFF;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function pickRandom<T>(pool: T[], rng: () => number): T {
|
|
80
|
+
const index = Math.floor(rng() * pool.length);
|
|
81
|
+
return pool[index];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function getDayKey(): string {
|
|
85
|
+
const now = new Date();
|
|
86
|
+
const offset = 8 * 60; // UTC+8
|
|
87
|
+
const local = new Date(now.getTime() + offset * 60 * 1000);
|
|
88
|
+
return local.toISOString().slice(0, 10);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ============ 核心逻辑 ============
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* 生成每日悬赏令
|
|
95
|
+
*/
|
|
96
|
+
export function generateDailyBounties(profile: UserProfile): DailyBountyState {
|
|
97
|
+
const today = getDayKey();
|
|
98
|
+
|
|
99
|
+
// 如果今天已经生成过,直接返回
|
|
100
|
+
if (profile.dailyBounty && profile.dailyBounty.date === today) {
|
|
101
|
+
return profile.dailyBounty;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const seed = hashString(profile.uidHash + today + 'bounty-salt');
|
|
105
|
+
const rng = seededRandom(seed);
|
|
106
|
+
|
|
107
|
+
const bronzeTemplate = pickRandom(BRONZE_POOL, rng);
|
|
108
|
+
const silverTemplate = pickRandom(SILVER_POOL, rng);
|
|
109
|
+
const goldTemplate = pickRandom(GOLD_POOL, rng);
|
|
110
|
+
|
|
111
|
+
const bounties = [bronzeTemplate, silverTemplate, goldTemplate].map(template =>
|
|
112
|
+
instantiateTemplate(template, rng)
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
const state: DailyBountyState = {
|
|
116
|
+
date: today,
|
|
117
|
+
bounties,
|
|
118
|
+
completedCount: 0,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
profile.dailyBounty = state;
|
|
122
|
+
return state;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* 将模板实例化为具体的悬赏令
|
|
127
|
+
*/
|
|
128
|
+
function instantiateTemplate(template: BountyTemplate, rng: () => number): Bounty {
|
|
129
|
+
let description = template.descriptionTemplate;
|
|
130
|
+
const condition = { ...template.condition };
|
|
131
|
+
|
|
132
|
+
if (template.hasProductPlaceholder) {
|
|
133
|
+
const product = pickRandom(AVAILABLE_PRODUCTS, rng);
|
|
134
|
+
description = description.replace('{product}', product);
|
|
135
|
+
condition.product = product;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
id: template.id,
|
|
140
|
+
tier: template.tier,
|
|
141
|
+
description,
|
|
142
|
+
target: condition.count,
|
|
143
|
+
current: 0,
|
|
144
|
+
completed: false,
|
|
145
|
+
reward: { ...template.reward },
|
|
146
|
+
condition,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ============ 品质比较 ============
|
|
151
|
+
|
|
152
|
+
const QUALITY_RANK: Record<MonsterQuality, number> = {
|
|
153
|
+
normal: 0,
|
|
154
|
+
fine: 1,
|
|
155
|
+
rare: 2,
|
|
156
|
+
epic: 3,
|
|
157
|
+
legendary: 4,
|
|
158
|
+
shiny: 5,
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
function isQualityAtLeast(actual: MonsterQuality, minimum: MonsterQuality): boolean {
|
|
162
|
+
return QUALITY_RANK[actual] >= QUALITY_RANK[minimum];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ============ 进度追踪 ============
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* 操作后更新悬赏令进度
|
|
169
|
+
*
|
|
170
|
+
* @returns 本次新完成的悬赏令列表
|
|
171
|
+
*/
|
|
172
|
+
export function updateBountyProgress(
|
|
173
|
+
profile: UserProfile,
|
|
174
|
+
context: BountyUpdateContext
|
|
175
|
+
): Bounty[] {
|
|
176
|
+
const bountyState = profile.dailyBounty;
|
|
177
|
+
if (!bountyState) return [];
|
|
178
|
+
|
|
179
|
+
const today = getDayKey();
|
|
180
|
+
if (bountyState.date !== today) return [];
|
|
181
|
+
|
|
182
|
+
const newlyCompleted: Bounty[] = [];
|
|
183
|
+
|
|
184
|
+
for (const bounty of bountyState.bounties) {
|
|
185
|
+
if (bounty.completed) continue;
|
|
186
|
+
|
|
187
|
+
const progressBefore = bounty.current;
|
|
188
|
+
updateSingleBountyProgress(bounty, context);
|
|
189
|
+
|
|
190
|
+
if (!bounty.completed && bounty.current >= bounty.target) {
|
|
191
|
+
bounty.completed = true;
|
|
192
|
+
bountyState.completedCount += 1;
|
|
193
|
+
newlyCompleted.push(bounty);
|
|
194
|
+
|
|
195
|
+
// 发放奖励
|
|
196
|
+
profile.totalExp += bounty.reward.exp;
|
|
197
|
+
|
|
198
|
+
// 更新悬赏历史
|
|
199
|
+
profile.bountyHistory.totalCompleted += 1;
|
|
200
|
+
switch (bounty.tier) {
|
|
201
|
+
case 'bronze': profile.bountyHistory.bronzeCompleted += 1; break;
|
|
202
|
+
case 'silver': profile.bountyHistory.silverCompleted += 1; break;
|
|
203
|
+
case 'gold': profile.bountyHistory.goldCompleted += 1; break;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// 检查全部完成
|
|
209
|
+
if (bountyState.completedCount === 3) {
|
|
210
|
+
profile.bountyHistory.consecutiveFullClear += 1;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return newlyCompleted;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export interface BountyUpdateContext {
|
|
217
|
+
commandSuccess: boolean;
|
|
218
|
+
product: string;
|
|
219
|
+
dropResult?: DropResult;
|
|
220
|
+
encounterTriggered: boolean;
|
|
221
|
+
encounterType?: string;
|
|
222
|
+
currentCombo: number;
|
|
223
|
+
/** 今日使用过的不同产品集合 */
|
|
224
|
+
todayProducts: Set<string>;
|
|
225
|
+
/** 今日收服的不同品质集合 */
|
|
226
|
+
todayQualities: Set<MonsterQuality>;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function updateSingleBountyProgress(bounty: Bounty, ctx: BountyUpdateContext): void {
|
|
230
|
+
const { condition } = bounty;
|
|
231
|
+
|
|
232
|
+
switch (condition.type) {
|
|
233
|
+
case 'command':
|
|
234
|
+
if (ctx.commandSuccess) {
|
|
235
|
+
if (condition.product) {
|
|
236
|
+
if (ctx.product === condition.product) bounty.current += 1;
|
|
237
|
+
} else {
|
|
238
|
+
bounty.current += 1;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
break;
|
|
242
|
+
|
|
243
|
+
case 'capture':
|
|
244
|
+
if (ctx.dropResult && !ctx.dropResult.escaped) {
|
|
245
|
+
let matches = true;
|
|
246
|
+
|
|
247
|
+
if (condition.qualityMin) {
|
|
248
|
+
matches = matches && isQualityAtLeast(ctx.dropResult.monster.quality, condition.qualityMin);
|
|
249
|
+
}
|
|
250
|
+
if (condition.isUpMonster) {
|
|
251
|
+
matches = matches && ctx.dropResult.isUpMonster;
|
|
252
|
+
}
|
|
253
|
+
if (condition.isNew) {
|
|
254
|
+
matches = matches && ctx.dropResult.isNew;
|
|
255
|
+
}
|
|
256
|
+
if (condition.product) {
|
|
257
|
+
matches = matches && ctx.dropResult.monster.relatedProduct === condition.product;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (matches) bounty.current += 1;
|
|
261
|
+
}
|
|
262
|
+
break;
|
|
263
|
+
|
|
264
|
+
case 'combo':
|
|
265
|
+
// 连击类悬赏:直接用当前连击数作为进度
|
|
266
|
+
bounty.current = Math.min(ctx.currentCombo, bounty.target);
|
|
267
|
+
break;
|
|
268
|
+
|
|
269
|
+
case 'encounter':
|
|
270
|
+
if (ctx.encounterTriggered) {
|
|
271
|
+
// B206 金令要求赐宝机缘
|
|
272
|
+
if (bounty.id === 'B206') {
|
|
273
|
+
if (ctx.encounterType === 'treasure') bounty.current += 1;
|
|
274
|
+
} else {
|
|
275
|
+
bounty.current += 1;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
break;
|
|
279
|
+
|
|
280
|
+
case 'product_variety':
|
|
281
|
+
bounty.current = Math.min(ctx.todayProducts.size, bounty.target);
|
|
282
|
+
break;
|
|
283
|
+
|
|
284
|
+
case 'quality_variety':
|
|
285
|
+
bounty.current = Math.min(ctx.todayQualities.size, bounty.target);
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* 初始化默认的悬赏历史
|
|
292
|
+
*/
|
|
293
|
+
export function createDefaultBountyHistory(): BountyHistory {
|
|
294
|
+
return {
|
|
295
|
+
totalCompleted: 0,
|
|
296
|
+
bronzeCompleted: 0,
|
|
297
|
+
silverCompleted: 0,
|
|
298
|
+
goldCompleted: 0,
|
|
299
|
+
consecutiveFullClear: 0,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* 检查今日是否需要重置连续全清计数
|
|
305
|
+
* (如果昨天没有全部完成,重置为 0)
|
|
306
|
+
*/
|
|
307
|
+
export function checkBountyDayReset(profile: UserProfile): void {
|
|
308
|
+
const today = getDayKey();
|
|
309
|
+
if (profile.dailyBounty && profile.dailyBounty.date !== today) {
|
|
310
|
+
// 昨天没全清
|
|
311
|
+
if (profile.dailyBounty.completedCount < 3) {
|
|
312
|
+
profile.bountyHistory.consecutiveFullClear = 0;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 聊天命令注册
|
|
3
|
+
*
|
|
4
|
+
* 处理 /修行 /图鉴 /成就 /法宝 /妖魔榜 /机缘 /保底 /使用 /炫耀 等命令。
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { UserProfile, UserCollection } from './types.ts';
|
|
8
|
+
import {
|
|
9
|
+
renderProfilePanel,
|
|
10
|
+
renderCollectionPanel,
|
|
11
|
+
renderAchievementPanel,
|
|
12
|
+
renderTreasurePanel,
|
|
13
|
+
renderPityPanel,
|
|
14
|
+
renderEncounterPanel,
|
|
15
|
+
renderLeaderboard,
|
|
16
|
+
renderTreasureUse,
|
|
17
|
+
renderShowOff,
|
|
18
|
+
renderBountyPanel,
|
|
19
|
+
renderEventPanel,
|
|
20
|
+
} from './renderer.ts';
|
|
21
|
+
import { consumeTreasure } from './treasure-system.ts';
|
|
22
|
+
import { getNextLevel } from './level-system.ts';
|
|
23
|
+
import { getMonsterById, getAllMonsters } from './monster-pool.ts';
|
|
24
|
+
import type { Monster } from './types.ts';
|
|
25
|
+
import { QUALITY_LABELS } from './types.ts';
|
|
26
|
+
|
|
27
|
+
/** 养成系统支持的命令列表 */
|
|
28
|
+
export const GAMIFICATION_COMMANDS = [
|
|
29
|
+
'/修行', '/图鉴', '/成就', '/法宝', '/使用',
|
|
30
|
+
'/妖魔榜', '/机缘', '/保底', '/炫耀',
|
|
31
|
+
'/悬赏', '/事件', '/西游',
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 检查消息是否是养成系统命令
|
|
36
|
+
*/
|
|
37
|
+
export function isGamificationCommand(text: string): boolean {
|
|
38
|
+
const trimmed = text.trim();
|
|
39
|
+
return GAMIFICATION_COMMANDS.some(cmd => trimmed.startsWith(cmd));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 处理养成系统命令,返回 Markdown 响应
|
|
44
|
+
*
|
|
45
|
+
* @returns Markdown 字符串,或 null(不是养成系统命令)
|
|
46
|
+
*/
|
|
47
|
+
export function handleGamificationCommand(
|
|
48
|
+
text: string,
|
|
49
|
+
profile: UserProfile,
|
|
50
|
+
collection: UserCollection,
|
|
51
|
+
saveCallback: () => void
|
|
52
|
+
): string | null {
|
|
53
|
+
const trimmed = text.trim();
|
|
54
|
+
|
|
55
|
+
if (trimmed === '/修行') {
|
|
56
|
+
return renderProfilePanel(profile, collection);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (trimmed === '/图鉴') {
|
|
60
|
+
return renderCollectionPanel(collection);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (trimmed.startsWith('/图鉴 ')) {
|
|
64
|
+
const monsterName = trimmed.slice(4).trim();
|
|
65
|
+
return renderMonsterDetail(monsterName, collection);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (trimmed === '/成就') {
|
|
69
|
+
return renderAchievementPanel(profile);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (trimmed === '/法宝') {
|
|
73
|
+
return renderTreasurePanel(profile);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (trimmed.startsWith('/使用 ')) {
|
|
77
|
+
const treasureName = trimmed.slice(4).trim();
|
|
78
|
+
return handleUseTreasure(profile, treasureName, saveCallback);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (trimmed === '/妖魔榜') {
|
|
82
|
+
return renderLeaderboard(profile, collection);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (trimmed === '/机缘') {
|
|
86
|
+
return renderEncounterPanel(profile);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (trimmed === '/保底') {
|
|
90
|
+
return renderPityPanel(profile);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (trimmed === '/炫耀') {
|
|
94
|
+
return renderShowOff(profile, collection);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (trimmed === '/悬赏' || trimmed === '/悬赏 历史') {
|
|
98
|
+
return renderBountyPanel(profile);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (trimmed === '/事件' || trimmed === '/事件 历史') {
|
|
102
|
+
return renderEventPanel(profile);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ===== 帮助面板 & 一键开关 =====
|
|
106
|
+
if (trimmed === '/西游' || trimmed === '/西游 --h' || trimmed === '/西游 -h' || trimmed === '/西游 help') {
|
|
107
|
+
const statusText = profile.settings.enabled ? '✅ 已开启' : '❌ 已关闭';
|
|
108
|
+
return [
|
|
109
|
+
`### 🐒 西游妖魔榜 · 命令一览`,
|
|
110
|
+
``,
|
|
111
|
+
`当前状态:${statusText}`,
|
|
112
|
+
``,
|
|
113
|
+
`| 命令 | 功能 |`,
|
|
114
|
+
`|------|------|`,
|
|
115
|
+
`| \`/修行\` | 查看个人修行面板(等级、修行值、连击、签到等) |`,
|
|
116
|
+
`| \`/图鉴\` | 查看妖怪图鉴收集进度 |`,
|
|
117
|
+
`| \`/图鉴 妖怪名\` | 查看指定妖怪详情 |`,
|
|
118
|
+
`| \`/成就\` | 查看成就列表及解锁状态 |`,
|
|
119
|
+
`| \`/法宝\` | 查看法宝背包 |`,
|
|
120
|
+
`| \`/使用 法宝名\` | 使用一次性法宝(如蟠桃、人参果) |`,
|
|
121
|
+
`| \`/妖魔榜\` | 查看本周 UP 妖怪、掉落统计、保底状态 |`,
|
|
122
|
+
`| \`/机缘\` | 查看神仙机缘录 |`,
|
|
123
|
+
`| \`/保底\` | 查看保底计数器详情(含软保底状态) |`,
|
|
124
|
+
`| \`/炫耀\` | 生成炫耀卡片 |`,
|
|
125
|
+
`| \`/悬赏\` | 查看今日悬赏令及历史统计 |`,
|
|
126
|
+
`| \`/事件\` | 查看当前活跃事件及事件统计 |`,
|
|
127
|
+
`| \`/西游 开启\` | 开启养成系统 |`,
|
|
128
|
+
`| \`/西游 关闭\` | 关闭养成系统 |`,
|
|
129
|
+
``,
|
|
130
|
+
`> 关闭后,dws 命令执行不再触发降妖掉落,但已有数据会保留。`,
|
|
131
|
+
].join('\n');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (trimmed === '/西游 开启' || trimmed === '/西游 开') {
|
|
135
|
+
profile.settings.enabled = true;
|
|
136
|
+
saveCallback();
|
|
137
|
+
return [
|
|
138
|
+
`### 🐒 西游妖魔榜 · 已开启`,
|
|
139
|
+
``,
|
|
140
|
+
`✅ 养成系统已开启!每次使用钉钉产品能力都会触发降妖掉落。`,
|
|
141
|
+
``,
|
|
142
|
+
`发送 \`/修行\` 查看你的修行面板。`,
|
|
143
|
+
].join('\n');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (trimmed === '/西游 关闭' || trimmed === '/西游 关') {
|
|
147
|
+
profile.settings.enabled = false;
|
|
148
|
+
saveCallback();
|
|
149
|
+
return [
|
|
150
|
+
`### 🐒 西游妖魔榜 · 已关闭`,
|
|
151
|
+
``,
|
|
152
|
+
`❌ 养成系统已关闭。dws 命令执行不再触发降妖掉落。`,
|
|
153
|
+
``,
|
|
154
|
+
`你的修行数据已保留,随时可以发送 \`/西游 开启\` 重新开启。`,
|
|
155
|
+
].join('\n');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* 渲染妖怪详情
|
|
163
|
+
*/
|
|
164
|
+
function renderMonsterDetail(monsterName: string, collection: UserCollection): string {
|
|
165
|
+
// 在所有妖怪中搜索
|
|
166
|
+
const allMonsters = getAllMonsters();
|
|
167
|
+
const monster = allMonsters.find(m => m.name === monsterName);
|
|
168
|
+
|
|
169
|
+
if (!monster) {
|
|
170
|
+
return `未找到名为「${monsterName}」的妖怪。`;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const entry = collection.entries.find(e => e.monsterId === monster.id && !e.isShiny);
|
|
174
|
+
const shinyEntry = collection.entries.find(e => e.monsterId === monster.id && e.isShiny);
|
|
175
|
+
|
|
176
|
+
const lines = [
|
|
177
|
+
`### ${QUALITY_LABELS[monster.quality]} ${monster.name}`,
|
|
178
|
+
'',
|
|
179
|
+
`- **出处**:${monster.origin}`,
|
|
180
|
+
`- **关联产品**:${monster.relatedProduct ?? '任意'}`,
|
|
181
|
+
`- **收服台词**:"${monster.captureQuote}"`,
|
|
182
|
+
'',
|
|
183
|
+
];
|
|
184
|
+
|
|
185
|
+
if (entry) {
|
|
186
|
+
lines.push(
|
|
187
|
+
`✅ **已收服**`,
|
|
188
|
+
`- 首次收服:${new Date(entry.firstCapturedAt).toLocaleDateString('zh-CN')}`,
|
|
189
|
+
`- 收服次数:${entry.captureCount}`,
|
|
190
|
+
);
|
|
191
|
+
} else {
|
|
192
|
+
lines.push(`❌ **未收服**`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (shinyEntry) {
|
|
196
|
+
lines.push('', `✨ **闪光变体已收服**`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return lines.join('\n');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* 处理使用法宝命令
|
|
204
|
+
*/
|
|
205
|
+
function handleUseTreasure(
|
|
206
|
+
profile: UserProfile,
|
|
207
|
+
treasureName: string,
|
|
208
|
+
saveCallback: () => void
|
|
209
|
+
): string {
|
|
210
|
+
const result = consumeTreasure(profile, treasureName);
|
|
211
|
+
|
|
212
|
+
if (!result) {
|
|
213
|
+
return `无法使用「${treasureName}」。可能原因:未拥有、已使用、或不是一次性法宝。\n\n发送 \`/法宝\` 查看背包。`;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const nextLevel = getNextLevel(profile.level);
|
|
217
|
+
const nextLevelExp = nextLevel?.requiredExp ?? null;
|
|
218
|
+
|
|
219
|
+
// 保存数据
|
|
220
|
+
saveCallback();
|
|
221
|
+
|
|
222
|
+
return renderTreasureUse(treasureName, result.expGained, profile.totalExp, nextLevelExp);
|
|
223
|
+
}
|