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_text` | 搜索消息内容 | `/text [关键词]` |
| `funstat_human` | 搜索用户 | `/human [关键词]` | | `funstat_human` | 搜索用户 | `/human [关键词]` |
| `funstat_user_info` | 查询用户详情 | `/user_info [username]` | | `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 pydantic import AnyUrl
from telethon import TelegramClient from telethon import TelegramClient
from telethon.errors import FloodWaitError
from telethon.tl.functions.messages import GetBotCallbackAnswerRequest
from telethon.tl.types import Message from telethon.tl.types import Message
# 配置日志 # 配置日志
@@ -56,6 +58,40 @@ RATE_LIMIT_WINDOW = 1.0 # 1秒时间窗口
# 缓存配置 # 缓存配置
CACHE_TTL = 3600 # 缓存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: class RateLimiter:
"""速率限制器""" """速率限制器"""
@@ -136,6 +172,123 @@ class FunstatMCPServer:
self.server.list_tools()(self.list_tools) self.server.list_tools()(self.list_tools)
self.server.call_tool()(self.call_tool) 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): async def initialize(self):
"""初始化 Telegram 客户端""" """初始化 Telegram 客户端"""
logger.info("初始化 Telegram 客户端...") logger.info("初始化 Telegram 客户端...")
@@ -246,6 +399,99 @@ class FunstatMCPServer:
raise TimeoutError(f"等待 BOT 响应超时 ({timeout}秒)") 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]: async def list_tools(self) -> List[Tool]:
"""列出所有可用工具""" """列出所有可用工具"""
return [ return [
@@ -318,6 +564,25 @@ class FunstatMCPServer:
"required": ["identifier"] "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( Tool(
name="funstat_balance", name="funstat_balance",
description="查询当前账号的积分余额和使用统计", description="查询当前账号的积分余额和使用统计",
@@ -381,6 +646,12 @@ class FunstatMCPServer:
response = await self.send_command_and_wait(f"/user_info {identifier}") response = await self.send_command_and_wait(f"/user_info {identifier}")
return [TextContent(type="text", text=response)] 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": elif name == "funstat_balance":
response = await self.send_command_and_wait("/balance") response = await self.send_command_and_wait("/balance")
return [TextContent(type="text", text=response)] 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"