157 lines
4.9 KiB
Python
157 lines
4.9 KiB
Python
from __future__ import annotations
|
||
|
||
from .shared import *
|
||
|
||
class FeishuConfigMixin:
|
||
def __init__(self, db: AsyncSession):
|
||
self.db = db
|
||
|
||
# 从数据库获取飞书配置
|
||
self.app_id = None
|
||
self.app_secret = None
|
||
self.encrypt_key = None
|
||
self.verification_token = None
|
||
self.event_mode = "long_connection"
|
||
self.tenant_id = 1
|
||
|
||
@staticmethod
|
||
def _normalize_event_mode(_: Optional[str] = None) -> str:
|
||
return "long_connection"
|
||
|
||
@staticmethod
|
||
def _is_postgresql_session(db: AsyncSession) -> bool:
|
||
try:
|
||
bind = db.get_bind()
|
||
except Exception:
|
||
return False
|
||
dialect_name = str(getattr(getattr(bind, "dialect", None), "name", "")).lower()
|
||
return dialect_name.startswith("postgres")
|
||
|
||
@staticmethod
|
||
async def _notify_outbox_ready(db: AsyncSession, *, outbox_id: Optional[int]) -> None:
|
||
"""仅在 PostgreSQL 下发送 outbox ready 事件。"""
|
||
if not FeishuConfigMixin._is_postgresql_session(db):
|
||
return
|
||
try:
|
||
payload = str(int(outbox_id))[:64] if outbox_id is not None else ""
|
||
await db.execute(
|
||
text("SELECT pg_notify('feishu_outbox_ready', :payload)"),
|
||
{"payload": payload},
|
||
)
|
||
except Exception as exc:
|
||
print(f"[Feishu] outbox notify failed: outbox_id={outbox_id}, error={exc}")
|
||
|
||
async def load_config(self, tenant_id: int = 1):
|
||
"""从数据库加载飞书配置"""
|
||
self.tenant_id = tenant_id
|
||
|
||
result = await self.db.execute(
|
||
select(Integration).where(
|
||
Integration.tenant_id == tenant_id,
|
||
Integration.type == 'feishu',
|
||
Integration.is_active.is_(True),
|
||
)
|
||
)
|
||
integration = result.scalar_one_or_none()
|
||
|
||
if integration:
|
||
if isinstance(integration.config, str):
|
||
config = json.loads(integration.config) if integration.config else {}
|
||
else:
|
||
config = integration.config or {}
|
||
self.app_id = config.get('app_id')
|
||
self.app_secret = config.get('app_secret')
|
||
self.encrypt_key = None
|
||
self.verification_token = None
|
||
self.event_mode = self._normalize_event_mode(config.get('event_mode'))
|
||
else:
|
||
self.app_id = None
|
||
self.app_secret = None
|
||
self.encrypt_key = None
|
||
self.verification_token = None
|
||
self.event_mode = 'long_connection'
|
||
|
||
def is_configured(self) -> bool:
|
||
"""检查是否已配置"""
|
||
return bool(self.app_id and self.app_secret)
|
||
|
||
async def save_config(
|
||
self,
|
||
app_id: str,
|
||
app_secret: str,
|
||
encrypt_key: str,
|
||
verification_token: str,
|
||
event_mode: str = "long_connection",
|
||
tenant_id: int = 1
|
||
):
|
||
"""保存飞书配置到数据库"""
|
||
mode = self._normalize_event_mode(event_mode)
|
||
config = {
|
||
'app_id': app_id,
|
||
'app_secret': app_secret,
|
||
'encrypt_key': "",
|
||
'verification_token': "",
|
||
'event_mode': mode,
|
||
}
|
||
|
||
# 查找或创建集成记录
|
||
result = await self.db.execute(
|
||
select(Integration).where(
|
||
Integration.tenant_id == tenant_id,
|
||
Integration.type == 'feishu'
|
||
)
|
||
)
|
||
integration = result.scalar_one_or_none()
|
||
|
||
if integration:
|
||
integration.config = config
|
||
integration.is_active = True
|
||
else:
|
||
integration = Integration(
|
||
type='feishu',
|
||
config=config,
|
||
is_active=True,
|
||
tenant_id=tenant_id
|
||
)
|
||
self.db.add(integration)
|
||
|
||
await self.db.commit()
|
||
|
||
# 更新内存配置
|
||
self.app_id = app_id
|
||
self.app_secret = app_secret
|
||
self.encrypt_key = None
|
||
self.verification_token = None
|
||
self.event_mode = mode
|
||
self.tenant_id = tenant_id
|
||
|
||
async def handle_message_event(
|
||
self,
|
||
event_data: Dict[str, Any]
|
||
) -> Dict[str, Any]:
|
||
"""
|
||
处理飞书消息事件
|
||
|
||
支持的事件类型:
|
||
- message.receive_v1: 收到群聊消息
|
||
- im.message.receive_v1: 收到群聊消息(schema 2.0)
|
||
"""
|
||
try:
|
||
event_type = (event_data.get('type') or '').strip()
|
||
|
||
# 飞书 schema 2.0 会返回 im.message.receive_v1
|
||
if event_type in {'message.receive_v1', 'im.message.receive_v1'}:
|
||
return await self._handle_message_receive(event_data)
|
||
|
||
return {
|
||
'code': 0,
|
||
'msg': f'Event type {event_type} not handled'
|
||
}
|
||
|
||
except Exception as e:
|
||
print(f"[Feishu] Handle event error: {e}")
|
||
return {
|
||
'code': 1,
|
||
'msg': str(e)
|
||
}
|