Add deployment scripts and documentation

- Add deployment scripts (deploy.sh, test_connection.sh, core/start_server.sh)
- Add deployment documentation (DEPLOYMENT_INFO.md, DEPLOYMENT_SUCCESS.md)
- Add .env.example configuration template
- Add requirements.txt for Python dependencies
- Update README.md with latest information
- Update core/server.py with improvements

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
你的用户名
2025-11-04 15:19:44 +08:00
parent a05a7dd40e
commit c4be264ea5
9 changed files with 911 additions and 1 deletions

15
.env.example Normal file
View File

@@ -0,0 +1,15 @@
# Telegram API 配置
TELEGRAM_API_ID=your_api_id
TELEGRAM_API_HASH=your_api_hash
TELEGRAM_SESSION_PATH=~/telegram_sessions/funstat_bot
FUNSTAT_BOT_USERNAME=@openaiw_bot
# 代理配置(如果服务器无法直接访问 Telegram需要配置代理
FUNSTAT_PROXY_TYPE=socks5
FUNSTAT_PROXY_HOST=127.0.0.1
FUNSTAT_PROXY_PORT=1080
# 服务器配置
# 监听地址: 0.0.0.0 表示所有网络接口127.0.0.1 表示仅本地访问
FUNSTAT_HOST=0.0.0.0
FUNSTAT_PORT=8091

177
DEPLOYMENT_INFO.md Normal file
View File

@@ -0,0 +1,177 @@
# Funstat MCP 部署信息
## 服务器信息
- **服务器 IP**: 172.16.74.159
- **用户**: atai
- **部署路径**: /home/atai/funstat-mcp
- **端口**: 8091 (本地监听)
## 部署状态
**部署成功** - 2025-11-02
### 已安装组件
- Python 3.12.3
- 虚拟环境: /home/atai/funstat-mcp/.venv
- MCP 服务器: v1.20.0
- Telethon: v1.41.2
- 代理: v2ray (SOCKS5 on 127.0.0.1:1080)
### 配置文件
- **环境变量**: /home/atai/funstat-mcp/.env
- **Session 文件**: ~/telegram_sessions/funstat_bot.session
- **启动脚本**: /home/atai/funstat-mcp/core/start_server_with_env.sh
## 服务管理
### 启动服务
```bash
ssh atai@172.16.74.159
cd /home/atai/funstat-mcp/core
bash start_server_with_env.sh
```
### 停止服务
```bash
ssh atai@172.16.74.159
pkill -f 'funstat.*server.py'
```
### 查看日志
```bash
ssh atai@172.16.74.159
tail -f /tmp/funstat_sse.log
```
### 检查状态
```bash
ssh atai@172.16.74.159
ps aux | grep python | grep server.py
netstat -tuln | grep 8091 # 或 ss -tuln | grep 8091
```
## 服务端点
- **SSE 端点**: http://172.16.74.159:8091/sse
- **消息端点**: http://172.16.74.159:8091/messages
- **内部访问**: http://127.0.0.1:8091/sse
✅ 服务监听 0.0.0.0:8091可从任何网络接口访问。
## 环境变量
当前配置在 `/home/atai/funstat-mcp/.env`:
```bash
# Telegram API 配置
TELEGRAM_API_ID=24660516
TELEGRAM_API_HASH=eae564578880a59c9963916ff1bbbd3a
TELEGRAM_SESSION_PATH=~/telegram_sessions/funstat_bot
FUNSTAT_BOT_USERNAME=@openaiw_bot
# 代理配置
FUNSTAT_PROXY_TYPE=socks5
FUNSTAT_PROXY_HOST=127.0.0.1
FUNSTAT_PROXY_PORT=1080
# 服务器配置
FUNSTAT_HOST=0.0.0.0
FUNSTAT_PORT=8091
```
## 重新部署
从本地更新代码到服务器:
```bash
cd /Users/hahaha/projects/funstat-mcp
bash deploy.sh
```
部署脚本会自动:
1. 打包项目文件
2. 上传到服务器
3. 停止旧服务
4. 解压更新文件
5. 安装依赖
部署后需要手动启动服务:
```bash
ssh atai@172.16.74.159
cd /home/atai/funstat-mcp/core
bash start_server_with_env.sh
```
## 故障排除
### 1. 服务无法启动
检查日志:
```bash
tail -50 /tmp/funstat_sse.log
```
### 2. 无法连接 Telegram
检查代理是否运行:
```bash
ps aux | grep v2ray
netstat -tuln | grep 1080
```
### 3. Session 文件锁定
强制停止并重启:
```bash
pkill -9 -f 'funstat.*server.py'
sleep 2
cd /home/atai/funstat-mcp/core
bash start_server_with_env.sh
```
## 运行日志示例
成功启动的日志应该包含:
```
✅ 已连接到: KT超级数据
✅ 当前账号: @xiaobai_80
🚀 Funstat MCP Server 已启动
🌐 启动 SSE 服务器: http://127.0.0.1:8091
Uvicorn running on http://127.0.0.1:8091
```
## 安全注意事项
1. ⚠️ .env 文件包含敏感信息,已设置权限保护
2. ⚠️ Session 文件包含登录凭证,定期备份
3. ⚠️ 服务监听 0.0.0.0:8091可从任何网络访问请注意安全
4. ⚠️ 服务器需要使用 v2ray 代理访问 Telegram端口 1080确保代理服务稳定
5. ⚠️ 必须使用 start_server_with_env.sh 启动脚本,才能正确加载环境变量和代理配置
6. ⚠️ 建议在生产环境配置防火墙规则限制访问来源IP
## 更新历史
- **2025-11-02**: 初次部署成功
- 安装所有依赖(包括 PySocks
- 配置 v2ray 代理socks5://127.0.0.1:1080
- 创建带环境变量的启动脚本
- 配置服务监听 0.0.0.0:8091支持外部访问
- 服务正常运行
- ⚠️ 注意:服务器无法直接访问 Telegram必须使用代理
- ✅ 可从任何网络通过 http://172.16.74.159:8091 访问
## 联系信息
- 服务器: 172.16.74.159:22
- 账号: atai
- Bot: @openaiw_bot
- 当前 Telegram 账号: @xiaobai_80

252
DEPLOYMENT_SUCCESS.md Normal file
View File

@@ -0,0 +1,252 @@
# Funstat MCP 部署成功报告
## ✅ 部署状态:成功
**部署时间**: 2025-11-02
**服务器**: 172.16.74.159 (atai)
**服务进程**: PID 105552
---
## 🌐 访问信息
### 外部访问
- **SSE 端点**: http://172.16.74.159:8091/sse
- **消息端点**: http://172.16.74.159:8091/messages
### 内部访问(服务器内)
- http://127.0.0.1:8091/sse
- http://127.0.0.1:8091/messages
### 监听配置
- **监听地址**: 0.0.0.0 (所有网络接口)
- **监听端口**: 8091
- **协议**: HTTP + SSE
---
## ✅ 服务状态
```bash
# 进程状态
PID: 105552
运行中: ✅
内存使用: 78304 KB
# 网络状态
端口: 0.0.0.0:8091 LISTEN
防火墙: inactive (无限制)
# Telegram 连接
状态: ✅ 已连接
Bot: @openaiw_bot (KT超级数据)
账号: @xiaobai_80 (ID: 7363537082)
代理: socks5://127.0.0.1:1080
```
---
## 📋 部署清单
### 1. 系统环境
- [x] Ubuntu 24.04.3 LTS
- [x] Python 3.12.3
- [x] v2ray 代理运行中 (端口 1080)
### 2. 项目部署
- [x] 代码部署到 /home/atai/funstat-mcp
- [x] 虚拟环境创建 (.venv)
- [x] 依赖包安装完成 (mcp, telethon, starlette, uvicorn, PySocks, etc.)
### 3. 配置文件
- [x] .env 环境变量配置
- [x] Session 文件 (~/telegram_sessions/funstat_bot.session)
- [x] 启动脚本 (start_server_with_env.sh)
### 4. 服务配置
- [x] 监听地址: 0.0.0.0:8091
- [x] 代理配置: socks5://127.0.0.1:1080
- [x] 自动重启脚本
---
## 🔧 管理命令
### 启动服务
```bash
ssh atai@172.16.74.159
cd /home/atai/funstat-mcp/core
bash start_server_with_env.sh
```
### 停止服务
```bash
ssh atai@172.16.74.159
pkill -f 'funstat.*server.py'
```
### 查看日志
```bash
ssh atai@172.16.74.159
tail -f /tmp/funstat_sse.log
```
### 检查状态
```bash
ssh atai@172.16.74.159
ps aux | grep 'python.*server.py' | grep -v grep
ss -tuln | grep 8091
```
### 测试访问
```bash
# 从服务器内部测试
ssh atai@172.16.74.159
curl -s -o /dev/null -w '%{http_code}\n' http://172.16.74.159:8091/sse
# 从本地测试(如果网络可达)
curl -s -o /dev/null -w '%{http_code}\n' http://172.16.74.159:8091/sse
```
---
## 🔄 重新部署
### 方法 1: 使用部署脚本(本地执行)
```bash
cd /Users/hahaha/projects/funstat-mcp
bash deploy.sh
```
然后登录服务器启动:
```bash
ssh atai@172.16.74.159
cd /home/atai/funstat-mcp/core
bash start_server_with_env.sh
```
### 方法 2: 手动部署
```bash
# 1. 打包代码
cd /Users/hahaha/projects/funstat-mcp
tar --exclude='.git' --exclude='.venv' -czf /tmp/funstat-mcp.tar.gz .
# 2. 上传到服务器
scp /tmp/funstat-mcp.tar.gz atai@172.16.74.159:/tmp/
# 3. 在服务器上解压并重启
ssh atai@172.16.74.159
cd /home/atai/funstat-mcp
tar -xzf /tmp/funstat-mcp.tar.gz
cd core
bash start_server_with_env.sh
```
---
## ⚠️ 重要说明
### 网络要求
- ⚠️ **服务器无法直接访问 Telegram**,必须通过 v2ray 代理(端口 1080
- ✅ v2ray 代理已配置并运行正常
- ✅ 服务已配置使用 socks5://127.0.0.1:1080 代理
### 代理依赖
如果代理服务停止Telegram 连接将失败。检查代理状态:
```bash
ssh atai@172.16.74.159
ps aux | grep v2ray
ss -tuln | grep 1080
```
### Session 文件
- 路径: ~/telegram_sessions/funstat_bot.session
- 大小: 72KB
- 权限: 600 (仅所有者可读写)
- ⚠️ 包含登录凭证,务必保管好
### 防火墙
- 当前状态: inactive (无防火墙限制)
- 端口 8091 对所有网络开放
- 如需限制访问,可配置 ufw 规则
---
## 📊 访问日志示例
服务器日志显示已有外部访问:
```
INFO: 172.16.72.87:61462 - "GET /sse HTTP/1.1" 406 Not Acceptable
INFO: 172.16.74.159:58984 - "GET /sse HTTP/1.1" 406 Not Acceptable
INFO: 172.16.72.87:61532 - "GET /sse HTTP/1.1" 200 OK
```
- 406 响应: 正常,表示客户端未发送正确的 Accept 头
- 200 响应: 成功建立 SSE 连接
---
## 🛡️ 安全建议
1. ✅ Session 文件已设置权限保护 (600)
2. ✅ .env 文件包含敏感信息,不要提交到 Git
3. ⚠️ 服务监听 0.0.0.0,建议配置防火墙限制访问 IP
4. ⚠️ 定期备份 session 文件
5. ⚠️ 确保 v2ray 代理服务稳定运行
### 推荐的防火墙配置(可选)
```bash
sudo ufw allow from <your-ip>/32 to any port 8091
sudo ufw enable
```
---
## 📞 技术支持
如遇问题,按以下顺序排查:
1. **服务未启动**
```bash
cd /home/atai/funstat-mcp/core
bash start_server_with_env.sh
```
2. **无法连接 Telegram**
- 检查 v2ray 代理: `ps aux | grep v2ray`
- 检查代理端口: `ss -tuln | grep 1080`
- 查看服务日志: `tail -50 /tmp/funstat_sse.log`
3. **Session 文件锁定**
```bash
pkill -9 python3
sleep 2
cd /home/atai/funstat-mcp/core
bash start_server_with_env.sh
```
4. **依赖包缺失**
```bash
cd /home/atai/funstat-mcp
source .venv/bin/activate
pip install -r requirements.txt
```
---
## ✅ 验证清单
部署完成后,确认以下项目:
- [x] 服务进程正在运行
- [x] 端口 8091 正在监听
- [x] Telegram 连接成功
- [x] SSE 端点响应正常
- [x] 日志文件正常记录
- [x] 可从外部访问(如果网络可达)
---
**部署完成时间**: 2025-11-02
**状态**: ✅ 成功运行
**下次维护**: 定期检查日志和代理状态

View File

@@ -49,7 +49,7 @@ funstat_mcp_package/
## 🎯 核心功能
### MCP 工具列表 (8个)
### MCP 工具列表 (9个)
| 工具名 | 功能 | 对应命令 |
|--------|------|---------|
@@ -61,6 +61,7 @@ funstat_mcp_package/
| `funstat_text` | 搜索消息内容 | `/text [关键词]` |
| `funstat_human` | 搜索用户 | `/human [关键词]` |
| `funstat_user_info` | 查询用户详情 | `/user_info [username]` |
| `funstat_user_messages` | 获取用户聊天记录(自动翻页) | `/user_info [username] ➜ Messages ➜ All` |
### 协议支持

View File

@@ -25,6 +25,8 @@ from mcp.types import (
)
from pydantic import AnyUrl
from telethon import TelegramClient
from telethon.errors import FloodWaitError
from telethon.tl.functions.messages import GetBotCallbackAnswerRequest
from telethon.tl.types import Message
# 配置日志
@@ -56,6 +58,40 @@ RATE_LIMIT_WINDOW = 1.0 # 1秒时间窗口
# 缓存配置
CACHE_TTL = 3600 # 缓存1小时
# 按钮文本转换表,用于将常见的变体字符标准化为 ASCII
BUTTON_TEXT_TRANSLATIONS = str.maketrans({
'ƒ': 'f',
'Μ': 'M',
'τ': 't',
'ѕ': 's',
'η': 'n',
'Ғ': 'F',
'α': 'a',
'ο': 'o',
'': 'u',
'о': 'o',
'е': 'e',
'с': 'c',
'': 'e',
'Τ': 'T',
'ρ': 'p',
'Δ': 'D',
'χ': 'x',
'β': 'b',
'λ': 'l',
'γ': 'y',
'Ν': 'N',
'μ': 'm',
'ψ': 'y',
'Α': 'A',
'Ρ': 'P',
'С': 'C',
'ё': 'e',
'ł': 'l',
'Ł': 'L',
'ց': 'g',
})
class RateLimiter:
"""速率限制器"""
@@ -136,6 +172,123 @@ class FunstatMCPServer:
self.server.list_tools()(self.list_tools)
self.server.call_tool()(self.call_tool)
def _normalize_button_text(self, text: str) -> str:
"""标准化按钮文本,消除不同字符集的影响"""
return text.translate(BUTTON_TEXT_TRANSLATIONS)
async def _press_button(self, message: Message, keyword: str) -> Message:
"""在消息中查找包含关键字的按钮并触发"""
if not message.buttons:
raise ValueError(f"消息中缺少可用按钮,无法执行 {keyword} 操作")
target_button = None
normalized_keyword = keyword.lower()
for row in message.buttons:
for button in row:
normalized_text = self._normalize_button_text(button.text).lower()
if normalized_keyword in normalized_text:
target_button = button
break
if target_button:
break
if not target_button:
available = [
self._normalize_button_text(button.text)
for row in message.buttons
for button in row
]
raise ValueError(
f"未找到包含关键字 '{keyword}' 的按钮。可用按钮: {available}"
)
await self.rate_limiter.acquire()
try:
await self.client(
GetBotCallbackAnswerRequest(
peer=self.bot_entity,
msg_id=message.id,
data=target_button.data,
)
)
except FloodWaitError as exc:
wait_seconds = exc.seconds + 1
logger.warning("触发 Telegram FloodWait需要等待 %s", wait_seconds)
await asyncio.sleep(wait_seconds)
await self.client(
GetBotCallbackAnswerRequest(
peer=self.bot_entity,
msg_id=message.id,
data=target_button.data,
)
)
await asyncio.sleep(1.2)
refreshed = await self.client.get_messages(self.bot_entity, ids=message.id)
if not refreshed:
raise RuntimeError("回调执行后未能获取最新消息内容")
return refreshed
def _extract_total_pages(self, message: Message) -> Optional[int]:
"""从按钮中提取总页数(如果提供了跳页按钮)"""
if not message.buttons:
return None
total_pages = None
for row in message.buttons:
for button in row:
if '' in button.text:
normalized = self._normalize_button_text(button.text)
digits = ''.join(ch for ch in normalized if ch.isdigit())
if digits:
try:
total_pages = int(digits)
except ValueError:
continue
return total_pages
async def send_command_and_wait_message(
self,
command: str,
timeout: int = 10,
) -> Message:
"""发送命令并返回原始消息对象(包含按钮等信息)"""
if not self.client or not self.bot_entity:
raise RuntimeError("Telegram 客户端尚未初始化")
await self.rate_limiter.acquire()
last_message_id = 0
async for message in self.client.iter_messages(self.bot_entity, limit=1):
last_message_id = message.id
break
logger.info("📤 发送命令(原始消息模式): %s", command)
await self.client.send_message(self.bot_entity, command)
await asyncio.sleep(1.5)
start_time = time.time()
while time.time() - start_time < timeout:
async for message in self.client.iter_messages(self.bot_entity, limit=5):
if message.id <= last_message_id:
continue
if message.out:
continue
if message.text or message.buttons:
logger.info(
"✅ 收到原始响应 (ID: %s, 文本长度: %s)",
message.id,
len(message.text or ""),
)
return message
await asyncio.sleep(0.5)
raise TimeoutError(f"等待 BOT 响应超时 ({timeout}秒)")
async def initialize(self):
"""初始化 Telegram 客户端"""
logger.info("初始化 Telegram 客户端...")
@@ -246,6 +399,99 @@ class FunstatMCPServer:
raise TimeoutError(f"等待 BOT 响应超时 ({timeout}秒)")
async def fetch_user_messages(
self,
identifier: str,
max_pages: Optional[int] = None
) -> str:
"""获取指定用户的历史消息,支持自动翻页"""
if not identifier or not identifier.strip():
raise ValueError("用户标识不能为空")
identifier = identifier.strip()
display_identifier = identifier
if identifier.startswith("/"):
command = identifier
else:
if not identifier.startswith("@") and not identifier.replace("+", "").isdigit():
identifier = f"@{identifier}"
display_identifier = identifier
command = f"/user_info {identifier}"
logger.info("开始获取用户消息: %s", display_identifier)
base_message = await self.send_command_and_wait_message(command, timeout=15)
message_stage = await self._press_button(base_message, "messages")
all_stage = await self._press_button(message_stage, "all")
collected_pages: List[str] = []
seen_texts: set[str] = set()
current_message = all_stage
current_page = 1
total_pages = self._extract_total_pages(current_message)
if max_pages is not None and max_pages <= 0:
raise ValueError("max_pages 必须大于 0")
while True:
page_text = current_message.text or ""
normalized_text = page_text.strip()
if normalized_text and normalized_text not in seen_texts:
header_parts = [f"{current_page}"]
if total_pages:
header_parts[-1] += f"/{total_pages}"
header_parts.append(f"用户: {display_identifier}")
collected_pages.append(
"\n".join(header_parts + ["", page_text.strip()])
)
seen_texts.add(normalized_text)
if max_pages and current_page >= max_pages:
logger.info("达到 max_pages 限制,停止翻页")
break
next_button = None
if current_message.buttons:
for row in current_message.buttons:
for button in row:
if "" in button.text:
next_button = button
break
if next_button:
break
if not next_button:
break
logger.info("翻到第 %s 页 (目标按钮: %s)", current_page + 1, next_button.text)
try:
current_message = await self._press_button(current_message, "")
except ValueError:
logger.warning("未能找到下一页按钮,提前结束翻页")
break
# 如果返回的内容与上一页一致,则终止
if (current_message.text or "").strip() in seen_texts:
logger.info("检测到重复页面内容,结束翻页")
break
current_page += 1
if not collected_pages:
return f"未找到 {display_identifier} 的消息记录。"
summary_lines = [
f"共收集 {len(collected_pages)} 页消息"
+ (f"(存在 {total_pages} 页)" if total_pages else ""),
""
]
return "\n\n".join(summary_lines + collected_pages)
async def list_tools(self) -> List[Tool]:
"""列出所有可用工具"""
return [
@@ -318,6 +564,25 @@ class FunstatMCPServer:
"required": ["identifier"]
}
),
Tool(
name="funstat_user_messages",
description="获取指定用户的历史消息列表,并自动翻页汇总",
inputSchema={
"type": "object",
"properties": {
"identifier": {
"type": "string",
"description": "用户标识: 用户名(@username) 或用户ID"
},
"max_pages": {
"type": "integer",
"minimum": 1,
"description": "可选,限制抓取的最大页数"
}
},
"required": ["identifier"]
}
),
Tool(
name="funstat_balance",
description="查询当前账号的积分余额和使用统计",
@@ -381,6 +646,12 @@ class FunstatMCPServer:
response = await self.send_command_and_wait(f"/user_info {identifier}")
return [TextContent(type="text", text=response)]
elif name == "funstat_user_messages":
identifier = arguments["identifier"]
max_pages = arguments.get("max_pages")
response = await self.fetch_user_messages(identifier, max_pages=max_pages)
return [TextContent(type="text", text=response)]
elif name == "funstat_balance":
response = await self.send_command_and_wait("/balance")
return [TextContent(type="text", text=response)]

64
core/start_server.sh Executable file
View File

@@ -0,0 +1,64 @@
#!/bin/bash
# Funstat MCP 服务器启动脚本(适配服务器环境)
set -e
cd "$(dirname "$0")"
# 停止旧实例
echo "🛑 停止旧服务器..."
pkill -f "funstat.*server.py" 2>/dev/null || true
sleep 2
# 确保 session 文件没有被锁定
SESSION_FILE=~/telegram_sessions/funstat_bot.session
if [ -f "$SESSION_FILE" ]; then
if lsof "$SESSION_FILE" 2>/dev/null; then
echo "⚠️ Session 文件被占用,强制终止..."
pkill -9 -f "funstat.*server.py" || true
sleep 2
fi
else
echo "❌ Session 文件不存在: $SESSION_FILE"
echo "请先上传 session 文件!"
exit 1
fi
# 激活虚拟环境
source ../.venv/bin/activate
# 启动新服务器(后台运行)
echo "🚀 启动新服务器..."
nohup python3 server.py > /tmp/funstat_sse.log 2>&1 &
SERVER_PID=$!
# 等待启动
sleep 3
# 验证启动
if ps -p $SERVER_PID > /dev/null 2>&1; then
echo "✅ 服务器已启动 (PID: $SERVER_PID)"
echo "📡 SSE 端点: http://127.0.0.1:8091/sse"
echo "📋 日志文件: /tmp/funstat_sse.log"
# 测试端点
echo ""
echo "🧪 测试端点..."
if curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:8091/sse | grep -q "200"; then
echo "✅ GET /sse 测试通过"
else
echo "❌ GET /sse 测试失败"
fi
echo ""
echo "📊 服务器状态:"
echo " 进程ID: $SERVER_PID"
echo " 监听地址: http://127.0.0.1:8091"
echo " 日志: tail -f /tmp/funstat_sse.log"
echo ""
echo "停止服务: pkill -f 'funstat.*server.py'"
else
echo "❌ 服务器启动失败!"
echo "查看日志: tail -50 /tmp/funstat_sse.log"
exit 1
fi

77
deploy.sh Executable file
View File

@@ -0,0 +1,77 @@
#!/bin/bash
# Funstat MCP 服务器部署脚本
set -e
SERVER_IP="172.16.74.159"
SERVER_USER="atai"
SERVER_PATH="/home/atai/funstat-mcp"
LOCAL_PATH="/Users/hahaha/projects/funstat-mcp"
echo "🚀 开始部署 Funstat MCP 到服务器..."
# 1. 打包项目文件(排除不需要的文件)
echo "📦 打包项目文件..."
cd "$LOCAL_PATH"
tar --exclude='.git' \
--exclude='.venv' \
--exclude='__pycache__' \
--exclude='*.pyc' \
--exclude='.DS_Store' \
--exclude='customer_data' \
-czf /tmp/funstat-mcp.tar.gz .
# 2. 上传到服务器
echo "⬆️ 上传文件到服务器..."
sshpass -p "wengewudi666808" scp /tmp/funstat-mcp.tar.gz ${SERVER_USER}@${SERVER_IP}:/tmp/
# 3. 在服务器上部署
echo "🔧 在服务器上部署..."
sshpass -p "wengewudi666808" ssh ${SERVER_USER}@${SERVER_IP} << 'ENDSSH'
set -e
# 创建部署目录
mkdir -p /home/atai/funstat-mcp
cd /home/atai/funstat-mcp
# 停止旧服务
echo "🛑 停止旧服务..."
pkill -f "funstat.*server.py" 2>/dev/null || true
sleep 2
# 解压新文件
echo "📂 解压新文件..."
tar -xzf /tmp/funstat-mcp.tar.gz -C /home/atai/funstat-mcp
rm /tmp/funstat-mcp.tar.gz
# 创建虚拟环境(如果不存在)
if [ ! -d ".venv" ]; then
echo "🔨 创建虚拟环境..."
python3 -m venv .venv
fi
# 激活虚拟环境并安装依赖
echo "📥 安装依赖..."
source .venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
# 创建 session 目录
mkdir -p ~/telegram_sessions
# 检查 session 文件
if [ ! -f ~/telegram_sessions/funstat_bot.session ]; then
echo "⚠️ 警告: Session 文件不存在"
echo "请确保已经上传 session 文件到 ~/telegram_sessions/funstat_bot.session"
fi
echo "✅ 部署完成!"
echo "启动服务: cd /home/atai/funstat-mcp/core && bash start_server.sh"
ENDSSH
echo ""
echo "✅ 部署完成!"
echo "下一步:"
echo "1. 确保 session 文件已上传到服务器: ~/telegram_sessions/funstat_bot.session"
echo "2. SSH 到服务器: ssh atai@172.16.74.159"
echo "3. 启动服务: cd /home/atai/funstat-mcp/core && bash start_server.sh"

8
requirements.txt Normal file
View File

@@ -0,0 +1,8 @@
mcp>=1.12.0
telethon>=1.34.0
starlette>=0.41.0
uvicorn>=0.29.0
httpx>=0.28.0
pydantic>=2.0.0
python-dotenv>=1.0.0
PySocks>=1.7.1

45
test_connection.sh Executable file
View File

@@ -0,0 +1,45 @@
#!/bin/bash
# 测试 Funstat MCP 服务器连接
SERVER_HOST="${1:-172.16.74.159}"
SERVER_PORT="${2:-8091}"
echo "测试 Funstat MCP 服务器连接..."
echo "服务器: $SERVER_HOST:$SERVER_PORT"
echo ""
# 测试 1: TCP 连接
echo "1. 测试 TCP 连接..."
if timeout 3 bash -c "cat < /dev/null > /dev/tcp/$SERVER_HOST/$SERVER_PORT" 2>/dev/null; then
echo " ✅ TCP 连接成功"
else
echo " ❌ TCP 连接失败"
exit 1
fi
# 测试 2: HTTP GET 请求
echo ""
echo "2. 测试 HTTP GET 请求..."
HTTP_CODE=$(curl -s -o /dev/null -w '%{http_code}' --connect-timeout 5 http://$SERVER_HOST:$SERVER_PORT/sse)
if [ "$HTTP_CODE" = "406" ] || [ "$HTTP_CODE" = "200" ]; then
echo " ✅ HTTP 响应: $HTTP_CODE (服务器正常)"
else
echo " ❌ HTTP 响应: $HTTP_CODE"
fi
# 测试 3: SSE 端点
echo ""
echo "3. 测试 SSE 端点Accept: text/event-stream..."
RESPONSE=$(timeout 3 curl -s -H "Accept: text/event-stream" http://$SERVER_HOST:$SERVER_PORT/sse 2>&1 | head -1)
if [ -n "$RESPONSE" ]; then
echo " ✅ SSE 端点响应: $RESPONSE"
else
echo " ⚠️ SSE 端点正在等待连接(正常)"
fi
echo ""
echo "✅ 服务器 $SERVER_HOST:$SERVER_PORT 可以正常访问!"
echo ""
echo "访问地址:"
echo " - SSE 端点: http://$SERVER_HOST:$SERVER_PORT/sse"
echo " - 消息端点: http://$SERVER_HOST:$SERVER_PORT/messages"