dsh-blackjack 0.2.1 → 0.2.2
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 +219 -164
- package/README.zh-CN.md +249 -0
- package/dist/client/command-input.d.ts +70 -0
- package/dist/client/command-input.js +89 -0
- package/dist/client/index.js +26 -0
- package/dist/client.js +12 -12
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,144 +1,180 @@
|
|
|
1
|
-
# dsh-blackjack
|
|
1
|
+
# dsh-blackjack plugin
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
>
|
|
5
|
-
> 本插件由个人开发者发布与维护,**与任何模型厂商均无关联**:不是任何厂商的官方
|
|
6
|
-
> 项目、不代表其立场、未获其背书或授权,也不使用其名称与标识作为本项目的品牌
|
|
7
|
-
> 元素。包名与命令中的 `dsh-` / `bj-` 前缀仅遵循社区插件的命名惯例,用于标识
|
|
8
|
-
> 本插件的兼容目标与命名空间,不代表 dsh 官方发布或认可。
|
|
9
|
-
|
|
10
|
-
安装进你自己的 dsh 后,在对话里直接打开一张 21 点牌桌:赢牌拿游戏币 **CHIP**,CHIP 可以单向兑换为「已兑换额度」,这份额
|
|
11
|
-
度只能在本插件的续命路由里消费——**当且仅当你自己配置的模型额度耗尽时**,插件
|
|
12
|
-
才会用这份额度接住那一次失败的请求,让你不用中断手上的任务去充值或等待。
|
|
3
|
+
**English** | [简体中文](./README.zh-CN.md)
|
|
13
4
|
|
|
14
|
-
|
|
15
|
-
|
|
5
|
+
> ⚠️ **Third-party community project disclaimer**
|
|
6
|
+
>
|
|
7
|
+
> This plugin is published and maintained by an individual developer and is **not
|
|
8
|
+
> affiliated with any model vendor**: it is not an official project of any vendor, does
|
|
9
|
+
> not represent their position, is not endorsed or authorized by them, and does not use
|
|
10
|
+
> their names or logos as branding elements. The `dsh-` / `bj-` prefixes in the package
|
|
11
|
+
> name and commands merely follow the community's plugin naming convention, identifying
|
|
12
|
+
> this plugin's compatibility target and namespace; they do not imply it is published or
|
|
13
|
+
> approved by dsh officially.
|
|
14
|
+
|
|
15
|
+
Install it into your own dsh and open a blackjack table right there in the conversation:
|
|
16
|
+
win hands to earn the game currency **CHIP**, convert CHIP one-way into "redeemed credit",
|
|
17
|
+
and spend that credit only through this plugin's fallback route — **and only when the model
|
|
18
|
+
credit you configured yourself has run out** will the plugin use it to catch that one failed
|
|
19
|
+
request, so you don't have to break off what you're doing to top up or wait.
|
|
20
|
+
|
|
21
|
+
Participation is **always free**: a fixed number of free hands every day, losing costs you
|
|
22
|
+
nothing, and it never touches your own key or balance. All the details are in
|
|
23
|
+
"Participation rules" below.
|
|
16
24
|
|
|
17
25
|
---
|
|
18
26
|
|
|
19
|
-
##
|
|
27
|
+
## What this is and how to play
|
|
20
28
|
|
|
21
|
-
|
|
22
|
-
|
|
29
|
+
The table's authoritative state is held by a game server (`packages/server`, self-hosted by
|
|
30
|
+
the operator); the plugin is just its client:
|
|
23
31
|
|
|
24
|
-
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
- **Text commands**: the `/blackjack` family costs nothing — they all go through dsh's
|
|
33
|
+
command layer rather than sending a model message, so playing itself consumes none of your
|
|
34
|
+
token credit.
|
|
35
|
+
- **Graphical table** (optional): if your dsh profile includes the Web GUI (e.g.
|
|
36
|
+
`@deepseek-ai/dsh-web-app`), a clickable table card appears in the conversation stream.
|
|
37
|
+
Its action buttons are equivalent to the text commands, just a different surface. In a
|
|
38
|
+
terminal-only profile without the Web GUI the text commands work as usual — the plugin does
|
|
39
|
+
not error out over a missing optional service.
|
|
30
40
|
|
|
31
|
-
|
|
32
|
-
|
|
41
|
+
One round: `/blackjack deal` to start (uses one of the day's free hands by default, or
|
|
42
|
+
`deal <amount>` to raise from your balance) → play it out with the action commands
|
|
43
|
+
`bj-hit` / `bj-stand` as prompted → settle, and the winner takes CHIP.
|
|
33
44
|
|
|
34
|
-
###
|
|
45
|
+
### Command table
|
|
35
46
|
|
|
36
|
-
|
|
|
47
|
+
| Command | What it does |
|
|
37
48
|
|---|---|
|
|
38
|
-
| `/blackjack` |
|
|
39
|
-
| `/blackjack agree` |
|
|
40
|
-
| `/blackjack agree confirm` |
|
|
41
|
-
| `/blackjack deal [
|
|
42
|
-
| `/blackjack exchange [
|
|
43
|
-
| `/blackjack reset confirm` |
|
|
44
|
-
| `/bj-hit` |
|
|
45
|
-
| `/bj-stand` |
|
|
46
|
-
| `/bj-double` |
|
|
47
|
-
| `/bj-split` |
|
|
48
|
-
| `/bj-insure` |
|
|
49
|
-
| `/bj-decline` |
|
|
50
|
-
|
|
51
|
-
`bj-*`
|
|
52
|
-
`/blackjack deal
|
|
53
|
-
|
|
54
|
-
###
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
49
|
+
| `/blackjack` | Show current status: the round if one is in progress, otherwise balance / free hands / redemption threshold |
|
|
50
|
+
| `/blackjack agree` | Display the full terms (third-party disclaimer, non-withdrawable, non-transferable, etc.). **Displays only — it is not consent** |
|
|
51
|
+
| `/blackjack agree confirm` | Confirm consent after reading the terms and register a table account. **Before this step, both dealing and redemption are refused** |
|
|
52
|
+
| `/blackjack deal [amount]` | Deal a new round. With no argument it uses one of the day's free hands; with a positive integer it raises that amount from your balance |
|
|
53
|
+
| `/blackjack exchange [amount]` | Convert an eligible balance into spendable credit. **The first time** (before you have bound GitHub) it gives you a pairing link to complete the binding in a browser; **after binding**, passing an amount redeems right there in the command with no browser involved |
|
|
54
|
+
| `/blackjack reset confirm` | Clear the table credentials stored **on this machine** (server-side records are unaffected; running `agree confirm` again registers a new account) |
|
|
55
|
+
| `/bj-hit` | Take another card |
|
|
56
|
+
| `/bj-stand` | Stand and go to settlement |
|
|
57
|
+
| `/bj-double` | Double the bet and take exactly one more card |
|
|
58
|
+
| `/bj-split` | Split a pair into two hands |
|
|
59
|
+
| `/bj-insure` | Buy insurance when the dealer's up card is an ace |
|
|
60
|
+
| `/bj-decline` | Decline insurance |
|
|
61
|
+
|
|
62
|
+
The six `bj-*` action commands only mean anything while a round is in progress; with no round
|
|
63
|
+
they tell you to run `/blackjack deal` first.
|
|
64
|
+
|
|
65
|
+
### The two redemption paths
|
|
66
|
+
|
|
67
|
+
Redemption requires binding GitHub (one GitHub identity maps to exactly one wallet — the only
|
|
68
|
+
anti-sockpuppet gate on the prize pool's exit). Binding can only be done in a browser, because
|
|
69
|
+
it goes through GitHub's official OAuth authorization code flow. But **binding only has to
|
|
70
|
+
happen once**:
|
|
71
|
+
|
|
72
|
+
1. **First time**: `/blackjack exchange` gives you a short-lived pairing code and link. Open it
|
|
73
|
+
in a browser, click "Continue with GitHub" to bind, then redeem with the form on that same
|
|
74
|
+
page.
|
|
75
|
+
2. **After that**: `/blackjack exchange <amount>`. The plugin calls the server's redemption
|
|
76
|
+
endpoint directly with the player token it already holds — **no pairing code, no browser**.
|
|
77
|
+
With no amount, it first echoes back your balance and the threshold and asks you to add the
|
|
78
|
+
amount: redemption is irreversible, and the plugin will not guess a number for you.
|
|
79
|
+
|
|
80
|
+
A balance below the threshold, or an amount larger than your balance, gets you a clear message
|
|
81
|
+
(spelling out how many CHIP the threshold is, or simply that your balance is short), and the
|
|
82
|
+
ledger does not move a cent. The language of these messages follows the `locale` config key —
|
|
83
|
+
English by default, see "Configuration" below.
|
|
69
84
|
|
|
70
85
|
---
|
|
71
86
|
|
|
72
|
-
##
|
|
87
|
+
## Installation
|
|
73
88
|
|
|
74
89
|
```sh
|
|
75
90
|
dsh plugin --profile web add -w dsh-blackjack
|
|
76
91
|
dsh --profile web
|
|
77
92
|
```
|
|
78
93
|
|
|
79
|
-
|
|
94
|
+
Replace `web` with whichever profile you are actually installing into. The package is published
|
|
95
|
+
on npm, so the command above works as-is.
|
|
80
96
|
|
|
81
|
-
###
|
|
97
|
+
### The launch screen used to be blank (fixed in 0.2.2)
|
|
82
98
|
|
|
83
|
-
|
|
84
|
-
|
|
99
|
+
**From 0.2.2, typing `/blackjack` straight into dsh's launch screen** (the "Into the Unknown" view
|
|
100
|
+
you get when you open a fresh dsh) **brings up the table** — no need to send a throwaway message
|
|
101
|
+
first, and no need to dig an old conversation out of the sidebar.
|
|
85
102
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
103
|
+
**On 0.2.1 and earlier it does not**: the command really did execute, but **nothing appeared on
|
|
104
|
+
screen**, which looked exactly like a broken plugin. On an older version, let a conversation
|
|
105
|
+
genuinely start first (send any message, or open an existing conversation from the sidebar) and
|
|
106
|
+
then run `/blackjack agree`. Clicking "New conversation" does not help — that sends you back to
|
|
107
|
+
the launch screen.
|
|
89
108
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
“新建会话”是没用的:那会把你送回启动页,正是坏掉的那个状态。
|
|
109
|
+
The cause is not in this plugin but in one predicate in the host
|
|
110
|
+
(`dsh-client-runtime@0.1.0-rc.6`, `lib/client.js:7753`):
|
|
93
111
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
112
|
+
```js
|
|
113
|
+
function hasVisibleConversationContent(chat) {
|
|
114
|
+
return chat.order.some((key) => chat.nodes.get(key)?.kind !== "command");
|
|
115
|
+
}
|
|
116
|
+
```
|
|
98
117
|
|
|
99
|
-
|
|
118
|
+
A generic command row is **deliberately** excluded from "visible conversation content", so the
|
|
119
|
+
session view considers the session still empty and renders nothing at all. The host's own `/goal`
|
|
120
|
+
is unaffected because `ui-goal` additionally projects a **non-command** node that activates the
|
|
121
|
+
conversation. This plugin now takes the same route: it projects the command line you typed as a
|
|
122
|
+
chat node of its own — just an existing event drawn under a different kind, writing nothing,
|
|
123
|
+
emitting no new events, and putting nothing into history the model can see.
|
|
124
|
+
|
|
125
|
+
**This is a plugin-side workaround, and the host-level gap remains**: a plugin with only a server
|
|
126
|
+
half and no browser half still has no way to make its commands visible on the launch screen. The
|
|
127
|
+
upstream discussion (with the line-by-line root cause and this plugin's full approach):
|
|
128
|
+
https://github.com/deepseek-ai/deepseek-harness/discussions/4066. The minimal plugin originally
|
|
129
|
+
used to reproduce it (13 lines, no slots and no services) is in this repo at
|
|
130
|
+
[`docs/repro/dsh-probe-repro/`](../../docs/repro/dsh-probe-repro/).
|
|
131
|
+
|
|
132
|
+
> **To change the code, or to install a development build that isn't on npm yet**, install from
|
|
133
|
+
> source:
|
|
100
134
|
>
|
|
101
135
|
> ```sh
|
|
102
136
|
> git clone https://github.com/yul761/dsh-blackjack
|
|
103
|
-
> cd dsh-blackjack && pnpm install #
|
|
137
|
+
> cd dsh-blackjack && pnpm install # builds the plugin's dist/ automatically
|
|
104
138
|
> dsh plugin --profile web add -w "$PWD/packages/plugin"
|
|
105
139
|
> ```
|
|
106
140
|
>
|
|
107
|
-
>
|
|
108
|
-
>
|
|
109
|
-
> `packages/plugin
|
|
141
|
+
> Note that you **cannot** just run `pnpm add github:yul761/dsh-blackjack`: this is a monorepo,
|
|
142
|
+
> and installing from the git source gets you the repository root (a private workspace root with
|
|
143
|
+
> no entry point) rather than the plugin package in `packages/plugin`.
|
|
110
144
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
145
|
+
**Why `-w` is mandatory:** every dsh profile directory is itself a pnpm workspace (it ships its
|
|
146
|
+
own `pnpm-workspace.yaml`), and pnpm refuses by default to add a dependency to the **root** of a
|
|
147
|
+
workspace without `-w`/`--workspace-root`. Omit the flag and the install command fails outright
|
|
148
|
+
rather than leaving you in a half-installed state. This is not a trap unique to this plugin —
|
|
149
|
+
other community dsh plugins' install docs hit the same thing and reached the same conclusion.
|
|
116
150
|
|
|
117
|
-
###
|
|
151
|
+
### Version requirement: this plugin pins dsh `0.1.0-rc.6`
|
|
118
152
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
`^`
|
|
122
|
-
|
|
123
|
-
`
|
|
153
|
+
The plugin pins every `@deepseek-ai/dsh-*` peer **exactly** at `0.1.0-rc.6`, not `^0.1.0-rc.6`.
|
|
154
|
+
That is not conservatism, it's necessary: these packages release in lockstep and the rcs are not
|
|
155
|
+
compatible with each other, and `^` lets the package manager install a newer rc for whichever
|
|
156
|
+
peer the host doesn't have — that is exactly how 0.1.0 broke
|
|
157
|
+
(`dsh-llm-deepseek@rc.8` against the host's `dsh-llm@rc.6`, failing at startup with
|
|
158
|
+
`does not provide an export named 'offloadRequestImages'`).
|
|
124
159
|
|
|
125
|
-
|
|
126
|
-
|
|
160
|
+
So: **please use `dsh@0.1.0-rc.6`**. On a different rc you'll see peer version warnings, and the
|
|
161
|
+
fix is to wait for the plugin to catch up with a new release.
|
|
127
162
|
|
|
128
|
-
|
|
129
|
-
`blackjack.cordis.yml`
|
|
130
|
-
`--patch`
|
|
163
|
+
After a successful install, dsh reads the `dsh.bundle.patch` declaration in the plugin's
|
|
164
|
+
`package.json` and wires `blackjack.cordis.yml` into the profile's bundle layer automatically —
|
|
165
|
+
you don't have to hand-write a `--patch` overlay.
|
|
131
166
|
|
|
132
|
-
###
|
|
167
|
+
### Configuration
|
|
133
168
|
|
|
134
|
-
|
|
135
|
-
|
|
169
|
+
It works out of the box: `serverUrl` points by default at the instance self-hosted by this
|
|
170
|
+
project's operator, so you can play without changing anything. You can override it in your
|
|
171
|
+
profile's `cordis.patch.yml`:
|
|
136
172
|
|
|
137
|
-
|
|
|
173
|
+
| Key | Type | Default | Description |
|
|
138
174
|
|---|---|---|---|
|
|
139
|
-
| `serverUrl` | string | `https://server-production-493c.up.railway.app` |
|
|
140
|
-
| `enabled` | boolean | `true` |
|
|
141
|
-
| `locale` | `'en'` \| `'zh'` | `'en'` | `/blackjack
|
|
175
|
+
| `serverUrl` | string | `https://server-production-493c.up.railway.app` | The game server's address. **To run your own prize pool, change this to your own deployment** — the server is open source in the same repo as this plugin; see the deployment section of the repo root [README](../../README.md) |
|
|
176
|
+
| `enabled` | boolean | `true` | Set to `false` to keep it installed but completely inert: no commands registered, no routes and no fallback hooks attached |
|
|
177
|
+
| `locale` | `'en'` \| `'zh'` | `'en'` | The language of replies from the **commands** (`/blackjack`, `/bj-*`, …). **The graphical table ignores this key** — it follows dsh's own Settings → General → Language automatically, so players using the GUI never have to touch this. Only terminal-only profiles (no Web GUI) need to set it once here, because the Node side cannot read the host's language setting |
|
|
142
178
|
|
|
143
179
|
```yaml
|
|
144
180
|
# ~/.dsh/profiles/<profile>/cordis.patch.yml
|
|
@@ -149,83 +185,102 @@ dsh --profile web
|
|
|
149
185
|
|
|
150
186
|
---
|
|
151
187
|
|
|
152
|
-
##
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
1.
|
|
158
|
-
|
|
159
|
-
2.
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
188
|
+
## Exactly what triggers the fallback route (please read all of it)
|
|
189
|
+
|
|
190
|
+
The plugin attaches an observer to `llm/stream`, but it **never intercepts a working call**.
|
|
191
|
+
The fallback only kicks in when all of the following hold at once:
|
|
192
|
+
|
|
193
|
+
1. the call used **a model provider you configured yourself** (not the plugin's own pool route —
|
|
194
|
+
this avoids an infinite loop);
|
|
195
|
+
2. the call produced **no content at all** — even a single character already emitted disqualifies
|
|
196
|
+
it; the plugin will never switch sources after content has started coming back, since that
|
|
197
|
+
could only produce duplication or truncation;
|
|
198
|
+
3. the call ended in a **quota-exhausted** class of error (not a network error, not a bad
|
|
199
|
+
parameter, not anything else);
|
|
200
|
+
4. you have **already completed `/blackjack agree confirm`** in this plugin (table credentials
|
|
201
|
+
exist on this machine; without them the plugin does not even try).
|
|
202
|
+
|
|
203
|
+
This checks only for credentials and **does not pre-check whether your redeemed credit is
|
|
204
|
+
sufficient**. That is deliberate: checking the balance would add a network request on top of
|
|
205
|
+
every failed call and would still return a possibly-stale snapshot, while the
|
|
206
|
+
"has credentials, zero balance" path is safe by construction — the pool call is rejected by the
|
|
207
|
+
server outright, and the plugin then hands you back **your original error** unchanged, exactly
|
|
208
|
+
as if the plugin weren't installed.
|
|
209
|
+
|
|
210
|
+
Only when all four hold does the plugin resend that failed call to the pool route.
|
|
211
|
+
|
|
212
|
+
**Which model the pool uses (important):** for a request resent to the pool, **the operator picks
|
|
213
|
+
the model server-side**, and it is not necessarily the one you selected in the UI. This is
|
|
214
|
+
deliberate — the pool spends the operator's own upstream balance, so whoever pays picks the model;
|
|
215
|
+
it also avoids the "you picked a model the server doesn't recognize, so the fallback wasted a
|
|
216
|
+
catch" class of failure. So for a call completed by the pool, **the model shown in the UI may
|
|
217
|
+
differ from the model that actually served it**. The current operator instance uses
|
|
218
|
+
`deepseek-v4-flash`.
|
|
219
|
+
|
|
220
|
+
**At most 4 concurrent pool calls at a time.** The server sets the per-player concurrency limit to
|
|
221
|
+
4: the turn in which dsh creates a session fires 3 LLM calls at once, and with the gate at 1 the
|
|
222
|
+
other two were rejected outright, so the fallback failed on exactly the first message where it
|
|
223
|
+
should have worked (measured on a real machine on 2026-08-22 and raised from 1 to 4 — the 3
|
|
224
|
+
measured plus one slot of headroom). Going over the limit gets you a clear "too many requests"
|
|
225
|
+
(429). Note also that a pool request first freezes an estimated hold, and a balance that has just
|
|
226
|
+
crossed the redemption threshold only fits one, so under real concurrency the later calls may be
|
|
227
|
+
rejected for insufficient credit — that comes back as a clear message stating how much was needed
|
|
228
|
+
and how much is left, not a vague failure.
|
|
229
|
+
|
|
230
|
+
**Where the switch notice went (please note):** on a successful switch the plugin emits a line
|
|
231
|
+
saying it has switched to pool credit to continue this request, but in the current dsh version it
|
|
232
|
+
**only goes into dsh's own log and does not appear in the conversation UI** — this version gives
|
|
233
|
+
plugins no channel for inserting a non-model message into a conversation, and the one available
|
|
234
|
+
channel would write the content into history visible to the model, which this project explicitly
|
|
235
|
+
refuses to do (no context pollution, nothing extra for the model to see). In other words: a call
|
|
236
|
+
may have been completed with pool credit while you see no notice in the UI. To confirm the
|
|
237
|
+
consumption, check how your redeemed credit changed with `/blackjack`. Once dsh offers plugins an
|
|
238
|
+
in-session notification slot, this will become an in-UI notice.
|
|
239
|
+
|
|
240
|
+
**If the pool is also out, or the pool service itself is unreachable, the plugin does not fabricate
|
|
241
|
+
a successful result** — you get back your call's original failure reason unchanged, exactly as if
|
|
242
|
+
this plugin weren't installed. Put differently: with this plugin installed, the outcome of a call
|
|
243
|
+
**can only stay the same or get better, never worse**.
|
|
194
244
|
|
|
195
245
|
---
|
|
196
246
|
|
|
197
|
-
##
|
|
198
|
-
|
|
199
|
-
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
-
|
|
204
|
-
|
|
205
|
-
-
|
|
206
|
-
-
|
|
207
|
-
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
247
|
+
## Participation rules
|
|
248
|
+
|
|
249
|
+
- **Participation is always free.** A fixed number of free hands every day, at zero cost. There is
|
|
250
|
+
no design — and there will never be one — that lets you *pay for hands, pay for better odds, or
|
|
251
|
+
pay for better payouts*.
|
|
252
|
+
- **CHIP and redeemed credit are one-way and closed.**
|
|
253
|
+
- **not withdrawable**, not convertible to any fiat currency, and no monetary amount is displayed
|
|
254
|
+
in the UI;
|
|
255
|
+
- **not transferable** (including between players) and non-refundable;
|
|
256
|
+
- **never land in any model vendor's account**;
|
|
257
|
+
- the **only** use is spending them through the game server's fallback route — there is no other
|
|
258
|
+
exit.
|
|
259
|
+
- **Inactive balances are recycled.** Accounts with no games and no consumption for 7 consecutive
|
|
260
|
+
days have their balance recycled into the prize pool.
|
|
261
|
+
- **Redeemed credit does not currently expire.** Early copy said "valid until the end of the
|
|
262
|
+
season", but the server's season settlement does not touch redeemed credit — that sentence did
|
|
263
|
+
not match the implementation, so it has been changed to describe reality. Whether to zero it out
|
|
264
|
+
at season end has not been decided; if it ever is, it will be announced first.
|
|
265
|
+
- **Your own API key never enters this system**: the plugin does not read, host or hold any key for
|
|
266
|
+
your own provider, and the fallback route uses credentials issued by the game server itself.
|
|
212
267
|
|
|
213
268
|
---
|
|
214
269
|
|
|
215
|
-
##
|
|
270
|
+
## Uninstalling and clearing local credentials
|
|
216
271
|
|
|
217
|
-
|
|
218
|
-
|
|
272
|
+
To clear only the table login credentials stored locally (say you're switching machines, or want to
|
|
273
|
+
start over with a fresh account), you don't have to uninstall the plugin:
|
|
219
274
|
|
|
220
275
|
```
|
|
221
276
|
/blackjack reset confirm
|
|
222
277
|
```
|
|
223
278
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
279
|
+
(Without `confirm` it first echoes back an explanation of what the operation does, to prevent
|
|
280
|
+
mistakes.) This clears only the credentials stored **on this machine**; the account record on the
|
|
281
|
+
server is unaffected, and `/blackjack agree confirm` registers a new account.
|
|
227
282
|
|
|
228
|
-
|
|
283
|
+
To remove the plugin entirely:
|
|
229
284
|
|
|
230
285
|
```sh
|
|
231
286
|
dsh plugin --profile web remove dsh-blackjack
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# dsh-blackjack 插件
|
|
2
|
+
|
|
3
|
+
[English](./README.md) | **简体中文**
|
|
4
|
+
|
|
5
|
+
> ⚠️ **社区第三方项目声明**
|
|
6
|
+
>
|
|
7
|
+
> 本插件由个人开发者发布与维护,**与任何模型厂商均无关联**:不是任何厂商的官方
|
|
8
|
+
> 项目、不代表其立场、未获其背书或授权,也不使用其名称与标识作为本项目的品牌
|
|
9
|
+
> 元素。包名与命令中的 `dsh-` / `bj-` 前缀仅遵循社区插件的命名惯例,用于标识
|
|
10
|
+
> 本插件的兼容目标与命名空间,不代表 dsh 官方发布或认可。
|
|
11
|
+
|
|
12
|
+
安装进你自己的 dsh 后,在对话里直接打开一张 21 点牌桌:赢牌拿游戏币 **CHIP**,CHIP 可以单向兑换为「已兑换额度」,这份额
|
|
13
|
+
度只能在本插件的续命路由里消费——**当且仅当你自己配置的模型额度耗尽时**,插件
|
|
14
|
+
才会用这份额度接住那一次失败的请求,让你不用中断手上的任务去充值或等待。
|
|
15
|
+
|
|
16
|
+
参与**永远免费**:每天有固定的免费手数,玩输了不扣任何东西,也不影响你自己的
|
|
17
|
+
密钥或余额。全部规则细节见下方「参与规则」。
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 这是什么、怎么玩
|
|
22
|
+
|
|
23
|
+
牌桌由一个牌局服务端(`packages/server`,由运营者自托管)维持权威状态,插件只
|
|
24
|
+
是它的客户端:
|
|
25
|
+
|
|
26
|
+
- **文字命令**:`/blackjack` 系列命令零成本——它们全部走 dsh 的命令层而不是发
|
|
27
|
+
一条模型消息,所以打牌本身不消耗你的任何 token 额度。
|
|
28
|
+
- **图形牌桌**(可选):如果你的 dsh profile 带了 Web GUI(例如
|
|
29
|
+
`@deepseek-ai/dsh-web-app`),对话流里会出现一张可点击的牌桌卡片,动作按钮
|
|
30
|
+
与文字命令等价,只是换了个界面。没有 Web GUI 的纯终端 profile 里,文字命令
|
|
31
|
+
照常可用,插件不会因为缺一个可选服务而报错。
|
|
32
|
+
|
|
33
|
+
一局牌:`/blackjack deal` 开局(默认用当天的免费手,也可以 `deal <注额>` 用余
|
|
34
|
+
额加注)→ 按提示用 `bj-hit` / `bj-stand` 等动作命令打完 → 结算,赢家拿 CHIP。
|
|
35
|
+
|
|
36
|
+
### 命令表
|
|
37
|
+
|
|
38
|
+
| 命令 | 作用 |
|
|
39
|
+
|---|---|
|
|
40
|
+
| `/blackjack` | 查看当前状态:有牌局在打就显示牌局,没有就显示余额/免费手/兑换门槛 |
|
|
41
|
+
| `/blackjack agree` | 显示须知全文(第三方声明、不可提现、不可转让等)。**只显示,不代表同意** |
|
|
42
|
+
| `/blackjack agree confirm` | 阅读须知后确认同意,注册牌桌账户。**这一步之前,开局与兑换都会被拒绝** |
|
|
43
|
+
| `/blackjack deal [注额]` | 开一局新牌。不带参数用当天的免费手;带一个正整数注额则从余额里加注 |
|
|
44
|
+
| `/blackjack exchange [数量]` | 把达标余额兑换为可消费额度。**首次**(还没绑过 GitHub)会给出一个配对链接,需要去浏览器完成绑定;**绑定之后**带上数量就在命令里直接兑换,不再需要浏览器 |
|
|
45
|
+
| `/blackjack reset confirm` | 清除**本机保存**的牌桌凭证(服务端记录不受影响,重新 `agree confirm` 会注册一个新账户) |
|
|
46
|
+
| `/bj-hit` | 要一张牌 |
|
|
47
|
+
| `/bj-stand` | 停牌,进入结算 |
|
|
48
|
+
| `/bj-double` | 加倍注额并再要一张牌 |
|
|
49
|
+
| `/bj-split` | 把一对相同点数的牌分成两手 |
|
|
50
|
+
| `/bj-insure` | 庄家明牌为 A 时购买保险 |
|
|
51
|
+
| `/bj-decline` | 不购买保险 |
|
|
52
|
+
|
|
53
|
+
`bj-*` 六个动作命令只在有进行中的牌局时才有意义;没有牌局时执行会提示先
|
|
54
|
+
`/blackjack deal`。
|
|
55
|
+
|
|
56
|
+
### 兑换的两条路径
|
|
57
|
+
|
|
58
|
+
兑换要求绑定 GitHub(一个 GitHub 身份只对应一个钱包,这是奖池出口唯一的反小号
|
|
59
|
+
闸门)。绑定这件事只能在浏览器里完成——它走的是 GitHub 官方的 OAuth 授权码流。
|
|
60
|
+
但**绑定只需要做一次**:
|
|
61
|
+
|
|
62
|
+
1. **首次**:`/blackjack exchange` 给你一个短期配对码和链接。在浏览器里打开、
|
|
63
|
+
点「用 GitHub 继续」完成绑定,然后在同一页面的表单里填数量兑换。
|
|
64
|
+
2. **之后**:`/blackjack exchange <数量>`。插件用它自己持有的令牌直接调服务端
|
|
65
|
+
的兑换接口,**不再生成配对码、不再需要打开浏览器**。不带数量时会先回显你的
|
|
66
|
+
余额与门槛,并提示补上数量——兑换不可逆,插件不会替你猜一个数字。
|
|
67
|
+
|
|
68
|
+
余额没到门槛、或者数量超过余额,都会得到一句明确的提示(说清楚门槛是多少 CHIP、
|
|
69
|
+
或者干脆说余额不够),账本一分不动。提示的语言跟 `locale` 配置项走——默认是
|
|
70
|
+
英文,见下面「配置」一节。
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 安装
|
|
75
|
+
|
|
76
|
+
```sh
|
|
77
|
+
dsh plugin --profile web add -w dsh-blackjack
|
|
78
|
+
dsh --profile web
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
把 `web` 换成你实际要装的 profile 名。包已经发布到 npm,上面这条命令可以直接用。
|
|
82
|
+
|
|
83
|
+
### 启动页曾经是空白的(0.2.2 已修)
|
|
84
|
+
|
|
85
|
+
**0.2.2 起,在 dsh 的启动页(新开 dsh 时那个 “Into the Unknown” 界面)直接敲
|
|
86
|
+
`/blackjack` 就能出牌桌**,不用先发一句废话,也不用从侧边栏翻一个旧对话出来。
|
|
87
|
+
|
|
88
|
+
**0.2.1 及更早版本不行**:那时命令会真的执行,但**画面上什么都不会出现**,看起来
|
|
89
|
+
就像插件坏了。装的是旧版本的话,先让对话真正开始(随便发一句,或从侧边栏打开一个
|
|
90
|
+
已有对话),再执行 `/blackjack agree`。点“新建会话”没用——那会把你送回启动页。
|
|
91
|
+
|
|
92
|
+
原因不在本插件,而在宿主的一行判定(`dsh-client-runtime@0.1.0-rc.6`,
|
|
93
|
+
`lib/client.js:7753`):
|
|
94
|
+
|
|
95
|
+
```js
|
|
96
|
+
function hasVisibleConversationContent(chat) {
|
|
97
|
+
return chat.order.some((key) => chat.nodes.get(key)?.kind !== "command");
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
通用 command 行被**刻意**排除在“可见对话内容”之外,于是会话视图判定这个会话
|
|
102
|
+
还是空的,直接不渲染。宿主自己的 `/goal` 不受影响,是因为 `ui-goal` 额外投影了一个
|
|
103
|
+
**非 command** 的节点把对话激活。本插件照这条路自救:把你敲的那行命令投影成自己的
|
|
104
|
+
一个 Chat 节点(只是把已有事件换个 kind 画出来,不写库、不发新事件、不进模型可见
|
|
105
|
+
的历史)。
|
|
106
|
+
|
|
107
|
+
**这只是插件侧的自救,宿主那个缺口还在**——任何只有服务端、没有浏览器侧的插件,
|
|
108
|
+
今天仍然没有办法让自己的命令在启动页上可见。上游讨论(含根因逐行分析与本插件的
|
|
109
|
+
完整做法):https://github.com/deepseek-ai/deepseek-harness/discussions/4066。
|
|
110
|
+
当初复现用的最小插件(13 行、不碰 slot 也不碰服务)在本仓库
|
|
111
|
+
[`docs/repro/dsh-probe-repro/`](../../docs/repro/dsh-probe-repro/)。
|
|
112
|
+
|
|
113
|
+
> **想改代码、或者想装一个还没发到 npm 的开发版**,从源码装:
|
|
114
|
+
>
|
|
115
|
+
> ```sh
|
|
116
|
+
> git clone https://github.com/yul761/dsh-blackjack
|
|
117
|
+
> cd dsh-blackjack && pnpm install # 会自动构建插件的 dist/
|
|
118
|
+
> dsh plugin --profile web add -w "$PWD/packages/plugin"
|
|
119
|
+
> ```
|
|
120
|
+
>
|
|
121
|
+
> 注意**不能**直接 `pnpm add github:yul761/dsh-blackjack`:这是个 monorepo,
|
|
122
|
+
> 从 git 源装会拿到仓库根(一个没有入口的私有 workspace 根),而不是
|
|
123
|
+
> `packages/plugin` 里的插件包。
|
|
124
|
+
|
|
125
|
+
**为什么一定要带 `-w`:** dsh 的每个 profile 目录本身就是一个 pnpm 工作区(自
|
|
126
|
+
带 `pnpm-workspace.yaml`),而 pnpm 默认拒绝在没有 `-w`/`--workspace-root` 的
|
|
127
|
+
情况下向工作区**根**添加依赖——省掉这个参数,安装命令会直接失败退出,而不是装
|
|
128
|
+
出一个残缺的状态。这不是本插件独有的坑,其它社区 dsh 插件的安装文档也踩过同一
|
|
129
|
+
处并确认了同样的结论。
|
|
130
|
+
|
|
131
|
+
### 版本要求:本插件锁定 dsh `0.1.0-rc.6`
|
|
132
|
+
|
|
133
|
+
插件把 `@deepseek-ai/dsh-*` 的 peer 全部**精确锁**在 `0.1.0-rc.6`,不是写成
|
|
134
|
+
`^0.1.0-rc.6`。这不是保守,是必须的:这套包锁步发布,rc 之间并不互相兼容,而
|
|
135
|
+
`^` 会让包管理器给宿主没有的那个 peer 装上更新的 rc——0.1.0 就是这么崩的
|
|
136
|
+
(`dsh-llm-deepseek@rc.8` 配宿主的 `dsh-llm@rc.6`,启动即报
|
|
137
|
+
`does not provide an export named 'offloadRequestImages'`)。
|
|
138
|
+
|
|
139
|
+
所以:**请用 `dsh@0.1.0-rc.6`**。跑在别的 rc 上会看到 peer 版本警告,届时等插件
|
|
140
|
+
跟进发新版即可。
|
|
141
|
+
|
|
142
|
+
安装成功后,dsh 会读取插件 `package.json` 里的 `dsh.bundle.patch` 声明并自动把
|
|
143
|
+
`blackjack.cordis.yml` 接进 profile 的 bundle 层——不需要你手工再写一份
|
|
144
|
+
`--patch` 覆盖层。
|
|
145
|
+
|
|
146
|
+
### 配置
|
|
147
|
+
|
|
148
|
+
装完开箱即用:`serverUrl` 默认指向本项目运营者自托管的那一份服务,不改配置也能
|
|
149
|
+
直接玩。要在 profile 的 `cordis.patch.yml` 里覆盖也可以:
|
|
150
|
+
|
|
151
|
+
| 配置项 | 类型 | 默认值 | 说明 |
|
|
152
|
+
|---|---|---|---|
|
|
153
|
+
| `serverUrl` | string | `https://server-production-493c.up.railway.app` | 牌局服务端地址。**想自建奖池就改成你自己的部署地址**——服务端与本插件同仓开源,见仓库根 [README.zh-CN.md](../../README.zh-CN.md) 的部署一节 |
|
|
154
|
+
| `enabled` | boolean | `true` | 设为 `false` 可保留安装但完全停用:不注册任何命令,也不挂任何路由或续命钩子 |
|
|
155
|
+
| `locale` | `'en'` \| `'zh'` | `'en'` | `/blackjack` 与 `/bj-*` 等**命令**回复的语言。**图形牌桌不看这一项**——它自动跟随 dsh 自己的 Settings → General → Language;用图形界面的玩家完全不用管这个配置。只有纯终端(没有 Web GUI)的 profile,才需要在这里手动设一次,因为 Node 侧读不到宿主的语言设置 |
|
|
156
|
+
|
|
157
|
+
```yaml
|
|
158
|
+
# ~/.dsh/profiles/<profile>/cordis.patch.yml
|
|
159
|
+
- id: blackjack
|
|
160
|
+
config:
|
|
161
|
+
serverUrl: https://your-blackjack-server.example.com
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## 续命路由到底怎么触发(务必读完)
|
|
167
|
+
|
|
168
|
+
插件会在 `llm/stream` 上挂一个观察者,但它**绝不会拦截正常调用**。触发续命只
|
|
169
|
+
有在同时满足以下条件时才会发生:
|
|
170
|
+
|
|
171
|
+
1. 这次调用用的是**你自己配置的模型 provider**(不是插件自己的奖池路由,避免
|
|
172
|
+
死循环);
|
|
173
|
+
2. 这次调用**没有产出任何内容**——哪怕已经吐出一个字,也不算数,插件绝不会在
|
|
174
|
+
内容已经开始返回后中途换源,那样只会造成重复或截断;
|
|
175
|
+
3. 调用是以**配额耗尽**这一类错误收尾的(不是网络错误、不是参数错误、不是别
|
|
176
|
+
的任何原因);
|
|
177
|
+
4. 你在本插件里**已经完成过 `/blackjack agree confirm`**(本机存有牌桌凭证;没有这份
|
|
178
|
+
凭证,插件根本不会尝试)。
|
|
179
|
+
|
|
180
|
+
这里只看凭证,**不预先检查你的已兑换额度够不够**。这是刻意的:查余额要在每
|
|
181
|
+
次失败调用上多打一次网络请求,拿到的还是一个可能已经过期的快照;而"有凭证、
|
|
182
|
+
余额为零"这条路本身就是安全的——奖池调用会被服务端直接拒绝,插件于是把**你
|
|
183
|
+
原本的那个错误**原样交还给你,跟没装插件完全一样。
|
|
184
|
+
|
|
185
|
+
四条同时成立时,插件才会把这一次失败的调用重发到奖池路由。
|
|
186
|
+
|
|
187
|
+
**奖池用哪个模型(重要):** 重发到奖池的请求,**由运营者在服务端指定模型**,
|
|
188
|
+
不一定是你在界面上选中的那个。这是刻意的——奖池花的是运营者自己的上游余额,
|
|
189
|
+
型号由出钱的人定;这样也免去了"你选了个服务端不认识的型号,续命白接管一场"
|
|
190
|
+
这类失败。所以一次由奖池完成的调用,**界面上显示的型号可能与实际服务的型号
|
|
191
|
+
不同**。当前运营者实例用的是 `deepseek-v4-flash`。
|
|
192
|
+
|
|
193
|
+
**同一时刻最多 4 次并发奖池调用。** 服务端把单玩家并发上限设为 4:dsh 创建会
|
|
194
|
+
话的那一轮会同时发 3 个 LLM 调用,闸设成 1 时另外两个会被直接拒掉,续命恰好在
|
|
195
|
+
最该生效的第一句话上失效(2026-08-22 真机实测后从 1 改到 4,3 加一格余量)。超
|
|
196
|
+
出上限会得到明确的「请求太频繁」(429)。另外,奖池请求会先冻结一笔估算额度,
|
|
197
|
+
刚过兑换门槛的余额只装得下一笔,所以真并发时后面几笔可能因额度不足被拒——那是
|
|
198
|
+
一条写清了「需要多少 / 还有多少」的明确提示,不是含糊的失败。
|
|
199
|
+
|
|
200
|
+
**切换提示去哪了(请注意):** 成功切换时插件会输出一行「已切换至奖池余额继续
|
|
201
|
+
本次请求。」,但在当前 dsh 版本里它**只写进 dsh 自己的日志,不会出现在对话界面
|
|
202
|
+
里**——这个版本没有给插件提供任何"往对话里插一条非模型消息"的通道,唯一现成的
|
|
203
|
+
通道会把内容写进模型可见的历史,那是本项目明确不做的(不污染上下文、不让模型看
|
|
204
|
+
到)。换句话说:一次调用可能是靠奖池额度完成的,而你在界面上看不到提示;要确认
|
|
205
|
+
消耗,请用 `/blackjack` 查看已兑换额度的变化。等 dsh 提供插件可用的会话内通知位,
|
|
206
|
+
这条会改成界面内提示。
|
|
207
|
+
|
|
208
|
+
**如果奖池也用完了、或者奖池服务本身连不上,插件不会伪造一个成功结果**——你会
|
|
209
|
+
原样收到你自己那次调用本来的失败原因,就像没装这个插件一样。换句话说:装上这
|
|
210
|
+
个插件,一次调用的结果**只可能不变或变好,不会变坏**。
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## 参与规则
|
|
215
|
+
|
|
216
|
+
- **参与永远免费。** 每日固定几手免费牌,零成本参与;不存在、也不会出现"付费
|
|
217
|
+
换手数 / 换概率 / 换赔率"的任何设计。
|
|
218
|
+
- **CHIP 与已兑换额度单向、封闭。**
|
|
219
|
+
- **不可提现**,不折算任何法定货币,界面不显示金额;
|
|
220
|
+
- **不可转让**(含玩家之间转让),不可退换;
|
|
221
|
+
- **不会进入任何模型厂商的账户**;
|
|
222
|
+
- **唯一用途**是通过牌局服务端的续命路由消费——除此之外没有任何出口。
|
|
223
|
+
- **不活跃会回收。** 连续 7 天无游戏且无消费的账户,余额会被回收进奖池。
|
|
224
|
+
- **已兑换额度目前不会过期。** 早期文案写作"有效期至本赛季末",但服务端的赛季
|
|
225
|
+
结算并没有触碰已兑换额度,那句话与实现不符,因此改成如实描述。是否在赛季末
|
|
226
|
+
清零尚未决定;真要做,会先公告。
|
|
227
|
+
- **你自己的 API key 永远不会进入这套系统**:插件不读取、不托管、不代管你自
|
|
228
|
+
己 provider 的任何密钥,续命路由用的是牌局服务端自己签发的凭证。
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## 卸载与清除本机凭证
|
|
233
|
+
|
|
234
|
+
只想清掉本地保存的牌桌登录凭证(例如换机器、或者想用一个全新账户重新开始),
|
|
235
|
+
不需要卸载插件:
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
/blackjack reset confirm
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
(不带 `confirm` 时会先回显一遍这条操作的说明,避免误触。)这只清除**本机**保
|
|
242
|
+
存的凭证,服务端那边的账户记录不受影响;用 `/blackjack agree confirm` 即可重新注册一
|
|
243
|
+
个新账户。
|
|
244
|
+
|
|
245
|
+
要完全移除插件本身:
|
|
246
|
+
|
|
247
|
+
```sh
|
|
248
|
+
dsh plugin --profile web remove dsh-blackjack
|
|
249
|
+
```
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { ConversationNodeDefinition } from '@deepseek-ai/dsh-client-runtime/client';
|
|
2
|
+
import type { SessionEvent } from '@deepseek-ai/dsh-session/types';
|
|
3
|
+
/**
|
|
4
|
+
* 让插件的命令在 dsh **启动页**上也能出现在画面里。
|
|
5
|
+
*
|
|
6
|
+
* 症结不在这个插件(上游 discussions/4066 里 Jstn-1g 对着 master `b150a551`
|
|
7
|
+
* 逐行追过,rc.6 的产物里逻辑一模一样):
|
|
8
|
+
*
|
|
9
|
+
* ```js
|
|
10
|
+
* // dsh-client-runtime@0.1.0-rc.6, lib/client.js:7753
|
|
11
|
+
* function hasVisibleConversationContent(chat) {
|
|
12
|
+
* return chat.order.some((key) => chat.nodes.get(key)?.kind !== "command");
|
|
13
|
+
* }
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* 通用 command 行被**刻意**排除在"可见对话内容"之外(宿主那边还有一条回归测试
|
|
17
|
+
* 钉着这个行为),于是 `composerPhase` 停在 `blank`,`ConversationSession` 直接
|
|
18
|
+
* `return null`——命令确实执行了,返回值也没丢,就是整屏什么都不画。宿主自己的
|
|
19
|
+
* `/goal` 不受影响,因为 `ui-goal` 额外投影了一个**非 command** 的
|
|
20
|
+
* `command-input` 节点,那个节点把 Chat 激活之后,通用结果行才跟着可见。
|
|
21
|
+
*
|
|
22
|
+
* 这个模块就是照 `ui-goal` 那条路走:把玩家敲的那行命令投影成一个自己的 Chat
|
|
23
|
+
* 节点。**只是把已有的 `command/run` 事件换个 kind 画出来**——不写库、不发新
|
|
24
|
+
* 事件、不往模型可见的历史里塞任何东西(这是本项目的红线:上下文不污染)。
|
|
25
|
+
*
|
|
26
|
+
* @module dsh-blackjack/client/command-input
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* 这个节点的 kind。**唯一的硬性要求是它不等于 `'command'`**——见上面那段
|
|
30
|
+
* predicate,等于 `'command'` 的话启动页照旧是白的,这整个模块就白写了。
|
|
31
|
+
*
|
|
32
|
+
* 不复用 `ui-goal` 的 `'command-input'`:那个 key 由 `ui-goal` 在
|
|
33
|
+
* `ChatNodeDataMap` 上声明并注册渲染器,蹭它等于把自己的显示交给一个不一定
|
|
34
|
+
* 装了的插件;带上自己的前缀,keyed 槽各认各的。
|
|
35
|
+
*/
|
|
36
|
+
export declare const BLACKJACK_COMMAND_NODE_KIND = "blackjack-command-input";
|
|
37
|
+
/**
|
|
38
|
+
* 从 `command/run` 还原玩家实际敲的那一行。
|
|
39
|
+
*
|
|
40
|
+
* `args` 是 `parseCommand` 原样保留的 rawInput,**包含分隔用的空白**——所以
|
|
41
|
+
* `/blackjack` 单独一条命令的 args 是 `' '`,直接拼会得到 `'/blackjack '`。
|
|
42
|
+
* 照 `ui-goal` 的 `goalCommandText` 一样只 `trimEnd()`:中间的空格是玩家自己
|
|
43
|
+
* 打的(`/blackjack agree confirm`),不能一起吃掉。
|
|
44
|
+
*/
|
|
45
|
+
export declare function commandLineOf(event: SessionEvent<'command/run'>): string;
|
|
46
|
+
/** 投影状态:够画一行命令输入,仅此而已。 */
|
|
47
|
+
interface CommandInputState {
|
|
48
|
+
readonly commandId: string;
|
|
49
|
+
readonly seq: number;
|
|
50
|
+
readonly time: number;
|
|
51
|
+
readonly text: string;
|
|
52
|
+
}
|
|
53
|
+
/** 这一行的 Chat 载荷。 */
|
|
54
|
+
export interface BlackjackCommandInputData {
|
|
55
|
+
readonly commandId: string;
|
|
56
|
+
readonly text: string;
|
|
57
|
+
readonly time: number;
|
|
58
|
+
}
|
|
59
|
+
declare module '@deepseek-ai/dsh-client-ui-conversation/client' {
|
|
60
|
+
interface ChatNodeDataMap {
|
|
61
|
+
/** 玩家敲的那行 `/blackjack` 或 `/bj-*`。 */
|
|
62
|
+
'blackjack-command-input': BlackjackCommandInputData;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 每条 `command/run` 自成一个 Context——命令的生命周期 id 就是稳定业务 id,
|
|
67
|
+
* 没有后续 update(结果行由宿主自己的通用 command 节点负责,本插件不碰)。
|
|
68
|
+
*/
|
|
69
|
+
export declare const blackjackCommandInputDefinition: ConversationNodeDefinition<CommandInputState>;
|
|
70
|
+
export {};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 让插件的命令在 dsh **启动页**上也能出现在画面里。
|
|
3
|
+
*
|
|
4
|
+
* 症结不在这个插件(上游 discussions/4066 里 Jstn-1g 对着 master `b150a551`
|
|
5
|
+
* 逐行追过,rc.6 的产物里逻辑一模一样):
|
|
6
|
+
*
|
|
7
|
+
* ```js
|
|
8
|
+
* // dsh-client-runtime@0.1.0-rc.6, lib/client.js:7753
|
|
9
|
+
* function hasVisibleConversationContent(chat) {
|
|
10
|
+
* return chat.order.some((key) => chat.nodes.get(key)?.kind !== "command");
|
|
11
|
+
* }
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* 通用 command 行被**刻意**排除在"可见对话内容"之外(宿主那边还有一条回归测试
|
|
15
|
+
* 钉着这个行为),于是 `composerPhase` 停在 `blank`,`ConversationSession` 直接
|
|
16
|
+
* `return null`——命令确实执行了,返回值也没丢,就是整屏什么都不画。宿主自己的
|
|
17
|
+
* `/goal` 不受影响,因为 `ui-goal` 额外投影了一个**非 command** 的
|
|
18
|
+
* `command-input` 节点,那个节点把 Chat 激活之后,通用结果行才跟着可见。
|
|
19
|
+
*
|
|
20
|
+
* 这个模块就是照 `ui-goal` 那条路走:把玩家敲的那行命令投影成一个自己的 Chat
|
|
21
|
+
* 节点。**只是把已有的 `command/run` 事件换个 kind 画出来**——不写库、不发新
|
|
22
|
+
* 事件、不往模型可见的历史里塞任何东西(这是本项目的红线:上下文不污染)。
|
|
23
|
+
*
|
|
24
|
+
* @module dsh-blackjack/client/command-input
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* 这个节点的 kind。**唯一的硬性要求是它不等于 `'command'`**——见上面那段
|
|
28
|
+
* predicate,等于 `'command'` 的话启动页照旧是白的,这整个模块就白写了。
|
|
29
|
+
*
|
|
30
|
+
* 不复用 `ui-goal` 的 `'command-input'`:那个 key 由 `ui-goal` 在
|
|
31
|
+
* `ChatNodeDataMap` 上声明并注册渲染器,蹭它等于把自己的显示交给一个不一定
|
|
32
|
+
* 装了的插件;带上自己的前缀,keyed 槽各认各的。
|
|
33
|
+
*/
|
|
34
|
+
export const BLACKJACK_COMMAND_NODE_KIND = 'blackjack-command-input';
|
|
35
|
+
/** 本插件拥有的命令:牌桌主命令,以及六个 `bj-` 动作命令。 */
|
|
36
|
+
function isOwnCommand(name) {
|
|
37
|
+
return name === 'blackjack' || name.startsWith('bj-');
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* 从 `command/run` 还原玩家实际敲的那一行。
|
|
41
|
+
*
|
|
42
|
+
* `args` 是 `parseCommand` 原样保留的 rawInput,**包含分隔用的空白**——所以
|
|
43
|
+
* `/blackjack` 单独一条命令的 args 是 `' '`,直接拼会得到 `'/blackjack '`。
|
|
44
|
+
* 照 `ui-goal` 的 `goalCommandText` 一样只 `trimEnd()`:中间的空格是玩家自己
|
|
45
|
+
* 打的(`/blackjack agree confirm`),不能一起吃掉。
|
|
46
|
+
*/
|
|
47
|
+
export function commandLineOf(event) {
|
|
48
|
+
return `/${event.data.name}${(event.data.args ?? '').trimEnd()}`;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 每条 `command/run` 自成一个 Context——命令的生命周期 id 就是稳定业务 id,
|
|
52
|
+
* 没有后续 update(结果行由宿主自己的通用 command 节点负责,本插件不碰)。
|
|
53
|
+
*/
|
|
54
|
+
export const blackjackCommandInputDefinition = {
|
|
55
|
+
kind: 'blackjack-command-input',
|
|
56
|
+
target: 'chat',
|
|
57
|
+
match: (event) => (event.type === 'command/run' && isOwnCommand(event.data.name)
|
|
58
|
+
? { id: String(event.data.commandId), role: 'start' }
|
|
59
|
+
: null),
|
|
60
|
+
start: (_context, match) => {
|
|
61
|
+
if (match.event.type !== 'command/run') {
|
|
62
|
+
throw new Error('blackjack-command-input start requires command/run');
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
commandId: String(match.event.data.commandId),
|
|
66
|
+
seq: match.event.seq,
|
|
67
|
+
time: match.event.time,
|
|
68
|
+
text: commandLineOf(match.event),
|
|
69
|
+
};
|
|
70
|
+
},
|
|
71
|
+
update: (context) => context.state,
|
|
72
|
+
buildViewNode: (context) => {
|
|
73
|
+
const state = context.state;
|
|
74
|
+
if (state === undefined)
|
|
75
|
+
return null;
|
|
76
|
+
return {
|
|
77
|
+
key: context.key,
|
|
78
|
+
kind: BLACKJACK_COMMAND_NODE_KIND,
|
|
79
|
+
id: context.id,
|
|
80
|
+
target: 'chat',
|
|
81
|
+
// 结果行锚在 `seq` 上,输入行要排在它**前面**——照 ui-goal 用 -0.1,
|
|
82
|
+
// 一个既小于本条 seq、又大于上一条整数 seq 的位置。
|
|
83
|
+
anchorSeq: state.seq - 0.1,
|
|
84
|
+
location: context.start?.location ?? { kind: 'unresolved' },
|
|
85
|
+
visibility: 'visible',
|
|
86
|
+
data: { commandId: state.commandId, text: state.text, time: state.time },
|
|
87
|
+
};
|
|
88
|
+
},
|
|
89
|
+
};
|
package/dist/client/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useState, useSyncExternalStore } from 'react';
|
|
3
3
|
import { BlackjackTable, Linkified } from './Table.js';
|
|
4
|
+
import { BLACKJACK_COMMAND_NODE_KIND, blackjackCommandInputDefinition } from './command-input.js';
|
|
4
5
|
import { reduce, rowPlan } from './state.js';
|
|
5
6
|
import { resolveMessages } from '../i18n/index.js';
|
|
6
7
|
/**
|
|
@@ -157,7 +158,32 @@ function BlackjackTableContainer({ node, locale: localeRuntime }) {
|
|
|
157
158
|
return notice;
|
|
158
159
|
return (_jsxs("div", { children: [notice, _jsx(BlackjackTable, { state: state, m: m, onAction: onAction, onDeal: onDeal, onDismiss: onDismiss, error: hasActionError ? m.actionFailedNotice() : undefined })] }));
|
|
159
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* 玩家敲的那行命令。样式内联(和 notice 那段同理:靠 class 的话这一行在没挂载
|
|
163
|
+
* 牌桌 `<style>` 的场合会失去全部样式),刻意做得像一条输入回显而不是一张卡片
|
|
164
|
+
* ——它存在的理由是把 Chat 激活,不是抢戏。
|
|
165
|
+
*/
|
|
166
|
+
function CommandInputRow({ node }) {
|
|
167
|
+
return (_jsx("div", { style: { opacity: 0.75, fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace', margin: '4px 0' }, children: node.data.text }));
|
|
168
|
+
}
|
|
160
169
|
export function apply(ctx) {
|
|
170
|
+
// 启动页可见性:把玩家敲的命令投影成一个**非 `command`** 的 Chat 节点,
|
|
171
|
+
// 宿主的 `hasVisibleConversationContent` 因此判定会话有内容,
|
|
172
|
+
// `ConversationSession` 不再 `return null`。详见 ./command-input.ts 的模块注释。
|
|
173
|
+
//
|
|
174
|
+
// `ctx.get('conversationEvents', false)` 而不是把它加进 `inject`:这是锦上添花
|
|
175
|
+
// 的一层,缺了它最坏也只是回到现在这个"启动页什么都不显示"的状态;写进 inject
|
|
176
|
+
// 的话,任何一个没有这个服务的 profile 会连整个 client 模块一起加载失败,
|
|
177
|
+
// 连对话里的牌桌都没了——那是把"只可能不变或变好"这条承诺反过来了。
|
|
178
|
+
const conversationEvents = ctx.get('conversationEvents', false);
|
|
179
|
+
if (conversationEvents !== undefined) {
|
|
180
|
+
conversationEvents.register(blackjackCommandInputDefinition);
|
|
181
|
+
ctx.slots.inject('conversation.chat.node', () => ctx.slots.register({
|
|
182
|
+
name: 'conversation.chat.node',
|
|
183
|
+
key: BLACKJACK_COMMAND_NODE_KIND,
|
|
184
|
+
inject: () => ({}),
|
|
185
|
+
}, CommandInputRow));
|
|
186
|
+
}
|
|
161
187
|
ctx.slots.inject('conversation.chat.commandview', () => ctx.slots.register({
|
|
162
188
|
name: 'conversation.chat.commandview',
|
|
163
189
|
key: 'blackjack',
|
package/dist/client.js
CHANGED
|
@@ -3,7 +3,7 @@ window.__ModuleLoader__.load({
|
|
|
3
3
|
factory: (require) => {
|
|
4
4
|
var module = { exports: {} };
|
|
5
5
|
var exports = module.exports;
|
|
6
|
-
"use strict";var v=Object.defineProperty;var
|
|
6
|
+
"use strict";var v=Object.defineProperty;var U=Object.getOwnPropertyDescriptor;var O=Object.getOwnPropertyNames;var Y=Object.prototype.hasOwnProperty;var X=(e,n)=>{for(var t in n)v(e,t,{get:n[t],enumerable:!0})},W=(e,n,t,o)=>{if(n&&typeof n=="object"||typeof n=="function")for(let i of O(n))!Y.call(e,i)&&i!==t&&v(e,i,{get:()=>n[i],enumerable:!(o=U(n,i))||o.enumerable});return e};var z=e=>W(v({},"__esModule",{value:!0}),e);var ye={};X(ye,{apply:()=>ke,inject:()=>pe,resolveLocaleId:()=>M,subscribeToLocale:()=>D,useLocaleId:()=>F});module.exports=z(ye);var u=require("react");var j=require("react");function S(e,n){return e.mode==="free"?n.freeStakeLine(e.betChips):n.raiseStakeLine(e.betChips)}function I(e,n){let t=e.payoutChips??0,o=e.betChips;return e.hands.length>0&&e.hands.every(i=>i.outcome==="push")?e.mode==="free"?n.freePush():n.raisePush(o):t>0?e.mode==="free"?n.freeWin(t):n.raiseWin(t,o):e.mode==="free"?n.freeLose():n.raiseLose(o)}var a=require("react/jsx-runtime"),_={S:"\u2660",H:"\u2665",D:"\u2666",C:"\u2663"},Q=new Set(["H","D"]),P=e=>e.toLocaleString("en-US"),K=`
|
|
7
7
|
.bj-table { --bj-felt: #0f3d2e; --bj-line: rgba(255,255,255,.18); font-size: 14px; }
|
|
8
8
|
.bj-hand { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; margin: 4px 0; }
|
|
9
9
|
.bj-card {
|
|
@@ -38,17 +38,17 @@ window.__ModuleLoader__.load({
|
|
|
38
38
|
color: #c0392b; border: 1px solid rgba(192,57,43,.4); border-radius: 6px;
|
|
39
39
|
padding: 4px 8px; margin-bottom: 8px;
|
|
40
40
|
}
|
|
41
|
-
`;function L({text:e}){let t=e.split(/(https?:\/\/[^\s,。))]+)/g);return(0,a.jsx)(a.Fragment,{children:t.map((n,i)=>/^https?:\/\//.test(n)?(0,a.jsx)("a",{href:n,target:"_blank",rel:"noreferrer noopener",children:n},i):n)})}function I({card:e,m:t}){if("hidden"in e)return(0,a.jsx)("div",{className:"bj-card bj-card-back","aria-label":t.hiddenCardLabel()});let n=z[e.suit]??e.suit,i=O.has(e.suit)?"bj-card-red":"bj-card-black";return(0,a.jsx)("div",{className:`bj-card ${i}`,children:`${e.rank}${n}`})}function _({hand:e,label:t,m:n}){return(0,a.jsxs)("div",{children:[(0,a.jsxs)("div",{className:"bj-meta",children:[t,n.totalInParens(e.total,e.soft),e.doubled?n.doubled():""]}),(0,a.jsx)("div",{className:"bj-hand",children:e.cards.map((i,o)=>(0,a.jsx)(I,{card:i,m:n},o))})]})}function K({dealer:e,m:t}){return(0,a.jsxs)("div",{children:[(0,a.jsxs)("div",{className:"bj-meta",children:[t.tableDealerLabel(),e.total!==void 0?t.totalInParens(e.total,!1):""]}),(0,a.jsx)("div",{className:"bj-hand",children:e.cards.map((n,i)=>(0,a.jsx)(I,{card:n,m:t},i))})]})}function Z({m:e}){return(0,a.jsx)("div",{className:"bj-table",children:(0,a.jsx)("div",{style:{whiteSpace:"pre-wrap"},children:e.consentText()})})}function ee({me:e,m:t}){let n=e.exchangeThresholdChips-e.chips;return(0,a.jsxs)("div",{className:"bj-meta",children:[t.tableBalanceHeader(e.chips,e.freeHandsRemaining),e.points>0?t.tablePointsSuffix(e.points):"",e.exchangedChips>0?t.tableExchangedSuffix(e.exchangedChips):"",(0,a.jsx)("div",{className:"bj-note",children:n<=0?t.tableExchangeThresholdReached(e.exchangeThresholdChips):t.tableExchangeThresholdShort(n,e.exchangeThresholdChips)}),e.exchangedChips>0?(0,a.jsx)("div",{className:"bj-note",children:t.tableExchangedQuotaNote(e.exchangedChips)}):null,e.poolEmpty?(0,a.jsx)("div",{children:t.tablePoolEmptyNotice()}):null,e.sponsorText?(0,a.jsx)("div",{children:e.sponsorText}):null]})}function te({round:e,m:t}){return(0,a.jsxs)("div",{className:"bj-meta",children:[e.phase==="settled"?P(e,t):j(e,t),e.pointsGranted?(0,a.jsx)("div",{children:t.pointsGrantedNotice(e.pointsGranted)}):null]})}function ne(e,t){return e==="double"||e==="split"?t:e==="insure"?Math.ceil(t/2):0}function ae({round:e,me:t,onAction:n,m:i}){if(e.actions.length===0)return null;let o=t?.chips??0,d=i.tableActionLabels;return(0,a.jsx)("div",{className:"bj-actions",children:e.actions.map(l=>{let c=ne(l,e.betChips),h=c>o;return(0,a.jsxs)("button",{type:"button",disabled:h,title:h?i.actionShortfallHint(c):void 0,onClick:()=>n(l),children:[d[l]??l,c>0?i.tableCostSuffix(c):""]},l)})})}function ie(e,t,n){return!Number.isInteger(e)||e<1?n.invalidBetAmount():e>t.maxChips?n.betAmountExceedsCap(t.maxChips):e>t.balanceChips?n.betAmountExceedsBalance():null}function oe({me:e,onDeal:t,freeLabel:n,m:i}){let o=e?.freeHandsRemaining??0,d=e?[e.baseBetChips,e.baseBetChips*2]:[];return(0,a.jsxs)("div",{className:"bj-actions",children:[(0,a.jsxs)("button",{type:"button",disabled:o<=0,onClick:()=>t(),children:[n,e?i.dealButtonFreeHandsSuffix(o):""]}),d.map(l=>(0,a.jsxs)("button",{type:"button",disabled:(e?.chips??0)<l,onClick:()=>t(l),children:[S(l)," CHIP"]},l))]})}function se({me:e,onDeal:t,m:n}){let i=e.maxRaiseBetChips,[o,d]=(0,H.useState)(!1),[l,c]=(0,H.useState)("");if(i===void 0)return null;let h=Number(l),g=l.trim()===""?n.betAmountMissing():ie(h,{balanceChips:e.chips,maxChips:i},n);return(0,a.jsxs)("div",{children:[(0,a.jsx)("div",{className:"bj-actions",children:(0,a.jsx)("button",{type:"button",onClick:()=>d(!o),children:n.customBetToggle()})}),o?(0,a.jsxs)("div",{className:"bj-custom",children:[(0,a.jsx)("input",{type:"number",min:1,max:i,value:l,onChange:x=>c(x.target.value),"aria-label":n.customBetToggle()}),(0,a.jsx)("button",{type:"button",disabled:g!==null,onClick:()=>t(h),children:n.customBetSubmit()}),(0,a.jsx)("span",{className:"bj-hint",children:g??n.customBetRange(i)})]}):null]})}function re({me:e,onDeal:t,m:n}){let i=e?.freeHandsRemaining??0,o=e?.baseBetChips??0,d=e?[o,o*2]:[],l=e?d.some(c=>e.chips>=c):!1;return(0,a.jsxs)("div",{children:[(0,a.jsx)("div",{className:"bj-meta",children:n.noActiveRoundNotice()}),(0,a.jsxs)("div",{className:"bj-group",children:[(0,a.jsxs)("div",{className:"bj-group-title",children:[n.freeHandsGroupTitle(),i>0?n.freeHandsRemainingToday(i):n.freeHandsExhaustedToday()]}),e?(0,a.jsx)("div",{className:"bj-note",children:n.freeHandExplainer(o,Math.floor(o*1.5))}):null,(0,a.jsx)("div",{className:"bj-actions",children:(0,a.jsx)("button",{type:"button",disabled:i<=0,onClick:()=>t(),children:n.startFreeHandButton()})})]}),e?(0,a.jsxs)("div",{className:"bj-group",children:[(0,a.jsxs)("div",{className:"bj-group-title",children:[n.raiseGroupTitle(),l?"":n.insufficientBalanceSuffix()]}),(0,a.jsx)("div",{className:"bj-note",children:n.raiseExplainer()}),(0,a.jsx)("div",{className:"bj-actions",children:d.map(c=>(0,a.jsxs)("button",{type:"button",disabled:e.chips<c,onClick:()=>t(c),children:[S(c)," CHIP"]},c))}),(0,a.jsx)(se,{me:e,onDeal:t,m:n})]}):null]})}function le({me:e,onDeal:t,onDismiss:n,m:i}){return(0,a.jsxs)("div",{children:[(0,a.jsx)(oe,{me:e,onDeal:t,freeLabel:i.rematchFreeLabel(),m:i}),(0,a.jsx)("div",{className:"bj-actions",children:(0,a.jsx)("button",{type:"button",onClick:n,children:i.dismissButton()})})]})}function $({state:e,m:t,onAction:n,onDeal:i,onDismiss:o,error:d}){if(!e.consented)return(0,a.jsx)(Z,{m:t});let{me:l,round:c}=e;return(0,a.jsxs)("div",{className:"bj-table",children:[(0,a.jsx)("style",{children:Q}),d?(0,a.jsx)("div",{className:"bj-error",children:d}):null,l?(0,a.jsx)(ee,{me:l,m:t}):null,c?(0,a.jsxs)("div",{children:[c.hands.map((h,g)=>(0,a.jsx)(_,{hand:h,m:t,label:c.hands.length>1?t.tableSplitHandLabel(g+1):t.tableHandLabel()},g)),(0,a.jsx)(K,{dealer:c.dealer,m:t}),(0,a.jsx)(te,{round:c,m:t}),(0,a.jsx)(ae,{round:c,me:l,onAction:n,m:t}),c.phase==="settled"?(0,a.jsx)(le,{me:l,onDeal:i,onDismiss:o,m:t}):null]}):(0,a.jsx)(re,{me:l,onDeal:i,m:t})]})}function f(e,t){switch(t.type){case"synced":return{consented:t.payload.consented,me:t.payload.me,round:t.payload.round};case"round":return{...e,round:t.round};case"meRefreshed":return{...e,me:t.me};case"dismissed":return{...e,round:void 0}}}var ce=new Set(["","deal"]);function T(e){let t=(e.args??"").trim().split(/\s+/)[0]??"",n=ce.has(t),i=e.outcome,o=i?.kind==="error";return n?{table:!0,isError:o,...o&&i?.text?{text:i.text}:{}}:{table:!1,isError:o,...i?.text!==void 0?{text:i.text}:{}}}var s=e=>e.toLocaleString("en-US"),E={freeStakeLine:e=>`\u8FD9\u662F\u514D\u8D39\u7684\u4E00\u5C40\uFF1A\u8D62\u4E86\u62FF ${s(e)} CHIP\uFF0C\u8F93\u4E86\u4E0D\u635F\u5931\u4EFB\u4F55\u4E1C\u897F\u3002`,raiseStakeLine:e=>`\u672C\u5C40\u5DF2\u52A0\u6CE8 ${s(e)} CHIP\uFF0C\u4ECE\u4F60\u7684\u4F59\u989D\u91CC\u51FA\u3002`,freeWin:e=>`\u8D62\u4E86\uFF0C\u62FF\u5230 ${s(e)} CHIP\u3002`,freeLose:()=>"\u8FD9\u624B\u8F93\u4E86\u3002\u514D\u8D39\u624B\u4E0D\u635F\u5931\u4EFB\u4F55\u4E1C\u897F\u3002",freePush:()=>"\u5E73\u5C40\u3002\u8FD9\u624B\u4E0D\u635F\u5931\u4E5F\u4E0D\u83B7\u5F97\u3002",raiseWin:(e,t)=>`\u8D62\u4E86\uFF0C\u62FF\u56DE ${s(e)} CHIP\uFF08\u52A0\u6CE8 ${s(t)}\uFF09\u3002`,raiseLose:e=>`\u8FD9\u624B\u8F93\u4E86\uFF0C\u52A0\u6CE8\u7684 ${s(e)} CHIP \u6536\u4E0D\u56DE\u3002`,raisePush:e=>`\u5E73\u5C40\u3002\u52A0\u6CE8\u7684 ${s(e)} CHIP \u539F\u6837\u9000\u56DE\u3002`,handLabel:()=>"\u4F60\u7684\u724C\uFF1A",splitHandLabel:e=>`\u4F60\u7684\u7B2C ${e} \u624B\uFF1A`,dealerLabel:()=>"\u5E84\u5BB6\uFF1A",totalInParens:(e,t)=>`\uFF08${t?"\u8F6F":""}${e}\uFF09`,doubled:()=>" [\u53CC\u500D]",actionsPrefix:()=>"\u53EF\u9009\uFF1A",actionLabels:{hit:"/bj-hit \u8981\u724C",stand:"/bj-stand \u505C\u724C",double:"/bj-double \u53CC\u500D",split:"/bj-split \u5206\u724C",insure:"/bj-insure \u4E70\u4FDD\u9669",decline:"/bj-decline \u4E0D\u4E70\u4FDD\u9669"},actionsSeparator:()=>"\u3000",balanceLine:(e,t)=>`\u4F59\u989D\uFF1A${s(e)} CHIP\u3000\u5DF2\u5151\u6362\u989D\u5EA6\uFF1A${s(t)} CHIP`,freeHandsLine:(e,t)=>`\u4ECA\u65E5\u5269\u4F59\u514D\u8D39\u624B\uFF1A${e}\u3000\u8D5B\u5B63\uFF1A${t}`,thresholdLine:e=>`\u5151\u6362\u95E8\u69DB\uFF1A${s(e)} CHIP\uFF08\u8FBE\u6807\u540E\u53EF\u7528 /blackjack exchange \u5151\u6362\uFF09`,pointsLine:e=>`\u79EF\u5206\uFF1A${s(e)}`,poolEmptyNotice:()=>"\u63D0\u793A\uFF1A\u672C\u671F\u5956\u6C60\u5DF2\u53D1\u5B8C\uFF0C\u73B0\u5728\u8D62\u724C\u5C06\u53D1\u653E\u79EF\u5206\u3002",pointsGrantedNotice:e=>`\u672C\u671F\u5956\u6C60\u5DF2\u53D1\u5B8C\uFF0C\u6539\u4E3A\u53D1\u653E ${s(e)} \u79EF\u5206\u3002`,chipValidityNotice:()=>"\uFF08\u8D5B\u5B63\u7ED3\u675F\u65F6\uFF0C\u672A\u8FBE\u5151\u6362\u95E8\u69DB\u7684 CHIP \u4F1A\u6298\u7B97\u4E3A\u79EF\u5206\u5E76\u56DE\u6536\uFF1B\u8FBE\u6807\u7684\u7ED3\u8F6C\uFF09",consentText:()=>["\u3010\u9996\u6B21\u4F7F\u7528\u8BF7\u5148\u9605\u8BFB\u3011","\xB7 \u8FD9\u662F\u793E\u533A\u5F00\u53D1\u8005\u53D1\u5E03\u7684\u7B2C\u4E09\u65B9\u5C0F\u6E38\u620F\uFF0C\u4E0E\u4EFB\u4F55\u6A21\u578B\u5382\u5546\u5747\u65E0\u5173\u8054\uFF0C\u4E0D\u4EE3\u8868\u5176\u7ACB\u573A\u3002","\xB7 \u53C2\u4E0E\u514D\u8D39\uFF1A\u6BCF\u65E5\u6709\u9650\u6B21\u514D\u8D39\u624B\uFF0C\u8F93\u4E86\u4E0D\u6263\u4EFB\u4F55\u4E1C\u897F\uFF0C\u4F60\u81EA\u5DF1\u7684\u5BC6\u94A5\u4E0E\u4F59\u989D\u4E0D\u53D7\u5F71\u54CD\u3002","\xB7 \u8D62\u5F97\u7684 CHIP \u53EA\u80FD\u5728\u672C\u63D2\u4EF6\u5185\u5151\u6362\u4E3A\u6A21\u578B\u989D\u5EA6\u6D88\u8D39\uFF1A\u4E0D\u53EF\u63D0\u73B0\u3001\u4E0D\u53EF\u8F6C\u8BA9\u3001\u4E0D\u53EF\u8F6C\u5165\u4EFB\u4F55\u5382\u5546\u8D26\u6237\u3002","\xB7 \u5956\u52B1\u6709\u8D5B\u5B63\u6709\u6548\u671F\uFF0C\u8D5B\u5B63\u7ED3\u675F\u540E\u3001\u6216\u957F\u671F\u4E0D\u6D3B\u8DC3\u65F6\uFF0C\u672A\u4F7F\u7528\u7684\u90E8\u5206\u6309\u89C4\u5219\u56DE\u6536\u3002","","\u9605\u8BFB\u5E76\u540C\u610F\u4EE5\u4E0A\u5185\u5BB9\u540E\uFF0C\u6267\u884C /blackjack agree confirm \u5F00\u59CB\u3002"].join(`
|
|
42
|
-
`),agreeHint:()=>"\u8BF7\u5148\u9605\u8BFB\u987B\u77E5\u5E76\u6267\u884C /blackjack agree \u540E\u518D\u4F7F\u7528\u3002",noRoundHint:()=>"\u5F53\u524D\u6CA1\u6709\u8FDB\u884C\u4E2D\u7684\u724C\u5C40\uFF0C\u5148\u7528 /blackjack deal \u5F00\u4E00\u5C40\u3002",commandUsage:()=>"agree | deal [\u6CE8\u989D] | exchange [\u6570\u91CF] | reset",actionDescriptions:{hit:"21 \u70B9\uFF1A\u5411\u724C\u684C\u8981\u4E00\u5F20\u724C",stand:"21 \u70B9\uFF1A\u505C\u724C\uFF0C\u8FDB\u5165\u7ED3\u7B97",double:"21 \u70B9\uFF1A\u52A0\u500D\u6CE8\u989D\u5E76\u518D\u8981\u4E00\u5F20\u724C",split:"21 \u70B9\uFF1A\u628A\u4E00\u5BF9\u76F8\u540C\u70B9\u6570\u7684\u724C\u5206\u6210\u4E24\u624B",insure:"21 \u70B9\uFF1A\u5E84\u5BB6\u660E\u724C\u4E3A A \u65F6\u8D2D\u4E70\u4FDD\u9669",decline:"21 \u70B9\uFF1A\u4E0D\u8D2D\u4E70\u4FDD\u9669"},blackjackCommandDescription:()=>"21 \u70B9\u724C\u684C\uFF08\u9700\u5148\u8FDB\u5165\u4F1A\u8BDD\uFF09\uFF1A\u67E5\u770B\u72B6\u6001\u3001\u5F00\u5C40\u3001\u5151\u6362\u989D\u5EA6\u3001\u91CD\u7F6E\u51ED\u8BC1",genericFailure:()=>"\u64CD\u4F5C\u6CA1\u6709\u6210\u529F\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\u3002",serviceUnreachable:()=>"\u6682\u65F6\u8FDE\u4E0D\u4E0A\u724C\u684C\u670D\u52A1\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\u3002",dailyFreeHandsExhausted:()=>"\u4ECA\u5929\u7684\u514D\u8D39\u624B\u7528\u5B8C\u4E86\uFF0C\u660E\u5929\u518D\u6765\u3002",belowExchangeThreshold:e=>`\u4F59\u989D\u8FD8\u6CA1\u5230\u5151\u6362\u95E8\u69DB\uFF08\u9700\u8981 ${s(e)} CHIP\uFF09\u3002`,insufficientBalance:()=>"\u4F59\u989D\u4E0D\u8DB3\u4EE5\u5B8C\u6210\u8FD9\u4E2A\u52A8\u4F5C\u3002",roundAlreadyInProgress:()=>"\u5DF2\u7ECF\u6709\u4E00\u5C40\u5728\u8FDB\u884C\u4E2D\u3002",githubBindingRequired:()=>"\u8FD9\u4E2A\u94B1\u5305\u7684 GitHub \u7ED1\u5B9A\u72B6\u6001\u6709\u95EE\u9898\uFF0C\u6682\u65F6\u65E0\u6CD5\u76F4\u63A5\u5151\u6362\u3002\u8BF7\u7A0D\u540E\u518D\u8BD5\uFF1B\u5982\u679C\u4E00\u76F4\u8FD9\u6837\uFF0C\u8BF7\u8054\u7CFB\u8FD0\u8425\u8005\u5904\u7406\u3002",seasonCapReached:(e,
|
|
43
|
-
`),invalidBetAmount:()=>"\u6CE8\u989D\u5FC5\u987B\u662F\u6B63\u6574\u6570\u3002",pairingInstructions:(e,
|
|
44
|
-
`),exchangeAmountPrompt:(e,t
|
|
45
|
-
`),invalidExchangeAmount:()=>"\u5151\u6362\u6570\u91CF\u5FC5\u987B\u662F\u6B63\u6574\u6570\u3002",exchangeSuccess:(e,
|
|
46
|
-
`),exchangedQuotaNote:(e,
|
|
47
|
-
`),agreeHint:()=>"Please read the notice and run /blackjack agree before using this.",noRoundHint:()=>"No hand is in progress right now \u2014 run /blackjack deal to start one.",commandUsage:()=>"agree | deal [amount] | exchange [amount] | reset",actionDescriptions:{hit:"Blackjack: draw one card from the shoe",stand:"Blackjack: stand, ending your turn and moving to settlement",double:"Blackjack: double down \u2014 double your committed CHIP and draw exactly one more card",split:"Blackjack: split a pair of equal-rank cards into two hands",insure:"Blackjack: buy insurance when the dealer shows an Ace",decline:"Blackjack: decline insurance"},blackjackCommandDescription:()=>"Blackjack table (needs an open conversation): status, deal, exchange quota, reset",genericFailure:()=>"That didn't go through \u2014 please try again in a moment.",serviceUnreachable:()=>"Can't reach the table service right now \u2014 please try again shortly.",dailyFreeHandsExhausted:()=>"You're out of free hands for today \u2014 come back tomorrow.",belowExchangeThreshold:e=>`Your balance hasn't reached the exchange threshold yet (needs ${r(e)} CHIP).`,insufficientBalance:()=>"Your balance isn't enough to complete this action.",roundAlreadyInProgress:()=>"A hand is already in progress.",githubBindingRequired:()=>"This wallet's GitHub link looks inconsistent, so a direct exchange isn't possible right now. Please try again later; if it keeps happening, contact the operator.",seasonCapReached:(e,
|
|
48
|
-
`),invalidBetAmount:()=>"The amount must be a positive integer.",pairingInstructions:(e,
|
|
49
|
-
`),exchangeAmountPrompt:(e,t
|
|
50
|
-
`),invalidExchangeAmount:()=>"The exchange amount must be a positive integer.",exchangeSuccess:(e,
|
|
51
|
-
`),exchangedQuotaNote:(e,
|
|
41
|
+
`;function L({text:e}){let n=e.split(/(https?:\/\/[^\s,。))]+)/g);return(0,a.jsx)(a.Fragment,{children:n.map((t,o)=>/^https?:\/\//.test(t)?(0,a.jsx)("a",{href:t,target:"_blank",rel:"noreferrer noopener",children:t},o):t)})}function E({card:e,m:n}){if("hidden"in e)return(0,a.jsx)("div",{className:"bj-card bj-card-back","aria-label":n.hiddenCardLabel()});let t=_[e.suit]??e.suit,o=Q.has(e.suit)?"bj-card-red":"bj-card-black";return(0,a.jsx)("div",{className:`bj-card ${o}`,children:`${e.rank}${t}`})}function Z({hand:e,label:n,m:t}){return(0,a.jsxs)("div",{children:[(0,a.jsxs)("div",{className:"bj-meta",children:[n,t.totalInParens(e.total,e.soft),e.doubled?t.doubled():""]}),(0,a.jsx)("div",{className:"bj-hand",children:e.cards.map((o,i)=>(0,a.jsx)(E,{card:o,m:t},i))})]})}function ee({dealer:e,m:n}){return(0,a.jsxs)("div",{children:[(0,a.jsxs)("div",{className:"bj-meta",children:[n.tableDealerLabel(),e.total!==void 0?n.totalInParens(e.total,!1):""]}),(0,a.jsx)("div",{className:"bj-hand",children:e.cards.map((t,o)=>(0,a.jsx)(E,{card:t,m:n},o))})]})}function ne({m:e}){return(0,a.jsx)("div",{className:"bj-table",children:(0,a.jsx)("div",{style:{whiteSpace:"pre-wrap"},children:e.consentText()})})}function te({me:e,m:n}){let t=e.exchangeThresholdChips-e.chips;return(0,a.jsxs)("div",{className:"bj-meta",children:[n.tableBalanceHeader(e.chips,e.freeHandsRemaining),e.points>0?n.tablePointsSuffix(e.points):"",e.exchangedChips>0?n.tableExchangedSuffix(e.exchangedChips):"",(0,a.jsx)("div",{className:"bj-note",children:t<=0?n.tableExchangeThresholdReached(e.exchangeThresholdChips):n.tableExchangeThresholdShort(t,e.exchangeThresholdChips)}),e.exchangedChips>0?(0,a.jsx)("div",{className:"bj-note",children:n.tableExchangedQuotaNote(e.exchangedChips)}):null,e.poolEmpty?(0,a.jsx)("div",{children:n.tablePoolEmptyNotice()}):null,e.sponsorText?(0,a.jsx)("div",{children:e.sponsorText}):null]})}function ae({round:e,m:n}){return(0,a.jsxs)("div",{className:"bj-meta",children:[e.phase==="settled"?I(e,n):S(e,n),e.pointsGranted?(0,a.jsx)("div",{children:n.pointsGrantedNotice(e.pointsGranted)}):null]})}function oe(e,n){return e==="double"||e==="split"?n:e==="insure"?Math.ceil(n/2):0}function ie({round:e,me:n,onAction:t,m:o}){if(e.actions.length===0)return null;let i=n?.chips??0,d=o.tableActionLabels;return(0,a.jsx)("div",{className:"bj-actions",children:e.actions.map(c=>{let l=oe(c,e.betChips),h=l>i;return(0,a.jsxs)("button",{type:"button",disabled:h,title:h?o.actionShortfallHint(l):void 0,onClick:()=>t(c),children:[d[c]??c,l>0?o.tableCostSuffix(l):""]},c)})})}function se(e,n,t){return!Number.isInteger(e)||e<1?t.invalidBetAmount():e>n.maxChips?t.betAmountExceedsCap(n.maxChips):e>n.balanceChips?t.betAmountExceedsBalance():null}function re({me:e,onDeal:n,freeLabel:t,m:o}){let i=e?.freeHandsRemaining??0,d=e?[e.baseBetChips,e.baseBetChips*2]:[];return(0,a.jsxs)("div",{className:"bj-actions",children:[(0,a.jsxs)("button",{type:"button",disabled:i<=0,onClick:()=>n(),children:[t,e?o.dealButtonFreeHandsSuffix(i):""]}),d.map(c=>(0,a.jsxs)("button",{type:"button",disabled:(e?.chips??0)<c,onClick:()=>n(c),children:[P(c)," CHIP"]},c))]})}function ce({me:e,onDeal:n,m:t}){let o=e.maxRaiseBetChips,[i,d]=(0,j.useState)(!1),[c,l]=(0,j.useState)("");if(o===void 0)return null;let h=Number(c),p=c.trim()===""?t.betAmountMissing():se(h,{balanceChips:e.chips,maxChips:o},t);return(0,a.jsxs)("div",{children:[(0,a.jsx)("div",{className:"bj-actions",children:(0,a.jsx)("button",{type:"button",onClick:()=>d(!i),children:t.customBetToggle()})}),i?(0,a.jsxs)("div",{className:"bj-custom",children:[(0,a.jsx)("input",{type:"number",min:1,max:o,value:c,onChange:x=>l(x.target.value),"aria-label":t.customBetToggle()}),(0,a.jsx)("button",{type:"button",disabled:p!==null,onClick:()=>n(h),children:t.customBetSubmit()}),(0,a.jsx)("span",{className:"bj-hint",children:p??t.customBetRange(o)})]}):null]})}function le({me:e,onDeal:n,m:t}){let o=e?.freeHandsRemaining??0,i=e?.baseBetChips??0,d=e?[i,i*2]:[],c=e?d.some(l=>e.chips>=l):!1;return(0,a.jsxs)("div",{children:[(0,a.jsx)("div",{className:"bj-meta",children:t.noActiveRoundNotice()}),(0,a.jsxs)("div",{className:"bj-group",children:[(0,a.jsxs)("div",{className:"bj-group-title",children:[t.freeHandsGroupTitle(),o>0?t.freeHandsRemainingToday(o):t.freeHandsExhaustedToday()]}),e?(0,a.jsx)("div",{className:"bj-note",children:t.freeHandExplainer(i,Math.floor(i*1.5))}):null,(0,a.jsx)("div",{className:"bj-actions",children:(0,a.jsx)("button",{type:"button",disabled:o<=0,onClick:()=>n(),children:t.startFreeHandButton()})})]}),e?(0,a.jsxs)("div",{className:"bj-group",children:[(0,a.jsxs)("div",{className:"bj-group-title",children:[t.raiseGroupTitle(),c?"":t.insufficientBalanceSuffix()]}),(0,a.jsx)("div",{className:"bj-note",children:t.raiseExplainer()}),(0,a.jsx)("div",{className:"bj-actions",children:d.map(l=>(0,a.jsxs)("button",{type:"button",disabled:e.chips<l,onClick:()=>n(l),children:[P(l)," CHIP"]},l))}),(0,a.jsx)(ce,{me:e,onDeal:n,m:t})]}):null]})}function de({me:e,onDeal:n,onDismiss:t,m:o}){return(0,a.jsxs)("div",{children:[(0,a.jsx)(re,{me:e,onDeal:n,freeLabel:o.rematchFreeLabel(),m:o}),(0,a.jsx)("div",{className:"bj-actions",children:(0,a.jsx)("button",{type:"button",onClick:t,children:o.dismissButton()})})]})}function $({state:e,m:n,onAction:t,onDeal:o,onDismiss:i,error:d}){if(!e.consented)return(0,a.jsx)(ne,{m:n});let{me:c,round:l}=e;return(0,a.jsxs)("div",{className:"bj-table",children:[(0,a.jsx)("style",{children:K}),d?(0,a.jsx)("div",{className:"bj-error",children:d}):null,c?(0,a.jsx)(te,{me:c,m:n}):null,l?(0,a.jsxs)("div",{children:[l.hands.map((h,p)=>(0,a.jsx)(Z,{hand:h,m:n,label:l.hands.length>1?n.tableSplitHandLabel(p+1):n.tableHandLabel()},p)),(0,a.jsx)(ee,{dealer:l.dealer,m:n}),(0,a.jsx)(ae,{round:l,m:n}),(0,a.jsx)(ie,{round:l,me:c,onAction:t,m:n}),l.phase==="settled"?(0,a.jsx)(de,{me:c,onDeal:o,onDismiss:i,m:n}):null]}):(0,a.jsx)(le,{me:c,onDeal:o,m:n})]})}var w="blackjack-command-input";function ue(e){return e==="blackjack"||e.startsWith("bj-")}function be(e){return`/${e.data.name}${(e.data.args??"").trimEnd()}`}var T={kind:"blackjack-command-input",target:"chat",match:e=>e.type==="command/run"&&ue(e.data.name)?{id:String(e.data.commandId),role:"start"}:null,start:(e,n)=>{if(n.event.type!=="command/run")throw new Error("blackjack-command-input start requires command/run");return{commandId:String(n.event.data.commandId),seq:n.event.seq,time:n.event.time,text:be(n.event)}},update:e=>e.state,buildViewNode:e=>{let n=e.state;return n===void 0?null:{key:e.key,kind:w,id:e.id,target:"chat",anchorSeq:n.seq-.1,location:e.start?.location??{kind:"unresolved"},visibility:"visible",data:{commandId:n.commandId,text:n.text,time:n.time}}}};function f(e,n){switch(n.type){case"synced":return{consented:n.payload.consented,me:n.payload.me,round:n.payload.round};case"round":return{...e,round:n.round};case"meRefreshed":return{...e,me:n.me};case"dismissed":return{...e,round:void 0}}}var he=new Set(["","deal"]);function N(e){let n=(e.args??"").trim().split(/\s+/)[0]??"",t=he.has(n),o=e.outcome,i=o?.kind==="error";return t?{table:!0,isError:i,...i&&o?.text?{text:o.text}:{}}:{table:!1,isError:i,...o?.text!==void 0?{text:o.text}:{}}}var s=e=>e.toLocaleString("en-US"),B={freeStakeLine:e=>`\u8FD9\u662F\u514D\u8D39\u7684\u4E00\u5C40\uFF1A\u8D62\u4E86\u62FF ${s(e)} CHIP\uFF0C\u8F93\u4E86\u4E0D\u635F\u5931\u4EFB\u4F55\u4E1C\u897F\u3002`,raiseStakeLine:e=>`\u672C\u5C40\u5DF2\u52A0\u6CE8 ${s(e)} CHIP\uFF0C\u4ECE\u4F60\u7684\u4F59\u989D\u91CC\u51FA\u3002`,freeWin:e=>`\u8D62\u4E86\uFF0C\u62FF\u5230 ${s(e)} CHIP\u3002`,freeLose:()=>"\u8FD9\u624B\u8F93\u4E86\u3002\u514D\u8D39\u624B\u4E0D\u635F\u5931\u4EFB\u4F55\u4E1C\u897F\u3002",freePush:()=>"\u5E73\u5C40\u3002\u8FD9\u624B\u4E0D\u635F\u5931\u4E5F\u4E0D\u83B7\u5F97\u3002",raiseWin:(e,n)=>`\u8D62\u4E86\uFF0C\u62FF\u56DE ${s(e)} CHIP\uFF08\u52A0\u6CE8 ${s(n)}\uFF09\u3002`,raiseLose:e=>`\u8FD9\u624B\u8F93\u4E86\uFF0C\u52A0\u6CE8\u7684 ${s(e)} CHIP \u6536\u4E0D\u56DE\u3002`,raisePush:e=>`\u5E73\u5C40\u3002\u52A0\u6CE8\u7684 ${s(e)} CHIP \u539F\u6837\u9000\u56DE\u3002`,handLabel:()=>"\u4F60\u7684\u724C\uFF1A",splitHandLabel:e=>`\u4F60\u7684\u7B2C ${e} \u624B\uFF1A`,dealerLabel:()=>"\u5E84\u5BB6\uFF1A",totalInParens:(e,n)=>`\uFF08${n?"\u8F6F":""}${e}\uFF09`,doubled:()=>" [\u53CC\u500D]",actionsPrefix:()=>"\u53EF\u9009\uFF1A",actionLabels:{hit:"/bj-hit \u8981\u724C",stand:"/bj-stand \u505C\u724C",double:"/bj-double \u53CC\u500D",split:"/bj-split \u5206\u724C",insure:"/bj-insure \u4E70\u4FDD\u9669",decline:"/bj-decline \u4E0D\u4E70\u4FDD\u9669"},actionsSeparator:()=>"\u3000",balanceLine:(e,n)=>`\u4F59\u989D\uFF1A${s(e)} CHIP\u3000\u5DF2\u5151\u6362\u989D\u5EA6\uFF1A${s(n)} CHIP`,freeHandsLine:(e,n)=>`\u4ECA\u65E5\u5269\u4F59\u514D\u8D39\u624B\uFF1A${e}\u3000\u8D5B\u5B63\uFF1A${n}`,thresholdLine:e=>`\u5151\u6362\u95E8\u69DB\uFF1A${s(e)} CHIP\uFF08\u8FBE\u6807\u540E\u53EF\u7528 /blackjack exchange \u5151\u6362\uFF09`,pointsLine:e=>`\u79EF\u5206\uFF1A${s(e)}`,poolEmptyNotice:()=>"\u63D0\u793A\uFF1A\u672C\u671F\u5956\u6C60\u5DF2\u53D1\u5B8C\uFF0C\u73B0\u5728\u8D62\u724C\u5C06\u53D1\u653E\u79EF\u5206\u3002",pointsGrantedNotice:e=>`\u672C\u671F\u5956\u6C60\u5DF2\u53D1\u5B8C\uFF0C\u6539\u4E3A\u53D1\u653E ${s(e)} \u79EF\u5206\u3002`,chipValidityNotice:()=>"\uFF08\u8D5B\u5B63\u7ED3\u675F\u65F6\uFF0C\u672A\u8FBE\u5151\u6362\u95E8\u69DB\u7684 CHIP \u4F1A\u6298\u7B97\u4E3A\u79EF\u5206\u5E76\u56DE\u6536\uFF1B\u8FBE\u6807\u7684\u7ED3\u8F6C\uFF09",consentText:()=>["\u3010\u9996\u6B21\u4F7F\u7528\u8BF7\u5148\u9605\u8BFB\u3011","\xB7 \u8FD9\u662F\u793E\u533A\u5F00\u53D1\u8005\u53D1\u5E03\u7684\u7B2C\u4E09\u65B9\u5C0F\u6E38\u620F\uFF0C\u4E0E\u4EFB\u4F55\u6A21\u578B\u5382\u5546\u5747\u65E0\u5173\u8054\uFF0C\u4E0D\u4EE3\u8868\u5176\u7ACB\u573A\u3002","\xB7 \u53C2\u4E0E\u514D\u8D39\uFF1A\u6BCF\u65E5\u6709\u9650\u6B21\u514D\u8D39\u624B\uFF0C\u8F93\u4E86\u4E0D\u6263\u4EFB\u4F55\u4E1C\u897F\uFF0C\u4F60\u81EA\u5DF1\u7684\u5BC6\u94A5\u4E0E\u4F59\u989D\u4E0D\u53D7\u5F71\u54CD\u3002","\xB7 \u8D62\u5F97\u7684 CHIP \u53EA\u80FD\u5728\u672C\u63D2\u4EF6\u5185\u5151\u6362\u4E3A\u6A21\u578B\u989D\u5EA6\u6D88\u8D39\uFF1A\u4E0D\u53EF\u63D0\u73B0\u3001\u4E0D\u53EF\u8F6C\u8BA9\u3001\u4E0D\u53EF\u8F6C\u5165\u4EFB\u4F55\u5382\u5546\u8D26\u6237\u3002","\xB7 \u5956\u52B1\u6709\u8D5B\u5B63\u6709\u6548\u671F\uFF0C\u8D5B\u5B63\u7ED3\u675F\u540E\u3001\u6216\u957F\u671F\u4E0D\u6D3B\u8DC3\u65F6\uFF0C\u672A\u4F7F\u7528\u7684\u90E8\u5206\u6309\u89C4\u5219\u56DE\u6536\u3002","","\u9605\u8BFB\u5E76\u540C\u610F\u4EE5\u4E0A\u5185\u5BB9\u540E\uFF0C\u6267\u884C /blackjack agree confirm \u5F00\u59CB\u3002"].join(`
|
|
42
|
+
`),agreeHint:()=>"\u8BF7\u5148\u9605\u8BFB\u987B\u77E5\u5E76\u6267\u884C /blackjack agree \u540E\u518D\u4F7F\u7528\u3002",noRoundHint:()=>"\u5F53\u524D\u6CA1\u6709\u8FDB\u884C\u4E2D\u7684\u724C\u5C40\uFF0C\u5148\u7528 /blackjack deal \u5F00\u4E00\u5C40\u3002",commandUsage:()=>"agree | deal [\u6CE8\u989D] | exchange [\u6570\u91CF] | reset",actionDescriptions:{hit:"21 \u70B9\uFF1A\u5411\u724C\u684C\u8981\u4E00\u5F20\u724C",stand:"21 \u70B9\uFF1A\u505C\u724C\uFF0C\u8FDB\u5165\u7ED3\u7B97",double:"21 \u70B9\uFF1A\u52A0\u500D\u6CE8\u989D\u5E76\u518D\u8981\u4E00\u5F20\u724C",split:"21 \u70B9\uFF1A\u628A\u4E00\u5BF9\u76F8\u540C\u70B9\u6570\u7684\u724C\u5206\u6210\u4E24\u624B",insure:"21 \u70B9\uFF1A\u5E84\u5BB6\u660E\u724C\u4E3A A \u65F6\u8D2D\u4E70\u4FDD\u9669",decline:"21 \u70B9\uFF1A\u4E0D\u8D2D\u4E70\u4FDD\u9669"},blackjackCommandDescription:()=>"21 \u70B9\u724C\u684C\uFF08\u9700\u5148\u8FDB\u5165\u4F1A\u8BDD\uFF09\uFF1A\u67E5\u770B\u72B6\u6001\u3001\u5F00\u5C40\u3001\u5151\u6362\u989D\u5EA6\u3001\u91CD\u7F6E\u51ED\u8BC1",genericFailure:()=>"\u64CD\u4F5C\u6CA1\u6709\u6210\u529F\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\u3002",serviceUnreachable:()=>"\u6682\u65F6\u8FDE\u4E0D\u4E0A\u724C\u684C\u670D\u52A1\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\u3002",dailyFreeHandsExhausted:()=>"\u4ECA\u5929\u7684\u514D\u8D39\u624B\u7528\u5B8C\u4E86\uFF0C\u660E\u5929\u518D\u6765\u3002",belowExchangeThreshold:e=>`\u4F59\u989D\u8FD8\u6CA1\u5230\u5151\u6362\u95E8\u69DB\uFF08\u9700\u8981 ${s(e)} CHIP\uFF09\u3002`,insufficientBalance:()=>"\u4F59\u989D\u4E0D\u8DB3\u4EE5\u5B8C\u6210\u8FD9\u4E2A\u52A8\u4F5C\u3002",roundAlreadyInProgress:()=>"\u5DF2\u7ECF\u6709\u4E00\u5C40\u5728\u8FDB\u884C\u4E2D\u3002",githubBindingRequired:()=>"\u8FD9\u4E2A\u94B1\u5305\u7684 GitHub \u7ED1\u5B9A\u72B6\u6001\u6709\u95EE\u9898\uFF0C\u6682\u65F6\u65E0\u6CD5\u76F4\u63A5\u5151\u6362\u3002\u8BF7\u7A0D\u540E\u518D\u8BD5\uFF1B\u5982\u679C\u4E00\u76F4\u8FD9\u6837\uFF0C\u8BF7\u8054\u7CFB\u8FD0\u8425\u8005\u5904\u7406\u3002",seasonCapReached:(e,n)=>`\u8FD9\u7B14\u4F1A\u8D85\u8FC7\u672C\u8D5B\u5B63\u5151\u6362\u603B\u989D\u4E0A\u9650\uFF08\u4E0A\u9650 ${s(e)} CHIP\uFF0C\u672C\u8D5B\u5B63\u5DF2\u5151\u6362 ${s(n)} CHIP\uFF09\u3002`,exchangeUnavailable:()=>"\u5151\u6362\u529F\u80FD\u6682\u65F6\u4E0D\u53EF\u7528\uFF08\u8FD0\u8425\u8005\u90E8\u7F72\u672A\u914D\u7F6E\u5B8C\u6574\uFF09\uFF0C\u724C\u5C40\u672C\u8EAB\u4E0D\u53D7\u5F71\u54CD\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\u3002",betAmountAboveLimit:e=>`\u6CE8\u989D\u8D85\u8FC7\u4E0A\u9650\uFF0C\u6700\u591A\u53EF\u4E0B\u6CE8 ${s(e)} CHIP\u3002`,illegalAction:()=>"\u8FD9\u4E2A\u52A8\u4F5C\u73B0\u5728\u4E0D\u53EF\u7528\u4E86\uFF0C\u724C\u5C40\u72B6\u6001\u53EF\u80FD\u5DF2\u7ECF\u53D8\u5316\uFF0C\u8BF7\u7528 /blackjack \u67E5\u770B\u6700\u65B0\u72B6\u6001\u3002",registerRateLimited:()=>"\u5F53\u524D\u7F51\u7EDC\u6CE8\u518C\u6B21\u6570\u8FC7\u591A\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\u3002",unknownPlayerToken:()=>"\u8FD9\u53F0\u673A\u5668\u4E0A\u7684\u724C\u684C\u51ED\u8BC1\uFF0C\u670D\u52A1\u7AEF\u5DF2\u7ECF\u4E0D\u8BA4\u8BC6\u4E86\uFF08\u670D\u52A1\u7AEF\u91CD\u7F6E\u8FC7\uFF0C\u6216\u8005 serverUrl \u6539\u6307\u5230\u4E86\u53E6\u4E00\u4E2A\u90E8\u7F72\uFF09\u3002\u8BF7\u5148\u6267\u884C /blackjack reset confirm \u6E05\u6389\u5B83\uFF0C\u518D\u6267\u884C /blackjack agree \u91CD\u65B0\u5F00\u59CB\u3002",agreeWelcome:()=>["\u5DF2\u786E\u8BA4\u987B\u77E5\uFF0C\u6B22\u8FCE\u4E0A\u684C\u3002","/blackjack deal \u5F00\u59CB\u4ECA\u5929\u7684\u514D\u8D39\u624B\uFF0C\u6216 /blackjack deal <\u6CE8\u989D> \u7528\u4F59\u989D\u52A0\u6CE8\u3002"].join(`
|
|
43
|
+
`),invalidBetAmount:()=>"\u6CE8\u989D\u5FC5\u987B\u662F\u6B63\u6574\u6570\u3002",pairingInstructions:(e,n)=>["\u9996\u6B21\u5151\u6362\u8981\u5148\u7ED1\u5B9A GitHub\uFF0C\u53EA\u9700\u505A\u8FD9\u4E00\u6B21\u3002","","\u8BF7\u5728\u6D4F\u89C8\u5668\u91CC\u6253\u5F00\u8FD9\u4E2A\u94FE\u63A5\uFF1A",e,"","\u6253\u5F00\u540E\u70B9\u300C\u7528 GitHub \u7EE7\u7EED\u300D\u6388\u6743\uFF0C\u7136\u540E\u5728\u540C\u4E00\u4E2A\u9875\u9762\u586B\u5199\u8981\u5151\u6362\u7684\u6570\u91CF\u5E76\u63D0\u4EA4\u3002",`\u94FE\u63A5\u7EA6 ${n} \u5206\u949F\u5185\u6709\u6548\uFF1B\u8FC7\u671F\u4E86\u5C31\u518D\u6267\u884C\u4E00\u6B21 /blackjack exchange \u62FF\u4E00\u4E2A\u65B0\u7684\u3002`,"\u7ED1\u5B9A\u5B8C\u6210\u540E\uFF0C\u4EE5\u540E\u76F4\u63A5\u7528 /blackjack exchange <\u6570\u91CF> \u5151\u6362\uFF0C\u4E0D\u7528\u518D\u5F00\u6D4F\u89C8\u5668\u3002"].join(`
|
|
44
|
+
`),exchangeAmountPrompt:(e,n,t)=>[`\u5DF2\u7ED1\u5B9A GitHub \u8D26\u53F7\uFF1A${e}`,`\u5F53\u524D\u4F59\u989D\uFF1A${s(n)} CHIP\u3000\u5151\u6362\u95E8\u69DB\uFF1A${s(t)} CHIP`,"\u8BF7\u7528 /blackjack exchange <\u6570\u91CF> \u6307\u5B9A\u8981\u5151\u6362\u7684 CHIP \u6570\u91CF\u3002"].join(`
|
|
45
|
+
`),invalidExchangeAmount:()=>"\u5151\u6362\u6570\u91CF\u5FC5\u987B\u662F\u6B63\u6574\u6570\u3002",exchangeSuccess:(e,n)=>[`\u5DF2\u5151\u6362 ${s(e)} CHIP\u3002`,`\u7D2F\u8BA1\u5DF2\u5151\u6362\u989D\u5EA6\uFF1A${s(n)} CHIP`,"\u8FD9\u4EFD\u989D\u5EA6\u4F1A\u5728\u4F60\u81EA\u5DF1\u7684\u6A21\u578B\u989D\u5EA6\u8017\u5C3D\u65F6\uFF0C\u81EA\u52A8\u63A5\u7BA1\u90A3\u4E00\u6B21\u5931\u8D25\u7684\u8BF7\u6C42\uFF1B\u5E73\u65F6\u4E0D\u4F1A\u52A8\u7528\uFF0C\u4F60\u4E0D\u9700\u8981\u505A\u4EFB\u4F55\u4E8B\u3002"].join(`
|
|
46
|
+
`),exchangedQuotaNote:(e,n)=>n?`\u5DF2\u5151\u6362\u989D\u5EA6\u4EC5\u9650\u672C\u63D2\u4EF6\u5185\u6D88\u8D39\uFF0C\u4E0D\u53EF\u63D0\u73B0\u3001\u4E0D\u53EF\u8F6C\u8BA9\u3002\u8FDE\u7EED ${e} \u5929\u65E0\u6E38\u620F\u4E14\u65E0\u6D88\u8D39\u7684\u8D26\u6237\uFF0C\u4F59\u989D\u4F1A\u88AB\u56DE\u6536\u8FDB\u5956\u6C60\u3002`:`\u5DF2\u5151\u6362\u989D\u5EA6\u4EC5\u9650\u672C\u63D2\u4EF6\u5185\u6D88\u8D39\uFF0C\u4E0D\u53EF\u63D0\u73B0\u3001\u4E0D\u53EF\u8F6C\u8BA9\u3002\u5B83\u4E0D\u4F1A\u56E0\u8D5B\u5B63\u7ED3\u675F\u800C\u6E05\u96F6\uFF1B\u4F46\u8FDE\u7EED ${e} \u5929\u65E0\u6E38\u620F\u4E14\u65E0\u6D88\u8D39\u7684\u8D26\u6237\uFF0C\u4F59\u989D\u4F1A\u88AB\u56DE\u6536\u8FDB\u5956\u6C60\u3002`,resetConfirmPrompt:()=>"\u8FD9\u4F1A\u6E05\u9664\u672C\u673A\u4FDD\u5B58\u7684\u724C\u684C\u51ED\u8BC1\uFF08\u4E0D\u5F71\u54CD\u670D\u52A1\u7AEF\u8BB0\u5F55\uFF0C\u91CD\u65B0\u914D\u5BF9\u5373\u53EF\u627E\u56DE\uFF09\u3002\u786E\u8BA4\u8BF7\u6267\u884C /blackjack reset confirm",resetDone:()=>"\u5DF2\u6E05\u9664\u672C\u673A\u51ED\u8BC1\uFF0C\u53EF\u7528 /blackjack agree \u91CD\u65B0\u5F00\u59CB\u3002",unknownSubcommand:(e,n)=>`\u672A\u77E5\u5B50\u547D\u4EE4\u300C${e}\u300D\u3002\u53EF\u7528\uFF1A${n}`,poolSwitchNotice:()=>"\u5DF2\u5207\u6362\u81F3\u5956\u6C60\u4F59\u989D\u7EE7\u7EED\u672C\u6B21\u8BF7\u6C42\u3002",poolFallbackFailed:e=>`\u4F60\u81EA\u5DF1\u7684\u6A21\u578B\u989D\u5EA6\u5DF2\u8017\u5C3D\uFF0C\u5956\u6C60\u63A5\u7BA1\u4E86\u8FD9\u6B21\u8BF7\u6C42\u4F46\u4E5F\u6CA1\u6210\u529F\uFF08\u5956\u6C60\uFF1A${e}\uFF09\u3002\u5DF2\u5151\u6362\u989D\u5EA6\u6CA1\u6709\u88AB\u6263\u3002`,poolFallbackBusy:()=>"\u4F60\u81EA\u5DF1\u7684\u6A21\u578B\u989D\u5EA6\u5DF2\u8017\u5C3D\uFF0C\u5956\u6C60\u63A5\u7BA1\u4E86\u8FD9\u6B21\u8BF7\u6C42\uFF0C\u4F46\u540C\u65F6\u8FDB\u884C\u7684\u8BF7\u6C42\u592A\u591A\u88AB\u6321\u4E0B\u4E86\u3002\u5DF2\u5151\u6362\u989D\u5EA6\u6CA1\u6709\u88AB\u6263\uFF0C\u7A0D\u7B49\u4E00\u4E0B\u518D\u8BD5\u3002",noCredentialHint:()=>"\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",hiddenCardLabel:()=>"\u6697\u724C",tableHandLabel:()=>"\u4F60\u7684\u724C",tableSplitHandLabel:e=>`\u4F60\u7684\u7B2C ${e} \u624B`,tableDealerLabel:()=>"\u5E84\u5BB6",tableActionLabels:{hit:"\u8981\u724C",stand:"\u505C\u724C",double:"\u53CC\u500D",split:"\u5206\u724C",insure:"\u4E70\u4FDD\u9669",decline:"\u4E0D\u4E70\u4FDD\u9669"},tableBalanceHeader:(e,n)=>`\u4F59\u989D\uFF1A${s(e)} CHIP\u3000\u4ECA\u65E5\u5269\u4F59\u514D\u8D39\u624B\uFF1A${n}`,tablePointsSuffix:e=>`\u3000\u79EF\u5206\uFF1A${s(e)}`,tableExchangedSuffix:e=>`\u3000\u5DF2\u5151\u6362\u989D\u5EA6\uFF1A${s(e)} CHIP`,tableExchangeThresholdReached:e=>`\u5DF2\u8FBE\u5151\u6362\u95E8\u69DB\uFF08${s(e)} CHIP\uFF09\xB7 \u7528 /blackjack exchange \u628A CHIP \u6362\u6210\u6A21\u578B\u989D\u5EA6`,tableExchangeThresholdShort:(e,n)=>`\u8FD8\u5DEE ${s(e)} CHIP \u5230\u5151\u6362\u95E8\u69DB\uFF08${s(n)}\uFF09\xB7 \u8FC7\u7EBF\u540E\u53EF\u7528 /blackjack exchange \u6362\u6210\u6A21\u578B\u989D\u5EA6`,tableExchangedQuotaNote:e=>`\u5DF2\u5151\u6362\u7684 ${s(e)} CHIP \u4F1A\u5728\u4F60\u81EA\u5DF1\u7684\u6A21\u578B\u989D\u5EA6\u8017\u5C3D\u65F6\u81EA\u52A8\u63A5\u7BA1\u90A3\u4E00\u6B21\u8BF7\u6C42\uFF0C\u5E73\u65F6\u4E0D\u4F1A\u52A8\u7528\u3002`,tablePoolEmptyNotice:()=>"\uFF08\u672C\u671F\u5956\u6C60\u5DF2\u53D1\u5B8C\uFF0C\u73B0\u5728\u8D62\u724C\u5C06\u53D1\u653E\u79EF\u5206\uFF09",actionShortfallHint:e=>`\u8FD8\u8981\u518D\u51FA ${s(e)} CHIP\uFF0C\u4F59\u989D\u4E0D\u8DB3`,tableCostSuffix:e=>`\uFF08${s(e)}\uFF09`,betAmountMissing:()=>"\u8BF7\u8F93\u5165\u6CE8\u989D\u3002",betAmountExceedsCap:e=>`\u5355\u5C40\u6700\u591A ${s(e)} CHIP\u3002`,betAmountExceedsBalance:()=>"\u4F59\u989D\u4E0D\u8DB3\u4EE5\u4E0B\u8FD9\u4E2A\u6CE8\u3002",customBetToggle:()=>"\u81EA\u5B9A\u4E49\u6CE8\u989D",customBetSubmit:()=>"\u5F00\u59CB",customBetRange:e=>`1 \uFF5E ${s(e)} CHIP`,noActiveRoundNotice:()=>"\u5F53\u524D\u6CA1\u6709\u8FDB\u884C\u4E2D\u7684\u724C\u5C40\u3002",freeHandsGroupTitle:()=>"\u514D\u8D39\u624B",freeHandsRemainingToday:e=>` \xB7 \u4ECA\u65E5\u5269 ${e} \u624B`,freeHandsExhaustedToday:()=>" \xB7 \u4ECA\u65E5\u5DF2\u7528\u5B8C\uFF0C\u660E\u5929\u518D\u6765",freeHandExplainer:(e,n)=>`\u4E0D\u7528\u51FA CHIP\u3002\u8D62\u4E86\u62FF ${s(e)} CHIP\uFF1B\u5934\u4E24\u5F20\u724C\u5C31\u51D1\u6210 21 \u70B9\uFF08A \u914D 10/J/Q/K\uFF09\u62FF ${s(n)}\u3002\u8F93\u4E86\u6216\u5E73\u5C40\u90FD\u4E0D\u635F\u5931\u4EFB\u4F55\u4E1C\u897F\u3002`,startFreeHandButton:()=>"\u5F00\u59CB\u514D\u8D39\u7684\u4E00\u5C40",raiseGroupTitle:()=>"\u7528\u4F59\u989D\u52A0\u6CE8",insufficientBalanceSuffix:()=>" \xB7 \u4F59\u989D\u4E0D\u8DB3",raiseExplainer:()=>"\u52A0\u6CE8\u7684 CHIP \u4ECE\u4F60\u7684\u4F59\u989D\u91CC\u51FA\uFF0C\u8D62\u4E86\u7FFB\u500D\uFF0C\u8F93\u4E86\u6536\u4E0D\u56DE\u3002",rematchFreeLabel:()=>"\u518D\u6765\u4E00\u5C40\uFF08\u514D\u8D39\uFF09",dealButtonFreeHandsSuffix:e=>`\xB7\u5269 ${e} \u624B`,dismissButton:()=>"\u6536\u8D77",actionFailedNotice:()=>"\u4E0A\u4E00\u6B21\u64CD\u4F5C\u6CA1\u6709\u6210\u529F\uFF0C\u5DF2\u4E3A\u4F60\u5237\u65B0\u5230\u6700\u65B0\u72B6\u6001\u3002"};var r=e=>e.toLocaleString("en-US"),A={freeStakeLine:e=>`This hand is free: win and you take ${r(e)} CHIP; lose and you are out nothing.`,raiseStakeLine:e=>`${r(e)} CHIP is committed from your balance for this hand.`,freeWin:e=>`You won ${r(e)} CHIP.`,freeLose:()=>"This hand did not win. A free hand costs you nothing.",freePush:()=>"A tie. Nothing gained, nothing lost.",raiseWin:(e,n)=>`You won \u2014 ${r(e)} CHIP back (${r(n)} committed).`,raiseLose:e=>`This hand lost; the ${r(e)} CHIP you committed is gone.`,raisePush:e=>`A tie. Your ${r(e)} CHIP comes back.`,handLabel:()=>"Your hand: ",splitHandLabel:e=>`Hand ${e}: `,dealerLabel:()=>"Dealer: ",totalInParens:(e,n)=>` (${n?"soft ":""}${e})`,doubled:()=>" [doubled]",actionsPrefix:()=>"Available: ",actionLabels:{hit:"/bj-hit hit",stand:"/bj-stand stand",double:"/bj-double double down",split:"/bj-split split",insure:"/bj-insure buy insurance",decline:"/bj-decline decline insurance"},actionsSeparator:()=>" ",balanceLine:(e,n)=>`Balance: ${r(e)} CHIP Exchanged so far: ${r(n)} CHIP`,freeHandsLine:(e,n)=>`Free hands left today: ${e} Season: ${n}`,thresholdLine:e=>`Exchange threshold: ${r(e)} CHIP (once reached, use /blackjack exchange)`,pointsLine:e=>`Points: ${r(e)}`,poolEmptyNotice:()=>"Note: this season's pool is spent; wins now grant points instead.",pointsGrantedNotice:e=>`This season's pool is spent, so you were granted ${r(e)} points instead.`,chipValidityNotice:()=>"(At season end, CHIP below the exchange threshold converts to points and is reclaimed; CHIP past the threshold carries over.)",consentText:()=>["[Please read before first use]","\xB7 This is a third-party mini-game published by a community developer; it is not affiliated with any model vendor and does not represent any vendor's position.","\xB7 Taking part is free: you get a limited number of free hands each day, losing costs you nothing, and your own API key and balance are unaffected.","\xB7 CHIP you win can only be exchanged, within this plugin, for model API quota to spend: it cannot be withdrawn, cannot be transferred to anyone else, and cannot be moved into any vendor account.","\xB7 Rewards carry a season-limited validity period; whatever is left unused is reclaimed under the rules after the season ends, or after a long stretch of inactivity.","","Once you have read and agree to the above, run /blackjack agree confirm to start."].join(`
|
|
47
|
+
`),agreeHint:()=>"Please read the notice and run /blackjack agree before using this.",noRoundHint:()=>"No hand is in progress right now \u2014 run /blackjack deal to start one.",commandUsage:()=>"agree | deal [amount] | exchange [amount] | reset",actionDescriptions:{hit:"Blackjack: draw one card from the shoe",stand:"Blackjack: stand, ending your turn and moving to settlement",double:"Blackjack: double down \u2014 double your committed CHIP and draw exactly one more card",split:"Blackjack: split a pair of equal-rank cards into two hands",insure:"Blackjack: buy insurance when the dealer shows an Ace",decline:"Blackjack: decline insurance"},blackjackCommandDescription:()=>"Blackjack table (needs an open conversation): status, deal, exchange quota, reset",genericFailure:()=>"That didn't go through \u2014 please try again in a moment.",serviceUnreachable:()=>"Can't reach the table service right now \u2014 please try again shortly.",dailyFreeHandsExhausted:()=>"You're out of free hands for today \u2014 come back tomorrow.",belowExchangeThreshold:e=>`Your balance hasn't reached the exchange threshold yet (needs ${r(e)} CHIP).`,insufficientBalance:()=>"Your balance isn't enough to complete this action.",roundAlreadyInProgress:()=>"A hand is already in progress.",githubBindingRequired:()=>"This wallet's GitHub link looks inconsistent, so a direct exchange isn't possible right now. Please try again later; if it keeps happening, contact the operator.",seasonCapReached:(e,n)=>`This would go over this season's exchange cap (cap ${r(e)} CHIP, already exchanged ${r(n)} CHIP this season).`,exchangeUnavailable:()=>"Exchanging isn't available right now (an operator deployment setting is missing) \u2014 the table itself is unaffected, please try again later.",betAmountAboveLimit:e=>`That amount is over the limit \u2014 the most you can raise is ${r(e)} CHIP.`,illegalAction:()=>"That move isn't available anymore \u2014 the hand's state may have changed. Run /blackjack to see the current state.",registerRateLimited:()=>"Too many registrations from this network right now \u2014 please try again later.",unknownPlayerToken:()=>"The table service no longer recognizes the credential stored on this machine (the server was reset, or serverUrl now points at a different deployment). Run /blackjack reset confirm to clear it, then /blackjack agree to start over.",agreeWelcome:()=>["You have confirmed the notice \u2014 welcome to the table.","Run /blackjack deal to start today's free hand, or /blackjack deal <amount> to raise from your balance."].join(`
|
|
48
|
+
`),invalidBetAmount:()=>"The amount must be a positive integer.",pairingInstructions:(e,n)=>["Linking a GitHub account is required for your first exchange \u2014 this is a one-time step.","","Open this link in your browser:",e,"",'After opening it, click "Continue with GitHub" to authorize, then enter the amount to exchange on the same page and submit.',`The link stays valid for about ${n} ${n===1?"minute":"minutes"}; if it expires, just run /blackjack exchange again for a new one.`,"Once linked, use /blackjack exchange <amount> directly from now on \u2014 no more browser trips needed."].join(`
|
|
49
|
+
`),exchangeAmountPrompt:(e,n,t)=>[`GitHub account linked: ${e}`,`Current balance: ${r(n)} CHIP Exchange threshold: ${r(t)} CHIP`,"Use /blackjack exchange <amount> to specify how much CHIP to exchange."].join(`
|
|
50
|
+
`),invalidExchangeAmount:()=>"The exchange amount must be a positive integer.",exchangeSuccess:(e,n)=>[`You exchanged ${r(e)} CHIP.`,`Total exchanged so far: ${r(n)} CHIP`,"This quota automatically takes over the one request that fails once your own model quota runs out; it isn't touched otherwise, and there's nothing you need to do."].join(`
|
|
51
|
+
`),exchangedQuotaNote:(e,n)=>n?`Exchanged quota is spendable only inside this plugin \u2014 it can't be withdrawn or transferred. An account with ${e} consecutive days of no play and no spending has its balance recycled into the pool.`:`Exchanged quota is spendable only inside this plugin \u2014 it can't be withdrawn or transferred. It doesn't expire at the end of a season; but an account with ${e} consecutive days of no play and no spending has its balance recycled into the pool.`,resetConfirmPrompt:()=>"This clears the table credential saved on this machine (server-side records are unaffected; pairing again will recover it). Confirm with /blackjack reset confirm",resetDone:()=>"Local credential cleared. Run /blackjack agree to start over.",unknownSubcommand:(e,n)=>`Unknown subcommand "${e}". Available: ${n}`,poolSwitchNotice:()=>"Switched to the pool balance to continue this request.",poolFallbackFailed:e=>`Your own model quota is used up. The pool took this request over but did not get through either (pool said: ${e}). Your exchanged quota was not charged.`,poolFallbackBusy:()=>"Your own model quota is used up. The pool took this request over but too many requests were in flight at once. Your exchanged quota was not charged \u2014 wait a moment and try again.",noCredentialHint:()=>"The fallback route only takes over once your own model quota is exhausted; right now no API key is configured, so your exchanged quota can't be used yet.",hiddenCardLabel:()=>"Hidden card",tableHandLabel:()=>"Your hand",tableSplitHandLabel:e=>`Hand ${e}`,tableDealerLabel:()=>"Dealer",tableActionLabels:{hit:"Hit",stand:"Stand",double:"Double down",split:"Split",insure:"Buy insurance",decline:"Decline insurance"},tableBalanceHeader:(e,n)=>`Balance: ${r(e)} CHIP \xB7 Free hands left today: ${n}`,tablePointsSuffix:e=>` \xB7 Points: ${r(e)}`,tableExchangedSuffix:e=>` \xB7 Exchanged so far: ${r(e)} CHIP`,tableExchangeThresholdReached:e=>`You've reached the exchange threshold (${r(e)} CHIP) \u2014 use /blackjack exchange to turn CHIP into model quota`,tableExchangeThresholdShort:(e,n)=>`${r(e)} CHIP short of the exchange threshold (${r(n)}) \u2014 once you cross it, use /blackjack exchange to turn CHIP into model quota`,tableExchangedQuotaNote:e=>`The ${r(e)} CHIP you've exchanged automatically takes over the one request that fails once your own model quota runs out; it isn't touched otherwise.`,tablePoolEmptyNotice:()=>"(This season's pool is spent \u2014 wins now grant points instead)",actionShortfallHint:e=>`${r(e)} more CHIP needed \u2014 balance too low`,tableCostSuffix:e=>` (${r(e)})`,betAmountMissing:()=>"Enter an amount.",betAmountExceedsCap:e=>`The cap for a single hand is ${r(e)} CHIP.`,betAmountExceedsBalance:()=>"Your balance isn't enough for this amount.",customBetToggle:()=>"Custom amount",customBetSubmit:()=>"Start",customBetRange:e=>`1 to ${r(e)} CHIP`,noActiveRoundNotice:()=>"No hand is in progress right now.",freeHandsGroupTitle:()=>"Free hands",freeHandsRemainingToday:e=>` \xB7 ${e} left today`,freeHandsExhaustedToday:()=>" \xB7 used up for today \u2014 come back tomorrow",freeHandExplainer:(e,n)=>`No CHIP required. Win and you take ${r(e)} CHIP; a natural blackjack in the first two cards (an Ace with a 10/J/Q/K) pays ${r(n)}. Lose or tie and you are out nothing.`,startFreeHandButton:()=>"Start a free hand",raiseGroupTitle:()=>"Raise from your balance",insufficientBalanceSuffix:()=>" \xB7 balance too low",raiseExplainer:()=>"The CHIP you raise comes from your balance: win and it doubles, lose and it is gone.",rematchFreeLabel:()=>"Play again (free)",dealButtonFreeHandsSuffix:e=>` \u2014 ${e} left`,dismissButton:()=>"Dismiss",actionFailedNotice:()=>"That didn't go through \u2014 you've been refreshed to the latest state."};function R(e){return e==="zh"?B:A}var m=require("react/jsx-runtime"),pe=["slots"];async function H(e,n){let t=await fetch(e,n),o=await t.text(),i=o.length>0?JSON.parse(o):{};if(!t.ok){let d=typeof i?.error=="string"?i.error:`${t.status}`;throw new Error(d)}return i}function me(){let e=typeof navigator=="object"&&navigator!==null?navigator.language:void 0;return typeof e=="string"&&e.toLowerCase().startsWith("zh")?"zh":"en"}function ge(){return"en"}function M(e){return e?e.getSnapshot().active:me()}function D(e,n){return e?e.subscribe(n):()=>{}}function F(e){let n=(0,u.useCallback)(o=>D(e,o),[e]),t=(0,u.useCallback)(()=>M(e),[e]);return(0,u.useSyncExternalStore)(n,t,ge)}function fe({node:e,locale:n}){let t=N(e),o=F(n),i=R(o),[d,c]=(0,u.useState)(null),[l,h]=(0,u.useState)(!1),p=(0,u.useCallback)(()=>{H("/blackjack/api/state").then(b=>c(g=>f(g??{consented:!1},{type:"synced",payload:b}))).catch(()=>c({consented:!1}))},[]),x=(0,u.useCallback)(()=>{H("/blackjack/api/state").then(b=>c(g=>g&&f(g,{type:"meRefreshed",me:b.me}))).catch(()=>{})},[]);(0,u.useEffect)(()=>{t.table&&p()},[p,t.table]);let k=(0,u.useCallback)((b,g)=>{H(b,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(g)}).then(J=>{h(!1),c(C=>C&&f(C,{type:"round",round:J})),x()}).catch(()=>{h(!0),p()})},[x,p]),q=(0,u.useCallback)(b=>k("/blackjack/api/deal",b===void 0?{}:{betChips:b}),[k]),V=(0,u.useCallback)(b=>k("/blackjack/api/action",{action:b}),[k]),G=(0,u.useCallback)(()=>c(b=>b&&f(b,{type:"dismissed"})),[]),y=t.text===void 0?null:(0,m.jsx)("div",{style:{whiteSpace:"pre-wrap",marginBottom:"8px",...t.isError?{color:"#c0392b"}:{}},children:(0,m.jsx)(L,{text:t.text})});return!t.table||d===null?y:(0,m.jsxs)("div",{children:[y,(0,m.jsx)($,{state:d,m:i,onAction:V,onDeal:q,onDismiss:G,error:l?i.actionFailedNotice():void 0})]})}function xe({node:e}){return(0,m.jsx)("div",{style:{opacity:.75,fontFamily:"ui-monospace, SFMono-Regular, Menlo, monospace",margin:"4px 0"},children:e.data.text})}function ke(e){let n=e.get("conversationEvents",!1);n!==void 0&&(n.register(T),e.slots.inject("conversation.chat.node",()=>e.slots.register({name:"conversation.chat.node",key:w,inject:()=>({})},xe))),e.slots.inject("conversation.chat.commandview",()=>e.slots.register({name:"conversation.chat.commandview",key:"blackjack",inject:()=>({})},t=>(0,m.jsx)(fe,{...t,locale:e.get("locale",!1)})))}
|
|
52
52
|
return module.exports;
|
|
53
53
|
}
|
|
54
54
|
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-blackjack",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "社区福利小游戏插件:在 dsh 里玩 21
|
|
3
|
+
"version": "0.2.2",
|
|
4
|
+
"description": "Community perk game plugin: play blackjack in dsh to win spendable model credit. 社区福利小游戏插件:在 dsh 里玩 21 点赢取可消费的模型额度。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"dsh-plugin",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"dist",
|
|
24
24
|
"blackjack.cordis.yml",
|
|
25
25
|
"README.md",
|
|
26
|
+
"README.zh-CN.md",
|
|
26
27
|
"LICENSE"
|
|
27
28
|
],
|
|
28
29
|
"engines": {
|