Files
ai-shiliu/app/application/bot_controller.py

135 lines
4.7 KiB
Python
Raw Permalink Normal View History

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()