前言:  最近研究zabbix告警,网上看了帖子有各式各样姿势:电话语音告警,邮件告警,短信告警,微信公众号告警等等等..姿势五花八门,真是纠结。  电话语音告警,短信告警首先pass 前者花钱,后者通过设置139邮箱,就可以实现伪短信告警效果。  剩下邮件告警与微信公众号告警。邮件告警已经在部署的时候配置完毕,剩下这个微信公众号告警,查一下帖子,申请各种麻烦。那么有没有基于微信个人账号的告警呢?想到这个点,马上github一番。  12345  搜索到一些优秀的开源代码:https://github.com/0x5e/wechat-deleted-friends  及封包:https://github.com/xiangzhai/qwx/blob/master/doc/protocol.md  经过一番思考,大致思路如下:  微信WEB保持活动状态,通过不断使用zabbix api抓取故障告警,入库后微信发送告警给相关人员。  123  一 需求实现具体思路  实现该需求是得有多个线程同时进行的  1  微信心跳,微信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返回  # 0 OK  # XXXX  # XXXX  # XXXX  # XXXX  # 1  #解析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 = """  123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309  [!]发现告警  告警服务器:%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  12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576  #告警信息入库  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()  1234567891011121314151617181920212223242526272829  #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])  123456789101112131415161718192021222324252627282930  第四部分:合体  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() #设置告警好友  1234567891011121314151617181920212223242526272829303132333435  #定义一个线程方法,加入zabbix运行线程与微信发送告警线程  def RUN():  thread.start_new(z.run, ())  thread.start_new(w.run, ())  RUN()  #启动微信心跳(让微信保持在线状态)  w.Wx_heartBeatLoop()  12  最终效果:  wKiom1eQeB_hmSQ1AAVSD5RfSEg653.png-wh_50  wKioL1eQeCLCuC69AAVjp8lldPI740.png-wh_50  wKiom1eQeCbw8cI8AAetjBfSqg4621.png-wh_50  wKiom1eQeCuz2KbaAAi3sz-LEfk380.png-wh_50  wKioL1eQeCvzVP4qAAEaS_emPzI880.png-wh_50沈阳治疗男科医院哪家好:http://www.024sdjk.com/ 
10-01 18:29