TrendRadar多渠道推送系统实现(企业微信、飞书、钉钉等)

摘要

TrendRadar作为一个热点资讯聚合系统,其核心价值在于将筛选后的新闻资讯及时推送给用户。本文将深入分析TrendRadar的多渠道推送系统实现,包括企业微信、飞书、钉钉、Telegram、邮件等多种推送方式的技术细节和实现原理,帮助开发者理解如何构建一个灵活、可靠的多渠道消息推送系统。

正文

1. 引言

在信息过载的时代,如何确保重要信息能够及时、准确地传达给用户是系统设计的关键。TrendRadar通过支持多种推送渠道,确保用户能够在不同场景下接收到热点资讯,大大提升了系统的实用性和用户体验。

多渠道推送的优势:

  • 覆盖不同用户群体的使用习惯
  • 提高消息送达率
  • 增强系统的容错能力
  • 满足多样化的通知需求

2. 推送系统架构设计

TrendRadar的推送系统采用模块化设计,各推送渠道相对独立,便于维护和扩展。

2.1 系统架构图
外部服务
推送系统
企业微信API
飞书API
钉钉API
Telegram Bot API
SMTP服务器
ntfy服务器
推送管理器
企业微信推送
飞书推送
钉钉推送
Telegram推送
邮件推送
ntfy推送
新闻数据
2.2 核心组件
  1. 推送管理器:统一管理各种推送渠道
  2. 渠道适配器:针对不同渠道的具体实现
  3. 消息格式化器:将新闻数据转换为各渠道支持的格式
  4. 错误处理器:处理推送过程中的异常情况

3. 企业微信推送实现

企业微信是TrendRadar支持的主要推送渠道之一,具有部署简单、使用方便的特点。

3.1 机器人配置
def send_we_work_message(webhook_url, message):
    """
    发送企业微信消息
    
    Args:
        webhook_url: 企业微信机器人Webhook地址
        message: 消息内容
    """
    headers = {
        'Content-Type': 'application/json'
    }
    
    payload = {
        "msgtype": "text",
        "text": {
            "content": message
        }
    }
    
    try:
        response = requests.post(webhook_url, json=payload, headers=headers)
        if response.status_code == 200:
            result = response.json()
            if result.get("errcode") == 0:
                return True, "发送成功"
            else:
                return False, f"发送失败: {result.get('errmsg')}"
        else:
            return False, f"HTTP错误: {response.status_code}"
    except Exception as e:
        return False, f"网络异常: {str(e)}"
3.2 消息分批处理

由于企业微信对消息长度有限制,需要对长消息进行分批处理:

def send_we_work_messages(webhook_url, messages, batch_size=4000):
    """
    分批发送企业微信消息
    
    Args:
        webhook_url: 企业微信机器人Webhook地址
        messages: 消息列表
        batch_size: 每批消息的最大字符数
    """
    current_batch = ""
    batch_count = 1
    
    for message in messages:
        # 检查是否需要分批
        if len(current_batch) + len(message) > batch_size:
            # 发送当前批次
            success, msg = send_we_work_message(
                webhook_url, 
                f"[第{batch_count}批]\n{current_batch}"
            )
            if not success:
                print(f"发送第{batch_count}批消息失败: {msg}")
            
            # 开始新批次
            current_batch = message
            batch_count += 1
        else:
            current_batch += message + "\n"
    
    # 发送最后一批
    if current_batch:
        success, msg = send_we_work_message(
            webhook_url, 
            f"[第{batch_count}批]\n{current_batch}"
        )
        if not success:
            print(f"发送第{batch_count}批消息失败: {msg}")

4. 飞书推送实现

飞书推送支持更丰富的消息格式,可以提供更好的用户体验。

4.1 消息格式化
def format_feishu_message(news_data):
    """
    格式化飞书消息
    
    Args:
        news_data: 新闻数据
        
    Returns:
        dict: 飞书消息格式
    """
    # 构建消息内容
    content_lines = []
    content_lines.append("📊 热点资讯推送")
    content_lines.append("")
    
    for i, news in enumerate(news_data, 1):
        line = f"{i}. [{news['platform']}] {news['title']}"
        if news.get('url'):
            line += f" {news['url']}"
        content_lines.append(line)
    
    content_lines.append("")
    content_lines.append(f"更新时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    
    return {
        "msg_type": "post",
        "content": {
            "post": {
                "zh_cn": {
                    "title": "热点资讯",
                    "content": [
                        [{
                            "tag": "text",
                            "text": "\n".join(content_lines)
                        }]
                    ]
                }
            }
        }
    }
4.2 推送实现
def send_feishu_message(webhook_url, message_data):
    """
    发送飞书消息
    
    Args:
        webhook_url: 飞书机器人Webhook地址
        message_data: 消息数据
    """
    headers = {
        'Content-Type': 'application/json'
    }
    
    try:
        response = requests.post(webhook_url, json=message_data, headers=headers)
        if response.status_code == 200:
            result = response.json()
            if result.get("code") == 0:
                return True, "发送成功"
            else:
                return False, f"发送失败: {result.get('msg')}"
        else:
            return False, f"HTTP错误: {response.status_code}"
    except Exception as e:
        return False, f"网络异常: {str(e)}"

5. 钉钉推送实现

钉钉推送需要处理自定义关键词的限制。

5.1 消息构建
def send_dingtalk_message(webhook_url, message, keywords="热点"):
    """
    发送钉钉消息
    
    Args:
        webhook_url: 钉钉机器人Webhook地址
        message: 消息内容
        keywords: 自定义关键词(钉钉机器人要求)
    """
    headers = {
        'Content-Type': 'application/json'
    }
    
    # 确保消息包含关键词
    if keywords not in message:
        message = f"{keywords}\n{message}"
    
    payload = {
        "msgtype": "text",
        "text": {
            "content": message
        }
    }
    
    try:
        response = requests.post(webhook_url, json=payload, headers=headers)
        if response.status_code == 200:
            result = response.json()
            if result.get("errcode") == 0:
                return True, "发送成功"
            else:
                return False, f"发送失败: {result.get('errmsg')}"
        else:
            return False, f"HTTP错误: {response.status_code}"
    except Exception as e:
        return False, f"网络异常: {str(e)}"

6. Telegram推送实现

Telegram推送通过Bot API实现,支持更丰富的交互功能。

6.1 Bot配置
def send_telegram_message(bot_token, chat_id, message):
    """
    发送Telegram消息
    
    Args:
        bot_token: Telegram Bot Token
        chat_id: 聊天ID
        message: 消息内容
    """
    url = f"https://api.telegram.org/bot{bot_token}/sendMessage"
    
    payload = {
        "chat_id": chat_id,
        "text": message,
        "parse_mode": "Markdown"
    }
    
    try:
        response = requests.post(url, json=payload)
        if response.status_code == 200:
            result = response.json()
            if result.get("ok"):
                return True, "发送成功"
            else:
                return False, f"发送失败: {result.get('description')}"
        else:
            return False, f"HTTP错误: {response.status_code}"
    except Exception as e:
        return False, f"网络异常: {str(e)}"

7. 邮件推送实现

邮件推送支持HTML格式,可以提供更美观的展示效果。

7.1 SMTP配置
# SMTP邮件配置
SMTP_CONFIGS = {
    # Gmail(使用 STARTTLS)
    "gmail.com": {"server": "smtp.gmail.com", "port": 587, "encryption": "TLS"},
    # QQ邮箱(使用 SSL,更稳定)
    "qq.com": {"server": "smtp.qq.com", "port": 465, "encryption": "SSL"},
    # Outlook(使用 STARTTLS)
    "outlook.com": {
        "server": "smtp-mail.outlook.com",
        "port": 587,
        "encryption": "TLS",
    },
    # 网易邮箱(使用 SSL,更稳定)
    "163.com": {"server": "smtp.163.com", "port": 465, "encryption": "SSL"},
    "126.com": {"server": "smtp.126.com", "port": 465, "encryption": "SSL"},
}
7.2 邮件发送
def send_email_notification(
    from_email, password, to_emails, subject, html_content, 
    smtp_server="", smtp_port=""
):
    """
    发送邮件通知
    
    Args:
        from_email: 发件人邮箱
        password: 邮箱密码或授权码
        to_emails: 收件人邮箱列表
        subject: 邮件主题
        html_content: HTML邮件内容
        smtp_server: SMTP服务器地址
        smtp_port: SMTP端口
    """
    # 自动识别SMTP配置
    domain = from_email.split('@')[-1]
    if not smtp_server or not smtp_port:
        if domain in SMTP_CONFIGS:
            smtp_config = SMTP_CONFIGS[domain]
            smtp_server = smtp_config["server"]
            smtp_port = smtp_config["port"]
            encryption = smtp_config["encryption"]
        else:
            return False, "不支持的邮箱服务商"
    else:
        encryption = "TLS" if int(smtp_port) == 587 else "SSL"
    
    # 创建邮件
    msg = MIMEMultipart('alternative')
    msg['From'] = formataddr(('TrendRadar', from_email))
    msg['To'] = ', '.join(to_emails)
    msg['Subject'] = Header(subject, 'utf-8')
    msg['Date'] = formatdate(localtime=True)
    msg['Message-ID'] = make_msgid()
    
    # 添加HTML内容
    html_part = MIMEText(html_content, 'html', 'utf-8')
    msg.attach(html_part)
    
    try:
        # 连接SMTP服务器
        if encryption == "SSL":
            server = smtplib.SMTP_SSL(smtp_server, smtp_port)
        else:
            server = smtplib.SMTP(smtp_server, smtp_port)
            server.starttls()
        
        # 登录并发送邮件
        server.login(from_email, password)
        server.sendmail(from_email, to_emails, msg.as_string())
        server.quit()
        
        return True, "邮件发送成功"
    except Exception as e:
        return False, f"邮件发送失败: {str(e)}"

8. ntfy推送实现

ntfy是一个开源的推送服务,支持自托管。

8.1 推送实现
def send_ntfy_message(server_url, topic, message, token=None):
    """
    发送ntfy消息
    
    Args:
        server_url: ntfy服务器地址
        topic: 主题名称
        message: 消息内容
        token: 访问令牌(可选)
    """
    url = f"{server_url}/{topic}"
    
    headers = {
        'Content-Type': 'text/plain'
    }
    
    if token:
        headers['Authorization'] = f'Bearer {token}'
    
    try:
        response = requests.post(url, data=message.encode('utf-8'), headers=headers)
        if response.status_code == 200:
            return True, "发送成功"
        else:
            return False, f"发送失败: {response.status_code} {response.text}"
    except Exception as e:
        return False, f"网络异常: {str(e)}"

9. 推送管理器实现

统一管理各种推送渠道:

class NotificationManager:
    def __init__(self, config):
        self.config = config
        self.enabled_channels = self._get_enabled_channels()
    
    def _get_enabled_channels(self):
        """获取启用的推送渠道"""
        channels = []
        if self.config.get("FEISHU_WEBHOOK_URL"):
            channels.append("feishu")
        if self.config.get("DINGTALK_WEBHOOK_URL"):
            channels.append("dingtalk")
        if self.config.get("WEWORK_WEBHOOK_URL"):
            channels.append("wework")
        if self.config.get("TELEGRAM_BOT_TOKEN") and self.config.get("TELEGRAM_CHAT_ID"):
            channels.append("telegram")
        if (self.config.get("EMAIL_FROM") and 
            self.config.get("EMAIL_PASSWORD") and 
            self.config.get("EMAIL_TO")):
            channels.append("email")
        return channels
    
    def send_notifications(self, message, html_message=None):
        """发送通知到所有启用的渠道"""
        results = {}
        
        for channel in self.enabled_channels:
            if channel == "feishu":
                results[channel] = self._send_feishu(message)
            elif channel == "dingtalk":
                results[channel] = self._send_dingtalk(message)
            elif channel == "wework":
                results[channel] = self._send_wework(message)
            elif channel == "telegram":
                results[channel] = self._send_telegram(message)
            elif channel == "email":
                results[channel] = self._send_email(message, html_message)
        
        return results
    
    def _send_feishu(self, message):
        """发送飞书消息"""
        webhook_url = self.config.get("FEISHU_WEBHOOK_URL")
        # 实现飞书推送逻辑
        pass
    
    def _send_dingtalk(self, message):
        """发送钉钉消息"""
        webhook_url = self.config.get("DINGTALK_WEBHOOK_URL")
        # 实现钉钉推送逻辑
        pass
    
    # ... 其他渠道的实现 ...

10. 消息格式化与优化

10.1 统一消息格式
def format_notification_message(news_groups, report_type="daily"):
    """
    格式化通知消息
    
    Args:
        news_groups: 分组的新闻数据
        report_type: 报告类型
        
    Returns:
        tuple: (纯文本消息, HTML消息)
    """
    # 构建文本消息
    text_lines = []
    text_lines.append("📊 热点资讯推送")
    text_lines.append("=" * 30)
    
    # 构建HTML消息
    html_lines = []
    html_lines.append("<h2>📊 热点资讯推送</h2>")
    html_lines.append("<hr>")
    
    for group in news_groups:
        # 文本格式
        text_lines.append(f"📈 {group['keywords']} : {group['count']} 条")
        text_lines.append("")
        
        # HTML格式
        html_lines.append(f"<h3>📈 {group['keywords']} : {group['count']} 条</h3>")
        html_lines.append("<ol>")
        
        for i, news in enumerate(group['news'], 1):
            # 文本格式
            line = f"  {i}. [{news['platform']}] {news['title']}"
            if news.get('rank') and news['rank'] <= 5:
                line += f" [**{news['rank']}**]"
            text_lines.append(line)
            
            # HTML格式
            html_line = f"<li>[{news['platform']}] {news['title']}"
            if news.get('url'):
                html_line = f"<li><a href='{news['url']}'>[{news['platform']}] {news['title']}</a>"
            if news.get('rank') and news['rank'] <= 5:
                html_line += f" <strong>[{news['rank']}]</strong>"
            html_line += "</li>"
            html_lines.append(html_line)
        
        text_lines.append("")
        html_lines.append("</ol>")
        html_lines.append("<br>")
    
    text_lines.append(f"更新时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    html_lines.append(f"<p><em>更新时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</em></p>")
    
    return "\n".join(text_lines), "\n".join(html_lines)

11. 错误处理与重试机制

11.1 重试机制
import time
from functools import wraps

def retry(max_attempts=3, delay=1):
    """重试装饰器"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if attempt == max_attempts - 1:
                        raise e
                    time.sleep(delay * (2 ** attempt))  # 指数退避
            return None
        return wrapper
    return decorator

@retry(max_attempts=3, delay=1)
def send_with_retry(channel, message):
    """带重试机制的消息发送"""
    # 实际的发送逻辑
    pass

12. 性能优化策略

12.1 异步推送
import asyncio
import aiohttp

async def send_notification_async(channel, message):
    """异步发送通知"""
    # 异步推送实现
    pass

async def send_all_notifications_async(messages):
    """并发发送所有通知"""
    tasks = []
    for channel, message in messages.items():
        task = send_notification_async(channel, message)
        tasks.append(task)
    
    results = await asyncio.gather(*tasks, return_exceptions=True)
    return results

13. 安全考虑

13.1 Webhook URL保护
import os
from urllib.parse import urlparse

def validate_webhook_url(url):
    """验证Webhook URL的安全性"""
    try:
        parsed = urlparse(url)
        # 检查协议
        if parsed.scheme not in ['https', 'http']:
            return False, "不支持的协议"
        
        # 检查域名白名单(可选)
        allowed_domains = os.getenv('ALLOWED_WEBHOOK_DOMAINS', '').split(',')
        if allowed_domains and parsed.netloc not in allowed_domains:
            return False, "域名不在白名单中"
        
        return True, "验证通过"
    except Exception as e:
        return False, f"URL格式错误: {str(e)}"

总结

TrendRadar的多渠道推送系统通过模块化设计和统一接口,实现了对企业微信、飞书、钉钉、Telegram、邮件等多种推送渠道的支持。该系统具有良好的扩展性、可靠性和安全性,能够满足不同用户群体的通知需求。通过合理的错误处理、重试机制和性能优化,确保了消息推送的稳定性和及时性。

参考资料

  1. TrendRadar GitHub仓库:https://github.com/sansan0/TrendRadar
  2. 企业微信机器人文档:https://work.weixin.qq.com/api/doc
  3. 飞书机器人文档:https://open.feishu.cn/document/ukTMukTMukTM/ucTM5YjL3ETO24yNxkjN
  4. 钉钉机器人文档:https://developers.dingtalk.com/document/app/custom-robot-access
  5. Telegram Bot API:https://core.telegram.org/bots/api
  6. ntfy文档:https://docs.ntfy.sh/
告别信息过载,只看真正关心的新闻 - 多平台热点聚合工具,一键监控今日头条、百度热搜、微博、抖音、知乎、B站等35个中文平台,智能关键词筛选,自动生成热点分析报告。支持企业微信飞书钉钉、Telegram推送,1分钟部署完毕。让算法为你服务,而非被算法绑架 核心功能: 全网热点聚合 一次监控 11+ 个主流平台(微博、知乎、抖音、百度等),再也不用逐个 APP 刷新闻) 理论上支持 35 个左右的平台,我默认只放了 11 个,想增加什么股票等金融相关资讯的,需要你自己手动添加,有需求的看最下方的高级用法 智能推送策略 提供两种工作模式满足不同需求:默认模式持续追踪热点排名变化、出现频次和时间跨度,定时推送完整分析报告(也包括新增热点);增量模式仅检测新增热点,无新内容时不推送,避免信息噪音 默认模式适合全面了解热点趋势,增量模式适合高频监控(≤30 分钟)场景,专注最新动态而非持续热度 精准内容筛选 设置个人关键词(如:AI、比亚迪、教育政策),只推送相关热点,过滤无关信息 比如关注"新能源",自动筛选出特斯拉降价、政策补贴等相关新闻 多渠道实时推送 支持企业微信飞书钉钉、Telegram 主流聊天工具,消息直达手机 零技术门槛部署 GitHub 一键 Fork 即可使用,无需服务器,无需编程基础。如果要手机接收通知, 要达到 1 分钟部署完毕的效果,请选择企业微信 就,很快 减少 APP 依赖 不再需要频繁打开各种资讯 APP,有效控制手机使用时间 从"被算法推荐绑架"变成"主动获取自己想要的信息" 适合人群: 投资者、自媒体人、企业公关、关心时事的普通用户 典型场景: 股市投资监控、品牌舆情追踪、行业动态关注、生活资讯获取
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CarlowZJ

我的文章对你有用的话,可以支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值