From 76614ae9e59582d2291958038676042d13164e97 Mon Sep 17 00:00:00 2001
From: zhayujie <yjzha1996@163.com>
Date: Fri, 5 Apr 2024 23:47:02 +0800
Subject: [PATCH 1/3] fix: remote config load bug

---
 channel/dingtalk/dingtalk_message.py | 10 ++---
 common/linkai_client.py              | 65 ++++++++++++++++++++++------
 config.py                            |  2 +
 plugins/linkai/linkai.py             | 24 +++++++---
 4 files changed, 75 insertions(+), 26 deletions(-)

diff --git a/channel/dingtalk/dingtalk_message.py b/channel/dingtalk/dingtalk_message.py
index ef9dc96..8a19cdc 100644
--- a/channel/dingtalk/dingtalk_message.py
+++ b/channel/dingtalk/dingtalk_message.py
@@ -32,13 +32,13 @@ class DingTalkMessage(ChatMessage):
             # 钉钉支持直接识别语音,所以此处将直接提取文字,当文字处理
             self.content = event.extensions['content']['recognition'].strip()
             self.ctype = ContextType.TEXT
-        self.from_user_id = event.sender_id
+        if self.is_group:
+            self.from_user_id = event.conversation_id
+            self.actual_user_id = event.sender_id
+        else:
+            self.from_user_id = event.sender_id
         self.to_user_id = event.chatbot_user_id
         self.other_user_nickname = event.conversation_title
         
         user_id = event.sender_id
         nickname =event.sender_nick
-
-        
-
-      
diff --git a/common/linkai_client.py b/common/linkai_client.py
index ad7d213..8112f19 100644
--- a/common/linkai_client.py
+++ b/common/linkai_client.py
@@ -2,12 +2,13 @@ from bridge.context import Context, ContextType
 from bridge.reply import Reply, ReplyType
 from common.log import logger
 from linkai import LinkAIClient, PushMsg
-from config import conf, pconf, plugin_config
+from config import conf, pconf, plugin_config, load_config
 from plugins import PluginManager
 
 
 chat_client: LinkAIClient
 
+
 class ChatClient(LinkAIClient):
     def __init__(self, api_key, host, channel):
         super().__init__(api_key, host)
@@ -27,29 +28,65 @@ class ChatClient(LinkAIClient):
     def on_config(self, config: dict):
         if not self.client_id:
             return
-        logger.info(f"从控制台加载配置: {config}")
+        logger.info(f"[LinkAI] 从客户端管理加载远程配置: {config}")
+        if config.get("enabled") != "Y":
+            return
+
         local_config = conf()
         for key in local_config.keys():
             if config.get(key) is not None:
                 local_config[key] = config.get(key)
-        if config.get("reply_voice_mode"):
-            if config.get("reply_voice_mode") == "voice_reply_voice":
+
+        # 语音配置
+        reply_voice_mode = config.get("reply_voice_mode")
+        if reply_voice_mode:
+            if reply_voice_mode == "voice_reply_voice":
                 local_config["voice_reply_voice"] = True
-            elif config.get("reply_voice_mode") == "always_reply_voice":
+            elif reply_voice_mode == "always_reply_voice":
                 local_config["always_reply_voice"] = True
-        # if config.get("admin_password") and plugin_config["Godcmd"]:
-        #     plugin_config["Godcmd"]["password"] = config.get("admin_password")
-        #     PluginManager().instances["Godcmd"].reload()
-        # if config.get("group_app_map") and pconf("linkai"):
-        #     local_group_map = {}
-        #     for mapping in config.get("group_app_map"):
-        #         local_group_map[mapping.get("group_name")] = mapping.get("app_code")
-        #     pconf("linkai")["group_app_map"] = local_group_map
-        #     PluginManager().instances["linkai"].reload()
+
+        if config.get("admin_password") and plugin_config["Godcmd"]:
+            plugin_config["Godcmd"]["password"] = config.get("admin_password")
+            PluginManager().instances["GODCMD"].reload()
+
+        if config.get("group_app_map") and pconf("linkai"):
+            local_group_map = {}
+            for mapping in config.get("group_app_map"):
+                local_group_map[mapping.get("group_name")] = mapping.get("app_code")
+            pconf("linkai")["group_app_map"] = local_group_map
+            PluginManager().instances["LINKAI"].reload()
 
 
 def start(channel):
     global chat_client
     chat_client = ChatClient(api_key=conf().get("linkai_api_key"),
                         host="link-ai.chat", channel=channel)
+    chat_client.config = _build_config()
     chat_client.start()
+
+
+def _build_config():
+    local_conf = conf()
+    config = {
+        "linkai_app_code": local_conf.get("linkai_app_code"),
+        "single_chat_prefix": local_conf.get("single_chat_prefix"),
+        "single_chat_reply_prefix": local_conf.get("single_chat_reply_prefix"),
+        "single_chat_reply_suffix": local_conf.get("single_chat_reply_suffix"),
+        "group_chat_prefix": local_conf.get("group_chat_prefix"),
+        "group_chat_reply_prefix": local_conf.get("group_chat_reply_prefix"),
+        "group_chat_reply_suffix": local_conf.get("group_chat_reply_suffix"),
+        "group_name_white_list": local_conf.get("group_name_white_list"),
+        "nick_name_black_list": local_conf.get("nick_name_black_list"),
+        "speech_recognition": "Y" if local_conf.get("speech_recognition") else "N",
+        "text_to_image": local_conf.get("text_to_image"),
+        "image_create_prefix": local_conf.get("image_create_prefix")
+    }
+    if local_conf.get("always_reply_voice"):
+        config["reply_voice_mode"] = "always_reply_voice"
+    elif local_conf.get("voice_reply_voice"):
+        config["reply_voice_mode"] = "voice_reply_voice"
+    if pconf("linkai"):
+        config["group_app_map"] = pconf("linkai").get("group_app_map")
+    if plugin_config.get("Godcmd"):
+        config["admin_password"] = plugin_config.get("Godcmd").get("password")
+    return config
diff --git a/config.py b/config.py
index 4bfcf26..71f79d0 100644
--- a/config.py
+++ b/config.py
@@ -258,6 +258,8 @@ def load_config():
     config.load_user_datas()
 
 
+
+
 def get_root():
     return os.path.dirname(os.path.abspath(__file__))
 
diff --git a/plugins/linkai/linkai.py b/plugins/linkai/linkai.py
index 7978743..2411fb7 100644
--- a/plugins/linkai/linkai.py
+++ b/plugins/linkai/linkai.py
@@ -10,6 +10,7 @@ from common import const
 import os
 from .utils import Util
 
+
 @plugins.register(
     name="linkai",
     desc="A plugin that supports knowledge base and midjourney drawing.",
@@ -32,7 +33,6 @@ class LinkAI(Plugin):
             self.sum_config = self.config.get("summary")
         logger.info(f"[LinkAI] inited, config={self.config}")
 
-
     def on_handle_context(self, e_context: EventContext):
         """
         消息处理逻辑
@@ -42,7 +42,8 @@ class LinkAI(Plugin):
             return
 
         context = e_context['context']
-        if context.type not in [ContextType.TEXT, ContextType.IMAGE, ContextType.IMAGE_CREATE, ContextType.FILE, ContextType.SHARING]:
+        if context.type not in [ContextType.TEXT, ContextType.IMAGE, ContextType.IMAGE_CREATE, ContextType.FILE,
+                                ContextType.SHARING]:
             # filter content no need solve
             return
 
@@ -76,7 +77,8 @@ class LinkAI(Plugin):
             if not res:
                 _set_reply_text("因为神秘力量无法获取文章内容,请稍后再试吧~", e_context, level=ReplyType.TEXT)
                 return
-            _set_reply_text(res.get("summary") + "\n\n💬 发送 \"开启对话\" 可以开启与文章内容的对话", e_context, level=ReplyType.TEXT)
+            _set_reply_text(res.get("summary") + "\n\n💬 发送 \"开启对话\" 可以开启与文章内容的对话", e_context,
+                            level=ReplyType.TEXT)
             USER_FILE_MAP[_find_user_id(context) + "-sum_id"] = res.get("summary_id")
             return
 
@@ -99,7 +101,8 @@ class LinkAI(Plugin):
                 _set_reply_text("开启对话失败,请稍后再试吧", e_context)
                 return
             USER_FILE_MAP[_find_user_id(context) + "-file_id"] = res.get("file_id")
-            _set_reply_text("💡你可以问我关于这篇文章的任何问题,例如:\n\n" + res.get("questions") + "\n\n发送 \"退出对话\" 可以关闭与文章的对话", e_context, level=ReplyType.TEXT)
+            _set_reply_text("💡你可以问我关于这篇文章的任何问题,例如:\n\n" + res.get(
+                "questions") + "\n\n发送 \"退出对话\" 可以关闭与文章的对话", e_context, level=ReplyType.TEXT)
             return
 
         if context.type == ContextType.TEXT and context.content == "退出对话" and _find_file_id(context):
@@ -117,12 +120,10 @@ class LinkAI(Plugin):
             e_context.action = EventAction.BREAK_PASS
             return
 
-
         if self._is_chat_task(e_context):
             # 文本对话任务处理
             self._process_chat_task(e_context)
 
-
     # 插件管理功能
     def _process_admin_cmd(self, e_context: EventContext):
         context = e_context['context']
@@ -177,7 +178,9 @@ class LinkAI(Plugin):
                 tips_text = "关闭"
                 is_open = False
             if not self.sum_config:
-                _set_reply_text(f"插件未启用summary功能,请参考以下链添加插件配置\n\nhttps://github.com/zhayujie/chatgpt-on-wechat/blob/master/plugins/linkai/README.md", e_context, level=ReplyType.INFO)
+                _set_reply_text(
+                    f"插件未启用summary功能,请参考以下链添加插件配置\n\nhttps://github.com/zhayujie/chatgpt-on-wechat/blob/master/plugins/linkai/README.md",
+                    e_context, level=ReplyType.INFO)
             else:
                 self.sum_config["enabled"] = is_open
                 _set_reply_text(f"文章总结功能{tips_text}", e_context, level=ReplyType.INFO)
@@ -254,6 +257,9 @@ class LinkAI(Plugin):
         except Exception as e:
             logger.exception(e)
 
+    def reload(self):
+        self.config = super().load_config()
+
 
 def _send_info(e_context: EventContext, content: str):
     reply = Reply(ReplyType.TEXT, content)
@@ -273,15 +279,19 @@ def _set_reply_text(content: str, e_context: EventContext, level: ReplyType = Re
     e_context["reply"] = reply
     e_context.action = EventAction.BREAK_PASS
 
+
 def _get_trigger_prefix():
     return conf().get("plugin_trigger_prefix", "$")
 
+
 def _find_sum_id(context):
     return USER_FILE_MAP.get(_find_user_id(context) + "-sum_id")
 
+
 def _find_file_id(context):
     user_id = _find_user_id(context)
     if user_id:
         return USER_FILE_MAP.get(user_id + "-file_id")
 
+
 USER_FILE_MAP = ExpiredDict(conf().get("expires_in_seconds") or 60 * 30)

From 1da596639f9316f971627b99c46704e3d6950526 Mon Sep 17 00:00:00 2001
From: zhayujie <yjzha1996@163.com>
Date: Sat, 6 Apr 2024 00:19:22 +0800
Subject: [PATCH 2/3] feat: update sdk version

---
 requirements-optional.txt | 3 ---
 requirements.txt          | 3 ++-
 2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/requirements-optional.txt b/requirements-optional.txt
index e076558..eed22c0 100644
--- a/requirements-optional.txt
+++ b/requirements-optional.txt
@@ -35,9 +35,6 @@ broadscope_bailian
 # google
 google-generativeai
 
-# linkai
-linkai>=0.0.3.5
-
 # dingtalk
 dingtalk_stream
 
diff --git a/requirements.txt b/requirements.txt
index 495917b..aed76c3 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -6,4 +6,5 @@ requests>=2.28.2
 chardet>=5.1.0
 Pillow
 pre-commit
-web.py
\ No newline at end of file
+web.py
+linkai>=0.0.3.7

From 66a014150b9f98d99689145da445298bee6b1777 Mon Sep 17 00:00:00 2001
From: zhayujie <yjzha1996@163.com>
Date: Sat, 6 Apr 2024 01:03:26 +0800
Subject: [PATCH 3/3] fix: config update bug

---
 channel/chat_channel.py          | 1 +
 channel/wechat/wechat_channel.py | 2 +-
 common/linkai_client.py          | 7 +++----
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/channel/chat_channel.py b/channel/chat_channel.py
index 907cea2..689fa0a 100644
--- a/channel/chat_channel.py
+++ b/channel/chat_channel.py
@@ -75,6 +75,7 @@ class ChatChannel(Channel):
                     ):
                         session_id = group_id
                 else:
+                    logger.debug(f"No need reply, groupName not in whitelist, group_name={group_name}")
                     return None
                 context["session_id"] = session_id
                 context["receiver"] = group_id
diff --git a/channel/wechat/wechat_channel.py b/channel/wechat/wechat_channel.py
index a65269c..5cab1fb 100644
--- a/channel/wechat/wechat_channel.py
+++ b/channel/wechat/wechat_channel.py
@@ -96,7 +96,7 @@ def qrCallback(uuid, status, qrcode):
         print(qr_api4)
         print(qr_api2)
         print(qr_api1)
-        _send_qr_code([qr_api1, qr_api2, qr_api3, qr_api4])
+        _send_qr_code([qr_api3, qr_api4, qr_api2, qr_api1])
         qr = qrcode.QRCode(border=1)
         qr.add_data(url)
         qr.make(fit=True)
diff --git a/common/linkai_client.py b/common/linkai_client.py
index 8112f19..071be0a 100644
--- a/common/linkai_client.py
+++ b/common/linkai_client.py
@@ -2,7 +2,7 @@ from bridge.context import Context, ContextType
 from bridge.reply import Reply, ReplyType
 from common.log import logger
 from linkai import LinkAIClient, PushMsg
-from config import conf, pconf, plugin_config, load_config
+from config import conf, pconf, plugin_config, available_setting
 from plugins import PluginManager
 
 
@@ -33,10 +33,9 @@ class ChatClient(LinkAIClient):
             return
 
         local_config = conf()
-        for key in local_config.keys():
-            if config.get(key) is not None:
+        for key in config.keys():
+            if key in available_setting and config.get(key) is not None:
                 local_config[key] = config.get(key)
-
         # 语音配置
         reply_voice_mode = config.get("reply_voice_mode")
         if reply_voice_mode: