mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-05-10 17:18:31 +00:00
support-bot: plans use params instead of regex in /join
This commit is contained in:
@@ -270,9 +270,9 @@ Card is a single message. The join command is the final line of the card text
|
||||
|
||||
**Message count:** All messages in chat history except the bot's own (`groupSnd` from main profile).
|
||||
|
||||
**Message preview:** Last several messages, most recent last, separated by ` / `. Newlines in message text are replaced with spaces to prevent card layout bloat from spam. The customer's display name is sanitized (newlines → spaces) for the card header; the `/join` command no longer embeds the customer name (numeric id only). Newest messages are prioritized — when the total exceeds ~500 chars (`maxTotal = 500` in `composeCard`), the oldest messages are truncated (with `[truncated]` prepended) while the newest are always shown. When truncation occurs, the first visible message is guaranteed to have a sender prefix even if it was a continuation in the original sequence. Each message is prefixed with the sender's name (`Name: message`) on the first message in a consecutive run from that sender - subsequent messages from the same sender omit the prefix until a different sender's message appears. Sender identification: Grok contact is detected by `grokContactId` and labeled "Grok"; the customer is identified by matching `memberId` to the group's `customerId` and labeled with their display name; all other members use their `memberProfile.displayName`. Bot's own messages (`groupSnd`) are excluded. Each message truncated to ~200 chars. Media-only messages show type labels: `[image]`, `[file]`, `[voice]`, `[video]`.
|
||||
**Message preview:** Last several messages, most recent last, separated by ` / `. Newlines in message text are replaced with spaces to prevent card layout bloat from spam. The customer's display name is sanitized (newlines → spaces) for the card header; the `/join` command embeds only the numeric group id. Newest messages are prioritized — when the total exceeds ~500 chars (`maxTotal = 500` in `composeCard`), the oldest messages are truncated (with `[truncated]` prepended) while the newest are always shown. When truncation occurs, the first visible message is guaranteed to have a sender prefix even if it was a continuation in the original sequence. Each message is prefixed with the sender's name (`Name: message`) on the first message in a consecutive run from that sender - subsequent messages from the same sender omit the prefix until a different sender's message appears. Sender identification: Grok contact is detected by `grokContactId` and labeled "Grok"; the customer is identified by matching `memberId` to the group's `customerId` and labeled with their display name; all other members use their `memberProfile.displayName`. Bot's own messages (`groupSnd`) are excluded. Each message truncated to ~200 chars. Media-only messages show type labels: `[image]`, `[file]`, `[voice]`, `[video]`.
|
||||
|
||||
**Join command:** the final line of the card renders as `/'join <groupId>'` where `<groupId>` is the customer group's numeric ID. The outer single quotes around `join <groupId>` are rendered by SimpleX clients as a clickable quoted command; tapping it sends `/join <groupId>` back to the team group. The bot's `/join` handler accepts both forms: `/join <groupId>` (numeric only — what the card produces and what a tap sends) and the legacy `/join <groupId>:<name>` (still parsed for backward compatibility with operators who type the old syntax; the `:<name>` suffix is extracted but ignored — the groupId alone identifies the target group).
|
||||
**Join command:** the final line of the card renders as `/'join <groupId>'` where `<groupId>` is the customer group's numeric ID. The outer single quotes around `join <groupId>` are rendered by SimpleX clients as a clickable quoted command; tapping it sends `/join <groupId>` back to the team group. The handler does not pattern-match the message text — it uses the framework's structured command parser (`util.ciBotCommand`) which returns `{keyword: "join", params: "<groupId>"}` directly from the chat item. The handler then converts `params` to an integer via `Number.parseInt(params, 10)` and rejects anything that is not a positive integer. There is no legacy `/join <groupId>:<name>` form — the card never emits it, so the handler never needs to strip it.
|
||||
|
||||
### Card lifecycle
|
||||
|
||||
@@ -833,13 +833,14 @@ Ordering guarantees preserved:
|
||||
|
||||
| Command | Effect |
|
||||
|---------|--------|
|
||||
| `/join <groupId>` (legacy `/join <groupId>:<name>` also accepted) | Join specified customer group |
|
||||
| `/join <groupId>` | Join specified customer group |
|
||||
|
||||
**`/join` handling:**
|
||||
1. Parse `groupId` from command. Regex: `/^\/join\s+(\d+)(?::.*)?\s*$/` — captures the numeric groupId; the optional `:<name>` tail (legacy) is matched but discarded. Cards emit `/'join <groupId>'`; when a team member taps that rendered token, the SimpleX client sends `/join <groupId>` (quotes stripped by the client as formatting) and this regex matches the numeric-only form. Operators who type the legacy `/join 42:Alice` still match and the `:Alice` suffix is ignored.
|
||||
2. Validate target is a business group (has `businessChat` property) — error in team group if not
|
||||
3. Add requesting team member to customer group via `apiAddMember`
|
||||
4. Member promoted to Owner on `connectedToGroupMember` (see §8)
|
||||
1. Extract `{keyword, params}` from the chat item with `util.ciBotCommand(chatItem)`. The framework already parses the leading `/keyword` and returns the trimmed remainder as `params` — the handler does not run its own regex over the message text. Cards emit `/'join <groupId>'`; a team-member tap delivers a chat item whose text is `/join <groupId>`, which `ciBotCommand` returns as `{keyword: "join", params: "<groupId>"}`.
|
||||
2. Convert `params` to a number with `const targetGroupId = Number.parseInt(params, 10)`. If `Number.isNaN(targetGroupId) || targetGroupId <= 0`, reply in the team group with `Error: invalid group id "${params}"` and return. No regex, no `split(":")`, no legacy fallback — operators must use the numeric form (which is what the card always emits).
|
||||
3. Validate target is a business group (has `businessChat` property) — error in team group if not.
|
||||
4. Add requesting team member to customer group via `apiAddMember`.
|
||||
5. Member promoted to Owner on `connectedToGroupMember` (see §8).
|
||||
|
||||
**Team member promotion:** On every `connectedToGroupMember` in a customer group, promote to Owner unless customer or Grok. Idempotent.
|
||||
|
||||
@@ -1289,8 +1290,8 @@ Called as: `const p = simulateGrokJoinSuccess(); await bot.onNewChatItems(...);
|
||||
- message count excludes bot's own
|
||||
|
||||
#### 10. /join Command (5 tests)
|
||||
- /join <groupId> (numeric only) → team member added
|
||||
- /join <groupId>:<name> (legacy form) → team member added, `:<name>` suffix ignored
|
||||
- /join <groupId> (the only accepted form) → team member added; `params` from `ciBotCommand` is parsed via `Number.parseInt`, no regex
|
||||
- /join with non-numeric `params` (e.g. `/join abc`) → error reply in team group, no `apiAddMember` call
|
||||
- /join non-business group → error
|
||||
- /join non-existent groupId → error
|
||||
- customer /join in customer group → treated as normal message
|
||||
|
||||
@@ -188,7 +188,7 @@ Each card is **one** message with five parts (the join command is the final line
|
||||
|
||||
**Markdown escaping in previews** — SimpleX markdown interprets `!N<space>` (where N is `1`–`6`, `r`, `g`, `b`, `y`, `c`, `m`, or `-`) as styled-text markup, closing at the next `!`. There is no escape mechanism in the parser. To prevent customer/agent message text from triggering false color formatting or interfering with the blue `/` separator, the bot inserts a zero-width space (U+200B) between `!` and any color-trigger character in preview text before joining with the separator. This is invisible to the user but breaks the parser trigger pattern.
|
||||
|
||||
**Join command** — the final line of the card is `/'join <id>'`. The single quotes around `join <id>` make the whole token clickable in SimpleX clients; when tapped, the client sends `/join <id>` back to the team group. The bot's `/join` parser accepts two forms: `/join <id>` (numeric group id only; this is what the card emits and taps produce) and `/join <id>:<name>` (legacy form, retained so operators who typed the old syntax still work). The name, when present, is ignored by the handler — the groupId alone identifies the target group.
|
||||
**Join command** — the final line of the card is `/'join <id>'`. The single quotes around `join <id>` make the whole token clickable in SimpleX clients; when tapped, the client sends `/join <id>` back to the team group. The bot does not pattern-match the message text — it asks the framework for the structured command (`util.ciBotCommand` returns `{keyword: "join", params: "<id>"}`) and converts `params` to a number with `Number.parseInt`. The numeric form is the only accepted form: there is no `/join <id>:<name>` legacy syntax and no regex fallback.
|
||||
|
||||
The icon in line 1 is the sole urgency indicator — no reactions are used.
|
||||
|
||||
@@ -312,7 +312,7 @@ Team members use these commands in the team group:
|
||||
|
||||
| Command | Effect |
|
||||
|---------|--------|
|
||||
| `/join <groupId>` (or legacy `/join <groupId>:<name>`) | Join the specified customer group (promoted to Owner once connected). Card emits the clickable form `/'join <groupId>'`. |
|
||||
| `/join <groupId>` | Join the specified customer group (promoted to Owner once connected). Card emits the clickable form `/'join <groupId>'`; the handler reads `groupId` from the framework's structured command (`util.ciBotCommand → {keyword, params}`), not from regex over the message text. |
|
||||
|
||||
`/join` is **team-only** — it is registered as a bot command only in the team group. If a customer sends `/join` in a customer group, the bot treats it as an ordinary message (per the existing rule: unrecognized commands are treated as normal messages).
|
||||
|
||||
@@ -372,7 +372,7 @@ GROK_API_KEY=... node dist/index.js --team-group "Support Team" [options]
|
||||
|
||||
| Command | Effect |
|
||||
|---------|--------|
|
||||
| `/join <groupId>` (or legacy `/join <groupId>:<name>`) | Join the specified customer group (promoted to Owner once connected). Card emits the clickable form `/'join <groupId>'`. |
|
||||
| `/join <groupId>` | Join the specified customer group (promoted to Owner once connected). Card emits the clickable form `/'join <groupId>'`; the handler reads `groupId` from the framework's structured command (`util.ciBotCommand → {keyword, params}`), not from regex over the message text. |
|
||||
|
||||
### 5.2 Bot Architecture
|
||||
|
||||
|
||||
Reference in New Issue
Block a user