├── groupfollow.txt
├── .travis.yml
├── CHANGELOG.md
├── HttpClient.py
├── README.md
├── QQBot.py
└── LICENSE
/groupfollow.txt:
--------------------------------------------------------------------------------
1 | 群名1
2 | 群名2
3 | 群名3
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | python:
3 | - '2.7'
4 | - '2.6'
5 | script:
6 | - py.test -q QQBot.py
7 | after_script:
8 | - nohup python QQBot.py >qbot.log&
9 | - sleep 500s
10 | - echo "still waiting"
11 | - sleep 500s
12 | - cat qbot.log
13 | - cat log.log
14 | - ls
15 | - killall python
16 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | Changelog
2 | =========
3 |
4 | ##2015-04-14
5 | + 项目COPY自[SmartQQ](https://github.com/Yinzo/SmartQQBot)
6 | + 移除命令行输入
7 | + 将follow群列表写入QQBot.py
8 | + 从文件读取follow群列表
9 | + 从AI网站拉取小黄鸡回复
10 | + 私聊从AI网站拉取
11 | + 群聊写入一个DATABASE
12 | + 把全部的PRINT输入LOG文件
13 | + 修改PROTOCOL,去除LOGIN_SIG必要性。即没有获取LOGIN_SIG不报错
14 |
15 | ##2015-04-15
16 | + 添加来自群或讨论组的私聊回复
17 | + 方法为先获取group_sig:
18 | ```json.loads(HttpClient_Ist.Get('http://d.web2.qq.com/channel/get_c2cmsg_sig2?id={0}&to_uin={1}&clientid={2}&psessionid={3}&service_type={4}&t={5}'.format(myid, tuin, ClientID, PSessionID, service_type, ts), Referer))```
19 | + 然后POST消息
20 | ```
21 | reqURL = "http://d.web2.qq.com/channel/send_sess_msg2"
22 | data = (
23 | ('r', '{{"to":{0}, "face":594, "content":"[\\"{4}\\", [\\"font\\", {{\\"name\\":\\"Arial\\", \\"size\\":\\"10\\", \\"style\\":[0, 0, 0], \\"color\\":\\"000000\\"}}]]", "clientid":"{1}", "msg_id":{2}, "psessionid":"{3}", "group_sig":"{5}", "service_type":{6}}}'.format(tuin, ClientID, msgId, PSessionID, str(content), group_sig, service_type)),
24 | ('clientid', ClientID),
25 | ('psessionid', PSessionID),
26 | ('group_sig', group_sig),
27 | ('service_type',service_type)
28 | )
29 | rsp = HttpClient_Ist.Post(reqURL, data, Referer)
30 | ```
31 |
32 | ##2015-04-16
33 | + 加入消息ID核对,避免重复处理私聊
34 | + 替换AI的换行符与<主人>,使其对应QQ协议
35 | + 优化线程管理
36 | + 群聊限制3秒回复一条信息,若不足3秒则放弃回复此信息。 (防止被封)
37 | + FIRST RELEASE
38 |
39 |
40 | ##2015-07-23
41 | + 加入about命令
42 | + 登陆失败直接终止线程
43 | + 新增WINDOWS版本
44 |
45 | ##2015-07-25
46 | + 加入deleteall命令,删除所有学习内容
47 |
48 | ##2016-01-26
49 | + TX在12月底进行了协议更新,修复协议错误造成的无法登陆/发消息问题。临时消息貌似已被TX屏蔽。
50 |
--------------------------------------------------------------------------------
/HttpClient.py:
--------------------------------------------------------------------------------
1 | # HttpClient.py is written by [xqin]: https://github.com/xqin/SmartQQ-for-Raspberry-Pi
2 | import cookielib, urllib, urllib2
3 |
4 | class HttpClient:
5 | __cookie = cookielib.CookieJar()
6 | __req = urllib2.build_opener(urllib2.HTTPCookieProcessor(__cookie))
7 | __req.addheaders = [
8 | ('Accept', 'application/javascript, */*;q=0.8'),
9 | ('User-Agent', 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)')
10 | ]
11 | urllib2.install_opener(__req)
12 |
13 | def Get(self, url, refer=None):
14 | try:
15 | req = urllib2.Request(url)
16 | if not (refer is None):
17 | req.add_header('Referer', refer)
18 | return urllib2.urlopen(req).read()
19 | except urllib2.HTTPError, e:
20 | return e.read()
21 |
22 | def Post(self, url, data, refer=None):
23 | try:
24 | req = urllib2.Request(url, urllib.urlencode(data))
25 | if not (refer is None):
26 | req.add_header('Referer', refer)
27 | return urllib2.urlopen(req).read()
28 | except urllib2.HTTPError, e:
29 | return e.read()
30 |
31 | def Download(self, url, file):
32 | output = open(file, 'wb')
33 | output.write(urllib2.urlopen(url).read())
34 | output.close()
35 |
36 | # def urlencode(self, data):
37 | # return urllib.quote(data)
38 |
39 | def getCookie(self, key):
40 | for c in self.__cookie:
41 | if c.name == key:
42 | return c.value
43 | return ''
44 |
45 | def setCookie(self, key, val, domain):
46 | ck = cookielib.Cookie(version=0, name=key, value=val, port=None, port_specified=False, domain=domain, domain_specified=False, domain_initial_dot=False, path='/', path_specified=True, secure=False, expires=None, discard=True, comment=None, comment_url=None, rest={'HttpOnly': None}, rfc2109=False)
47 | self.__cookie.set_cookie(ck)
48 | #self.__cookie.clear() clean cookie
49 | # vim : tabstop=2 shiftwidth=2 softtabstop=2 expandtab
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | QQ小黄鸡VPS挂机版
2 | =========
3 | [](https://travis-ci.org/zeruniverse/QQRobot)
4 | [](https://landscape.io/github/zeruniverse/QQRobot/master)
5 | 
6 | 
7 | 
8 | ***该项目修改自[SmartQQBOT](https://github.com/Yinzo/SmartQQBot)这一项目***,支持在VPS下nohup命令挂机。QQ协议说明请参考原项目。
9 |
10 | **请帮忙分析Android QQ协议**:此项目现已稳定,在更新协议前不会有大更新。希望有人能跟我一起搞手机QQ协议,SmartQQ协议稳定性不是很理想。
11 |
12 | 重要:群聊被TX认为是**极度危险**的行为,因此如果账号被怀疑被盗号(异地登陆),群聊消息会发不出去。表现为程序能收到群聊消息,群聊消息发送返回值为发送成功,但其他群成员无法看到您发出的消息。大约登陆10分钟后您会收到QQ提醒提示账号被盗,要求改密码,同时账号被临时冻结。不知为何该程序刚运行时总是被怀疑异地登陆,当您重复解冻3次后(就是改密码),TX基本就不再怀疑您了,一般一次能稳定挂机2-3天。强烈推荐您用小号挂QQ小黄鸡!
13 |
14 | This project is a chatting robot in QQ, implemented in Python. The robot uses Artificial Intelligent API to generate response. QQ is a popular instant chatting service in China, which is similar to Facebook Messenger. The robot supports group chatting and private chatting and should be only used for fun.
15 |
16 | [Here](https://github.com/zeruniverse/QQParking) is a similar project used to keep QQ account online with the function to record messages and forward to your E-mail.
17 |
18 | 运行群聊示例:
19 | 
20 | 
21 | 
22 |
23 | 私聊示例:
24 | 
25 |
26 |
27 | 登陆时采用QQ安全中心的二维码做为登陆条件, 不需要在程序里输入QQ号码及QQ密码。QQ自动回复私聊(无群聊功能)及留言邮件提醒版本请看[这里](https://github.com/zeruniverse/QQParking)
28 |
29 | ## RELEASE
30 | 最新RELEASE:[点击下载](https://github.com/zeruniverse/QQRobot/releases/latest)
31 | ~~WINDOWS EXE 32位: [点击下载](https://github.com/zeruniverse/QQRobot/releases/tag/w1.3)~~
32 | 目前只有master分支持续更新。建议WINDOWS用户使用技术门槛更低的[QBotWrap](https://github.com/zeruniverse/QBotWebWrap)
33 |
34 | ## 如何使用
35 | + 从http://www.tuling123.com/openapi/ 申请一个API KEY(免费,5000次/天), 贴到```QQBot.py```的第36行 (测试KEY:c7c5abbc9ec9cad3a63bde71d17e3c2c)
36 | + 修改groupfollow.txt,将需要小黄鸡回复的群的群名写入(小黄鸡必须为群成员),每行一个群名,请不要打多余的空格。(新版WEBQQ已移除获取群号的接口,输入中文群名请务必使用UTF-8编码)
37 | + ```nohup python2 QQBot.py >qbot.log&```
38 | + ```ls```
39 | + 若出现v.png则用QQ安全中心扫描,否则继续```ls```
40 | + ```cat log.log```可以输出运行LOG
41 | + 强烈建议使用小号挂小黄鸡,已知QQ会临时封禁机器人的临时对话回复和群回复,原理未知,每次封禁约为10分钟。表现为发送消息返回值retcode 为 0 但其他人无法看到。长时间挂机会导致QQ被冻结错误,QQ安全中心提示发布不良信息
42 | + 据反馈此AI平台回复中带有少量广告。。。(如问iphone6价格回复小米799)
43 |
44 |
45 | ## 功能
46 | 注:以下命令皆是在QQ中发送,群聊命令发送到所在群中
47 |
48 | + 关于及帮助,在群聊中发送```!about```
49 |
50 | + 群聊智能回复,在群中通过发送```!ai 问题```语句,则机器人向AI平台请求```问题```的回复并回复到群,带有!ai关键字时优先触发此功能
51 |
52 | + 私聊智能回复,对于收到的私聊,机器人向AI平台请求该聊天记录的回复并回复给消息发送者
53 |
54 | + 群聊学习功能,类似于小黄鸡,在群中通过发送```!learn {ha}{哈哈}```语句,则机器人检测到发言中包含“ha”时将自动回复“哈哈”。```!delete {ha}{哈哈}```可以删除该内容。学习内容会自动储存在```database.群号.save```文件。```!deleteall```可删除该群所有记录。注意`learn`和`{`之间有空格,`{}`与`{}`之间没有。
55 |
56 | + 群聊复读功能,检测到群聊中**连续两个**回复内容相同,将自动复读该内容1次。
57 |
58 | + 群聊关注功能,使用命令```!follow {QQ昵称}!```可以使机器人复读此人所有发言(除命令外)使用命令```!unfollow {QQ昵称}!```解除关注。例如 ```!follow 卖火柴的小女孩!```。{QQ昵称}处可使用"me"来快速关注与解除关注自己,例:```!follow me!```
59 |
60 | 私聊直接聊天即可,不需要加任何前缀。
61 |
--------------------------------------------------------------------------------
/QQBot.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import re
4 | import random
5 | import json
6 | import os
7 | import sys
8 | import datetime
9 | import time
10 | import threading
11 | import logging
12 | import urllib
13 | from HttpClient import HttpClient
14 |
15 | reload(sys)
16 | sys.setdefaultencoding("utf-8")
17 |
18 | HttpClient_Ist = HttpClient()
19 |
20 | ClientID = 53999199
21 | PTWebQQ = ''
22 | APPID = 0
23 | msgId = 0
24 | ThreadList = []
25 | GroupThreadList = []
26 | GroupWatchList = []
27 | GroupNameList = {}
28 | GroupCodeList = {}
29 | PSessionID = ''
30 | Referer = 'http://s.web2.qq.com/proxy.html?v=20130916001&callback=1&id=1'
31 | httpsReferer = 'https://d1.web2.qq.com/cfproxy.html?v=20151105001&callback=1'
32 | SmartQQUrl = 'https://ui.ptlogin2.qq.com/cgi-bin/login?daid=164&target=self&style=16&mibao_css=m_webqq&appid=501004106&enable_qlogin=0&no_verifyimg=1&s_url=http%3A%2F%2Fw.qq.com%2Fproxy.html&f_url=loginerroralert&strong_login=1&login_state=10&t=20131024001'
33 | VFWebQQ = ''
34 | AdminQQ = '0'
35 | MyUIN = ''
36 | tulingkey='#YOUR KEY HERE#'
37 | tmpUserName = ''
38 |
39 | initTime = time.time()
40 |
41 |
42 | logging.basicConfig(filename='log.log', level=logging.DEBUG, format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', datefmt='%a, %d %b %Y %H:%M:%S')
43 |
44 | # -----------------
45 | # 方法声明
46 | # -----------------
47 |
48 |
49 | def pass_time():
50 | global initTime
51 | rs = (time.time() - initTime)
52 | initTime = time.time()
53 | return str(round(rs, 3))
54 |
55 | def get_ts():
56 | ts = time.time()
57 | while ts < 1000000000000:
58 | ts = ts * 10
59 | ts = int(ts)
60 | return ts
61 |
62 | def CProcess(content):
63 | return str(content.replace("\\", r"\\").replace("\n", r"\n").replace("\r", r"\r").replace("\t", r"\t").replace('"', r'\"'))
64 |
65 | def getQRtoken(qrsig):
66 | e = 0
67 | for i in qrsig:
68 | e += (e << 5) + ord(i)
69 | return 2147483647 & e;
70 |
71 | #Encryption Algorithm Used By QQ
72 | def gethash(selfuin, ptwebqq):
73 | selfuin += ""
74 | N=[0,0,0,0]
75 | for T in range(len(ptwebqq)):
76 | N[T%4]=N[T%4]^ord(ptwebqq[T])
77 | U=["EC","OK"]
78 | V=[0, 0, 0, 0]
79 | V[0]=int(selfuin) >> 24 & 255 ^ ord(U[0][0])
80 | V[1]=int(selfuin) >> 16 & 255 ^ ord(U[0][1])
81 | V[2]=int(selfuin) >> 8 & 255 ^ ord(U[1][0])
82 | V[3]=int(selfuin) & 255 ^ ord(U[1][1])
83 | U=[0,0,0,0,0,0,0,0]
84 | U[0]=N[0]
85 | U[1]=V[0]
86 | U[2]=N[1]
87 | U[3]=V[1]
88 | U[4]=N[2]
89 | U[5]=V[2]
90 | U[6]=N[3]
91 | U[7]=V[3]
92 | N=["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"]
93 | V=""
94 | for T in range(len(U)):
95 | V+= N[ U[T]>>4 & 15]
96 | V+= N[ U[T] & 15]
97 | return V
98 |
99 | def getReValue(html, rex, er, ex):
100 | v = re.search(rex, html)
101 |
102 | if v is None:
103 | logging.error(er)
104 |
105 | if ex:
106 | raise Exception, er
107 | return ''
108 |
109 | return v.group(1)
110 |
111 |
112 | def date_to_millis(d):
113 | return int(time.mktime(d.timetuple())) * 1000
114 |
115 |
116 | def msg_handler(msgObj):
117 | for msg in msgObj:
118 | msgType = msg['poll_type']
119 |
120 | # QQ私聊消息
121 | if msgType == 'message' or msgType == 'sess_message': # 私聊 or 临时对话
122 | txt = combine_msg(msg['value']['content'])
123 | tuin = msg['value']['from_uin']
124 | msg_id = msg['value']['msg_id']
125 |
126 | # print "{0}:{1}".format(from_account, txt)
127 | targetThread = thread_exist(tuin)
128 | if targetThread:
129 | targetThread.push(txt, msg_id)
130 | else:
131 | try:
132 | service_type = 0
133 | isSess = 0
134 | group_sig = ''
135 | if msgType == 'sess_message':
136 | isSess = 1
137 | service_type = msg['value']['service_type']
138 | myid = msg['value']['id']
139 | info = json.loads(HttpClient_Ist.Get('http://d1.web2.qq.com/channel/get_c2cmsg_sig2?id={0}&to_uin={1}&clientid={2}&psessionid={3}&service_type={4}&t={5}'.format(myid, tuin, ClientID, PSessionID, service_type, get_ts()), Referer))
140 | logging.info("Get group sig:" + str(info))
141 | if info['retcode'] != 0:
142 | raise ValueError, info
143 | info = info['result']
144 | group_sig = info['value']
145 | tmpThread = pmchat_thread(tuin,isSess,group_sig,service_type)
146 | tmpThread.start()
147 | ThreadList.append(tmpThread)
148 | tmpThread.push(txt,msg_id)
149 | except Exception, e:
150 | logging.info("error"+str(e))
151 |
152 | # print "{0}:{1}".format(self.FriendList.get(tuin, 0), txt)
153 |
154 | # if FriendList.get(tuin, 0) == AdminQQ:#如果消息的发送者与AdminQQ不相同, 则忽略本条消息不往下继续执行
155 | # if txt[0] == '#':
156 | # thread.start_new_thread(self.runCommand, (tuin, txt[1:].strip(), msgId))
157 | # msgId += 1
158 |
159 | # if txt[0:4] == 'exit':
160 | # logging.info(self.Get('http://d1.web2.qq.com/channel/logout2?ids=&clientid={0}&psessionid={1}'.format(self.ClientID, self.PSessionID), Referer))
161 | # exit(0)
162 |
163 | # 群消息
164 | if msgType == 'group_message':
165 | global GroupWatchList
166 | txt = combine_msg(msg['value']['content'])
167 | guin = msg['value']['from_uin']
168 | gid = GroupCodeList[int(guin)]
169 | tuin = msg['value']['send_uin']
170 | seq = msg['value']['msg_id']
171 | if str(guin) in GroupWatchList:
172 | g_exist = group_thread_exist(gid)
173 | if g_exist:
174 | g_exist.handle(tuin, txt, seq)
175 | else:
176 | tmpThread = group_thread(guin, gid)
177 | tmpThread.start()
178 | GroupThreadList.append(tmpThread)
179 | tmpThread.handle(tuin, txt, seq)
180 | logging.info("群线程已生成")
181 | else:
182 | logging.info(str(gid) + "群有动态,但是没有被监控")
183 |
184 | # QQ号在另一个地方登陆, 被挤下线
185 | if msgType == 'kick_message':
186 | logging.error(msg['value']['reason'])
187 | raise Exception, msg['value']['reason'] # 抛出异常, 重新启动WebQQ, 需重新扫描QRCode来完成登陆
188 |
189 |
190 | def combine_msg(content):
191 | msgTXT = ""
192 | for part in content:
193 | # print type(part)
194 | if type(part) == type(u'\u0000'):
195 | msgTXT += part
196 | elif len(part) > 1:
197 | # 如果是图片
198 | if str(part[0]) == "offpic" or str(part[0]) == "cface":
199 | msgTXT += "[图片]"
200 |
201 | return msgTXT
202 |
203 |
204 | def send_msg(tuin, content, isSess, group_sig, service_type):
205 | if isSess == 0:
206 | reqURL = "https://d1.web2.qq.com/channel/send_buddy_msg2"
207 | data = (
208 | ('r', '{{"to":{0}, "face":594, "content":"[\\"{4}\\", [\\"font\\", {{\\"name\\":\\"Arial\\", \\"size\\":\\"10\\", \\"style\\":[0, 0, 0], \\"color\\":\\"000000\\"}}]]", "clientid":{1}, "msg_id":{2}, "psessionid":"{3}"}}'.format(tuin, ClientID, msgId, PSessionID, CProcess(content))),
209 | ('clientid', ClientID),
210 | ('psessionid', PSessionID)
211 | )
212 | rsp = HttpClient_Ist.Post(reqURL, data, httpsReferer)
213 | rspp = json.loads(rsp)
214 | if rspp['errCode']!= 0:
215 | logging.error("reply pmchat error"+str(rspp['errCode']))
216 | else:
217 | reqURL = "https://d1.web2.qq.com/channel/send_sess_msg2"
218 | data = (
219 | ('r', '{{"to":{0}, "face":594, "content":"[\\"{4}\\", [\\"font\\", {{\\"name\\":\\"Arial\\", \\"size\\":\\"10\\", \\"style\\":[0, 0, 0], \\"color\\":\\"000000\\"}}]]", "clientid":{1}, "msg_id":{2}, "psessionid":"{3}", "group_sig":"{5}", "service_type":{6}}}'.format(tuin, ClientID, msgId, PSessionID, CProcess(content), group_sig, service_type)),
220 | ('clientid', ClientID),
221 | ('psessionid', PSessionID),
222 | ('group_sig', group_sig),
223 | ('service_type',service_type)
224 | )
225 | rsp = HttpClient_Ist.Post(reqURL, data, httpsReferer)
226 | rspp = json.loads(rsp)
227 | if rspp['errCode']!= 0:
228 | logging.error("reply temp pmchat error"+str(rspp['errCode']))
229 |
230 | return rsp
231 |
232 |
233 | def thread_exist(tuin):
234 | for t in ThreadList:
235 | if t.isAlive():
236 | if t.tuin == tuin:
237 | t.check()
238 | return t
239 | else:
240 | ThreadList.remove(t)
241 | return False
242 |
243 |
244 | def group_thread_exist(gid):
245 | for t in GroupThreadList:
246 | if str(t.gid) == str(gid):
247 | return t
248 | return False
249 |
250 | # -----------------
251 | # 类声明
252 | # -----------------
253 |
254 |
255 | class Login(HttpClient):
256 | MaxTryTime = 5
257 |
258 | def __init__(self, vpath, qq=0):
259 | global APPID, AdminQQ, PTWebQQ, VFWebQQ, PSessionID, msgId, MyUIN, GroupNameList, tmpUserName, GroupCodeList
260 | self.VPath = vpath # QRCode保存路径
261 | AdminQQ = int(qq)
262 | logging.critical("正在获取登陆页面")
263 | self.Get('http://w.qq.com/')
264 | html = self.Get(SmartQQUrl,'http://w.qq.com/')
265 | logging.critical("正在获取appid")
266 | APPID = getReValue(html, r'', 'Get AppId Error', 1)
267 | logging.critical("正在获取login_sig")
268 | sign = getReValue(html, r'g_login_sig\s*=\s*encodeURIComponent\s*\("(.*?)"\)', 'Get Login Sign Error', 0)
269 | logging.info('get sign : %s', sign)
270 | logging.critical("正在获取pt_version")
271 | JsVer = getReValue(html, r'g_pt_version\s*=\s*encodeURIComponent\s*\("(\d+)"\)', 'Get g_pt_version Error', 1)
272 | logging.info('get g_pt_version : %s', JsVer)
273 | logging.critical("正在获取mibao_css")
274 | MiBaoCss = getReValue(html, r'g_mibao_css\s*=\s*encodeURIComponent\s*\("(.*?)"\)', 'Get g_mibao_css Error', 1)
275 | logging.info('get g_mibao_css : %s', sign)
276 | StarTime = date_to_millis(datetime.datetime.utcnow())
277 | T = 0
278 | while True:
279 | T = T + 1
280 | self.Download('https://ssl.ptlogin2.qq.com/ptqrshow?appid={0}&e=0&l=M&s=5&d=72&v=4&t=0.0836106{1}4250{2}6653'.format(APPID,random.randint(0,9),random.randint(0,9)), self.VPath)
281 |
282 | logging.info('[{0}] Get QRCode Picture Success.'.format(T))
283 |
284 | QRSig = self.getCookie('qrsig')
285 | while True:
286 | html = self.Get('https://ssl.ptlogin2.qq.com/ptqrlogin?ptqrtoken={0}&webqq_type=10&remember_uin=1&login2qq=1&aid={1}&u1=http%3A%2F%2Fw.qq.com%2Fproxy.html%3Flogin2qq%3D1%26webqq_type%3D10&ptredirect=0&ptlang=2052&daid=164&from_ui=1&pttype=1&dumy=&fp=loginerroralert&action=0-0-{2}&mibao_css={3}&t=1&g=1&js_type=0&js_ver={4}&login_sig={5}&pt_randsalt=2'.format(getQRtoken(QRSig),APPID, date_to_millis(datetime.datetime.utcnow()) - StarTime, MiBaoCss, JsVer, sign),
287 | SmartQQUrl)
288 | # logging.info(html)
289 | ret = html.split("'")
290 | if ret[1] == '65' or ret[1] == '0': # 65: QRCode 失效, 0: 验证成功, 66: 未失效, 67: 验证中
291 | break
292 | time.sleep(2)
293 | if ret[1] == '0' or T > self.MaxTryTime:
294 | break
295 |
296 | logging.info(ret)
297 | if ret[1] != '0':
298 | raise ValueError, "RetCode = "+ret['retcode']
299 | return
300 | logging.critical("二维码已扫描,正在登陆")
301 | pass_time()
302 | # 删除QRCode文件
303 | if os.path.exists(self.VPath):
304 | os.remove(self.VPath)
305 |
306 | # 记录登陆账号的昵称
307 | tmpUserName = ret[11]
308 |
309 | html = self.Get(ret[5])
310 | url = getReValue(html, r' src="(.+?)"', 'Get mibao_res Url Error.', 0)
311 | if url != '':
312 | html = self.Get(url.replace('&', '&'))
313 | url = getReValue(html, r'location\.href="(.+?)"', 'Get Redirect Url Error', 1)
314 | html = self.Get(url)
315 |
316 | PTWebQQ = self.getCookie('ptwebqq')
317 |
318 | logging.info('PTWebQQ: {0}'.format(PTWebQQ))
319 |
320 | LoginError = 3
321 | while LoginError > 0:
322 | try:
323 | html = self.Post('http://d1.web2.qq.com/channel/login2', {
324 | 'r': '{{"ptwebqq":"{0}","clientid":{1},"psessionid":"{2}","status":"online"}}'.format(PTWebQQ, ClientID, PSessionID)
325 | }, 'http://d1.web2.qq.com/proxy.html?v=20151105001&callback=1&id=2')
326 | ret = json.loads(html)
327 | html2 = self.Get("http://s.web2.qq.com/api/getvfwebqq?ptwebqq={0}&clientid={1}&psessionid={2}&t={3}".format(PTWebQQ, ClientID, PSessionID, get_ts()), Referer)
328 | logging.info("getvfwebqq html: " + str(html2))
329 | ret2 = json.loads(html2)
330 | LoginError = 0
331 | except:
332 | LoginError -= 1
333 | logging.critical("登录失败,正在重试")
334 |
335 | if ret['retcode'] != 0 or ret2['retcode'] != 0:
336 | raise ValueError, "Login Retcode="+str(ret['retcode'])
337 | return
338 |
339 | VFWebQQ = ret2['result']['vfwebqq']
340 | PSessionID = ret['result']['psessionid']
341 | MyUIN = ret['result']['uin']
342 | logging.critical("QQ号:{0} 登陆成功, 用户名:{1}".format(ret['result']['uin'], tmpUserName))
343 | logging.info('Login success')
344 | logging.critical("登陆二维码用时" + pass_time() + "秒")
345 |
346 | msgId = int(random.uniform(20000, 50000))
347 | html = self.Post('http://s.web2.qq.com/api/get_group_name_list_mask2', {
348 | 'r': '{{"vfwebqq":"{0}","hash":"{1}"}}'.format(str(VFWebQQ),gethash(str(MyUIN),str(PTWebQQ)))
349 | }, Referer)
350 | ret = json.loads(html)
351 | if ret['retcode']!= 0:
352 | raise ValueError, "retcode error when getting group list: retcode="+str(ret['retcode'])
353 | for t in ret['result']['gnamelist']:
354 | GroupNameList[str(t["name"])]=t["gid"]
355 | GroupCodeList[int(t["gid"])]=int(t["code"])
356 | self.Get('http://d1.web2.qq.com/channel/get_online_buddies2?vfwebqq={0}&clientid={1}&psessionid={2}&t={3}'.format(VFWebQQ,ClientID,PSessionID,get_ts()),Referer)
357 |
358 | class check_msg(threading.Thread):
359 | # try:
360 | # pass
361 | # except KeybordInterrupt:
362 | # try:
363 | # user_input = (raw_input("回复系统:(输入格式:{群聊2or私聊1}, {群号or账号}, {内容})\n")).split(",")
364 | # if (user_input[0] == 1):
365 |
366 | # for kv in self.FriendList :
367 | # if str(kv[1]) == str(user_input[1]):
368 | # tuin == kv[0]
369 |
370 | # self.send_msg(tuin, user_input[2])
371 |
372 | # except KeybordInterrupt:
373 | # exit(0)
374 | # except Exception, e:
375 | # print Exception, e
376 |
377 | def __init__(self):
378 | threading.Thread.__init__(self)
379 |
380 | def run(self):
381 | global PTWebQQ
382 | E = 0
383 | # 心跳包轮询
384 | while 1:
385 | if E > 5:
386 | break
387 | try:
388 | ret = self.check()
389 | except:
390 | E += 1
391 | continue
392 | # logging.info(ret)
393 |
394 | # 返回数据有误
395 | if ret == "":
396 | E += 1
397 | continue
398 |
399 | # POST数据有误
400 | if ret['retcode'] == 100006:
401 | break
402 |
403 | # 无消息
404 | if ret['retcode'] == 102:
405 | E = 0
406 | continue
407 |
408 | # 更新PTWebQQ值
409 | if ret['retcode'] == 116:
410 | PTWebQQ = ret['p']
411 | E = 0
412 | continue
413 |
414 | if ret['retcode'] == 0:
415 | # 信息分发
416 | if 'result' in ret:
417 | msg_handler(ret['result'])
418 | E = 0
419 | continue
420 |
421 | # Other retcode e.g.: 103
422 | E += 1
423 | HttpClient_Ist.Get('http://d1.web2.qq.com/channel/get_online_buddies2?vfwebqq={0}&clientid={1}&psessionid={2}&t={3}'.format(VFWebQQ,ClientID,PSessionID,get_ts()),Referer)
424 |
425 | logging.critical("轮询错误超过五次")
426 |
427 | # 向服务器查询新消息
428 | def check(self):
429 |
430 | html = HttpClient_Ist.Post('https://d1.web2.qq.com/channel/poll2', {
431 | 'r': '{{"ptwebqq":"{1}","clientid":{2},"psessionid":"{0}","key":""}}'.format(PSessionID, PTWebQQ, ClientID)
432 | }, httpsReferer)
433 | logging.info("Check html: " + str(html))
434 | try:
435 | ret = json.loads(html)
436 | except Exception as e:
437 | logging.error(str(e))
438 | logging.critical("Check error occured, retrying.")
439 | return self.check()
440 |
441 | return ret
442 |
443 |
444 | class pmchat_thread(threading.Thread):
445 |
446 |
447 | # con = threading.Condition()
448 | # newIp = ''
449 |
450 | def __init__(self, tuin, isSess, group_sig, service_type):
451 | threading.Thread.__init__(self)
452 | self.tuin = tuin
453 | self.isSess = isSess
454 | self.group_sig=group_sig
455 | self.service_type=service_type
456 | self.lastcheck = time.time()
457 | self.lastseq=0
458 | self.replystreak = 0
459 | logging.info("私聊线程生成,私聊对象UIN:"+str(self.tuin))
460 | def check(self):
461 | self.lastcheck = time.time()
462 | def run(self):
463 | while 1:
464 | time.sleep(199)
465 | if time.time() - self.lastcheck > 300:
466 | break
467 |
468 | def reply(self, content):
469 | send_msg(self.tuin, str(content), self.isSess, self.group_sig, self.service_type)
470 | logging.info("Reply to " + str(self.tuin) + ":" + str(content))
471 |
472 | def push(self, ipContent, seq):
473 | if seq == self.lastseq:
474 | return True
475 | else:
476 | self.lastseq=seq
477 | #防止机器人对聊
478 | if self.replystreak>30:
479 | self.replystreak = 0
480 | return True
481 | try:
482 | self.replystreak = self.replystreak + 1
483 | logging.info("PM get info from AI: "+ipContent)
484 | paraf={ 'userid' : str(self.tuin), 'key' : tulingkey, 'info' : ipContent}
485 | info = HttpClient_Ist.Get('http://www.tuling123.com/openapi/api?'+urllib.urlencode(paraf))
486 | logging.info("AI REPLY:"+str(info))
487 | info = json.loads(info)
488 | if info["code"] in [40001, 40003, 40004]:
489 | self.reply("我今天累了,不聊了")
490 | logging.warning("Reach max AI call")
491 | elif info["code"] in [40002, 40005, 40006, 40007]:
492 | self.reply("我遇到了一点问题,请稍后@我")
493 | logging.warning("PM AI return error, code:"+str(info["code"]))
494 | else:
495 | rpy = str(info["text"]).replace('<主人>','你').replace(' ',"\n")
496 | self.reply(rpy)
497 | return True
498 | except Exception, e:
499 | logging.error("ERROR:"+str(e))
500 | return False
501 |
502 |
503 |
504 | class group_thread(threading.Thread):
505 | last1 = ''
506 | lastseq = 0
507 | replyList = {}
508 | followList = []
509 | NickList = {}
510 |
511 | # 属性
512 | repeatPicture = True
513 |
514 | def __init__(self, guin, gcode):
515 | threading.Thread.__init__(self)
516 | self.guin = guin
517 | self.gid = gcode
518 | self.load()
519 | self.lastreplytime=0
520 | ret = HttpClient_Ist.Get('http://s.web2.qq.com/api/get_group_info_ext2?gcode={0}&vfwebqq={1}&t={2}'.format(gcode,VFWebQQ,get_ts()),Referer)
521 | ret = json.loads(ret)
522 | for t in ret['result']['minfo']:
523 | self.NickList[str(t["nick"])]=int(t["uin"])
524 |
525 | def learn(self, key, value, needreply=True):
526 | if key in self.replyList:
527 | self.replyList[key].append(value)
528 | else:
529 | self.replyList[key] = [value]
530 |
531 | if needreply:
532 | self.reply("我记住" + str(key) + "的回复了")
533 | self.save()
534 |
535 | def delete(self, key, value, needreply=True):
536 | if key in self.replyList and self.replyList[key].count(value):
537 | self.replyList[key].remove(value)
538 | if needreply:
539 | self.reply("我已经不会说" + str(value) + "了")
540 | self.save()
541 |
542 | else:
543 | if needreply:
544 | self.reply("没找到你说的那句话哦")
545 |
546 | def reply(self, content):
547 | if time.time() - self.lastreplytime < 3.0:
548 | logging.info("REPLY TOO FAST, ABANDON:"+content)
549 | return False
550 | self.lastreplytime = time.time()
551 | reqURL = "https://d1.web2.qq.com/channel/send_qun_msg2"
552 | data = (
553 | ('r', '{{"group_uin":{0}, "face":564,"content":"[\\"{4}\\",[\\"font\\",{{\\"name\\":\\"Arial\\",\\"size\\":\\"10\\",\\"style\\":[0,0,0],\\"color\\":\\"000000\\"}}]]","clientid":{1},"msg_id":{2},"psessionid":"{3}"}}'.format(self.guin, ClientID, msgId, PSessionID, CProcess(content))),
554 | ('clientid', ClientID),
555 | ('psessionid', PSessionID)
556 | )
557 | logging.info("Reply package: " + str(data))
558 | rsp = HttpClient_Ist.Post(reqURL, data, httpsReferer)
559 | try:
560 | rspp = json.loads(rsp)
561 | if rspp['errCode'] == 0:
562 | logging.info("[Reply to group " + str(self.gid) + "]:" + str(content))
563 | return True
564 | except:
565 | pass
566 | logging.error("[Fail to reply group " + str(self.gid)+ "]:" + str(rsp))
567 | return rsp
568 |
569 | def handle(self, send_uin, content, seq):
570 | # 避免重复处理相同信息
571 | if seq != self.lastseq:
572 | pattern = re.compile(r'^(?:!|!)(learn|delete) {(.+)}{(.+)}')
573 | match = pattern.match(content)
574 | if match:
575 | if match.group(1) == 'learn':
576 | self.learn(str(match.group(2)).decode('UTF-8'), str(match.group(3)).decode('UTF-8'))
577 | logging.debug(self.replyList)
578 | if match.group(1) == 'delete':
579 | self.delete(str(match.group(2)).decode('UTF-8'), str(match.group(3)).decode('UTF-8'))
580 | logging.debug(self.replyList)
581 |
582 | else:
583 | # if not self.follow(send_uin, content):
584 | # if not self.tucao(content):
585 | # if not self.repeat(content):
586 | # if not self.callout(content):
587 | # pass
588 | if self.aboutme(content):
589 | return
590 | if self.deleteall(content):
591 | return
592 | if self.callout(send_uin, content):
593 | return
594 | if self.follow(send_uin, content):
595 | return
596 | if self.tucao(content):
597 | return
598 | if self.repeat(content):
599 | return
600 |
601 | else:
602 | logging.warning("message seq repeat detected.")
603 | self.lastseq = seq
604 |
605 | def tucao(self, content):
606 | for key in self.replyList:
607 | if str(key) in content and self.replyList[key]:
608 | rd = random.randint(0, len(self.replyList[key]) - 1)
609 | self.reply(self.replyList[key][rd])
610 | logging.info('Group Reply'+str(self.replyList[key][rd]))
611 | return True
612 | return False
613 |
614 | def repeat(self, content):
615 | if self.last1 == str(content) and content != '' and content != ' ':
616 | if self.repeatPicture or "[图片]" not in content:
617 | self.reply(content)
618 | logging.info("已复读:{" + str(content) + "}")
619 | return True
620 | self.last1 = content
621 |
622 | return False
623 |
624 | def follow(self, send_uin, content):
625 | pattern = re.compile(r'^(?:!|!)(follow|unfollow) (.*)!')
626 | match = pattern.match(content)
627 |
628 | if match:
629 | target1 = str(match.group(2))
630 | if target1 == 'me':
631 | target = send_uin
632 | target1 = '你'
633 | else:
634 | if target1 in self.NickList:
635 | target = self.NickList[target1]
636 | else:
637 | self.reply("找不到成员:"+target1)
638 | return True
639 |
640 | if match.group(1) == 'follow' and target not in self.followList:
641 | self.followList.append(target)
642 | self.reply("正在关注" + target1)
643 | return True
644 | if match.group(1) == 'unfollow' and target in self.followList:
645 | self.followList.remove(target)
646 | self.reply("我不关注" + target1 + "了!")
647 | return True
648 | else:
649 | if send_uin in self.followList:
650 | self.reply(content)
651 | return True
652 | return False
653 |
654 | def save(self):
655 | try:
656 | with open("database."+str(self.gid)+".save", "w+") as savefile:
657 | savefile.write(json.dumps(self.replyList))
658 | savefile.close()
659 | except Exception, e:
660 | logging.error("写存档出错:"+str(e))
661 | def load(self):
662 | try:
663 | with open("database."+str(self.gid)+".save", "r") as savefile:
664 | saves = savefile.read()
665 | if saves:
666 | self.replyList = json.loads(saves)
667 | savefile.close()
668 | except Exception, e:
669 | logging.info("读取存档出错:"+str(e))
670 |
671 | def callout(self, send_uin, content):
672 | pattern = re.compile(r'^(?:!|!)(ai) (.+)')
673 | match = pattern.match(content)
674 | try:
675 | if match:
676 | logging.info("get info from AI: "+str(match.group(2)).decode('UTF-8'))
677 | usr = str(send_uin)
678 | paraf={ 'userid' : usr+'g', 'key' : tulingkey, 'info' : str(match.group(2)).decode('UTF-8')}
679 |
680 | info = HttpClient_Ist.Get('http://www.tuling123.com/openapi/api?'+urllib.urlencode(paraf))
681 | logging.info("AI REPLY:"+str(info))
682 | info = json.loads(info)
683 | if info["code"] in [40001, 40003, 40004]:
684 | self.reply("我今天累了,不聊了")
685 | logging.warning("Reach max AI call")
686 | elif info["code"] in [40002, 40005, 40006, 40007]:
687 | self.reply("我遇到了一点问题,请稍后@我")
688 | logging.warning("AI return error, code:"+str(info["code"]))
689 | else:
690 | self.reply(str(info["text"]).replace('<主人>','你').replace(' ',"\n"))
691 | return True
692 | except Exception, e:
693 | logging.error("ERROR"+str(e))
694 | return False
695 |
696 | def aboutme(self, content):
697 | pattern = re.compile(r'^(?:!|!)(about)')
698 | match = pattern.match(content)
699 | try:
700 | if match:
701 | logging.info("output about info")
702 | info="小黄鸡3.8 By Jeffery 详细说明见github.com/zeruniverse/QQRobot"
703 | self.reply(info)
704 | return True
705 | except Exception, e:
706 | logging.error("ERROR"+str(e))
707 | return False
708 |
709 | def deleteall(self, content):
710 | pattern = re.compile(r'^(?:!|!)(deleteall)')
711 | match = pattern.match(content)
712 | try:
713 | if match:
714 | logging.info("Delete all learned data for group:"+str(self.gid))
715 | info="已删除所有学习内容"
716 | self.replyList.clear()
717 | self.save()
718 | self.reply(info)
719 | return True
720 | except Exception, e:
721 | logging.error("ERROR:"+str(e))
722 | return False
723 | # -----------------
724 | # 主程序
725 | # -----------------
726 |
727 | if __name__ == "__main__":
728 | vpath = './v.png'
729 | qq = 0
730 | if len(sys.argv) > 1:
731 | vpath = sys.argv[1]
732 | if len(sys.argv) > 2:
733 | qq = sys.argv[2]
734 |
735 | try:
736 | pass_time()
737 | qqLogin = Login(vpath, qq)
738 | except Exception, e:
739 | logging.critical(str(e))
740 | os._exit(1)
741 | t_check = check_msg()
742 | t_check.setDaemon(True)
743 | t_check.start()
744 | try:
745 | with open('groupfollow.txt','r') as f:
746 | for line in f:
747 | tmp = line.strip('\n').strip('\r')
748 | if str(tmp) in GroupNameList:
749 | GroupWatchList.append(str(GroupNameList[str(tmp)]))
750 | logging.info("关注:"+str(tmp))
751 | else:
752 | logging.error("无法找到群:"+str(tmp))
753 | except Exception, e:
754 | logging.error("读取组存档出错:"+str(e))
755 |
756 |
757 | t_check.join()
758 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | All files in this software are licensed under GNU GPL 3.0
2 | HttpClient.py is written by [xqin]: https://github.com/xqin/SmartQQ-for-Raspberry-Pi
3 | All other files are written by [Jeffery]: https://github.com/zeruniverse/QQRobot
4 | The implementation of SmartQQ Message handler and part of group chatting logic are from [Yinzo]: https://github.com/Yinzo/SmartQQBot
5 | All source codes I referred to are licensed under GNU GPL 3.0 or compatible to GNU GPL 3.0
6 |
7 | - Jeffery Zhao
8 | Below is the GNU GPL 3.0 License
9 |
10 | ------------------------------------------------------------------------------------------------------------------------------------
11 |
12 | GNU GENERAL PUBLIC LICENSE
13 | Version 3, 29 June 2007
14 |
15 | Copyright (C) 2007 Free Software Foundation, Inc.
16 | Everyone is permitted to copy and distribute verbatim copies
17 | of this license document, but changing it is not allowed.
18 |
19 | Preamble
20 |
21 | The GNU General Public License is a free, copyleft license for
22 | software and other kinds of works.
23 |
24 | The licenses for most software and other practical works are designed
25 | to take away your freedom to share and change the works. By contrast,
26 | the GNU General Public License is intended to guarantee your freedom to
27 | share and change all versions of a program--to make sure it remains free
28 | software for all its users. We, the Free Software Foundation, use the
29 | GNU General Public License for most of our software; it applies also to
30 | any other work released this way by its authors. You can apply it to
31 | your programs, too.
32 |
33 | When we speak of free software, we are referring to freedom, not
34 | price. Our General Public Licenses are designed to make sure that you
35 | have the freedom to distribute copies of free software (and charge for
36 | them if you wish), that you receive source code or can get it if you
37 | want it, that you can change the software or use pieces of it in new
38 | free programs, and that you know you can do these things.
39 |
40 | To protect your rights, we need to prevent others from denying you
41 | these rights or asking you to surrender the rights. Therefore, you have
42 | certain responsibilities if you distribute copies of the software, or if
43 | you modify it: responsibilities to respect the freedom of others.
44 |
45 | For example, if you distribute copies of such a program, whether
46 | gratis or for a fee, you must pass on to the recipients the same
47 | freedoms that you received. You must make sure that they, too, receive
48 | or can get the source code. And you must show them these terms so they
49 | know their rights.
50 |
51 | Developers that use the GNU GPL protect your rights with two steps:
52 | (1) assert copyright on the software, and (2) offer you this License
53 | giving you legal permission to copy, distribute and/or modify it.
54 |
55 | For the developers' and authors' protection, the GPL clearly explains
56 | that there is no warranty for this free software. For both users' and
57 | authors' sake, the GPL requires that modified versions be marked as
58 | changed, so that their problems will not be attributed erroneously to
59 | authors of previous versions.
60 |
61 | Some devices are designed to deny users access to install or run
62 | modified versions of the software inside them, although the manufacturer
63 | can do so. This is fundamentally incompatible with the aim of
64 | protecting users' freedom to change the software. The systematic
65 | pattern of such abuse occurs in the area of products for individuals to
66 | use, which is precisely where it is most unacceptable. Therefore, we
67 | have designed this version of the GPL to prohibit the practice for those
68 | products. If such problems arise substantially in other domains, we
69 | stand ready to extend this provision to those domains in future versions
70 | of the GPL, as needed to protect the freedom of users.
71 |
72 | Finally, every program is threatened constantly by software patents.
73 | States should not allow patents to restrict development and use of
74 | software on general-purpose computers, but in those that do, we wish to
75 | avoid the special danger that patents applied to a free program could
76 | make it effectively proprietary. To prevent this, the GPL assures that
77 | patents cannot be used to render the program non-free.
78 |
79 | The precise terms and conditions for copying, distribution and
80 | modification follow.
81 |
82 | TERMS AND CONDITIONS
83 |
84 | 0. Definitions.
85 |
86 | "This License" refers to version 3 of the GNU General Public License.
87 |
88 | "Copyright" also means copyright-like laws that apply to other kinds of
89 | works, such as semiconductor masks.
90 |
91 | "The Program" refers to any copyrightable work licensed under this
92 | License. Each licensee is addressed as "you". "Licensees" and
93 | "recipients" may be individuals or organizations.
94 |
95 | To "modify" a work means to copy from or adapt all or part of the work
96 | in a fashion requiring copyright permission, other than the making of an
97 | exact copy. The resulting work is called a "modified version" of the
98 | earlier work or a work "based on" the earlier work.
99 |
100 | A "covered work" means either the unmodified Program or a work based
101 | on the Program.
102 |
103 | To "propagate" a work means to do anything with it that, without
104 | permission, would make you directly or secondarily liable for
105 | infringement under applicable copyright law, except executing it on a
106 | computer or modifying a private copy. Propagation includes copying,
107 | distribution (with or without modification), making available to the
108 | public, and in some countries other activities as well.
109 |
110 | To "convey" a work means any kind of propagation that enables other
111 | parties to make or receive copies. Mere interaction with a user through
112 | a computer network, with no transfer of a copy, is not conveying.
113 |
114 | An interactive user interface displays "Appropriate Legal Notices"
115 | to the extent that it includes a convenient and prominently visible
116 | feature that (1) displays an appropriate copyright notice, and (2)
117 | tells the user that there is no warranty for the work (except to the
118 | extent that warranties are provided), that licensees may convey the
119 | work under this License, and how to view a copy of this License. If
120 | the interface presents a list of user commands or options, such as a
121 | menu, a prominent item in the list meets this criterion.
122 |
123 | 1. Source Code.
124 |
125 | The "source code" for a work means the preferred form of the work
126 | for making modifications to it. "Object code" means any non-source
127 | form of a work.
128 |
129 | A "Standard Interface" means an interface that either is an official
130 | standard defined by a recognized standards body, or, in the case of
131 | interfaces specified for a particular programming language, one that
132 | is widely used among developers working in that language.
133 |
134 | The "System Libraries" of an executable work include anything, other
135 | than the work as a whole, that (a) is included in the normal form of
136 | packaging a Major Component, but which is not part of that Major
137 | Component, and (b) serves only to enable use of the work with that
138 | Major Component, or to implement a Standard Interface for which an
139 | implementation is available to the public in source code form. A
140 | "Major Component", in this context, means a major essential component
141 | (kernel, window system, and so on) of the specific operating system
142 | (if any) on which the executable work runs, or a compiler used to
143 | produce the work, or an object code interpreter used to run it.
144 |
145 | The "Corresponding Source" for a work in object code form means all
146 | the source code needed to generate, install, and (for an executable
147 | work) run the object code and to modify the work, including scripts to
148 | control those activities. However, it does not include the work's
149 | System Libraries, or general-purpose tools or generally available free
150 | programs which are used unmodified in performing those activities but
151 | which are not part of the work. For example, Corresponding Source
152 | includes interface definition files associated with source files for
153 | the work, and the source code for shared libraries and dynamically
154 | linked subprograms that the work is specifically designed to require,
155 | such as by intimate data communication or control flow between those
156 | subprograms and other parts of the work.
157 |
158 | The Corresponding Source need not include anything that users
159 | can regenerate automatically from other parts of the Corresponding
160 | Source.
161 |
162 | The Corresponding Source for a work in source code form is that
163 | same work.
164 |
165 | 2. Basic Permissions.
166 |
167 | All rights granted under this License are granted for the term of
168 | copyright on the Program, and are irrevocable provided the stated
169 | conditions are met. This License explicitly affirms your unlimited
170 | permission to run the unmodified Program. The output from running a
171 | covered work is covered by this License only if the output, given its
172 | content, constitutes a covered work. This License acknowledges your
173 | rights of fair use or other equivalent, as provided by copyright law.
174 |
175 | You may make, run and propagate covered works that you do not
176 | convey, without conditions so long as your license otherwise remains
177 | in force. You may convey covered works to others for the sole purpose
178 | of having them make modifications exclusively for you, or provide you
179 | with facilities for running those works, provided that you comply with
180 | the terms of this License in conveying all material for which you do
181 | not control copyright. Those thus making or running the covered works
182 | for you must do so exclusively on your behalf, under your direction
183 | and control, on terms that prohibit them from making any copies of
184 | your copyrighted material outside their relationship with you.
185 |
186 | Conveying under any other circumstances is permitted solely under
187 | the conditions stated below. Sublicensing is not allowed; section 10
188 | makes it unnecessary.
189 |
190 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
191 |
192 | No covered work shall be deemed part of an effective technological
193 | measure under any applicable law fulfilling obligations under article
194 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
195 | similar laws prohibiting or restricting circumvention of such
196 | measures.
197 |
198 | When you convey a covered work, you waive any legal power to forbid
199 | circumvention of technological measures to the extent such circumvention
200 | is effected by exercising rights under this License with respect to
201 | the covered work, and you disclaim any intention to limit operation or
202 | modification of the work as a means of enforcing, against the work's
203 | users, your or third parties' legal rights to forbid circumvention of
204 | technological measures.
205 |
206 | 4. Conveying Verbatim Copies.
207 |
208 | You may convey verbatim copies of the Program's source code as you
209 | receive it, in any medium, provided that you conspicuously and
210 | appropriately publish on each copy an appropriate copyright notice;
211 | keep intact all notices stating that this License and any
212 | non-permissive terms added in accord with section 7 apply to the code;
213 | keep intact all notices of the absence of any warranty; and give all
214 | recipients a copy of this License along with the Program.
215 |
216 | You may charge any price or no price for each copy that you convey,
217 | and you may offer support or warranty protection for a fee.
218 |
219 | 5. Conveying Modified Source Versions.
220 |
221 | You may convey a work based on the Program, or the modifications to
222 | produce it from the Program, in the form of source code under the
223 | terms of section 4, provided that you also meet all of these conditions:
224 |
225 | a) The work must carry prominent notices stating that you modified
226 | it, and giving a relevant date.
227 |
228 | b) The work must carry prominent notices stating that it is
229 | released under this License and any conditions added under section
230 | 7. This requirement modifies the requirement in section 4 to
231 | "keep intact all notices".
232 |
233 | c) You must license the entire work, as a whole, under this
234 | License to anyone who comes into possession of a copy. This
235 | License will therefore apply, along with any applicable section 7
236 | additional terms, to the whole of the work, and all its parts,
237 | regardless of how they are packaged. This License gives no
238 | permission to license the work in any other way, but it does not
239 | invalidate such permission if you have separately received it.
240 |
241 | d) If the work has interactive user interfaces, each must display
242 | Appropriate Legal Notices; however, if the Program has interactive
243 | interfaces that do not display Appropriate Legal Notices, your
244 | work need not make them do so.
245 |
246 | A compilation of a covered work with other separate and independent
247 | works, which are not by their nature extensions of the covered work,
248 | and which are not combined with it such as to form a larger program,
249 | in or on a volume of a storage or distribution medium, is called an
250 | "aggregate" if the compilation and its resulting copyright are not
251 | used to limit the access or legal rights of the compilation's users
252 | beyond what the individual works permit. Inclusion of a covered work
253 | in an aggregate does not cause this License to apply to the other
254 | parts of the aggregate.
255 |
256 | 6. Conveying Non-Source Forms.
257 |
258 | You may convey a covered work in object code form under the terms
259 | of sections 4 and 5, provided that you also convey the
260 | machine-readable Corresponding Source under the terms of this License,
261 | in one of these ways:
262 |
263 | a) Convey the object code in, or embodied in, a physical product
264 | (including a physical distribution medium), accompanied by the
265 | Corresponding Source fixed on a durable physical medium
266 | customarily used for software interchange.
267 |
268 | b) Convey the object code in, or embodied in, a physical product
269 | (including a physical distribution medium), accompanied by a
270 | written offer, valid for at least three years and valid for as
271 | long as you offer spare parts or customer support for that product
272 | model, to give anyone who possesses the object code either (1) a
273 | copy of the Corresponding Source for all the software in the
274 | product that is covered by this License, on a durable physical
275 | medium customarily used for software interchange, for a price no
276 | more than your reasonable cost of physically performing this
277 | conveying of source, or (2) access to copy the
278 | Corresponding Source from a network server at no charge.
279 |
280 | c) Convey individual copies of the object code with a copy of the
281 | written offer to provide the Corresponding Source. This
282 | alternative is allowed only occasionally and noncommercially, and
283 | only if you received the object code with such an offer, in accord
284 | with subsection 6b.
285 |
286 | d) Convey the object code by offering access from a designated
287 | place (gratis or for a charge), and offer equivalent access to the
288 | Corresponding Source in the same way through the same place at no
289 | further charge. You need not require recipients to copy the
290 | Corresponding Source along with the object code. If the place to
291 | copy the object code is a network server, the Corresponding Source
292 | may be on a different server (operated by you or a third party)
293 | that supports equivalent copying facilities, provided you maintain
294 | clear directions next to the object code saying where to find the
295 | Corresponding Source. Regardless of what server hosts the
296 | Corresponding Source, you remain obligated to ensure that it is
297 | available for as long as needed to satisfy these requirements.
298 |
299 | e) Convey the object code using peer-to-peer transmission, provided
300 | you inform other peers where the object code and Corresponding
301 | Source of the work are being offered to the general public at no
302 | charge under subsection 6d.
303 |
304 | A separable portion of the object code, whose source code is excluded
305 | from the Corresponding Source as a System Library, need not be
306 | included in conveying the object code work.
307 |
308 | A "User Product" is either (1) a "consumer product", which means any
309 | tangible personal property which is normally used for personal, family,
310 | or household purposes, or (2) anything designed or sold for incorporation
311 | into a dwelling. In determining whether a product is a consumer product,
312 | doubtful cases shall be resolved in favor of coverage. For a particular
313 | product received by a particular user, "normally used" refers to a
314 | typical or common use of that class of product, regardless of the status
315 | of the particular user or of the way in which the particular user
316 | actually uses, or expects or is expected to use, the product. A product
317 | is a consumer product regardless of whether the product has substantial
318 | commercial, industrial or non-consumer uses, unless such uses represent
319 | the only significant mode of use of the product.
320 |
321 | "Installation Information" for a User Product means any methods,
322 | procedures, authorization keys, or other information required to install
323 | and execute modified versions of a covered work in that User Product from
324 | a modified version of its Corresponding Source. The information must
325 | suffice to ensure that the continued functioning of the modified object
326 | code is in no case prevented or interfered with solely because
327 | modification has been made.
328 |
329 | If you convey an object code work under this section in, or with, or
330 | specifically for use in, a User Product, and the conveying occurs as
331 | part of a transaction in which the right of possession and use of the
332 | User Product is transferred to the recipient in perpetuity or for a
333 | fixed term (regardless of how the transaction is characterized), the
334 | Corresponding Source conveyed under this section must be accompanied
335 | by the Installation Information. But this requirement does not apply
336 | if neither you nor any third party retains the ability to install
337 | modified object code on the User Product (for example, the work has
338 | been installed in ROM).
339 |
340 | The requirement to provide Installation Information does not include a
341 | requirement to continue to provide support service, warranty, or updates
342 | for a work that has been modified or installed by the recipient, or for
343 | the User Product in which it has been modified or installed. Access to a
344 | network may be denied when the modification itself materially and
345 | adversely affects the operation of the network or violates the rules and
346 | protocols for communication across the network.
347 |
348 | Corresponding Source conveyed, and Installation Information provided,
349 | in accord with this section must be in a format that is publicly
350 | documented (and with an implementation available to the public in
351 | source code form), and must require no special password or key for
352 | unpacking, reading or copying.
353 |
354 | 7. Additional Terms.
355 |
356 | "Additional permissions" are terms that supplement the terms of this
357 | License by making exceptions from one or more of its conditions.
358 | Additional permissions that are applicable to the entire Program shall
359 | be treated as though they were included in this License, to the extent
360 | that they are valid under applicable law. If additional permissions
361 | apply only to part of the Program, that part may be used separately
362 | under those permissions, but the entire Program remains governed by
363 | this License without regard to the additional permissions.
364 |
365 | When you convey a copy of a covered work, you may at your option
366 | remove any additional permissions from that copy, or from any part of
367 | it. (Additional permissions may be written to require their own
368 | removal in certain cases when you modify the work.) You may place
369 | additional permissions on material, added by you to a covered work,
370 | for which you have or can give appropriate copyright permission.
371 |
372 | Notwithstanding any other provision of this License, for material you
373 | add to a covered work, you may (if authorized by the copyright holders of
374 | that material) supplement the terms of this License with terms:
375 |
376 | a) Disclaiming warranty or limiting liability differently from the
377 | terms of sections 15 and 16 of this License; or
378 |
379 | b) Requiring preservation of specified reasonable legal notices or
380 | author attributions in that material or in the Appropriate Legal
381 | Notices displayed by works containing it; or
382 |
383 | c) Prohibiting misrepresentation of the origin of that material, or
384 | requiring that modified versions of such material be marked in
385 | reasonable ways as different from the original version; or
386 |
387 | d) Limiting the use for publicity purposes of names of licensors or
388 | authors of the material; or
389 |
390 | e) Declining to grant rights under trademark law for use of some
391 | trade names, trademarks, or service marks; or
392 |
393 | f) Requiring indemnification of licensors and authors of that
394 | material by anyone who conveys the material (or modified versions of
395 | it) with contractual assumptions of liability to the recipient, for
396 | any liability that these contractual assumptions directly impose on
397 | those licensors and authors.
398 |
399 | All other non-permissive additional terms are considered "further
400 | restrictions" within the meaning of section 10. If the Program as you
401 | received it, or any part of it, contains a notice stating that it is
402 | governed by this License along with a term that is a further
403 | restriction, you may remove that term. If a license document contains
404 | a further restriction but permits relicensing or conveying under this
405 | License, you may add to a covered work material governed by the terms
406 | of that license document, provided that the further restriction does
407 | not survive such relicensing or conveying.
408 |
409 | If you add terms to a covered work in accord with this section, you
410 | must place, in the relevant source files, a statement of the
411 | additional terms that apply to those files, or a notice indicating
412 | where to find the applicable terms.
413 |
414 | Additional terms, permissive or non-permissive, may be stated in the
415 | form of a separately written license, or stated as exceptions;
416 | the above requirements apply either way.
417 |
418 | 8. Termination.
419 |
420 | You may not propagate or modify a covered work except as expressly
421 | provided under this License. Any attempt otherwise to propagate or
422 | modify it is void, and will automatically terminate your rights under
423 | this License (including any patent licenses granted under the third
424 | paragraph of section 11).
425 |
426 | However, if you cease all violation of this License, then your
427 | license from a particular copyright holder is reinstated (a)
428 | provisionally, unless and until the copyright holder explicitly and
429 | finally terminates your license, and (b) permanently, if the copyright
430 | holder fails to notify you of the violation by some reasonable means
431 | prior to 60 days after the cessation.
432 |
433 | Moreover, your license from a particular copyright holder is
434 | reinstated permanently if the copyright holder notifies you of the
435 | violation by some reasonable means, this is the first time you have
436 | received notice of violation of this License (for any work) from that
437 | copyright holder, and you cure the violation prior to 30 days after
438 | your receipt of the notice.
439 |
440 | Termination of your rights under this section does not terminate the
441 | licenses of parties who have received copies or rights from you under
442 | this License. If your rights have been terminated and not permanently
443 | reinstated, you do not qualify to receive new licenses for the same
444 | material under section 10.
445 |
446 | 9. Acceptance Not Required for Having Copies.
447 |
448 | You are not required to accept this License in order to receive or
449 | run a copy of the Program. Ancillary propagation of a covered work
450 | occurring solely as a consequence of using peer-to-peer transmission
451 | to receive a copy likewise does not require acceptance. However,
452 | nothing other than this License grants you permission to propagate or
453 | modify any covered work. These actions infringe copyright if you do
454 | not accept this License. Therefore, by modifying or propagating a
455 | covered work, you indicate your acceptance of this License to do so.
456 |
457 | 10. Automatic Licensing of Downstream Recipients.
458 |
459 | Each time you convey a covered work, the recipient automatically
460 | receives a license from the original licensors, to run, modify and
461 | propagate that work, subject to this License. You are not responsible
462 | for enforcing compliance by third parties with this License.
463 |
464 | An "entity transaction" is a transaction transferring control of an
465 | organization, or substantially all assets of one, or subdividing an
466 | organization, or merging organizations. If propagation of a covered
467 | work results from an entity transaction, each party to that
468 | transaction who receives a copy of the work also receives whatever
469 | licenses to the work the party's predecessor in interest had or could
470 | give under the previous paragraph, plus a right to possession of the
471 | Corresponding Source of the work from the predecessor in interest, if
472 | the predecessor has it or can get it with reasonable efforts.
473 |
474 | You may not impose any further restrictions on the exercise of the
475 | rights granted or affirmed under this License. For example, you may
476 | not impose a license fee, royalty, or other charge for exercise of
477 | rights granted under this License, and you may not initiate litigation
478 | (including a cross-claim or counterclaim in a lawsuit) alleging that
479 | any patent claim is infringed by making, using, selling, offering for
480 | sale, or importing the Program or any portion of it.
481 |
482 | 11. Patents.
483 |
484 | A "contributor" is a copyright holder who authorizes use under this
485 | License of the Program or a work on which the Program is based. The
486 | work thus licensed is called the contributor's "contributor version".
487 |
488 | A contributor's "essential patent claims" are all patent claims
489 | owned or controlled by the contributor, whether already acquired or
490 | hereafter acquired, that would be infringed by some manner, permitted
491 | by this License, of making, using, or selling its contributor version,
492 | but do not include claims that would be infringed only as a
493 | consequence of further modification of the contributor version. For
494 | purposes of this definition, "control" includes the right to grant
495 | patent sublicenses in a manner consistent with the requirements of
496 | this License.
497 |
498 | Each contributor grants you a non-exclusive, worldwide, royalty-free
499 | patent license under the contributor's essential patent claims, to
500 | make, use, sell, offer for sale, import and otherwise run, modify and
501 | propagate the contents of its contributor version.
502 |
503 | In the following three paragraphs, a "patent license" is any express
504 | agreement or commitment, however denominated, not to enforce a patent
505 | (such as an express permission to practice a patent or covenant not to
506 | sue for patent infringement). To "grant" such a patent license to a
507 | party means to make such an agreement or commitment not to enforce a
508 | patent against the party.
509 |
510 | If you convey a covered work, knowingly relying on a patent license,
511 | and the Corresponding Source of the work is not available for anyone
512 | to copy, free of charge and under the terms of this License, through a
513 | publicly available network server or other readily accessible means,
514 | then you must either (1) cause the Corresponding Source to be so
515 | available, or (2) arrange to deprive yourself of the benefit of the
516 | patent license for this particular work, or (3) arrange, in a manner
517 | consistent with the requirements of this License, to extend the patent
518 | license to downstream recipients. "Knowingly relying" means you have
519 | actual knowledge that, but for the patent license, your conveying the
520 | covered work in a country, or your recipient's use of the covered work
521 | in a country, would infringe one or more identifiable patents in that
522 | country that you have reason to believe are valid.
523 |
524 | If, pursuant to or in connection with a single transaction or
525 | arrangement, you convey, or propagate by procuring conveyance of, a
526 | covered work, and grant a patent license to some of the parties
527 | receiving the covered work authorizing them to use, propagate, modify
528 | or convey a specific copy of the covered work, then the patent license
529 | you grant is automatically extended to all recipients of the covered
530 | work and works based on it.
531 |
532 | A patent license is "discriminatory" if it does not include within
533 | the scope of its coverage, prohibits the exercise of, or is
534 | conditioned on the non-exercise of one or more of the rights that are
535 | specifically granted under this License. You may not convey a covered
536 | work if you are a party to an arrangement with a third party that is
537 | in the business of distributing software, under which you make payment
538 | to the third party based on the extent of your activity of conveying
539 | the work, and under which the third party grants, to any of the
540 | parties who would receive the covered work from you, a discriminatory
541 | patent license (a) in connection with copies of the covered work
542 | conveyed by you (or copies made from those copies), or (b) primarily
543 | for and in connection with specific products or compilations that
544 | contain the covered work, unless you entered into that arrangement,
545 | or that patent license was granted, prior to 28 March 2007.
546 |
547 | Nothing in this License shall be construed as excluding or limiting
548 | any implied license or other defenses to infringement that may
549 | otherwise be available to you under applicable patent law.
550 |
551 | 12. No Surrender of Others' Freedom.
552 |
553 | If conditions are imposed on you (whether by court order, agreement or
554 | otherwise) that contradict the conditions of this License, they do not
555 | excuse you from the conditions of this License. If you cannot convey a
556 | covered work so as to satisfy simultaneously your obligations under this
557 | License and any other pertinent obligations, then as a consequence you may
558 | not convey it at all. For example, if you agree to terms that obligate you
559 | to collect a royalty for further conveying from those to whom you convey
560 | the Program, the only way you could satisfy both those terms and this
561 | License would be to refrain entirely from conveying the Program.
562 |
563 | 13. Use with the GNU Affero General Public License.
564 |
565 | Notwithstanding any other provision of this License, you have
566 | permission to link or combine any covered work with a work licensed
567 | under version 3 of the GNU Affero General Public License into a single
568 | combined work, and to convey the resulting work. The terms of this
569 | License will continue to apply to the part which is the covered work,
570 | but the special requirements of the GNU Affero General Public License,
571 | section 13, concerning interaction through a network will apply to the
572 | combination as such.
573 |
574 | 14. Revised Versions of this License.
575 |
576 | The Free Software Foundation may publish revised and/or new versions of
577 | the GNU General Public License from time to time. Such new versions will
578 | be similar in spirit to the present version, but may differ in detail to
579 | address new problems or concerns.
580 |
581 | Each version is given a distinguishing version number. If the
582 | Program specifies that a certain numbered version of the GNU General
583 | Public License "or any later version" applies to it, you have the
584 | option of following the terms and conditions either of that numbered
585 | version or of any later version published by the Free Software
586 | Foundation. If the Program does not specify a version number of the
587 | GNU General Public License, you may choose any version ever published
588 | by the Free Software Foundation.
589 |
590 | If the Program specifies that a proxy can decide which future
591 | versions of the GNU General Public License can be used, that proxy's
592 | public statement of acceptance of a version permanently authorizes you
593 | to choose that version for the Program.
594 |
595 | Later license versions may give you additional or different
596 | permissions. However, no additional obligations are imposed on any
597 | author or copyright holder as a result of your choosing to follow a
598 | later version.
599 |
600 | 15. Disclaimer of Warranty.
601 |
602 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
603 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
604 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
605 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
606 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
607 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
608 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
609 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
610 |
611 | 16. Limitation of Liability.
612 |
613 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
614 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
615 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
616 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
617 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
618 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
619 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
620 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
621 | SUCH DAMAGES.
622 |
623 | 17. Interpretation of Sections 15 and 16.
624 |
625 | If the disclaimer of warranty and limitation of liability provided
626 | above cannot be given local legal effect according to their terms,
627 | reviewing courts shall apply local law that most closely approximates
628 | an absolute waiver of all civil liability in connection with the
629 | Program, unless a warranty or assumption of liability accompanies a
630 | copy of the Program in return for a fee.
631 |
632 | END OF TERMS AND CONDITIONS
633 |
634 | How to Apply These Terms to Your New Programs
635 |
636 | If you develop a new program, and you want it to be of the greatest
637 | possible use to the public, the best way to achieve this is to make it
638 | free software which everyone can redistribute and change under these terms.
639 |
640 | To do so, attach the following notices to the program. It is safest
641 | to attach them to the start of each source file to most effectively
642 | state the exclusion of warranty; and each file should have at least
643 | the "copyright" line and a pointer to where the full notice is found.
644 |
645 | {one line to give the program's name and a brief idea of what it does.}
646 | Copyright (C) {year} {name of author}
647 |
648 | This program is free software: you can redistribute it and/or modify
649 | it under the terms of the GNU General Public License as published by
650 | the Free Software Foundation, either version 3 of the License, or
651 | (at your option) any later version.
652 |
653 | This program is distributed in the hope that it will be useful,
654 | but WITHOUT ANY WARRANTY; without even the implied warranty of
655 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
656 | GNU General Public License for more details.
657 |
658 | You should have received a copy of the GNU General Public License
659 | along with this program. If not, see .
660 |
661 | Also add information on how to contact you by electronic and paper mail.
662 |
663 | If the program does terminal interaction, make it output a short
664 | notice like this when it starts in an interactive mode:
665 |
666 | {project} Copyright (C) {year} {fullname}
667 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
668 | This is free software, and you are welcome to redistribute it
669 | under certain conditions; type `show c' for details.
670 |
671 | The hypothetical commands `show w' and `show c' should show the appropriate
672 | parts of the General Public License. Of course, your program's commands
673 | might be different; for a GUI interface, you would use an "about box".
674 |
675 | You should also get your employer (if you work as a programmer) or school,
676 | if any, to sign a "copyright disclaimer" for the program, if necessary.
677 | For more information on this, and how to apply and follow the GNU GPL, see
678 | .
679 |
680 | The GNU General Public License does not permit incorporating your program
681 | into proprietary programs. If your program is a subroutine library, you
682 | may consider it more useful to permit linking proprietary applications with
683 | the library. If this is what you want to do, use the GNU Lesser General
684 | Public License instead of this License. But first, please read
685 | .
686 |
687 |
--------------------------------------------------------------------------------