mirror of
https://github.com/zhigang1992/telegram-mcp.git
synced 2026-01-12 08:24:40 +08:00
feat: init commit
This commit is contained in:
12
.claude/settings.local.json
Normal file
12
.claude/settings.local.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(bun add:*)",
|
||||
"Bash(bun run tsc:*)",
|
||||
"Bash(find:*)",
|
||||
"Bash(bun run:*)",
|
||||
"Bash(mkdir:*)"
|
||||
],
|
||||
"deny": []
|
||||
}
|
||||
}
|
||||
37
.github/workflows/ci.yml
vendored
Normal file
37
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install --frozen-lockfile
|
||||
|
||||
- name: Type check
|
||||
run: bun run tsc --noEmit
|
||||
|
||||
- name: Build
|
||||
run: bun run build
|
||||
|
||||
- name: Check build output
|
||||
run: |
|
||||
if [ ! -f telegram-mcp ]; then
|
||||
echo "Build failed: telegram-mcp executable not found"
|
||||
exit 1
|
||||
fi
|
||||
echo "Build successful: telegram-mcp executable created"
|
||||
95
.github/workflows/release.yml
vendored
Normal file
95
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
name: Build and Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
target: linux-x64
|
||||
build_name: telegram-mcp-linux-x64
|
||||
- os: macos-latest
|
||||
target: darwin-x64
|
||||
build_name: telegram-mcp-darwin-x64
|
||||
- os: macos-latest
|
||||
target: darwin-arm64
|
||||
build_name: telegram-mcp-darwin-arm64
|
||||
- os: windows-latest
|
||||
target: win-x64
|
||||
build_name: telegram-mcp-win-x64.exe
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install --frozen-lockfile
|
||||
|
||||
- name: Build executable
|
||||
run: |
|
||||
if [ "${{ matrix.os }}" = "windows-latest" ]; then
|
||||
bun build ./src/main.ts --compile --outfile ${{ matrix.build_name }} --target=bun-${{ matrix.target }}
|
||||
else
|
||||
bun build ./src/main.ts --compile --outfile ${{ matrix.build_name }} --target=bun-${{ matrix.target }}
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Create tarball (Unix)
|
||||
if: matrix.os != 'windows-latest'
|
||||
run: tar -czf ${{ matrix.build_name }}.tar.gz ${{ matrix.build_name }} README.md
|
||||
|
||||
- name: Create zip (Windows)
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
Compress-Archive -Path ${{ matrix.build_name }}, README.md -DestinationPath ${{ matrix.build_name }}.zip
|
||||
shell: pwsh
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.build_name }}
|
||||
path: |
|
||||
${{ matrix.build_name }}.tar.gz
|
||||
${{ matrix.build_name }}.zip
|
||||
|
||||
release:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: |
|
||||
telegram-mcp-linux-x64/telegram-mcp-linux-x64.tar.gz
|
||||
telegram-mcp-darwin-x64/telegram-mcp-darwin-x64.tar.gz
|
||||
telegram-mcp-darwin-arm64/telegram-mcp-darwin-arm64.tar.gz
|
||||
telegram-mcp-win-x64.exe/telegram-mcp-win-x64.exe.zip
|
||||
draft: false
|
||||
prerelease: false
|
||||
generate_release_notes: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
50
.gitignore
vendored
50
.gitignore
vendored
@@ -1,9 +1,49 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
.pnp
|
||||
.pnp.js
|
||||
|
||||
# Build outputs
|
||||
telegram-mcp
|
||||
telegram-mcp-*
|
||||
*.exe
|
||||
dist/
|
||||
.nyc_output/
|
||||
**/.DS_Store
|
||||
.idea
|
||||
.vscode
|
||||
build/
|
||||
|
||||
# Session data
|
||||
bot-data/
|
||||
session*
|
||||
*.session
|
||||
|
||||
# Environment files
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# TypeScript
|
||||
*.tsbuildinfo
|
||||
.env
|
||||
|
||||
# Testing
|
||||
.nyc_output/
|
||||
|
||||
# Misc
|
||||
.cache/
|
||||
*.tmp
|
||||
*.temp
|
||||
telegram-mcp
|
||||
|
||||
128
README.md
128
README.md
@@ -1,14 +1,130 @@
|
||||
# telegram-mcp
|
||||
|
||||
mtcute powered Telegram bot
|
||||
A Model Context Protocol (MCP) server for interacting with Telegram using mtcute.
|
||||
|
||||
## Features
|
||||
|
||||
- Send text messages to chats
|
||||
- Read messages from chats
|
||||
- Search messages
|
||||
- List and get information about dialogs (chats)
|
||||
- Get recent messages across all chats
|
||||
|
||||
## Setup
|
||||
|
||||
### Installation
|
||||
|
||||
#### Option 1: Download Pre-built Binary
|
||||
|
||||
Download the latest release for your platform from the [releases page](https://github.com/yourusername/telegram-mcp/releases):
|
||||
|
||||
- **macOS (Apple Silicon)**: `telegram-mcp-darwin-arm64.tar.gz`
|
||||
- **macOS (Intel)**: `telegram-mcp-darwin-x64.tar.gz`
|
||||
- **Linux**: `telegram-mcp-linux-x64.tar.gz`
|
||||
- **Windows**: `telegram-mcp-win-x64.exe.zip`
|
||||
|
||||
Extract the archive and make the binary executable (Unix systems):
|
||||
```bash
|
||||
tar -xzf telegram-mcp-*.tar.gz
|
||||
chmod +x telegram-mcp
|
||||
```
|
||||
|
||||
#### Option 2: Build from Source
|
||||
|
||||
1. Clone the repository and install dependencies:
|
||||
```bash
|
||||
git clone https://github.com/yourusername/telegram-mcp.git
|
||||
cd telegram-mcp
|
||||
bun install
|
||||
```
|
||||
|
||||
2. Build the executable:
|
||||
```bash
|
||||
bun run build
|
||||
```
|
||||
|
||||
### Initial Setup (First Time Only)
|
||||
|
||||
1. Get your Telegram API credentials from https://my.telegram.org
|
||||
|
||||
2. Run the initial setup to authenticate with Telegram:
|
||||
```bash
|
||||
export API_ID=your_api_id
|
||||
export API_HASH=your_api_hash
|
||||
./telegram-mcp
|
||||
```
|
||||
|
||||
The server will:
|
||||
- Prompt you to enter your phone number
|
||||
- Send you a verification code via Telegram
|
||||
- Ask for the verification code
|
||||
- Display the absolute storage path (you'll need this for MCP configuration)
|
||||
|
||||
3. Note the storage path displayed in the output. It will look something like:
|
||||
```
|
||||
Storage path: /Users/username/telegram-mcp/bot-data/session
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### As an MCP Server
|
||||
|
||||
Add to your Claude Desktop config using the storage path from the initial setup:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"telegram": {
|
||||
"command": "/path/to/telegram-mcp",
|
||||
"env": {
|
||||
"API_ID": "your_api_id",
|
||||
"API_HASH": "your_api_hash",
|
||||
"TELEGRAM_STORAGE_PATH": "/absolute/path/from/initial/setup"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Important**: The `TELEGRAM_STORAGE_PATH` must be the absolute path shown during initial setup. This ensures the MCP server uses the authenticated session.
|
||||
|
||||
### Available Tools
|
||||
|
||||
#### Message Tools
|
||||
|
||||
- `messages_sendText` - Send a text message to a chat
|
||||
- `chatId` (required): Chat/User ID or username
|
||||
- `text` (required): Message text to send
|
||||
- `replyToMessageId`: Optional message ID to reply to
|
||||
|
||||
- `messages_getHistory` - Get message history from a chat
|
||||
- `chatId` (required): Chat/User ID or username
|
||||
- `limit`: Number of messages (default: 100, max: 100)
|
||||
- `offsetId`: Message ID for pagination
|
||||
|
||||
- `messages_search` - Search for messages
|
||||
- `query` (required): Search query
|
||||
- `chatId`: Specific chat to search in (optional)
|
||||
- `limit`: Number of results (default: 50)
|
||||
|
||||
- `messages_getRecent` - Get recent messages from all chats
|
||||
- `limit`: Number of chats (default: 10)
|
||||
- `messagesPerChat`: Messages per chat (default: 10)
|
||||
|
||||
#### Dialog Tools
|
||||
|
||||
- `dialogs_list` - List all dialogs
|
||||
- `limit`: Maximum dialogs (default: 50)
|
||||
- `filter`: Filter options (onlyUsers, onlyGroups, onlyChannels)
|
||||
|
||||
- `dialogs_getInfo` - Get detailed dialog information
|
||||
- `chatId` (required): Chat/User ID or username
|
||||
|
||||
## Development
|
||||
|
||||
Run in development mode:
|
||||
```bash
|
||||
bun install --frozen-lockfile
|
||||
cp .env.example .env
|
||||
# edit .env
|
||||
bun start
|
||||
bun run dev
|
||||
```
|
||||
|
||||
*generated with @mtcute/create-bot*
|
||||
The server stores session data in `bot-data/` directory.
|
||||
175
bun.lock
175
bun.lock
@@ -4,6 +4,7 @@
|
||||
"": {
|
||||
"name": "telegram-mcp",
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.13.2",
|
||||
"@mtcute/bun": "^0.24.3",
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -22,6 +23,8 @@
|
||||
|
||||
"@fuman/utils": ["@fuman/utils@0.0.15", "", {}, "sha512-3H3WzkfG7iLKCa/yNV4s80lYD4yr5hgiNzU13ysLY2BcDqFjM08XGYuLd5wFVp4V8+DA/fe8gIDW96To/JwDyA=="],
|
||||
|
||||
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.13.2", "", { "dependencies": { "ajv": "^6.12.6", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-Vx7qOcmoKkR3qhaQ9qf3GxiVKCEu+zfJddHv6x3dY/9P6+uIwJnmuAur5aB+4FDXf41rRrDnOEGkviX5oYZ67w=="],
|
||||
|
||||
"@mtcute/bun": ["@mtcute/bun@0.24.3", "", { "dependencies": { "@fuman/bun": "0.0.15", "@fuman/io": "0.0.15", "@fuman/net": "0.0.15", "@fuman/utils": "0.0.15", "@mtcute/core": "^0.24.3", "@mtcute/html-parser": "^0.24.0", "@mtcute/markdown-parser": "^0.24.0", "@mtcute/wasm": "^0.24.3" } }, "sha512-voNLlACw7Su8+DKCiiBRi8F1yjl3r/AulUW8KFncPMILXo2H94qNengVg9KXUYx1mTLhJIqfRM3ouuuQQna9Lg=="],
|
||||
|
||||
"@mtcute/core": ["@mtcute/core@0.24.4", "", { "dependencies": { "@fuman/io": "0.0.15", "@fuman/net": "0.0.15", "@fuman/utils": "0.0.15", "@mtcute/file-id": "^0.24.3", "@mtcute/tl": "^204.0.0", "@mtcute/tl-runtime": "^0.24.3", "@types/events": "3.0.0", "long": "5.2.3" } }, "sha512-4dQ1MhY1DmEUqpOLyssgv9WljDr/itpUTVanTg6pZsraU6Z+ohH4E3V2UOQcfmWCQbecmMZL7UeLlScfw0oDXQ=="],
|
||||
@@ -44,8 +47,36 @@
|
||||
|
||||
"@types/node": ["@types/node@24.0.7", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-YIEUUr4yf8q8oQoXPpSlnvKNVKDQlPMWrmOcgzoduo7kvA2UF0/BwJ/eMKFTiTtkNL17I0M6Xe2tvwFU7be6iw=="],
|
||||
|
||||
"accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
|
||||
|
||||
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
|
||||
|
||||
"body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="],
|
||||
|
||||
"bun-types": ["bun-types@1.2.17", "", { "dependencies": { "@types/node": "*" } }, "sha512-ElC7ItwT3SCQwYZDYoAH+q6KT4Fxjl8DtZ6qDulUFBmXA8YB4xo+l54J9ZJN+k2pphfn9vk7kfubeSd5QfTVJQ=="],
|
||||
|
||||
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
||||
|
||||
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
||||
|
||||
"call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
|
||||
|
||||
"content-disposition": ["content-disposition@1.0.0", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg=="],
|
||||
|
||||
"content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
|
||||
|
||||
"cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
|
||||
|
||||
"cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="],
|
||||
|
||||
"cors": ["cors@2.8.5", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g=="],
|
||||
|
||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
|
||||
"debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||
|
||||
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
|
||||
|
||||
"dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="],
|
||||
|
||||
"domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
|
||||
@@ -54,16 +85,160 @@
|
||||
|
||||
"domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
|
||||
|
||||
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||
|
||||
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
|
||||
|
||||
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
|
||||
|
||||
"entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
|
||||
|
||||
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
|
||||
|
||||
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
|
||||
|
||||
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
|
||||
|
||||
"escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="],
|
||||
|
||||
"etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
|
||||
|
||||
"eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="],
|
||||
|
||||
"eventsource-parser": ["eventsource-parser@3.0.3", "", {}, "sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA=="],
|
||||
|
||||
"express": ["express@5.1.0", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="],
|
||||
|
||||
"express-rate-limit": ["express-rate-limit@7.5.1", "", { "peerDependencies": { "express": ">= 4.11" } }, "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw=="],
|
||||
|
||||
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
||||
|
||||
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
|
||||
|
||||
"finalhandler": ["finalhandler@2.1.0", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q=="],
|
||||
|
||||
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
|
||||
|
||||
"fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="],
|
||||
|
||||
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||
|
||||
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
||||
|
||||
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
|
||||
|
||||
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
||||
|
||||
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
||||
|
||||
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
||||
|
||||
"htmlparser2": ["htmlparser2@10.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.2.1", "entities": "^6.0.0" } }, "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g=="],
|
||||
|
||||
"http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="],
|
||||
|
||||
"iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
|
||||
|
||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||
|
||||
"ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
|
||||
|
||||
"is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
|
||||
|
||||
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||
|
||||
"json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
|
||||
|
||||
"long": ["long@5.2.3", "", {}, "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q=="],
|
||||
|
||||
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
||||
|
||||
"media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
|
||||
|
||||
"merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
|
||||
|
||||
"mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
|
||||
|
||||
"mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="],
|
||||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
|
||||
|
||||
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
||||
|
||||
"object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
|
||||
|
||||
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
|
||||
|
||||
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
|
||||
|
||||
"parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="],
|
||||
|
||||
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
||||
|
||||
"path-to-regexp": ["path-to-regexp@8.2.0", "", {}, "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ=="],
|
||||
|
||||
"pkce-challenge": ["pkce-challenge@5.0.0", "", {}, "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ=="],
|
||||
|
||||
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
|
||||
|
||||
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||
|
||||
"qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="],
|
||||
|
||||
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
|
||||
|
||||
"raw-body": ["raw-body@3.0.0", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.6.3", "unpipe": "1.0.0" } }, "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g=="],
|
||||
|
||||
"router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="],
|
||||
|
||||
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
||||
|
||||
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
||||
|
||||
"send": ["send@1.2.0", "", { "dependencies": { "debug": "^4.3.5", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.0", "mime-types": "^3.0.1", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.1" } }, "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw=="],
|
||||
|
||||
"serve-static": ["serve-static@2.2.0", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ=="],
|
||||
|
||||
"setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="],
|
||||
|
||||
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
||||
|
||||
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
||||
|
||||
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
|
||||
|
||||
"side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="],
|
||||
|
||||
"side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="],
|
||||
|
||||
"side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
|
||||
|
||||
"statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="],
|
||||
|
||||
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
||||
|
||||
"type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
|
||||
|
||||
"typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
|
||||
|
||||
"undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="],
|
||||
|
||||
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
|
||||
|
||||
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
||||
|
||||
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
|
||||
|
||||
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||
|
||||
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
|
||||
|
||||
"zod": ["zod@3.25.67", "", {}, "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw=="],
|
||||
|
||||
"zod-to-json-schema": ["zod-to-json-schema@3.24.6", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg=="],
|
||||
|
||||
"dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,11 @@
|
||||
"packageManager": "bun@1.2.16",
|
||||
"scripts": {
|
||||
"dev": "bun --watch ./src/main.ts",
|
||||
"start": "bun ./src/main.ts"
|
||||
"start": "bun ./src/main.ts",
|
||||
"build": "bun build ./src/main.ts --compile --outfile telegram-mcp"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.13.2",
|
||||
"@mtcute/bun": "^0.24.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
11
src/env.ts
11
src/env.ts
@@ -1,10 +1,11 @@
|
||||
import process from 'node:process'
|
||||
import process from 'node:process';
|
||||
|
||||
const API_ID = Number.parseInt(process.env.API_ID!)
|
||||
const API_HASH = process.env.API_HASH!
|
||||
const API_ID = Number.parseInt(process.env.API_ID!);
|
||||
const API_HASH = process.env.API_HASH!;
|
||||
const STORAGE_PATH = process.env.TELEGRAM_STORAGE_PATH || 'bot-data/session';
|
||||
|
||||
if (Number.isNaN(API_ID) || !API_HASH) {
|
||||
throw new Error('API_ID or API_HASH not set!')
|
||||
throw new Error('API_ID or API_HASH not set!');
|
||||
}
|
||||
|
||||
export { API_HASH, API_ID }
|
||||
export { API_HASH, API_ID, STORAGE_PATH };
|
||||
|
||||
23
src/main.ts
23
src/main.ts
@@ -1,13 +1,14 @@
|
||||
import { TelegramClient } from '@mtcute/bun'
|
||||
import { TelegramServer } from './server/telegram-server.js';
|
||||
|
||||
import * as env from './env.ts'
|
||||
async function main() {
|
||||
const server = new TelegramServer();
|
||||
|
||||
try {
|
||||
await server.start();
|
||||
} catch (error) {
|
||||
console.error('Failed to start server:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
const tg = new TelegramClient({
|
||||
apiId: env.API_ID,
|
||||
apiHash: env.API_HASH,
|
||||
storage: 'bot-data/session',
|
||||
})
|
||||
|
||||
|
||||
const user = await tg.start()
|
||||
console.log('Logged in as', user.username)
|
||||
main().catch(console.error);
|
||||
|
||||
101
src/server/telegram-server.ts
Normal file
101
src/server/telegram-server.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
||||
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
||||
import {
|
||||
ListToolsRequestSchema,
|
||||
CallToolRequestSchema,
|
||||
ErrorCode,
|
||||
McpError,
|
||||
} from '@modelcontextprotocol/sdk/types.js';
|
||||
import { TelegramClient } from '@mtcute/bun';
|
||||
import * as path from 'node:path';
|
||||
import * as env from '../env.js';
|
||||
import { registerTools, handleToolCall } from '../tools/index.js';
|
||||
|
||||
export class TelegramServer {
|
||||
private server: Server;
|
||||
private telegramClient: TelegramClient | null = null;
|
||||
|
||||
constructor() {
|
||||
this.server = new Server(
|
||||
{
|
||||
name: 'telegram-mcp',
|
||||
version: '0.0.1',
|
||||
},
|
||||
{
|
||||
capabilities: {
|
||||
tools: {},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
this.setupHandlers();
|
||||
this.setupErrorHandling();
|
||||
}
|
||||
|
||||
private setupHandlers() {
|
||||
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
||||
tools: registerTools(),
|
||||
}));
|
||||
|
||||
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
if (!this.telegramClient) {
|
||||
throw new McpError(
|
||||
ErrorCode.InternalError,
|
||||
'Telegram client not initialized'
|
||||
);
|
||||
}
|
||||
|
||||
return handleToolCall(request.params.name, request.params.arguments || {}, this.telegramClient);
|
||||
});
|
||||
}
|
||||
|
||||
private setupErrorHandling() {
|
||||
process.on('SIGINT', async () => {
|
||||
await this.cleanup();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
process.on('SIGTERM', async () => {
|
||||
await this.cleanup();
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
private async cleanup() {
|
||||
if (this.telegramClient) {
|
||||
await this.telegramClient.disconnect();
|
||||
}
|
||||
await this.server.close();
|
||||
}
|
||||
|
||||
async start() {
|
||||
// Initialize Telegram client
|
||||
this.telegramClient = new TelegramClient({
|
||||
apiId: env.API_ID,
|
||||
apiHash: env.API_HASH,
|
||||
storage: env.STORAGE_PATH,
|
||||
});
|
||||
|
||||
try {
|
||||
// Print storage path for initial setup
|
||||
const absoluteStoragePath = path.resolve(env.STORAGE_PATH);
|
||||
console.error(`\n=== Telegram MCP Setup ===`);
|
||||
console.error(`Storage path: ${absoluteStoragePath}`);
|
||||
console.error(`\nIf this is your first run, you'll need to authenticate with your phone number.`);
|
||||
console.error(`After authentication, use the storage path above in your MCP configuration.\n`);
|
||||
|
||||
const user = await this.telegramClient.start();
|
||||
console.error(`\nConnected to Telegram as ${user.username || user.id}`);
|
||||
console.error(`Storage path: ${absoluteStoragePath}`);
|
||||
console.error(`\nReady to accept MCP requests.`);
|
||||
} catch (error) {
|
||||
console.error('Failed to start Telegram client:', error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Start MCP server
|
||||
const transport = new StdioServerTransport();
|
||||
await this.server.connect(transport);
|
||||
console.error('Telegram MCP server started');
|
||||
}
|
||||
}
|
||||
196
src/tools/dialog-tools.ts
Normal file
196
src/tools/dialog-tools.ts
Normal file
@@ -0,0 +1,196 @@
|
||||
import type { TelegramClient, Dialog } from '@mtcute/bun';
|
||||
import type { ToolInfo } from './index.js';
|
||||
|
||||
export const dialogTools: ToolInfo[] = [
|
||||
{
|
||||
name: 'dialogs_list',
|
||||
description: 'List all dialogs (chats)',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
limit: {
|
||||
type: 'number',
|
||||
description: 'Maximum number of dialogs to return (default: 50)',
|
||||
default: 50,
|
||||
},
|
||||
filter: {
|
||||
type: 'object',
|
||||
description: 'Filter options',
|
||||
properties: {
|
||||
onlyUsers: {
|
||||
type: 'boolean',
|
||||
description: 'Only show user chats',
|
||||
},
|
||||
onlyGroups: {
|
||||
type: 'boolean',
|
||||
description: 'Only show group chats',
|
||||
},
|
||||
onlyChannels: {
|
||||
type: 'boolean',
|
||||
description: 'Only show channels',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'dialogs_getInfo',
|
||||
description: 'Get detailed information about a specific dialog',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
chatId: {
|
||||
type: 'string',
|
||||
description: 'Chat/User ID or username',
|
||||
},
|
||||
},
|
||||
required: ['chatId'],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export async function handleDialogTools(
|
||||
name: string,
|
||||
args: any,
|
||||
client: TelegramClient
|
||||
) {
|
||||
switch (name) {
|
||||
case 'dialogs_list':
|
||||
return await listDialogs(client, args);
|
||||
case 'dialogs_getInfo':
|
||||
return await getDialogInfo(client, args);
|
||||
default:
|
||||
throw new Error(`Unknown dialog tool: ${name}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function listDialogs(client: TelegramClient, args: any) {
|
||||
const { limit = 50, filter = {} } = args;
|
||||
|
||||
try {
|
||||
const dialogs: Dialog[] = [];
|
||||
let count = 0;
|
||||
|
||||
for await (const dialog of client.iterDialogs()) {
|
||||
if (count >= limit) break;
|
||||
|
||||
// Apply filters
|
||||
if (filter.onlyUsers && dialog.peer.type !== 'user') continue;
|
||||
if (filter.onlyGroups && dialog.peer.type !== 'chat') continue;
|
||||
if (filter.onlyChannels && dialog.peer.type !== 'chat') continue;
|
||||
|
||||
dialogs.push(dialog);
|
||||
count++;
|
||||
}
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify({
|
||||
dialogs: dialogs.map(formatDialog),
|
||||
count: dialogs.length,
|
||||
}, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error: any) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `Error listing dialogs: ${error.message}`,
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function getDialogInfo(client: TelegramClient, args: any) {
|
||||
const { chatId } = args;
|
||||
|
||||
try {
|
||||
// Find the dialog first
|
||||
let dialog: Dialog | null = null;
|
||||
const numericChatId = Number(chatId);
|
||||
const searchId = Number.isNaN(numericChatId) ? chatId : numericChatId;
|
||||
|
||||
for await (const d of client.iterDialogs()) {
|
||||
if (d.peer.id === searchId || d.peer.username === chatId) {
|
||||
dialog = d;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dialog) {
|
||||
throw new Error('Dialog not found');
|
||||
}
|
||||
|
||||
// Get full info about the peer
|
||||
let fullInfo: any = {};
|
||||
if (dialog.peer.type === 'user') {
|
||||
const userFull = await client.getFullUser(dialog.peer);
|
||||
fullInfo = {
|
||||
bio: userFull.bio,
|
||||
commonChatsCount: userFull.commonChatsCount,
|
||||
isBlocked: userFull.isBlocked,
|
||||
};
|
||||
} else if (dialog.peer.type === 'chat') {
|
||||
const chatFull = await client.getFullChat(dialog.peer);
|
||||
fullInfo = {
|
||||
bio: chatFull.bio,
|
||||
participantsCount: chatFull.onlineCount || 0,
|
||||
adminsCount: chatFull.adminsCount,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify({
|
||||
peer: {
|
||||
id: dialog.peer.id,
|
||||
type: dialog.peer.type,
|
||||
username: dialog.peer.username,
|
||||
displayName: dialog.peer.displayName,
|
||||
},
|
||||
dialog: formatDialog(dialog),
|
||||
fullInfo,
|
||||
}, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error: any) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `Error getting dialog info: ${error.message}`,
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function formatDialog(dialog: Dialog) {
|
||||
return {
|
||||
id: dialog.peer.id,
|
||||
name: dialog.peer.displayName || `Chat ${dialog.peer.id}`,
|
||||
username: dialog.peer.username,
|
||||
type: dialog.peer.type,
|
||||
unreadCount: dialog.unreadCount,
|
||||
unreadMentionsCount: dialog.unreadMentionsCount,
|
||||
isPinned: dialog.isPinned,
|
||||
isMuted: dialog.isMuted,
|
||||
lastMessage: dialog.lastMessage ? {
|
||||
id: dialog.lastMessage.id,
|
||||
date: dialog.lastMessage.date,
|
||||
text: dialog.lastMessage.text,
|
||||
isOutgoing: dialog.lastMessage.isOutgoing,
|
||||
} : null,
|
||||
};
|
||||
}
|
||||
35
src/tools/index.ts
Normal file
35
src/tools/index.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import type { TelegramClient } from '@mtcute/bun';
|
||||
import { messageTools, handleMessageTools } from './message-tools.js';
|
||||
import { dialogTools, handleDialogTools } from './dialog-tools.js';
|
||||
|
||||
export type ToolInfo = {
|
||||
name: string;
|
||||
description: string;
|
||||
inputSchema: {
|
||||
type: 'object';
|
||||
properties: Record<string, any>;
|
||||
required?: string[];
|
||||
};
|
||||
};
|
||||
|
||||
export function registerTools(): ToolInfo[] {
|
||||
return [
|
||||
...messageTools,
|
||||
...dialogTools,
|
||||
];
|
||||
}
|
||||
|
||||
export async function handleToolCall(
|
||||
name: string,
|
||||
args: any,
|
||||
client: TelegramClient
|
||||
) {
|
||||
// Route to appropriate handler based on tool name prefix
|
||||
if (name.startsWith('messages_')) {
|
||||
return handleMessageTools(name, args, client);
|
||||
} else if (name.startsWith('dialogs_')) {
|
||||
return handleDialogTools(name, args, client);
|
||||
}
|
||||
|
||||
throw new Error(`Unknown tool: ${name}`);
|
||||
}
|
||||
297
src/tools/message-tools.ts
Normal file
297
src/tools/message-tools.ts
Normal file
@@ -0,0 +1,297 @@
|
||||
import type { TelegramClient, Dialog, Message } from '@mtcute/bun';
|
||||
import type { ToolInfo } from './index.js';
|
||||
|
||||
export const messageTools: ToolInfo[] = [
|
||||
{
|
||||
name: 'messages_sendText',
|
||||
description: 'Send a text message to a chat',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
chatId: {
|
||||
type: 'string',
|
||||
description: 'Chat/User ID or username to send message to',
|
||||
},
|
||||
text: {
|
||||
type: 'string',
|
||||
description: 'Message text to send',
|
||||
},
|
||||
replyToMessageId: {
|
||||
type: 'number',
|
||||
description: 'Optional message ID to reply to',
|
||||
},
|
||||
},
|
||||
required: ['chatId', 'text'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'messages_getHistory',
|
||||
description: 'Get message history from a chat',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
chatId: {
|
||||
type: 'string',
|
||||
description: 'Chat/User ID or username to get messages from',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
description: 'Number of messages to retrieve (default: 100, max: 100)',
|
||||
default: 100,
|
||||
},
|
||||
offsetId: {
|
||||
type: 'number',
|
||||
description: 'Message ID to start from (for pagination)',
|
||||
},
|
||||
},
|
||||
required: ['chatId'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'messages_search',
|
||||
description: 'Search for messages in a chat',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
chatId: {
|
||||
type: 'string',
|
||||
description: 'Chat/User ID or username to search in (optional, searches all chats if not provided)',
|
||||
},
|
||||
query: {
|
||||
type: 'string',
|
||||
description: 'Search query',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
description: 'Number of messages to retrieve (default: 50)',
|
||||
default: 50,
|
||||
},
|
||||
},
|
||||
required: ['query'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'messages_getRecent',
|
||||
description: 'Get recent messages from all chats',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
limit: {
|
||||
type: 'number',
|
||||
description: 'Number of recent chats to include (default: 10)',
|
||||
default: 10,
|
||||
},
|
||||
messagesPerChat: {
|
||||
type: 'number',
|
||||
description: 'Number of messages per chat (default: 10)',
|
||||
default: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export async function handleMessageTools(
|
||||
name: string,
|
||||
args: any,
|
||||
client: TelegramClient
|
||||
) {
|
||||
switch (name) {
|
||||
case 'messages_sendText':
|
||||
return await sendTextMessage(client, args);
|
||||
case 'messages_getHistory':
|
||||
return await getMessageHistory(client, args);
|
||||
case 'messages_search':
|
||||
return await searchMessages(client, args);
|
||||
case 'messages_getRecent':
|
||||
return await getRecentMessages(client, args);
|
||||
default:
|
||||
throw new Error(`Unknown message tool: ${name}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function sendTextMessage(client: TelegramClient, args: any) {
|
||||
const { chatId, text, replyToMessageId } = args;
|
||||
|
||||
try {
|
||||
const sentMessage = await client.sendText(
|
||||
Number.isNaN(Number(chatId)) ? chatId : Number(chatId),
|
||||
text,
|
||||
{
|
||||
replyTo: replyToMessageId ? replyToMessageId : undefined,
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify({
|
||||
success: true,
|
||||
message: formatMessage(sentMessage),
|
||||
}, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error: any) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `Error sending message: ${error.message}`,
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function getMessageHistory(client: TelegramClient, args: any) {
|
||||
const { chatId, limit = 100, offsetId } = args;
|
||||
|
||||
try {
|
||||
const messages = await client.getHistory(Number.isNaN(Number(chatId)) ? chatId : Number(chatId), {
|
||||
limit: Math.min(limit, 100),
|
||||
offset: offsetId ? { id: offsetId, date: 0 } : undefined,
|
||||
});
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify({
|
||||
messages: messages.map(formatMessage),
|
||||
count: messages.length,
|
||||
}, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error: any) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `Error getting message history: ${error.message}`,
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function searchMessages(client: TelegramClient, args: any) {
|
||||
const { chatId, query, limit = 50 } = args;
|
||||
|
||||
try {
|
||||
const results: Message[] = [];
|
||||
|
||||
if (chatId) {
|
||||
// Search in specific chat
|
||||
const messages = await client.searchMessages({
|
||||
chatId: Number.isNaN(Number(chatId)) ? chatId : Number(chatId),
|
||||
query,
|
||||
limit,
|
||||
});
|
||||
results.push(...messages);
|
||||
} else {
|
||||
// Search globally
|
||||
const messages = await client.searchGlobal({
|
||||
query,
|
||||
limit,
|
||||
});
|
||||
results.push(...messages);
|
||||
}
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify({
|
||||
messages: results.map(formatMessage),
|
||||
count: results.length,
|
||||
query,
|
||||
}, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error: any) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `Error searching messages: ${error.message}`,
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function getRecentMessages(client: TelegramClient, args: any) {
|
||||
const { limit = 10, messagesPerChat = 10 } = args;
|
||||
|
||||
try {
|
||||
const recentChats: Array<{
|
||||
dialog: any;
|
||||
messages: any[];
|
||||
}> = [];
|
||||
|
||||
let count = 0;
|
||||
for await (const dialog of client.iterDialogs()) {
|
||||
if (count >= limit) break;
|
||||
|
||||
const messages = await client.getHistory(dialog.peer, {
|
||||
limit: messagesPerChat,
|
||||
});
|
||||
|
||||
recentChats.push({
|
||||
dialog: {
|
||||
id: dialog.peer.id,
|
||||
name: dialog.peer.displayName || `Chat ${dialog.peer.id}`,
|
||||
username: dialog.peer.username,
|
||||
type: dialog.peer.type,
|
||||
lastMessageDate: dialog.lastMessage?.date,
|
||||
},
|
||||
messages: messages.map(formatMessage),
|
||||
});
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify({
|
||||
chats: recentChats,
|
||||
count: recentChats.length,
|
||||
}, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error: any) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `Error getting recent messages: ${error.message}`,
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function formatMessage(msg: Message) {
|
||||
return {
|
||||
id: msg.id,
|
||||
date: msg.date,
|
||||
text: msg.text,
|
||||
senderId: msg.sender.id,
|
||||
senderName: msg.sender.displayName || msg.sender.username || `User ${msg.sender.id}`,
|
||||
senderUsername: msg.sender.username,
|
||||
isOutgoing: msg.isOutgoing,
|
||||
chatId: msg.chat.id,
|
||||
chatName: msg.chat.displayName || msg.chat.username || `Chat ${msg.chat.id}`,
|
||||
};
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"noEmit": true,
|
||||
"allowImportingTsExtensions": true,
|
||||
"allowImportingTsExtensions": false,
|
||||
"target": "es2022",
|
||||
"allowJs": true,
|
||||
"sourceMap": true,
|
||||
|
||||
Reference in New Issue
Block a user