├── requirements.txt ├── config.ini ├── README.MD └── butian.py /requirements.txt: -------------------------------------------------------------------------------- 1 | requests==2.22.0 2 | -------------------------------------------------------------------------------- /config.ini: -------------------------------------------------------------------------------- 1 | { 2 | "发送邮件的配置(暂时只支持qq邮箱和163邮箱)" : "配置错误会导致发送不了文件", 3 | "send_mail" : "xxxxxxxxxx@qq.com", 4 | "send_mail_stmp_key" : "xxxxxxxxxxxxxx", 5 | 6 | "接收邮件的配置" : "", 7 | "add_company_email" : ["xxxxxxxxx@qq.com","xxxxxxxxxx@163.com","xxxxxxxxx#gmail.com"], 8 | "add_message_email" : "xxxxxxxxx@qq.com", 9 | 10 | "配置需要运行的服务,有专属上架监控,和审核消息通知" : "", 11 | "run_add_company" : 1, 12 | "run_add_message" : 1, 13 | 14 | "补天平台的cookie" : "不监控审核通知的话不用填", 15 | "cookie" : "btuc_xxxxxxxxxxxxxxxxx=xxxxxxxxxxxxxxxxxxxxxxxxxxx", 16 | 17 | "监控延时" : "单位是秒", 18 | "delay" : 600 19 | } -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | ## 程序说明 2 | 3 | 该程序采用Python编写,可监控补天平台的专属列表变动和审核通知。 4 | 5 | ### 文件说明 6 | 7 | - butian.py 监控程序,采用Python3编写 8 | - check_config 检测配置文件格式是否错误 9 | - log.txt 程序运行日志,包括监控信息和邮件发送日志 10 | - requirements.txt 程序依赖包 11 | - config.ini 配置文件 12 | 13 | ### 拉取程序: 14 | 15 | ``` 16 | wget https://github.com/0verf1ow/butian/archive/refs/heads/main.zip 17 | ``` 18 | 19 | ### 安装依赖: 20 | 21 | ``` 22 | pip intsall -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple some-package 23 | ``` 24 | 25 | ### 编辑配置文件说明 26 | 27 | qq邮箱获取smtp口令方法:https://www.cnblogs.com/hai-/p/13490814.html?ivk_sa=1024320u 28 | 29 | 浏览器获取cookie,只需要 "btuc" 开头的这一个,建议在浏览器修改下cookie的有效时间,这样不会被浏览器自动清除cookie,就不用频繁更换配置文件中的cookie. 30 | 31 | ![image-20211126214955229](http://0verflow-img-submit.oss-cn-beijing.aliyuncs.com/img/image-20211126214955229.png) 32 | 33 | ```json 34 | # 这里填的是要发送邮件的邮箱号 35 | "send_mail": "xxxxxxxxxx@qq.com", 36 | 37 | # 这里填的是要发送邮件的邮箱号的SMTP口令 38 | "send_mail_stmp_key": "xxxxxxx", 39 | 40 | # 这里填的是接收专属厂商上架的邮箱号 41 | "add_company_email": ["xxxxxx@qq.com","xxxxxxx@163.com","xxxxxxx@gmail.com"], 42 | 43 | # 这里填的是接收审核通知的邮箱号 44 | "add_message_email": "xxxxx@qq.com", 45 | 46 | # 是否运行监控专属厂商上架监控,1 是运行,0 是不运行 47 | "run_add_company" : 1, 48 | 49 | # 是否运行监控账号漏洞审核信息变动,1 是运行,0 是不运行 50 | "run_add_message" : 1, 51 | 52 | # 补天平台的cookie,如果不监控漏洞审核的话不用填 53 | "cookie" : "btuc_xxxxxxxxxxxxxxxxxxxx=xxxxxxxxxxxxxxxxxxxxxxxxxxxx;", 54 | 55 | # 监控时间间隔,单位(秒),时间不宜太小,否则会导致IP被Ban 56 | "delay" : 600 57 | ``` 58 | 59 | ### 运行程序: 60 | 61 | linux(放到后台运行): 62 | 63 | ```linux 64 | nohup python3 butian.py>log 2>&1 & 65 | ``` 66 | 67 | windows: 68 | 69 | ``` 70 | python3 butian.py 71 | ``` 72 | 73 | ### 邮件说明 74 | 75 | 该通知为厂商上架通知: 76 | 77 | image-20211126215409919 78 | 79 | 该通知为漏洞审核通知: 80 | 81 | image-20211126215449114 82 | 83 | 该通知为cookie过期告警(请及时更换有效cookie,否则无法监控漏洞审核变动): 84 | 85 | image-20211126215532316 86 | 87 | -------------------------------------------------------------------------------- /butian.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/python3 2 | # coding: utf-8 3 | """ 4 | date: 2022.03.13 5 | 监控补天专属厂商上架,或者监控用户漏洞审核信息 6 | 如果报错信息显示:"错误信息:'utf-8' codec can't decode byte 0xbb in position 0: invalid start byte",请放linux上跑 7 | """ 8 | 9 | import requests 10 | import smtplib 11 | from email.mime.text import MIMEText 12 | from email.utils import formataddr 13 | import time 14 | import re 15 | import json 16 | 17 | 18 | class Butian(): 19 | 20 | # 初始化读取配置 21 | def __init__(self): 22 | # 检查配置文件格式 23 | try: 24 | with open('config.ini', encoding="utf-8") as f: 25 | self.config = json.loads(f.read().strip()) 26 | except: 27 | exit("[!] 配置文件格式错误") 28 | self.send_mail = self.config["send_mail"] # 发送邮件通知的Email账号 29 | self.send_mail_stmp_key = self.config["send_mail_stmp_key"] # 发送邮件通知的Smtp口令 30 | self.add_company_email = self.config["add_company_email"] # 需要接收厂商上架通知的邮箱账号 31 | self.add_message_email = self.config["add_message_email"] # 需要接受漏洞审核信息的邮箱账号 32 | self.h = {} 33 | self.h["cookie"] = self.config["cookie"] # 补天平台的cookie 34 | self.delay = self.config["delay"] # 监控延时 35 | self.run_add_company = self.config["run_add_company"] # 是否监控厂商上架 36 | self.run_add_message = self.config["run_add_message"] # 时候监控信息通知 37 | self.run() # 开启运行 38 | 39 | # 程序的主方法 40 | def run(self): 41 | self.number, self.old_name_list = self.get_num() # 获取初始的厂商数量 42 | self.old_id_list = self.get_id_list() # 获取初始的消息id列表 43 | while True: 44 | if self.run_add_company == 1: # 运行厂商监控 45 | self.number, self.old_name_list = self.add_company() 46 | 47 | if self.run_add_message == 1: # 运行消息监控 48 | self.old_id_list, self.new_msg_dict = self.get_new_id(self.old_id_list) 49 | if self.new_msg_dict: # 如果新增列表不为空 50 | self.add_message() # 调用发送邮件通知 51 | else: 52 | s = "[-]审核无变动" 53 | self.output_log(s) 54 | 55 | # 没有选择操作的话直接退出程序 56 | if self.run_add_company == 0 and self.run_add_message == 0: break # 57 | 58 | time.sleep(self.delay) 59 | 60 | # 获取专属厂商列表数量 61 | def get_num(self): 62 | if self.run_add_company == 0: return 0, 0 63 | try: 64 | url = "https://www.butian.net/Reward/corps" # 查询厂商列表的接口 65 | data = {'s': 3, 'p': 1, 'sort': 1, 'token': ''} 66 | r = requests.post(url, data=data) 67 | j = json.loads(r.text) 68 | num = len(j['data']['list']) # 厂商数量 69 | name_list = [] # 厂商列表 70 | for row in j['data']['list']: 71 | d = {'company_name': row['company_name'], 'company_id': row['company_id']} 72 | name_list.append(d) 73 | return num, name_list 74 | except: 75 | time.sleep(30) 76 | return self.get_num() 77 | 78 | # 监控厂商上架 79 | def add_company(self): 80 | num, new_name_list = self.get_num() # 获取网页内的礼品数量 81 | if num > self.number: 82 | """ 83 | 对比变化前后的厂商,找出新增的厂商 84 | """ 85 | try: 86 | for new in new_name_list: 87 | if not new in self.old_name_list: 88 | company_name = new['company_name'] 89 | company_id = new['company_id'] 90 | break 91 | except: 92 | company_name = '' 93 | company_id = '' 94 | 95 | msg = "检测到厂商[ {} ]上架,请登录到平台查看 https://www.butian.net/Company/{}".format(company_name, company_id) 96 | ret = self.send_Email(self.send_mail, self.send_mail_stmp_key, self.add_company_email, msg) # 调用发送邮件 97 | if ret: 98 | s = "[+]检测到厂商[ {} ]上架,邮件发送成功".format(company_name) 99 | self.output_log(s) 100 | else: 101 | s = "[!]检测到厂商[ {} ]上架,但邮件发送失败".format(company_name) 102 | self.output_log(s) 103 | 104 | else: 105 | s = "[-]厂商没改动" 106 | self.output_log(s) 107 | 108 | # 把获取到的礼品数返回,这样下次就是对比上次的数量,而不是对比初始值,防止变动后一直发邮件 109 | return num, new_name_list 110 | 111 | # 监控审核信息 112 | def add_message(self): 113 | msg = "有新的服务消息通知:可点击 https://www.butian.net/Message 查看\n" 114 | for id, title in self.new_msg_dict.items(): 115 | msg += title + "\n" 116 | ret = self.send_Email(self.send_mail, self.send_mail_stmp_key, self.add_message_email, msg) # 调用发送邮件 117 | if ret: 118 | s = "[+]检测到有新的漏洞审核通知,邮件发送成功" 119 | self.output_log(s) 120 | else: 121 | s = "[!]检测到有新的漏洞审核通知,但邮件发送失败" 122 | self.output_log(s) 123 | 124 | # 获取所有的通知信息 125 | def get_id_list(self): 126 | # 未读消息 127 | data = {"ajax": 1, "id": 0, "status": 0, "page": 1} 128 | # 获取通知的接口,返回对象类型是字符串 129 | try: 130 | r = requests.post("https://www.butian.net/Home/Message/lists", headers=self.h, data=data, ) 131 | # 转为json对象好操作 132 | r_data = json.loads(r.text) 133 | 134 | # cookie失效检测 135 | if r_data["status"] == 0: 136 | msg = "[!]Cookie已过期,请从新获取cookie填入配置文件,再从新运行程序" 137 | ret = self.send_Email(self.send_mail, self.send_mail_stmp_key, self.add_company_email, msg) # 调用发送邮件 138 | if ret: 139 | s = "[+]检测到 cookie 失效,邮件发送成功" 140 | self.output_log(s) 141 | self.run_add_message = 0 # 发送不再监控漏洞审核通知的信号 142 | return 0 143 | else: 144 | s = "[!]检测到 cookie 失效,但邮件发送失败" 145 | self.output_log(s) 146 | self.run_add_message = 0 # 发送不再监控漏洞审核通知的信号 147 | return 0 148 | 149 | last_lists = r_data['data']['list'] # 通知列表 150 | last_ids = {} # 通知的id和title的字典 151 | try: 152 | for i in last_lists: 153 | last_ids[i["id"]] = i["title"] 154 | return last_ids # 返回的是最新的id和title字典 155 | except: 156 | return {'1': ''} # 防止用户的消息列表为空 157 | except: 158 | # 网络错误的话,延时60秒再重新获取 159 | time.sleep(60) 160 | return self.get_id_list() 161 | 162 | # 获取新信息的id 163 | def get_new_id(self, old_id_list): 164 | new_id_lists = self.get_id_list() # 获取最新的消息列表 165 | if self.run_add_message == 0: return 0, 0 # 截取 166 | new_msg_dict = {} 167 | for new_id in new_id_lists.keys(): 168 | if new_id not in old_id_list.keys(): # id不在上一个返回值的列表中,那么他是新的 169 | new_msg_dict[new_id] = new_id_lists[new_id] 170 | new_id_lists['1'] = '' 171 | return new_id_lists, new_msg_dict # 返回的是最新获取的消息列表,和已经新增的消息列表 172 | 173 | # 发送邮件,暂只支持qq邮箱和163邮箱作为发送箱 174 | def send_Email(self, send_mail, key, person, content): 175 | ret = True 176 | if re.findall(r'@\w+.com', send_mail)[0] == '@qq.com': # 判断是否qq邮箱 177 | smtp_server = "smtp.qq.com" 178 | elif re.findall(r'@\w+.com', send_mail)[0] == '@163.com': # 判断是否网易邮箱 179 | smtp_server = "smtp.163.com" 180 | try: 181 | msg = MIMEText(content, 'plain', 'utf-8') 182 | msg['From'] = formataddr(["监控信息", send_mail]) # 括号里的对应发件人邮箱昵称、发件人邮箱账号 183 | msg['Subject'] = "补天监控" # 邮件的主题,也可以说是标题 184 | server = smtplib.SMTP_SSL(smtp_server, 465) # 发件人邮箱中的SMTP服务器,端口是465 185 | server.login(send_mail, key) # 括号中对应的是发件人邮箱账号、邮箱密码 186 | server.sendmail(send_mail, person, msg.as_string()) # 括号中对应的是发件人邮箱账号、收件人邮箱账号、发送邮件 187 | server.quit() # 关闭连接 188 | except Exception as e: # 如果 try 中的语句没有执行,则会执行下面的 ret=False 189 | ret = False 190 | s = "[!]错误信息:" + str(e) 191 | self.output_log(s) 192 | return ret 193 | 194 | # 日志保存 195 | def output_log(self, s): # 写日志,打印信息 196 | print(s) 197 | with open('log.txt', 'a', encoding='utf-8') as log: 198 | t = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) 199 | log.write(t + " " + s + "\n") 200 | 201 | 202 | if __name__ == '__main__': 203 | Butian() 204 | --------------------------------------------------------------------------------