钉钉告警推送@多人脚本

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

钉钉告警推送@多人脚本

  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()

相关推荐
小高不会迪斯科15 小时前
CMU 15445学习心得(二) 内存管理及数据移动--数据库系统如何玩转内存
数据库·oracle
YJlio15 小时前
1.7 通过 Sysinternals Live 在线运行工具:不下载也能用的“云端工具箱”
c语言·网络·python·数码相机·ios·django·iphone
e***89015 小时前
MySQL 8.0版本JDBC驱动Jar包
数据库·mysql·jar
l1t15 小时前
在wsl的python 3.14.3容器中使用databend包
开发语言·数据库·python·databend
山塘小鱼儿16 小时前
本地Ollama+Agent+LangGraph+LangSmith运行
python·langchain·ollama·langgraph·langsimth
码说AI17 小时前
python快速绘制走势图对比曲线
开发语言·python
失忆爆表症17 小时前
03_数据库配置指南:PostgreSQL 17 + pgvector 向量存储
数据库·postgresql
AI_567817 小时前
Excel数据透视表提速:Power Query预处理百万数据
数据库·excel
wait_luky17 小时前
python作业3
开发语言·python