mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-02 12:05:54 +00:00
* fix: wecombot id * feat: add unified webhook for wecom * feat: add support for wecombot,wxoa,slack and qqo * fix: slack adapter * feat: qqo * fix: errors when npm lint * fix: qqo webhook * feat: add wecomcs * fix: modify wecomcs * fix: import errors * feat: add configurable webhook display prefix (#1797) * Initial plan * Add webhook_display_prefix configuration option Co-authored-by: RockChinQ <45992437+RockChinQ@users.noreply.github.com> * perf: change config field name --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: RockChinQ <45992437+RockChinQ@users.noreply.github.com> Co-authored-by: Junyan Qin <rockchinq@gmail.com> * feat: finish the fxxking line adapter --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Junyan Qin <rockchinq@gmail.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: RockChinQ <45992437+RockChinQ@users.noreply.github.com>
128 lines
4.5 KiB
Python
128 lines
4.5 KiB
Python
import json
|
||
import traceback
|
||
from quart import Quart, jsonify, request
|
||
from slack_sdk.web.async_client import AsyncWebClient
|
||
from .slackevent import SlackEvent
|
||
from typing import Callable
|
||
import langbot_plugin.api.entities.builtin.platform.events as platform_events
|
||
|
||
|
||
class SlackClient:
|
||
def __init__(self, bot_token: str, signing_secret: str, logger: None, unified_mode: bool = False):
|
||
self.bot_token = bot_token
|
||
self.signing_secret = signing_secret
|
||
self.unified_mode = unified_mode
|
||
self.app = Quart(__name__)
|
||
self.client = AsyncWebClient(self.bot_token)
|
||
|
||
# 只有在非统一模式下才注册独立路由
|
||
if not self.unified_mode:
|
||
self.app.add_url_rule(
|
||
'/callback/command', 'handle_callback', self.handle_callback_request, methods=['GET', 'POST']
|
||
)
|
||
|
||
self._message_handlers = {
|
||
'example': [],
|
||
}
|
||
self.bot_user_id = None # 避免机器人回复自己的消息
|
||
self.logger = logger
|
||
|
||
async def handle_callback_request(self):
|
||
"""处理回调请求(独立端口模式,使用全局 request)"""
|
||
return await self._handle_callback_internal(request)
|
||
|
||
async def handle_unified_webhook(self, req):
|
||
"""处理回调请求(统一 webhook 模式,显式传递 request)。
|
||
|
||
Args:
|
||
req: Quart Request 对象
|
||
|
||
Returns:
|
||
响应数据
|
||
"""
|
||
return await self._handle_callback_internal(req)
|
||
|
||
async def _handle_callback_internal(self, req):
|
||
"""处理回调请求的内部实现。
|
||
|
||
Args:
|
||
req: Quart Request 对象
|
||
"""
|
||
try:
|
||
body = await req.get_data()
|
||
data = json.loads(body)
|
||
if 'type' in data:
|
||
if data['type'] == 'url_verification':
|
||
return data['challenge']
|
||
|
||
bot_user_id = data.get('event', {}).get('bot_id', '')
|
||
|
||
if self.bot_user_id and bot_user_id == self.bot_user_id:
|
||
return jsonify({'status': 'ok'})
|
||
|
||
# 处理私信
|
||
if data and data.get('event', {}).get('channel_type') in ['im']:
|
||
event = SlackEvent.from_payload(data)
|
||
await self._handle_message(event)
|
||
return jsonify({'status': 'ok'})
|
||
|
||
# 处理群聊
|
||
if data.get('event', {}).get('type') == 'app_mention':
|
||
data.setdefault('event', {})['channel_type'] = 'channel'
|
||
event = SlackEvent.from_payload(data)
|
||
await self._handle_message(event)
|
||
return jsonify({'status': 'ok'})
|
||
|
||
return jsonify({'status': 'ok'})
|
||
|
||
except Exception as e:
|
||
await self.logger.error(f'Error in handle_callback_request: {traceback.format_exc()}')
|
||
raise (e)
|
||
|
||
async def _handle_message(self, event: SlackEvent):
|
||
"""
|
||
处理消息事件。
|
||
"""
|
||
msg_type = event.type
|
||
if msg_type in self._message_handlers:
|
||
for handler in self._message_handlers[msg_type]:
|
||
await handler(event)
|
||
|
||
def on_message(self, msg_type: str):
|
||
"""注册消息类型处理器"""
|
||
|
||
def decorator(func: Callable[[platform_events.Event], None]):
|
||
if msg_type not in self._message_handlers:
|
||
self._message_handlers[msg_type] = []
|
||
self._message_handlers[msg_type].append(func)
|
||
return func
|
||
|
||
return decorator
|
||
|
||
async def send_message_to_channel(self, text: str, channel_id: str):
|
||
try:
|
||
response = await self.client.chat_postMessage(channel=channel_id, text=text)
|
||
if self.bot_user_id is None and response.get('ok'):
|
||
self.bot_user_id = response['message']['bot_id']
|
||
return
|
||
except Exception as e:
|
||
await self.logger.error(f'Error in send_message: {e}')
|
||
raise e
|
||
|
||
async def send_message_to_one(self, text: str, user_id: str):
|
||
try:
|
||
response = await self.client.chat_postMessage(channel='@' + user_id, text=text)
|
||
if self.bot_user_id is None and response.get('ok'):
|
||
self.bot_user_id = response['message']['bot_id']
|
||
|
||
return
|
||
except Exception as e:
|
||
await self.logger.error(f'Error in send_message: {traceback.format_exc()}')
|
||
raise e
|
||
|
||
async def run_task(self, host: str, port: int, *args, **kwargs):
|
||
"""
|
||
启动 Quart 应用。
|
||
"""
|
||
await self.app.run_task(host=host, port=port, *args, **kwargs)
|