监控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

相关推荐
小宁爱Python26 分钟前
Python从入门到精通2:SQLite数据库(FastAPI + SQLite全流程开发指南)
数据库·python·sqlite·fastapi
xinxiyinhe29 分钟前
Python在图像处理领域的第三方库支持(三)
开发语言·图像处理·python
李昊哲小课31 分钟前
垃圾短信分类
人工智能·python·机器学习·自然语言处理·分类·数据挖掘·中文分词
长安海31 分钟前
SPPAS安装及问题汇总
python·sppas
Cccc吃吃吃2 小时前
python中深浅拷贝以及可变对象和不可变对象
开发语言·jvm·python·beautifulsoup·numpy·pyqt·fastapi
hx20243 小时前
R语言ggplot2散点形状和填充
开发语言·python·r语言
带娃的IT创业者3 小时前
《Python实战进阶》第33集:PyTorch 入门-动态计算图的优势
开发语言·pytorch·python
恶霸不委屈3 小时前
如何基于ios部署Deep Seek?
python·ios·swift
葡萄成熟时_3 小时前
【第十三届“泰迪杯”数据挖掘挑战赛】【2025泰迪杯】A题解题全流程(持续更新)
人工智能·python·数据挖掘·蓝桥杯
蝉叫醒了夏天3 小时前
【 数据分析入门指南:核心概念与Python实践】
python·数据挖掘·数据分析