@mcp_factory/telegram-mcp-server 1.0.0

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 ADDED
@@ -0,0 +1,81 @@
1
+ # @mcp_factory/telegram-mcp-server
2
+
3
+ MCP server for Telegram with built-in MTProto authentication.
4
+
5
+ Read chats, send messages, search across conversations, manage contacts, forward, pin, and delete messages - all through MCP.
6
+
7
+ ## Setup (one time)
8
+
9
+ ### 1. Get Telegram API credentials
10
+ Go to https://my.telegram.org/apps and create an app. Copy the `api_id` and `api_hash`.
11
+
12
+ ### 2. Run auth
13
+ ```bash
14
+ TG_API_ID=12345 TG_API_HASH=abc123 npx @mcp_factory/telegram-mcp-server auth
15
+ ```
16
+
17
+ A browser window opens where you:
18
+ 1. Enter your phone number
19
+ 2. Enter the OTP code from Telegram
20
+ 3. (If 2FA enabled) Enter your password
21
+
22
+ Session is saved to `~/.telegram-mcp/session.json`. You only do this once.
23
+
24
+ ## Usage
25
+
26
+ ```bash
27
+ TG_API_ID=12345 TG_API_HASH=abc123 npx @mcp_factory/telegram-mcp-server
28
+ ```
29
+
30
+ Or in Claude Desktop `claude_desktop_config.json`:
31
+
32
+ ```json
33
+ {
34
+ "mcpServers": {
35
+ "telegram": {
36
+ "command": "npx",
37
+ "args": ["-y", "@mcp_factory/telegram-mcp-server"],
38
+ "env": {
39
+ "TG_API_ID": "12345",
40
+ "TG_API_HASH": "abc123"
41
+ }
42
+ }
43
+ }
44
+ }
45
+ ```
46
+
47
+ On first run with no session, the browser opens automatically for login.
48
+
49
+ ## Available Tools (11)
50
+
51
+ | Tool | Description |
52
+ |------|-------------|
53
+ | `telegram_get_me` | Get connected user's profile |
54
+ | `telegram_list_chats` | List recent chats with unread counts |
55
+ | `telegram_get_messages` | Get messages from a chat |
56
+ | `telegram_send_message` | Send a message to any chat |
57
+ | `telegram_search_messages` | Search messages (in chat or globally) |
58
+ | `telegram_get_contacts` | Get contact list |
59
+ | `telegram_get_chat_info` | Get chat/group/channel details |
60
+ | `telegram_mark_read` | Mark chat as read |
61
+ | `telegram_forward_message` | Forward messages between chats |
62
+ | `telegram_delete_messages` | Delete messages |
63
+ | `telegram_pin_message` | Pin a message in chat |
64
+
65
+ ## Auth Types
66
+
67
+ | Context | How it works |
68
+ |---------|-------------|
69
+ | **Standalone** | Built-in web UI: phone + OTP + optional 2FA |
70
+ | **MCP Bind / Wexa** | Backend handles MTProto session via `/telegram/sendCode` and `/telegram/verifyCode` APIs |
71
+
72
+ ## Security
73
+
74
+ - Session stored locally at `~/.telegram-mcp/session.json` (file permissions: 600)
75
+ - No data sent to third-party servers
76
+ - Session = full account access - treat it like a password
77
+ - Only runs on `127.0.0.1` during auth (not exposed to network)
78
+
79
+ ## License
80
+
81
+ MIT
package/bin/cli.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import("../dist/index.js");
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Auth Server
3
+ *
4
+ * Spins up a local HTTP server with a web UI for Telegram OTP verification.
5
+ * Flow: Phone number → Send OTP → Verify code → (optional 2FA) → Session saved
6
+ */
7
+ export declare function startAuthServer(port?: number): Promise<{
8
+ port: number;
9
+ close: () => void;
10
+ }>;
11
+ /**
12
+ * Run auth flow: start server, open browser, wait for completion
13
+ */
14
+ export declare function runAuthFlow(): Promise<void>;
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Auth Server
3
+ *
4
+ * Spins up a local HTTP server with a web UI for Telegram OTP verification.
5
+ * Flow: Phone number → Send OTP → Verify code → (optional 2FA) → Session saved
6
+ */
7
+ import { createServer } from "http";
8
+ import { readFileSync } from "fs";
9
+ import { join, dirname } from "path";
10
+ import { fileURLToPath } from "url";
11
+ import { exec } from "child_process";
12
+ import { TelegramClient } from "telegram";
13
+ import { StringSession } from "telegram/sessions/index.js";
14
+ import { saveSession } from "./session-manager.js";
15
+ const __dirname = dirname(fileURLToPath(import.meta.url));
16
+ // GramJS client state during auth
17
+ let client = null;
18
+ let pendingPhoneCodeHash = null;
19
+ function getApiCredentials() {
20
+ const apiId = parseInt(process.env.TG_API_ID || process.env.TELEGRAM_API_ID || "0", 10);
21
+ const apiHash = process.env.TG_API_HASH || process.env.TELEGRAM_API_HASH || "";
22
+ if (!apiId || !apiHash) {
23
+ throw new Error("Missing Telegram API credentials.\n" +
24
+ "Set TG_API_ID and TG_API_HASH env vars.\n" +
25
+ "Get them at: https://my.telegram.org/apps");
26
+ }
27
+ return { apiId, apiHash };
28
+ }
29
+ async function handleAPI(req, res, body) {
30
+ const url = new URL(req.url || "/", "http://localhost");
31
+ const path = url.pathname;
32
+ res.setHeader("Content-Type", "application/json");
33
+ try {
34
+ if (path === "/api/send-code" && req.method === "POST") {
35
+ const { phone } = JSON.parse(body);
36
+ if (!phone) {
37
+ res.writeHead(400);
38
+ res.end(JSON.stringify({ error: "Phone number required" }));
39
+ return;
40
+ }
41
+ const { apiId, apiHash } = getApiCredentials();
42
+ client = new TelegramClient(new StringSession(""), apiId, apiHash, {
43
+ connectionRetries: 5,
44
+ });
45
+ await client.connect();
46
+ const result = await client.sendCode({ apiId, apiHash }, phone);
47
+ pendingPhoneCodeHash = result.phoneCodeHash;
48
+ res.writeHead(200);
49
+ res.end(JSON.stringify({ success: true, phoneCodeHash: result.phoneCodeHash }));
50
+ }
51
+ else if (path === "/api/verify-code" && req.method === "POST") {
52
+ const { phone, code, phoneCodeHash } = JSON.parse(body);
53
+ if (!client || !phone || !code) {
54
+ res.writeHead(400);
55
+ res.end(JSON.stringify({ error: "Missing phone, code, or session expired. Restart the flow." }));
56
+ return;
57
+ }
58
+ try {
59
+ await client.invoke(new (await import("telegram/tl/index.js")).Api.auth.SignIn({
60
+ phoneNumber: phone,
61
+ phoneCodeHash: phoneCodeHash || pendingPhoneCodeHash || "",
62
+ phoneCode: code,
63
+ }));
64
+ // Success — save session
65
+ const sessionStr = client.session.save();
66
+ const me = await client.getMe();
67
+ saveSession({
68
+ stringSession: sessionStr,
69
+ userId: me?.id?.toString(),
70
+ username: me?.username || undefined,
71
+ firstName: me?.firstName || undefined,
72
+ connectedAt: new Date().toISOString(),
73
+ });
74
+ res.writeHead(200);
75
+ res.end(JSON.stringify({ success: true, username: me?.username }));
76
+ }
77
+ catch (err) {
78
+ // Check if 2FA is needed
79
+ if (err.message?.includes("SESSION_PASSWORD_NEEDED")) {
80
+ res.writeHead(400);
81
+ res.end(JSON.stringify({ error: "2FA password required", need2FA: true }));
82
+ return;
83
+ }
84
+ throw err;
85
+ }
86
+ }
87
+ else if (path === "/api/verify-2fa" && req.method === "POST") {
88
+ const { password } = JSON.parse(body);
89
+ if (!client || !password) {
90
+ res.writeHead(400);
91
+ res.end(JSON.stringify({ error: "Missing password or session expired" }));
92
+ return;
93
+ }
94
+ // Use GramJS built-in 2FA sign-in
95
+ await client.signInWithPassword({ apiId: getApiCredentials().apiId, apiHash: getApiCredentials().apiHash }, { password: () => Promise.resolve(password), onError: (e) => { throw e; } });
96
+ const sessionStr = client.session.save();
97
+ const me = await client.getMe();
98
+ saveSession({
99
+ stringSession: sessionStr,
100
+ userId: me?.id?.toString(),
101
+ username: me?.username || undefined,
102
+ firstName: me?.firstName || undefined,
103
+ connectedAt: new Date().toISOString(),
104
+ });
105
+ res.writeHead(200);
106
+ res.end(JSON.stringify({ success: true, username: me?.username }));
107
+ }
108
+ else {
109
+ res.writeHead(404);
110
+ res.end(JSON.stringify({ error: "Not found" }));
111
+ }
112
+ }
113
+ catch (err) {
114
+ console.error("Auth API error:", err.message);
115
+ res.writeHead(500);
116
+ res.end(JSON.stringify({ error: err.message || "Internal error" }));
117
+ }
118
+ }
119
+ function serveHTML(_req, res) {
120
+ // Try to find auth.html - check multiple locations
121
+ const candidates = [
122
+ join(__dirname, "..", "public", "auth.html"),
123
+ join(__dirname, "..", "..", "public", "auth.html"),
124
+ ];
125
+ for (const path of candidates) {
126
+ try {
127
+ const html = readFileSync(path, "utf-8");
128
+ res.writeHead(200, { "Content-Type": "text/html" });
129
+ res.end(html);
130
+ return;
131
+ }
132
+ catch {
133
+ continue;
134
+ }
135
+ }
136
+ // Fallback: inline minimal HTML
137
+ res.writeHead(200, { "Content-Type": "text/html" });
138
+ res.end(`<html><body><h2>Auth page not found. Check installation.</h2></body></html>`);
139
+ }
140
+ export function startAuthServer(port = 0) {
141
+ return new Promise((resolve, reject) => {
142
+ const server = createServer((req, res) => {
143
+ const url = new URL(req.url || "/", "http://localhost");
144
+ if (url.pathname.startsWith("/api/")) {
145
+ let body = "";
146
+ req.on("data", (chunk) => (body += chunk));
147
+ req.on("end", () => handleAPI(req, res, body));
148
+ }
149
+ else {
150
+ serveHTML(req, res);
151
+ }
152
+ });
153
+ server.listen(port, "127.0.0.1", () => {
154
+ const addr = server.address();
155
+ const actualPort = addr.port;
156
+ resolve({
157
+ port: actualPort,
158
+ close: () => {
159
+ server.close();
160
+ if (client) {
161
+ client.disconnect().catch(() => { });
162
+ client = null;
163
+ }
164
+ },
165
+ });
166
+ });
167
+ server.on("error", reject);
168
+ });
169
+ }
170
+ /**
171
+ * Run auth flow: start server, open browser, wait for completion
172
+ */
173
+ export async function runAuthFlow() {
174
+ const { apiId, apiHash } = getApiCredentials();
175
+ console.error(`Telegram API ID: ${apiId}`);
176
+ const { port, close } = await startAuthServer(0);
177
+ const url = `http://localhost:${port}`;
178
+ console.error(`\nTelegram auth server running at: ${url}`);
179
+ console.error("Opening browser...\n");
180
+ openBrowser(url);
181
+ // Wait for session to be saved (poll every second, timeout 3 min)
182
+ const { hasSession } = await import("./session-manager.js");
183
+ const start = Date.now();
184
+ while (!hasSession() && Date.now() - start < 180_000) {
185
+ await new Promise((r) => setTimeout(r, 1000));
186
+ }
187
+ close();
188
+ if (hasSession()) {
189
+ console.error("Telegram connected successfully!");
190
+ }
191
+ else {
192
+ throw new Error("Auth flow timed out. Please try again.");
193
+ }
194
+ }
195
+ function openBrowser(url) {
196
+ const cmd = process.platform === "darwin"
197
+ ? `open "${url}"`
198
+ : process.platform === "win32"
199
+ ? `start "${url}"`
200
+ : `xdg-open "${url}"`;
201
+ exec(cmd, (err) => {
202
+ if (err) {
203
+ console.error(`Could not open browser. Visit: ${url}`);
204
+ }
205
+ });
206
+ }
207
+ // If run directly: start auth flow
208
+ if (process.argv[1]?.includes("auth-server")) {
209
+ runAuthFlow().catch((e) => {
210
+ console.error("Auth failed:", e.message);
211
+ process.exit(1);
212
+ });
213
+ }
214
+ //# sourceMappingURL=auth-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-server.js","sourceRoot":"","sources":["../src/auth-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Telegram MCP Server
4
+ *
5
+ * MCP server for Telegram via MTProto (GramJS).
6
+ * Supports: read chats, send messages, search, contacts, forward, pin, delete.
7
+ *
8
+ * Auth: Built-in OTP flow with local web UI.
9
+ * - First run opens browser for phone + OTP verification
10
+ * - Session cached at ~/.telegram-mcp/session.json
11
+ * - Subsequent runs use cached session (no login needed)
12
+ *
13
+ * Usage:
14
+ * TG_API_ID=xxx TG_API_HASH=yyy npx @mcp_factory/telegram-mcp-server
15
+ *
16
+ * Auth only:
17
+ * TG_API_ID=xxx TG_API_HASH=yyy npx @mcp_factory/telegram-mcp-server auth
18
+ */
19
+ declare function main(): Promise<void>;
20
+ export { main };
package/dist/index.js ADDED
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Telegram MCP Server
4
+ *
5
+ * MCP server for Telegram via MTProto (GramJS).
6
+ * Supports: read chats, send messages, search, contacts, forward, pin, delete.
7
+ *
8
+ * Auth: Built-in OTP flow with local web UI.
9
+ * - First run opens browser for phone + OTP verification
10
+ * - Session cached at ~/.telegram-mcp/session.json
11
+ * - Subsequent runs use cached session (no login needed)
12
+ *
13
+ * Usage:
14
+ * TG_API_ID=xxx TG_API_HASH=yyy npx @mcp_factory/telegram-mcp-server
15
+ *
16
+ * Auth only:
17
+ * TG_API_ID=xxx TG_API_HASH=yyy npx @mcp_factory/telegram-mcp-server auth
18
+ */
19
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
20
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
21
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
22
+ import { getTools, handleToolCall } from "./tools.js";
23
+ import { hasSession } from "./session-manager.js";
24
+ import { runAuthFlow } from "./auth-server.js";
25
+ const SERVER_NAME = "telegram-mcp-server";
26
+ const SERVER_VERSION = "1.0.0";
27
+ async function main() {
28
+ const args = process.argv.slice(2);
29
+ // `auth` subcommand: run auth flow only
30
+ if (args[0] === "auth") {
31
+ await runAuthFlow();
32
+ process.exit(0);
33
+ }
34
+ // Check for session, run auth if needed
35
+ if (!hasSession()) {
36
+ console.error("No Telegram session found. Starting auth flow...");
37
+ await runAuthFlow();
38
+ }
39
+ // Create MCP server
40
+ const server = new Server({ name: SERVER_NAME, version: SERVER_VERSION }, { capabilities: { tools: {} } });
41
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
42
+ return { tools: getTools() };
43
+ });
44
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
45
+ const { name, arguments: toolArgs } = request.params;
46
+ try {
47
+ const result = await handleToolCall(name, toolArgs || {});
48
+ return {
49
+ content: [
50
+ {
51
+ type: "text",
52
+ text: typeof result === "string" ? result : JSON.stringify(result, null, 2),
53
+ },
54
+ ],
55
+ };
56
+ }
57
+ catch (error) {
58
+ const message = error instanceof Error ? error.message : String(error);
59
+ return {
60
+ content: [{ type: "text", text: `Error: ${message}` }],
61
+ isError: true,
62
+ };
63
+ }
64
+ });
65
+ const transport = new StdioServerTransport();
66
+ await server.connect(transport);
67
+ console.error(`${SERVER_NAME} v${SERVER_VERSION} running on stdio`);
68
+ }
69
+ main().catch((error) => {
70
+ console.error("Fatal error:", error);
71
+ process.exit(1);
72
+ });
73
+ export { main };
74
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Session Manager
3
+ *
4
+ * Stores and retrieves Telegram MTProto sessions securely.
5
+ * Sessions are saved at ~/.telegram-mcp/session.json (chmod 600).
6
+ */
7
+ export interface SessionData {
8
+ stringSession: string;
9
+ userId?: string;
10
+ username?: string;
11
+ firstName?: string;
12
+ connectedAt?: string;
13
+ }
14
+ export declare function loadSession(): SessionData | null;
15
+ export declare function saveSession(data: SessionData): void;
16
+ export declare function deleteSession(): void;
17
+ export declare function hasSession(): boolean;
18
+ export declare function getSessionDir(): string;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Session Manager
3
+ *
4
+ * Stores and retrieves Telegram MTProto sessions securely.
5
+ * Sessions are saved at ~/.telegram-mcp/session.json (chmod 600).
6
+ */
7
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
8
+ import { homedir } from "os";
9
+ import { join } from "path";
10
+ const SESSION_DIR = join(homedir(), ".telegram-mcp");
11
+ const SESSION_FILE = join(SESSION_DIR, "session.json");
12
+ export function loadSession() {
13
+ try {
14
+ if (!existsSync(SESSION_FILE))
15
+ return null;
16
+ const raw = readFileSync(SESSION_FILE, "utf-8");
17
+ const data = JSON.parse(raw);
18
+ if (data.stringSession)
19
+ return data;
20
+ return null;
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ }
26
+ export function saveSession(data) {
27
+ if (!existsSync(SESSION_DIR)) {
28
+ mkdirSync(SESSION_DIR, { recursive: true });
29
+ }
30
+ writeFileSync(SESSION_FILE, JSON.stringify(data, null, 2), { mode: 0o600 });
31
+ }
32
+ export function deleteSession() {
33
+ if (existsSync(SESSION_FILE)) {
34
+ writeFileSync(SESSION_FILE, "{}", { mode: 0o600 });
35
+ }
36
+ }
37
+ export function hasSession() {
38
+ const session = loadSession();
39
+ return session !== null && session.stringSession.length > 0;
40
+ }
41
+ export function getSessionDir() {
42
+ return SESSION_DIR;
43
+ }
44
+ //# sourceMappingURL=session-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-manager.js","sourceRoot":"","sources":["../src/session-manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACxE,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,eAAe,CAAC,CAAC;AACrD,MAAM,YAAY,GAAG,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;AAUvD,MAAM,UAAU,WAAW;IACzB,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC;YAAE,OAAO,IAAI,CAAC;QAC3C,MAAM,GAAG,GAAG,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QAChD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,IAAI,CAAC,aAAa;YAAE,OAAO,IAAmB,CAAC;QACnD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAiB;IAC3C,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7B,SAAS,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,CAAC;IACD,aAAa,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,IAAI,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC7B,aAAa,CAAC,YAAY,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACrD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;IAC9B,OAAO,OAAO,KAAK,IAAI,IAAI,OAAO,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,OAAO,WAAW,CAAC;AACrB,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Telegram Client Wrapper
3
+ *
4
+ * Wraps GramJS TelegramClient for use by MCP tools.
5
+ * Handles session loading, connection, and provides high-level methods.
6
+ */
7
+ import { TelegramClient } from "telegram";
8
+ export declare function getClient(): Promise<TelegramClient>;
9
+ export declare function getMe(): Promise<Record<string, unknown>>;
10
+ export declare function getDialogs(limit?: number): Promise<unknown[]>;
11
+ export declare function getMessages(chatId: string, limit?: number): Promise<unknown[]>;
12
+ export declare function sendMessage(chatId: string, text: string): Promise<unknown>;
13
+ export declare function searchMessages(chatId: string | undefined, query: string, limit?: number): Promise<unknown[]>;
14
+ export declare function getContacts(): Promise<unknown[]>;
15
+ export declare function getChatInfo(chatId: string): Promise<unknown>;
16
+ export declare function markAsRead(chatId: string): Promise<void>;
17
+ export declare function forwardMessage(fromChatId: string, toChatId: string, messageIds: number[]): Promise<unknown>;
18
+ export declare function deleteMessages(chatId: string, messageIds: number[]): Promise<void>;
19
+ export declare function pinMessage(chatId: string, messageId: number): Promise<void>;
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Telegram Client Wrapper
3
+ *
4
+ * Wraps GramJS TelegramClient for use by MCP tools.
5
+ * Handles session loading, connection, and provides high-level methods.
6
+ */
7
+ import { TelegramClient } from "telegram";
8
+ import { StringSession } from "telegram/sessions/index.js";
9
+ import { Api } from "telegram/tl/index.js";
10
+ import { loadSession } from "./session-manager.js";
11
+ let _client = null;
12
+ export async function getClient() {
13
+ if (_client?.connected)
14
+ return _client;
15
+ const apiId = parseInt(process.env.TG_API_ID || process.env.TELEGRAM_API_ID || "0", 10);
16
+ const apiHash = process.env.TG_API_HASH || process.env.TELEGRAM_API_HASH || "";
17
+ if (!apiId || !apiHash) {
18
+ throw new Error("Set TG_API_ID and TG_API_HASH. Get them at https://my.telegram.org/apps");
19
+ }
20
+ const session = loadSession();
21
+ if (!session?.stringSession) {
22
+ throw new Error("No Telegram session found. Run the auth flow first:\n" +
23
+ " npx @mcp_factory/telegram-mcp-server auth\n" +
24
+ "Or set TG_API_ID + TG_API_HASH and restart.");
25
+ }
26
+ _client = new TelegramClient(new StringSession(session.stringSession), apiId, apiHash, {
27
+ connectionRetries: 5,
28
+ });
29
+ await _client.connect();
30
+ return _client;
31
+ }
32
+ // --- High-level helpers ---
33
+ export async function getMe() {
34
+ const client = await getClient();
35
+ const me = await client.getMe();
36
+ return {
37
+ id: me?.id?.toString(),
38
+ username: me?.username,
39
+ firstName: me?.firstName,
40
+ lastName: me?.lastName,
41
+ phone: me?.phone,
42
+ };
43
+ }
44
+ export async function getDialogs(limit = 20) {
45
+ const client = await getClient();
46
+ const dialogs = await client.getDialogs({ limit });
47
+ return dialogs.map((d) => ({
48
+ id: d.id?.toString(),
49
+ name: d.name || d.title,
50
+ unreadCount: d.unreadCount,
51
+ isUser: d.isUser,
52
+ isGroup: d.isGroup,
53
+ isChannel: d.isChannel,
54
+ lastMessage: d.message?.message?.substring(0, 100),
55
+ date: d.message?.date,
56
+ }));
57
+ }
58
+ export async function getMessages(chatId, limit = 20) {
59
+ const client = await getClient();
60
+ const entity = await client.getEntity(chatId);
61
+ const messages = await client.getMessages(entity, { limit });
62
+ return messages.map((m) => ({
63
+ id: m.id,
64
+ text: m.message,
65
+ date: m.date,
66
+ senderId: m.senderId?.toString(),
67
+ replyTo: m.replyTo?.replyToMsgId,
68
+ out: m.out,
69
+ }));
70
+ }
71
+ export async function sendMessage(chatId, text) {
72
+ const client = await getClient();
73
+ const entity = await client.getEntity(chatId);
74
+ const result = await client.sendMessage(entity, { message: text });
75
+ return {
76
+ id: result.id,
77
+ text: result.message,
78
+ date: result.date,
79
+ chatId: chatId,
80
+ };
81
+ }
82
+ export async function searchMessages(chatId, query, limit = 20) {
83
+ const client = await getClient();
84
+ if (chatId) {
85
+ const entity = await client.getEntity(chatId);
86
+ const messages = await client.getMessages(entity, { search: query, limit });
87
+ return messages.map((m) => ({
88
+ id: m.id,
89
+ text: m.message,
90
+ date: m.date,
91
+ chatId: chatId,
92
+ senderId: m.senderId?.toString(),
93
+ }));
94
+ }
95
+ // Global search
96
+ const result = await client.invoke(new Api.messages.SearchGlobal({
97
+ q: query,
98
+ filter: new Api.InputMessagesFilterEmpty(),
99
+ minDate: 0,
100
+ maxDate: 0,
101
+ offsetRate: 0,
102
+ offsetPeer: new Api.InputPeerEmpty(),
103
+ offsetId: 0,
104
+ limit,
105
+ }));
106
+ const messages = result.messages || [];
107
+ return messages.map((m) => ({
108
+ id: m.id,
109
+ text: m.message,
110
+ date: m.date,
111
+ peerId: m.peerId?.toString(),
112
+ }));
113
+ }
114
+ export async function getContacts() {
115
+ const client = await getClient();
116
+ const result = await client.invoke(new Api.contacts.GetContacts({ hash: 0 }));
117
+ const users = result.users || [];
118
+ return users.map((u) => ({
119
+ id: u.id?.toString(),
120
+ firstName: u.firstName,
121
+ lastName: u.lastName,
122
+ username: u.username,
123
+ phone: u.phone,
124
+ }));
125
+ }
126
+ export async function getChatInfo(chatId) {
127
+ const client = await getClient();
128
+ const entity = await client.getEntity(chatId);
129
+ return {
130
+ id: entity.id?.toString(),
131
+ title: entity.title || entity.firstName,
132
+ username: entity.username,
133
+ participantsCount: entity.participantsCount,
134
+ about: entity.about,
135
+ type: entity.className,
136
+ };
137
+ }
138
+ export async function markAsRead(chatId) {
139
+ const client = await getClient();
140
+ const entity = await client.getEntity(chatId);
141
+ await client.markAsRead(entity);
142
+ }
143
+ export async function forwardMessage(fromChatId, toChatId, messageIds) {
144
+ const client = await getClient();
145
+ const fromEntity = await client.getEntity(fromChatId);
146
+ const toEntity = await client.getEntity(toChatId);
147
+ const result = await client.forwardMessages(toEntity, {
148
+ messages: messageIds,
149
+ fromPeer: fromEntity,
150
+ });
151
+ return { forwarded: result.length };
152
+ }
153
+ export async function deleteMessages(chatId, messageIds) {
154
+ const client = await getClient();
155
+ const entity = await client.getEntity(chatId);
156
+ await client.deleteMessages(entity, messageIds, { revoke: true });
157
+ }
158
+ export async function pinMessage(chatId, messageId) {
159
+ const client = await getClient();
160
+ const entity = await client.getEntity(chatId);
161
+ await client.pinMessage(entity, messageId);
162
+ }
163
+ //# sourceMappingURL=telegram-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telegram-client.js","sourceRoot":"","sources":["../src/telegram-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * MCP Tool definitions and handlers for Telegram
3
+ */
4
+ import type { Tool } from "@modelcontextprotocol/sdk/types.js";
5
+ export declare function getTools(): Tool[];
6
+ export declare function handleToolCall(name: string, args: Record<string, unknown>): Promise<unknown>;
package/dist/tools.js ADDED
@@ -0,0 +1,163 @@
1
+ import * as tg from "./telegram-client.js";
2
+ const tools = [
3
+ {
4
+ name: "telegram_get_me",
5
+ description: "Get the connected Telegram user's profile info",
6
+ inputSchema: { type: "object", properties: {}, required: [] },
7
+ },
8
+ {
9
+ name: "telegram_list_chats",
10
+ description: "List recent Telegram chats/dialogs (DMs, groups, channels) with unread counts",
11
+ inputSchema: {
12
+ type: "object",
13
+ properties: {
14
+ limit: { type: "number", default: 20, description: "Max chats to return (default: 20)" },
15
+ },
16
+ required: [],
17
+ },
18
+ },
19
+ {
20
+ name: "telegram_get_messages",
21
+ description: "Get recent messages from a specific chat",
22
+ inputSchema: {
23
+ type: "object",
24
+ properties: {
25
+ chat_id: { type: "string", description: "Chat ID or @username" },
26
+ limit: { type: "number", default: 20, description: "Max messages (default: 20)" },
27
+ },
28
+ required: ["chat_id"],
29
+ },
30
+ },
31
+ {
32
+ name: "telegram_send_message",
33
+ description: "Send a text message to a Telegram chat, group, or user",
34
+ inputSchema: {
35
+ type: "object",
36
+ properties: {
37
+ chat_id: { type: "string", description: "Chat ID or @username" },
38
+ text: { type: "string", description: "Message text (supports Telegram markdown)" },
39
+ },
40
+ required: ["chat_id", "text"],
41
+ },
42
+ },
43
+ {
44
+ name: "telegram_search_messages",
45
+ description: "Search messages in a specific chat or globally across all chats",
46
+ inputSchema: {
47
+ type: "object",
48
+ properties: {
49
+ query: { type: "string", description: "Search query" },
50
+ chat_id: { type: "string", description: "Chat ID to search in (omit for global search)" },
51
+ limit: { type: "number", default: 20, description: "Max results (default: 20)" },
52
+ },
53
+ required: ["query"],
54
+ },
55
+ },
56
+ {
57
+ name: "telegram_get_contacts",
58
+ description: "Get the user's Telegram contact list",
59
+ inputSchema: { type: "object", properties: {}, required: [] },
60
+ },
61
+ {
62
+ name: "telegram_get_chat_info",
63
+ description: "Get detailed info about a chat, group, or channel",
64
+ inputSchema: {
65
+ type: "object",
66
+ properties: {
67
+ chat_id: { type: "string", description: "Chat ID or @username" },
68
+ },
69
+ required: ["chat_id"],
70
+ },
71
+ },
72
+ {
73
+ name: "telegram_mark_read",
74
+ description: "Mark all messages in a chat as read",
75
+ inputSchema: {
76
+ type: "object",
77
+ properties: {
78
+ chat_id: { type: "string", description: "Chat ID or @username" },
79
+ },
80
+ required: ["chat_id"],
81
+ },
82
+ },
83
+ {
84
+ name: "telegram_forward_message",
85
+ description: "Forward messages from one chat to another",
86
+ inputSchema: {
87
+ type: "object",
88
+ properties: {
89
+ from_chat_id: { type: "string", description: "Source chat ID" },
90
+ to_chat_id: { type: "string", description: "Destination chat ID" },
91
+ message_ids: {
92
+ type: "array",
93
+ items: { type: "number" },
94
+ description: "Array of message IDs to forward",
95
+ },
96
+ },
97
+ required: ["from_chat_id", "to_chat_id", "message_ids"],
98
+ },
99
+ },
100
+ {
101
+ name: "telegram_delete_messages",
102
+ description: "Delete messages from a chat (revokes for all participants)",
103
+ inputSchema: {
104
+ type: "object",
105
+ properties: {
106
+ chat_id: { type: "string", description: "Chat ID" },
107
+ message_ids: {
108
+ type: "array",
109
+ items: { type: "number" },
110
+ description: "Array of message IDs to delete",
111
+ },
112
+ },
113
+ required: ["chat_id", "message_ids"],
114
+ },
115
+ },
116
+ {
117
+ name: "telegram_pin_message",
118
+ description: "Pin a message in a chat",
119
+ inputSchema: {
120
+ type: "object",
121
+ properties: {
122
+ chat_id: { type: "string", description: "Chat ID" },
123
+ message_id: { type: "number", description: "Message ID to pin" },
124
+ },
125
+ required: ["chat_id", "message_id"],
126
+ },
127
+ },
128
+ ];
129
+ export function getTools() {
130
+ return tools;
131
+ }
132
+ export async function handleToolCall(name, args) {
133
+ switch (name) {
134
+ case "telegram_get_me":
135
+ return tg.getMe();
136
+ case "telegram_list_chats":
137
+ return tg.getDialogs(args.limit || 20);
138
+ case "telegram_get_messages":
139
+ return tg.getMessages(args.chat_id, args.limit || 20);
140
+ case "telegram_send_message":
141
+ return tg.sendMessage(args.chat_id, args.text);
142
+ case "telegram_search_messages":
143
+ return tg.searchMessages(args.chat_id, args.query, args.limit || 20);
144
+ case "telegram_get_contacts":
145
+ return tg.getContacts();
146
+ case "telegram_get_chat_info":
147
+ return tg.getChatInfo(args.chat_id);
148
+ case "telegram_mark_read":
149
+ await tg.markAsRead(args.chat_id);
150
+ return { success: true };
151
+ case "telegram_forward_message":
152
+ return tg.forwardMessage(args.from_chat_id, args.to_chat_id, args.message_ids);
153
+ case "telegram_delete_messages":
154
+ await tg.deleteMessages(args.chat_id, args.message_ids);
155
+ return { success: true };
156
+ case "telegram_pin_message":
157
+ await tg.pinMessage(args.chat_id, args.message_id);
158
+ return { success: true };
159
+ default:
160
+ throw new Error(`Unknown tool: ${name}. Available: ${tools.map((t) => t.name).join(", ")}`);
161
+ }
162
+ }
163
+ //# sourceMappingURL=tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@mcp_factory/telegram-mcp-server",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for Telegram - read chats, send messages, manage contacts, search messages. Built-in MTProto auth with OTP verification UI.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "telegram-mcp-server": "./bin/cli.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "start": "node bin/cli.js",
13
+ "auth": "node dist/auth-server.js",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "keywords": [
17
+ "mcp",
18
+ "modelcontextprotocol",
19
+ "telegram",
20
+ "mtproto",
21
+ "gramjs"
22
+ ],
23
+ "license": "MIT",
24
+ "dependencies": {
25
+ "@modelcontextprotocol/sdk": "latest",
26
+ "telegram": "^2.26.0",
27
+ "input": "^1.0.1"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^20.0.0",
31
+ "typescript": "^5.4.0"
32
+ },
33
+ "engines": {
34
+ "node": ">=18.0.0"
35
+ },
36
+ "files": [
37
+ "dist",
38
+ "bin",
39
+ "public",
40
+ "README.md"
41
+ ],
42
+ "publishConfig": {
43
+ "access": "public"
44
+ }
45
+ }
@@ -0,0 +1,277 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Telegram MCP - Connect Account</title>
7
+ <style>
8
+ * { margin: 0; padding: 0; box-sizing: border-box; }
9
+ body {
10
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
11
+ background: #0f0f0f;
12
+ color: #e4e4e4;
13
+ display: flex;
14
+ justify-content: center;
15
+ align-items: center;
16
+ min-height: 100vh;
17
+ }
18
+ .container {
19
+ background: #1a1a2e;
20
+ border-radius: 16px;
21
+ padding: 40px;
22
+ width: 400px;
23
+ box-shadow: 0 8px 32px rgba(0,0,0,0.4);
24
+ }
25
+ .logo {
26
+ text-align: center;
27
+ margin-bottom: 24px;
28
+ }
29
+ .logo svg { width: 48px; height: 48px; }
30
+ h1 {
31
+ text-align: center;
32
+ font-size: 22px;
33
+ margin-bottom: 8px;
34
+ color: #fff;
35
+ }
36
+ .subtitle {
37
+ text-align: center;
38
+ color: #888;
39
+ font-size: 14px;
40
+ margin-bottom: 32px;
41
+ }
42
+ .step { display: none; }
43
+ .step.active { display: block; }
44
+ label {
45
+ display: block;
46
+ font-size: 13px;
47
+ color: #aaa;
48
+ margin-bottom: 6px;
49
+ }
50
+ input {
51
+ width: 100%;
52
+ padding: 14px 16px;
53
+ border: 1px solid #333;
54
+ border-radius: 10px;
55
+ background: #0f0f1a;
56
+ color: #fff;
57
+ font-size: 16px;
58
+ outline: none;
59
+ margin-bottom: 16px;
60
+ transition: border 0.2s;
61
+ }
62
+ input:focus { border-color: #0088cc; }
63
+ input::placeholder { color: #555; }
64
+ button {
65
+ width: 100%;
66
+ padding: 14px;
67
+ border: none;
68
+ border-radius: 10px;
69
+ background: #0088cc;
70
+ color: #fff;
71
+ font-size: 16px;
72
+ font-weight: 600;
73
+ cursor: pointer;
74
+ transition: background 0.2s;
75
+ }
76
+ button:hover { background: #006daa; }
77
+ button:disabled { background: #333; cursor: not-allowed; }
78
+ .error {
79
+ background: #2d1515;
80
+ border: 1px solid #5c2020;
81
+ color: #ff6b6b;
82
+ padding: 12px;
83
+ border-radius: 8px;
84
+ font-size: 13px;
85
+ margin-bottom: 16px;
86
+ display: none;
87
+ }
88
+ .success {
89
+ text-align: center;
90
+ padding: 24px 0;
91
+ }
92
+ .success .check {
93
+ width: 64px;
94
+ height: 64px;
95
+ border-radius: 50%;
96
+ background: #0a3d1a;
97
+ display: flex;
98
+ align-items: center;
99
+ justify-content: center;
100
+ margin: 0 auto 16px;
101
+ font-size: 28px;
102
+ }
103
+ .info {
104
+ background: #1a1a3e;
105
+ border: 1px solid #2a2a5e;
106
+ padding: 12px;
107
+ border-radius: 8px;
108
+ font-size: 12px;
109
+ color: #888;
110
+ margin-top: 24px;
111
+ line-height: 1.5;
112
+ }
113
+ .spinner {
114
+ display: inline-block;
115
+ width: 16px;
116
+ height: 16px;
117
+ border: 2px solid #fff;
118
+ border-top-color: transparent;
119
+ border-radius: 50%;
120
+ animation: spin 0.6s linear infinite;
121
+ margin-right: 8px;
122
+ vertical-align: middle;
123
+ }
124
+ @keyframes spin { to { transform: rotate(360deg); } }
125
+ </style>
126
+ </head>
127
+ <body>
128
+ <div class="container">
129
+ <div class="logo">
130
+ <svg viewBox="0 0 48 48" fill="none"><circle cx="24" cy="24" r="24" fill="#0088cc"/><path d="M10.6 23.4l24.2-9.4c1.1-.4 2.1.3 1.7 1.8l-4.1 19.3c-.3 1.3-1.1 1.6-2.2 1l-6.1-4.5-2.9 2.8c-.3.3-.6.6-1.3.6l.4-6.2 11-9.9c.5-.4-.1-.7-.7-.3L16 25l-6-1.9c-1.3-.4-1.3-1.3.3-1.9z" fill="#fff"/></svg>
131
+ </div>
132
+ <h1>Connect Telegram</h1>
133
+ <p class="subtitle">Link your Telegram account to MCP</p>
134
+
135
+ <div class="error" id="error"></div>
136
+
137
+ <!-- Step 1: Phone Number -->
138
+ <div class="step active" id="step-phone">
139
+ <label>Phone Number (with country code)</label>
140
+ <input type="tel" id="phone" placeholder="+1 234 567 8900" autofocus>
141
+ <button onclick="sendCode()" id="btn-send">Send Verification Code</button>
142
+ </div>
143
+
144
+ <!-- Step 2: OTP -->
145
+ <div class="step" id="step-otp">
146
+ <label>Verification Code</label>
147
+ <input type="text" id="otp" placeholder="Enter the code from Telegram" maxlength="6" inputmode="numeric">
148
+ <button onclick="verifyCode()" id="btn-verify">Verify & Connect</button>
149
+ </div>
150
+
151
+ <!-- Step 3: 2FA Password (if needed) -->
152
+ <div class="step" id="step-2fa">
153
+ <label>Two-Factor Password</label>
154
+ <input type="password" id="password" placeholder="Enter your 2FA password">
155
+ <button onclick="verify2FA()" id="btn-2fa">Submit Password</button>
156
+ </div>
157
+
158
+ <!-- Step 4: Success -->
159
+ <div class="step" id="step-success">
160
+ <div class="success">
161
+ <div class="check">&#10003;</div>
162
+ <h2 style="margin-bottom:8px">Connected!</h2>
163
+ <p style="color:#888">Your Telegram account is linked. You can close this tab.</p>
164
+ </div>
165
+ </div>
166
+
167
+ <div class="info">
168
+ Your session is stored locally at <code>~/.telegram-mcp/</code> and encrypted.
169
+ No data is sent to any third-party server.
170
+ </div>
171
+ </div>
172
+
173
+ <script>
174
+ let phoneHash = "";
175
+
176
+ function showError(msg) {
177
+ const el = document.getElementById("error");
178
+ el.textContent = msg;
179
+ el.style.display = msg ? "block" : "none";
180
+ }
181
+
182
+ function showStep(id) {
183
+ document.querySelectorAll(".step").forEach(s => s.classList.remove("active"));
184
+ document.getElementById("step-" + id).classList.add("active");
185
+ showError("");
186
+ }
187
+
188
+ function setLoading(btnId, loading) {
189
+ const btn = document.getElementById(btnId);
190
+ if (loading) {
191
+ btn.disabled = true;
192
+ btn.dataset.text = btn.textContent;
193
+ btn.innerHTML = '<span class="spinner"></span>Please wait...';
194
+ } else {
195
+ btn.disabled = false;
196
+ btn.textContent = btn.dataset.text;
197
+ }
198
+ }
199
+
200
+ async function sendCode() {
201
+ const phone = document.getElementById("phone").value.trim();
202
+ if (!phone || !phone.startsWith("+")) {
203
+ showError("Enter phone number with country code (e.g. +1234567890)");
204
+ return;
205
+ }
206
+ setLoading("btn-send", true);
207
+ try {
208
+ const res = await fetch("/api/send-code", {
209
+ method: "POST",
210
+ headers: { "Content-Type": "application/json" },
211
+ body: JSON.stringify({ phone }),
212
+ });
213
+ const data = await res.json();
214
+ if (!res.ok) throw new Error(data.error || "Failed to send code");
215
+ phoneHash = data.phoneCodeHash;
216
+ showStep("otp");
217
+ document.getElementById("otp").focus();
218
+ } catch (e) {
219
+ showError(e.message);
220
+ }
221
+ setLoading("btn-send", false);
222
+ }
223
+
224
+ async function verifyCode() {
225
+ const code = document.getElementById("otp").value.trim();
226
+ if (!code) { showError("Enter the verification code"); return; }
227
+ setLoading("btn-verify", true);
228
+ try {
229
+ const phone = document.getElementById("phone").value.trim();
230
+ const res = await fetch("/api/verify-code", {
231
+ method: "POST",
232
+ headers: { "Content-Type": "application/json" },
233
+ body: JSON.stringify({ phone, code, phoneCodeHash: phoneHash }),
234
+ });
235
+ const data = await res.json();
236
+ if (!res.ok) {
237
+ if (data.need2FA) {
238
+ showStep("2fa");
239
+ document.getElementById("password").focus();
240
+ setLoading("btn-verify", false);
241
+ return;
242
+ }
243
+ throw new Error(data.error || "Verification failed");
244
+ }
245
+ showStep("success");
246
+ } catch (e) {
247
+ showError(e.message);
248
+ }
249
+ setLoading("btn-verify", false);
250
+ }
251
+
252
+ async function verify2FA() {
253
+ const password = document.getElementById("password").value;
254
+ if (!password) { showError("Enter your 2FA password"); return; }
255
+ setLoading("btn-2fa", true);
256
+ try {
257
+ const res = await fetch("/api/verify-2fa", {
258
+ method: "POST",
259
+ headers: { "Content-Type": "application/json" },
260
+ body: JSON.stringify({ password }),
261
+ });
262
+ const data = await res.json();
263
+ if (!res.ok) throw new Error(data.error || "2FA verification failed");
264
+ showStep("success");
265
+ } catch (e) {
266
+ showError(e.message);
267
+ }
268
+ setLoading("btn-2fa", false);
269
+ }
270
+
271
+ // Enter key handlers
272
+ document.getElementById("phone").addEventListener("keydown", e => { if (e.key === "Enter") sendCode(); });
273
+ document.getElementById("otp").addEventListener("keydown", e => { if (e.key === "Enter") verifyCode(); });
274
+ document.getElementById("password").addEventListener("keydown", e => { if (e.key === "Enter") verify2FA(); });
275
+ </script>
276
+ </body>
277
+ </html>