herdrchat 1.1.1 → 1.1.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/config.env.example +4 -3
- package/lib/chats/telegram/index.mjs +19 -9
- package/lib/core/config.mjs +6 -4
- package/lib/core/engine.mjs +6 -0
- package/lib/core/menu.mjs +10 -1
- package/package.json +1 -1
- package/skills/README.md +1 -1
- package/skills/herdrchat/SKILL.md +16 -9
- package/skills/herdrchat/references/ops.md +1 -1
- package/skills/herdrchat/references/telegram.md +77 -23
package/config.env.example
CHANGED
|
@@ -17,10 +17,11 @@ TELEGRAM_BOT_TOKEN=
|
|
|
17
17
|
TELEGRAM_OWNER_ID=
|
|
18
18
|
|
|
19
19
|
HERDR_SESSIONS=session-a,session-b
|
|
20
|
-
#
|
|
21
|
-
# empty for none, or spaces,
|
|
20
|
+
# spaces whose rooms are quiet, so only "needs you" and "done" ring (Feishu: groups muted
|
|
21
|
+
# once /auth is done; Telegram: silent posts): `all` (default), empty for none, or spaces,
|
|
22
|
+
# comma-separated; elsewhere every post rings
|
|
22
23
|
HERDR_MUTED_SPACES=all
|
|
23
|
-
# spaces
|
|
24
|
+
# spaces where nothing rings, not even "needs you": empty (default), `all`, or spaces
|
|
24
25
|
HERDR_SILENT_SPACES=
|
|
25
26
|
# the bot's tags next to group names (tenant tags): on (default) or off
|
|
26
27
|
HERDR_BOT_TAGS=on
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
// herdrchat's Telegram side. A bot cannot make groups (no such Bot API method), so each
|
|
2
2
|
// herdr-app tab is a topic in your private chat with the bot (topic mode must be on in
|
|
3
3
|
// @BotFather; getMe says has_topics_enabled), and the chat's main thread is the direct
|
|
4
|
-
// chat: the overview and the commands.
|
|
5
|
-
// notification without sound)
|
|
6
|
-
// you" and "done"
|
|
4
|
+
// chat: the overview and the commands. In a space of HERDR_MUTED_SPACES (all, by
|
|
5
|
+
// default) the bot posts silently (a notification without sound) and `ring` sends a short
|
|
6
|
+
// reply that sounds, for "needs you" and "done"; a bot cannot mute a chat for you, so
|
|
7
|
+
// this stands in for Feishu's muted groups. Elsewhere every post sounds. A space of
|
|
8
|
+
// HERDR_SILENT_SPACES never sounds. The core (lib/core/engine.mjs) calls only what is returned here; the
|
|
7
9
|
// contract is lib/chats/contract.mjs.
|
|
8
10
|
//
|
|
9
11
|
// What Telegram does differently, and what stands in:
|
|
@@ -71,11 +73,18 @@ export function create({ cfg, store, log, logWarn, logError }) {
|
|
|
71
73
|
const buffer = images.get(id);
|
|
72
74
|
if (!buffer) continue;
|
|
73
75
|
images.delete(id);
|
|
74
|
-
last = String((await tg.sendPhoto(owner, room, buffer)).message_id);
|
|
76
|
+
last = String((await tg.sendPhoto(owner, room, buffer, { silent: quiet(room) })).message_id);
|
|
75
77
|
}
|
|
76
78
|
return last;
|
|
77
79
|
}
|
|
78
80
|
|
|
81
|
+
// Whether a room's posts go silently: its space is in HERDR_MUTED_SPACES.
|
|
82
|
+
const muted = cfg.muted || { all: true, spaces: [] };
|
|
83
|
+
function quiet(room) {
|
|
84
|
+
const c = Object.values(read().chats).find((x) => x.chat === room);
|
|
85
|
+
return !c || inSpaces(muted, spaceOfName(c.name));
|
|
86
|
+
}
|
|
87
|
+
|
|
79
88
|
// HTML that Telegram refuses (an entity it cannot parse) goes again as plain text.
|
|
80
89
|
async function sendText(chatId, threadId, html, plain, { silent = true, extra = {} } = {}) {
|
|
81
90
|
const base = { chat_id: chatId, ...(threadId ? { message_thread_id: Number(threadId) } : {}), disable_notification: silent, link_preview_options: { is_disabled: true }, ...extra };
|
|
@@ -91,7 +100,7 @@ export function create({ cfg, store, log, logWarn, logError }) {
|
|
|
91
100
|
async function post(room, markdown) {
|
|
92
101
|
const { text, found } = takeImages(markdown);
|
|
93
102
|
let last = null;
|
|
94
|
-
if (text) for (const piece of split(text)) last = String((await sendText(owner, room, toHtml(piece), piece)).message_id);
|
|
103
|
+
if (text) for (const piece of split(text)) last = String((await sendText(owner, room, toHtml(piece), piece, { silent: quiet(room) })).message_id);
|
|
95
104
|
return (await sendImages(room, found)) || last;
|
|
96
105
|
}
|
|
97
106
|
|
|
@@ -99,7 +108,7 @@ export function create({ cfg, store, log, logWarn, logError }) {
|
|
|
99
108
|
|
|
100
109
|
async function postCard(room, look) {
|
|
101
110
|
const { text, found } = takeImages(look.markdown);
|
|
102
|
-
const message = String((await sendText(owner, room, lookHtml({ ...look, markdown: text }), `${look.title}\n\n${text}
|
|
111
|
+
const message = String((await sendText(owner, room, lookHtml({ ...look, markdown: text }), `${look.title}\n\n${text}`, { silent: quiet(room) })).message_id);
|
|
103
112
|
await sendImages(room, found);
|
|
104
113
|
return message;
|
|
105
114
|
}
|
|
@@ -146,7 +155,7 @@ export function create({ cfg, store, log, logWarn, logError }) {
|
|
|
146
155
|
const doneHtml = (dialog, done) => `<b>${escapeHtml(dialog.doneTitle)}</b>\n\n${toHtml(dialog.markdown)}\n\n<b>${escapeHtml(done)}</b>`;
|
|
147
156
|
|
|
148
157
|
async function postDialog(room, dialog, value) {
|
|
149
|
-
const res = await sendText(owner, room, dialogHtml(dialog), dialog.markdown, { extra: { reply_markup: keyboard(dialog, value) } });
|
|
158
|
+
const res = await sendText(owner, room, dialogHtml(dialog), dialog.markdown, { silent: quiet(room), extra: { reply_markup: keyboard(dialog, value) } });
|
|
150
159
|
return String(res.message_id);
|
|
151
160
|
}
|
|
152
161
|
|
|
@@ -195,10 +204,11 @@ export function create({ cfg, store, log, logWarn, logError }) {
|
|
|
195
204
|
const pin = async (k) => { pins.add(k); };
|
|
196
205
|
const unpin = async (k) => { pins.delete(k); };
|
|
197
206
|
|
|
198
|
-
//
|
|
207
|
+
// In a quiet room this reply is what sounds; in another the post sounded already. A
|
|
208
|
+
// space in HERDR_SILENT_SPACES never sounds.
|
|
199
209
|
async function ring(k, message, text) {
|
|
200
210
|
const c = read().chats[k];
|
|
201
|
-
if (!message || !c || inSpaces(cfg.silent, spaceOfName(c.name))) return;
|
|
211
|
+
if (!message || !c || !quiet(c.chat) || inSpaces(cfg.silent, spaceOfName(c.name))) return;
|
|
202
212
|
await sendText(owner, c.chat, escapeHtml(text), text, { silent: false, extra: { reply_parameters: { message_id: Number(message), allow_sending_without_reply: true } } })
|
|
203
213
|
.catch((err) => logError(`ring: ${err.message}`));
|
|
204
214
|
}
|
package/lib/core/config.mjs
CHANGED
|
@@ -12,10 +12,12 @@
|
|
|
12
12
|
// TELEGRAM_BOT_TOKEN herdrchat's Telegram bot (topic mode on, in @BotFather)
|
|
13
13
|
// TELEGRAM_OWNER_ID your Telegram user id; the only person it listens to
|
|
14
14
|
// HERDR_SESSIONS hc sessions to mirror, comma-separated
|
|
15
|
-
// HERDR_MUTED_SPACES spaces whose
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
15
|
+
// HERDR_MUTED_SPACES spaces whose rooms are quiet, so only "needs you" and
|
|
16
|
+
// "done" ring (Feishu: groups muted, once /auth is done,
|
|
17
|
+
// and an @; Telegram: silent posts and a reply that
|
|
18
|
+
// rings): `all` (default), empty for none, or spaces,
|
|
19
|
+
// comma-separated; elsewhere every post rings
|
|
20
|
+
// HERDR_SILENT_SPACES spaces where nothing rings, not even "needs you":
|
|
19
21
|
// empty (default) for none, `all`, or spaces
|
|
20
22
|
// HERDR_BOT_TAGS `on` (default) or `off`: the bot's tags next to group
|
|
21
23
|
// names (tenant tags: session or machine, and "ended")
|
package/lib/core/engine.mjs
CHANGED
|
@@ -595,6 +595,12 @@ export function createEngine({ cfg, chat, hc, readers, store, log, logWarn, logE
|
|
|
595
595
|
if (steady) break;
|
|
596
596
|
}
|
|
597
597
|
const k = key(session, address);
|
|
598
|
+
// answered while its screen was being read: nothing waits, so no card
|
|
599
|
+
const nowStatus = (await hc.getTerminal(session, address).catch(() => null))?.status;
|
|
600
|
+
if (nowStatus && nowStatus.state !== 'blocked') {
|
|
601
|
+
log(`<- ${session}/${address}: no card, it no longer waits (${nowStatus.state})`);
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
598
604
|
if (!claimDialog(k, dialog, record.status) && !again) return;
|
|
599
605
|
await closeCard(k, 'It moved on; see the next card.');
|
|
600
606
|
if (!dialog.menu) {
|
package/lib/core/menu.mjs
CHANGED
|
@@ -13,12 +13,21 @@
|
|
|
13
13
|
// above a multi-question dialog, which change as you answer: the same dialog keeps its key). A card click is pressed only when the
|
|
14
14
|
// screen still shows the dialog with that key, so an old card can never answer a newer
|
|
15
15
|
// dialog that looks alike (two permission prompts both offer "1. Yes").
|
|
16
|
+
//
|
|
17
|
+
// A question with previews draws a box beside its options, on the same lines, and the
|
|
18
|
+
// box shows the preview of the option under the cursor: what is right of it is cut off
|
|
19
|
+
// (three spaces or more, then a box line), so it is neither part of an option's label
|
|
20
|
+
// nor of the key; a question's own left border (│) goes too.
|
|
16
21
|
import crypto from 'node:crypto';
|
|
17
22
|
|
|
18
23
|
const OPTION = /^\s*(?:❯\s*)?(\d{1,2})\.\s+(?:\[(.)\]\s+)?(.*\S)\s*$/;
|
|
19
24
|
const RULE = /^[\s─╌━-]*$/;
|
|
20
25
|
const BORDER = /^\s*─{10,}/;
|
|
21
26
|
const FREE = /^Type something/i;
|
|
27
|
+
const BESIDE = /\s{3,}[┌└│├┤┐┘╭╰─┬┴┼║╔╚].*$/u;
|
|
28
|
+
const LEFT_BORDER = /^(\s*)│\s?/u;
|
|
29
|
+
|
|
30
|
+
const ownPart = (line) => line.replace(BESIDE, '').replace(LEFT_BORDER, '$1');
|
|
22
31
|
|
|
23
32
|
const TEXT_LINES = 15; // a dialog with no border found: this many lines above its options
|
|
24
33
|
|
|
@@ -36,7 +45,7 @@ function keyOf(lines, top, last) {
|
|
|
36
45
|
}
|
|
37
46
|
|
|
38
47
|
export function menuOf(screen) {
|
|
39
|
-
const lines = screen.split('\n');
|
|
48
|
+
const lines = screen.split('\n').map(ownPart);
|
|
40
49
|
let run = null;
|
|
41
50
|
let menu = null;
|
|
42
51
|
lines.forEach((line, i) => {
|
package/package.json
CHANGED
package/skills/README.md
CHANGED
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
it to set up, run and look after herdrchat. Its commands are `/herdrchat <command>`.
|
|
5
5
|
|
|
6
6
|
Each part of the system has one reference under `herdrchat/references/`, named after the
|
|
7
|
-
part: a chat app (`feishu.md`,
|
|
7
|
+
part: a chat app (`feishu.md`, `telegram.md`, later `slack.md`, `discord.md`) or a part of
|
|
8
8
|
the core (`ops.md`, later `reader.md`). New work goes into the reference of its part, or
|
|
9
9
|
a new one for a new part; `SKILL.md` only lists commands and points to references.
|
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: herdrchat
|
|
3
|
-
description: Set up, run and look after herdrchat, the chat front-end for hc that mirrors every
|
|
3
|
+
description: Set up, run and look after herdrchat, the chat front-end for hc that mirrors every herdr-app tab into a chat app - Feishu (one group per tab) or Telegram (one topic per tab in the bot's private chat), with dialog buttons, an overview and quiet phones - first setup of either app, running both side by side, start and stop, status and logs, /auth, settings, fixing problems, the Feishu developer console and @BotFather. Use for anything about herdrchat or its Feishu or Telegram bot, even when the user only says "set up the Telegram bot", "is it running", "it doesn't post", "mute the workers" or "publish the bot". Trigger with "/herdrchat", "/herdrchat setup", "/herdrchat status", "/herdrchat fix".
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# herdrchat
|
|
7
7
|
|
|
8
|
-
herdrchat mirrors every
|
|
9
|
-
|
|
10
|
-
chat.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
herdrchat mirrors every herdr-app tab into a chat app: a group per tab in Feishu, a topic
|
|
9
|
+
per tab in the bot's private chat in Telegram; the conversation as it happens, dialogs as
|
|
10
|
+
buttons, an overview in the direct chat. One profile is one chat app (`HERDRCHAT_CHAT`);
|
|
11
|
+
to use both, run a profile each (`--profile telegram` beside the default Feishu one).
|
|
12
|
+
|
|
13
|
+
Its command is `herdrchat` (`node bin/cli.mjs` in a clone): check with `command -v
|
|
14
|
+
herdrchat`; when it is missing, ask before `npm install -g herdrchat`. A profile
|
|
15
|
+
(`--profile`, default `default`) keeps its settings in
|
|
16
|
+
`~/.config/herdrchat/<profile>/config.env` and its state in
|
|
14
17
|
`~/.local/state/herdrchat/<profile>/`; `herdrchat status` prints both.
|
|
15
18
|
|
|
16
19
|
## Commands
|
|
17
20
|
|
|
18
21
|
| Command | Do |
|
|
19
22
|
|---|---|
|
|
20
|
-
| `/herdrchat setup` | First
|
|
23
|
+
| `/herdrchat setup` | First ask which chat app (Feishu, Telegram, or both: one profile each). Telegram: `/herdrchat telegram setup`. Feishu, in order: `herdrchat init`; make the Feishu app (`references/feishu.md`: create, bot, scopes, events and callbacks); fill `config.env` with the user (app id, secret, `HERDR_SESSIONS`: the hc sessions herdr-app shows on this machine); `herdrchat start`; publish a version; get the user's open_id (they message the bot, it answers with it) into `FEISHU_OWNER_OPEN_ID`; start again. Optional: `/herdrchat auth`. |
|
|
21
24
|
| `/herdrchat status` | `herdrchat status`; explain each line; on errors, `/herdrchat fix`. |
|
|
22
25
|
| `/herdrchat start`, `/herdrchat stop` | Read `config.env` (its `HERDR_SESSIONS`) in one step, then `herdrchat start` / `stop` in the next; never both at once. |
|
|
23
26
|
| `/herdrchat logs` | `herdrchat logs --errors` (or `-n 100`); explain them with `references/ops.md` "Logs". |
|
|
@@ -25,6 +28,7 @@ in `~/.config/herdrchat/<profile>/config.env` and its state in
|
|
|
25
28
|
| `/herdrchat config` | Change a setting in `config.env` with the user (sessions, `HERDR_MUTED_SPACES`, `HERDR_SILENT_SPACES`, `HERDR_BOT_TAGS`, `HERDR_USER_TAGS`, `HERDR_HISTORY`, `HERDRCHAT_CHAT`; each is explained in `config.env.example`), read the file back, then restart. |
|
|
26
29
|
| `/herdrchat fix` | Find the cause from `herdrchat status`, the logs and the live result, then fix it: `references/ops.md`. |
|
|
27
30
|
| `/herdrchat feishu <task>` | Anything in the Feishu developer console: scopes, callbacks, publishing, the icon, deleting an app: `references/feishu.md`. |
|
|
31
|
+
| `/herdrchat telegram <task>` | The Telegram bot, as its own profile: `setup` (@BotFather, topic mode, token, owner id, first start), checking the token, quiet and silent spaces, what Telegram cannot do and what stands in: `references/telegram.md`. |
|
|
28
32
|
|
|
29
33
|
With no subcommand, run `herdrchat status` and suggest the next step from it.
|
|
30
34
|
|
|
@@ -32,7 +36,8 @@ With no subcommand, run `herdrchat status` and suggest the next step from it.
|
|
|
32
36
|
|
|
33
37
|
- Ask before anything outward-facing: publishing a version, applying scopes, deleting or
|
|
34
38
|
disbanding anything, sending to a real group. One yes covers one action.
|
|
35
|
-
- Never print, screenshot or paste `FEISHU_APP_SECRET` or
|
|
39
|
+
- Never print, screenshot or paste `FEISHU_APP_SECRET`, `TELEGRAM_BOT_TOKEN` or
|
|
40
|
+
`user-token.json`; ask the user to paste a token into `config.env` themselves.
|
|
36
41
|
- A check that decides an action runs on its own first (read the config, then start;
|
|
37
42
|
scan, then commit).
|
|
38
43
|
- Say what was checked on the live bot and what was not.
|
|
@@ -41,5 +46,7 @@ With no subcommand, run `herdrchat status` and suggest the next step from it.
|
|
|
41
46
|
|
|
42
47
|
- `references/feishu.md`: the bot's app in the Feishu developer console, with Ego or as a
|
|
43
48
|
guided walk-through; the scopes this bot uses.
|
|
49
|
+
- `references/telegram.md`: the Telegram bot, from @BotFather to the first topic; what
|
|
50
|
+
it does differently from Feishu.
|
|
44
51
|
- `references/ops.md`: running and checking the bot, its logs and state, the usual
|
|
45
52
|
problems, trying a Feishu API safely, committing without a leak.
|
|
@@ -90,7 +90,7 @@ Say which checks were run and which were not; "the code is there" is not "it wor
|
|
|
90
90
|
| nothing posts | bot down, or `FEISHU_OWNER_OPEN_ID` unset | check it runs; the log says "owner not set" |
|
|
91
91
|
| a button gives error 200340 | the card callback is not set up | `references/feishu.md`: events and card callbacks |
|
|
92
92
|
| a dialog had no card | answered in the terminal before the card; or no numbered menu on screen | log: `blocked`; the card says `/answer` when it saw no menu |
|
|
93
|
-
| the phone rings too much | not `/auth`ed
|
|
93
|
+
| the phone rings too much | Feishu: not `/auth`ed; either app: settings | Feishu `/auth`; `HERDR_MUTED_SPACES` (quiet but for needs you and done), `HERDR_SILENT_SPACES` (nothing rings), per profile |
|
|
94
94
|
| a ⚠️ warning in the direct chat about acting as you | the token could not be refreshed | send `/auth` again |
|
|
95
95
|
| a group keeps an old name | its terminal is not live | it renames when the terminal is back |
|
|
96
96
|
| a Feishu call fails with 400 in a burst | a rate limit (tags: 5 a second) | space the calls; the bot queues tag calls |
|
|
@@ -1,23 +1,77 @@
|
|
|
1
|
-
# Telegram:
|
|
2
|
-
|
|
3
|
-
A Telegram bot cannot make groups
|
|
4
|
-
your private chat with the bot
|
|
5
|
-
(
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
1
|
+
# Telegram: the bot, from @BotFather to the first topic
|
|
2
|
+
|
|
3
|
+
A Telegram bot cannot make groups (the Bot API has no such method), so herdrchat puts each
|
|
4
|
+
herdr-app tab in a **topic** of your private chat with the bot: each has its own name,
|
|
5
|
+
icon color (one per space), unread count, and can be muted on its own. The chat's main
|
|
6
|
+
thread holds the overview and the commands. It runs as its **own profile**, beside a
|
|
7
|
+
Feishu one; each bot follows the same hc terminals, so an answer in either reaches the
|
|
8
|
+
terminal, and a dialog answered in one closes its card in the other.
|
|
9
|
+
|
|
10
|
+
## Setup (`/herdrchat telegram setup`)
|
|
11
|
+
|
|
12
|
+
Do these in order; the user does the Telegram steps, you check each one.
|
|
13
|
+
|
|
14
|
+
1. **The bot.** The user messages **@BotFather**: `/newbot`, a name, a username ending in
|
|
15
|
+
`bot`. BotFather answers with a token. The user keeps it: never ask them to paste it
|
|
16
|
+
into the chat.
|
|
17
|
+
2. **Topic mode.** It is not in BotFather's `/mybots` → Bot Settings list; it is in
|
|
18
|
+
BotFather's mini app (the **Open** button at the bottom left of the BotFather chat):
|
|
19
|
+
pick the bot, turn on Threaded Mode / topics. Check it (step 5): `has_topics_enabled`
|
|
20
|
+
must be `true`, or no topic can be made.
|
|
21
|
+
3. **The profile's config.** Make `~/.config/herdrchat/telegram/config.env` (folder mode
|
|
22
|
+
700, file 600) with:
|
|
23
|
+
```
|
|
24
|
+
HERDRCHAT_CHAT=telegram
|
|
25
|
+
TELEGRAM_BOT_TOKEN=
|
|
26
|
+
TELEGRAM_OWNER_ID=
|
|
27
|
+
HERDR_SESSIONS=<the same sessions as the Feishu profile>
|
|
28
|
+
```
|
|
29
|
+
and ask the user to paste the token after `TELEGRAM_BOT_TOKEN=` themselves
|
|
30
|
+
(`open -e <file>` opens it), no quotes, no spaces.
|
|
31
|
+
4. **First start, to learn the owner.** `herdrchat start --profile telegram`; the user
|
|
32
|
+
messages the bot anything; it answers `TELEGRAM_OWNER_ID=<digits>`. The user puts that
|
|
33
|
+
in the config. Until then the bot makes no topic.
|
|
34
|
+
5. **Check, without printing secrets.** In a short `node -e`: read the token from the
|
|
35
|
+
config, call `https://api.telegram.org/bot<token>/getMe`, print only `username` and
|
|
36
|
+
`has_topics_enabled`; for the owner, print only whether `TELEGRAM_OWNER_ID` is digits.
|
|
37
|
+
6. **Start again.** Read the config (secrets hidden) in one step, then
|
|
38
|
+
`herdrchat start --profile telegram` in the next. One topic per tab appears; Telegram
|
|
39
|
+
takes about one message a second in a chat, so twenty tabs take twenty seconds. A new
|
|
40
|
+
topic gets the conversation before it (`HERDR_HISTORY`); `/history` in a topic posts it
|
|
41
|
+
again.
|
|
42
|
+
|
|
43
|
+
## What rings
|
|
44
|
+
|
|
45
|
+
The same settings as Feishu, in the Telegram profile's own config.env:
|
|
46
|
+
|
|
47
|
+
- `HERDR_MUTED_SPACES` (default `all`): in these spaces posts go silently (a notification
|
|
48
|
+
without sound) and only "⚠️ needs you" and "✅ done" ring, as a short reply. Elsewhere
|
|
49
|
+
every post rings.
|
|
50
|
+
- `HERDR_SILENT_SPACES` (default none): nothing rings there, not even "needs you".
|
|
51
|
+
|
|
52
|
+
A bot cannot mute a chat or a topic for the user. A topic the user mutes in Telegram is
|
|
53
|
+
fully quiet, "needs you" included; `HERDR_SILENT_SPACES` does the same for a whole space
|
|
54
|
+
without muting each topic by hand.
|
|
55
|
+
|
|
56
|
+
## What Telegram does differently
|
|
57
|
+
|
|
58
|
+
| Feishu | Telegram, and what stands in |
|
|
59
|
+
|---|---|
|
|
60
|
+
| a group per tab | a topic per tab, in the bot's private chat |
|
|
61
|
+
| an ended group gets the "ended" tag | an ended topic's name starts with 🔚 (closing a topic is documented for supergroups only) |
|
|
62
|
+
| an image inside a card | each image as its own photo after the text |
|
|
63
|
+
| a text box on a dialog card | type the answer in the topic |
|
|
64
|
+
| tags, labels, pins, `/auth` | none (a topic cannot be pinned; a bot cannot act as the user) |
|
|
65
|
+
| the overview card | one message in the main thread, edited in place |
|
|
66
|
+
| `/` commands: typed | `/` shows a menu: ls, new, kill, screen, answer, esc, history |
|
|
67
|
+
|
|
68
|
+
## When something is off
|
|
69
|
+
|
|
70
|
+
| Seen | Cause | Do |
|
|
71
|
+
|---|---|---|
|
|
72
|
+
| log: `topic mode is off for @<bot>` | step 2 not done | turn it on in BotFather's mini app, check `has_topics_enabled`, start again |
|
|
73
|
+
| the bot answers `Owner is not set` | `TELEGRAM_OWNER_ID` empty | step 4 |
|
|
74
|
+
| `herdrchat start` says it did not reach its chat app | wrong token, or no network | check with getMe (step 5) |
|
|
75
|
+
| log: `telegram poll: ... 409` | another copy polls the same token | `herdrchat stop --profile telegram`, find the other one (another machine?), start once |
|
|
76
|
+
| log: `too fast, waiting N s` | Telegram's flood limit; the bot waits as told | nothing; it catches up |
|
|
77
|
+
| a click answers "This dialog is gone" | the bot restarted after the card was posted (button ids live in memory) | answer the newest card, or in the terminal |
|