Use wcferry
This commit is contained in:
@@ -16,7 +16,7 @@ class Config(object):
|
||||
with open(f"{pwd}/config.yaml", "rb") as fp:
|
||||
yconfig = yaml.safe_load(fp)
|
||||
except FileNotFoundError:
|
||||
with open(f"{pwd}/../config.yaml.template", "rb") as fp:
|
||||
with open(f"{pwd}/config.yaml.template", "rb") as fp:
|
||||
yconfig = yaml.safe_load(fp)
|
||||
with open(f"{pwd}/config.yaml", "w+") as yf:
|
||||
yaml.dump(yconfig, yf, default_flow_style=False)
|
||||
113
main.py
113
main.py
@@ -1,103 +1,10 @@
|
||||
#! /usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import re
|
||||
import signal
|
||||
from wcferry import Wcf
|
||||
|
||||
from func_chengyu import cy
|
||||
import robot.sdk.wcferry as WxSDK
|
||||
from robot.base_robot import BaseRobot
|
||||
from robot.configuration import Config
|
||||
|
||||
|
||||
class Robot(BaseRobot):
|
||||
"""个性化自己的机器人
|
||||
"""
|
||||
|
||||
def __init__(self, sdk: WxSDK, config: Config) -> None:
|
||||
super().__init__(sdk)
|
||||
self.config = config
|
||||
|
||||
def toAt(self, msg) -> bool:
|
||||
"""
|
||||
处理被 @ 消息,现在只固定回复: "你@我干嘛?"
|
||||
:param msg: 微信消息结构
|
||||
:return: 处理状态,`True` 成功,`False` 失败
|
||||
"""
|
||||
status = True
|
||||
rsp = "你@我干嘛?"
|
||||
self.sendTextMsg(msg.roomId, rsp, msg.wxId)
|
||||
|
||||
return status
|
||||
|
||||
def toChengyu(self, msg) -> bool:
|
||||
"""
|
||||
处理成语查询/接龙消息
|
||||
:param msg: 微信消息结构
|
||||
:return: 处理状态,`True` 成功,`False` 失败
|
||||
"""
|
||||
status = False
|
||||
texts = re.findall(r"^([#|?|?])(.*)$", msg.content)
|
||||
# [('#', '天天向上')]
|
||||
if texts:
|
||||
flag = texts[0][0]
|
||||
text = texts[0][1]
|
||||
if flag == "#": # 接龙
|
||||
if cy.isChengyu(text):
|
||||
rsp = cy.getNext(text)
|
||||
if rsp:
|
||||
self.sendTextMsg(msg.roomId, rsp)
|
||||
status = True
|
||||
elif flag in ["?", "?"]: # 查词
|
||||
if cy.isChengyu(text):
|
||||
rsp = cy.getMeaning(text)
|
||||
if rsp:
|
||||
self.sendTextMsg(msg.roomId, rsp)
|
||||
status = True
|
||||
|
||||
return status
|
||||
|
||||
def toChitchat(self, msg):
|
||||
"""闲聊,目前未实现
|
||||
"""
|
||||
pass
|
||||
|
||||
def processMsg(self, msg) -> None:
|
||||
"""当接收到消息的时候,会调用本方法。如果不实现本方法,则打印原始消息。
|
||||
"""
|
||||
|
||||
self.printRawMsg(msg) # 打印信息
|
||||
|
||||
# 群聊消息
|
||||
if self.isGroupChat(msg):
|
||||
# 如果在群里被 @,回复发信人:“收到你的消息了!” 并 @他
|
||||
if msg.roomId not in self.config.GROUPS: # 不在配置的响应的群列表里,忽略
|
||||
return
|
||||
|
||||
if self.isAt(msg): # 被@
|
||||
self.toAt(msg)
|
||||
|
||||
else: # 其他消息
|
||||
self.toChengyu(msg)
|
||||
|
||||
# 非群聊信息
|
||||
elif msg.type == 37: # 好友请求
|
||||
self.autoAcceptFriendRequest(msg)
|
||||
|
||||
elif msg.type == 10000: # 系统信息
|
||||
nickName = re.findall(r"你已添加了(.*),现在可以开始聊天了。", msg.content)
|
||||
if nickName:
|
||||
# 添加了好友,更新好友列表
|
||||
self.allContacts[msg.wxId] = nickName
|
||||
|
||||
elif msg.type == 0x01: # 文本消息
|
||||
# 让配置加载更灵活,自己可以更新配置。也可以利用定时任务更新。
|
||||
if msg.self and msg.content == "^更新$":
|
||||
self.config.reload()
|
||||
self.LOG.info("已更新")
|
||||
return
|
||||
|
||||
# 闲聊
|
||||
self.toChitchat(msg)
|
||||
from robot import Robot
|
||||
|
||||
|
||||
def weather_report(robot: Robot):
|
||||
@@ -110,14 +17,20 @@ def weather_report(robot: Robot):
|
||||
report = "这就是获取到的天气情况了"
|
||||
|
||||
for r in receivers:
|
||||
robot.sendTextMsg(r, report)
|
||||
robot.sendTextMsg(report, r)
|
||||
|
||||
|
||||
def main():
|
||||
robot = Robot(WxSDK, Config())
|
||||
wcf = Wcf()
|
||||
|
||||
# 初始化机器人
|
||||
robot.initSDK()
|
||||
def handler(sig, frame):
|
||||
wcf.cleanup() # 退出前清理环境
|
||||
exit(0)
|
||||
|
||||
signal.signal(signal.SIGINT, handler)
|
||||
|
||||
robot = Robot(wcf)
|
||||
robot.LOG.info("机器人已启动")
|
||||
|
||||
# 接收消息
|
||||
robot.enableRecvMsg()
|
||||
|
||||
157
robot.py
Normal file
157
robot.py
Normal file
@@ -0,0 +1,157 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import re
|
||||
import time
|
||||
import logging
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from wcferry import Wcf
|
||||
|
||||
from configuration import Config
|
||||
from func_chengyu import cy
|
||||
from job_mgmt import Job
|
||||
|
||||
|
||||
class Robot(Job):
|
||||
"""个性化自己的机器人
|
||||
"""
|
||||
|
||||
def __init__(self, wcf: Wcf) -> None:
|
||||
self.wcf = wcf
|
||||
self.config = Config()
|
||||
self.LOG = logging.getLogger("Robot")
|
||||
self.wxid = self.wcf.get_self_wxid()
|
||||
self.allContacts = self.getAllContacts()
|
||||
|
||||
def toAt(self, msg: Wcf.WxMsg) -> bool:
|
||||
"""
|
||||
处理被 @ 消息,现在只固定回复: "你@我干嘛?"
|
||||
:param msg: 微信消息结构
|
||||
:return: 处理状态,`True` 成功,`False` 失败
|
||||
"""
|
||||
status = True
|
||||
rsp = "你@我干嘛?"
|
||||
self.sendTextMsg(rsp, msg.roomid, msg.sender)
|
||||
|
||||
return status
|
||||
|
||||
def toChengyu(self, msg: Wcf.WxMsg) -> bool:
|
||||
"""
|
||||
处理成语查询/接龙消息
|
||||
:param msg: 微信消息结构
|
||||
:return: 处理状态,`True` 成功,`False` 失败
|
||||
"""
|
||||
status = False
|
||||
texts = re.findall(r"^([#|?|?])(.*)$", msg.content)
|
||||
# [('#', '天天向上')]
|
||||
if texts:
|
||||
flag = texts[0][0]
|
||||
text = texts[0][1]
|
||||
if flag == "#": # 接龙
|
||||
if cy.isChengyu(text):
|
||||
rsp = cy.getNext(text)
|
||||
if rsp:
|
||||
self.sendTextMsg(rsp, msg.roomid)
|
||||
status = True
|
||||
elif flag in ["?", "?"]: # 查词
|
||||
if cy.isChengyu(text):
|
||||
rsp = cy.getMeaning(text)
|
||||
if rsp:
|
||||
self.sendTextMsg(rsp, msg.roomid)
|
||||
status = True
|
||||
|
||||
return status
|
||||
|
||||
def toChitchat(self, msg: Wcf.WxMsg):
|
||||
"""闲聊,目前未实现
|
||||
"""
|
||||
pass
|
||||
|
||||
def processMsg(self, msg: Wcf.WxMsg) -> None:
|
||||
"""当接收到消息的时候,会调用本方法。如果不实现本方法,则打印原始消息。
|
||||
"""
|
||||
# 群聊消息
|
||||
if msg.from_group():
|
||||
# 如果在群里被 @,回复发信人:“收到你的消息了!” 并 @他
|
||||
if msg.roomid not in self.config.GROUPS: # 不在配置的响应的群列表里,忽略
|
||||
return
|
||||
|
||||
if msg.is_at(self.wxid): # 被@
|
||||
self.toAt(msg)
|
||||
|
||||
else: # 其他消息
|
||||
self.toChengyu(msg)
|
||||
|
||||
# 非群聊信息
|
||||
elif msg.type == 37: # 好友请求
|
||||
self.autoAcceptFriendRequest(msg)
|
||||
|
||||
elif msg.type == 10000: # 系统信息
|
||||
self.sayHiToNewFriend(msg)
|
||||
|
||||
elif msg.type == 0x01: # 文本消息
|
||||
# 让配置加载更灵活,自己可以更新配置。也可以利用定时任务更新。
|
||||
if msg.from_self() and msg.content == "^更新$":
|
||||
self.config.reload()
|
||||
self.LOG.info("已更新")
|
||||
return
|
||||
|
||||
# 闲聊
|
||||
self.toChitchat(msg)
|
||||
|
||||
def onMsg(self, msg) -> int:
|
||||
self.LOG.info(msg) # 打印信息
|
||||
try:
|
||||
self.processMsg(msg)
|
||||
except Exception as e:
|
||||
self.LOG.error(e)
|
||||
|
||||
return 0
|
||||
|
||||
def enableRecvMsg(self) -> None:
|
||||
self.wcf.enable_recv_msg(self.onMsg)
|
||||
|
||||
def sendTextMsg(self, msg, receiver, at_list=""):
|
||||
# msg 中需要有 @ 名单中一样数量的 @
|
||||
ats = ""
|
||||
if at_list:
|
||||
wxids = at_list.split(",")
|
||||
for wxid in wxids:
|
||||
# 这里偷个懒,直接 @昵称。有必要的话可以通过 MicroMsg.db 里的 ChatRoom 表,解析群昵称
|
||||
ats = f" @{self.allContacts.get(wxid, '')}"
|
||||
|
||||
self.LOG.info(f"To {receiver}: {msg}{ats}")
|
||||
self.wcf.send_text(f"{msg}{ats}", receiver, at_list)
|
||||
|
||||
def getAllContacts(self):
|
||||
"""
|
||||
获取联系人(包括好友、公众号、服务号、群成员……)
|
||||
格式: {"wxid": "NickName"}
|
||||
"""
|
||||
contacts = self.wcf.query_sql("MicroMsg.db", "SELECT UserName, NickName FROM Contact;")
|
||||
return {contact["UserName"]: contact["NickName"] for contact in contacts}
|
||||
|
||||
def keepRunningAndBlockProcess(self) -> None:
|
||||
"""
|
||||
保持机器人运行,不让进程退出
|
||||
"""
|
||||
while True:
|
||||
self.runPendingJobs()
|
||||
time.sleep(1)
|
||||
|
||||
def autoAcceptFriendRequest(self, msg):
|
||||
try:
|
||||
xml = ET.fromstring(msg.content)
|
||||
v3 = xml.attrib["encryptusername"]
|
||||
v4 = xml.attrib["ticket"]
|
||||
self.wcf.accept_new_friend(v3, v4)
|
||||
|
||||
except Exception as e:
|
||||
self.LOG.error(f"同意好友出错:{e}")
|
||||
|
||||
def sayHiToNewFriend(self, msg):
|
||||
nickName = re.findall(r"你已添加了(.*),现在可以开始聊天了。", msg.content)
|
||||
if nickName:
|
||||
# 添加了好友,更新好友列表
|
||||
self.allContacts[msg.sender] = nickName[0]
|
||||
self.sendTextMsg(f"Hi {nickName[0]},我自动通过了你的好友请求。", msg.sender)
|
||||
@@ -1,103 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import re
|
||||
import time
|
||||
import logging
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from robot.job_mgmt import Job
|
||||
|
||||
|
||||
class BaseRobot(Job):
|
||||
"""
|
||||
机器人基类。用户需要实现 `processMsg` 方法以个性化处理消息。
|
||||
"""
|
||||
|
||||
def __init__(self, sdk) -> None:
|
||||
self.sdk = sdk
|
||||
self.LOG = logging.getLogger("Robot")
|
||||
|
||||
def onMsg(self, msg) -> int:
|
||||
try:
|
||||
self.processMsg(msg)
|
||||
except Exception as e:
|
||||
self.LOG.error(e)
|
||||
self.printRawMsg(msg)
|
||||
|
||||
return 0
|
||||
|
||||
def initSDK(self):
|
||||
if self.sdk.WxInitSDK() != 0:
|
||||
self.LOG.error("初始化失败")
|
||||
exit(-1)
|
||||
self.LOG.info("初始化成功")
|
||||
self.wxid = self.sdk.WxGetSelfWxid()
|
||||
self.allContacts = self.getAllContacts()
|
||||
|
||||
def enableRecvMsg(self):
|
||||
self.sdk.WxEnableRecvMsg(self.onMsg)
|
||||
|
||||
def sendTextMsg(self, receiver, msg, at_list=""):
|
||||
# msg 中需要有 @ 名单中一样数量的 @
|
||||
ats = ""
|
||||
if at_list:
|
||||
wxids = at_list.split(",")
|
||||
for wxid in wxids:
|
||||
# 这里偷个懒,直接 @昵称。有必要的话可以通过 MicroMsg.db 里的 ChatRoom 表,解析群昵称
|
||||
ats = f" @{self.allContacts.get(wxid, '')}"
|
||||
|
||||
self.LOG.info(f"To {receiver}: {msg}{ats}")
|
||||
self.sdk.WxSendTextMsg(receiver, f"{msg}{ats}", at_list)
|
||||
|
||||
def isGroupChat(self, msg):
|
||||
return msg.source == 1
|
||||
|
||||
def isAt(self, msg, exclude_at_all=True):
|
||||
atall = []
|
||||
atuserlist = re.findall(f"<atuserlist>.*({self.wxid}).*</atuserlist>", msg.xml)
|
||||
if exclude_at_all:
|
||||
atall = re.findall(f"@所有人", msg.content) # 排除@所有人
|
||||
|
||||
return (len(atuserlist) > 0) and (len(atall) == 0)
|
||||
|
||||
def printRawMsg(self, msg) -> None:
|
||||
rmsg = {}
|
||||
rmsg["id"] = msg.id
|
||||
rmsg["self"] = msg.self
|
||||
rmsg["wxId"] = msg.wxId
|
||||
rmsg["roomId"] = msg.roomId
|
||||
rmsg["type"] = msg.type
|
||||
rmsg["source"] = msg.source
|
||||
rmsg["xml"] = msg.xml
|
||||
rmsg["content"] = msg.content
|
||||
|
||||
self.LOG.info(rmsg)
|
||||
|
||||
def getAllContacts(self):
|
||||
"""
|
||||
获取联系人(包括好友、公众号、服务号、群成员……)
|
||||
格式: {"wxid": "NickName"}
|
||||
"""
|
||||
contacts = self.sdk.WxExecDbQuery("MicroMsg.db", "SELECT UserName, NickName FROM Contact;")
|
||||
return {contact["UserName"]: contact["NickName"] for contact in contacts}
|
||||
|
||||
def autoAcceptFriendRequest(self, msg):
|
||||
try:
|
||||
xml = ET.fromstring(msg.content)
|
||||
v3 = xml.attrib["encryptusername"]
|
||||
v4 = xml.attrib["ticket"]
|
||||
self.sdk.WxAcceptNewFriend(v3, v4)
|
||||
|
||||
except Exception as e:
|
||||
self.LOG.error(f"同意好友出错:{e}")
|
||||
|
||||
def processMsg(self, msg) -> None:
|
||||
raise NotImplementedError("Method [processMsg] should be implemented.")
|
||||
|
||||
def keepRunningAndBlockProcess(self) -> None:
|
||||
"""
|
||||
保持机器人运行,不让进程退出
|
||||
"""
|
||||
while True:
|
||||
self.runPendingJobs()
|
||||
time.sleep(1)
|
||||
Binary file not shown.
@@ -1,84 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
import time
|
||||
import wcferry as sdk
|
||||
|
||||
|
||||
def main():
|
||||
help(sdk) # 查看SDK支持的方法和属性
|
||||
|
||||
# 初始化SDK,如果成功,返回0;否则失败
|
||||
status = sdk.WxInitSDK()
|
||||
if status != 0:
|
||||
print("初始化失败")
|
||||
exit(-1)
|
||||
|
||||
print("初始化成功")
|
||||
WxMsgTypes = sdk.WxGetMsgTypes() # 获取消息类型
|
||||
print(WxMsgTypes) # 查看消息类型
|
||||
|
||||
time.sleep(2)
|
||||
print("打印通讯录......")
|
||||
contacts = sdk.WxGetContacts()
|
||||
for k, v in contacts.items():
|
||||
print(k, v.wxCode, v.wxName, v.wxCountry, v.wxProvince, v.wxCity, v.wxGender)
|
||||
|
||||
time.sleep(2)
|
||||
print("发送文本消息......")
|
||||
sdk.WxSendTextMsg("filehelper", "message from WeChatFerry...") # 往文件传输助手发消息
|
||||
# sdk.WxSendTextMsg("xxxx@chatroom", "message from WeChatFerry...") # 往群里发消息(需要改成正确的 ID,下同)
|
||||
# sdk.WxSendTextMsg("xxxx@chatroom", "message from WeChatFerry... @ ", "wxid_xxxxxxxxxxxx") # 往群里发消息,@某人
|
||||
# sdk.WxSendTextMsg("xxxx@chatroom", "message from WeChatFerry... @ ", "notify@all") # 往群里发消息,@所有人
|
||||
|
||||
time.sleep(2)
|
||||
print("发送图片消息......")
|
||||
sdk.WxSendImageMsg("filehelper", "test.jpg")
|
||||
|
||||
dbs = sdk.WxGetDbNames()
|
||||
for db in dbs:
|
||||
print(db)
|
||||
|
||||
tables = sdk.WxGetDbTables(dbs[0])
|
||||
for t in tables:
|
||||
print(f"{t.table}\n{t.sql}\n\n")
|
||||
|
||||
# 接收消息。先定义消息处理回调
|
||||
def OnTextMsg(msg: sdk.WxMessage):
|
||||
def getName(id):
|
||||
contact = contacts.get(id)
|
||||
if contact is None:
|
||||
return id
|
||||
return contact.wxName
|
||||
|
||||
s = "收到"
|
||||
if msg.self == 1: # 忽略自己发的消息
|
||||
s += f"来自自己的消息"
|
||||
print(f"\n{s}")
|
||||
return 0
|
||||
|
||||
msgType = WxMsgTypes.get(msg.type, '未知类型')
|
||||
nickName = getName(msg.wxId)
|
||||
if msg.source == 1:
|
||||
groupName = getName(msg.roomId)
|
||||
s += f"来自群[{groupName}]的[{nickName}]的{msgType}消息:"
|
||||
else:
|
||||
s += f"来自[{nickName}]的{msgType}消息:"
|
||||
|
||||
s += f"\r\n{msg.content}"
|
||||
if msg.type != 0x01:
|
||||
s += f"\r\n{msg.xml}"
|
||||
|
||||
print(f"\n{s}")
|
||||
|
||||
return 0
|
||||
|
||||
print("Message: 接收通知中......")
|
||||
sdk.WxEnableRecvMsg(OnTextMsg) # 设置回调,接收消息
|
||||
|
||||
while True:
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 399 KiB |
Binary file not shown.
Reference in New Issue
Block a user