157 lines
5.7 KiB
Python
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()
|