test/framework/bot.py
2026-03-17 14:59:41 +08:00

157 lines
5.7 KiB
Python

import re
import json
import threading
import concurrent.futures
from datetime import datetime
import lark_oapi as lark
from lark_oapi.api.im.v1 import (
CreateMessageRequest,
CreateMessageRequestBody,
)
from lark_oapi.event.callback.model.p2_card_action_trigger import P2CardActionTrigger
from framework.config.settings import Config as config
from framework.core.logger import get_logger
from framework.business.cloud_desktop import CloudDesktopService
from framework.business.dev_machine import DevMachineService
from framework.business.billing import BillingService
from framework.business.meal_reminder import MealReminderService
from framework.business.boilerplate import BoilerplateService
from framework.core.exceptions import TokenExpiredError
logger = get_logger("FeishuBot")
# ---------- 飞书卡片常量 ----------
TOKEN_UPDATE_CARD_ID = "AAq2uZZvyWY01"
RESULT_CARD_ID = "AAqKcs3OrZBnJ"
INTRO_CARD_ID = "AAq2uZZvyWY06"
# 线程池
executor = concurrent.futures.ThreadPoolExecutor(max_workers=10)
# ---------- 业务服务实例化 ----------
cd_service = CloudDesktopService()
dm_service = DevMachineService()
billing_service = BillingService()
meal_service = MealReminderService()
bp_service = BoilerplateService()
# ---------- 全局计数器 ----------
_counter_lock = threading.Lock()
_global_counter = 0
def _next_default_name() -> str:
global _global_counter
with _counter_lock:
_global_counter += 1
return f"验证{_global_counter:02d}"
# ---------- 飞书 API Client ----------
_lark_client = lark.Client.builder() \
.app_id(config.FEISHU_APP_ID) \
.app_secret(config.FEISHU_APP_SECRET) \
.build()
def send_feishu_message(chat_id: str, text: str):
body = CreateMessageRequestBody.builder().receive_id(chat_id).msg_type("text").content(json.dumps({"text": text})).build()
request = CreateMessageRequest.builder().receive_id_type("chat_id").request_body(body).build()
_lark_client.im.v1.message.create(request)
def send_card_json(chat_id: str, card_json: dict):
body = CreateMessageRequestBody.builder().receive_id(chat_id).msg_type("interactive").content(json.dumps(card_json)).build()
request = CreateMessageRequest.builder().receive_id_type("chat_id").request_body(body).build()
_lark_client.im.v1.message.create(request)
def _handle_single_task(chat_id, name, task_type):
try:
if task_type == "cd_post":
results, instance_id = cd_service.run_lifecycle_test(name, flow_type="postpaid_to_prepaid")
label = "云桌面(按转包)"
elif task_type == "cd_pre":
results, instance_id = cd_service.run_lifecycle_test(name, flow_type="prepaid_to_postpaid")
label = "云桌面(包转按)"
elif task_type == "bp":
results, instance_id = bp_service.run_lifecycle_test(name)
label = "样本工程"
else:
results, instance_id = dm_service.run_lifecycle_test(name)
label = "开发机"
send_feishu_message(chat_id, f"{label} [{name}] 验证完成!")
except TokenExpiredError:
send_feishu_message(chat_id, "⚠️ 认证过期,请更新 Token")
except Exception as e:
logger.error(f"Task error: {e}")
send_feishu_message(chat_id, f"{label} 验证异常: {e}")
def _parse_command(text: str):
patterns = {
"cd_post": r"robogo-云桌面[ \-:=]+(.+)$",
"cd_pre": r"robogo-云桌面包月[ \-:=]+(.+)$",
"dm": r"robogo-开发机[ \-:=]+(.+)$",
"bp": r"robogo-样本工程[ \-:=]+(.+)$",
"billing": r"账单[ \-:=]*(.*)$",
}
# 辅助匹配模式(不带名称的情况)
fallback_patterns = {
"cd_post": r"robogo-云桌面\s*$",
"cd_pre": r"robogo-云桌面包月\s*$",
"dm": r"robogo-开发机\s*$",
"bp": r"robogo-样本工程\s*$",
}
text_clean = text.strip()
logger.info(f"Parsing command from text: '{text_clean}'")
# 先试带有名称的模式
for cmd, p in patterns.items():
m = re.search(p, text_clean)
if m:
logger.info(f"Matched named pattern: {cmd}, name: {m.group(1).strip()}")
return cmd, m.group(1).strip()
# 再试默认模式
for cmd, p in fallback_patterns.items():
m = re.search(p, text_clean)
if m:
logger.info(f"Matched fallback pattern: {cmd}")
return cmd, None
logger.warning(f"No pattern matched for text: '{text_clean}'")
return None, None
def _on_message(data):
msg = data.event.message
chat_id = msg.chat_id
content = json.loads(msg.content)
text = content.get("text", "")
clean_text = re.sub(r'@[^ ]+', '', text).strip()
if "饭否" in clean_text:
send_card_json(chat_id, meal_service.build_card())
return
cmd, name = _parse_command(clean_text)
if cmd == "billing":
items = billing_service.fetch_billing_data(name)
send_feishu_message(chat_id, f"共找到 {len(items)} 条账单记录")
elif cmd:
name = name or _next_default_name()
executor.submit(_handle_single_task, chat_id, name, cmd)
else:
send_feishu_message(chat_id, "未能识别指令,请尝试 [robogo-云桌面] 或 [账单]")
def main():
# 使用 WebSocket (Long Polling) 模式,无需公网 IP
event_handler = lark.EventDispatcherHandler.builder(config.FEISHU_APP_ID, config.FEISHU_APP_SECRET) \
.register_p2_im_message_receive_v1(_on_message) \
.build()
ws_client = lark.ws.Client(config.FEISHU_APP_ID, config.FEISHU_APP_SECRET, event_handler=event_handler)
logger.info("Bot started in WebSocket mode...")
ws_client.start()
if __name__ == "__main__":
main()