diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2f47bb4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +# Use an official Python runtime as a parent image +FROM python:3.12-slim + +# Set the working directory in the container to /app +WORKDIR /app + +# Copy only necessary files +COPY ServerChanPush2TelegramBot.py /app/ +COPY wsgi.py /app/ +COPY bot_config_example.json /app/ +COPY requirements.txt /app/ + +# Install any needed packages specified in requirements.txt +RUN pip install --no-cache-dir -r requirements.txt + +# Ensure bot_config.json exists +RUN if [ ! -f /app/data/bot_config.json ]; then \ + mkdir -p /app/data && \ + cp /app/bot_config_example.json /app/data/bot_config.json; \ + fi + +# Expose the port +EXPOSE 5000 + +# Run wsgi server with gunicorn +CMD ["gunicorn", "--bind", "0.0.0.0:5000", "wsgi:app"] diff --git a/ServerChanPush2TelegramBot.py b/ServerChanPush2TelegramBot.py index 8637e56..69da1f0 100644 --- a/ServerChanPush2TelegramBot.py +++ b/ServerChanPush2TelegramBot.py @@ -6,12 +6,38 @@ import re from urllib.parse import unquote from datetime import datetime from logging.handlers import TimedRotatingFileHandler +import os +import sys +import shutil # 获取当前日期 current_date = datetime.now().strftime("%Y-%m-%d") +# 获取当前工作目录 +base_dir = os.path.dirname(os.path.abspath(__file__)) + +# 定义数据和日志目录 +data_dir = os.path.join(base_dir, "data") +log_dir = os.path.join(data_dir, "log") + +# 创建必要的目录 +os.makedirs(data_dir, exist_ok=True) +os.makedirs(log_dir, exist_ok=True) + +# 检查配置文件是否存在,如果不存在则复制示例配置文件并退出程序 +config_path = os.path.join(data_dir, 'bot_config.json') +example_config_path = os.path.join(base_dir, 'bot_config_example.json') +if not os.path.exists(config_path): + if os.path.exists(example_config_path): + shutil.copyfile(example_config_path, config_path) + logging.error(f"Configuration file not found. {example_config_path} copied to {config_path}. Exiting.") + sys.exit(1) + else: + logging.error(f"Configuration file not found and example config file {example_config_path} does not exist. Exiting.") + sys.exit(1) + # 创建一个处理器,该处理器每天午夜都会创建一个新的日志文件 -handler = TimedRotatingFileHandler(f"received_requests_StartAt{current_date}.log", when="midnight", interval=1, backupCount=10, encoding='utf-8') +handler = TimedRotatingFileHandler(os.path.join(log_dir, f"received_requests_StartAt{current_date}.log"), when="midnight", interval=1, backupCount=10, encoding='utf-8') handler.suffix = "To%Y-%m-%d.log" # 配置日志 @@ -27,16 +53,20 @@ app = Flask(__name__) # 读取配置文件 def load_config(): - with open('bot_config.json', 'r', encoding='utf-8') as f: - return json.load(f) + with open(config_path, 'r', encoding='utf-8') as f: + config = json.load(f) + for bot_config in config: + api_url_template = bot_config.get('api_url', '') + main_bot_id = bot_config.get('main_bot_id', '') + if '[AUTO_REPLACE_MAIN_BOT_ID]' in api_url_template: + bot_config['api_url'] = api_url_template.replace('[AUTO_REPLACE_MAIN_BOT_ID]', main_bot_id) + return config - - - # 保存接收到的请求数据 +# 保存接收到的请求数据 def save_received_data(received_url, received_data): try: current_date = datetime.now().strftime("%Y-%m-%d") - with open(f"received_data_{current_date}.json", "a", encoding='utf-8') as f: + with open(os.path.join(log_dir, f"received_data_{current_date}.json"), "a", encoding='utf-8') as f: json.dump({"received_url": received_url, "received_data": received_data}, f, ensure_ascii=False) f.write("\n") except Exception as e: @@ -46,7 +76,7 @@ def save_received_data(received_url, received_data): def save_sent_data(api_url, payload): try: current_date = datetime.now().strftime("%Y-%m-%d") - with open(f"sent_data_{current_date}.json", "a", encoding='utf-8') as f: + with open(os.path.join(log_dir, f"sent_data_{current_date}.json"), "a", encoding='utf-8') as f: json.dump({"sent_url": api_url, "sent_data": payload}, f, ensure_ascii=False) f.write("\n") except Exception as e: @@ -61,11 +91,10 @@ def convert_str_gbk_to_utf8(text_str): except: return text_str # 如果转换失败,则返回原始字符串 - # 读取待发送的消息 def read_pending_messages(): try: - with open("pending_messages.json", "r") as f: + with open(os.path.join(data_dir, "pending_messages.json"), "r") as f: return json.load(f) except FileNotFoundError: return [] @@ -74,7 +103,7 @@ def read_pending_messages(): # 写入待发送的消息 def write_pending_messages(messages): - with open("pending_messages.json", "w") as f: + with open(os.path.join(data_dir, "pending_messages.json"), "w") as f: json.dump(messages, f, ensure_ascii=False) # 发送 Telegram 消息 @@ -84,11 +113,15 @@ def send_telegram_message(bot_id, chat_id, title, desp=None, url=None): # 用于标记是否找到匹配的关键词 found = False delimiter = None + api_url = None + proxies = None # 遍历所有主 bot_id 的配置 for config in all_bots_config: main_bot_id = config['main_bot_id'] main_chat_id = config.get('main_chat_id', '') # 如果没有main_chat_id,默认为空字符串 sub_bots = config['sub_bots'] + api_url = config.get('api_url', None) + proxies = config.get('proxies', None) # 如果传入的 bot_id 和 chat_id 匹配某个主 bot_id 和主 chat_id if bot_id == main_bot_id and chat_id == main_chat_id: # 检查关键词,如果匹配则替换 bot_id 和 chat_id @@ -96,32 +129,23 @@ def send_telegram_message(bot_id, chat_id, title, desp=None, url=None): for keyword in sub_bot['keywords']: keyword_decode = keyword.decode('utf-8') if isinstance(keyword, bytes) else keyword title_decode = title.decode('utf-8') if isinstance(title, bytes) else title - desp_decode = desp.decode('utf-8') if isinstance(desp, bytes) and desp is not None else desp - if (keyword_decode.lower() in title_decode.lower()) or (desp is not None and keyword_decode.lower() in desp_decode.lower()): + if keyword_decode.lower() in title_decode.lower(): bot_id = sub_bot['bot_id'] chat_id = sub_bot['chat_id'] # 替换 chat_id delimiter = sub_bot.get('delimiter') # 获取隔断符配置 found = True break if found: - break - + break + # 一旦找到匹配的主 bot_id 和主 chat_id,就跳出循环 if found: break - - - api_url = f"https://api.telegram.org/bot{bot_id}/sendMessage" - proxies = { - 'http': 'http://127.0.0.1:7890', - 'https': 'http://127.0.0.1:7890', - } + api_url = api_url or f"https://api.telegram.org/bot{bot_id}/sendMessage" text = title # 初始化 text 为 title - text += f"\n\n{(desp.split(delimiter)[0] if delimiter and desp else desp) if desp else ''}" - text =text.rstrip() - + text = text.rstrip() # 使用正则表达式来识别受影响的链接 affected_urls = re.findall(r'(https|http|ftp)\\\\\\/\\\\\\/[\\w\\\\:\\\\/\\.\\-]+', text) @@ -134,23 +158,22 @@ def send_telegram_message(bot_id, chat_id, title, desp=None, url=None): if url: # 如果有 url,添加到 text text += f"\n\n详情:" text += f"{url}" # 直接添加 URL,Telegram 会自动处理预览 + text = unescape_url(text) - text=unescape_url(text) - - payload = { 'chat_id': chat_id, 'text': text, 'parse_mode': 'HTML', 'disable_web_page_preview': False # 启用网页预览 } + try: response = requests.post(api_url, data=payload, proxies=proxies, timeout=2) logging.info(f"response: {response.text}") if response.status_code == 200 and response.json().get("ok"): # 保存发送的请求数据 converted_sent_data = convert_str_gbk_to_utf8(str(payload)) - save_sent_data(api_url,converted_sent_data) + save_sent_data(api_url, converted_sent_data) return True, response.json() else: return False, response.json() @@ -166,18 +189,11 @@ def index(): # 保存接收到的请求数据 converted_received_data = convert_str_gbk_to_utf8(str(received_data)) - save_received_data(received_url,converted_received_data) + save_received_data(received_url, converted_received_data) logging.info(f"Received URL: {received_url}") logging.info(f"Received POST Data: {received_data}") - - - #escaped_desp = received_data.get('desp', '') if received_data else '' - #unescaped_desp = json.loads(f'"{escaped_desp}"') - - - bot_id = request.args.get('bot_id') or (received_data.get('bot_id') if received_data else None) chat_id = request.args.get('chat_id') or (received_data.get('chat_id') if received_data else None) title = request.args.get('title') or (received_data.get('title') if received_data else None) @@ -197,18 +213,16 @@ def index(): # 如果 error_list 不为空,返回错误信息和 400 状态码 if error_list: - TestStatus=request.args.get('TestStatus') or (received_data.get('TestStatus') if received_data else None) + TestStatus = request.args.get('TestStatus') or (received_data.get('TestStatus') if received_data else None) if TestStatus is None: return jsonify({"error": error_list}), 400 else: return jsonify({"ok": "the test passed"}), 200 - pending_messages = read_pending_messages() success, response = send_telegram_message(bot_id, chat_id, title, desp, url) - - + if success: new_pending_messages = [] for msg in pending_messages: @@ -223,7 +237,7 @@ def index(): desp = f"【This is a delayed message】" else: desp = desp + f"\n\n【This is a delayed message】" - + pending_messages.append({ 'bot_id': bot_id, 'chat_id': chat_id, @@ -235,4 +249,6 @@ def index(): return jsonify({"error": "Failed to send message, added to pending list"}), 200 if __name__ == "__main__": - app.run(host='0.0.0.0', port=5000) + config = load_config() + port = config[0].get("port", 5000) + app.run(host='0.0.0.0', port=port) \ No newline at end of file diff --git a/ServerChanPush2TelegramBot_DockerBuilder.ps1 b/ServerChanPush2TelegramBot_DockerBuilder.ps1 new file mode 100644 index 0000000..447179f --- /dev/null +++ b/ServerChanPush2TelegramBot_DockerBuilder.ps1 @@ -0,0 +1,74 @@ +# Set-ExecutionPolicy RemoteSigned -Scope CurrentUser + +# ǷԹԱȨ +if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { + # ԱȨ + Start-Process powershell -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs + exit +} + +# лűĿ¼ +Set-Location $PSScriptRoot + +Write-Host "ǰĿ¼лΪűĿ¼: $PSScriptRoot" + +# ȡǰںʱ +$dateTime = Get-Date -Format "yyyyMMdd" +Write-Host "ǰ: $dateTime" + +# ʾȡ汾һλ +$revision = Read-Host -Prompt "İ汾 ($dateTime,ûдΣֱӻس)" +Write-Host "İ汾: $revision" + +# 汾 +if ([string]::IsNullOrWhiteSpace($revision)) { + $version = "$dateTime" +} else { + $version = "$dateTime" + "_$revision" +} +Write-Host "İ汾: $version" + +# ϰ汾űǩ Docker +Write-Host "ڹ Docker ..." +$tempFileBuild = [System.IO.Path]::GetTempFileName() +docker build -t yshtcn/serverchanpush2telegrambot:$version . 2> $tempFileBuild + +if ($LASTEXITCODE -ne 0) { + Write-Host "Docker 񹹽ʧ" -ForegroundColor Red + Write-Host (Get-Content $tempFileBuild) -ForegroundColor Red + Remove-Item $tempFileBuild + exit +} +Write-Host "Docker 񹹽ɹ" +Remove-Item $tempFileBuild + +# ʹа汾űǩ Docker Docker Hub +Write-Host " Docker Docker Hub..." +$tempFilePush = [System.IO.Path]::GetTempFileName() +docker push yshtcn/serverchanpush2telegrambot:$version 2> $tempFilePush + +if ($LASTEXITCODE -ne 0) { + Write-Host "Docker ʧ" -ForegroundColor Red + Write-Host (Get-Content $tempFilePush) -ForegroundColor Red + Remove-Item $tempFilePush + exit +} +Write-Host "Docker ͳɹ" +Remove-Item $tempFilePush + +# Ϊ 'latest' ǩ +Write-Host "Ϊ 'latest' ǩ..." +$tempFilePushLatest = [System.IO.Path]::GetTempFileName() +docker tag yshtcn/serverchanpush2telegrambot:$version yshtcn/serverchanpush2telegrambot:latest +docker push yshtcn/serverchanpush2telegrambot:latest 2> $tempFilePushLatest + +if ($LASTEXITCODE -ne 0) { + Write-Host "Docker 'latest' ǩʧ" -ForegroundColor Red + Write-Host (Get-Content $tempFilePushLatest) -ForegroundColor Red + Remove-Item $tempFilePushLatest + exit +} +Write-Host "Docker 'latest' ǩͳɹ" +Remove-Item $tempFilePushLatest + +Write-Host "Docker 񹹽ȫ" diff --git a/bot_config_example.json b/bot_config_example.json index 2fbb292..ff703ab 100644 --- a/bot_config_example.json +++ b/bot_config_example.json @@ -1,9 +1,8 @@ - [ { "main_bot_id": "YOUR_MAIN_BOT_ID_HERE", "main_chat_id": "YOUR_MAIN_CHAT_ID_HERE", - "sub_bots": [ + "sub_bots": [ { "bot_id": "YOUR_SUB_BOT_ID_1", "chat_id": "YOUR_SUB_CHAT_ID_1", @@ -15,6 +14,12 @@ "chat_id": "YOUR_SUB_CHAT_ID_2", "keywords": ["keyword3", "keyword4"] } - ] + ], + "api_url": "https://api.telegram.org/bot[AUTO_REPLACE_MAIN_BOT_ID]/sendMessage", + "proxies": { + "http": "http://127.0.0.1:7890", + "https": "http://127.0.0.1:7890" + }, + "port": 5000 } ] diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..40fb41b --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +Flask +requests +gunicorn diff --git a/wsgi.py b/wsgi.py new file mode 100644 index 0000000..b9dd0df --- /dev/null +++ b/wsgi.py @@ -0,0 +1,4 @@ +from ServerChanPush2TelegramBot import app + +if __name__ == "__main__": + app.run()