Skip to content

Commit abab9cb

Browse files
committed
first commit
0 parents  commit abab9cb

23 files changed

+1905
-0
lines changed

.env.example

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Telegram Bot Token
2+
TELEGRAM_BOT_TOKEN=your_telegram_bot_token_here
3+
4+
# Webhook Configuration
5+
WEBHOOK_URL=https://yourname-project.hf.space
6+
WEBHOOK_PATH=/api/v1/webhook
7+
8+
# Cache Settings
9+
CACHE_TIMEOUT=5
10+
CACHE_TTL_MINUTES=10
11+
12+
# Privacy Settings
13+
REDACT_USER_DATA=True
14+
15+
BOC_URL=https://www.bankofchina.com/sourcedb/whpj/enindex_1619.html
16+
17+
# Timezone Settings
18+
TIMEZONE=Asia/Shanghai
19+
20+
# Application Settings
21+
APP_HOST=0.0.0.0
22+
APP_PORT=7860
23+
APP_WORKERS=1
24+
DEBUG=False
25+
26+
SUPPORTED_CURRENCIES=USD,EUR,JPY,GBP,AUD,CAD,CHF,HKD,SGD,SEK,DKK,NOK,NZD,THB,PHP,MOP,KRW,CNY,AED,BRL,IDR,INR,RUB,SAR,TRY,TWD,ZAR
27+
CURRENCY_NAMES=USD:美元,EUR:欧元,JPY:日元,GBP:英镑,AUD:澳大利亚元,CAD:加拿大元,CHF:瑞士法郎,HKD:港币,SGD:新加坡元,SEK:瑞典克朗,DKK:丹麦克朗,NOK:挪威克朗,NZD:新西兰元,THB:泰国铢,PHP:菲律宾比索,MOP:澳门元,KRW:韩元,CNY:人民币,AED:阿联酋迪拉姆,BRL:巴西雷亚尔,IDR:印尼盾,INR:印度卢比,RUB:俄罗斯卢布,SAR:沙特里亚尔,TRY:土耳其里拉,TWD:新台币,ZAR:南非兰特
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
# .github/workflows/docker-publish-ghcr.yml
2+
3+
name: Docker Image CI (GHCR - CET_BOT)
4+
5+
on:
6+
push:
7+
branches: [master]
8+
paths-ignore:
9+
- "**.md"
10+
- "docs/**"
11+
workflow_dispatch:
12+
13+
jobs:
14+
check_commit:
15+
name: Check Build Settings
16+
runs-on: ubuntu-latest
17+
outputs:
18+
should_build: ${{ steps.check.outputs.should_build }}
19+
apply_tags: ${{ steps.check.outputs.apply_tags }}
20+
steps:
21+
- name: Checkout Repository
22+
uses: actions/checkout@v4
23+
with:
24+
fetch-depth: 2
25+
26+
- name: Get commit message
27+
id: get_commit_message
28+
run: |
29+
COMMIT_MSG=$(git log -1 --pretty=format:"%s")
30+
echo "commit_message=$COMMIT_MSG" >> $GITHUB_OUTPUT
31+
echo "Commit message: $COMMIT_MSG"
32+
33+
- name: Set build settings
34+
id: check
35+
run: |
36+
COMMIT_MSG="${{ steps.get_commit_message.outputs.commit_message }}"
37+
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
38+
echo "should_build=true" >> $GITHUB_OUTPUT
39+
echo "apply_tags=true" >> $GITHUB_OUTPUT
40+
echo "Workflow dispatched manually. Building with tags..."
41+
elif [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" == "refs/heads/master" ]]; then
42+
echo "should_build=true" >> $GITHUB_OUTPUT
43+
# Only apply tags if commit message contains 'build'
44+
if [[ "$COMMIT_MSG" == *"build"* ]]; then
45+
echo "apply_tags=true" >> $GITHUB_OUTPUT
46+
echo "Push to master branch with 'build' in commit message. Building with tags..."
47+
else
48+
echo "apply_tags=false" >> $GITHUB_OUTPUT
49+
echo "Push to master branch without 'build' in commit message. Building without tags..."
50+
fi
51+
else
52+
echo "should_build=false" >> $GITHUB_OUTPUT
53+
echo "apply_tags=false" >> $GITHUB_OUTPUT
54+
echo "Not a master branch push or manual dispatch. Skipping build."
55+
fi
56+
57+
read_version:
58+
name: Read Version
59+
runs-on: ubuntu-latest
60+
outputs:
61+
version: ${{ steps.get_version.outputs.version }}
62+
steps:
63+
- name: Checkout Repository
64+
uses: actions/checkout@v4
65+
66+
- name: Read version from file
67+
id: get_version
68+
run: |
69+
VERSION=$(grep -oP 'version=\K[0-9]+\.[0-9]+\.[0-9]+' version.txt)
70+
echo "Version read from file: $VERSION"
71+
echo "version=$VERSION" >> $GITHUB_OUTPUT
72+
73+
build_and_push:
74+
name: Build and Push CET_BOT Image to GHCR
75+
runs-on: ubuntu-latest
76+
needs: [check_commit, read_version]
77+
if: ${{ needs.check_commit.outputs.should_build == 'true' }}
78+
permissions:
79+
contents: write
80+
packages: write
81+
82+
steps:
83+
- name: Checkout Repository
84+
uses: actions/checkout@v4
85+
with:
86+
fetch-depth: 0
87+
88+
- name: Set up QEMU
89+
uses: docker/setup-qemu-action@v3
90+
91+
- name: Set up Docker Buildx
92+
uses: docker/setup-buildx-action@v3
93+
94+
- name: Login to GitHub Container Registry
95+
uses: docker/login-action@v3
96+
with:
97+
registry: ghcr.io
98+
username: ${{ github.actor }}
99+
password: ${{ secrets.GITHUB_TOKEN }}
100+
101+
# --- 新增步骤:转换所有者名称为小写 ---
102+
- name: Convert owner name to lowercase
103+
id: owner_lc
104+
run: echo "OWNER_LC=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
105+
# ------------------------------------
106+
107+
# Test image deletion section removed as per requirement
108+
109+
# Removed GitHub repository tagging
110+
111+
- name: Create Version Tag
112+
if: ${{ needs.check_commit.outputs.apply_tags == 'true' }}
113+
run: |
114+
VERSION=${{ needs.read_version.outputs.version }}
115+
echo "Creating tag v$VERSION"
116+
git config user.name "GitHub Actions"
117+
git config user.email "[email protected]"
118+
git tag -a "v$VERSION" -m "Release version $VERSION"
119+
git push origin "v$VERSION"
120+
121+
- name: Build and push Docker image to GHCR with tags
122+
id: build-and-push-with-tags
123+
if: ${{ needs.check_commit.outputs.apply_tags == 'true' }}
124+
uses: docker/build-push-action@v5
125+
with:
126+
context: .
127+
file: ./Dockerfile
128+
push: true
129+
tags: | # --- 使用转换后的小写所有者名称和版本号 ---
130+
ghcr.io/${{ env.OWNER_LC }}/cet_bot:latest
131+
ghcr.io/${{ env.OWNER_LC }}/cet_bot:sha-${{ github.sha }}
132+
ghcr.io/${{ env.OWNER_LC }}/cet_bot:v${{ needs.read_version.outputs.version }}
133+
labels: |
134+
org.opencontainers.image.title=CET_BOT
135+
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
136+
org.opencontainers.image.revision=${{ github.sha }}
137+
org.opencontainers.image.created=${{ steps.build-and-push-with-tags.outputs.metadata.image.created }}
138+
139+
# Test build section removed as per requirement
140+
141+
- name: Build and push Docker image to GHCR without version tags
142+
id: build-and-push-without-tags
143+
if: ${{ needs.check_commit.outputs.apply_tags == 'false' && needs.check_commit.outputs.should_build == 'true' }}
144+
uses: docker/build-push-action@v5
145+
with:
146+
context: .
147+
file: ./Dockerfile
148+
push: true
149+
tags: | # --- 使用唯一标识符和主分支标签 ---
150+
ghcr.io/${{ env.OWNER_LC }}/cet_bot:commit-${{ github.sha }}
151+
ghcr.io/${{ env.OWNER_LC }}/cet_bot:master
152+
# platforms: linux/amd64,linux/arm64
153+
labels: |
154+
org.opencontainers.image.title=CET_BOT
155+
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
156+
org.opencontainers.image.revision=${{ github.sha }}
157+
org.opencontainers.image.created=${{ steps.build-and-push-without-tags.outputs.metadata.image.created }}
158+
# (可选) 输出构建的镜像 Digest 和元数据
159+
# - name: Print image digest
160+
# run: echo ${{ steps.build-and-push.outputs.digest }}
161+
# - name: Print image metadata
162+
# run: echo '${{ steps.build-and-push.outputs.metadata }}'
163+
164+
create_release:
165+
name: Create GitHub Release
166+
runs-on: ubuntu-latest
167+
needs: [read_version, build_and_push, check_commit]
168+
if: ${{ needs.check_commit.outputs.apply_tags == 'true' }}
169+
permissions:
170+
contents: write
171+
steps:
172+
- name: Checkout Repository
173+
uses: actions/checkout@v4
174+
175+
- name: Create Release
176+
uses: actions/create-release@v1
177+
env:
178+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
179+
with:
180+
tag_name: v${{ needs.read_version.outputs.version }}
181+
release_name: Release v${{ needs.read_version.outputs.version }}
182+
draft: false
183+
prerelease: false
184+
body: |
185+
Release version ${{ needs.read_version.outputs.version }}
186+
187+
Docker image available at:
188+
- ghcr.io/${{ github.repository_owner }}/cet_bot:v${{ needs.read_version.outputs.version }}
189+
- ghcr.io/${{ github.repository_owner }}/cet_bot:latest

.gitignore

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# 配置文件(包含敏感信息)
2+
config.yaml
3+
4+
# 缓存文件
5+
chinaBank-Exchange-TelegramBot.html # 旧的缓存文件(根目录)
6+
data/ # 包含下载的 HTML 等缓存数据
7+
__pycache__/
8+
*.py[cod]
9+
*$py.class
10+
11+
# 环境文件
12+
.env
13+
*venv/
14+
env/
15+
ENV/
16+
17+
# 日志文件
18+
*.log
19+
20+
# 系统文件
21+
.DS_Store
22+
Thumbs.db
23+
24+
# IDE相关
25+
.idea/
26+
.vscode/
27+
*.swp
28+
*.swo

Dockerfile

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
FROM python:3.11-slim
2+
3+
WORKDIR /app
4+
5+
# 复制依赖文件
6+
COPY requirements.txt .
7+
8+
# 安装依赖
9+
RUN pip install --no-cache-dir -r requirements.txt
10+
11+
# 复制应用代码
12+
COPY . .
13+
COPY .env.example .env
14+
15+
# 暴露端口(Hugging Face Spaces 使用 7860 端口)
16+
EXPOSE 7860
17+
18+
# 启动应用(注意:指向 app.main:app 而不是原来的 app.master:app)
19+
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]

Dockerfile.hf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
FROM ghcr.io/goojoe/cet_bot:latest
2+
3+
ENV TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
4+
ENV CACHE_TTL_MINUTES=${CACHE_TTL_MINUTES}
5+
ENV WEBHOOK_URL=${WEBHOOK_URL}
6+
7+
EXPOSE 7860
8+
9+
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
10+

README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# 中国银行汇率查询 Telegram 机器人
2+
3+
这是一个提供中国银行汇率查询服务的 Telegram 机器人。用户可以通过简单的命令获取最新的汇率信息。
4+
5+
## 功能
6+
7+
- FastAPI
8+
9+
- 查询最新的中国银行外汇汇率
10+
- 支持多种货币转换
11+
- 实时数据更新和异步缓存
12+
- 用户日志隐私保护(可关闭)
13+
14+
## 本地部署
15+
16+
```
17+
python -m venv .venv
18+
pip install -r requirements.txt
19+
# Activate virtual environment
20+
.\.venv\Scripts\Activate.ps1
21+
22+
# Start Uvicorn server
23+
uvicorn main:app --host 0.0.0.0 --port 7860 --reload
24+
```
25+
26+
## hf space部署说明
27+
28+
此项目已配置为在 Hugging Face Space 上运行。部署后,您需要:
29+
30+
1. 在 Space 设置中配置以下环境变量:
31+
- `TELEGRAM_BOT_TOKEN`: 您的 Telegram 机器人令牌
32+
- `WEBHOOK_URL`: 您的 Hugging Face Space URL (例如 https://yourname-project.hf.space)
33+
- `CACHE_TTL_MINUTES`: 缓存超时时间(分钟)
34+
35+
2. 确保 Telegram 机器人已通过 BotFather 创建并获取了令牌
36+
37+
3. 部署完成后,机器人将自动设置 webhook 并开始响应用户消息

app/api/v1/endpoints/webhook.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"""
2+
Webhook Endpoint for Telegram Bot
3+
4+
This module defines the FastAPI endpoint that receives webhook updates from Telegram
5+
and forwards them to the bot's dispatcher for processing.
6+
"""
7+
from fastapi import APIRouter, Request, Depends, HTTPException
8+
from aiogram.types import Update
9+
import json
10+
from app.bot.bot_instance import application
11+
from app.utils.redact import sanitize_user_data
12+
from app.core.config import settings
13+
14+
router = APIRouter()
15+
16+
@router.post("")
17+
async def telegram_webhook(request: Request):
18+
"""
19+
Endpoint for receiving webhook updates from Telegram.
20+
21+
This endpoint accepts POST requests from Telegram containing updates,
22+
and passes them to the appropriate handlers via the dispatcher.
23+
"""
24+
# Get the update data from the request
25+
update_data = await request.json()
26+
27+
# Convert the data to an Update object
28+
telegram_update = Update.model_validate(update_data)
29+
30+
# Log update type for debugging (with sensitive information removed)
31+
import logging
32+
logger = logging.getLogger(__name__)
33+
34+
# Get JSON representation of the update
35+
update_json = telegram_update.model_dump_json(exclude_none=True)
36+
update_dict = json.loads(update_json)
37+
38+
# Sanitize the update data
39+
sanitized_update = sanitize_user_data(update_dict)
40+
41+
# Log sanitized update
42+
logger.info(f"Received update: {json.dumps(sanitized_update)}")
43+
44+
try:
45+
# Log command information for debugging
46+
if telegram_update.message and telegram_update.message.text and telegram_update.message.text.startswith('/'):
47+
command_text = telegram_update.message.text.split()[0]
48+
logger.info(f"Received command: {command_text} from user: [REDACTED_ID]")
49+
50+
# Log entity information if available
51+
if telegram_update.message.entities:
52+
for entity in telegram_update.message.entities:
53+
logger.info(f"Entity: {entity.type} at offset {entity.offset}, length {entity.length}")
54+
55+
# Process the update with aiogram v3 style
56+
await application["dispatcher"].feed_update(
57+
bot=application["bot"],
58+
update=telegram_update
59+
)
60+
61+
# Note: We've removed the fallback mechanism because it was causing duplicate messages.
62+
# The aiogram dispatcher is already handling the commands correctly.
63+
except Exception as e:
64+
logger.error(f"Error processing update: {e}", exc_info=True)
65+
66+
# Return a success response
67+
return {"status": "success"}

0 commit comments

Comments
 (0)