first commit

This commit is contained in:
wood 2024-09-03 17:25:50 +08:00
commit e2936b8243
8 changed files with 291 additions and 0 deletions

65
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,65 @@
name: Build and Push Docker Image
on:
push:
branches:
- main
tags:
- v*
env:
IMAGE_NAME: telegrambot-binance
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run tests (if any)
run: |
# Add your test commands here
# For example: python -m unittest discover tests
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: woodchen
password: ${{ secrets.ACCESS_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: |
woodchen/${{ env.IMAGE_NAME }}:latest
woodchen/${{ env.IMAGE_NAME }}:${{ github.sha }}
- name: Update Docker Hub description
uses: peter-evans/dockerhub-description@v3
with:
username: woodchen
password: ${{ secrets.ACCESS_TOKEN }}
repository: woodchen/${{ env.IMAGE_NAME }}
short-description: ${{ github.event.repository.description }}

15
Dockerfile Normal file
View File

@ -0,0 +1,15 @@
FROM python:3.9-slim
# 设置时区
ENV TZ=Asia/Singapore
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY src /app/src
COPY data /app/data
CMD ["python", "src/main.py"]

1
data/keywords.json Normal file
View File

@ -0,0 +1 @@
["推广", "广告", "ad", "promotion"]

13
docker-compose.yml Normal file
View File

@ -0,0 +1,13 @@
services:
q58bot:
container_name: q58bot
image: woodchen/q58bot:latest
restart: always
environment:
- BOT_TOKEN=719XXX42:AAEydXXXX8rg #换成自己的机器人ID
- ADMIN_ID=5912366993 #换成自己的ID
- CHAT_ID=-100xxx781 #换成自己的群ID
- SYMBOLS=DOGS/USDT,TON/USDT
- TZ=Asia/Singapore
volumes:
- ./data:/app/data

5
requirement.txt Normal file
View File

@ -0,0 +1,5 @@
telethon
ccxt
pyTelegramBotAPI
schedule
pytz

87
src/binance.py Normal file
View File

@ -0,0 +1,87 @@
import os
import ccxt
import telebot
import schedule
import time
import logging
from datetime import datetime, timedelta
import pytz
singapore_tz = pytz.timezone('Asia/Singapore')
exchange = ccxt.binance()
BOT_TOKEN = os.environ['BOT_TOKEN']
CHAT_ID = os.environ['CHAT_ID']
bot = telebot.TeleBot(BOT_TOKEN)
SYMBOLS = os.environ['SYMBOLS'].split(',')
def get_ticker_info(symbol):
ticker = exchange.fetch_ticker(symbol)
return {
'symbol': symbol,
'last': ticker['last'],
'change_percent': ticker['percentage'],
'high': ticker['high'],
'low': ticker['low'],
'volume': ticker['baseVolume'],
'quote_volume': ticker['quoteVolume'],
'bid': ticker['bid'],
'ask': ticker['ask']
}
def format_change(change_percent):
if change_percent > 0:
return f"🔼 +{change_percent:.2f}%"
elif change_percent < 0:
return f"🔽 {change_percent:.2f}%"
else:
return f"◀▶ {change_percent:.2f}%"
def send_price_update():
now = datetime.now(singapore_tz)
message = f"市场更新 - {now.strftime('%Y-%m-%d %H:%M:%S')} (SGT)\n\n"
for symbol in SYMBOLS:
info = get_ticker_info(symbol)
change_str = format_change(info['change_percent'])
message += f"*{info['symbol']}*\n"
message += f"价格: ${info['last']:.7f}\n"
message += f"24h 涨跌: {change_str}\n"
message += f"24h 高/低: ${info['high']:.7f} / ${info['low']:.7f}\n"
message += f"24h 成交量: {info['volume']:.2f}\n"
message += f"24h 成交额: ${info['quote_volume']:.2f}\n"
message += f"买一/卖一: ${info['bid']:.7f} / ${info['ask']:.7f}\n\n"
bot.send_message(CHAT_ID, message, parse_mode='Markdown')
def run():
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger('BinanceUpdater')
while True:
try:
# 立即执行一次价格更新
logger.info("Sending initial price update...")
send_price_update()
# 设置定时任务,每小时整点执行
for hour in range(24):
schedule.every().day.at(f"{hour:02d}:00").do(send_price_update)
logger.info("Scheduled tasks set. Waiting for next hour...")
# 等待下一个整点
now = datetime.now(singapore_tz)
next_hour = (now + timedelta(hours=1)).replace(minute=0, second=0, microsecond=0)
time.sleep((next_hour - now).total_seconds())
logger.info("Starting main loop...")
while True:
schedule.run_pending()
time.sleep(30) # 每30秒检查一次可以根据需要调整
except Exception as e:
logger.error(f"An error occurred in BinanceUpdater: {str(e)}")
logger.info("Attempting to restart BinanceUpdater in 60 seconds...")
time.sleep(60) # 等待60秒后重试
if __name__ == '__main__':
run()

70
src/guard.py Normal file
View File

@ -0,0 +1,70 @@
import os
import json
import logging
import time
from telethon import TelegramClient, events
BOT_TOKEN = os.environ.get('BOT_TOKEN')
ADMIN_ID = int(os.environ.get('ADMIN_ID')) # 从环境变量获取 ADMIN_ID 并转换为整数
KEYWORDS_FILE = '/app/data/keywords.json'
def load_keywords():
if os.path.exists(KEYWORDS_FILE):
with open(KEYWORDS_FILE, 'r') as f:
return json.load(f)
return ['推广', '广告', 'ad', 'promotion']
def save_keywords(keywords):
with open(KEYWORDS_FILE, 'w') as f:
json.dump(keywords, f)
KEYWORDS = load_keywords()
client = TelegramClient('bot', api_id=6, api_hash='eb06d4abfb49dc3eeb1aeb98ae0f581e')
client.start(bot_token=BOT_TOKEN)
@client.on(events.NewMessage(pattern=''))
async def handler(event):
global KEYWORDS
if event.is_private and event.sender_id == ADMIN_ID:
command = event.message.text.split()
if command[0].lower() == '/add' and len(command) > 1:
new_keyword = command[1].lower()
if new_keyword not in KEYWORDS:
KEYWORDS.append(new_keyword)
save_keywords(KEYWORDS)
await event.respond(f"关键词 '{new_keyword}' 已添加到列表中。")
else:
await event.respond(f"关键词 '{new_keyword}' 已经在列表中。")
elif command[0].lower() == '/delete' and len(command) > 1:
keyword_to_delete = command[1].lower()
if keyword_to_delete in KEYWORDS:
KEYWORDS.remove(keyword_to_delete)
save_keywords(KEYWORDS)
await event.respond(f"关键词 '{keyword_to_delete}' 已从列表中删除。")
else:
await event.respond(f"关键词 '{keyword_to_delete}' 不在列表中。")
elif command[0].lower() == '/list':
await event.respond(f"当前关键词列表:{', '.join(KEYWORDS)}")
return
if not event.is_private and any(keyword in event.message.text.lower() for keyword in KEYWORDS):
if event.sender_id != ADMIN_ID:
await event.delete()
await event.respond("已撤回该消息。注:已发送的推广链接不要多次发送,置顶已有项目的推广链接也会自动撤回。")
def run():
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger('TeleGuard')
while True:
try:
logger.info("TeleGuard is starting...")
client.run_until_disconnected()
except Exception as e:
logger.error(f"An error occurred in TeleGuard: {str(e)}")
logger.info("Attempting to restart TeleGuard in 60 seconds...")
time.sleep(60) # 等待60秒后重试
if __name__ == '__main__':
run()

35
src/main.py Normal file
View File

@ -0,0 +1,35 @@
import multiprocessing
import guard
import binance
import logging
def run_guard():
while True:
try:
guard.run()
except Exception as e:
logging.error(f"Guard process crashed: {str(e)}")
logging.info("Restarting Guard process...")
def run_binance():
while True:
try:
binance.run()
except Exception as e:
logging.error(f"Binance process crashed: {str(e)}")
logging.info("Restarting Binance process...")
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# 创建两个进程分别运行 guard 和 binance 服务
guard_process = multiprocessing.Process(target=run_guard)
binance_process = multiprocessing.Process(target=run_binance)
# 启动进程
guard_process.start()
binance_process.start()
# 等待进程结束
guard_process.join()
binance_process.join()