CoolBotDataSys/scripts/daily_report.py

191 lines
6.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""CoolBotDataSys 每日飞书日报推送"""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import requests
import logging
from datetime import datetime
from database import db
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def get_collection_stats():
"""获取藏品统计"""
with db.get_cursor() as cursor:
cursor.execute("SELECT COUNT(*) as cnt FROM collections")
total = cursor.fetchone()["cnt"]
return {"total_collections": total}
def get_yichens_stats():
"""获取一尘数据统计"""
with db.get_cursor() as cursor:
# 总帖子数
cursor.execute("SELECT COUNT(*) as cnt FROM yichens_posts")
total_posts = cursor.fetchone()["cnt"]
# 今日新增
cursor.execute("SELECT COUNT(*) as cnt FROM yichens_posts WHERE DATE(crawled_at) = CURRENT_DATE")
today_posts = cursor.fetchone()["cnt"]
# 交易帖数量
cursor.execute("SELECT COUNT(*) as cnt FROM yichens_posts WHERE post_type = 'deal'")
deal_posts = cursor.fetchone()["cnt"]
# 有价格标注的帖子
cursor.execute("SELECT COUNT(*) as cnt FROM yichens_posts WHERE price IS NOT NULL AND price > 0")
priced_posts = cursor.fetchone()["cnt"]
# 今日价格区间统计
cursor.execute("""
SELECT
COUNT(*) as cnt,
AVG(price) as avg_price,
MIN(price) as min_price,
MAX(price) as max_price
FROM yichens_posts
WHERE price IS NOT NULL AND price > 0 AND DATE(crawled_at) = CURRENT_DATE
""")
today_price_stats = cursor.fetchone()
# 最新帖子(标题示例)
cursor.execute("""
SELECT title, price, price_unit
FROM yichens_posts
WHERE price IS NOT NULL AND price > 0
ORDER BY crawled_at DESC LIMIT 5
""")
latest_with_price = cursor.fetchall()
# 最近爬虫状态
cursor.execute("""
SELECT source, status, items_count, finished_at
FROM crawl_logs
ORDER BY finished_at DESC LIMIT 3
""")
crawl_status = cursor.fetchall()
return {
"total_posts": total_posts,
"today_posts": today_posts,
"deal_posts": deal_posts,
"priced_posts": priced_posts,
"today_price_stats": today_price_stats,
"latest_with_price": latest_with_price,
"crawl_status": crawl_status
}
def build_feishu_message(collection_stats, yichens_stats):
"""构建飞书消息卡片"""
now = datetime.now()
# 最新价格帖子示例
latest_items = ""
for item in yichens_stats.get("latest_with_price", []):
price_str = f"¥{item['price']}{item['price_unit']}" if item['price'] else "价格待确认"
latest_items += f"- {item['title'][:30]}... : {price_str}\n"
if not latest_items:
latest_items = "今日暂无价格数据\n"
# 爬虫状态
crawl_info = ""
for log in yichens_stats.get("crawl_status", []):
status_emoji = "" if log["status"] == "success" else ""
crawl_info += f"{status_emoji} {log['source']}: {log['items_count']}条 @ {log['finished_at']}\n"
if not crawl_info:
crawl_info = "暂无爬虫运行记录\n"
# 价格统计
price_stats = yichens_stats.get("today_price_stats", {})
price_info = ""
if price_stats and price_stats.get("cnt", 0) > 0:
price_info = f"均价: ¥{price_stats['avg_price']:.0f} | 区间: ¥{price_stats['min_price']:.0f}{price_stats['max_price']:.0f} ({price_stats['cnt']}条)"
else:
price_info = "今日暂无价格统计"
message = {
"msg_type": "interactive",
"card": {
"header": {
"title": {"tag": "plain_text", "content": f"📊 CoolBotDataSys 日报 {now.strftime('%Y-%m-%d %H:%M')}"},
"template": "blue"
},
"elements": [
{
"tag": "div",
"text": {
"tag": "lark_md",
"content": (
f"**🐉 一尘连体纪念钞数据**\n"
f"- 总帖子: {yichens_stats['total_posts']}\n"
f"- 今日新增: +{yichens_stats['today_posts']}\n"
f"- 交易帖: {yichens_stats['deal_posts']}\n"
f"- 有价格: {yichens_stats['priced_posts']}\n\n"
f"**💰 今日价格动态**\n{price_info}\n\n"
f"**📈 最新交易帖**\n{latest_items}\n\n"
f"**🔄 爬虫状态**\n{crawl_info}"
)
}
},
{"tag": "hr"},
{
"tag": "note",
"elements": [
{"tag": "plain_text", "content": f"CoolBotDataSys · {now.strftime('%H:%M:%S')}"}
]
}
]
}
}
return message
def send_feishu(message):
"""发送飞书消息"""
webhook = os.environ.get("FEISHU_WEBHOOK_URL", "")
if not webhook:
logger.warning("飞书Webhook未配置跳过发送")
return False
try:
response = requests.post(webhook, json=message, timeout=10)
result = response.json()
if result.get("code") == 0 or result.get("StatusCode") == 0:
logger.info("✅ 飞书消息发送成功")
return True
else:
logger.error(f"❌ 飞书发送失败: {result}")
return False
except Exception as e:
logger.error(f"❌ 飞书发送异常: {e}")
return False
def main():
logger.info("📊 开始生成日报...")
collection_stats = get_collection_stats()
yichens_stats = get_yichens_stats()
print(f"统计概览:")
print(f" 一尘帖子总数: {yichens_stats['total_posts']}")
print(f" 今日新增: {yichens_stats['today_posts']}")
print(f" 交易帖: {yichens_stats['deal_posts']}")
print(f" 有价格标注: {yichens_stats['priced_posts']}")
message = build_feishu_message(collection_stats, yichens_stats)
print(f"\n消息卡片内容预览:")
print(message["card"]["elements"][0]["text"]["content"][:500])
# 检查是否启用飞书推送
webhook = os.environ.get("FEISHU_WEBHOOK_URL", "")
if webhook:
send_feishu(message)
else:
print("\n⚠️ 未配置 FEISHU_WEBHOOK_URL 环境变量,跳过飞书推送")
return yichens_stats
if __name__ == "__main__":
main()