为了账号安全,请及时绑定邮箱和手机立即绑定

Zabbix微信个人账号告警

标签:
Python


前言:

    最近研究zabbix告警,网上看了帖子有各式各样姿势:电话语音告警,邮件告警,短信告警,微信公众号告警等等等..姿势五花八门,真是纠结。

    电话语音告警,短信告警首先pass 前者花钱,后者通过设置139邮箱,就可以实现伪短信告警效果。

    剩下邮件告警与微信公众号告警。邮件告警已经在部署的时候配置完毕,剩下这个微信公众号告警,查一下帖子,申请各种麻烦。那么有没有基于微信个人账号的告警呢?想到这个点,马上github一番。

搜索到一些优秀的开源代码:https://github.com/0x5e/wechat-deleted-friends

及封包:https://github.com/xiangzhai/qwx/blob/master/doc/protocol.md

    经过一番思考,大致思路如下:

     微信WEB保持活动状态,通过不断使用zabbix api抓取故障告警,入库后微信发送告警给相关人员。

一 需求实现具体思路

    实现该需求是得有多个线程同时进行的

微信心跳,微信WEB版保持活动状态。

ZABBIX API ,不断请求zabbix告警,发现告警后,判断后入库。

使用数据库不断查询告警,如果发现符合条件告警则发送告警给相关人员。

二 部分代码及注释

第一部分:wechat

# coding=utf-8

#伪装请求头

headers = {'User-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.125 Safari/537.36'}

myRequests = requests.Session()

myRequests.headers.update(headers)

DEBUG = False

#微信类

class WeChat(object):

    def __init__(self):

        self.uuid = ''

        self.base_uri = ''

        self.push_uri = ''

        self.redirect_uri = ''

        self.BaseRequest = {}

        self.skey = ''

        self.wxuin = ''

        self.wxsid = ''

        self.skey = ''

        self.deviceId = 'e' + repr(random())[2:17]     #随机生成15位机器码

        self.pass_ticket = ''

        self.MemberList =[]

        self.ContactList = []

        self.AlarmFriends =[]

        self.Intervals = ''

        self.xintiao = ''

        

    #获取UUID

    def Get_UUID(self):

        url = 'https://login.weixin.qq.com/jslogin'

        params = {

            'appid': 'wx782c26e4c19acffb',

            'fun': 'new',

            'lang': 'zh_CN',

            '_': int(time.time()),

        }

        r = myRequests.get(url=url, params=params)

        r.encoding = 'utf-8'

        data = r.text

        #       data返回,code=200为状态.uuid="IZTW06WnSg=="为uuid

        #   window.QRLogin.code = 200; window.QRLogin.uuid = "IZtWO6WnSg==";

        # 正则匹配:匹配出状态码    以及UUID

        regx = r'window.QRLogin.code = (\d+); window.QRLogin.uuid = "(\S+?)"'

        PM = re.search(regx, data)

        code = PM.group(1)

        if code == '200':

            self.uuid = PM.group(2)

            return True

        return False

        

    #   windows下直接打开二维码图

    def _openWinQRCodeImg(self):

        url = 'https://login.weixin.qq.com/qrcode/' + self.uuid

        params = {

            't': 'webwx',

            '_': int(time.time())

        }

        r = myRequests.get(url=url, params=params)

        f = open(QRImagePath, 'wb')

        f.write(r.content)

        f.close()

        time.sleep(1)

        os.startfile(QRImagePath)

        

    #   Linux下的二维码处理

    def _printQR(self, mat):

        for i in mat:

            BLACK = '\033[40m  \033[0m'

            WHITE = '\033[47m  \033[0m'

            print (''.join([BLACK if j else WHITE for j in i]))

    def _str2qr(self, str):

        qr = qrcode.QRCode()

        qr.border = 1

        qr.add_data(str)

        mat = qr.get_matrix()

        self._printQR(mat)  # qr.print_tty() or qr.print_ascii()

        

    #   判断操作系统,选择打开二维码扫描方式

    def genQRCode(self):

        if sys.platform.startswith('win'):

            self._openWinQRCodeImg()

        else:

            self._str2qr('https://login.weixin.qq.com/l/' + self.uuid)

            

    #等待登陆

    def WaitForLogin(self, tip=1):

        time.sleep(tip)

        url = 'https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?tip=%s&uuid=%s&_=%s' % (

            tip, self.uuid, int(time.time()))

        r = myRequests.get(url=url)

        r.encoding = 'utf-8'

        data = r.text

        #   data返回:

        #   window.code = 201;

        #判断返回码

        regx = r'window.code=(\d+);'

        pm = re.search(regx, data)

        code = pm.group(1)

        if code == '201':  # 已扫描

            print('[*]成功扫描,请在手机上点击确认以登录')

        elif code == '200':  # 已登录

            print('[.]正在登录...')

            regx = r'window.redirect_uri="(\S+?)";'

            pm = re.search(regx, data)

            self.redirect_uri = pm.group(1) + '&fun=new'

            base_uri = self.redirect_uri[:self.redirect_uri.rfind('/')]

            # push_uri与base_uri对应关系(排名分先后)

            services = [

                ('wx2.qq.com', 'webpush2.weixin.qq.com'),

                ('qq.com', 'webpush.weixin.qq.com'),

                ('web1.wechat.com', 'webpush1.wechat.com'),

                ('web2.wechat.com', 'webpush2.wechat.com'),

                ('wechat.com', 'webpush.wechat.com'),

                ('web1.wechatapp.com', 'webpush1.wechatapp.com'),

            ]

          #  self.push_uri = self.base_uri

            self.push_uri = base_uri

            for (searchUrl, pushUrl) in services:

                if base_uri.find(searchUrl) >= 0:

                    self.push_uri = 'https://%s/cgi-bin/mmwebwx-bin' % pushUrl

                    self.base_uri = 'https://%s/cgi-bin/mmwebwx-bin' % searchUrl

                    break

        elif code == '408':  # 超时

            pass

        # elif code == '400' or code == '500':

        return code

        

    #登陆

    def login(self):

        r = myRequests.get(url=self.redirect_uri)

        r.encoding = 'utf-8'

        data = r.text

    #    print (data)

        # data返回

        #   < ret > 0 < / ret > < message > OK < / message >

        #   < skey >XXXX < skey >

        #   < wxsid > XXXX < / wxsid >

        #   < wxuin > XXXX < / wxuin >

        #   < pass_ticket > XXXX < / pass_ticket >

        #   < isgrayscale > 1 < / isgrayscale >

        

        #解析XML文件

        doc = xml.dom.minidom.parseString(data)

        root = doc.documentElement

        for node in root.childNodes:

            if node.nodeName == 'skey':

                self.skey = node.childNodes[0].data

            elif node.nodeName == 'wxsid':

                self.wxsid = node.childNodes[0].data

            elif node.nodeName == 'wxuin':

                self. wxuin = node.childNodes[0].data

            elif node.nodeName == 'pass_ticket':

                self.pass_ticket = node.childNodes[0].data

    #    print('skey: %s, wxsid: %s, wxuin: %s, pass_ticket: %s' % (skey, wxsid,wxuin, pass_ticket))

        if not all((self.skey, self.wxsid, self.wxuin, self.pass_ticket)):

            return False

        self.BaseRequest = {

            'Uin': int(self.wxuin),

            'Sid': self.wxsid,

            'Skey': self.skey,

            'DeviceID': self.deviceId,

        }

     #   print (self.push_uri)

        return True

    def webwxinit(self):

        url = ( self.base_uri +'/webwxinit?pass_ticket=%s&skey=%s&r=%s' \

                % (self.pass_ticket, self.skey, int(time.time())))

        params = {'BaseRequest': self.BaseRequest}

        headers = {'content-type': 'application/json; charset=UTF-8'}

        r = myRequests.post(url=url, data=json.dumps(params), headers=headers)

        r.encoding = 'utf-8'

        data = r.json()

        self.SyncKey = data['SyncKey']

        self.User = data['User']

        if False:

            f = open(os.path.join(os.getcwd(), 'webwxinit.json'), 'wb')

            f.write(r.content)

            f.close()

        self.synckey = '|'.join([str(keyVal['Key']) + '_' + str(keyVal['Val']) for keyVal in self.SyncKey['List']])

        state = self.responseState('webwxinit', data['BaseResponse'])

        return state

    def webwxstatusnotify(self):

        url = self.base_uri + \

            '/webwxstatusnotify?lang=zh_CN&pass_ticket=%s' % (self.pass_ticket)

        params = {

            'BaseRequest': self.BaseRequest,

            "Code": 3,

            "FromUserName": self.User['UserName'],

            "ToUserName": self.User['UserName'],

            "ClientMsgId": int(time.time())

        }

        r = myRequests.post(url=url, params=json.dumps(params))

        data = r.json()

        state = self.responseState('WexinStatusNoTify',data['BaseResponse'])

        #return data['BaseResponse']['Ret'] == 0

        return state

        

    #获取好友列表

    def webwxgetcontact(self):

        url = (self.base_uri +'/webwxgetcontact?pass_ticket=%s&skey=%s&r=%s' % (\

                self.pass_ticket, self.skey, int(time.time())))

        headers = {'content-type': 'application/json; charset=UTF-8'}

        r = myRequests.post(url=url, headers=headers)

        r.encoding = 'utf-8'

        data = r.json()

        if False:

            f = open(os.path.join(os.getcwd(), 'webwxgetcontact.json'), 'wb')

            f.write(r.content)

            f.close()

        self.MemberList = data['MemberList']

        SpecialUsers = ["newsapp", "fmessage", "filehelper", "weibo", "qqmail", "tmessage", "qmessage",\

                        "qqsync","floatbottle", "lbsapp", "shakeapp", "medianote", "qqfriend", "readerapp",\

                        "blogapp", "facebookapp", "masssendapp","meishiapp", "feedsapp", "voip",\

                        "blogappweixin", "weixin", "brandsessionholder", "weixinreminder",\

                        "wxid_novlwrv3lqwv11", "gh_22b87fa7cb3c", "officialaccounts",\

                        "notification_messages", "wxitil", "userexperience_alarm"]

        #将列表中特殊账号删除

        for i in range(len(self.MemberList) - 1, -1, -1):

            Member = self.MemberList[i]

            if Member['VerifyFlag'] & 8 != 0:  # 公众号/服务号

                self.MemberList.remove(Member)

            elif Member['UserName'] in SpecialUsers:  # 特殊账号

                self.MemberList.remove(Member)

            elif Member['UserName'].find('@@') != -1:  # 群聊

                self.MemberList.remove(Member)

            elif Member['UserName'] ==  self.User:  # 自己

                self.MemberList.remove(Member)

        self.ContactList = self.MemberList

        return True

    #发送信息

    def webwxsendmsg(self, word, to='filehelper'):

        url = self.base_uri + \

              '/webwxsendmsg?pass_ticket=%s' % (self.pass_ticket)

        clientMsgId = str(int(time.time() * 1000)) + \

                      str(random())[:5].replace('.', '')

        params = {

            'BaseRequest':{

                "Uin": int(self.wxuin),

                "Sid": self.wxsid,

                "Skey":self.skey,

                "DeviceID": self.deviceId,

            },

            'Scene': 0,

            'Msg':{

                "Type": 1,

                "Content": self._transcoding(word),

                "FromUserName": self.User['UserName'],

                "ToUserName": to,

                "LocalID": clientMsgId,

                "ClientMsgId": clientMsgId,

            }

        }

        headers = {'content-type': 'application/json; charset=UTF-8'}

        data = json.dumps(params, ensure_ascii=False).encode('utf8')

        r =myRequests.post(url,data=data,headers=headers)

        dic = r.json()

        state = self.responseState('SendMsg', dic['BaseResponse'])

        print (params)

        return state

      #  print (params)

    def Wx_Views(self):

        print ('[.]正在获取好友列表..')

        list = self.ContactList

        Alarmlist=[]

        # list = json.dump(List,ensure_ascii=False)

        for i in range(0,len(list)):

            if list:

                list[i]['id'] = i

                Name = self._untostr(list[i]['NickName'])

                Rname = self._untostr(list[i]['RemarkName'])

                Id = i

                #print (list[i])

                print ('\t %d  \t姓名:%s \t 备注:%s' %(Id,Name,Rname))

            else:

                print ('[!]获取失败!')

                exit()

        while True:

            try:

                iNput = raw_input("[.]请设置告警对象ID,使用空格隔开\n")

                Alist = iNput.split(' ')

            except:

                print ("[!]输入错误!")

            try:

                for i in range(0,len(Alist)):

                    if Alist:

                        for j in range(0, len(list)):

                            if int(Alist[i]) == list[j]['id']:

                                print ('[*]你设置的对象是:%s' % self._untostr(list[j]['NickName']))

                                Alarmlist.append(list[j]['UserName'])

                                self.AlarmFriends.append(list[j]['UserName'])

                            else:

                                pass

            except:

                continue

            if self.AlarmFriends:

                Input = raw_input ("[!]确认设置(y/n)")

                if Input == 'y':

                    self.AlarmFriends = Alarmlist

                    break

                elif Input == 'n':

                    Alarmlist = []

                    self.AlarmFriends = []

                    pass

                else:

                    Alarmlist = []

                    self.AlarmFriends = []

                    print ("[!]输入错误")

            else:

                print ("[!]检测不到有效输入,请重试")

        print (self.AlarmFriends)

    def Wx_heartBeatLoop(self):

        while True:

            selector = self.syncCheck()

            if selector != '0':

                self.webwxsync()

            time.sleep(int(self.xintiao))

            print ("[*]Wechat心跳正常..")

    def run(self):

        while True:

            time.sleep(5)

            SleepTime = int(self.Intervals)

            print("[*]告警检测心跳..")

            Time = int(time.time())

            LastTime = Time - int(SleepTime)

            Select_sql = "SELECT * FROM wechat_sendmsg WHERE TIME BETWEEN %d and %d" % (LastTime,Time)

            data = db.select(Select_sql)

            # print (data)

            if data:

                for i in data:

                    print (data )

                    triggerTime = self._untostr(i[0])

                    Hostname =  self._untostr(i[2])

                    HostIP = self._untostr(i[3])

                    Description = self._untostr(i[4])

                    level = self._untostr(i[5])

                    msg = """

[!]发现告警

告警服务器:%s

告警时间:%s

告警IP:%s

告警项:%s

告警级别:%s

""" %(Hostname,triggerTime,HostIP,Description,level)

                    print (msg)

                    for j in range(0,len(self.AlarmFriends)):

                        self.webwxsendmsg(msg,self.AlarmFriends[j])

                        # print (j)

                        # print (self.AlarmFriends)

            else:

                pass

第二部分:zabbix API

from ZabbixTriggerDb import SQLiteDB

myRequests = requests.Session()

db = SQLiteDB

class Zabbix(object):

    def __init__(self):

        self.Holist = []

        self.Zabbix_Address = ''

        self.Zabbix_Username=''

        self.Time = time.strftime('%Y-%m-%d %H:%M')

        self.Passwd = ''

        self.z_Intervals = ''

        self.w_Intervals = ''

        self.sleeptime = ''

        self.Trigger= []

        self.LastTrigger = []

        self.WxTriggerList = []

        

        #获取zabbix api token 

    def get_auth(self):

        url = '%s/api_jsonrpc.php' % self.Zabbix_Address

        params = json.dumps({

            "jsonrpc": "2.0",

            "method": "user.login",

            "params": {

                "user": self.Zabbix_Username,

                "password": self.Passwd

                },

            "id": 0

        })

        headers = {'content-type': 'application/json; charset=UTF-8'}

        r = myRequests.post(url=url, data=params, headers=headers)

        r.encoding = 'utf-8'

        data = r.json()

        return data['result']

        

        #获取zabbix监控主机列表

    def get_host(self):

        url = '%s/api_jsonrpc.php' % self.Zabbix_Address

        params = json.dumps({

                "jsonrpc": "2.0",

                "method": "host.get",

                "params": {

                    "output":[

                        "hostid",

                        "name"

                    ],

                "selectInterfaces":[

                    "interfaceid",

                    "ip",

                ]

                },

                "id":2,

                "auth":self.get_auth()

        })

        headers = {'content-type': 'application/json; charset=UTF-8'}

        r = myRequests.post(url=url, data=params, headers=headers)

        r.encoding = 'utf-8'

        data = r.json()

        self.Holist = data['result']

        return self.Holist

        

        

        #获取告警

    def get_trig(self,hostid):

        url = '%s/api_jsonrpc.php' % self.Zabbix_Address

        params = json.dumps({

                "jsonrpc":"2.0",

                "method":"trigger.get",

                "params": {

                    "output": [

                            "triggerid",

                            "description",

                            "priority"

                            ],

                    "filter": {

                            "value": 1,

                            "hostid":hostid

                            },

                    "sortfield": "priority",

                    "sortorder": "DESC"

                         },

                "auth": self.get_auth(),

                "id":1

        })

        headers = {'content-type': 'application/json; charset=UTF-8'}

        r = myRequests.post(url=url, data=params, headers=headers)

        r.encoding = 'utf-8'

        data = r.json()

        if data['result']:

     #       text = json.dumps(data,ensure_ascii=False)

            return data['result']

        else:

            return None

            

#告警信息入库

    def get_triggerlist(self):

        list = self.Holist

        if list:

            for i in range(0,len(list)):

                # ip = self._untostr(list[i]['interfaces']['ip'])

                trigger = self.get_trig(list[i]['hostid'])

                Level = {'1':'DISASTER','2':'HIGH','3':'AVERAGE','4':'WARNING','5':'INFORMATION',\

                            '6':'NOT CLASSIFIED'}

                if trigger != None:

                    Trigger = self._untostr(trigger[0]['description'])

                    level = self._untostr(trigger[0]['priority'])

                    name = self._untostr(list[i]['name'])

                    ip = self._untostr(list[i]['interfaces'][0]['ip'])

                    Datatime = time.strftime("%Y-%m-%d %H:%M", time.localtime())

                    Time = int(time.time())

                    z_LastTime = Time - int(self.z_Intervals)

                    w_LastTime = Time - int(self.w_Intervals)

                    

                     #这个地方先查询在间隔时间段内有没有存在相同数据,如果没有就插入,有就跳过

                    zabbix_sql = "SELECT * FROM zabbix_trigger WHERE HOSTNAME='%s' and \

    DESCRIPTION='%s' and TIME BETWEEN %d and %d " % (name,Trigger,z_LastTime,Time)

                    z_data = db.select(zabbix_sql)

                    if z_data:

                        pass

                    else:

                        z_Inset_sql = "INSERT INTO zabbix_trigger(DATA,TIME,HOSTNAME,HOSTIP,DESCRIPTION,LEVEL)\

    VALUES('%s',%d,'%s','%s','%s','%s');" % (Datatime,Time,name,ip,Trigger,Level[level])

                        db.insert(z_Inset_sql)

                        

                        #判断间隔时间内wechat_sendmsg表中是否存在相同输入,没有则插入

                    wechat_sql = "SELECT * FROM wechat_sendmsg WHERE HOSTNAME='%s' and \

    DESCRIPTION='%s' and TIME BETWEEN %d and %d limit 1" % (name, Trigger, w_LastTime, Time)

                    w_data = db.select(wechat_sql)

                    if w_data:

                        pass

                    else:

                        w_Inset_sql = "INSERT INTO wechat_sendmsg(DATA,TIME,HOSTNAME,HOSTIP,DESCRIPTION,LEVEL)\

    VALUES('%s',%d,'%s','%s','%s','%s');" % (Datatime, Time, name, ip, Trigger, Level[level])

                        db.insert(w_Inset_sql)

                  #这里思路是 设定时间内获取告警信息存入zabbix表,

                  #然后再另外设定一个时间写入weixin表 这样做是为了一个时间范围内不重复告警      

        else:

            print ("[!]获取主机列表失败,正在重新获取...")

            self.get_auth()

            self.get_host()

            self.get_triggerlist()

            

   #ZABBIX 心跳

    def run(self):

        while True:

            print ("[*]Zabbix心跳正常..")

            time.sleep(self.sleeptime)

            self.get_triggerlist()

第三部分:SQLite

# coding=utf-8

import sqlite3,os

SQLiteDB = os.path.join(os.getcwd(), 'TriggerDB.db')

DBCON = sqlite3.connect(SQLiteDB,check_same_thread=False) #多线程操作要开启这个选项

DBCUR = DBCON.cursor()

class SQLiteDB(object):

    @staticmethod

    def insert(sql):

        try:

            DBCUR.execute(sql)

        except sqlite3.Error as e:

            print ("[!]Insert Error! %s" % e.args[0])

        DBCON.commit()

        

    @staticmethod

    def select(sql):

        data = []

        try:

            DBCUR.execute(sql)

            data = DBCUR.fetchall()

        except sqlite3.Error as e:

            print ("[!]Slect Error!%s"% e.args[0])

        return data

        

        #初始化,新建两个表

    @staticmethod

    def CreatTable():

        zabbix_sql ="create table if not exists Zabbix_Trigger (DATA text,TIME integer,HOSTNAME text, \

             HOSTIP text,DESCRIPTION text,LEVEL text);"

        weixin_sql = "create table if not exists Wechat_Sendmsg (DATA text,TIME integer,HOSTNAME text, \

             HOSTIP text,DESCRIPTION text,LEVEL text);"

        try:

            DBCUR.execute(zabbix_sql)

            DBCUR.execute(weixin_sql)

        except sqlite3.Error as e:

            print ("[!]Creat Error! %s" % e.args[0])

第四部分:合体

# coding=utf-8

from Zabbix import Zabbix

from ZabbixTriggerDb import SQLiteDB

from WeChat import WeChat

import os,sys,thread

if __name__ == '__main__':

    db = SQLiteDB

    db.CreatTable()

    z = Zabbix()    #zabbix类

    w = WeChat()    #wechat类

    z.Zabbix_Address = 'http://Zabbix服务器地址'

    z.Zabbix_Username = 'zabbix用户'

    z.Passwd = 'zabbix密码'

    z.z_Intervals = 600     #zabbix告警入库间隔

    z.w_Intervals = 3600    #wechat告警入库间隔

    z.sleeptime = 10        #Zabbix心跳间隔

    w.Intervals = 3         #告警检测心跳间隔

    w.xintiao = 2           #微信心跳间隔

    z.get_auth()            #zabbix token

    z.get_host()            #zabbix hostlist

    z.get_triggerlist()     #zabbix triggerlist

    if not w.Get_UUID():

        print('[!]获取uuid失败,请重新运行!')

    print('[*]正在获取二维码图片...')

    w.genQRCode()       #获取二维码

    while w.WaitForLogin() != '200':

        pass

    w.login()           #登陆

    w.webwxinit()       #初始化

    w.webwxgetcontact() #获取好友列表

    w.Wx_Views()        #设置告警好友

   #定义一个线程方法,加入zabbix运行线程与微信发送告警线程

    def RUN():

        thread.start_new(z.run, ())

        thread.start_new(w.run, ())

    RUN()

    #启动微信心跳(让微信保持在线状态)

    w.Wx_heartBeatLoop()

最终效果:

wKiom1eQeB_hmSQ1AAVSD5RfSEg653.png-wh_50

wKioL1eQeCLCuC69AAVjp8lldPI740.png-wh_50

wKiom1eQeCbw8cI8AAetjBfSqg4621.png-wh_50

wKiom1eQeCuz2KbaAAi3sz-LEfk380.png-wh_50

wKioL1eQeCvzVP4qAAEaS_emPzI880.png-wh_50

©著作权归作者所有:来自51CTO博客作者迷失的猫妖的原创作品,如需转载,请注明出处,否则将追究法律责任


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 1
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消