Skip to content
This repository has been archived by the owner on Aug 23, 2020. It is now read-only.

Commit

Permalink
去掉qterm客户端,去掉后台fetchforever
Browse files Browse the repository at this point in the history
  • Loading branch information
pandolia committed Feb 28, 2017
1 parent 37c43d8 commit 0dba09e
Show file tree
Hide file tree
Showing 9 changed files with 51 additions and 116 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
v2.0.8
---------

# 去掉 qterm 客户端功能,去掉登陆成功后自动弹出 qterm 客户端,只保留 qq 命令行工具来操作机器人。
# 去掉 “在后台获取联系人资料” 的功能,联系人资料只在登陆时获取一次。之后就不再获取,如果需要更新联系人资料,则需要重新手动登陆一次。
32 changes: 13 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,43 +26,35 @@ QQBot 是一个用 python 实现的、基于腾讯 SmartQQ 协议的简单 QQ

在命令行输入: **qqbot** 。启动过程中会自动弹出二维码图片,需要用手机 QQ 客户端扫码并授权登录。启动成功后,会将本次登录信息保存到本地文件中,下次启动时,可以输入: **qqbot -q qq号码** ,先尝试从本地文件中恢复登录信息(不需要手动扫码),只有恢复不成功或登录信息已过期时才会需要手动扫码登录。一般来说,保存的登录信息将在 2 ~ 3 天之后过期。

注意: Linux 下,需要系统中有 gvfs-open 或者 shotwell 命令才能自动弹出二维码图片(一般系统中安装有 GNOME 虚拟文件系统 gvfs 的系统中都会含这两个命令之一)。若系统无法自动弹出二维码图片,可以手动打开图片文件进行扫码,也可以将二维码显示模式设置为邮箱模式或服务器模式进行远程扫码,详见本文档的第六节。
注意: Linux 下,需要系统中有 gvfs-open 或者 shotwell 命令才能自动弹出二维码图片(一般安装有 GNOME 虚拟文件系统 gvfs 的系统中都会含这两个命令之一)。

若系统无法自动弹出二维码图片,可以手动打开图片文件进行扫码,也可以将二维码显示模式设置为邮箱模式或服务器模式进行远程扫码,详见本文档的第六节。

##### 2. 操作 QQBot

QQBot 启动后,会自动弹出一个控制台窗口( qterm 客户端)用来输入操作 QQBot 的命令,目前提供以下命令:
QQBot 启动后,在另一个控制台窗口使用 qq 命令来操作 QQBot ,目前提供以下命令:

1) 帮助
help
qq help

2) 列出所有 好友/群/讨论组
list buddy|group|discuss
qq list buddy|group|discuss

3) 向 好友/群/讨论组 发送消息
send buddy|group|discuss x|uin=x|qq=x|name=x|nick=x|mark=x message
qq send buddy|group|discuss x|uin=x|qq=x|name=x|nick=x|mark=x message

4) 获取 好友/群/讨论组 的信息
get buddy|group|discuss x|uin=x|qq=x|name=x|nick=x|mark=x
qq get buddy|group|discuss x|uin=x|qq=x|name=x|nick=x|mark=x

5) 获取 群/讨论组 的成员
member group|discuss x|uin=x|qq=x|name=x|mark=x
qq member group|discuss x|uin=x|qq=x|name=x|mark=x

6) 停止 QQBot
stop
qq stop

在 send/get/member 命令中,第三个参数可以是 好友/群/讨论组 的 名称/昵称/备注名/qq/uin 。

如果系统中没有图形界面,则不会自动弹出控制台窗口,需要手动在另外的控制台中输入 “qterm [port]” 命令来打开 qterm 客户端。

也可以在另一个控制台用 qq 命令操作 QQBot ,如:

$ qq send buddy jack hello
$ qq send buddy uin=37489877 nihao
$ qq member group chatbot
$ qq list buddy
$ qq stop

QQBot 启动后,用另外一个 QQ 向本 QQ 发送 “qqbot --version” ,则 QQBot 会自动回复: “QQBot-v2.x.x” 。(注:2.0.3之前的版本中,通过 QQ 消息发送以上 6 个命令也可以操作 QQBot , 2.0.4 版之后,为安全起见,去掉此功能,只保留一个 “qqbot --version” 的命令用来远程测试 QQBot 是否正常运行)。
QQBot 启动后,用另外一个 QQ 向本 QQ 发送 “--version” ,则 QQBot 会自动回复: “QQBot-v2.x.x” 。(注:2.0.3之前的版本中,通过 QQ 消息发送以上 6 个命令也可以操作 QQBot , 2.0.4 版之后,为安全起见,去掉此功能,只保留一个 “--version” 的命令用来远程测试 QQBot 是否正常运行)。


四、实现你自己的 QQ 机器人
Expand Down Expand Up @@ -182,6 +174,8 @@ SmartQQ 登录时需要用手机 QQ 扫描二维码图片,在 QQBot 中,二

GUI 模式是默认的模式,只适用于个人电脑。邮箱模式可以适用于个人电脑和远程服务器。服务器模式一般只在有公网ip的系统中使用。最方便的是使用 QQ 邮箱的邮箱模式,当发送二维码图片后,手机 QQ 客户端一般会立即收到通知,在手机 QQ 客户端上打开邮件,再长按二维码就可以扫描了。

注意:当开启了邮箱模式或服务器模式时, GUI 模式是关闭的,登陆时不会自动弹出二维码图片。

每次登录时会创建一个二维码管理器 (QrcodeManager 对象) ,二维码管理器会根据配置文件及命令行参数来选择二维码图片的显示方式。

配置文件为 **~/.qqbot-tmp/v2.x.conf** ,第一次运行 QQBot 后就会自动创建这个配置文件,其中内容如下:
Expand Down
2 changes: 2 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from qqbot import Main; Main()
2 changes: 1 addition & 1 deletion qqbot/qconf.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-

version = 'v2.0.7'
version = 'v2.0.8'

sampleConfStr = '''{
Expand Down
38 changes: 9 additions & 29 deletions qqbot/qqbot.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
import random, time, sys, subprocess

from qconf import QConf
from utf8logger import INFO, WARN, DEBUG
from qsession import QLogin, QSession
from utf8logger import INFO
from qsession import QLogin
from qterm import QTermServer
from common import Utf8Partition
from qcontacts import QContact
Expand All @@ -28,13 +28,10 @@ def __init__(self, qq=None, user=None, conf=None, ai=None):
self.On('qqmessage', ai.OnQQMessage) # main thread
self.On('polltimeout', ai.OnPollTimeout) # main thread
self.On('termmessage', ai.OnTermMessage) # main thread

self.On('pollcomplete', QQBot.onPollComplete) # main thread
self.On('fetchcomplete', QQBot.onFetchComplete) # main thread
self.On('pollcomplete', QQBot.onPollComplete) # main thread

self.AddGenerator(self.pollForever) # child thread 1
self.AddGenerator(self.fetchForever) # child thread 2
self.AddGenerator(termServer.Run) # child thread 3
self.AddGenerator(termServer.Run) # child thread 2

def Login(self):
session, contacts = QLogin(conf=self.conf)
Expand All @@ -45,7 +42,6 @@ def Login(self):
self.send = session.Send # main thread

self.poll = session.Copy().Poll # child thread 1
self.fetch = session.Copy().Fetch # child thread 2

# send buddy|group|discuss x|uin=x|qq=x|name=x content
# Send('buddy', '1234', 'hello')
Expand Down Expand Up @@ -85,18 +81,6 @@ def pollForever(self):
yield Message('pollcomplete', result=self.poll())
finally:
yield Message('stop', code=1)

def fetchForever(self):
INFO('已在后台运行 fetchForever 方法,每隔 5 分钟获取一次联系人资料')
while True:
time.sleep(300)
try:
contacts = self.fetch()
except (QSession.Error, Exception):
WARN(' fetchForever 方法出错')
DEBUG('', exc_info=True)
else:
yield Message('fetchcomplete', contacts=contacts)

def onPollComplete(self, message):
ctype, fromUin, memberUin, content = message.result
Expand All @@ -122,9 +106,6 @@ def onPollComplete(self, message):
contact, memberUin, memberName, content, self.SendTo
))

def onFetchComplete(self, message):
self.assignContacts(message.contacts)

def onStop(self, code):
if code == 0:
INFO('QQBot 正常停止')
Expand Down Expand Up @@ -161,18 +142,16 @@ def __init__(self):
self.termUsage = '欢迎使用 QQBot ,使用方法:'
self.qqUsage = self.termUsage
for doc in self.docs:
self.termUsage += '\n ' + doc[2:]
self.termUsage += '\n qq ' + doc[2:]
self.qqUsage += '\n -' + doc[2:]
self.termUsage += '\n quit'

def OnPollTimeout(self, bot, msg):
pass

def OnQQMessage(self, bot, msg):
if msg.contact.ctype == 'buddy' and msg.content == 'qqbot --version':
if msg.content == '--version':
msg.Reply('QQbot-' + bot.conf.version)

# 去掉通过 qq 消息来操作 QQBot 的方式
# if msg.content.strip().startswith('-'):
# msg.content = msg.content.strip()[1:]
# msg.Reply(self.execute(bot, msg))
Expand Down Expand Up @@ -237,12 +216,13 @@ def Main():
bot.Login()
sys.exit(bot.Run())
else:
args = ['python', __file__] + sys.argv[1:] + \
args = ['python'] + sys.argv + \
['--mailAuthCode', conf.mailAuthCode, '--subprocessCall']
while subprocess.call(args) != 0:
INFO('重新启动 QQBot ')
except KeyboardInterrupt:
sys.exit(0)

if __name__ == '__main__':
Main()
from utf8logger import PRINT
PRINT('请运行 python ../main.py')
11 changes: 6 additions & 5 deletions qqbot/qrcodemanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,12 @@ def __init__(self, conf):
def Show(self, qrcode):
with open(self.qrcodePath, 'wb') as f:
f.write(qrcode)

try:
showImage(self.qrcodePath)
except Exception as e:
WARN('无法弹出二维码图片 file://%s 。%s', self.qrcodePath, e)

if self.qrcodeServer is None and self.mailAgent is None:
try:
showImage(self.qrcodePath)
except Exception as e:
WARN('无法弹出二维码图片 file://%s 。%s', self.qrcodePath, e)

if self.qrcodeServer:
INFO('请使用浏览器访问二维码,图片地址: %s', self.qrcodeURL)
Expand Down
1 change: 0 additions & 1 deletion qqbot/qsession.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ def prepareSession(self):
self.clientid = 53999199
self.msgId = 6000000
self.session = requests.Session()
# self.session.verify
self.session.headers.update({
'User-Agent': ('Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9;'
' rv:27.0) Gecko/20100101 Firefox/27.0'),
Expand Down
74 changes: 14 additions & 60 deletions qqbot/qterm.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,7 @@

import sys, socket, time

try:
import readline
except ImportError:
pass

from common import CallInNewConsole
from utf8logger import INFO, WARN, RAWINPUT, PRINT
from utf8logger import INFO, WARN, PRINT
from messagefactory import MessageFactory, Message

HOST, DEFPORT = '127.0.0.1', 8188
Expand All @@ -20,17 +14,17 @@ def __init__(self, port):
def Run(self):
try:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sock.bind((HOST, self.port))
self.sock.listen(5)
except socket.error as e:
WARN('无法开启 QQBot term 服务器。%s', e)
WARN(' qq 命令无法使用')
else:
time.sleep(0.1)
INFO('已在 %s 端口开启 QQBot-Term 服务器', self.port)
if CallInNewConsole(['python', __file__, str(self.port)]) != 0:
WARN('无法自动打开新控制台运行 QTerm 客户端,'
'请手动打开新控制台并运行 qterm %s 命令', self.port)

INFO('已在 %s 端口开启 QQBot-Term 服务器,', self.port)
INFO('请在其他控制台窗口使用 qq 命令来控制 QQBot ,'
'示例: qq send buddy jack hello')
while True:
try:
sock, addr = self.sock.accept()
Expand Down Expand Up @@ -71,50 +65,11 @@ def __init__(self, name, sock, content):
def Reply(self, rep):
try:
self.sock.sendall(rep and str(rep) or '\r\n')
# INFO('已向 %s 回复消息', self.name)
except socket.error:
WARN('回复 %s 失败', self.name)
finally:
self.sock.close()

def qterm(port):
req = 'help'
while req != 'quit':
if req:
resp = query(port, req)
if not resp:
RAWINPUT('与 QQBot term 服务器的连接已断开,按回车键退出')
break
if resp == 'QQBot已停止':
RAWINPUT('QQBot已停止,按回车键退出')
break
resp = resp.strip()
while True:
front, resp = partition(resp)
if resp:
RAWINPUT(front+'--More--')
else:
resp = front
break
else:
resp = ''

if resp:
req = RAWINPUT(resp+'\nqterm>> ').strip()
else:
req = RAWINPUT('qterm>> ').strip()

def partition(s):
n = len(s)
if n <= 800:
return s, ''
else:
for i in range(800, min(n, 900)):
if s[i] == '\n':
i += 1
break
return s[:i], s[i:]

def query(port, req):
resp = ''
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Expand All @@ -124,32 +79,31 @@ def query(port, req):
while True:
data = sock.recv(8096)
if not data:
return resp
return resp.strip()
resp += data
except socket.error:
return resp
return resp.strip()
finally:
sock.close()

def QTerm():
# python qterm.py -s
# python qterm.py [PORT] [COMMAND]
try:
# python qterm.py -s
# python qterm.py [PORT] [COMMAND]
if len(sys.argv) == 2 and sys.argv[1] == '-s':
QTermServer(DEFPORT).Test()
else:
if len(sys.argv) >= 2 and sys.argv[1].isdigit():
port = int(sys.argv[1])
command = ' '.join(sys.argv[2:])
command = ' '.join(sys.argv[2:]).strip()
else:
port = DEFPORT
command = ' '.join(sys.argv[1:])
command = ' '.join(sys.argv[1:]).strip()

if not command:
qterm(port)
else:
if command:
coding = sys.getfilesystemencoding()
PRINT(query(port, command.decode(coding).encode('utf8')))

except KeyboardInterrupt:
pass

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from setuptools import setup

version = '2.0.7'
version = '2.0.8'

setup(
name = 'qqbot',
Expand Down

0 comments on commit 0dba09e

Please sign in to comment.