• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

popstas / telegram-functions-bot / 18804794430

25 Oct 2025 03:10PM UTC coverage: 77.544% (+0.3%) from 77.275%
18804794430

push

github

web-flow
test: Speed up slow Jest suites by reusing module state (#166)

1320 of 1988 branches covered (66.4%)

Branch coverage included in aggregate %.

10 of 13 new or added lines in 2 files covered. (76.92%)

36 existing lines in 7 files now uncovered.

2468 of 2897 relevant lines covered (85.19%)

5.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.52
/src/httpHandlers.ts
1
import type express from "express";
2
import { Context } from "telegraf";
3
import { Message } from "telegraf/types";
4
import { useConfig } from "./config.ts";
5
import { log, stringToId } from "./helpers.ts";
6
import { requestGptAnswer } from "./helpers/gpt/llm.ts";
7
import { resolveChatTools } from "./helpers/gpt/tools.ts";
8
import type { ThreadStateType, ConfigChatType } from "./types.ts";
9
import { readFileSync } from "fs";
10
import { addToHistory } from "./helpers/history.ts";
11

12
const HTTP_LOG_PATH = "data/http.log";
17✔
13

14
const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
17✔
15
const version = pkg.version;
17✔
16

17
export async function agentGetHandler(req: express.Request, res: express.Response) {
18
  const { agentName } = req.params;
1✔
19

20
  try {
1✔
21
    res.json({
1✔
22
      name: agentName,
23
      version,
24
      status: "online",
25
      timestamp: new Date().toISOString(),
26
    });
27
  } catch (error) {
28
    console.error("Error in agentGetHandler:", error);
×
29
    res.status(500).json({ error: "Internal server error" });
×
30
  }
31
}
32

33
function checkAuth(chatConfig?: ConfigChatType, token?: string) {
34
  const globalToken = useConfig().http?.http_token;
8✔
35
  const chatToken = chatConfig?.http_token;
8✔
36
  const requestToken = token?.split(" ")[1];
8✔
37
  const isAccess =
38
    (chatToken && requestToken === chatToken) || (globalToken && requestToken === globalToken);
8✔
39
  return isAccess;
8✔
40
}
41

42
export async function agentPostHandler(req: express.Request, res: express.Response) {
43
  const { agentName } = req.params;
5✔
44
  const { text, webhook, chat_id: chatIdFromBody } = req.body || {};
5!
45
  const token = req.headers["authorization"];
5✔
46
  const agentConfig = useConfig().chats.find((c) => c.agent_name === agentName);
5✔
47
  if (!checkAuth(agentConfig, token)) {
5✔
48
    log({
1✔
49
      msg: "Unauthorized",
50
      logLevel: "warn",
51
      logPath: HTTP_LOG_PATH,
52
    });
53
    return res.status(401).send("Unauthorized");
1✔
54
  }
55
  if (!text) {
4✔
56
    log({
1✔
57
      msg: "Message text is required.",
58
      logLevel: "warn",
59
      logPath: HTTP_LOG_PATH,
60
    });
61
    return res.status(400).send("Message text is required.");
1✔
62
  }
63
  if (!agentConfig) {
3✔
64
    log({
1✔
65
      msg: "Wrong agent_name",
66
      logLevel: "warn",
67
      logPath: HTTP_LOG_PATH,
68
    });
69
    return res.status(400).send("Wrong agent_name");
1✔
70
  }
71
  const forwardedFor = req.headers["x-forwarded-for"];
2✔
72
  const ipAddress = Array.isArray(forwardedFor)
2!
73
    ? forwardedFor[0]
74
    : forwardedFor?.split(",")[0]?.trim() || req.ip || req.socket?.remoteAddress || "";
8✔
75
  const defaultChatId =
76
    Number.parseInt(`444${stringToId(agentName)}`, 10) + stringToId(ipAddress || "");
2✔
77
  const bodyChatId = (() => {
2✔
78
    if (chatIdFromBody === undefined || chatIdFromBody === null) {
2!
79
      return undefined;
2✔
80
    }
81
    if (typeof chatIdFromBody === "number") {
×
82
      return Number.isFinite(chatIdFromBody) ? Math.trunc(chatIdFromBody) : undefined;
×
83
    }
84
    if (typeof chatIdFromBody === "string") {
×
85
      const trimmedChatId = chatIdFromBody.trim();
×
86
      if (!trimmedChatId) {
×
87
        return undefined;
×
88
      }
89
      return /^\d+$/.test(trimmedChatId)
×
90
        ? Number.parseInt(trimmedChatId, 10)
91
        : stringToId(trimmedChatId);
92
    }
93
    return stringToId(String(chatIdFromBody));
×
94
  })();
95
  const configuredChatId =
96
    typeof agentConfig.id === "number" && Number.isFinite(agentConfig.id)
2!
97
      ? agentConfig.id
98
      : undefined;
99
  const chatId = bodyChatId ?? configuredChatId ?? defaultChatId;
2✔
100
  const msg = {
2✔
101
    chat: { id: chatId, type: "private" as const },
102
    text,
103
    message_id: Date.now(),
104
    date: Math.floor(Date.now() / 1000),
105
    from: { id: 0, is_bot: false, first_name: "http" },
106
  } as Message.TextMessage;
107
  const answerId = msg.message_id?.toString() || "";
2!
108
  log({
2✔
109
    msg: msg.text,
110
    chatId,
111
    answerId,
112
    chatTitle: "http",
113
    username: "http",
114
    role: "user",
115
    logPath: HTTP_LOG_PATH,
116
  });
117

118
  // Add user message to history before requesting answer
119
  addToHistory(msg, agentConfig);
2✔
120

121
  const resObj = await requestGptAnswer(msg, agentConfig, {
2✔
122
    noSendTelegram: true,
123
  } as unknown as Context);
124
  const answer = resObj?.content || "";
2!
125
  log({
2✔
126
    msg: answer,
127
    chatId,
128
    answerId,
129
    chatTitle: "http: " + agentName,
130
    username: "http",
131
    role: "assistant",
132
    logPath: HTTP_LOG_PATH,
133
  });
134
  res.contentType("text/plain; charset=utf-8");
2✔
135
  res.end(answer);
2✔
136
  if (webhook) {
2✔
137
    try {
1✔
138
      await fetch(webhook, {
1✔
139
        method: "POST",
140
        headers: { "Content-Type": "application/json" },
141
        body: JSON.stringify({ answer }),
142
      });
143
    } catch {
144
      // ignore webhook errors
145
    }
146
  }
147
}
148

149
export async function toolPostHandler(req: express.Request, res: express.Response) {
150
  const { agentName, toolName } = req.params;
3✔
151
  const args = req.body || {};
3!
152
  const token = req.headers["authorization"];
3✔
153
  const agentConfig = useConfig().chats.find((c) => c.agent_name === agentName);
3✔
154
  if (!checkAuth(agentConfig, token)) {
3✔
155
    log({
1✔
156
      msg: "Unauthorized",
157
      logLevel: "warn",
158
      logPath: HTTP_LOG_PATH,
159
    });
160
    return res.status(401).send("Unauthorized");
1✔
161
  }
162
  if (!agentConfig) {
2!
UNCOV
163
    log({
×
164
      msg: "Wrong agent_name",
165
      logLevel: "warn",
166
      logPath: HTTP_LOG_PATH,
167
    });
UNCOV
168
    return res.status(400).send("Wrong agent_name");
×
169
  }
170
  const chatId = agentConfig.id || parseInt("666" + Math.floor(100000 + Math.random() * 900000));
2✔
171
  const thread = {
2✔
172
    id: chatId,
173
    msgs: [],
174
    messages: [],
175
    completionParams: agentConfig.completionParams,
176
  } as ThreadStateType;
177
  const chatTools = await resolveChatTools(
2✔
178
    {
179
      chat: { id: chatId, type: "private" as const },
180
      text: "",
181
      message_id: Date.now(),
182
      date: Math.floor(Date.now() / 1000),
183
      from: { id: 0, is_bot: false, first_name: "http" },
184
    } as Message.TextMessage,
185
    agentConfig,
186
  );
187
  const chatTool = chatTools.find((f) => f.name === toolName);
2✔
188
  if (!chatTool) {
2!
UNCOV
189
    log({
×
190
      msg: `Wrong tool_name: ${toolName}`,
191
      logLevel: "warn",
192
      logPath: HTTP_LOG_PATH,
193
    });
UNCOV
194
    return res.status(400).send(`Wrong tool_name: ${toolName}`);
×
195
  }
196
  const fn = chatTool.module.call(agentConfig, thread).functions.get(toolName);
2✔
197
  const argsStr = typeof args === "string" ? args : JSON.stringify(args || {});
2!
198
  log({
2✔
199
    msg: `${toolName}: ${argsStr}`,
200
    chatId,
201
    chatTitle: "http",
202
    username: "http",
203
    role: "user",
204
    logPath: HTTP_LOG_PATH,
205
  });
206
  let result;
207
  try {
2✔
208
    let jsonAnswer;
209
    result = await fn(argsStr);
2✔
210
    const toolAnswer = JSON.parse(result.content);
2✔
211
    const arr = Array.isArray(toolAnswer) ? toolAnswer : toolAnswer.content;
2!
212
    if (arr && arr[0]) {
2!
213
      jsonAnswer = JSON.parse(arr[0].text);
2✔
214
    }
215
    log({
2✔
216
      msg: JSON.stringify(jsonAnswer),
217
      chatId,
218
      chatTitle: "http",
219
      username: "http",
220
      role: "assistant",
221
      logPath: HTTP_LOG_PATH,
222
    });
223
    const response = jsonAnswer || { text: result.content };
2!
224
    log({
2✔
225
      msg: `Sending response: ${JSON.stringify(response).substring(0, 200)}`,
226
      chatId,
227
      chatTitle: "http",
228
      username: "http",
229
      role: "assistant",
230
      logPath: HTTP_LOG_PATH,
231
    });
232
    res.json(response);
2✔
233
  } catch (err) {
UNCOV
234
    const msg = err instanceof Error ? err.message : String(err);
×
UNCOV
235
    log({
×
236
      msg: `toolPostHandler error: ${msg}, result: ${JSON.stringify(result)}`,
237
      logLevel: "error",
238
      logPath: HTTP_LOG_PATH,
239
    });
UNCOV
240
    res.status(500).send(msg);
×
241
  }
242
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc