Files
LangBot/src/langbot/libs/slack_api/api.py
Guanchao Wang 0aa5188b29 Feat/unified webhook (#1793)
* 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>
2025-12-01 22:09:20 +08:00

128 lines
4.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)