From 365593f2c6c9f77e6be15dc8c2b2b75fb6427e96 Mon Sep 17 00:00:00 2001 From: pandolia Date: Tue, 13 Jun 2017 00:04:05 +0000 Subject: [PATCH] v2.3.2 --- README.MD | 18 +++++++++--- changes | 10 ++++--- plugins-in-dev/qqbotdefault/a.py | 35 ---------------------- plugins-in-dev/qqbotdefault/b.py | 36 ----------------------- plugins-in-dev/qqbotdefault/c/__init__.py | 2 -- qqbot/exitcode.py | 5 ---- qqbot/plugins/sampleslots.py | 30 +++++++++---------- qqbot/qconf.py | 2 +- qqbot/qqbotcls.py | 29 ++++++++++++++---- setup.py | 2 +- 10 files changed, 61 insertions(+), 108 deletions(-) delete mode 100644 plugins-in-dev/qqbotdefault/a.py delete mode 100644 plugins-in-dev/qqbotdefault/b.py delete mode 100644 plugins-in-dev/qqbotdefault/c/__init__.py delete mode 100644 qqbot/exitcode.py diff --git a/README.MD b/README.MD index 444e808..342ae3e 100644 --- a/README.MD +++ b/README.MD @@ -178,7 +178,7 @@ send 命令中第三个参数和 list 命令中的第三个参数格式一致。 以上所有命令都提供对应的 HTTP API 接口,供 web 前端开发者调用,接口的 url 地址为 http://127.0.0.1:8188/{command} ,只需要将 qq 后面的命令各参数用 "/" 分隔开替换 url 中的 command 就可以了,如: http://127.0.0.1:8188/send/buddy/jack/hello ,其他示例详见 [urltestbot.md](https://github.com/pandolia/qqbot/blob/master/urltestbot.html) 。注意:如果命令中含有中文或特殊字符,需要先进行 url 编码( utf8 ),例如,调用 http://127.0.0.1:8188/send/buddy/jack/nihao%20%E4%BD%A0%E5%A5%BD%20wohao 将发送消息 ”nihao 你好 wohao“ 。(提示:在 JavaScript 中,可以使用 encodeURIComponent 函数进行编码)。 -另外,QQBot 启动后,用另外一个 QQ 向本 QQ 发送 “--version” ,则 QQBot 会自动回复: “QQBot-v2.x.x” 。 +另外, QQBot 启动后,用本 QQ 号在其他客户端(如:手机 QQ )上向某个 群/讨论组 发消息 “--version” ,则 QQBot 会自动在该 群/讨论组 回复: “QQBot-v2.x.x” 。 四、实现你自己的 QQ 机器人 --------------------------- @@ -333,12 +333,12 @@ bot.conf 中保存全局的配置信息,各项配置详见本文档第七节 注意: bot.conf 中保存的配置信息是只读的,请勿修改这些配置信息。 -六、 注册回调函数、被他人 @ 的通知、定制定时任务 --------------------------------------------------- +六、 注册回调函数、被他人 @ 的通知、判断是否是自己发的消息、定制定时任务 +------------------------------------------------------------- #### 注册回调函数 -除了上面提到的 onQQMessage 响应函数,还可以注册 onInit/onQrcode/onUpdate/onPlug/onUnplug/onExpire/onInterval/onStartupComplete 共计九种事件的回调函数,所有事件的回调函数参数格式、含义及示例详见 [sampleslots.py](https://github.com/pandolia/qqbot/blob/master/qqbot/plugins/sampleslots.py) 。 +除了上面提到的 onQQMessage 响应函数,还可以注册 onInit/onQrcode/onStartupComplete/onInterval/onUpdate/onPlug/onUnplug/onExit 共计九种事件的回调函数,所有事件的回调函数参数格式、含义及示例详见 [sampleslots.py](https://github.com/pandolia/qqbot/blob/master/qqbot/plugins/sampleslots.py) 。 程序的运行流程以及各回调函数的调用时机如下: @@ -356,6 +356,16 @@ bot.conf 中保存全局的配置信息,各项配置详见本文档第七节 请注意,若群内有另一个成员的名字和自己的名字的开头部分相同(如:自己的名字是 ab ,另一个成员的名字是 abc ),那么当有人 @abc 时,也会误报成 @ME ,在这种情况下,需要修改自己的群名片,以免误报。 +#### 判断是否是自己发的消息 + +当本 QQ 在群内或讨论组内发言时, QQBot 也会收到一条同样的消息,此时 onMessage 中的 contact 参数就是该 群/讨论组 对象, member 参数就是自己在该 群/讨论组 中的成员对象,此时 member.uin 就是本次登录的 QQ 号码,因此,在 onQQMessage 中,只要判断 member.uin 是否是本次登录的 QQ 号码就可以知道是否是自己的发的消息了,例如: + + from qqbot.utf8logger import INFO + + def onQQMessage(bot, contact, member, content): + if getattr(member, 'uin', None) == bot.conf.qq: + INFO('你在 %s 内发言', contact) + #### 定制定时任务 从 2.1.13 起, qqbot 中提供一个功能强大的函数装饰器 -- qqbotsched 来定制定时任务,示例代码: diff --git a/changes b/changes index d8e01b5..3d7b904 100644 --- a/changes +++ b/changes @@ -1,12 +1,14 @@ -2017-06-12 qqbot v2.3.2 +2017-06-13 qqbot v2.3.2 -1) 修改文档中 qqbotsched 的 day_of_week 参数说明, 0 代表周一, 6 代表周日。 +1) 原 onExpire 扩充为 onExit 函数,在 QQBot 停止、重启、登录过期、未知错误时被调用。原 onExpire 被删除。 -2) 增加功能: QQBot-term 服务器端口号设置为 0 时,将不会开启此服务器。 +2) 修改默认插件 sampleslots 中的 onQQMessage 函数,该函数仅回复自己在群内发的 “--version” 。 -3) 修正 “因协议更改引起的部分表情被解析为 /惊讶 " 的错误。 +3) 修改文档中 qqbotsched 的 day_of_week 参数说明, 0 代表周一, 6 代表周日。 +4) 增加功能: QQBot-term 服务器端口号设置为 0 时,将不会开启此服务器。 +5) 修正 “因协议更改引起的部分表情被解析为 /惊讶 " 的错误。 2017-06-11 qqbot v2.3.1 diff --git a/plugins-in-dev/qqbotdefault/a.py b/plugins-in-dev/qqbotdefault/a.py deleted file mode 100644 index 525639c..0000000 --- a/plugins-in-dev/qqbotdefault/a.py +++ /dev/null @@ -1,35 +0,0 @@ -# -*- coding: utf-8 -*- - -from qqbot.utf8logger import DEBUG -from qqbot import QQBotSched as qqbotsched - -def onInit(bot): - DEBUG('%s.ON-INIT', __name__) - -def onQrcode(bot, pngPath, pngContent): - DEBUG('%s.ON-QRCODE: %s (%d bytes)', __name__, pngPath, len(pngContent)) - -def onQQMessage(bot, contact, member, content): - DEBUG('%s.ON-QQ-MESSAGE: %s, %s, %s', __name__, contact, member, content) - -def onInterval(bot): - DEBUG('%s.ON-INTERVAL', __name__) - -def onStartupComplete(bot): - DEBUG('%s.ON-START-UP-COMPLETE', __name__) - -def onUpdate(bot, tinfo): - DEBUG('%s.ON-UPDATE: %s', __name__, tinfo) - -def onPlug(bot): - DEBUG('%s.ON-PLUG', __name__) - -def onUnplug(bot): - DEBUG('%s.ON-UNPLUG', __name__) - -def onExpire(bot): - DEBUG('%s.ON-EXPIRE', __name__) - -@qqbotsched(minute='0-59') -def schedTask(bot): - DEBUG('%s.SCHED-TASK', __name__) diff --git a/plugins-in-dev/qqbotdefault/b.py b/plugins-in-dev/qqbotdefault/b.py deleted file mode 100644 index 5ce5295..0000000 --- a/plugins-in-dev/qqbotdefault/b.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- coding: utf-8 -*- -''' -from qqbot.utf8logger import DEBUG -from qqbot import QQBotSched as qqbotsched - -def onInit(bot): - 1/0 - -def onQrcode(bot, pngPath, pngContent): - 1/0 - -def onQQMessage(bot, contact, member, content): - 1/0 - -def onInterval(bot): - 1/0 - -def onStartupComplete(bot): - 1/0 - -def onUpdate(bot, tinfo): - 1/0 - -def onPlug(bot): - 1/0 - -def onUnplug(bot): - 1/0 - -def onExpire(bot): - 1/0 - -@qqbotsched(minute='0-59') -def schedTask(bot): - 1/0 -''' \ No newline at end of file diff --git a/plugins-in-dev/qqbotdefault/c/__init__.py b/plugins-in-dev/qqbotdefault/c/__init__.py deleted file mode 100644 index 8dec292..0000000 --- a/plugins-in-dev/qqbotdefault/c/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -def onPlug(bot): - pass \ No newline at end of file diff --git a/qqbot/exitcode.py b/qqbot/exitcode.py deleted file mode 100644 index 167b86c..0000000 --- a/qqbot/exitcode.py +++ /dev/null @@ -1,5 +0,0 @@ -# -*- coding: utf-8 -*- - -RESTART = 201 -FRESH_RESTART = 202 -POLL_ERROR = 203 diff --git a/qqbot/plugins/sampleslots.py b/qqbot/plugins/sampleslots.py index 44a9c8f..0bb2b56 100644 --- a/qqbot/plugins/sampleslots.py +++ b/qqbot/plugins/sampleslots.py @@ -10,7 +10,7 @@ def onInit(bot): # 注意 : 此时 bot 尚未启动,因此请勿在本函数中调用 bot.List/SendTo/GroupXXX/Stop/Restart 等接口 # 只可以访问配置信息 bot.conf # bot : QQBot 对象 - DEBUG('ON-INIT : qqbot.plugins.sampleslots') + DEBUG('%s.onInit', __name__) def onQrcode(bot, pngPath, pngContent): # 获取到二维码时被调用 @@ -19,7 +19,7 @@ def onQrcode(bot, pngPath, pngContent): # bot : QQBot 对象 # pngPath : 二维码图片路径 # pngContent : 二维码图片内容 - DEBUG('ON-QRCODE: %s (%d bytes)', pngPath, len(pngContent)) + DEBUG('%s.onQrcode: %s (%d bytes)', __name__, pngPath, len(pngContent)) def onQQMessage(bot, contact, member, content): # 当收到 QQ 消息时被调用 @@ -27,39 +27,39 @@ def onQQMessage(bot, contact, member, content): # contact : QContact 对象,消息的发送者 # member : QContact 对象,仅当本消息为 群或讨论组 消息时有效,代表实际发消息的成员 # content : str 对象,消息内容 - if content == '--version': + if content == '--version' and getattr(member, 'uin') == bot.conf.qq: bot.SendTo(contact, 'QQbot-' + bot.conf.version) def onInterval(bot): # 每隔 5 分钟被调用 # bot : QQBot 对象,提供 List/SendTo/GroupXXX/Stop/Restart 等接口,详见文档第五节 - DEBUG('INTERVAL') + DEBUG('%s.onInterval', __name__) def onStartupComplete(bot): # 启动完成时被调用 # bot : QQBot 对象,提供 List/SendTo/GroupXXX/Stop/Restart 等接口,详见文档第五节 - DEBUG('START-UP-COMPLETE') + DEBUG('%s.onStartupComplete', __name__) def onUpdate(bot, tinfo): # 某个联系人列表更新时被调用 # bot : QQBot 对象,提供 List/SendTo/GroupXXX/Stop/Restart 等接口,详见文档第五节 # tinfo : 联系人列表的代号,详见文档中关于 bot.List 的第一个参数的含义解释 - DEBUG('ON-UPDATE: %s', tinfo) + DEBUG('%s.onUpdate: %s', __name__, tinfo) def onPlug(bot): # 本插件被加载时被调用,提供 List/SendTo/GroupXXX/Stop/Restart 等接口,详见文档第五节 # 提醒:如果本插件设置为启动时自动加载,则本函数将延迟到登录完成后被调用 # bot : QQBot 对象 - DEBUG('ON-PLUG : qqbot.plugins.sampleslots') + DEBUG('%s.onPlug', __name__) def onUnplug(bot): # 本插件被卸载时被调用 # bot : QQBot 对象,提供 List/SendTo/GroupXXX/Stop/Restart 等接口,详见文档第五节 - DEBUG('ON-UNPLUG : qqbot.plugins.sampleslots') + DEBUG('%s.onUnplug', __name__) def onExit(bot, code, reason, error): - # 主循环(MainLoop)终止时被调用, Mainloop 是一个无限循环,QQBot 登录成功后便开始运 - # 行,当且仅当以下事件发生时,Mainloop 终止: + # MainLoop(主循环)终止时被调用, Mainloop 是一个无限循环,QQBot 登录成功后便开始运 + # 行,当且仅当以下事件发生时 Mainloop 终止: # 1) 调用了 bot.Stop() ,此时: # code = 0, reason = 'stop', error = None # 2) 调用了 bot.Restart() ,此时: @@ -75,12 +75,12 @@ def onExit(bot, code, reason, error): # # 一般情况下: # 发生 1/2/3/4 时,可以安全的调用 bot.List/SendTo/GroupXXX 等接口 - # 发生 5/6 时,调用 bot.List/SendTo/GroupXXX 等接口 将出错 + # 发生 5/6 时,调用 bot.List/SendTo/GroupXXX 等接口将出错 # - # 一般情况下,用户自定义插件内的代码错误会被捕捉并忽略,不会引起 MainLoop 的退出 + # 一般情况下,用户插件内的代码和运行错误会被捕捉并忽略,不会引起 MainLoop 的退出 # - # 本函数被调用后,会执行 sys.exit(code) 退出本次进程并返回到父进程,父进程会根据“ code - # 的数值” 以及 “是否配置为自动重启模式” 来决定是否重启 QQBot 。 + # 本函数被调用后,会执行 sys.exit(code) 退出本次进程并返回到父进程,父进程会根据 + # “ code 的数值” 以及 “是否配置为自动重启模式” 来决定是否重启 QQBot 。 # - DEBUG('ON-EXPIRE') + DEBUG('%s.onExit: %r %r %r', __name__, code, reason, error) diff --git a/qqbot/qconf.py b/qqbot/qconf.py index 569cf20..99fec0e 100644 --- a/qqbot/qconf.py +++ b/qqbot/qconf.py @@ -5,7 +5,7 @@ if p not in sys.path: sys.path.insert(0, p) -version = 'v2.3.1' +version = 'v2.3.2' sampleConfStr = ''' { diff --git a/qqbot/qqbotcls.py b/qqbot/qqbotcls.py index 1d57255..1b69b9f 100644 --- a/qqbot/qqbotcls.py +++ b/qqbot/qqbotcls.py @@ -19,13 +19,23 @@ from qqbot.qconf import QConf from qqbot.utf8logger import INFO, CRITICAL, ERROR, WARN from qqbot.qsession import QLogin, RequestError -from qqbot.exitcode import RESTART, POLL_ERROR, FRESH_RESTART from qqbot.common import StartDaemonThread, Import from qqbot.qterm import QTermServer from qqbot.mainloop import MainLoop, Put from qqbot.groupmanager import GroupManager from qqbot.termbot import TermBot +RESTART = 201 +FRESH_RESTART = 202 +LOGIN_EXPIRE = 203 + +codeInfo = { + 0: 'stop', 201: 'restart', 202: 'fresh-restart', 203: 'login-expire' +} + +def getReason(code): + return codeInfo.get(code, 'system-exit') + def runBot(argv): if sys.argv[-1] == '--subprocessCall': sys.argv.pop() @@ -128,7 +138,17 @@ def Run(self): self.scheduler.start() self.started = True - MainLoop() + + try: + MainLoop() + except SystemExit as e: + self.onExit(e.code, getReason(e.code), None) + raise + except Exception as e: + ERROR('', exc_info=True) + ERROR('Mainloop 发生未知错误:%r', e) + self.onExit(1, 'unknown-error', e) + raise SystemExit(1) def Stop(self): sys.exit(0) @@ -145,8 +165,7 @@ def pollForever(self): try: result = self.poll() except RequestError: - Put(self.onExpire) - Put(sys.exit, POLL_ERROR) + Put(sys.exit, LOGIN_EXPIRE) break except: ERROR('qsession.Poll 方法出错', exc_info=True) @@ -198,7 +217,7 @@ def __init__(self): 'onUpdate': [], 'onPlug': [], 'onUnplug': [], - 'onExpire': [], + 'onExit': [], } self.started = False self.plugins = {} diff --git a/setup.py b/setup.py index 2a57525..9e95370 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup -version = 'v2.3.1' +version = 'v2.3.2' setup( name = 'qqbot',