dsh-blackjack 0.1.4 → 0.2.0
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 +39 -36
- package/dist/client/index.d.ts +20 -0
- package/dist/client/index.js +91 -9
- package/dist/client.js +12 -2
- package/dist/commands.d.ts +4 -1
- package/dist/commands.js +165 -80
- 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 +110 -0
- package/dist/i18n/en.js +170 -0
- package/dist/i18n/index.d.ts +4 -0
- package/dist/i18n/index.js +6 -0
- package/dist/i18n/messages.d.ts +412 -0
- package/dist/i18n/messages.js +33 -0
- package/dist/i18n/zh.d.ts +110 -0
- package/dist/i18n/zh.js +168 -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 +16 -3
- package/dist/router.js +12 -11
- package/package.json +2 -1
|
@@ -0,0 +1,110 @@
|
|
|
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
|
+
noCredentialHint: () => string;
|
|
70
|
+
hiddenCardLabel: () => string;
|
|
71
|
+
tableHandLabel: () => string;
|
|
72
|
+
tableSplitHandLabel: (handIndex: number) => string;
|
|
73
|
+
tableDealerLabel: () => string;
|
|
74
|
+
tableActionLabels: {
|
|
75
|
+
hit: string;
|
|
76
|
+
stand: string;
|
|
77
|
+
double: string;
|
|
78
|
+
split: string;
|
|
79
|
+
insure: string;
|
|
80
|
+
decline: string;
|
|
81
|
+
};
|
|
82
|
+
tableBalanceHeader: (chips: number, freeHandsRemaining: number) => string;
|
|
83
|
+
tablePointsSuffix: (points: number) => string;
|
|
84
|
+
tableExchangedSuffix: (exchangedChips: number) => string;
|
|
85
|
+
tableExchangeThresholdReached: (exchangeThresholdChips: number) => string;
|
|
86
|
+
tableExchangeThresholdShort: (shortChips: number, exchangeThresholdChips: number) => string;
|
|
87
|
+
tableExchangedQuotaNote: (exchangedChips: number) => string;
|
|
88
|
+
tablePoolEmptyNotice: () => string;
|
|
89
|
+
actionShortfallHint: (costChips: number) => string;
|
|
90
|
+
tableCostSuffix: (costChips: number) => string;
|
|
91
|
+
betAmountMissing: () => string;
|
|
92
|
+
betAmountExceedsCap: (maxChips: number) => string;
|
|
93
|
+
betAmountExceedsBalance: () => string;
|
|
94
|
+
customBetToggle: () => string;
|
|
95
|
+
customBetSubmit: () => string;
|
|
96
|
+
customBetRange: (maxChips: number) => string;
|
|
97
|
+
noActiveRoundNotice: () => string;
|
|
98
|
+
freeHandsGroupTitle: () => string;
|
|
99
|
+
freeHandsRemainingToday: (freeHandsRemaining: number) => string;
|
|
100
|
+
freeHandsExhaustedToday: () => string;
|
|
101
|
+
freeHandExplainer: (baseChips: number, naturalBlackjackPayoutChips: number) => string;
|
|
102
|
+
startFreeHandButton: () => string;
|
|
103
|
+
raiseGroupTitle: () => string;
|
|
104
|
+
insufficientBalanceSuffix: () => string;
|
|
105
|
+
raiseExplainer: () => string;
|
|
106
|
+
rematchFreeLabel: () => string;
|
|
107
|
+
dealButtonFreeHandsSuffix: (freeHandsRemaining: number) => string;
|
|
108
|
+
dismissButton: () => string;
|
|
109
|
+
actionFailedNotice: () => string;
|
|
110
|
+
};
|
package/dist/i18n/zh.js
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
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
|
+
noCredentialHint: () => '续命只在你自己的模型额度耗尽时接管;当前是没有配置 key,所以用不到你已兑换的额度。',
|
|
123
|
+
hiddenCardLabel: () => '暗牌',
|
|
124
|
+
tableHandLabel: () => '你的牌',
|
|
125
|
+
tableSplitHandLabel: (handIndex) => `你的第 ${handIndex} 手`,
|
|
126
|
+
tableDealerLabel: () => '庄家',
|
|
127
|
+
tableActionLabels: {
|
|
128
|
+
hit: '要牌',
|
|
129
|
+
stand: '停牌',
|
|
130
|
+
double: '双倍',
|
|
131
|
+
split: '分牌',
|
|
132
|
+
insure: '买保险',
|
|
133
|
+
decline: '不买保险',
|
|
134
|
+
},
|
|
135
|
+
tableBalanceHeader: (chips, freeHandsRemaining) => `余额:${n(chips)} CHIP 今日剩余免费手:${freeHandsRemaining}`,
|
|
136
|
+
tablePointsSuffix: (points) => ` 积分:${n(points)}`,
|
|
137
|
+
tableExchangedSuffix: (exchangedChips) => ` 已兑换额度:${n(exchangedChips)} CHIP`,
|
|
138
|
+
tableExchangeThresholdReached: (exchangeThresholdChips) => `已达兑换门槛(${n(exchangeThresholdChips)} CHIP)· 用 /blackjack exchange 把 CHIP 换成模型额度`,
|
|
139
|
+
tableExchangeThresholdShort: (shortChips, exchangeThresholdChips) => `还差 ${n(shortChips)} CHIP 到兑换门槛(${n(exchangeThresholdChips)})· 过线后可用 /blackjack exchange 换成模型额度`,
|
|
140
|
+
tableExchangedQuotaNote: (exchangedChips) => `已兑换的 ${n(exchangedChips)} CHIP 会在你自己的模型额度耗尽时自动接管那一次请求,平时不会动用。`,
|
|
141
|
+
tablePoolEmptyNotice: () => '(本期奖池已发完,现在赢牌将发放积分)',
|
|
142
|
+
actionShortfallHint: (costChips) => `还要再出 ${n(costChips)} CHIP,余额不足`,
|
|
143
|
+
tableCostSuffix: (costChips) => `(${n(costChips)})`,
|
|
144
|
+
betAmountMissing: () => '请输入注额。',
|
|
145
|
+
betAmountExceedsCap: (maxChips) => `单局最多 ${n(maxChips)} CHIP。`,
|
|
146
|
+
betAmountExceedsBalance: () => '余额不足以下这个注。',
|
|
147
|
+
customBetToggle: () => '自定义注额',
|
|
148
|
+
customBetSubmit: () => '开始',
|
|
149
|
+
customBetRange: (maxChips) => `1 ~ ${n(maxChips)} CHIP`,
|
|
150
|
+
noActiveRoundNotice: () => '当前没有进行中的牌局。',
|
|
151
|
+
freeHandsGroupTitle: () => '免费手',
|
|
152
|
+
freeHandsRemainingToday: (freeHandsRemaining) => ` · 今日剩 ${freeHandsRemaining} 手`,
|
|
153
|
+
freeHandsExhaustedToday: () => ' · 今日已用完,明天再来',
|
|
154
|
+
freeHandExplainer: (baseChips, naturalBlackjackPayoutChips) => `不用出 CHIP。赢了拿 ${n(baseChips)} CHIP;头两张牌就凑成 21 点(A 配 10/J/Q/K)拿 ${n(naturalBlackjackPayoutChips)}。输了或平局都不损失任何东西。`,
|
|
155
|
+
startFreeHandButton: () => '开始免费的一局',
|
|
156
|
+
raiseGroupTitle: () => '用余额加注',
|
|
157
|
+
insufficientBalanceSuffix: () => ' · 余额不足',
|
|
158
|
+
raiseExplainer: () => '加注的 CHIP 从你的余额里出,赢了翻倍,输了收不回。',
|
|
159
|
+
// 全角括号自己开自己闭:这颗按钮渲染出来是 `再来一局(免费)·剩 3 手`,
|
|
160
|
+
// 后缀 `dealButtonFreeHandsSuffix` 不带右括号(英文那版 `Play again (free)`
|
|
161
|
+
// 从一开始就是闭合的——同一颗按钮,两个 locale 对"这是不是个 bug"给了相反
|
|
162
|
+
// 的答案)。少一个右括号的写法在图形牌桌最常被点的按钮上活了这么久,唯一的
|
|
163
|
+
// 原因是从没有任何断言拼完整之后的整串;client.test.tsx 现在钉住渲染结果本身。
|
|
164
|
+
rematchFreeLabel: () => '再来一局(免费)',
|
|
165
|
+
dealButtonFreeHandsSuffix: (freeHandsRemaining) => `·剩 ${freeHandsRemaining} 手`,
|
|
166
|
+
dismissButton: () => '收起',
|
|
167
|
+
actionFailedNotice: () => '上一次操作没有成功,已为你刷新到最新状态。',
|
|
168
|
+
};
|
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 开始。',
|
|
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
|
|
@@ -114,4 +126,5 @@ export declare function registerRouter(ctx: Context, deps: {
|
|
|
114
126
|
serverUrl: string;
|
|
115
127
|
identity: Identity;
|
|
116
128
|
notify?: (line: string) => void;
|
|
129
|
+
m: Messages;
|
|
117
130
|
}): 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;
|
|
@@ -96,6 +95,7 @@ export function shouldHintNoCredential(input) {
|
|
|
96
95
|
* instead of both being forwarded and double-counting tokens.
|
|
97
96
|
*/
|
|
98
97
|
export async function* runWithFallback(options, next, deps) {
|
|
98
|
+
const m = deps.m;
|
|
99
99
|
let emittedContent = false;
|
|
100
100
|
let finish;
|
|
101
101
|
const pendingUsage = [];
|
|
@@ -126,7 +126,7 @@ export async function* runWithFallback(options, next, deps) {
|
|
|
126
126
|
hinted = false;
|
|
127
127
|
}
|
|
128
128
|
if (hinted)
|
|
129
|
-
deps.notify(
|
|
129
|
+
deps.notify(m.noCredentialHint());
|
|
130
130
|
}
|
|
131
131
|
throw error;
|
|
132
132
|
}
|
|
@@ -150,7 +150,7 @@ export async function* runWithFallback(options, next, deps) {
|
|
|
150
150
|
hinted = false;
|
|
151
151
|
}
|
|
152
152
|
if (hinted) {
|
|
153
|
-
deps.notify(
|
|
153
|
+
deps.notify(m.noCredentialHint());
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
156
|
yield* pendingUsage;
|
|
@@ -186,7 +186,7 @@ export async function* runWithFallback(options, next, deps) {
|
|
|
186
186
|
}
|
|
187
187
|
if (!announced) {
|
|
188
188
|
announced = true;
|
|
189
|
-
deps.notify(
|
|
189
|
+
deps.notify(m.poolSwitchNotice());
|
|
190
190
|
}
|
|
191
191
|
if (c.type === 'finish')
|
|
192
192
|
poolFinished = true;
|
|
@@ -243,5 +243,6 @@ export function registerRouter(ctx, deps) {
|
|
|
243
243
|
poolStream: (o) => ctx.llm.stream(o),
|
|
244
244
|
notify,
|
|
245
245
|
logError: (error) => ctx.logger.warn('dsh-blackjack: pool fallback failed after a quota exhaustion; the original failure was surfaced instead', error),
|
|
246
|
+
m: deps.m,
|
|
246
247
|
}));
|
|
247
248
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-blackjack",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
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",
|