(args: {
chatId: string | number;
sinceUpdateId: number;
intervalMs: number;
timeoutMs: number;
onSample: (sample: {
elapsedMs: number;
text: string | undefined;
message: TelegramMessage | undefined;
}) => Promise<void> | void;
})
| 209 | * pass it as the baseline for a follow-up `watchForNextReply` call. |
| 210 | */ |
| 211 | export async function watchForReply(args: { |
| 212 | chatId: string | number; |
| 213 | sinceUpdateId: number; |
| 214 | intervalMs: number; |
| 215 | timeoutMs: number; |
| 216 | onSample: (sample: { |
| 217 | elapsedMs: number; |
| 218 | text: string | undefined; |
| 219 | message: TelegramMessage | undefined; |
| 220 | }) => Promise<void> | void; |
| 221 | }): Promise<{ |
| 222 | finalText: string | undefined; |
| 223 | finalMessage: TelegramMessage | undefined; |
| 224 | reachedUpdateId: number; |
| 225 | }> { |
| 226 | const start = Date.now(); |
| 227 | let offset = args.sinceUpdateId + 1; |
| 228 | // Map from message_id → latest known TelegramMessage (tracks edits). |
| 229 | const botMessageMap = new Map<number, TelegramMessage>(); |
| 230 | let stable = 0; |
| 231 | let lastLen = -1; |
| 232 | // Track the highest update_id we have consumed so callers can use it as the |
| 233 | // next baseline without re-delivering already-confirmed updates. |
| 234 | let reachedUpdateId = args.sinceUpdateId; |
| 235 | |
| 236 | while (Date.now() - start < args.timeoutMs) { |
| 237 | const updates = await getUpdates(offset); |
| 238 | for (const u of updates) { |
| 239 | if (u.update_id >= offset) offset = u.update_id + 1; |
| 240 | if (u.update_id > reachedUpdateId) reachedUpdateId = u.update_id; |
| 241 | // Accept both new messages and edits. |
| 242 | const msg = u.message ?? u.edited_message; |
| 243 | if (!msg) continue; |
| 244 | if (String(msg.chat.id) !== String(args.chatId)) continue; |
| 245 | // Track the latest text for each bot message_id. |
| 246 | if (msg.from?.is_bot) { |
| 247 | botMessageMap.set(msg.message_id, msg); |
| 248 | } |
| 249 | } |
| 250 | // The "last" bot message is the one with the highest message_id. |
| 251 | let lastMessage: TelegramMessage | undefined; |
| 252 | for (const msg of botMessageMap.values()) { |
| 253 | if (!lastMessage || msg.message_id > lastMessage.message_id) { |
| 254 | lastMessage = msg; |
| 255 | } |
| 256 | } |
| 257 | const text = lastMessage?.text; |
| 258 | await args.onSample({ |
| 259 | elapsedMs: Date.now() - start, |
| 260 | text, |
| 261 | message: lastMessage, |
| 262 | }); |
| 263 | const len = text?.length ?? 0; |
| 264 | if (len === lastLen && len > 0) { |
| 265 | stable++; |
| 266 | if (stable >= 3) break; |
| 267 | } else { |
| 268 | stable = 0; |
nothing calls this directly
no test coverage detected