钉钉告警推送@多人脚本

原创作者:运维工程师 谢晋

钉钉告警推送@多人脚本

  1. 钉钉脚本编写

    vi /usr/lib/zabbix/alertscripts/dingding.py

    #!/usr/bin/python3

    -- coding: utf-8 --

    import requests
    import json
    import sys
    import time
    import hmac
    import hashlib
    import base64
    import urllib.parse
    import logging
    import os
    from datetime import datetime

    配置日志系统

    def setup_logging():
    log_file = '/var/log/zabbix/dingding.log'

    复制代码
     # 创建日志目录(如果不存在)
     log_dir = os.path.dirname(log_file)
     if not os.path.exists(log_dir):
         try:
             os.makedirs(log_dir, mode=0o755, exist_ok=True)
             logging.info(f"创建日志目录: {log_dir}")
         except Exception as e:
             print(f"无法创建日志目录 {log_dir}: {e}")
             sys.exit(1)
    
     # 配置日志格式和级别
     logging.basicConfig(
         level=logging.INFO,
         format='%(asctime)s - %(levelname)s - %(message)s',
         handlers=[
             logging.FileHandler(log_file),
             logging.StreamHandler(sys.stdout)  # 同时输出到控制台
         ]
     )
    
     # 设置日志文件权限(如果文件已存在)
     try:
         if os.path.exists(log_file):
             os.chmod(log_file, 0o644)
     except Exception as e:
         print(f"无法设置日志文件权限: {e}")

    def msg(content, at_mobiles=None, is_at_all=False):
    # 记录发送的消息
    logging.info(f"准备发送消息: {content}")
    logging.info(f"@人员: {at_mobiles}, @所有人: {is_at_all}")

    复制代码
     # 钉钉机器人地址
     dingding_url = "https://oapi.dingtalk.com/robot/send?access_token=9c1cbf04a1f37bd44ce736664b6f2f5f2a488fff3c8fbb65538278987aaad8c2"
     # 钉钉的加签
     secret = 'SECf654831ae1d4d4ab32cbd93e7d11e491113c96c986f2110beb29e2e32dacd324'
    
     try:
         # 加签算法
         timestamp = str(round(time.time() * 1000))
         secret_enc = secret.encode('utf-8')
         string_to_sign = '{}\n{}'.format(timestamp, secret)
         string_to_sign_enc = string_to_sign.encode('utf-8')
         hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
         sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))
    
         headers = {'Content-Type': 'application/json;charset=utf-8'}
         api_url = dingding_url + "&timestamp={}&sign={}".format(timestamp, sign)
    
         # 构建@人信息
         at_info = {}
         if at_mobiles:
             at_info["atMobiles"] = at_mobiles
         if is_at_all:
             at_info["isAtAll"] = True
         
         # 在消息内容中添加@信息
         formatted_content = content
         if at_mobiles:
             for mobile in at_mobiles:
                 # 在消息内容中添加 @手机号
                 formatted_content += f" @{mobile}"
         
         json_text = {
             "msgtype": "markdown",
             "markdown": {
                 "title": "zabbix告警",
                 "text": formatted_content  # 使用包含@信息的格式化内容
             },
             "at": at_info
         }
    
         # 记录请求详情(隐藏敏感信息)
         logging.info(f"请求URL: {dingding_url.split('?')[0]}...")
         logging.info(f"请求数据: {json.dumps(json_text, ensure_ascii=False)}")
    
         # 发送请求
         start_time = time.time()
         response = requests.post(api_url, json.dumps(json_text), headers=headers, timeout=30)
         response_time = round((time.time() - start_time) * 1000, 2)
    
         # 记录响应
         logging.info(f"响应状态码: {response.status_code}")
         logging.info(f"响应时间: {response_time}ms")
         logging.info(f"响应内容: {response.text}")
    
         # 检查响应
         if response.status_code == 200:
             result = response.json()
             if result.get('errcode') == 0:
                 logging.info("消息发送成功")
                 return True
             else:
                 error_msg = result.get('errmsg', '未知错误')
                 logging.error(f"消息发送失败: {error_msg}")
                 return False
         else:
             logging.error(f"HTTP请求失败: {response.status_code}")
             return False
    
     except requests.exceptions.Timeout:
         logging.error("请求超时:30秒内未收到响应")
         return False
     except requests.exceptions.ConnectionError:
         logging.error("网络连接错误:无法连接到钉钉服务器")
         return False
     except requests.exceptions.RequestException as e:
         logging.error(f"网络请求异常: {str(e)}")
         return False
     except Exception as e:
         logging.error(f"发送消息时发生未知异常: {str(e)}")
         return False

    def main():
    # 设置日志
    setup_logging()

    复制代码
     # 记录脚本启动
     logging.info("=" * 60)
     logging.info("钉钉通知脚本启动")
     logging.info(f"执行时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
     logging.info(f"命令行参数数量: {len(sys.argv)}")
    
     # 解析参数
     at_mobiles = None
     is_at_all = False
     message_content = ""
     
     # 解析参数
     i = 1
     while i < len(sys.argv):
         if sys.argv[i] == "--at" and i + 1 < len(sys.argv):
             at_mobiles = sys.argv[i + 1].split(',')
             i += 2
         elif sys.argv[i] == "--at-all":
             is_at_all = True
             i += 1
         else:
             # 处理带空格的消息内容
             if message_content:
                 message_content += " " + sys.argv[i]
             else:
                 message_content = sys.argv[i]
             i += 1
    
     # 默认@多个人(这里设置多个手机号)
     if at_mobiles is None:
         at_mobiles = ["13800138000", "13900139000", "15000150000", "15100151000"]  # 替换为实际的多个手机号
         logging.info(f"使用默认@多人: {at_mobiles}")
    
     try:
         if not message_content.strip():
             logging.error("错误: 消息内容为空")
             print("错误: 消息内容为空")
             sys.exit(1)
    
         # 发送消息
         success = msg(message_content, at_mobiles, is_at_all)
    
         if success:
             logging.info("钉钉通知脚本执行成功")
             print("消息发送成功")
             sys.exit(0)
         else:
             logging.error("钉钉通知脚本执行失败")
             print("消息发送失败")
             sys.exit(1)
    
     except KeyboardInterrupt:
         logging.warning("脚本被用户中断")
         print("\n操作已取消")
         sys.exit(1)
     except Exception as e:
         logging.exception("脚本执行过程中发生未预期的异常")
         print(f"错误: {str(e)}")
         sys.exit(1)

    if name == 'main':
    main()

相关推荐
leisoo80971 分钟前
财报舞弊预警系统实战用Python挖掘应收存货异常因子 IG50免费开源股票数据API接口
开发语言·python
智购科技自动贩卖机8 分钟前
自动售货机嵌入式系统Go语言开发实践:从资源受限设备到RTOS协程调度的工程化之路
大数据·linux·数据库·人工智能·yolo·架构·golang
SelectDB15 分钟前
2026 年 Apache Doris 和 StarRocks 怎么选?一篇更接近真实选型的对比
大数据·数据库·数据分析
祢真伟大18 分钟前
DM8 归档日志挖掘导致 TEMP 表空间不释放的问题排查
数据库
qq_4850152124 分钟前
MyBatis-Plus 3.x FieldStrategy 作用
java·数据库·mybatis
lhldsg26 分钟前
课程排课系统实战指南:从数据库设计到算法调优全流程解析
java·数据库·算法·小程序
丰锋ff43 分钟前
数据库操作的一些相关命令
数据库
cui_ruicheng1 小时前
LangChain 应用开发(十二):Agent 中间件与内置中间件
人工智能·python·中间件·langchain
往事只能回味味道1 小时前
MySQL创建数据库并授权指导用户与IP
数据库·tcp/ip·mysql
zx_741484811 小时前
【Python 入门】Python 正则表达式零基础讲解:基础语法 + 元字符 + 常用案例
python·正则表达式