diff --git a/.gitignore b/.gitignore index 02baa67..1cead96 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,5 @@ plugins/banwords/lib/__pycache__ !plugins/jina_sum !plugins/jina_sum/**/ client_config.json +# qrcode +wx_qrcode.png \ No newline at end of file diff --git a/channel/wechat/wechat_channel.py b/channel/wechat/wechat_channel.py index 473168b..b1c43c2 100644 --- a/channel/wechat/wechat_channel.py +++ b/channel/wechat/wechat_channel.py @@ -106,7 +106,16 @@ 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]) + qrcodes = [qr_api2, qr_api1, qr_api3, qr_api4] + for item in qrcodes: + try: + response = requests.get(item) + response.raise_for_status() + with open("wx_qrcode.png", "wb") as f: + f.write(response.content) + break + except Exception as e: + logger.exception(f"[WX_QRCODE]: failed to download qrcode: {e}") qr = qrcode.QRCode(border=1) qr.add_data(url) qr.make(fit=True) @@ -146,20 +155,16 @@ class WechatChannel(ChatChannel): def exitCallback(self): try: - from common.linkai_client import chat_client - if chat_client.client_id and conf().get("use_linkai"): - _send_logout() - time.sleep(2) - self.auto_login_times += 1 - if self.auto_login_times < 100: - chat_channel.handler_pool._shutdown = False - self.startup() + time.sleep(2) + self.auto_login_times += 1 + if self.auto_login_times < 100: + chat_channel.handler_pool._shutdown = False + self.startup() except Exception as e: pass def loginCallback(self): logger.debug("Login success") - _send_login_success() # handle_* 系列函数处理收到的消息后构造Context,然后传入produce函数中处理Context和发送回复 # Context包含了消息的所有信息,包括以下属性 diff --git a/config.py b/config.py index 59f889c..2436c4f 100644 --- a/config.py +++ b/config.py @@ -10,6 +10,10 @@ from common.log import logger # 将所有可用的配置项写在字典里, 请使用小写字母 # 此处的配置值无实际意义,程序不会读取此处的配置,仅用于提示格式,请将配置加入到config.json中 available_setting = { + # webui配置 + "web_ui_port": 7860, + "web_ui_username": "dow", + "web_ui_password": "dify-on-wechat", # openai api配置 "open_ai_api_key": "", # openai api key # openai apibase,当use_azure_chatgpt为true时,需要设置对应的api base diff --git a/requirements-optional.txt b/requirements-optional.txt index 74f1780..f517ca4 100644 --- a/requirements-optional.txt +++ b/requirements-optional.txt @@ -40,3 +40,7 @@ dingtalk_stream # zhipuai zhipuai>=2.0.1 + +# webui +gradio==4.37.2 +gradio_client==1.0.2 diff --git a/web_ui.py b/web_ui.py new file mode 100644 index 0000000..63e40ee --- /dev/null +++ b/web_ui.py @@ -0,0 +1,130 @@ +import os +from multiprocessing import Process +import signal +import time + +import gradio as gr + +from channel import channel_factory +from common import const +from config import load_config, conf +from plugins import * + +current_process_instance = None + +def start_channel(channel_name: str): + channel = channel_factory.create_channel(channel_name) + available_channels = [ + "wx", + "terminal", + "wechatmp", + "wechatmp_service", + "wechatcom_app", + "wework", + "wechatcom_service", + const.FEISHU, + const.DINGTALK + ] + if channel_name in available_channels: + PluginManager().load_plugins() + channel.startup() + +def run(): + try: + # load config + load_config() + # create channel + channel_name = conf().get("channel_type", "wx") + start_channel(channel_name) + except Exception as e: + logger.error("App startup failed!") + logger.exception(e) + +def start_run(): + global current_process_instance + + if current_process_instance is not None and current_process_instance.is_alive(): + os.kill(current_process_instance.pid, signal.SIGTERM) # 杀掉当前进程 + current_process_instance.join() # 等待当前进程结束 + + current_process_instance = Process(target=run) + current_process_instance.start() + time.sleep(10) # 等待进程启动 + return f"重启成功!!" + +def get_qrcode_image(): + image_path = 'wx_qrcode.png' + if os.path.exists(image_path): + return image_path + else: + return None + +def verify_login(username, password): + correct_username = conf().get("web_ui_username", "dow") + correct_password = conf().get("web_ui_password", "dify-on-wechat") + if username == correct_username and password == correct_password: + return True + return False + +def login(username, password): + if verify_login(username, password): + return ( + gr.update(visible=False), + gr.update(visible=True), + gr.update(visible=True), + gr.update(visible=True), + gr.update(visible=True), + gr.update(visible=False), # Hide username input + gr.update(visible=False), # Hide password input + gr.update(visible=False) # Hide login button + ) + else: + return ( + "用户名或密码错误", + gr.update(visible=False), + gr.update(visible=False), + gr.update(visible=False), + gr.update(visible=False), + gr.update(visible=True), # Show username input + gr.update(visible=True), # Show password input + gr.update(visible=True) # Show login button + ) + +with gr.Blocks() as demo: + username_input = gr.Textbox(label="用户名") + password_input = gr.Textbox(label="密码", type="password") + login_button = gr.Button("登录") + login_status = gr.Textbox(label="登录状态", value="", interactive=False) + + qrcode_image = gr.Image(value=get_qrcode_image(), label="微信二维码", width=400, height=400, visible=False) + restart_status = gr.Textbox(label="状态", value="启动成功", visible=False) + + with gr.Row(): + restart_button = gr.Button("异常退出后请点击此按钮重启", visible=False) + refresh_button = gr.Button("登录前请点击此按钮刷新二维码", visible=False) # 添加手动刷新的按钮 + + login_button.click( + login, + inputs=[username_input, password_input], + outputs=[ + login_status, + qrcode_image, + restart_button, + refresh_button, + restart_status, + username_input, + password_input, + login_button + ] + ) + + restart_button.click(start_run, outputs=restart_status) + + def refresh_image(): + return get_qrcode_image() + refresh_button.click(refresh_image, outputs=qrcode_image) # 手动刷新按钮的点击事件 + +if __name__ == "__main__": + start_run() + load_config() + demo.launch(server_name="0.0.0.0", server_port=conf().get("web_ui_port", 7860))