From c0dbf6fd134d92b7f424f46fe7331ebe0ca9129b Mon Sep 17 00:00:00 2001 From: wangcham Date: Sun, 30 Mar 2025 12:53:48 -0400 Subject: [PATCH 1/9] feat:add support for slack --- .gitignore | 1 + libs/slack_api/__init__.py | 0 libs/slack_api/api.py | 104 +++++++++++++++++ libs/slack_api/slackevent.py | 66 +++++++++++ pkg/platform/sources/slack.py | 200 ++++++++++++++++++++++++++++++++ pkg/platform/sources/slack.yaml | 44 +++++++ pkg/utils/image.py | 6 +- requirements.txt | 2 +- templates/platform.json | 8 ++ 9 files changed, 429 insertions(+), 2 deletions(-) create mode 100644 libs/slack_api/__init__.py create mode 100644 libs/slack_api/api.py create mode 100644 libs/slack_api/slackevent.py create mode 100644 pkg/platform/sources/slack.py create mode 100644 pkg/platform/sources/slack.yaml diff --git a/.gitignore b/.gitignore index 17271201..1c2147a8 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ botpy.log* /libs/wecom_api/test.py /venv /jp-tyo-churros-05.rockchin.top +test.py \ No newline at end of file diff --git a/libs/slack_api/__init__.py b/libs/slack_api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/libs/slack_api/api.py b/libs/slack_api/api.py new file mode 100644 index 00000000..b9e39b9f --- /dev/null +++ b/libs/slack_api/api.py @@ -0,0 +1,104 @@ +import json +from quart import Quart, jsonify,request +from slack_sdk.web.async_client import AsyncWebClient +from .slackevent import SlackEvent +from typing import Callable, Dict, Any +from pkg.platform.types import events as platform_events, message as platform_message + +class SlackClient(): + + def __init__(self,bot_token:str,signing_secret:str): + + self.bot_token = bot_token + self.signing_secret = signing_secret + self.app = Quart(__name__) + self.client = AsyncWebClient(self.bot_token) + 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 # avoid block + + async def handle_callback_request(self): + try: + body = await request.get_data() + data = json.loads(body) + print("shoudao:") + print(data) + 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", "channel"]: + event = SlackEvent.from_payload(data) + await self._handle_message(event) + return jsonify({'status': 'ok'}) + + except Exception as e: + 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_channle(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"] + print("bot_id:") + print(self.bot_user_id) + print("fanhui:") + print(response) + return + except Exception as 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"] + print("bot_id:") + print(self.bot_user_id) + + return + except Exception as e: + 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) + + + + + + + + diff --git a/libs/slack_api/slackevent.py b/libs/slack_api/slackevent.py new file mode 100644 index 00000000..9eb7137d --- /dev/null +++ b/libs/slack_api/slackevent.py @@ -0,0 +1,66 @@ +from typing import Dict, Any, Optional + +class SlackEvent(dict): + @staticmethod + def from_payload(payload: Dict[str, Any]) -> Optional["SlackEvent"]: + try: + event = SlackEvent(payload) + return event + except KeyError: + return None + + @property + def text(self) -> str: + if self.get("event", {}).get("channel_type") == "im": + elements = self["event"]["blocks"][0]["elements"][0]["elements"] + for el in elements: + if el.get("type") == "text": + return el.get("text", "") + + if self.get("event",{}).get("channel_type") == "channel": + elements = self["event"]["blocks"][0]["elements"][0]["elements"] + text_result = next((el["text"] for el in elements if el["type"] == "text"), "") + return text_result + + + return "" + + + @property + def user_id(self) -> Optional[str]: + return self.get("event", {}).get("user","") + + @property + def channel_id(self) -> Optional[str]: + return self.get("event", {}).get("channel","") + + @property + def type(self) -> str: + """ message对应私聊,app_mention对应频道at """ + return self.get("event", {}).get("channel_type", "") + + @property + def message_id(self) -> str: + return self.get("event_id","") + + @property + def pic_url(self) -> str: + """提取 Slack 事件中的图片 URL""" + files = self.get("event", {}).get("files", []) + if files: + return files[0].get("url_private", "") + return "" + + + @property + def sender_name(self) -> str: + return self.get("event", {}).get("user","") + + def __getattr__(self, key: str) -> Optional[Any]: + return self.get(key) + + def __setattr__(self, key: str, value: Any) -> None: + self[key] = value + + def __repr__(self) -> str: + return f"" diff --git a/pkg/platform/sources/slack.py b/pkg/platform/sources/slack.py new file mode 100644 index 00000000..e4eafe17 --- /dev/null +++ b/pkg/platform/sources/slack.py @@ -0,0 +1,200 @@ +from __future__ import annotations +import typing +import asyncio +import traceback + +import datetime + +from libs.slack_api.api import SlackClient +from pkg.platform.adapter import MessagePlatformAdapter +from pkg.platform.types import events as platform_events, message as platform_message +from libs.slack_api.slackevent import SlackEvent +from pkg.core import app +from .. import adapter +from ...core import app +from ..types import message as platform_message +from ..types import events as platform_events +from ..types import entities as platform_entities +from ...command.errors import ParamNotEnoughError +from ...utils import image + +class SlackMessageConverter(adapter.MessageConverter): + + @staticmethod + async def yiri2target(message_chain:platform_message.MessageChain): + content_list = [] + for msg in message_chain: + if type(msg) is platform_message.Plain: + content_list.append({ + "content":msg.text, + }) + + return content_list + + @staticmethod + async def target2yiri(message:str,message_id:str,pic_url:str): + yiri_msg_list = [] + yiri_msg_list.append( + platform_message.Source(id=message_id,time=datetime.datetime.now()) + ) + if pic_url is not None: + base64_url = await image.get_slack_image_to_base64(pic_url=pic_url) + yiri_msg_list.append( + platform_message.Image(base64=base64_url) + ) + + yiri_msg_list.append(platform_message.Plain(text=message)) + chain = platform_message.MessageChain(yiri_msg_list) + return chain + + +class SlackEventConverter(adapter.EventConverter): + + @staticmethod + async def yiri2target(event:platform_events.MessageEvent) -> SlackEvent: + return event.source_platform_object + + @staticmethod + async def target2yiri(event:SlackEvent): + yiri_chain = await SlackMessageConverter.target2yiri( + message=event.text,message_id=event.message_id,pic_url=event.pic_url + ) + + if event.type == 'channel': + yiri_chain.insert(0, platform_message.At(target="SlackBot")) + + sender = platform_entities.GroupMember( + id = event.user_id, + member_name= str(event.sender_name), + permission= 'MEMBER', + group = platform_entities.Group( + id = event.channel_id, + name = 'MEMBER', + permission= platform_entities.Permission.Member + ), + special_title='', + join_timestamp=0, + last_speak_timestamp=0, + mute_time_remaining=0 + ) + time = int(datetime.datetime.utcnow().timestamp()) + return platform_events.GroupMessage( + sender = sender, + message_chain=yiri_chain, + time = time, + source_platform_object=event + ) + + if event.type == 'im': + return platform_events.FriendMessage( + sender=platform_entities.Friend( + id=event.user_id, + nickname = event.sender_name, + remark="" + ), + message_chain = yiri_chain, + time = float(datetime.datetime.now().timestamp()), + source_platform_object=event, + ) + + + + +class SlackAdapter(adapter.MessagePlatformAdapter): + bot: SlackClient + ap: app.Application + bot_account_id: str + message_converter: SlackMessageConverter = SlackMessageConverter() + event_converter: SlackEventConverter = SlackEventConverter() + config: dict + + def __init__(self,config:dict,ap:app.Application): + self.config = config + self.ap = app.Application + required_keys = [ + "bot_token", + "signing_secret", + ] + missing_keys = [key for key in required_keys if key not in config] + if missing_keys: + raise ParamNotEnoughError("Slack机器人缺少相关配置项,请查看文档或联系管理员") + + self.bot = SlackClient( + bot_token=self.config["bot_token"], + signing_secret=self.config["signing_secret"] + ) + + async def reply_message( + self, + message_source: platform_events.MessageEvent, + message: platform_message.MessageChain, + quote_origin: bool = False, + ): + slack_event = await SlackEventConverter.yiri2target( + message_source + ) + + content_list = await SlackMessageConverter.yiri2target(message) + + for content in content_list: + if slack_event.type == 'channel': + print("fasong1") + await self.bot.send_message_to_channle( + content['content'],slack_event.channel_id + ) + if slack_event.type == 'im': + await self.bot.send_message_to_one( + content['content'],slack_event.user_id + ) + + async def send_message(self, target_type: str, target_id: str, message: platform_message.MessageChain): + pass + + + def register_listener( + self, + event_type: typing.Type[platform_events.Event], + callback: typing.Callable[ + [platform_events.Event, adapter.MessagePlatformAdapter], None + ], + ): + async def on_message(event:SlackEvent): + self.bot_account_id = "SlackBot" + try: + return await callback( + await self.event_converter.target2yiri(event),self + ) + except: + traceback.print_exc() + + if event_type == platform_events.FriendMessage: + self.bot.on_message("im")(on_message) + elif event_type == platform_events.GroupMessage: + self.bot.on_message("channel")(on_message) + + + async def run_async(self): + async def shutdown_trigger_placeholder(): + while True: + await asyncio.sleep(1) + + await self.bot.run_task( + host=self.config["host"], + port=self.config["port"], + shutdown_trigger=shutdown_trigger_placeholder, + ) + + async def kill(self) -> bool: + return False + + async def unregister_listener( + self, + event_type: type, + callback: typing.Callable[[platform_events.Event, MessagePlatformAdapter], None], + ): + return super().unregister_listener(event_type, callback) + + + + + diff --git a/pkg/platform/sources/slack.yaml b/pkg/platform/sources/slack.yaml new file mode 100644 index 00000000..ffc924e3 --- /dev/null +++ b/pkg/platform/sources/slack.yaml @@ -0,0 +1,44 @@ +apiVersion: v1 +kind: MessagePlatformAdapter +metadata: + name: slack + label: + en_US: Slack API + zh_CN: Slack API + description: + en_US: Slack API + zh_CN: Slack API +spec: + config: + - name: bot_token + label: + en_US: Bot Token + zh_CN: 机器人令牌 + type: string + required: true + default: "" + - name: signing_secret + label: + en_US: signing_secret + zh_CN: 密钥 + type: string + required: true + default: "" + - name: port + label: + en_US: Port + zh_CN: 监听端口 + type: int + required: true + default: 2288 + - name: host + label: + en_US: Host + zh_CN: 监听主机 + type: string + required: true + default: 0.0.0.0 +execution: + python: + path: ./slack.py + attr: SlackAdapter \ No newline at end of file diff --git a/pkg/utils/image.py b/pkg/utils/image.py index 760c2128..2aedbfd7 100644 --- a/pkg/utils/image.py +++ b/pkg/utils/image.py @@ -212,4 +212,8 @@ async def extract_b64_and_format(image_base64_data: str) -> typing.Tuple[str, st """ base64_str = image_base64_data.split(',')[-1] image_format = image_base64_data.split(':')[-1].split(';')[0].split('/')[-1] - return base64_str, image_format \ No newline at end of file + return base64_str, image_format + + +async def get_slack_image_to_base64(pic_url:str): + pass \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 243d2da7..cd82211c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -34,6 +34,6 @@ dashscope python-telegram-bot certifi mcp - +slack_sdk # indirect taskgroup==0.0.0a4 \ No newline at end of file diff --git a/templates/platform.json b/templates/platform.json index fe39947c..ddb9f045 100644 --- a/templates/platform.json +++ b/templates/platform.json @@ -94,6 +94,14 @@ "adapter":"telegram", "enable": false, "token":"" + }, + { + "adapter":"slack", + "enable":true, + "bot_token":"", + "signing_secret":"", + "host":"0.0.0.0", + "port":2288 } ], "track-function-calls": true, From be1328cee9c64d06fe6ed0a37b8d60865aeb2abd Mon Sep 17 00:00:00 2001 From: wangcham Date: Sun, 30 Mar 2025 22:24:53 -0400 Subject: [PATCH 2/9] feat: add support for slack --- libs/slack_api/api.py | 24 ++++++++++++++---------- libs/slack_api/slackevent.py | 10 ++++------ pkg/platform/sources/slack.py | 11 +++++------ pkg/utils/image.py | 19 +++++++++++++++++-- 4 files changed, 40 insertions(+), 24 deletions(-) diff --git a/libs/slack_api/api.py b/libs/slack_api/api.py index b9e39b9f..5988e3e7 100644 --- a/libs/slack_api/api.py +++ b/libs/slack_api/api.py @@ -17,23 +17,33 @@ class SlackClient(): self._message_handlers = { "example":[], } - self.bot_user_id = None # avoid block + self.bot_user_id = None # 避免机器人回复自己的消息 async def handle_callback_request(self): try: body = await request.get_data() data = json.loads(body) - print("shoudao:") - print(data) + 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", "channel"]: + # 处理私信 + 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'}) except Exception as e: raise(e) @@ -66,10 +76,6 @@ class SlackClient(): ) if self.bot_user_id is None and response.get("ok"): self.bot_user_id = response["message"]["bot_id"] - print("bot_id:") - print(self.bot_user_id) - print("fanhui:") - print(response) return except Exception as e: raise e @@ -82,8 +88,6 @@ class SlackClient(): ) if self.bot_user_id is None and response.get("ok"): self.bot_user_id = response["message"]["bot_id"] - print("bot_id:") - print(self.bot_user_id) return except Exception as e: diff --git a/libs/slack_api/slackevent.py b/libs/slack_api/slackevent.py index 9eb7137d..63c949e0 100644 --- a/libs/slack_api/slackevent.py +++ b/libs/slack_api/slackevent.py @@ -16,11 +16,9 @@ class SlackEvent(dict): for el in elements: if el.get("type") == "text": return el.get("text", "") - - if self.get("event",{}).get("channel_type") == "channel": - elements = self["event"]["blocks"][0]["elements"][0]["elements"] - text_result = next((el["text"] for el in elements if el["type"] == "text"), "") - return text_result + if self.get("event",{}).get("channel_type") == 'channel': + message_text = next((el["text"] for block in self.get("event", {}).get("blocks", []) if block.get("type") == "rich_text" for element in block.get("elements", []) if element.get("type") == "rich_text_section" for el in element.get("elements", []) if el.get("type") == "text"), "") + return message_text return "" @@ -49,7 +47,7 @@ class SlackEvent(dict): files = self.get("event", {}).get("files", []) if files: return files[0].get("url_private", "") - return "" + return None @property diff --git a/pkg/platform/sources/slack.py b/pkg/platform/sources/slack.py index e4eafe17..8101e5c5 100644 --- a/pkg/platform/sources/slack.py +++ b/pkg/platform/sources/slack.py @@ -32,13 +32,13 @@ class SlackMessageConverter(adapter.MessageConverter): return content_list @staticmethod - async def target2yiri(message:str,message_id:str,pic_url:str): + async def target2yiri(message:str,message_id:str,pic_url:str,bot:SlackClient): yiri_msg_list = [] yiri_msg_list.append( platform_message.Source(id=message_id,time=datetime.datetime.now()) ) if pic_url is not None: - base64_url = await image.get_slack_image_to_base64(pic_url=pic_url) + base64_url = await image.get_slack_image_to_base64(pic_url=pic_url,bot_token=bot.bot_token) yiri_msg_list.append( platform_message.Image(base64=base64_url) ) @@ -55,9 +55,9 @@ class SlackEventConverter(adapter.EventConverter): return event.source_platform_object @staticmethod - async def target2yiri(event:SlackEvent): + async def target2yiri(event:SlackEvent,bot:SlackClient): yiri_chain = await SlackMessageConverter.target2yiri( - message=event.text,message_id=event.message_id,pic_url=event.pic_url + message=event.text,message_id=event.message_id,pic_url=event.pic_url,bot=bot ) if event.type == 'channel': @@ -138,7 +138,6 @@ class SlackAdapter(adapter.MessagePlatformAdapter): for content in content_list: if slack_event.type == 'channel': - print("fasong1") await self.bot.send_message_to_channle( content['content'],slack_event.channel_id ) @@ -162,7 +161,7 @@ class SlackAdapter(adapter.MessagePlatformAdapter): self.bot_account_id = "SlackBot" try: return await callback( - await self.event_converter.target2yiri(event),self + await self.event_converter.target2yiri(event,self.bot),self ) except: traceback.print_exc() diff --git a/pkg/utils/image.py b/pkg/utils/image.py index 2aedbfd7..6e499340 100644 --- a/pkg/utils/image.py +++ b/pkg/utils/image.py @@ -215,5 +215,20 @@ async def extract_b64_and_format(image_base64_data: str) -> typing.Tuple[str, st return base64_str, image_format -async def get_slack_image_to_base64(pic_url:str): - pass \ No newline at end of file +async def get_slack_image_to_base64(pic_url:str,bot_token:str): + """ + 将Slack图片转换为base64 + """ + +async def get_slack_image_to_base64(pic_url:str, bot_token:str): + headers = {"Authorization": f"Bearer {bot_token}"} + try: + async with aiohttp.ClientSession() as session: + async with session.get(pic_url, headers=headers) as resp: + image_data = await resp.read() + return base64.b64encode(image_data).decode('utf-8') + except Exception as e: + raise(e) + + + From 70f8ddb1baa17fa43b8d0bc85f2a23144a32123c Mon Sep 17 00:00:00 2001 From: wangcham Date: Sun, 30 Mar 2025 22:56:51 -0400 Subject: [PATCH 3/9] fix: delete useless image function in slack --- pkg/utils/image.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkg/utils/image.py b/pkg/utils/image.py index 6e499340..8f395c35 100644 --- a/pkg/utils/image.py +++ b/pkg/utils/image.py @@ -215,10 +215,6 @@ async def extract_b64_and_format(image_base64_data: str) -> typing.Tuple[str, st return base64_str, image_format -async def get_slack_image_to_base64(pic_url:str,bot_token:str): - """ - 将Slack图片转换为base64 - """ async def get_slack_image_to_base64(pic_url:str, bot_token:str): headers = {"Authorization": f"Bearer {bot_token}"} From 5744eca37aea004157b49898643aeb2f15ef4510 Mon Sep 17 00:00:00 2001 From: wangcham Date: Sun, 30 Mar 2025 23:06:03 -0400 Subject: [PATCH 4/9] fix: bot user id in slack --- pkg/platform/sources/slack.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/platform/sources/slack.py b/pkg/platform/sources/slack.py index 8101e5c5..2e71defc 100644 --- a/pkg/platform/sources/slack.py +++ b/pkg/platform/sources/slack.py @@ -158,7 +158,10 @@ class SlackAdapter(adapter.MessagePlatformAdapter): ], ): async def on_message(event:SlackEvent): - self.bot_account_id = "SlackBot" + if self.bot.bot_user_id: + self.bot_account_id = self.bot.bot_user_id + else: + self.bot_account_id = 'SlackBot' try: return await callback( await self.event_converter.target2yiri(event,self.bot),self From 686be4acbcb9e1f45500840438aa47a35c612855 Mon Sep 17 00:00:00 2001 From: wangcham Date: Mon, 31 Mar 2025 01:10:45 -0400 Subject: [PATCH 5/9] fix: eliminate host config --- libs/slack_api/slackevent.py | 5 +++-- pkg/platform/sources/slack.py | 2 +- templates/platform.json | 1 - 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/slack_api/slackevent.py b/libs/slack_api/slackevent.py index 63c949e0..bf335ac8 100644 --- a/libs/slack_api/slackevent.py +++ b/libs/slack_api/slackevent.py @@ -16,11 +16,12 @@ class SlackEvent(dict): for el in elements: if el.get("type") == "text": return el.get("text", "") - if self.get("event",{}).get("channel_type") == 'channel': + + if self.get("event",{}).get("channel_type") == 'channel': + message_text = next((el["text"] for block in self.get("event", {}).get("blocks", []) if block.get("type") == "rich_text" for element in block.get("elements", []) if element.get("type") == "rich_text_section" for el in element.get("elements", []) if el.get("type") == "text"), "") return message_text - return "" diff --git a/pkg/platform/sources/slack.py b/pkg/platform/sources/slack.py index 2e71defc..f98c2a85 100644 --- a/pkg/platform/sources/slack.py +++ b/pkg/platform/sources/slack.py @@ -181,7 +181,7 @@ class SlackAdapter(adapter.MessagePlatformAdapter): await asyncio.sleep(1) await self.bot.run_task( - host=self.config["host"], + host="0.0.0.0", port=self.config["port"], shutdown_trigger=shutdown_trigger_placeholder, ) diff --git a/templates/platform.json b/templates/platform.json index ddb9f045..a2fbd36e 100644 --- a/templates/platform.json +++ b/templates/platform.json @@ -100,7 +100,6 @@ "enable":true, "bot_token":"", "signing_secret":"", - "host":"0.0.0.0", "port":2288 } ], From 8799f86ea4a714f2ea9d6235bb69ea8e95f22b4f Mon Sep 17 00:00:00 2001 From: Guanchao Wang Date: Mon, 31 Mar 2025 13:48:37 +0800 Subject: [PATCH 6/9] Update pkg/platform/sources/slack.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- pkg/platform/sources/slack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/platform/sources/slack.py b/pkg/platform/sources/slack.py index f98c2a85..b899bbe2 100644 --- a/pkg/platform/sources/slack.py +++ b/pkg/platform/sources/slack.py @@ -110,7 +110,7 @@ class SlackAdapter(adapter.MessagePlatformAdapter): def __init__(self,config:dict,ap:app.Application): self.config = config - self.ap = app.Application + self.ap = ap required_keys = [ "bot_token", "signing_secret", From 715da548c8b1bbd20f56fb1821b133a739363a60 Mon Sep 17 00:00:00 2001 From: wangcham Date: Tue, 1 Apr 2025 02:34:16 -0400 Subject: [PATCH 7/9] fix: put the link and content together --- libs/slack_api/api.py | 5 ++++- libs/slack_api/slackevent.py | 40 +++++++++++++++++++++++++++++------ pkg/platform/sources/slack.py | 7 ++---- 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/libs/slack_api/api.py b/libs/slack_api/api.py index 5988e3e7..86239ce9 100644 --- a/libs/slack_api/api.py +++ b/libs/slack_api/api.py @@ -32,6 +32,7 @@ class SlackClient(): 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) @@ -44,6 +45,8 @@ class SlackClient(): event = SlackEvent.from_payload(data) await self._handle_message(event) return jsonify({'status':'ok'}) + + return jsonify({'status': 'ok'}) except Exception as e: raise(e) @@ -68,7 +71,7 @@ class SlackClient(): return func return decorator - async def send_message_to_channle(self,text:str,channel_id:str): + async def send_message_to_channel(self,text:str,channel_id:str): try: response = await self.client.chat_postMessage( channel=channel_id, diff --git a/libs/slack_api/slackevent.py b/libs/slack_api/slackevent.py index bf335ac8..5a6e9f90 100644 --- a/libs/slack_api/slackevent.py +++ b/libs/slack_api/slackevent.py @@ -11,18 +11,44 @@ class SlackEvent(dict): @property def text(self) -> str: + if self.get("event", {}).get("channel_type") == "im": - elements = self["event"]["blocks"][0]["elements"][0]["elements"] - for el in elements: - if el.get("type") == "text": - return el.get("text", "") + blocks = self.get("event", {}).get("blocks", []) + if not blocks: + return "" + + elements = blocks[0].get("elements", []) + if not elements: + return "" + + elements = elements[0].get("elements", []) + text = "" + + for el in elements: + if el.get("type") == "text": + text += el.get("text", "") + elif el.get("type") == "link": + text += el.get("url", "") + + return text + if self.get("event",{}).get("channel_type") == 'channel': - - message_text = next((el["text"] for block in self.get("event", {}).get("blocks", []) if block.get("type") == "rich_text" for element in block.get("elements", []) if element.get("type") == "rich_text_section" for el in element.get("elements", []) if el.get("type") == "text"), "") + message_text = "" + for block in self.get("event", {}).get("blocks", []): + if block.get("type") == "rich_text": + for element in block.get("elements", []): + if element.get("type") == "rich_text_section": + parts = [] + for el in element.get("elements", []): + if el.get("type") == "text": + parts.append(el["text"]) + elif el.get("type") == "link": + parts.append(el["url"]) + message_text = "".join(parts) + return message_text - return "" @property diff --git a/pkg/platform/sources/slack.py b/pkg/platform/sources/slack.py index b899bbe2..9d910a70 100644 --- a/pkg/platform/sources/slack.py +++ b/pkg/platform/sources/slack.py @@ -138,7 +138,7 @@ class SlackAdapter(adapter.MessagePlatformAdapter): for content in content_list: if slack_event.type == 'channel': - await self.bot.send_message_to_channle( + await self.bot.send_message_to_channel( content['content'],slack_event.channel_id ) if slack_event.type == 'im': @@ -158,10 +158,7 @@ class SlackAdapter(adapter.MessagePlatformAdapter): ], ): async def on_message(event:SlackEvent): - if self.bot.bot_user_id: - self.bot_account_id = self.bot.bot_user_id - else: - self.bot_account_id = 'SlackBot' + self.bot_account_id = 'SlackBot' try: return await callback( await self.event_converter.target2yiri(event,self.bot),self From 873a0339d8a9d8aa8452e3842fbfe8a579d477b7 Mon Sep 17 00:00:00 2001 From: wangcham Date: Tue, 1 Apr 2025 03:03:48 -0400 Subject: [PATCH 8/9] feat: add support for sending active message in slack --- pkg/platform/sources/slack.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/platform/sources/slack.py b/pkg/platform/sources/slack.py index 9d910a70..bc4e4d8e 100644 --- a/pkg/platform/sources/slack.py +++ b/pkg/platform/sources/slack.py @@ -147,7 +147,12 @@ class SlackAdapter(adapter.MessagePlatformAdapter): ) async def send_message(self, target_type: str, target_id: str, message: platform_message.MessageChain): - pass + content_list = await SlackMessageConverter.yiri2target(message) + for content in content_list: + if target_type == 'person': + await self.bot.send_message_to_one(content['content'],target_id) + if target_type == 'group': + await self.bot.send_message_to_channel(content['content'],target_id) def register_listener( From 011a7958959b3e2773dbbc1ae3950626476a2cd4 Mon Sep 17 00:00:00 2001 From: Junyan Qin Date: Tue, 1 Apr 2025 15:32:48 +0800 Subject: [PATCH 9/9] doc(README): add slack --- README.md | 2 +- README_EN.md | 2 +- README_JP.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0b7df23d..f7ef74e6 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ | 钉钉 | ✅ | | | Discord | ✅ | | | Telegram | ✅ | | -| Slack | 🚧 | | +| Slack | ✅ | | | LINE | 🚧 | | | WhatsApp | 🚧 | | diff --git a/README_EN.md b/README_EN.md index 47df5ec1..77338219 100644 --- a/README_EN.md +++ b/README_EN.md @@ -90,7 +90,7 @@ Directly use the released version to run, see the [Manual Deployment](https://do | DingTalk | ✅ | | | Discord | ✅ | | | Telegram | ✅ | | -| Slack | 🚧 | | +| Slack | ✅ | | | LINE | 🚧 | | | WhatsApp | 🚧 | | diff --git a/README_JP.md b/README_JP.md index db4b0cf7..440134a2 100644 --- a/README_JP.md +++ b/README_JP.md @@ -89,7 +89,7 @@ LangBotはBTPanelにリストされています。BTPanelをインストール | DingTalk | ✅ | | | Discord | ✅ | | | Telegram | ✅ | | -| Slack | 🚧 | | +| Slack | ✅ | | | LINE | 🚧 | | | WhatsApp | 🚧 | |