# _*_ coding:utf-8 _*_
# author:slx
# createdate:2022/11/25
# content : 获取路由器公网ip,并将数据上传到sql数据库
# */30 * * * * /usr/bin/python /root/ip_wan.py > /root/ip_wan.log 2>&1 &

import os
import pymysql
import requests
import json
from datetime import datetime


class IPMonitor:
    """
    家庭公网 IP 监控与推送工具
    """

    def __init__(self):
        # 数据库配置
        self.db_config = {
            "host": "192.168.36.123",
            "user": "hailey",
            "password": "PSzLRW!9efXxWC9du",
            "database": "localData",
            "port": 3306,
            "charset": "utf8mb4"
        }

        # Webhook 地址(IFTTT)
        self.ifttt_url = "https://maker.ifttt.com/trigger/openwrt_ip/with/key/bPCYm3o8Nm-3TfcN_5P2fX"

        # Synology Chat Webhook
        self.chat_url = (
            "https://chat.52uz.com/webapi/entry.cgi"
            "?api=SYNO.Chat.External&method=incoming&version=2"
            "&token=%22DiDyQVwREcHE4njbX3cVpmjzNJ6gVg3rVRvmDnE7dqIyXeK8E5vGOdKuJRUvt4Lp%22"
        )
        
        # Bark 推送地址
        self.bark_url = "https://bark.52or.com/tzqGtnEpNoJUEuS2dG7oQg"

        # magicpush 对应的url
        self.magic_push_url = "https://info.slzxf.com/api/push/d3fffd7525b7406cb9d38ee9a20121e5"

        # ntfy 推送地址
        self.ntfy_url = "https://ntfy.52or.com"
        self.BEARER_TOKEN = "tk_pfzbvyy6x8q10kft76ohzuyba9d48"
    
    # ===============================
    # 数据库操作
    # ===============================
    
    def get_last_ip(self):
        """
        查询数据库中最新一条IP记录
        """
        sql = "SELECT `ip` FROM `openwrtip` ORDER BY id DESC LIMIT 1;"
        try:
            with pymysql.connect(**self.db_config) as conn:
                with conn.cursor() as cursor:
                    cursor.execute(sql)
                    result = cursor.fetchone()
                    if result:
                        print(f"[INFO] 数据库最新IP:\t{result[0]}")
                        return result[0]
                    else:
                        print("[INFO] 数据库中暂无记录。")
                        return ""
        except Exception as e:
            print(f"[ERROR] 查询数据库失败:{e}")
            return ""

    def insert_record(self, date, time, ip_addr):
        """
        插入新的 IP 记录
        :param date:        日期
        :param time:        时间
        :param ip_addr:     ip地址
        """
        sql = "INSERT INTO openwrtip(`date`, `time`, `ip`) VALUES (%s, %s, %s)"
        try:
            with pymysql.connect(**self.db_config) as conn:
                with conn.cursor() as cursor:
                    cursor.execute(sql, (date, time, ip_addr))
                    conn.commit()
            print(f"[SUCCESS] 数据库插入成功:\t{ip_addr}")
        except Exception as e:
            print(f"[ERROR] 数据库插入失败:{e}")

    # ===============================
    # Webhook 推送
    # ===============================

    def send_ifttt(self, ip_addr):
        """
        推送到 IFTTT
        :param ip_addr: ip地址
        """
        payload = {"value1": ip_addr}
        headers = {"Content-Type": "application/json"}
        try:
            requests.post(self.ifttt_url, json=payload, headers=headers, timeout=10)
            print(f"[SUCCESS] 已推送到 IFTTT:{ip_addr}")
        except Exception as e:
            print(f"[ERROR] IFTTT 推送失败:{e}")

    def send_synology_chat(self, date, time, msg):
        """
        发送消息到Synology Chat(兼容markdown风格);
        :param date:    日期
        :param time:    时间
        :param msg:     要发送的消息内容(可包含简单的 HTML)。
        """

        # 这里 Chat 只认 "payload"，且 payload 内必须是 JSON 格式字符串
        payload = {
            "text": date + " " + time + " \n" + "【今日ip地址】" + msg  # 支持简单 HTML，例如 <b>加粗</b>、<font color="red">红字</font> 等
        }

        data = {
            "payload": json.dumps(payload, ensure_ascii=False)
        }

        try:
            response = requests.post(self.chat_url, data=data, timeout=10)
            response.raise_for_status()
            print("✅ Synology Chat 消息发送成功")
        except requests.RequestException as e:
            print(f"❌ Synology Chat 消息发送失败:{e}")
    
    def send_bark(self, title, message):
        """
        通过 Bark 推送通知到手机
        :param title:   通知标题
        :param message: 通知内容
        """
        try:
            url = f"{self.bark_url}/{title}/{message}"
            # 可选参数:icon、group、sound、isArchive 等
            params = {
                "icon": "https://www.bit.ac.cn/wp-content/uploads/2022/10/avast.jpg",
                "group": "OpenWRT",
                "sound": "bell"
            }
            response = requests.get(url, params=params, timeout=10)
            response.raise_for_status()
            print(f"✅ Bark 推送成功:\t{message}")
        except requests.RequestException as e:
            print(f"❌ Bark 推送失败!: {e}")

    def send_magic_push(self, title, message):
        """
        推送通知接口
        :param title:   通知标题
        :param message: 通知内容
        """
        try:
            
            headers = {
                "Content-Type": "application/json"
            }
            payload = {
                "title": title,
                "content": message,
                "type": "text"
            }
            response = requests.post(
                url=self.magic_push_url,
                headers=headers,
                data=json.dumps(payload),
                timeout=10
            )
            response.raise_for_status()
            print(f"✅ 推送成功:\t{message}")
        except requests.RequestException as e:
            print(f"❌ 推送失败!: {e}")

    def send_ntfy(
        self,
        topic: str,
        msg: str,
        title: str = "系统通知",
        priority: int = 3,
        tags: list = None,
        email: str = None
    ):
        """
        发送 ntfy 通知

        :param topic: ntfy主题
        :param msg: 通知正文
        :param title: 通知标题
        :param priority: 优先级 1-5
        :param tags: 标签列表，例如 ["warning", "globe_with_meridians"]
        :param email: 邮件通知地址
        """

        url = self.ntfy_url.rstrip("/")
        payload = {
            "topic": topic,
            "message": msg,
            "title": title,
            "priority": priority,
            "tags": tags or []
        }

        if email:
            payload["email"] = email

        headers = {
            "Authorization": f"Bearer {self.BEARER_TOKEN}"
        }

        try:
            resp = requests.post(
                url,
                json=payload,
                headers=headers,
                timeout=15
            )
            resp.raise_for_status()
            print("✅ ntfy通知发送成功")
            # print(resp.json())
            return True

        except requests.exceptions.RequestException as e:
            print(f"❌ ntfy发送失败:{e}")
            if "resp" in locals():
                print(resp.text)
            return False

    # ===============================
    # 系统命令与执行逻辑
    # ===============================

    def get_current_ip(self):
        """
        获取当前pppoe-wan的公网IP
        """
        cmd = "ifconfig pppoe-wan | grep 'inet ' | awk '{print $2}'| cut -d ':' -f 2"
        try:
            ip_addr = os.popen(cmd).read().strip()
            if not ip_addr:
                raise ValueError("未获取到 IP 地址")
            print(f"[INFO] 当前公网IP: \t{ip_addr}")
            return ip_addr
        except Exception as e:
            print(f"[ERROR] 获取IP失败: {e}")
            return ""

    def run(self):
        """
        执行主逻辑
        """
        print("=" * 50)
        now = datetime.now()
        date = now.strftime('%Y-%m-%d')
        time = now.strftime('%H:%M:%S')
        print(f'[INFO] 运行时间:\t{date} {time}')
        current_ip = self.get_current_ip()
        last_ip = self.get_last_ip()
        if not current_ip:
            print("[WARN] 未检测到有效IP, 程序结束")
            return

        if current_ip != last_ip:
            print("[INFO] 检测到IP变更, 准备写入数据库并推送通知...")
            self.insert_record(date, time, current_ip)
            # self.send_ifttt(current_ip)
            self.send_synology_chat(date, time, current_ip)
            self.send_bark("OpenWRT公网IP变更", f"今日({date})IP地址:{current_ip}")
            self.send_ntfy(topic="local-ip", title="OpenWRT公网IP变更", msg=f"今日({date})IP地址:{current_ip}")
            self.send_magic_push("OpenWRT公网IP变更", f"今日({date})IP地址:{current_ip}")
            
        else:
            print("[INFO] IP未变化,无需更新")
        
        print("=" * 50)

if __name__ == "__main__":
    monitor = IPMonitor()
    monitor.run()
