Zabbix微信个人账号告警
前言:
最近研究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==";# 正则匹配:匹配出状态码 以及UUIDregx = 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 Truereturn False# windows下直接打开二维码图
def _openWinQRCodeImg(self):url = 'https://login.weixin.qq.com/qrcode/' + self.uuidparams = {'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 = 1qr.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_uriself.push_uri = base_urifor (searchUrl, pushUrl) in services:if base_uri.find(searchUrl) >= 0:self.push_uri = 'https://%s/cgi-bin/mmwebwx-bin' % pushUrlself.base_uri = 'https://%s/cgi-bin/mmwebwx-bin' % searchUrlbreakelif 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.documentElementfor node in root.childNodes:if node.nodeName == 'skey':self.skey = node.childNodes[0].dataelif node.nodeName == 'wxsid':self.wxsid = node.childNodes[0].dataelif node.nodeName == 'wxuin':self. wxuin = node.childNodes[0].dataelif 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 Falseself.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'] == 0return 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.MemberListreturn 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.ContactListAlarmlist=[]# list = json.dump(List,ensure_ascii=False)for i in range(0,len(list)):if list:list[i]['id'] = iName = 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:passexcept:continueif self.AlarmFriends:Input = raw_input ("[!]确认设置(y/n)")if Input == 'y':self.AlarmFriends = Alarmlistbreakelif Input == 'n':Alarmlist = []self.AlarmFriends = []passelse: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_Addressparams = 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_Addressparams = 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_Addressparams = 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:passelse: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:passelse: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 triggerlistif not w.Get_UUID():print('[!]获取uuid失败,请重新运行!')
print('[*]正在获取二维码图片...')w.genQRCode() #获取二维码while w.WaitForLogin() != '200':passw.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
沈阳人流哪家好
:http://mobile.120sdfuke.com/
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
