Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

增加wechat channel “关键词自动接收好友申请”功能和“关键词自动邀请进群”插件 #1900

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bridge/reply.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class ReplyType(Enum):
TEXT_ = 11 # 强制文本
VIDEO = 12
MINIAPP = 13 # 小程序
ACCEPT_FRIEND = 19 # 接受好友申请

def __str__(self):
return self.name
Expand Down
17 changes: 16 additions & 1 deletion channel/chat_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ def __init__(self):
def _compose_context(self, ctype: ContextType, content, **kwargs):
context = Context(ctype, content)
context.kwargs = kwargs
if ctype == ContextType.ACCEPT_FRIEND:
return context
# context首次传入时,origin_ctype是None,
# 引入的起因是:当输入语音时,会嵌套生成两个context,第一步语音转文本,第二步通过文本生成文字回复。
# origin_ctype用于第二步文本回复时,判断是否需要匹配前缀,如果是私聊的语音,就不需要匹配前缀
Expand Down Expand Up @@ -223,6 +225,8 @@ def _generate_reply(self, context: Context, reply: Reply = Reply()) -> Reply:
"path": context.content,
"msg": context.get("msg")
}
elif context.type == ContextType.ACCEPT_FRIEND: # 好友申请,匹配字符串
reply = self._build_friend_request_reply(context)
elif context.type == ContextType.SHARING: # 分享信息,当前无默认逻辑
pass
elif context.type == ContextType.FUNCTION or context.type == ContextType.FILE: # 文件消息及函数调用等,当前无默认逻辑
Expand Down Expand Up @@ -264,6 +268,8 @@ def _decorate_reply(self, context: Context, reply: Reply) -> Reply:
reply.content = "[" + str(reply.type) + "]\n" + reply.content
elif reply.type == ReplyType.IMAGE_URL or reply.type == ReplyType.VOICE or reply.type == ReplyType.IMAGE or reply.type == ReplyType.FILE or reply.type == ReplyType.VIDEO or reply.type == ReplyType.VIDEO_URL:
pass
elif reply.type == ReplyType.ACCEPT_FRIEND:
pass
else:
logger.error("[WX] unknown reply type: {}".format(reply.type))
return
Expand Down Expand Up @@ -296,6 +302,15 @@ def _send(self, reply: Reply, context: Context, retry_cnt=0):
time.sleep(3 + 3 * retry_cnt)
self._send(reply, context, retry_cnt + 1)

# 处理好友申请
def _build_friend_request_reply(self, context):
logger.info("friend request content: {}".format(context.content["Content"]))
logger.info("accept_friend_commands list: {}".format(conf().get("accept_friend_commands", [])))
if context.content["Content"] in conf().get("accept_friend_commands", []):
return Reply(type=ReplyType.ACCEPT_FRIEND, content=True)
else:
return Reply(type=ReplyType.ACCEPT_FRIEND, content=False)

def _success_callback(self, session_id, **kwargs): # 线程正常结束时的回调函数
logger.debug("Worker return success, session_id = {}".format(session_id))

Expand All @@ -320,7 +335,7 @@ def func(worker: Future):
return func

def produce(self, context: Context):
session_id = context["session_id"]
session_id = context.get("session_id", 0)
with self.lock:
if session_id not in self.sessions:
self.sessions[session_id] = [
Expand Down
69 changes: 68 additions & 1 deletion channel/wechat/wechat_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@ def handler_group_msg(msg):
return None


# 自动接受加好友
@itchat.msg_register(FRIENDS)
def deal_with_friend(msg):
try:
cmsg = WechatMessage(msg, False)
except NotImplementedError as e:
logger.debug("[WX]friend request {} skipped: {}".format(msg["MsgId"], e))
return None
WechatChannel().handle_friend_request(cmsg)
return None


def _check(func):
def wrapper(self, cmsg: ChatMessage):
msgId = cmsg.msg_id
Expand Down Expand Up @@ -206,9 +218,20 @@ def handle_group(self, cmsg: ChatMessage):
if context:
self.produce(context)

@time_checker
@_check
def handle_friend_request(self, cmsg: ChatMessage):
if cmsg.ctype == ContextType.ACCEPT_FRIEND:
logger.debug("[WX]receive friend request: {}".format(cmsg.content["NickName"]))
else:
logger.debug("[WX]receive friend request: {}, cmsg={}".format(cmsg.content["NickName"], cmsg))
context = self._compose_context(cmsg.ctype, cmsg.content, msg=cmsg)
if context:
self.produce(context)

# 统一的发送函数,每个Channel自行实现,根据reply的type字段发送不同类型的消息
def send(self, reply: Reply, context: Context):
receiver = context["receiver"]
receiver = context.get("receiver")
if reply.type == ReplyType.TEXT:
itchat.send(reply.content, toUserName=receiver)
logger.info("[WX] sendMsg={}, receiver={}".format(reply, receiver))
Expand Down Expand Up @@ -257,6 +280,50 @@ def send(self, reply: Reply, context: Context):
video_storage.seek(0)
itchat.send_video(video_storage, toUserName=receiver)
logger.info("[WX] sendVideo url={}, receiver={}".format(video_url, receiver))
elif reply.type == ReplyType.ACCEPT_FRIEND: # 新增接受好友申请回复类型
# 假设 reply.content 包含了新好友的用户名
is_accept = reply.content
if is_accept:
try:
# 自动接受好友申请
debug_msg = itchat.accept_friend(userName=context.content["UserName"], v4=context.content["Ticket"])
logger.debug("[WX] accept_friend return: {}".format(debug_msg))
logger.info("[WX] Accepted new friend, UserName={}, NickName={}".format(context.content["UserName"], context.content["NickName"]))
except Exception as e:
logger.error("[WX] Failed to add friend. Error: {}".format(e))
else:
logger.info("[WX] Ignored new friend, username={}".format(context.content["NickName"]))
elif reply.type == ReplyType.INVITE_ROOM: # 新增邀请好友进群回复类型
# 假设 reply.content 包含了群聊的名字

def get_group_id(group_name):
"""
根据群聊名称获取群聊ID。
:param group_name: 群聊的名称。
:return: 群聊的ID (UserName)。
"""
group_list = itchat.search_chatrooms(name=group_name)
if group_list:
return group_list[0]["UserName"]
else:
return None

try:
chatroomUserName = reply.content
group_id = get_group_id(chatroomUserName)
logger.debug("[WX] find group_id={}, where chatroom={}".format(group_id, chatroomUserName))
if group_id is None:
raise ValueError("The specified group chat was not found: {}".format(chatroomUserName))
# 调用 itchat 的 add_member_into_chatroom 方法来添加成员
debug_msg = itchat.add_member_into_chatroom(group_id, receiver)
logger.debug("[WX] add_member_into_chatroom return: {}".format(debug_msg))
logger.info("[WX] invite members={}, to chatroom={}".format(receiver, chatroomUserName))
except ValueError as ve:
# 记录查找群聊失败的错误信息
logger.error("[WX] {}".format(ve))
except Exception as e:
# 记录添加成员失败的错误信息
logger.error("[WX] Failed to invite members to chatroom. Error: {}".format(e))

def _send_login_success():
try:
Expand Down
4 changes: 3 additions & 1 deletion channel/wechat/wechat_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ def __init__(self, itchat_msg, is_group=False):
elif itchat_msg["Type"] == SHARING:
self.ctype = ContextType.SHARING
self.content = itchat_msg.get("Url")

elif itchat_msg["Type"] == FRIENDS:
self.ctype = ContextType.ACCEPT_FRIEND
self.content = itchat_msg.get("RecommendInfo")
else:
raise NotImplementedError("Unsupported message type: Type:{} MsgType:{}".format(itchat_msg["Type"], itchat_msg["MsgType"]))

Expand Down
3 changes: 2 additions & 1 deletion config-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,6 @@
"subscribe_msg": "感谢您的关注!\n这里是AI智能助手,可以自由对话。\n支持语音对话。\n支持图片输入。\n支持图片输出,画字开头的消息将按要求创作图片。\n支持tool、角色扮演和文字冒险等丰富的插件。\n输入{trigger_prefix}#help 查看详细指令。",
"use_linkai": false,
"linkai_api_key": "",
"linkai_app_code": ""
"linkai_app_code": "",
"accept_friend_commands": ["加好友"]
}
2 changes: 2 additions & 0 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@
"linkai_api_key": "",
"linkai_app_code": "",
"linkai_api_base": "https://api.link-ai.tech", # linkAI服务地址
# 自动接受好友请求的申请信息
"accept_friend_commands": ["加好友"],
}


Expand Down
4 changes: 4 additions & 0 deletions plugins/source.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
"Blackroom": {
"url": "https://github.com/dividduang/blackroom.git",
"desc": "小黑屋插件,被拉进小黑屋的人将不能使用@bot的功能的插件"
},
"GroupInvitation": {
"url": "https://github.com/dfldylan/GroupInvitation.git",
"desc": "根据特定关键词自动邀请用户加入指定的群聊"
}
}
}