初始提交:识流 AI 助手项目
微信自动回复机器人,基于截图+OCR识别消息,支持关键词规则和 AI(OpenAI/DeepSeek/Dify)自动回复。 技术栈:PySide6 + Flask + Vue3 + RapidOCR + SQLite 注:OCR大模型文件(.onnx / .pdiparams)不纳入版本控制,需单独下载。 🤖 Generated with [Qoder][https://qoder.com]
This commit is contained in:
134
app/application/bot_controller.py
Normal file
134
app/application/bot_controller.py
Normal file
@@ -0,0 +1,134 @@
|
||||
import os
|
||||
import threading
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
|
||||
from app.infrastructure.wechat_multi_chat_bot import WechatMultiChatBot
|
||||
|
||||
|
||||
class BotController:
|
||||
def __init__(self):
|
||||
self._lock = threading.RLock()
|
||||
self._thread = None
|
||||
self._bot = None
|
||||
self._status = "stopped"
|
||||
self._last_error = ""
|
||||
self._started_at = ""
|
||||
self._stopped_at = ""
|
||||
self._listeners = {}
|
||||
self._listener_seq = 0
|
||||
|
||||
def start(self, backend_url):
|
||||
with self._lock:
|
||||
if self._thread and self._thread.is_alive():
|
||||
return self._status_payload_locked()
|
||||
self._status = "starting"
|
||||
self._last_error = ""
|
||||
self._stopped_at = ""
|
||||
os.environ["BACKEND_URL"] = backend_url
|
||||
self._thread = threading.Thread(target=self._run, daemon=True, name="WechatBotThread")
|
||||
self._thread.start()
|
||||
status = self._status_payload_locked()
|
||||
listeners = list(self._listeners.values())
|
||||
self._notify_status_listeners(listeners, status)
|
||||
return status
|
||||
|
||||
def stop(self):
|
||||
with self._lock:
|
||||
if not self._thread or not self._thread.is_alive():
|
||||
self._status = "stopped"
|
||||
self._bot = None
|
||||
self._stopped_at = self._now()
|
||||
status = self._status_payload_locked()
|
||||
listeners = list(self._listeners.values())
|
||||
self._notify_status_listeners(listeners, status)
|
||||
return status
|
||||
self._status = "stopping"
|
||||
bot = self._bot
|
||||
status = self._status_payload_locked()
|
||||
listeners = list(self._listeners.values())
|
||||
self._notify_status_listeners(listeners, status)
|
||||
if bot:
|
||||
bot.stop()
|
||||
return status
|
||||
|
||||
def status(self):
|
||||
listeners = None
|
||||
with self._lock:
|
||||
if self._status in {"running", "starting", "stopping"} and self._thread and not self._thread.is_alive():
|
||||
if self._status != "error":
|
||||
self._status = "stopped"
|
||||
self._bot = None
|
||||
self._stopped_at = self._stopped_at or self._now()
|
||||
listeners = list(self._listeners.values())
|
||||
status = self._status_payload_locked()
|
||||
if listeners is not None:
|
||||
self._notify_status_listeners(listeners, status)
|
||||
return status
|
||||
|
||||
def add_status_listener(self, callback, emit_initial=False):
|
||||
with self._lock:
|
||||
self._listener_seq += 1
|
||||
listener_id = self._listener_seq
|
||||
self._listeners[listener_id] = callback
|
||||
status = self._status_payload_locked()
|
||||
if emit_initial:
|
||||
try:
|
||||
callback(status)
|
||||
except Exception:
|
||||
pass
|
||||
return listener_id
|
||||
|
||||
def remove_status_listener(self, listener_id):
|
||||
with self._lock:
|
||||
self._listeners.pop(listener_id, None)
|
||||
|
||||
def _run(self):
|
||||
try:
|
||||
bot = WechatMultiChatBot()
|
||||
with self._lock:
|
||||
self._bot = bot
|
||||
self._status = "running"
|
||||
self._started_at = self._now()
|
||||
status = self._status_payload_locked()
|
||||
listeners = list(self._listeners.values())
|
||||
self._notify_status_listeners(listeners, status)
|
||||
bot.run_forever()
|
||||
with self._lock:
|
||||
self._status = "stopped"
|
||||
self._bot = None
|
||||
self._stopped_at = self._now()
|
||||
status = self._status_payload_locked()
|
||||
listeners = list(self._listeners.values())
|
||||
self._notify_status_listeners(listeners, status)
|
||||
except Exception:
|
||||
with self._lock:
|
||||
self._status = "error"
|
||||
self._bot = None
|
||||
self._last_error = traceback.format_exc()
|
||||
self._stopped_at = self._now()
|
||||
status = self._status_payload_locked()
|
||||
listeners = list(self._listeners.values())
|
||||
self._notify_status_listeners(listeners, status)
|
||||
|
||||
def _status_payload_locked(self):
|
||||
return {
|
||||
"status": self._status,
|
||||
"running": self._status == "running",
|
||||
"last_error": self._last_error,
|
||||
"started_at": self._started_at,
|
||||
"stopped_at": self._stopped_at,
|
||||
}
|
||||
|
||||
def _notify_status_listeners(self, listeners, status):
|
||||
for callback in listeners:
|
||||
try:
|
||||
callback(status)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _now(self):
|
||||
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
|
||||
bot_controller = BotController()
|
||||
Reference in New Issue
Block a user