新增webhook url参数

This commit is contained in:
2025-10-29 12:06:18 +08:00
parent 003ff9a94e
commit 38e81dbfe6
8 changed files with 715 additions and 18 deletions

View File

@@ -89,24 +89,58 @@ pip install -r requirements.txt
# 注意成语接龙游戏需要pypinyin库进行拼音处理 # 注意成语接龙游戏需要pypinyin库进行拼音处理
``` ```
3. **配置环境变量** 3. **配置Webhook**
有三种方式配置Webhook URL
#### 方式1命令行参数推荐
```bash
# Linux/Mac
python app.py --webhook-url "https://xz.wps.cn/api/v1/webhook/send?key=your_key"
# Windows
python app.py --webhook-url "https://xz.wps.cn/api/v1/webhook/send?key=your_key"
```
#### 方式2使用启动脚本
```bash
# Linux/Mac
./start.sh -w "https://xz.wps.cn/api/v1/webhook/send?key=your_key"
# Windows
start.bat -w "https://xz.wps.cn/api/v1/webhook/send?key=your_key"
```
#### 方式3环境变量
```bash ```bash
# 复制配置文件模板 # 复制配置文件模板
cp env.example .env cp config.env.example config.env
# 编辑配置文件填入你的Webhook URL # 编辑配置文件填入你的Webhook URL
nano .env nano config.env
``` ```
4. **运行应用** 4. **运行应用**
```bash ```bash
# 开发模式 # 基本启动
python app.py python app.py
# 自定义参数启动
python app.py --webhook-url "your_webhook_url" --port 8080 --log-level debug
# 使用启动脚本
./start.sh -w "your_webhook_url" -p 8080 -l debug
# 生产模式使用uvicorn # 生产模式使用uvicorn
uvicorn app:app --host 0.0.0.0 --port 8000 --workers 1 uvicorn app:app --host 0.0.0.0 --port 8000 --workers 1
``` ```
### 命令行参数说明
- `--webhook-url, -w`: WPS Webhook URL
- `--host, -H`: 服务器主机地址 (默认: 0.0.0.0)
- `--port, -p`: 服务器端口 (默认: 11000)
- `--workers`: 工作进程数 (默认: 1)
- `--log-level`: 日志级别 (默认: info)
## 📝 配置说明 ## 📝 配置说明
### 环境变量 ### 环境变量

243
STARTUP_GUIDE.md Normal file
View File

@@ -0,0 +1,243 @@
# WPS Bot Game 启动方式说明
## 🚀 多种启动方式
### 1. 命令行参数启动(推荐)
#### 基本启动
```bash
python app.py --webhook-url "https://xz.wps.cn/api/v1/webhook/send?key=your_key"
```
#### 自定义参数启动
```bash
python app.py \
--webhook-url "https://xz.wps.cn/api/v1/webhook/send?key=your_key" \
--host 0.0.0.0 \
--port 8080 \
--workers 1 \
--log-level debug
```
#### 短参数形式
```bash
python app.py -w "https://xz.wps.cn/api/v1/webhook/send?key=your_key" -p 8080 -l debug
```
### 2. 使用启动脚本
#### Linux/Mac
```bash
# 基本启动
./start.sh -w "https://xz.wps.cn/api/v1/webhook/send?key=your_key"
# 自定义参数
./start.sh -w "your_webhook_url" -p 8080 -l debug
# 查看帮助
./start.sh --help
```
#### Windows
```cmd
REM 基本启动
start.bat -w "https://xz.wps.cn/api/v1/webhook/send?key=your_key"
REM 自定义参数
start.bat -w "your_webhook_url" -p 8080 -l debug
REM 查看帮助
start.bat --help
```
### 3. 环境变量启动
#### 设置环境变量
```bash
# Linux/Mac
export WEBHOOK_URL="https://xz.wps.cn/api/v1/webhook/send?key=your_key"
export HOST="0.0.0.0"
export PORT="8080"
export LOG_LEVEL="debug"
# Windows
set WEBHOOK_URL=https://xz.wps.cn/api/v1/webhook/send?key=your_key
set HOST=0.0.0.0
set PORT=8080
set LOG_LEVEL=debug
```
#### 启动应用
```bash
python app.py
```
### 4. 配置文件启动
#### 创建配置文件
```bash
# 复制配置模板
cp config.env.example config.env
# 编辑配置文件
nano config.env
```
#### 配置文件内容
```env
WEBHOOK_URL=https://xz.wps.cn/api/v1/webhook/send?key=your_key
HOST=0.0.0.0
PORT=8080
WORKERS=1
LOG_LEVEL=debug
```
#### 启动应用
```bash
python app.py
```
## 📋 参数说明
| 参数 | 短参数 | 默认值 | 说明 |
|------|--------|--------|------|
| `--webhook-url` | `-w` | 无 | WPS Webhook URL |
| `--host` | `-H` | `0.0.0.0` | 服务器主机地址 |
| `--port` | `-p` | `11000` | 服务器端口 |
| `--workers` | 无 | `1` | 工作进程数 |
| `--log-level` | `-l` | `info` | 日志级别 |
## 🔧 生产环境部署
### 使用systemd服务
```bash
# 编辑服务文件
sudo nano /etc/systemd/system/wps-bot.service
# 服务文件内容
[Unit]
Description=WPS Bot Game
After=network.target
[Service]
Type=simple
User=your_user
WorkingDirectory=/path/to/WPSBotGame
ExecStart=/usr/bin/python3 app.py --webhook-url "your_webhook_url" --port 11000
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
```
### 使用Docker
```dockerfile
FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "app.py", "--webhook-url", "your_webhook_url"]
```
### 使用Nginx反向代理
```nginx
server {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://127.0.0.1:11000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
## 🐛 故障排除
### 1. 端口被占用
```bash
# 查看端口占用
netstat -tlnp | grep :11000
# 杀死占用进程
sudo kill -9 PID
# 或使用其他端口
python app.py --port 8080
```
### 2. 权限问题
```bash
# 给启动脚本执行权限
chmod +x start.sh
# 检查文件权限
ls -la start.sh
```
### 3. 依赖问题
```bash
# 检查Python版本
python --version
# 安装依赖
pip install -r requirements.txt
# 检查依赖
python -c "import fastapi, uvicorn"
```
### 4. 网络问题
```bash
# 测试Webhook URL
curl -X POST "your_webhook_url" \
-H "Content-Type: application/json" \
-d '{"msg_type": "text", "content": {"text": "test"}}'
# 检查防火墙
sudo ufw status
sudo ufw allow 11000
```
## 📊 监控和日志
### 查看日志
```bash
# 实时查看日志
tail -f /var/log/wps-bot.log
# 查看系统日志
journalctl -u wps-bot -f
# 查看错误日志
grep ERROR /var/log/wps-bot.log
```
### 健康检查
```bash
# 检查服务状态
curl http://localhost:11000/health
# 检查统计信息
curl http://localhost:11000/stats
```
### 性能监控
```bash
# 查看进程状态
ps aux | grep python
# 查看内存使用
free -h
# 查看磁盘使用
df -h
```

41
app.py
View File

@@ -1,5 +1,7 @@
"""WPS Bot Game - FastAPI主应用""" """WPS Bot Game - FastAPI主应用"""
import logging import logging
import argparse
import os
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
@@ -94,14 +96,45 @@ async def global_exception_handler(request, exc):
) )
def parse_args():
"""解析命令行参数"""
parser = argparse.ArgumentParser(description='WPS Bot Game')
parser.add_argument('--webhook-url', '-w',
help='WPS Webhook URL')
parser.add_argument('--host', '-H',
default='0.0.0.0',
help='服务器主机地址 (默认: 0.0.0.0)')
parser.add_argument('--port', '-p',
type=int, default=11000,
help='服务器端口 (默认: 11000)')
parser.add_argument('--workers',
type=int, default=1,
help='工作进程数 (默认: 1)')
parser.add_argument('--log-level',
default='info',
choices=['debug', 'info', 'warning', 'error'],
help='日志级别 (默认: info)')
return parser.parse_args()
if __name__ == "__main__": if __name__ == "__main__":
import uvicorn import uvicorn
# 解析命令行参数
args = parse_args()
# 如果提供了webhook URL设置环境变量
if args.webhook_url:
os.environ['WEBHOOK_URL'] = args.webhook_url
logger.info(f"设置Webhook URL: {args.webhook_url}")
# 启动服务器
uvicorn.run( uvicorn.run(
"app:app", "app:app",
host="0.0.0.0", host=args.host,
port=11000, port=args.port,
workers=1, workers=args.workers,
limit_concurrency=5, limit_concurrency=5,
log_level="info" log_level=args.log_level
) )

21
config.env.example Normal file
View File

@@ -0,0 +1,21 @@
# WPS Bot Game 配置文件示例
# 复制此文件为 config.env 并修改相应配置
# WPS Webhook URL (必需)
# 格式: https://xz.wps.cn/api/v1/webhook/send?key=your_key
WEBHOOK_URL=https://xz.wps.cn/api/v1/webhook/send?key=your_key_here
# 服务器配置
HOST=0.0.0.0
PORT=11000
WORKERS=1
# 日志配置
LOG_LEVEL=info
# 数据库配置
DATABASE_URL=sqlite:///data/bot.db
# 其他配置
SESSION_TIMEOUT=300
CONCURRENCY_LIMIT=5

View File

@@ -33,12 +33,25 @@ class Database:
def conn(self) -> sqlite3.Connection: def conn(self) -> sqlite3.Connection:
"""获取数据库连接(懒加载)""" """获取数据库连接(懒加载)"""
if self._conn is None: if self._conn is None:
self._conn = sqlite3.connect( try:
self.db_path, self._conn = sqlite3.connect(
check_same_thread=False, # 允许多线程访问 self.db_path,
isolation_level=None # 自动提交 check_same_thread=False, # 允许多线程访问
) isolation_level=None, # 自动提交
self._conn.row_factory = sqlite3.Row # 支持字典式访问 timeout=30.0 # 增加超时时间
)
self._conn.row_factory = sqlite3.Row # 支持字典式访问
# 启用WAL模式以提高并发性能
self._conn.execute("PRAGMA journal_mode=WAL")
self._conn.execute("PRAGMA synchronous=NORMAL")
self._conn.execute("PRAGMA cache_size=1000")
self._conn.execute("PRAGMA temp_store=MEMORY")
logger.info(f"数据库连接成功: {self.db_path}")
except Exception as e:
logger.error(f"数据库连接失败: {e}", exc_info=True)
raise
return self._conn return self._conn
def init_tables(self): def init_tables(self):
@@ -416,12 +429,16 @@ class Database:
是否成功 是否成功
""" """
if points <= 0: if points <= 0:
logger.warning(f"积分数量无效: {points}")
return False return False
cursor = self.conn.cursor() cursor = self.conn.cursor()
current_time = int(time.time()) current_time = int(time.time())
try: try:
# 确保用户存在
self.get_or_create_user(user_id)
# 更新用户积分 # 更新用户积分
cursor.execute(""" cursor.execute("""
INSERT INTO user_points (user_id, total_points, available_points, created_at, updated_at) INSERT INTO user_points (user_id, total_points, available_points, created_at, updated_at)
@@ -433,17 +450,24 @@ class Database:
updated_at = ? updated_at = ?
""", (user_id, points, points, current_time, current_time, points, points, current_time)) """, (user_id, points, points, current_time, current_time, points, points, current_time))
# 验证积分是否真的更新了
cursor.execute("SELECT available_points FROM user_points WHERE user_id = ?", (user_id,))
updated_points = cursor.fetchone()
if not updated_points:
logger.error(f"积分更新后查询失败: user_id={user_id}")
return False
# 记录积分变动 # 记录积分变动
cursor.execute( cursor.execute(
"INSERT INTO points_records (user_id, points, source, description, created_at) VALUES (?, ?, ?, ?, ?)", "INSERT INTO points_records (user_id, points, source, description, created_at) VALUES (?, ?, ?, ?, ?)",
(user_id, points, source, description, current_time) (user_id, points, source, description, current_time)
) )
logger.debug(f"用户 {user_id} 获得 {points} 积分,来源:{source}") logger.info(f"用户 {user_id} 成功获得 {points} 积分,来源:{source},当前积分:{updated_points[0]}")
return True return True
except Exception as e: except Exception as e:
logger.error(f"添加积分失败: {e}") logger.error(f"添加积分失败: user_id={user_id}, points={points}, error={e}", exc_info=True)
return False return False
def consume_points(self, user_id: int, points: int, source: str, description: str = None) -> bool: def consume_points(self, user_id: int, points: int, source: str, description: str = None) -> bool:
@@ -554,6 +578,10 @@ class Database:
# 确保用户存在 # 确保用户存在
self.get_or_create_user(user_id) self.get_or_create_user(user_id)
# 获取签到前积分
points_before = self.get_user_points(user_id)
logger.info(f"用户 {user_id} 签到前积分: {points_before['available_points']}")
# 记录签到 # 记录签到
cursor.execute( cursor.execute(
"INSERT INTO daily_checkins (user_id, checkin_date, points_earned, created_at) VALUES (?, ?, ?, ?)", "INSERT INTO daily_checkins (user_id, checkin_date, points_earned, created_at) VALUES (?, ?, ?, ?)",
@@ -562,8 +590,17 @@ class Database:
# 添加积分 # 添加积分
result = self.add_points(user_id, points, "daily_checkin", f"每日签到奖励") result = self.add_points(user_id, points, "daily_checkin", f"每日签到奖励")
logger.info(f"用户 {user_id} 签到结果: {result}, 积分: {points}")
return result # 验证积分是否真的增加了
points_after = self.get_user_points(user_id)
logger.info(f"用户 {user_id} 签到后积分: {points_after['available_points']}")
if points_after['available_points'] > points_before['available_points']:
logger.info(f"用户 {user_id} 签到成功,积分增加: {points_after['available_points'] - points_before['available_points']}")
return True
else:
logger.error(f"用户 {user_id} 签到失败,积分未增加")
return False
except Exception as e: except Exception as e:
logger.error(f"每日签到失败: {e}", exc_info=True) logger.error(f"每日签到失败: {e}", exc_info=True)

75
diagnose_checkin.py Normal file
View File

@@ -0,0 +1,75 @@
#!/usr/bin/env python3
"""签到问题诊断脚本"""
import os
import sys
import sqlite3
from pathlib import Path
from datetime import datetime
import time
def diagnose_checkin_issue():
"""诊断签到问题"""
print("=== 签到问题诊断 ===")
# 1. 检查数据库文件
db_path = "data/bot.db"
print(f"1. 数据库文件路径: {db_path}")
print(f" 文件存在: {os.path.exists(db_path)}")
if os.path.exists(db_path):
stat = os.stat(db_path)
print(f" 文件大小: {stat.st_size} bytes")
print(f" 文件权限: {oct(stat.st_mode)}")
print(f" 最后修改: {datetime.fromtimestamp(stat.st_mtime)}")
# 2. 检查数据库目录
db_dir = Path(db_path).parent
print(f"2. 数据库目录: {db_dir}")
print(f" 目录存在: {db_dir.exists()}")
print(f" 目录可写: {os.access(db_dir, os.W_OK)}")
# 3. 测试数据库连接
print("3. 测试数据库连接...")
try:
conn = sqlite3.connect(db_path, timeout=10.0)
cursor = conn.cursor()
cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
tables = cursor.fetchall()
print(f" 数据库表: {[table[0] for table in tables]}")
# 检查用户积分表
if ('user_points',) in tables:
cursor.execute("SELECT COUNT(*) FROM user_points")
count = cursor.fetchone()[0]
print(f" 用户积分记录数: {count}")
if count > 0:
cursor.execute("SELECT user_id, available_points FROM user_points LIMIT 3")
users = cursor.fetchall()
print(f" 示例用户积分: {users}")
conn.close()
print(" 数据库连接: 成功")
except Exception as e:
print(f" 数据库连接: 失败 - {e}")
# 4. 检查磁盘空间
print("4. 检查磁盘空间...")
try:
statvfs = os.statvfs(db_dir)
free_space = statvfs.f_frsize * statvfs.f_bavail
total_space = statvfs.f_frsize * statvfs.f_blocks
print(f" 可用空间: {free_space / (1024*1024):.1f} MB")
print(f" 总空间: {total_space / (1024*1024):.1f} MB")
except Exception as e:
print(f" 磁盘空间检查失败: {e}")
# 5. 检查进程权限
print("5. 检查进程权限...")
print(f" 当前用户: {os.getuid() if hasattr(os, 'getuid') else 'N/A'}")
print(f" 当前组: {os.getgid() if hasattr(os, 'getgid') else 'N/A'}")
print("=== 诊断完成 ===")
if __name__ == "__main__":
diagnose_checkin_issue()

150
start.bat Normal file
View File

@@ -0,0 +1,150 @@
@echo off
REM WPS Bot Game Windows启动脚本
setlocal enabledelayedexpansion
REM 默认配置
set DEFAULT_WEBHOOK_URL=
set DEFAULT_HOST=0.0.0.0
set DEFAULT_PORT=11000
set DEFAULT_WORKERS=1
set DEFAULT_LOG_LEVEL=info
REM 显示帮助信息
:show_help
echo WPS Bot Game Windows启动脚本
echo.
echo 用法: %0 [选项]
echo.
echo 选项:
echo -w, --webhook-url URL 设置WPS Webhook URL
echo -H, --host HOST 服务器主机地址 (默认: %DEFAULT_HOST%)
echo -p, --port PORT 服务器端口 (默认: %DEFAULT_PORT%)
echo -W, --workers NUM 工作进程数 (默认: %DEFAULT_WORKERS%)
echo -l, --log-level LEVEL 日志级别 (默认: %DEFAULT_LOG_LEVEL%)
echo -h, --help 显示此帮助信息
echo.
echo 示例:
echo %0 -w "https://xz.wps.cn/api/v1/webhook/send?key=your_key"
echo %0 -w "https://xz.wps.cn/api/v1/webhook/send?key=your_key" -p 8080
echo %0 --webhook-url "https://xz.wps.cn/api/v1/webhook/send?key=your_key" --port 8080 --log-level debug
goto :eof
REM 初始化变量
set WEBHOOK_URL=%DEFAULT_WEBHOOK_URL%
set HOST=%DEFAULT_HOST%
set PORT=%DEFAULT_PORT%
set WORKERS=%DEFAULT_WORKERS%
set LOG_LEVEL=%DEFAULT_LOG_LEVEL%
REM 解析命令行参数
:parse_args
if "%~1"=="" goto :start_app
if "%~1"=="-w" (
set WEBHOOK_URL=%~2
shift
shift
goto :parse_args
)
if "%~1"=="--webhook-url" (
set WEBHOOK_URL=%~2
shift
shift
goto :parse_args
)
if "%~1"=="-H" (
set HOST=%~2
shift
shift
goto :parse_args
)
if "%~1"=="--host" (
set HOST=%~2
shift
shift
goto :parse_args
)
if "%~1"=="-p" (
set PORT=%~2
shift
shift
goto :parse_args
)
if "%~1"=="--port" (
set PORT=%~2
shift
shift
goto :parse_args
)
if "%~1"=="-W" (
set WORKERS=%~2
shift
shift
goto :parse_args
)
if "%~1"=="--workers" (
set WORKERS=%~2
shift
shift
goto :parse_args
)
if "%~1"=="-l" (
set LOG_LEVEL=%~2
shift
shift
goto :parse_args
)
if "%~1"=="--log-level" (
set LOG_LEVEL=%~2
shift
shift
goto :parse_args
)
if "%~1"=="-h" (
call :show_help
exit /b 0
)
if "%~1"=="--help" (
call :show_help
exit /b 0
)
echo 未知参数: %~1
call :show_help
exit /b 1
:start_app
REM 检查Python环境
python --version >nul 2>&1
if errorlevel 1 (
echo 错误: 未找到Python
exit /b 1
)
REM 检查依赖
python -c "import fastapi, uvicorn" >nul 2>&1
if errorlevel 1 (
echo 错误: 缺少必要的Python依赖
echo 请运行: pip install -r requirements.txt
exit /b 1
)
REM 构建启动命令
set CMD=python app.py --host %HOST% --port %PORT% --workers %WORKERS% --log-level %LOG_LEVEL%
if not "%WEBHOOK_URL%"=="" (
set CMD=%CMD% --webhook-url "%WEBHOOK_URL%"
)
REM 显示启动信息
echo 启动WPS Bot Game...
echo 主机: %HOST%
echo 端口: %PORT%
echo 工作进程: %WORKERS%
echo 日志级别: %LOG_LEVEL%
if not "%WEBHOOK_URL%"=="" (
echo Webhook URL: %WEBHOOK_URL%
)
echo.
REM 启动应用
%CMD%

104
start.sh Normal file
View File

@@ -0,0 +1,104 @@
#!/bin/bash
# WPS Bot Game 启动脚本
# 默认配置
DEFAULT_WEBHOOK_URL=""
DEFAULT_HOST="0.0.0.0"
DEFAULT_PORT="11000"
DEFAULT_WORKERS="1"
DEFAULT_LOG_LEVEL="info"
# 显示帮助信息
show_help() {
echo "WPS Bot Game 启动脚本"
echo ""
echo "用法: $0 [选项]"
echo ""
echo "选项:"
echo " -w, --webhook-url URL 设置WPS Webhook URL"
echo " -H, --host HOST 服务器主机地址 (默认: $DEFAULT_HOST)"
echo " -p, --port PORT 服务器端口 (默认: $DEFAULT_PORT)"
echo " -W, --workers NUM 工作进程数 (默认: $DEFAULT_WORKERS)"
echo " -l, --log-level LEVEL 日志级别 (默认: $DEFAULT_LOG_LEVEL)"
echo " -h, --help 显示此帮助信息"
echo ""
echo "示例:"
echo " $0 -w 'https://xz.wps.cn/api/v1/webhook/send?key=your_key'"
echo " $0 -w 'https://xz.wps.cn/api/v1/webhook/send?key=your_key' -p 8080"
echo " $0 --webhook-url 'https://xz.wps.cn/api/v1/webhook/send?key=your_key' --port 8080 --log-level debug"
}
# 解析命令行参数
WEBHOOK_URL="$DEFAULT_WEBHOOK_URL"
HOST="$DEFAULT_HOST"
PORT="$DEFAULT_PORT"
WORKERS="$DEFAULT_WORKERS"
LOG_LEVEL="$DEFAULT_LOG_LEVEL"
while [[ $# -gt 0 ]]; do
case $1 in
-w|--webhook-url)
WEBHOOK_URL="$2"
shift 2
;;
-H|--host)
HOST="$2"
shift 2
;;
-p|--port)
PORT="$2"
shift 2
;;
-W|--workers)
WORKERS="$2"
shift 2
;;
-l|--log-level)
LOG_LEVEL="$2"
shift 2
;;
-h|--help)
show_help
exit 0
;;
*)
echo "未知参数: $1"
show_help
exit 1
;;
esac
done
# 检查Python环境
if ! command -v python3 &> /dev/null; then
echo "错误: 未找到python3"
exit 1
fi
# 检查依赖
if ! python3 -c "import fastapi, uvicorn" &> /dev/null; then
echo "错误: 缺少必要的Python依赖"
echo "请运行: pip install -r requirements.txt"
exit 1
fi
# 构建启动命令
CMD="python3 app.py --host $HOST --port $PORT --workers $WORKERS --log-level $LOG_LEVEL"
if [ -n "$WEBHOOK_URL" ]; then
CMD="$CMD --webhook-url '$WEBHOOK_URL'"
fi
# 显示启动信息
echo "启动WPS Bot Game..."
echo "主机: $HOST"
echo "端口: $PORT"
echo "工作进程: $WORKERS"
echo "日志级别: $LOG_LEVEL"
if [ -n "$WEBHOOK_URL" ]; then
echo "Webhook URL: $WEBHOOK_URL"
fi
echo ""
# 启动应用
eval $CMD