监控IP,网站将异常情况通过飞书机器人发至指定群内

界面如下,丑是丑了点,但主打一个实用。

主要就是通过ping,就是一直在ping,当不通的时候,就根据你设置的报警时间,主要是利用飞书机器人来给飞书指定群里发异常信息报警。

直接上代码

python 复制代码
import subprocess
import time
import requests
import threading
import logging
import tkinter as tk
from tkinter import messagebox, simpledialog
import json
import os
import sys

# 获取程序所在目录
if getattr(sys, 'frozen', False):  # 如果程序被打包成exe
    PROGRAM_DIR = os.path.dirname(sys.executable)  # 使用exe所在目录
else:  # 如果是脚本运行
    PROGRAM_DIR = os.path.dirname(os.path.abspath(__file__))  # 使用脚本所在目录

# 日志文件路径
LOG_FILE = os.path.join(PROGRAM_DIR, "ip_monitor.log")
CONFIG_FILE = os.path.join(PROGRAM_DIR, "config.json")

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s",
    handlers=[
        logging.FileHandler(LOG_FILE, encoding="utf-8"),  # 日志写入文件
        logging.StreamHandler()  # 日志输出到控制台
    ]
)

# 输出日志和配置文件的路径,方便调试
logging.info(f"程序所在目录: {PROGRAM_DIR}")
logging.info(f"日志文件路径: {LOG_FILE}")
logging.info(f"配置文件路径: {CONFIG_FILE}")

class IPMonitor:
    def __init__(self):
        self.running = False
        self.thread = None
        self.start_time = None  # 记录监控启动时间
        self.last_normal_notify_time = {}  # 记录每个目标最后一次正常通知的时间

    def ping_target(self, target, ping_count, ping_size):
        """Ping目标(IP或域名),返回是否连通,并在日志中显示详细过程"""
        try:
            # 执行Ping命令,捕获输出
            result = subprocess.run(
                ['ping', '-n', str(ping_count), '-l', str(ping_size), '-w', '1000', target],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True,
                creationflags=subprocess.CREATE_NO_WINDOW  # 隐藏窗口(仅Windows有效)
            )
            # 将Ping命令的完整输出写入日志
            logging.info(f"Ping {target} 过程:\n{result.stdout}")
            if result.stderr:
                logging.error(f"Ping {target} 错误:\n{result.stderr}")
            # 检查输出中是否包含"回复"或"Reply"
            if "回复" in result.stdout or "Reply" in result.stdout:
                return True
            else:
                return False
        except Exception as e:
            logging.error(f"Ping命令执行错误 {target}: {e}")
            return False

    def send_feishu_message(self, webhook_url, message):
        """发送飞书群消息"""
        try:
            response = requests.post(webhook_url, json={"msg_type": "text", "content": {"text": message}})
            if response.status_code == 200:
                logging.info("消息发送成功")
            else:
                logging.error(f"消息发送失败: {response.status_code}, {response.text}")
        except Exception as e:
            logging.error(f"发送消息错误: {e}")
        finally:
            if 'response' in locals():
                response.close()

    def monitor_targets(self, target_info_list, webhook_url, monitor_interval, enable_normal_notify, normal_notify_interval, ping_count, ping_size):
        """监控目标列表(IP或域名),根据设置发送通知"""
        while self.running:
            for target_info in target_info_list:
                target, comment = target_info
                if not self.ping_target(target, ping_count, ping_size):
                    self.send_feishu_message(webhook_url, f"🛑 检测到目标无法访问:{target} ({comment}),请检查!")
                else:
                    current_time = time.time()
                    # 检查是否启用了正常通知,并且满足时间间隔
                    if enable_normal_notify and (
                        target not in self.last_normal_notify_time or
                        (current_time - self.last_normal_notify_time.get(target, 0)) >= normal_notify_interval * 60
                    ):
                        self.send_feishu_message(webhook_url, f"✅ 目标 {target} ({comment}) 正常")
                        self.last_normal_notify_time[target] = current_time
                    logging.info(f"✅ 目标 {target} ({comment}) 正常")
            time.sleep(monitor_interval)  # 根据设置的间隔检查

    def start(self, target_info_list, webhook_url, monitor_interval, enable_normal_notify, normal_notify_interval, ping_count, ping_size):
        """启动监控"""
        if self.running:
            logging.warning("监控已在运行中")
            return
        self.running = True
        self.start_time = time.time()  # 记录启动时间
        self.thread = threading.Thread(
            target=self.monitor_targets,
            args=(target_info_list, webhook_url, monitor_interval, enable_normal_notify, normal_notify_interval, ping_count, ping_size)
        )
        self.thread.daemon = True  # 设置为守护线程,主线程退出时自动结束
        self.thread.start()
        logging.info("监控已启动")

    def stop(self):
        """停止监控"""
        self.running = False
        if self.thread:
            self.thread.join(timeout=1)  # 最多等待1秒让线程退出
            if self.thread.is_alive():
                logging.warning("监控线程未能及时退出,强制终止")
        self.start_time = None  # 清空启动时间
        logging.info("监控已停止")

class IPMonitorGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("IP和域名监控工具")
        self.root.geometry("500x600")

        self.monitor = IPMonitor()

        # 加载配置文件
        self.config = self.load_config()

        # 飞书Webhook地址输入
        self.label_webhook = tk.Label(root, text="飞书Webhook地址:")
        self.label_webhook.pack(pady=5)
        self.button_set_webhook = tk.Button(root, text="设置飞书Webhook地址", command=self.set_webhook)
        self.button_set_webhook.pack(pady=5)

        # 目标(IP或域名)输入框(带滚动条)
        self.label_target = tk.Label(root, text="需要监控的目标(每行一个,格式:IP/域名 | 备注):")
        self.label_target.pack(pady=5)
        self.frame_target = tk.Frame(root)
        self.frame_target.pack(pady=5)
        self.text_target = tk.Text(self.frame_target, height=10, width=50)
        self.text_target.pack(side=tk.LEFT, fill=tk.BOTH)
        self.scroll_target = tk.Scrollbar(self.frame_target, command=self.text_target.yview)
        self.scroll_target.pack(side=tk.RIGHT, fill=tk.Y)
        self.text_target.config(yscrollcommand=self.scroll_target.set)
        if "target_info_list" in self.config:
            self.text_target.insert(tk.END, "\n".join([" | ".join(info) for info in self.config["target_info_list"]]))

        # Ping包次数设置
        self.label_ping_count = tk.Label(root, text="Ping包次数:")
        self.label_ping_count.pack(pady=5)
        self.entry_ping_count = tk.Entry(root)
        self.entry_ping_count.pack(pady=5)
        self.entry_ping_count.insert(0, str(self.config.get("ping_count", 1)))

        # Ping包大小设置
        self.label_ping_size = tk.Label(root, text="Ping包大小(字节):")
        self.label_ping_size.pack(pady=5)
        self.entry_ping_size = tk.Entry(root)
        self.entry_ping_size.pack(pady=5)
        self.entry_ping_size.insert(0, str(self.config.get("ping_size", 32)))

        # 报警时间间隔设置
        self.label_monitor_interval = tk.Label(root, text="报警时间间隔(秒):")
        self.label_monitor_interval.pack(pady=5)
        self.entry_monitor_interval = tk.Entry(root)
        self.entry_monitor_interval.pack(pady=5)
        self.entry_monitor_interval.insert(0, str(self.config.get("monitor_interval", 30)))

        # 正常通知开关
        self.enable_normal_notify = tk.BooleanVar(value=self.config.get("enable_normal_notify", False))
        self.check_normal_notify = tk.Checkbutton(root, text="启用正常通知", variable=self.enable_normal_notify)
        self.check_normal_notify.pack(pady=5)

        # 正常通知时间间隔设置
        self.label_normal_notify_interval = tk.Label(root, text="正常通知时间间隔(分钟):")
        self.label_normal_notify_interval.pack(pady=5)
        self.entry_normal_notify_interval = tk.Entry(root)
        self.entry_normal_notify_interval.pack(pady=5)
        self.entry_normal_notify_interval.insert(0, str(self.config.get("normal_notify_interval", 5)))

        # 按钮区域
        self.button_frame = tk.Frame(root)
        self.button_frame.pack(pady=10)

        self.button_start = tk.Button(self.button_frame, text="开始监控", command=self.start_monitor)
        self.button_start.pack(side=tk.LEFT, padx=10)

        self.button_stop = tk.Button(self.button_frame, text="停止监控", command=self.stop_monitor)
        self.button_stop.pack(side=tk.LEFT, padx=10)
        self.button_stop.config(state=tk.DISABLED)  # 初始禁用停止按钮

        # 日志显示区域
        self.label_log = tk.Label(root, text="日志:")
        self.label_log.pack(pady=5)
        self.text_log = tk.Text(root, height=10, width=60)
        self.text_log.pack(pady=5)

        # 绑定窗口关闭事件
        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)

    def set_webhook(self):
        """设置飞书Webhook地址"""
        webhook_url = simpledialog.askstring("设置飞书Webhook地址", "请输入飞书Webhook地址:")
        if webhook_url:
            self.config["webhook_url"] = webhook_url
            self.save_config()

    def load_config(self):
        """加载配置文件"""
        try:
            if os.path.exists(CONFIG_FILE):
                with open(CONFIG_FILE, "r", encoding="utf-8") as f:
                    return json.load(f)
        except Exception as e:
            logging.error(f"加载配置文件失败: {e}")
        return {}

    def save_config(self):
        """保存配置文件"""
        try:
            # 获取当前目标列表
            target_info_list = []
            for line in self.text_target.get("1.0", tk.END).strip().splitlines():
                if "|" in line:
                    target, comment = map(str.strip, line.split("|", 1))
                    target_info_list.append((target, comment))
                else:
                    target_info_list.append((line.strip(), "无备注"))
            self.config["target_info_list"] = target_info_list

            # 保存其他设置
            self.config["ping_count"] = int(self.entry_ping_count.get())
            self.config["ping_size"] = int(self.entry_ping_size.get())
            self.config["monitor_interval"] = int(self.entry_monitor_interval.get())
            self.config["enable_normal_notify"] = self.enable_normal_notify.get()
            self.config["normal_notify_interval"] = int(self.entry_normal_notify_interval.get())
            with open(CONFIG_FILE, "w", encoding="utf-8") as f:
                json.dump(self.config, f, ensure_ascii=False, indent=4)
            logging.info("配置文件已保存")
        except Exception as e:
            logging.error(f"保存配置文件失败: {e}")

    def start_monitor(self):
        """启动监控"""
        webhook_url = self.config.get("webhook_url", "").strip()
        target_info_list = []
        for line in self.text_target.get("1.0", tk.END).strip().splitlines():
            if "|" in line:
                target, comment = map(str.strip, line.split("|", 1))
                target_info_list.append((target, comment))
            else:
                target_info_list.append((line.strip(), "无备注"))

        if not webhook_url or not target_info_list:
            messagebox.showerror("错误", "请设置飞书Webhook地址和填写需要监控的目标!")
            return

        try:
            ping_count = int(self.entry_ping_count.get())
            ping_size = int(self.entry_ping_size.get())
            monitor_interval = int(self.entry_monitor_interval.get())
            normal_notify_interval = int(self.entry_normal_notify_interval.get())
        except ValueError:
            messagebox.showerror("错误", "Ping包次数、Ping包大小和时间间隔必须为整数!")
            return

        self.monitor.start(
            target_info_list,
            webhook_url,
            monitor_interval,
            self.enable_normal_notify.get(),
            normal_notify_interval,
            ping_count,
            ping_size
        )
        self.button_start.config(state=tk.DISABLED)
        self.button_stop.config(state=tk.NORMAL)
        self.log("监控已启动")

        # 保存配置
        self.save_config()

    def stop_monitor(self):
        """停止监控"""
        self.monitor.stop()
        self.button_start.config(state=tk.NORMAL)
        self.button_stop.config(state=tk.DISABLED)
        self.log("监控已停止")

    def log(self, message):
        """在日志区域显示消息"""
        self.text_log.insert(tk.END, message + "\n")
        self.text_log.see(tk.END)

    def on_closing(self):
        """窗口关闭时确保监控停止"""
        if self.monitor.running:
            self.monitor.stop()
        self.root.destroy()

# 启动GUI
if __name__ == "__main__":
    root = tk.Tk()
    app = IPMonitorGUI(root)
    root.mainloop()

直接打包即可使用。打包命令

pyinstaller --onefile --windowed 12.py

相关推荐
Dxy123931021630 分钟前
Python基于BERT的上下文纠错详解
开发语言·python·bert
SiYuanFeng2 小时前
Colab复现 NanoChat:从 Tokenizer(CPU)、Base Train(CPU) 到 SFT(GPU) 的完整踩坑实录
python·colab
炸炸鱼.3 小时前
Python 操作 MySQL 数据库
android·数据库·python·adb
_深海凉_3 小时前
LeetCode热题100-颜色分类
python·算法·leetcode
AC赳赳老秦4 小时前
OpenClaw email技能:批量发送邮件、自动回复,高效处理工作邮件
运维·人工智能·python·django·自动化·deepseek·openclaw
zhaoshuzhaoshu4 小时前
Python 语法之数据结构详细解析
python
AI问答工程师4 小时前
Meta Muse Spark 的"思维压缩"到底是什么?我用 Python 复现了核心思路(附代码)
人工智能·python
zfan5205 小时前
python对Excel数据处理(1)
python·excel·pandas
小饕5 小时前
我从零搭建 RAG 学到的 10 件事
python
老歌老听老掉牙5 小时前
PyQt5+Qt Designer实战:可视化设计智能参数配置界面,告别手动布局时代!
python·qt