dsh-blackjack 0.1.5 → 0.2.1
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/README.md +43 -26
- package/blackjack.cordis.yml +10 -0
- package/dist/api.d.ts +27 -3
- package/dist/api.js +43 -3
- package/dist/client/Table.d.ts +10 -2
- package/dist/client/Table.js +33 -37
- package/dist/client/index.d.ts +20 -0
- package/dist/client/index.js +91 -9
- package/dist/client.js +12 -3
- package/dist/commands.d.ts +4 -1
- package/dist/commands.js +153 -79
- package/dist/config.d.ts +2 -0
- package/dist/config.js +5 -0
- package/dist/copy.d.ts +16 -2
- package/dist/copy.js +14 -19
- package/dist/i18n/en.d.ts +112 -0
- package/dist/i18n/en.js +172 -0
- package/dist/i18n/index.d.ts +4 -0
- package/dist/i18n/index.js +6 -0
- package/dist/i18n/messages.d.ts +429 -0
- package/dist/i18n/messages.js +33 -0
- package/dist/i18n/zh.d.ts +112 -0
- package/dist/i18n/zh.js +170 -0
- package/dist/index.js +9 -2
- package/dist/render.d.ts +3 -3
- package/dist/render.js +19 -31
- package/dist/router.d.ts +31 -3
- package/dist/router.js +35 -12
- package/package.json +2 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 面向玩家的全部文案,按 locale 分成两份实现。
|
|
3
|
+
*
|
|
4
|
+
* 为什么是"接口 + 两个实现"而不是 t(key) 查表:`satisfies Messages` 让漏翻一条
|
|
5
|
+
* 直接编译失败,根本发不出去;查表方案漏翻通常只是静默回退或输出 key。这个项目
|
|
6
|
+
* 已经因为"同一句话在两处不同步"吃过两次亏,多语言会把这个风险乘以二。
|
|
7
|
+
*
|
|
8
|
+
* 为什么每条是函数而不是模板串:中文没有复数、英文有(1 hand / 2 hands),语序
|
|
9
|
+
* 也不同。函数让两种语言各自把话说顺。
|
|
10
|
+
*/
|
|
11
|
+
export const LOCALES = ['zh', 'en'];
|
|
12
|
+
/**
|
|
13
|
+
* 英文文案的红线,对标 render.ts 的 `BANNED`。
|
|
14
|
+
*
|
|
15
|
+
* 曾经写作 `/gambl|casino|\bwager\b|\bbet\b|real money/i`,只挡五个字面 token,
|
|
16
|
+
* 比中文那条松得多,而且松在两个地方:
|
|
17
|
+
*
|
|
18
|
+
* 一是词形。`\bbet\b` 的词边界在 bet 之后就断了,bets / betting / bettor 全部
|
|
19
|
+
* 漏网——而这三个正是英文里真正会写出来的形态,光秃秃的 "bet" 反而少见。
|
|
20
|
+
*
|
|
21
|
+
* 二是概念。中文的 `赢钱` 禁的是"把赢来的东西说成钱"这件事本身,英文这边却
|
|
22
|
+
* 只禁 real money,cash out / winnings / payout / jackpot 一律放行。这个项目的
|
|
23
|
+
* 全部合规站位就建立在"赢来的 CHIP 不是钱、不可提现"上,用 payout 或 winnings
|
|
24
|
+
* 描述结算,等于在文案层面把这条站位拆掉。
|
|
25
|
+
*
|
|
26
|
+
* 收紧的时机是现在而不是以后:后面还有五个任务要写新的英文文案,每一条都按
|
|
27
|
+
* 这条正则验收。先把线划到位,比等文案写完再回头重审所有文案便宜得多。
|
|
28
|
+
*
|
|
29
|
+
* 词边界要当心:`bett?` 单独用会误伤 better/betterment,所以后缀写成显式的
|
|
30
|
+
* `(?:s|ing|or|ors)?` 再收一个 `\b`。i18n.test.ts 里有一张"必须拦下"和一张
|
|
31
|
+
* "不许误伤"的清单钉住这两侧。
|
|
32
|
+
*/
|
|
33
|
+
export const BANNED_EN = /gambl|casino|\bwager(?:s|ing|ed)?\b|\bbett?(?:s|ing|or|ors)?\b|real money|\bcash(?:ing|es|ed)?[\s-]?out|winnings|payout|jackpot/i;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
export declare const zh: {
|
|
2
|
+
freeStakeLine: (bet: number) => string;
|
|
3
|
+
raiseStakeLine: (bet: number) => string;
|
|
4
|
+
freeWin: (payout: number) => string;
|
|
5
|
+
freeLose: () => string;
|
|
6
|
+
freePush: () => string;
|
|
7
|
+
raiseWin: (payout: number, bet: number) => string;
|
|
8
|
+
raiseLose: (bet: number) => string;
|
|
9
|
+
raisePush: (bet: number) => string;
|
|
10
|
+
handLabel: () => string;
|
|
11
|
+
splitHandLabel: (handIndex: number) => string;
|
|
12
|
+
dealerLabel: () => string;
|
|
13
|
+
totalInParens: (total: number, isSoft: boolean) => string;
|
|
14
|
+
doubled: () => string;
|
|
15
|
+
actionsPrefix: () => string;
|
|
16
|
+
actionLabels: {
|
|
17
|
+
hit: string;
|
|
18
|
+
stand: string;
|
|
19
|
+
double: string;
|
|
20
|
+
split: string;
|
|
21
|
+
insure: string;
|
|
22
|
+
decline: string;
|
|
23
|
+
};
|
|
24
|
+
actionsSeparator: () => string;
|
|
25
|
+
balanceLine: (chips: number, exchangedChips: number) => string;
|
|
26
|
+
freeHandsLine: (freeHandsRemaining: number, seasonId: string) => string;
|
|
27
|
+
thresholdLine: (exchangeThresholdChips: number) => string;
|
|
28
|
+
pointsLine: (points: number) => string;
|
|
29
|
+
poolEmptyNotice: () => string;
|
|
30
|
+
pointsGrantedNotice: (points: number) => string;
|
|
31
|
+
chipValidityNotice: () => string;
|
|
32
|
+
consentText: () => string;
|
|
33
|
+
agreeHint: () => string;
|
|
34
|
+
noRoundHint: () => string;
|
|
35
|
+
commandUsage: () => string;
|
|
36
|
+
actionDescriptions: {
|
|
37
|
+
hit: string;
|
|
38
|
+
stand: string;
|
|
39
|
+
double: string;
|
|
40
|
+
split: string;
|
|
41
|
+
insure: string;
|
|
42
|
+
decline: string;
|
|
43
|
+
};
|
|
44
|
+
blackjackCommandDescription: () => string;
|
|
45
|
+
genericFailure: () => string;
|
|
46
|
+
serviceUnreachable: () => string;
|
|
47
|
+
dailyFreeHandsExhausted: () => string;
|
|
48
|
+
belowExchangeThreshold: (thresholdChips: number) => string;
|
|
49
|
+
insufficientBalance: () => string;
|
|
50
|
+
roundAlreadyInProgress: () => string;
|
|
51
|
+
githubBindingRequired: () => string;
|
|
52
|
+
seasonCapReached: (capChips: number, exchangedThisSeasonChips: number) => string;
|
|
53
|
+
exchangeUnavailable: () => string;
|
|
54
|
+
betAmountAboveLimit: (maxChips: number) => string;
|
|
55
|
+
illegalAction: () => string;
|
|
56
|
+
registerRateLimited: () => string;
|
|
57
|
+
unknownPlayerToken: () => string;
|
|
58
|
+
agreeWelcome: () => string;
|
|
59
|
+
invalidBetAmount: () => string;
|
|
60
|
+
pairingInstructions: (url: string, minutes: number) => string;
|
|
61
|
+
exchangeAmountPrompt: (githubLogin: string, chips: number, exchangeThresholdChips: number) => string;
|
|
62
|
+
invalidExchangeAmount: () => string;
|
|
63
|
+
exchangeSuccess: (chips: number, exchangedChips: number) => string;
|
|
64
|
+
exchangedQuotaNote: (inactiveRecycleDays: number, expiresAtSeasonEnd: boolean) => string;
|
|
65
|
+
resetConfirmPrompt: () => string;
|
|
66
|
+
resetDone: () => string;
|
|
67
|
+
unknownSubcommand: (sub: string, usage: string) => string;
|
|
68
|
+
poolSwitchNotice: () => string;
|
|
69
|
+
poolFallbackFailed: (reason: string) => string;
|
|
70
|
+
poolFallbackBusy: () => string;
|
|
71
|
+
noCredentialHint: () => string;
|
|
72
|
+
hiddenCardLabel: () => string;
|
|
73
|
+
tableHandLabel: () => string;
|
|
74
|
+
tableSplitHandLabel: (handIndex: number) => string;
|
|
75
|
+
tableDealerLabel: () => string;
|
|
76
|
+
tableActionLabels: {
|
|
77
|
+
hit: string;
|
|
78
|
+
stand: string;
|
|
79
|
+
double: string;
|
|
80
|
+
split: string;
|
|
81
|
+
insure: string;
|
|
82
|
+
decline: string;
|
|
83
|
+
};
|
|
84
|
+
tableBalanceHeader: (chips: number, freeHandsRemaining: number) => string;
|
|
85
|
+
tablePointsSuffix: (points: number) => string;
|
|
86
|
+
tableExchangedSuffix: (exchangedChips: number) => string;
|
|
87
|
+
tableExchangeThresholdReached: (exchangeThresholdChips: number) => string;
|
|
88
|
+
tableExchangeThresholdShort: (shortChips: number, exchangeThresholdChips: number) => string;
|
|
89
|
+
tableExchangedQuotaNote: (exchangedChips: number) => string;
|
|
90
|
+
tablePoolEmptyNotice: () => string;
|
|
91
|
+
actionShortfallHint: (costChips: number) => string;
|
|
92
|
+
tableCostSuffix: (costChips: number) => string;
|
|
93
|
+
betAmountMissing: () => string;
|
|
94
|
+
betAmountExceedsCap: (maxChips: number) => string;
|
|
95
|
+
betAmountExceedsBalance: () => string;
|
|
96
|
+
customBetToggle: () => string;
|
|
97
|
+
customBetSubmit: () => string;
|
|
98
|
+
customBetRange: (maxChips: number) => string;
|
|
99
|
+
noActiveRoundNotice: () => string;
|
|
100
|
+
freeHandsGroupTitle: () => string;
|
|
101
|
+
freeHandsRemainingToday: (freeHandsRemaining: number) => string;
|
|
102
|
+
freeHandsExhaustedToday: () => string;
|
|
103
|
+
freeHandExplainer: (baseChips: number, naturalBlackjackPayoutChips: number) => string;
|
|
104
|
+
startFreeHandButton: () => string;
|
|
105
|
+
raiseGroupTitle: () => string;
|
|
106
|
+
insufficientBalanceSuffix: () => string;
|
|
107
|
+
raiseExplainer: () => string;
|
|
108
|
+
rematchFreeLabel: () => string;
|
|
109
|
+
dealButtonFreeHandsSuffix: (freeHandsRemaining: number) => string;
|
|
110
|
+
dismissButton: () => string;
|
|
111
|
+
actionFailedNotice: () => string;
|
|
112
|
+
};
|
package/dist/i18n/zh.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
const n = (v) => v.toLocaleString('en-US');
|
|
2
|
+
export const zh = {
|
|
3
|
+
freeStakeLine: (bet) => `这是免费的一局:赢了拿 ${n(bet)} CHIP,输了不损失任何东西。`,
|
|
4
|
+
raiseStakeLine: (bet) => `本局已加注 ${n(bet)} CHIP,从你的余额里出。`,
|
|
5
|
+
freeWin: (payout) => `赢了,拿到 ${n(payout)} CHIP。`,
|
|
6
|
+
freeLose: () => '这手输了。免费手不损失任何东西。',
|
|
7
|
+
freePush: () => '平局。这手不损失也不获得。',
|
|
8
|
+
raiseWin: (payout, bet) => `赢了,拿回 ${n(payout)} CHIP(加注 ${n(bet)})。`,
|
|
9
|
+
raiseLose: (bet) => `这手输了,加注的 ${n(bet)} CHIP 收不回。`,
|
|
10
|
+
raisePush: (bet) => `平局。加注的 ${n(bet)} CHIP 原样退回。`,
|
|
11
|
+
handLabel: () => '你的牌:',
|
|
12
|
+
splitHandLabel: (handIndex) => `你的第 ${handIndex} 手:`,
|
|
13
|
+
dealerLabel: () => '庄家:',
|
|
14
|
+
totalInParens: (total, isSoft) => `(${isSoft ? '软' : ''}${total})`,
|
|
15
|
+
doubled: () => ' [双倍]',
|
|
16
|
+
actionsPrefix: () => '可选:',
|
|
17
|
+
actionLabels: {
|
|
18
|
+
hit: '/bj-hit 要牌',
|
|
19
|
+
stand: '/bj-stand 停牌',
|
|
20
|
+
double: '/bj-double 双倍',
|
|
21
|
+
split: '/bj-split 分牌',
|
|
22
|
+
insure: '/bj-insure 买保险',
|
|
23
|
+
decline: '/bj-decline 不买保险',
|
|
24
|
+
},
|
|
25
|
+
actionsSeparator: () => ' ',
|
|
26
|
+
balanceLine: (chips, exchangedChips) => `余额:${n(chips)} CHIP 已兑换额度:${n(exchangedChips)} CHIP`,
|
|
27
|
+
freeHandsLine: (freeHandsRemaining, seasonId) => `今日剩余免费手:${freeHandsRemaining} 赛季:${seasonId}`,
|
|
28
|
+
thresholdLine: (exchangeThresholdChips) => `兑换门槛:${n(exchangeThresholdChips)} CHIP(达标后可用 /blackjack exchange 兑换)`,
|
|
29
|
+
pointsLine: (points) => `积分:${n(points)}`,
|
|
30
|
+
poolEmptyNotice: () => '提示:本期奖池已发完,现在赢牌将发放积分。',
|
|
31
|
+
pointsGrantedNotice: (points) => `本期奖池已发完,改为发放 ${n(points)} 积分。`,
|
|
32
|
+
chipValidityNotice: () => '(赛季结束时,未达兑换门槛的 CHIP 会折算为积分并回收;达标的结转)',
|
|
33
|
+
// 第四条的措辞是运营者单独批准改的,不属于本分支"中文只搬运不改写"的范围
|
|
34
|
+
// (改动理由与授权见 messages.ts 里 consentText 的文档)。原句只披露了赛季末
|
|
35
|
+
// 回收,而服务端 jobs.ts 的 recycleInactive 每天 00:15 跑一次,对
|
|
36
|
+
// `inactive_recycle_days`(当前 7 天)内无游戏且无消费的账户调 recycleAll——
|
|
37
|
+
// 游戏余额和已兑换额度**一起清零**,而且是**赛季中途**就发生。一个赢了 CHIP、
|
|
38
|
+
// 停手一周、回来发现余额归零的玩家,在他点"同意"的那一刻什么都没被告知。
|
|
39
|
+
// 仍然不写天数:`inactive_recycle_days` 是运营者可调的配置,写死数字就是又
|
|
40
|
+
// 一次"文案比代码更精确"的假承诺(这个项目已经为这个形状翻过两次车)。
|
|
41
|
+
consentText: () => [
|
|
42
|
+
'【首次使用请先阅读】',
|
|
43
|
+
'· 这是社区开发者发布的第三方小游戏,与任何模型厂商均无关联,不代表其立场。',
|
|
44
|
+
'· 参与免费:每日有限次免费手,输了不扣任何东西,你自己的密钥与余额不受影响。',
|
|
45
|
+
'· 赢得的 CHIP 只能在本插件内兑换为模型额度消费:不可提现、不可转让、不可转入任何厂商账户。',
|
|
46
|
+
'· 奖励有赛季有效期,赛季结束后、或长期不活跃时,未使用的部分按规则回收。',
|
|
47
|
+
'',
|
|
48
|
+
'阅读并同意以上内容后,执行 /blackjack agree confirm 开始。',
|
|
49
|
+
].join('\n'),
|
|
50
|
+
agreeHint: () => '请先阅读须知并执行 /blackjack agree 后再使用。',
|
|
51
|
+
noRoundHint: () => '当前没有进行中的牌局,先用 /blackjack deal 开一局。',
|
|
52
|
+
commandUsage: () => 'agree | deal [注额] | exchange [数量] | reset',
|
|
53
|
+
actionDescriptions: {
|
|
54
|
+
hit: '21 点:向牌桌要一张牌',
|
|
55
|
+
stand: '21 点:停牌,进入结算',
|
|
56
|
+
double: '21 点:加倍注额并再要一张牌',
|
|
57
|
+
split: '21 点:把一对相同点数的牌分成两手',
|
|
58
|
+
insure: '21 点:庄家明牌为 A 时购买保险',
|
|
59
|
+
decline: '21 点:不购买保险',
|
|
60
|
+
},
|
|
61
|
+
blackjackCommandDescription: () => '21 点牌桌(需先进入会话):查看状态、开局、兑换额度、重置凭证',
|
|
62
|
+
genericFailure: () => '操作没有成功,请稍后再试。',
|
|
63
|
+
serviceUnreachable: () => '暂时连不上牌桌服务,请稍后再试。',
|
|
64
|
+
dailyFreeHandsExhausted: () => '今天的免费手用完了,明天再来。',
|
|
65
|
+
belowExchangeThreshold: (thresholdChips) => `余额还没到兑换门槛(需要 ${n(thresholdChips)} CHIP)。`,
|
|
66
|
+
insufficientBalance: () => '余额不足以完成这个动作。',
|
|
67
|
+
roundAlreadyInProgress: () => '已经有一局在进行中。',
|
|
68
|
+
// 不建议玩家重新执行 /blackjack exchange——那正是触发这个 code 的那条命令,
|
|
69
|
+
// 重新执行只会命中"请补上数量"的提示,不是一条真的能走通的恢复路径(详见
|
|
70
|
+
// messages.ts 里这个方法的文档:核实过服务端没有任何路径能让这个状态发生)。
|
|
71
|
+
githubBindingRequired: () => '这个钱包的 GitHub 绑定状态有问题,暂时无法直接兑换。请稍后再试;如果一直这样,请联系运营者处理。',
|
|
72
|
+
seasonCapReached: (capChips, exchangedThisSeasonChips) => `这笔会超过本赛季兑换总额上限(上限 ${n(capChips)} CHIP,本赛季已兑换 ${n(exchangedThisSeasonChips)} CHIP)。`,
|
|
73
|
+
exchangeUnavailable: () => '兑换功能暂时不可用(运营者部署未配置完整),牌局本身不受影响,请稍后再试。',
|
|
74
|
+
betAmountAboveLimit: (maxChips) => `注额超过上限,最多可下注 ${n(maxChips)} CHIP。`,
|
|
75
|
+
illegalAction: () => '这个动作现在不可用了,牌局状态可能已经变化,请用 /blackjack 查看最新状态。',
|
|
76
|
+
registerRateLimited: () => '当前网络注册次数过多,请稍后再试。',
|
|
77
|
+
// 两条命令都要点名:只 reset 不 agree 拿不到新令牌,只 agree 不 reset 会被
|
|
78
|
+
// identity.accept() 的 `if (existing) return existing` 原样退回那枚坏令牌
|
|
79
|
+
// (见 messages.ts 里这条方法的文档,恢复路径是照着代码核对过的)。
|
|
80
|
+
unknownPlayerToken: () => '这台机器上的牌桌凭证,服务端已经不认识了(服务端重置过,或者 serverUrl 改指到了另一个部署)。请先执行 /blackjack reset confirm 清掉它,再执行 /blackjack agree 重新开始。',
|
|
81
|
+
agreeWelcome: () => [
|
|
82
|
+
'已确认须知,欢迎上桌。',
|
|
83
|
+
'/blackjack deal 开始今天的免费手,或 /blackjack deal <注额> 用余额加注。',
|
|
84
|
+
].join('\n'),
|
|
85
|
+
invalidBetAmount: () => '注额必须是正整数。',
|
|
86
|
+
pairingInstructions: (url, minutes) => [
|
|
87
|
+
'首次兑换要先绑定 GitHub,只需做这一次。',
|
|
88
|
+
'',
|
|
89
|
+
'请在浏览器里打开这个链接:',
|
|
90
|
+
url,
|
|
91
|
+
'',
|
|
92
|
+
'打开后点「用 GitHub 继续」授权,然后在同一个页面填写要兑换的数量并提交。',
|
|
93
|
+
`链接约 ${minutes} 分钟内有效;过期了就再执行一次 /blackjack exchange 拿一个新的。`,
|
|
94
|
+
'绑定完成后,以后直接用 /blackjack exchange <数量> 兑换,不用再开浏览器。',
|
|
95
|
+
].join('\n'),
|
|
96
|
+
exchangeAmountPrompt: (githubLogin, chips, exchangeThresholdChips) => [
|
|
97
|
+
`已绑定 GitHub 账号:${githubLogin}`,
|
|
98
|
+
`当前余额:${n(chips)} CHIP 兑换门槛:${n(exchangeThresholdChips)} CHIP`,
|
|
99
|
+
'请用 /blackjack exchange <数量> 指定要兑换的 CHIP 数量。',
|
|
100
|
+
].join('\n'),
|
|
101
|
+
invalidExchangeAmount: () => '兑换数量必须是正整数。',
|
|
102
|
+
exchangeSuccess: (chips, exchangedChips) => [
|
|
103
|
+
`已兑换 ${n(chips)} CHIP。`,
|
|
104
|
+
`累计已兑换额度:${n(exchangedChips)} CHIP`,
|
|
105
|
+
'这份额度会在你自己的模型额度耗尽时,自动接管那一次失败的请求;平时不会动用,你不需要做任何事。',
|
|
106
|
+
].join('\n'),
|
|
107
|
+
// 原句从服务端 exchangedQuotaNote() 搬运,字节不变——这是 Task 6 的要求:
|
|
108
|
+
// 这句话的分寸是踩过两次坑才校准的(有效期至本赛季末是一句代码不兑现的
|
|
109
|
+
// 承诺),移动过程中不能顺手"顺一顺"措辞。
|
|
110
|
+
// expiresAtSeasonEnd 为 true 时只把中间那半句拿掉(承接的「但」跟着去掉,
|
|
111
|
+
// 否则前面没有转折可承接),不反过来说"会在赛季末清零"——服务端只说了"会
|
|
112
|
+
// 过期",没说什么时候、按什么比例,补一个细节出来就是再造一次假承诺。
|
|
113
|
+
// 今天服务端恒发 false(seasonSettle 从头到尾不碰 exchanged_micro),这条
|
|
114
|
+
// 分支是为"哪天真改了"准备的,见 messages.ts 里这条方法的文档。
|
|
115
|
+
exchangedQuotaNote: (inactiveRecycleDays, expiresAtSeasonEnd) => expiresAtSeasonEnd
|
|
116
|
+
? `已兑换额度仅限本插件内消费,不可提现、不可转让。连续 ${inactiveRecycleDays} 天无游戏且无消费的账户,余额会被回收进奖池。`
|
|
117
|
+
: `已兑换额度仅限本插件内消费,不可提现、不可转让。它不会因赛季结束而清零;但连续 ${inactiveRecycleDays} 天无游戏且无消费的账户,余额会被回收进奖池。`,
|
|
118
|
+
resetConfirmPrompt: () => '这会清除本机保存的牌桌凭证(不影响服务端记录,重新配对即可找回)。确认请执行 /blackjack reset confirm',
|
|
119
|
+
resetDone: () => '已清除本机凭证,可用 /blackjack agree 重新开始。',
|
|
120
|
+
unknownSubcommand: (sub, usage) => `未知子命令「${sub}」。可用:${usage}`,
|
|
121
|
+
poolSwitchNotice: () => '已切换至奖池余额继续本次请求。',
|
|
122
|
+
poolFallbackFailed: (reason) => `你自己的模型额度已耗尽,奖池接管了这次请求但也没成功(奖池:${reason})。已兑换额度没有被扣。`,
|
|
123
|
+
poolFallbackBusy: () => '你自己的模型额度已耗尽,奖池接管了这次请求,但同时进行的请求太多被挡下了。已兑换额度没有被扣,稍等一下再试。',
|
|
124
|
+
noCredentialHint: () => '续命只在你自己的模型额度耗尽时接管;当前是没有配置 key,所以用不到你已兑换的额度。',
|
|
125
|
+
hiddenCardLabel: () => '暗牌',
|
|
126
|
+
tableHandLabel: () => '你的牌',
|
|
127
|
+
tableSplitHandLabel: (handIndex) => `你的第 ${handIndex} 手`,
|
|
128
|
+
tableDealerLabel: () => '庄家',
|
|
129
|
+
tableActionLabels: {
|
|
130
|
+
hit: '要牌',
|
|
131
|
+
stand: '停牌',
|
|
132
|
+
double: '双倍',
|
|
133
|
+
split: '分牌',
|
|
134
|
+
insure: '买保险',
|
|
135
|
+
decline: '不买保险',
|
|
136
|
+
},
|
|
137
|
+
tableBalanceHeader: (chips, freeHandsRemaining) => `余额:${n(chips)} CHIP 今日剩余免费手:${freeHandsRemaining}`,
|
|
138
|
+
tablePointsSuffix: (points) => ` 积分:${n(points)}`,
|
|
139
|
+
tableExchangedSuffix: (exchangedChips) => ` 已兑换额度:${n(exchangedChips)} CHIP`,
|
|
140
|
+
tableExchangeThresholdReached: (exchangeThresholdChips) => `已达兑换门槛(${n(exchangeThresholdChips)} CHIP)· 用 /blackjack exchange 把 CHIP 换成模型额度`,
|
|
141
|
+
tableExchangeThresholdShort: (shortChips, exchangeThresholdChips) => `还差 ${n(shortChips)} CHIP 到兑换门槛(${n(exchangeThresholdChips)})· 过线后可用 /blackjack exchange 换成模型额度`,
|
|
142
|
+
tableExchangedQuotaNote: (exchangedChips) => `已兑换的 ${n(exchangedChips)} CHIP 会在你自己的模型额度耗尽时自动接管那一次请求,平时不会动用。`,
|
|
143
|
+
tablePoolEmptyNotice: () => '(本期奖池已发完,现在赢牌将发放积分)',
|
|
144
|
+
actionShortfallHint: (costChips) => `还要再出 ${n(costChips)} CHIP,余额不足`,
|
|
145
|
+
tableCostSuffix: (costChips) => `(${n(costChips)})`,
|
|
146
|
+
betAmountMissing: () => '请输入注额。',
|
|
147
|
+
betAmountExceedsCap: (maxChips) => `单局最多 ${n(maxChips)} CHIP。`,
|
|
148
|
+
betAmountExceedsBalance: () => '余额不足以下这个注。',
|
|
149
|
+
customBetToggle: () => '自定义注额',
|
|
150
|
+
customBetSubmit: () => '开始',
|
|
151
|
+
customBetRange: (maxChips) => `1 ~ ${n(maxChips)} CHIP`,
|
|
152
|
+
noActiveRoundNotice: () => '当前没有进行中的牌局。',
|
|
153
|
+
freeHandsGroupTitle: () => '免费手',
|
|
154
|
+
freeHandsRemainingToday: (freeHandsRemaining) => ` · 今日剩 ${freeHandsRemaining} 手`,
|
|
155
|
+
freeHandsExhaustedToday: () => ' · 今日已用完,明天再来',
|
|
156
|
+
freeHandExplainer: (baseChips, naturalBlackjackPayoutChips) => `不用出 CHIP。赢了拿 ${n(baseChips)} CHIP;头两张牌就凑成 21 点(A 配 10/J/Q/K)拿 ${n(naturalBlackjackPayoutChips)}。输了或平局都不损失任何东西。`,
|
|
157
|
+
startFreeHandButton: () => '开始免费的一局',
|
|
158
|
+
raiseGroupTitle: () => '用余额加注',
|
|
159
|
+
insufficientBalanceSuffix: () => ' · 余额不足',
|
|
160
|
+
raiseExplainer: () => '加注的 CHIP 从你的余额里出,赢了翻倍,输了收不回。',
|
|
161
|
+
// 全角括号自己开自己闭:这颗按钮渲染出来是 `再来一局(免费)·剩 3 手`,
|
|
162
|
+
// 后缀 `dealButtonFreeHandsSuffix` 不带右括号(英文那版 `Play again (free)`
|
|
163
|
+
// 从一开始就是闭合的——同一颗按钮,两个 locale 对"这是不是个 bug"给了相反
|
|
164
|
+
// 的答案)。少一个右括号的写法在图形牌桌最常被点的按钮上活了这么久,唯一的
|
|
165
|
+
// 原因是从没有任何断言拼完整之后的整串;client.test.tsx 现在钉住渲染结果本身。
|
|
166
|
+
rematchFreeLabel: () => '再来一局(免费)',
|
|
167
|
+
dealButtonFreeHandsSuffix: (freeHandsRemaining) => `·剩 ${freeHandsRemaining} 手`,
|
|
168
|
+
dismissButton: () => '收起',
|
|
169
|
+
actionFailedNotice: () => '上一次操作没有成功,已为你刷新到最新状态。',
|
|
170
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { createApi } from './api.js';
|
|
|
2
2
|
import { registerCommands } from './commands.js';
|
|
3
3
|
import { Config, PLUGIN_NAME } from './config.js';
|
|
4
4
|
import { createIdentity } from './identity.js';
|
|
5
|
+
import { resolveMessages } from './i18n/index.js';
|
|
5
6
|
import { registerRouter } from './router.js';
|
|
6
7
|
import { registerRoutes } from './routes.js';
|
|
7
8
|
export const name = PLUGIN_NAME;
|
|
@@ -20,13 +21,19 @@ export function apply(ctx, config) {
|
|
|
20
21
|
return;
|
|
21
22
|
const api = createApi({ serverUrl: config.serverUrl });
|
|
22
23
|
const identity = createIdentity(ctx, api);
|
|
23
|
-
|
|
24
|
+
// Node 侧读不到宿主(dsh 客户端)的语言设置——`ctx.settings.get('locale')`
|
|
25
|
+
// 这个 namespace 的 owner 是个 client 插件,host 作用域里没有它的注册者,
|
|
26
|
+
// 三次尝试自己挂这个 namespace 都在模块解析上失败(细节见 spec)。所以命令
|
|
27
|
+
// 面的语言只能来自插件自己的配置项,不是"读不到就退化",是设计本身。
|
|
28
|
+
// 图形牌桌是另一条链路(浏览器能读到宿主设置),归下一个任务管,这里不碰。
|
|
29
|
+
const m = resolveMessages(config.locale);
|
|
30
|
+
registerCommands(ctx, { api, identity, m });
|
|
24
31
|
// The switch notice goes to ctx.logger.info and nowhere else — never a
|
|
25
32
|
// session message, so a fallback can never pollute model-visible
|
|
26
33
|
// conversation history. That also means the player does not normally SEE
|
|
27
34
|
// it; see the known-limitation note on `registerRouter` for why no
|
|
28
35
|
// in-conversation channel is reachable from a waterfall listener here.
|
|
29
|
-
registerRouter(ctx, { serverUrl: config.serverUrl, identity });
|
|
36
|
+
registerRouter(ctx, { serverUrl: config.serverUrl, identity, m });
|
|
30
37
|
// The graphical table's HTTP surface is a NESTED plugin injecting
|
|
31
38
|
// `webServer` — a headless/tui host simply leaves this fiber pending
|
|
32
39
|
// (no `webServer` service to satisfy the inject) while the commands
|
package/dist/render.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Card, HiddenCard, MeView, RoundView } from './api.js';
|
|
2
|
+
import type { Messages } from './i18n/index.js';
|
|
2
3
|
/**
|
|
3
4
|
* Regex of vocabulary this project's copy must never contain (parent spec §2
|
|
4
5
|
* red line 6, 文案去赌场化).
|
|
@@ -10,7 +11,6 @@ import type { Card, HiddenCard, MeView, RoundView } from './api.js';
|
|
|
10
11
|
* which meant tightening one copy silently left the other looser.
|
|
11
12
|
*/
|
|
12
13
|
export declare const BANNED: RegExp;
|
|
13
|
-
export declare const CONSENT_TEXT: string;
|
|
14
14
|
export declare function renderCard(card: Card | HiddenCard): string;
|
|
15
|
-
export declare function renderRound(view: RoundView): string;
|
|
16
|
-
export declare function renderMe(me: MeView): string;
|
|
15
|
+
export declare function renderRound(view: RoundView, m: Messages): string;
|
|
16
|
+
export declare function renderMe(me: MeView, m: Messages): string;
|
package/dist/render.js
CHANGED
|
@@ -11,61 +11,49 @@ const SUITS = { S: '♠', H: '♥', D: '♦', C: '♣' };
|
|
|
11
11
|
* which meant tightening one copy silently left the other looser.
|
|
12
12
|
*/
|
|
13
13
|
export const BANNED = /赌|博彩|casino|赢钱|下注赢|筹码/i;
|
|
14
|
-
export const CONSENT_TEXT = [
|
|
15
|
-
'【首次使用请先阅读】',
|
|
16
|
-
'· 这是社区开发者发布的第三方小游戏,与任何模型厂商均无关联,不代表其立场。',
|
|
17
|
-
'· 参与免费:每日有限次免费手,输了不扣任何东西,你自己的密钥与余额不受影响。',
|
|
18
|
-
'· 赢得的 CHIP 只能在本插件内兑换为模型额度消费:不可提现、不可转让、不可转入任何厂商账户。',
|
|
19
|
-
'· 奖励有赛季有效期,赛季结束后未使用的部分按规则回收。',
|
|
20
|
-
'',
|
|
21
|
-
'阅读并同意以上内容后,执行 /blackjack agree confirm 开始。',
|
|
22
|
-
].join('\n');
|
|
23
14
|
export function renderCard(card) {
|
|
24
15
|
if ('hidden' in card)
|
|
25
16
|
return '??';
|
|
26
17
|
return `${card.rank}${SUITS[card.suit] ?? card.suit}`;
|
|
27
18
|
}
|
|
28
|
-
|
|
29
|
-
export function renderRound(view) {
|
|
19
|
+
export function renderRound(view, m) {
|
|
30
20
|
const lines = [];
|
|
31
21
|
for (const [i, hand] of view.hands.entries()) {
|
|
32
|
-
const label = view.hands.length > 1 ?
|
|
22
|
+
const label = view.hands.length > 1 ? m.splitHandLabel(i + 1) : m.handLabel();
|
|
33
23
|
const cards = hand.cards.map(renderCard).join(' ');
|
|
34
|
-
|
|
35
|
-
lines.push(`${label}:${cards}(${soft}${hand.total})${hand.doubled ? ' [双倍]' : ''}`);
|
|
24
|
+
lines.push(`${label}${cards}${m.totalInParens(hand.total, hand.soft)}${hand.doubled ? m.doubled() : ''}`);
|
|
36
25
|
}
|
|
37
26
|
const dealer = view.dealer.cards.map(renderCard).join(' ');
|
|
38
|
-
|
|
27
|
+
const dealerTotal = view.dealer.total !== undefined ? m.totalInParens(view.dealer.total, false) : '';
|
|
28
|
+
lines.push(`${m.dealerLabel()}${dealer}${dealerTotal}`);
|
|
39
29
|
if (view.phase === 'settled') {
|
|
40
30
|
const won = view.payoutChips ?? 0;
|
|
41
|
-
lines.push(outcomeLine(view));
|
|
31
|
+
lines.push(outcomeLine(view, m));
|
|
42
32
|
if (view.pointsGranted)
|
|
43
|
-
lines.push(
|
|
33
|
+
lines.push(m.pointsGrantedNotice(view.pointsGranted));
|
|
34
|
+
// 按事实:赛季结算只回收**未达兑换门槛**的余额(折算积分),达标的结转。
|
|
44
35
|
if (won > 0)
|
|
45
|
-
lines.push(
|
|
36
|
+
lines.push(m.chipValidityNotice());
|
|
46
37
|
}
|
|
47
38
|
else {
|
|
48
|
-
lines.push(stakeLine(view));
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
split: '/bj-split 分牌', insure: '/bj-insure 买保险', decline: '/bj-decline 不买保险',
|
|
52
|
-
};
|
|
53
|
-
const actions = view.actions.map((a) => map[a] ?? a).join(' ');
|
|
39
|
+
lines.push(stakeLine(view, m));
|
|
40
|
+
const actionLabels = m.actionLabels;
|
|
41
|
+
const actions = view.actions.map((a) => actionLabels[a] ?? a).join(m.actionsSeparator());
|
|
54
42
|
if (actions)
|
|
55
|
-
lines.push(
|
|
43
|
+
lines.push(`${m.actionsPrefix()}${actions}`);
|
|
56
44
|
}
|
|
57
45
|
return lines.join('\n');
|
|
58
46
|
}
|
|
59
|
-
export function renderMe(me) {
|
|
47
|
+
export function renderMe(me, m) {
|
|
60
48
|
const lines = [
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
49
|
+
m.balanceLine(me.chips, me.exchangedChips),
|
|
50
|
+
m.freeHandsLine(me.freeHandsRemaining, me.seasonId),
|
|
51
|
+
m.thresholdLine(me.exchangeThresholdChips),
|
|
64
52
|
];
|
|
65
53
|
if (me.points > 0)
|
|
66
|
-
lines.push(
|
|
54
|
+
lines.push(m.pointsLine(me.points));
|
|
67
55
|
if (me.poolEmpty)
|
|
68
|
-
lines.push(
|
|
56
|
+
lines.push(m.poolEmptyNotice());
|
|
69
57
|
if (me.sponsorText)
|
|
70
58
|
lines.push(me.sponsorText);
|
|
71
59
|
return lines.join('\n');
|
package/dist/router.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Context } from '@deepseek-ai/cordis';
|
|
2
2
|
import { type GenerateOptions, type StreamChunk } from '@deepseek-ai/dsh-llm';
|
|
3
3
|
import type { Identity } from './identity.js';
|
|
4
|
+
import type { Messages } from './i18n/index.js';
|
|
4
5
|
/** Provider route the pool adapter is registered under; also the loop-guard for `shouldFallback`. */
|
|
5
6
|
export declare const POOL_PROVIDER = "blackjack-pool";
|
|
6
7
|
/**
|
|
@@ -27,6 +28,8 @@ export declare function shouldFallback(input: {
|
|
|
27
28
|
hasToken: boolean;
|
|
28
29
|
provider: string;
|
|
29
30
|
}): boolean;
|
|
31
|
+
/** 抛出来的错误是否读作"没配 key"。 */
|
|
32
|
+
export declare function isMissingCredentialError(error: unknown): boolean;
|
|
30
33
|
/**
|
|
31
34
|
* 这次失败是否该提示玩家"你有已兑换额度,但续命现在用不上"。
|
|
32
35
|
*
|
|
@@ -35,9 +38,6 @@ export declare function shouldFallback(input: {
|
|
|
35
38
|
* MISSING_CREDENTIAL,于是什么都没发生,也没有任何东西解释为什么。这里只负责
|
|
36
39
|
* 说明,不改变任何行为——原始失败照样原样透传。
|
|
37
40
|
*/
|
|
38
|
-
export declare const NO_CREDENTIAL_HINT = "\u7EED\u547D\u53EA\u5728\u4F60\u81EA\u5DF1\u7684\u6A21\u578B\u989D\u5EA6\u8017\u5C3D\u65F6\u63A5\u7BA1\uFF1B\u5F53\u524D\u662F\u6CA1\u6709\u914D\u7F6E key\uFF0C\u6240\u4EE5\u7528\u4E0D\u5230\u4F60\u5DF2\u5151\u6362\u7684\u989D\u5EA6\u3002";
|
|
39
|
-
/** 抛出来的错误是否读作"没配 key"。 */
|
|
40
|
-
export declare function isMissingCredentialError(error: unknown): boolean;
|
|
41
41
|
export declare function shouldHintNoCredential(input: {
|
|
42
42
|
finish: StreamChunk & {
|
|
43
43
|
type: 'finish';
|
|
@@ -50,6 +50,18 @@ export interface FallbackDeps {
|
|
|
50
50
|
notify: (line: string) => void;
|
|
51
51
|
/** Diagnostic-only sink for a swallowed pool-retry failure; never required, never surfaced to the player. */
|
|
52
52
|
logError?: (error: unknown) => void;
|
|
53
|
+
/**
|
|
54
|
+
* Locale-resolved copy for the two player-facing strings this module ever
|
|
55
|
+
* produces itself (the pool-switch notice and the no-credential hint).
|
|
56
|
+
* Required, not defaulted: this is the fallback route, the one code path
|
|
57
|
+
* this whole product exists for, and a silent default is exactly the
|
|
58
|
+
* failure mode the plugin's i18n migration exists to eliminate — a caller
|
|
59
|
+
* that forgets this argument should fail to compile, not quietly speak
|
|
60
|
+
* the wrong language. Every call site (including the three test files
|
|
61
|
+
* that call `runWithFallback`/`registerRouter` directly) passes it
|
|
62
|
+
* explicitly.
|
|
63
|
+
*/
|
|
64
|
+
m: Messages;
|
|
53
65
|
}
|
|
54
66
|
/**
|
|
55
67
|
* Wrap one model call: pass everything through, and only when the call ends
|
|
@@ -73,6 +85,21 @@ export interface FallbackDeps {
|
|
|
73
85
|
* entirely once a fallback is underway — the pool call reports its own —
|
|
74
86
|
* instead of both being forwarded and double-counting tokens.
|
|
75
87
|
*/
|
|
88
|
+
/**
|
|
89
|
+
* 奖池也失败时,替换交还给宿主的失败**消息**,保留 `code`/`status` 等机器字段。
|
|
90
|
+
*
|
|
91
|
+
* 原样交还原始失败是有意的("装了这个插件不会让任何调用变差"),但那条消息说的是
|
|
92
|
+
* 玩家自己额度不足——他明明有已兑换额度,续命也确实接管了,只是奖池这侧没成。
|
|
93
|
+
* 假话出现在他最需要看懂的时刻,所以人读的那半必须换掉。
|
|
94
|
+
*/
|
|
95
|
+
export declare function withTruthfulMessage(original: StreamChunk & {
|
|
96
|
+
type: 'finish';
|
|
97
|
+
}, poolFailure: {
|
|
98
|
+
code?: string;
|
|
99
|
+
message?: string;
|
|
100
|
+
}, m: Messages): StreamChunk & {
|
|
101
|
+
type: 'finish';
|
|
102
|
+
};
|
|
76
103
|
export declare function runWithFallback(options: GenerateOptions, next: () => AsyncIterable<StreamChunk>, deps: FallbackDeps): AsyncIterable<StreamChunk>;
|
|
77
104
|
/**
|
|
78
105
|
* Register the pool-route adapter and hang the `llm/stream` fallback
|
|
@@ -114,4 +141,5 @@ export declare function registerRouter(ctx: Context, deps: {
|
|
|
114
141
|
serverUrl: string;
|
|
115
142
|
identity: Identity;
|
|
116
143
|
notify?: (line: string) => void;
|
|
144
|
+
m: Messages;
|
|
117
145
|
}): void;
|
package/dist/router.js
CHANGED
|
@@ -49,6 +49,13 @@ export function shouldFallback(input) {
|
|
|
49
49
|
* 的注释("Distinct from `MISSING_CREDENTIAL`")与宿主 UI 上的徽章共同确认。
|
|
50
50
|
*/
|
|
51
51
|
const MISSING_CREDENTIAL_CODE = 'MISSING_CREDENTIAL';
|
|
52
|
+
/** 抛出来的错误是否读作"没配 key"。 */
|
|
53
|
+
export function isMissingCredentialError(error) {
|
|
54
|
+
const e = error;
|
|
55
|
+
if (e && typeof e === 'object' && e.code === MISSING_CREDENTIAL_CODE)
|
|
56
|
+
return true;
|
|
57
|
+
return typeof e?.message === 'string' && /no API key|MISSING_CREDENTIAL/i.test(e.message);
|
|
58
|
+
}
|
|
52
59
|
/**
|
|
53
60
|
* 这次失败是否该提示玩家"你有已兑换额度,但续命现在用不上"。
|
|
54
61
|
*
|
|
@@ -57,14 +64,6 @@ const MISSING_CREDENTIAL_CODE = 'MISSING_CREDENTIAL';
|
|
|
57
64
|
* MISSING_CREDENTIAL,于是什么都没发生,也没有任何东西解释为什么。这里只负责
|
|
58
65
|
* 说明,不改变任何行为——原始失败照样原样透传。
|
|
59
66
|
*/
|
|
60
|
-
export const NO_CREDENTIAL_HINT = '续命只在你自己的模型额度耗尽时接管;当前是没有配置 key,所以用不到你已兑换的额度。';
|
|
61
|
-
/** 抛出来的错误是否读作"没配 key"。 */
|
|
62
|
-
export function isMissingCredentialError(error) {
|
|
63
|
-
const e = error;
|
|
64
|
-
if (e && typeof e === 'object' && e.code === MISSING_CREDENTIAL_CODE)
|
|
65
|
-
return true;
|
|
66
|
-
return typeof e?.message === 'string' && /no API key|MISSING_CREDENTIAL/i.test(e.message);
|
|
67
|
-
}
|
|
68
67
|
export function shouldHintNoCredential(input) {
|
|
69
68
|
if (input.provider === POOL_PROVIDER)
|
|
70
69
|
return false;
|
|
@@ -95,7 +94,28 @@ export function shouldHintNoCredential(input) {
|
|
|
95
94
|
* entirely once a fallback is underway — the pool call reports its own —
|
|
96
95
|
* instead of both being forwarded and double-counting tokens.
|
|
97
96
|
*/
|
|
97
|
+
/**
|
|
98
|
+
* 奖池也失败时,替换交还给宿主的失败**消息**,保留 `code`/`status` 等机器字段。
|
|
99
|
+
*
|
|
100
|
+
* 原样交还原始失败是有意的("装了这个插件不会让任何调用变差"),但那条消息说的是
|
|
101
|
+
* 玩家自己额度不足——他明明有已兑换额度,续命也确实接管了,只是奖池这侧没成。
|
|
102
|
+
* 假话出现在他最需要看懂的时刻,所以人读的那半必须换掉。
|
|
103
|
+
*/
|
|
104
|
+
export function withTruthfulMessage(original, poolFailure, m) {
|
|
105
|
+
const reason = original.reason;
|
|
106
|
+
if (reason.kind !== 'error' || !reason.failure)
|
|
107
|
+
return original;
|
|
108
|
+
const busy = poolFailure.code === 'RATE_LIMIT' || /concurrent|rate limit/i.test(poolFailure.message ?? '');
|
|
109
|
+
const message = busy
|
|
110
|
+
? m.poolFallbackBusy()
|
|
111
|
+
: m.poolFallbackFailed(poolFailure.message ?? poolFailure.code ?? 'unknown');
|
|
112
|
+
return {
|
|
113
|
+
...original,
|
|
114
|
+
reason: { ...reason, failure: { ...reason.failure, message } },
|
|
115
|
+
};
|
|
116
|
+
}
|
|
98
117
|
export async function* runWithFallback(options, next, deps) {
|
|
118
|
+
const m = deps.m;
|
|
99
119
|
let emittedContent = false;
|
|
100
120
|
let finish;
|
|
101
121
|
const pendingUsage = [];
|
|
@@ -126,7 +146,7 @@ export async function* runWithFallback(options, next, deps) {
|
|
|
126
146
|
hinted = false;
|
|
127
147
|
}
|
|
128
148
|
if (hinted)
|
|
129
|
-
deps.notify(
|
|
149
|
+
deps.notify(m.noCredentialHint());
|
|
130
150
|
}
|
|
131
151
|
throw error;
|
|
132
152
|
}
|
|
@@ -150,7 +170,7 @@ export async function* runWithFallback(options, next, deps) {
|
|
|
150
170
|
hinted = false;
|
|
151
171
|
}
|
|
152
172
|
if (hinted) {
|
|
153
|
-
deps.notify(
|
|
173
|
+
deps.notify(m.noCredentialHint());
|
|
154
174
|
}
|
|
155
175
|
}
|
|
156
176
|
yield* pendingUsage;
|
|
@@ -181,12 +201,14 @@ export async function* runWithFallback(options, next, deps) {
|
|
|
181
201
|
// 奖池回的是 `unknown model: deepseek-v4-flash`(模型不在价目表里),
|
|
182
202
|
// 而玩家只看到原始的 Insufficient Balance,跟没装插件一模一样。
|
|
183
203
|
deps.logError?.(new Error(`dsh-blackjack: pool retry failed after a quota exhaustion; pool said ${JSON.stringify(c.reason.failure)}`));
|
|
184
|
-
|
|
204
|
+
// 交还的仍然是**原始**失败(不改 code,宿主的路由与徽章认的是它),但把
|
|
205
|
+
// 给人读的那半换成真话:原样交还等于告诉一个明明有额度的玩家"余额不足"。
|
|
206
|
+
yield withTruthfulMessage(finish, c.reason.failure, m);
|
|
185
207
|
return;
|
|
186
208
|
}
|
|
187
209
|
if (!announced) {
|
|
188
210
|
announced = true;
|
|
189
|
-
deps.notify(
|
|
211
|
+
deps.notify(m.poolSwitchNotice());
|
|
190
212
|
}
|
|
191
213
|
if (c.type === 'finish')
|
|
192
214
|
poolFinished = true;
|
|
@@ -243,5 +265,6 @@ export function registerRouter(ctx, deps) {
|
|
|
243
265
|
poolStream: (o) => ctx.llm.stream(o),
|
|
244
266
|
notify,
|
|
245
267
|
logError: (error) => ctx.logger.warn('dsh-blackjack: pool fallback failed after a quota exhaustion; the original failure was surfaced instead', error),
|
|
268
|
+
m: deps.m,
|
|
246
269
|
}));
|
|
247
270
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-blackjack",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "社区福利小游戏插件:在 dsh 里玩 21 点赢取可消费的模型额度",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
"@deepseek-ai/cordis": "4.0.1",
|
|
66
66
|
"@deepseek-ai/dsh-agent": "0.1.0-rc.6",
|
|
67
67
|
"@deepseek-ai/dsh-anonymous-user-id": "0.1.0-rc.6",
|
|
68
|
+
"@deepseek-ai/dsh-client-locale": "0.1.0-rc.6",
|
|
68
69
|
"@deepseek-ai/dsh-client-runtime": "0.1.0-rc.6",
|
|
69
70
|
"@deepseek-ai/dsh-client-ui-conversation": "0.1.0-rc.6",
|
|
70
71
|
"@deepseek-ai/dsh-client-ui-slots": "0.1.0-rc.6",
|