ping-a-human 0.1.0 → 0.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/README.md +13 -0
- package/dist/channels/telegram.js +26 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -98,6 +98,19 @@ The server reads configuration with this precedence:
|
|
|
98
98
|
The bot token is a secret — it is never logged or echoed. All diagnostics go to **stderr**; **stdout**
|
|
99
99
|
is reserved for the MCP JSON-RPC channel.
|
|
100
100
|
|
|
101
|
+
## Use it in pi
|
|
102
|
+
|
|
103
|
+
Using the [pi](https://pi.dev) coding agent? One command wires ping-a-human into
|
|
104
|
+
pi's hooks for notifications, human-in-the-loop approval, a `/ping` command, and a
|
|
105
|
+
global `pah` CLI:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
npx ping-a-human-pi
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
See the [`ping-a-human-pi`](./ping-a-human-pi) package or the
|
|
112
|
+
[pi guide](https://github.com/startriseio/ping-a-human/tree/main/ping-a-human-pi).
|
|
113
|
+
|
|
101
114
|
## Local development
|
|
102
115
|
|
|
103
116
|
```bash
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram bot commands (text beginning with "/", e.g. "/start", "/help") are
|
|
3
|
+
* client/protocol messages, never a human's answer to a free-text question.
|
|
4
|
+
* The most common offender is the "/start" Telegram auto-sends when a user
|
|
5
|
+
* first opens the bot, which would otherwise be returned as the reply.
|
|
6
|
+
*/
|
|
7
|
+
function isBotCommand(text) {
|
|
8
|
+
return /^\/[A-Za-z0-9_]+(@\w+)?(\s|$)/.test(text.trim());
|
|
9
|
+
}
|
|
1
10
|
/**
|
|
2
11
|
* Telegram implementation of the Channel interface using the Bot API over
|
|
3
12
|
* plain HTTPS (Node's built-in fetch). No third-party Telegram SDK.
|
|
@@ -58,6 +67,15 @@ export class TelegramChannel {
|
|
|
58
67
|
}
|
|
59
68
|
async awaitReply(options) {
|
|
60
69
|
const deadline = Date.now() + options.timeoutMs;
|
|
70
|
+
// Anchor: only accept replies that arrive AFTER the question was sent.
|
|
71
|
+
// Telegram message ids are monotonically increasing per chat, so any
|
|
72
|
+
// update whose message predates the question (e.g. a queued `/start` from
|
|
73
|
+
// first opening the bot) must be ignored — otherwise it would be wrongly
|
|
74
|
+
// returned as the human's answer. Honors AwaitReplyOptions.sinceRef.
|
|
75
|
+
const sinceMessageId = options.sinceRef != null ? Number(options.sinceRef.id) : undefined;
|
|
76
|
+
const isStale = (messageId) => sinceMessageId != null &&
|
|
77
|
+
messageId != null &&
|
|
78
|
+
messageId <= sinceMessageId;
|
|
61
79
|
while (Date.now() < deadline) {
|
|
62
80
|
const remainingMs = deadline - Date.now();
|
|
63
81
|
// Don't long-poll longer than the time we have left.
|
|
@@ -73,6 +91,13 @@ export class TelegramChannel {
|
|
|
73
91
|
// Free-text reply.
|
|
74
92
|
const msg = update.message;
|
|
75
93
|
if (msg?.text && this.fromConfiguredChat(msg.chat)) {
|
|
94
|
+
// Skip backlog that predates the question (e.g. a stale `/start`).
|
|
95
|
+
if (isStale(msg.message_id))
|
|
96
|
+
continue;
|
|
97
|
+
// Bot commands ("/start", "/help", ...) are never valid answers to a
|
|
98
|
+
// free-text question; treat them as noise and keep waiting.
|
|
99
|
+
if (isBotCommand(msg.text))
|
|
100
|
+
continue;
|
|
76
101
|
return {
|
|
77
102
|
status: "answered",
|
|
78
103
|
answer: msg.text,
|
|
@@ -81,7 +106,7 @@ export class TelegramChannel {
|
|
|
81
106
|
}
|
|
82
107
|
// Inline-button tap.
|
|
83
108
|
const cb = update.callback_query;
|
|
84
|
-
if (cb) {
|
|
109
|
+
if (cb && !isStale(cb.message?.message_id)) {
|
|
85
110
|
// Best-effort: clear the client's loading spinner.
|
|
86
111
|
try {
|
|
87
112
|
await this.api("answerCallbackQuery", { callback_query_id: cb.id });
|
package/package.json
CHANGED