Skip to content

Commit 55fcfd3

Browse files
committed
✨ support message reaction
1 parent 7423f61 commit 55fcfd3

File tree

11 files changed

+273
-12
lines changed

11 files changed

+273
-12
lines changed

src/nonebot_plugin_alconna/uniseg/adapters/discord/exporter.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,40 @@ async def edit(self, new: Sequence[Segment], mid: Any, bot: Bot, context: Union[
195195
return await bot.edit_message(channel_id=context.channel_id, message_id=int(mid), **parse_message(new_msg))
196196
return await bot.edit_message(channel_id=mid.channel_id, message_id=_mid.id, **parse_message(new_msg))
197197

198+
async def reaction(self, emoji: Emoji, mid: Any, bot: Bot, context: Union[Target, Event], delete: bool = False):
199+
assert isinstance(bot, DiscordBot)
200+
if isinstance(mid, (str, SnowflakeType)):
201+
assert isinstance(context, MessageEvent)
202+
if delete:
203+
await bot.delete_own_reaction(
204+
channel_id=context.channel_id,
205+
message_id=int(mid),
206+
emoji=emoji.name or emoji.id,
207+
emoji_id=int(emoji.id) if emoji.name else None,
208+
)
209+
else:
210+
await bot.create_reaction(
211+
channel_id=context.channel_id,
212+
message_id=int(mid),
213+
emoji=emoji.name or emoji.id,
214+
emoji_id=int(emoji.id) if emoji.name else None,
215+
)
216+
_mid: MessageGet = cast(MessageGet, mid)
217+
if delete:
218+
await bot.delete_own_reaction(
219+
channel_id=_mid.channel_id,
220+
message_id=_mid.id,
221+
emoji=emoji.name or emoji.id,
222+
emoji_id=int(emoji.id) if emoji.name else None,
223+
)
224+
else:
225+
await bot.create_reaction(
226+
channel_id=_mid.channel_id,
227+
message_id=_mid.id,
228+
emoji=emoji.name or emoji.id,
229+
emoji_id=int(emoji.id) if emoji.name else None,
230+
)
231+
198232
def get_reply(self, mid: Any):
199233
_mid: MessageGet = cast(MessageGet, mid)
200234
return Reply(str(_mid.id), origin=_mid.id)

src/nonebot_plugin_alconna/uniseg/adapters/dodo/exporter.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77
from nonebot.adapters.dodo.bot import Bot as DoDoBot
88
from nonebot.adapters.dodo.event import MessageEvent
99
from nonebot.adapters.dodo.event import Event as DoDoEvent
10+
from nonebot.adapters.dodo.models import Emoji as DodoEmoji
1011
from nonebot.adapters.dodo.message import Message, MessageSegment
1112

1213
from nonebot_plugin_alconna.uniseg.constraint import SupportScope
13-
from nonebot_plugin_alconna.uniseg.segment import At, Text, Image, Reply, Video, Segment
14+
from nonebot_plugin_alconna.uniseg.segment import At, Text, Emoji, Image, Reply, Video, Segment
1415
from nonebot_plugin_alconna.uniseg.exporter import Target, SupportAdapter, MessageExporter, SerializeFailed, export
1516

1617

@@ -117,5 +118,13 @@ async def edit(self, new: Sequence[Segment], mid: Any, bot: Bot, context: Union[
117118
return await bot.set_channel_message_edit(message_id=mid, message_body=new_msg.to_message_body()[0])
118119
return None
119120

121+
async def reaction(self, emoji: Emoji, mid: Any, bot: Bot, context: Union[Target, Event], delete: bool = False):
122+
assert isinstance(bot, DoDoBot)
123+
emj = DodoEmoji(type=1, id=emoji.id)
124+
if delete:
125+
await bot.set_channel_message_reaction_remove(message_id=mid, emoji=emj)
126+
else:
127+
await bot.set_channel_message_reaction_add(message_id=mid, emoji=emj)
128+
120129
def get_reply(self, mid: Any):
121130
return Reply(mid)

src/nonebot_plugin_alconna/uniseg/adapters/feishu/exporter.py

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,19 @@
1010

1111
from nonebot_plugin_alconna.uniseg.constraint import SupportScope
1212
from nonebot_plugin_alconna.uniseg.exporter import Target, SupportAdapter, MessageExporter, SerializeFailed, export
13-
from nonebot_plugin_alconna.uniseg.segment import At, File, Text, AtAll, Audio, Image, Reply, Video, Voice, Segment
13+
from nonebot_plugin_alconna.uniseg.segment import (
14+
At,
15+
File,
16+
Text,
17+
AtAll,
18+
Audio,
19+
Emoji,
20+
Image,
21+
Reply,
22+
Video,
23+
Voice,
24+
Segment,
25+
)
1426

1527

1628
class FeishuMessageExporter(MessageExporter[Message]):
@@ -172,10 +184,8 @@ async def send_to(self, target: Union[Target, Event], bot: Bot, message: Message
172184

173185
async def recall(self, mid: Any, bot: Bot, context: Union[Target, Event]):
174186
assert isinstance(bot, FeishuBot)
175-
176-
params = {"method": "DELETE"}
177187
message_id = mid if isinstance(mid, str) else mid["message_id"]
178-
return await bot.call_api(f"im/v1/messages/{message_id}", **params)
188+
return await bot.delete_msg(message_id)
179189

180190
async def edit(self, new: Sequence[Segment], mid: Any, bot: Bot, context: Union[Target, Event]):
181191
assert isinstance(bot, FeishuBot)
@@ -184,5 +194,24 @@ async def edit(self, new: Sequence[Segment], mid: Any, bot: Bot, context: Union[
184194
message_id = mid if isinstance(mid, str) else mid["message_id"]
185195
return await bot.edit_msg(message_id, content=content, msg_type=msg_type)
186196

197+
async def reaction(self, emoji: Emoji, mid: Any, bot: Bot, context: Union[Target, Event], delete: bool = False):
198+
assert isinstance(bot, FeishuBot)
199+
message_id = mid if isinstance(mid, str) else mid["message_id"]
200+
if delete:
201+
queries = {"reaction_type": emoji.name or emoji.id}
202+
resp = await bot.call_api(f"im/v1/messages/{message_id}/reactions", method="GET", params=queries)
203+
if items := resp.get("items"):
204+
reaction_id = items[0]["reaction_id"]
205+
return await bot.call_api(f"im/v1/messages/{message_id}/reactions/{reaction_id}", method="DELETE")
206+
else:
207+
params = {
208+
"method": "POST",
209+
"data": {
210+
"emoji": emoji.name,
211+
"reaction_type": {"emoji_type": emoji.name or emoji.id},
212+
},
213+
}
214+
return await bot.call_api(f"im/v1/messages/{message_id}/reactions", **params)
215+
187216
def get_reply(self, mid: Any):
188217
return Reply(mid["message_id"])

src/nonebot_plugin_alconna/uniseg/adapters/kook/exporter.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,28 @@ async def edit(self, new: Sequence[Segment], mid: Any, bot: Bot, context: Union[
184184
await bot.message_update(**data)
185185
return
186186

187+
async def reaction(
188+
self,
189+
emoji: Emoji,
190+
mid: Any,
191+
bot: Bot,
192+
context: Union[Target, Event],
193+
delete: bool = False,
194+
):
195+
assert isinstance(bot, KBot)
196+
if isinstance(mid, str):
197+
if delete:
198+
await bot.message_deleteReaction(msg_id=mid, emoji=emoji.name or emoji.id)
199+
else:
200+
await bot.message_addReaction(msg_id=mid, emoji=emoji.name or emoji.id)
201+
_mid: MessageCreateReturn = cast(MessageCreateReturn, mid)
202+
if not _mid.msg_id:
203+
return
204+
if delete:
205+
await bot.message_deleteReaction(msg_id=_mid.msg_id, emoji=emoji.name or emoji.id)
206+
else:
207+
await bot.message_addReaction(msg_id=_mid.msg_id, emoji=emoji.name or emoji.id)
208+
187209
def get_reply(self, mid: Any):
188210
_mid: MessageCreateReturn = cast(MessageCreateReturn, mid)
189211
if not _mid.msg_id:

src/nonebot_plugin_alconna/uniseg/adapters/kritor/exporter.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,5 +315,27 @@ async def recall(self, mid: Any, bot: Bot, context: Union[Target, Event]):
315315
assert isinstance(mid, (SendMessageByResIdResponse, SendMessageResponse))
316316
await bot.recall_message(message_id=mid.message_id)
317317

318+
async def reaction(self, emoji: Emoji, mid: Any, bot: Bot, context: Union[Target, Event], delete: bool = False):
319+
assert isinstance(bot, KritorBot)
320+
if isinstance(context, Event):
321+
assert isinstance(context, MessageEvent)
322+
contact = context.contact
323+
elif context.private:
324+
if context.parent_id:
325+
contact = Contact(type=SceneType.STRANGER_FROM_GROUP, id=context.id, sub_id=context.parent_id)
326+
else:
327+
contact = Contact(type=SceneType.FRIEND, id=context.id)
328+
elif context.channel:
329+
contact = Contact(type=SceneType.GUILD, id=context.parent_id, sub_id=context.id)
330+
else:
331+
contact = Contact(type=SceneType.GROUP, id=context.id)
332+
if isinstance(mid, str):
333+
await bot.set_message_comment_emoji(contact=contact, message_id=mid, emoji=int(emoji.id), is_set=not delete)
334+
else:
335+
assert isinstance(mid, (SendMessageByResIdResponse, SendMessageResponse))
336+
await bot.set_message_comment_emoji(
337+
contact=contact, message_id=mid.message_id, emoji=int(emoji.id), is_set=not delete
338+
)
339+
318340
def get_reply(self, mid: Any):
319341
return Reply(str(mid["message_id"]))

src/nonebot_plugin_alconna/uniseg/adapters/qq/exporter.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,39 @@ async def recall(self, mid: Any, bot: Bot, context: Union[Target, Event]):
459459
message_id=mid,
460460
)
461461

462+
async def reaction(self, emoji: Emoji, mid: Any, bot: Bot, context: Union[Target, Event], delete: bool = False):
463+
assert isinstance(bot, QQBot)
464+
if isinstance(mid, GuildMessage):
465+
if delete:
466+
await bot.delete_own_message_reaction(
467+
channel_id=mid.channel_id,
468+
message_id=mid.id,
469+
type=1,
470+
id=emoji.id,
471+
)
472+
else:
473+
await bot.put_message_reaction(
474+
channel_id=mid.channel_id,
475+
message_id=mid.id,
476+
type=1,
477+
id=emoji.id,
478+
)
479+
elif isinstance(mid, str) and isinstance(context, GuildMessageEvent):
480+
if delete:
481+
await bot.delete_own_message_reaction(
482+
channel_id=context.channel_id,
483+
message_id=mid,
484+
type=1,
485+
id=emoji.id,
486+
)
487+
else:
488+
await bot.put_message_reaction(
489+
channel_id=context.channel_id,
490+
message_id=mid,
491+
type=1,
492+
id=emoji.id,
493+
)
494+
462495
def get_reply(self, mid: Any):
463496
if isinstance(mid, GuildMessage):
464497
return Reply(mid.id)

src/nonebot_plugin_alconna/uniseg/adapters/satori/exporter.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
Text,
1919
AtAll,
2020
Audio,
21+
Emoji,
2122
Image,
2223
Reply,
2324
Video,
@@ -211,15 +212,42 @@ async def edit(self, new: Sequence[Segment], mid: Any, bot: Bot, context: Union[
211212
return await bot.update_message(context.channel.id, mid, new_msg)
212213
_mid: MessageObject = cast(MessageObject, mid)
213214
if isinstance(context, Target):
215+
channel_id = mid.channel.id if mid.channel else context.id
214216
if context.private:
215-
channel = await bot.user_channel_create(user_id=context.id)
216-
return await bot.update_message(channel.id, _mid.id, new_msg)
217-
return await bot.update_message(context.id, _mid.id, new_msg)
217+
channel_id = (await bot.user_channel_create(user_id=context.id)).id
218+
return await bot.update_message(channel_id, _mid.id, new_msg)
218219
if TYPE_CHECKING:
219220
assert isinstance(context, MessageEvent)
220221
channel = mid.channel or context.channel
221222
return await bot.update_message(channel.id, _mid.id, new_msg)
222223

224+
async def reaction(self, emoji: Emoji, mid: Any, bot: Bot, context: Union[Target, Event], delete: bool = False):
225+
assert isinstance(bot, SatoriBot)
226+
if isinstance(context, MessageEvent) and isinstance(mid, str):
227+
if delete:
228+
return await bot.reaction_delete(
229+
channel_id=context.channel.id, message_id=mid, emoji=emoji.name or emoji.id
230+
)
231+
return await bot.reaction_create(
232+
channel_id=context.channel.id, message_id=mid, emoji=emoji.name or emoji.id
233+
)
234+
_mid: MessageObject = cast(MessageObject, mid)
235+
if isinstance(context, Target):
236+
channel_id = mid.channel.id if mid.channel else context.id
237+
if context.private:
238+
channel_id = (await bot.user_channel_create(user_id=context.id)).id
239+
if delete:
240+
return await bot.reaction_delete(
241+
channel_id=channel_id, message_id=_mid.id, emoji=emoji.name or emoji.id
242+
)
243+
return await bot.reaction_create(channel_id=channel_id, message_id=_mid.id, emoji=emoji.name or emoji.id)
244+
if TYPE_CHECKING:
245+
assert isinstance(context, MessageEvent)
246+
channel = mid.channel or context.channel
247+
if delete:
248+
return await bot.reaction_delete(channel_id=channel.id, message_id=_mid.id, emoji=emoji.name or emoji.id)
249+
return await bot.reaction_create(channel_id=channel.id, message_id=_mid.id, emoji=emoji.name or emoji.id)
250+
223251
def get_reply(self, mid: Any):
224252
_mid: MessageObject = cast(MessageObject, mid)
225253
return Reply(_mid.id)

src/nonebot_plugin_alconna/uniseg/adapters/telegram/exporter.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from nonebot.adapters.telegram.message import Message as TgMessage
1414
from nonebot.adapters.telegram.model import Message as MessageModel
1515
from nonebot.adapters.telegram.event import MessageEvent, EventWithChat
16-
from nonebot.adapters.telegram.model import InlineKeyboardButton, InlineKeyboardMarkup
16+
from nonebot.adapters.telegram.model import ReactionTypeEmoji, InlineKeyboardButton, InlineKeyboardMarkup
1717

1818
from nonebot_plugin_alconna.uniseg.constraint import SupportScope
1919
from nonebot_plugin_alconna.uniseg.exporter import Target, SupportAdapter, MessageExporter, SerializeFailed, export
@@ -198,6 +198,22 @@ async def edit(self, new: Sequence[Segment], mid: Any, bot: Bot, context: Union[
198198
return res
199199
return None
200200

201+
async def reaction(self, emoji: Emoji, mid: Any, bot: Bot, context: Union[Target, Event], delete: bool = False):
202+
assert isinstance(bot, TgBot)
203+
if delete:
204+
return # FIXME: how to delete reaction in telegram?
205+
if isinstance(mid, (str, int)) and isinstance(context, MessageEvent):
206+
await bot.set_message_reaction(
207+
chat_id=context.chat.id, message_id=int(mid), reaction=[ReactionTypeEmoji(emoji=emoji.name or emoji.id)]
208+
)
209+
else:
210+
_mid: MessageModel = cast(MessageModel, mid)
211+
await bot.set_message_reaction(
212+
chat_id=_mid.chat.id,
213+
message_id=_mid.message_id,
214+
reaction=[ReactionTypeEmoji(emoji=emoji.name or emoji.id)],
215+
)
216+
201217
def get_reply(self, mid: Any):
202218
_mid: MessageModel = cast(MessageModel, mid)
203219
return Reply(str(_mid.message_id))

src/nonebot_plugin_alconna/uniseg/exporter.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,5 +243,8 @@ async def recall(self, mid: Any, bot: Bot, context: Union[Target, Event]):
243243
async def edit(self, new: Sequence[Segment], mid: Any, bot: Bot, context: Union[Target, Event]):
244244
raise NotImplementedError
245245

246+
async def reaction(self, emoji: Emoji, mid: Any, bot: Bot, context: Union[Target, Event], delete: bool = False):
247+
raise NotImplementedError
248+
246249
def get_reply(self, mid: Any) -> Reply:
247250
raise NotImplementedError

src/nonebot_plugin_alconna/uniseg/functions.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from nonebot.adapters import Bot, Event
77
from nonebot.internal.matcher import current_bot, current_event
88

9+
from .segment import Emoji
910
from .exporter import SerializeFailed
1011
from .adapters import alter_get_exporter
1112

@@ -31,7 +32,10 @@ async def message_recall(
3132
_adapter = bot.adapter
3233
adapter = _adapter.get_name()
3334
if fn := alter_get_exporter(adapter):
34-
return await fn.recall(message_id or fn.get_message_id(event), bot, event)
35+
try:
36+
return await fn.recall(message_id or fn.get_message_id(event), bot, event)
37+
except NotImplementedError:
38+
return
3539
raise SerializeFailed(lang.require("nbp-uniseg", "unsupported").format(adapter=adapter))
3640

3741

@@ -56,7 +60,40 @@ async def message_edit(
5660
_adapter = bot.adapter
5761
adapter = _adapter.get_name()
5862
if fn := alter_get_exporter(adapter):
59-
return await fn.edit(msg, message_id or fn.get_message_id(event), bot, event)
63+
try:
64+
return await fn.edit(msg, message_id or fn.get_message_id(event), bot, event)
65+
except NotImplementedError:
66+
return
67+
raise SerializeFailed(lang.require("nbp-uniseg", "unsupported").format(adapter=adapter))
68+
69+
70+
async def message_reaction(
71+
emoji: str | Emoji,
72+
message_id: str | None = None,
73+
event: Event | None = None,
74+
bot: Bot | None = None,
75+
adapter: str | None = None,
76+
delete: bool = False,
77+
):
78+
if not event:
79+
try:
80+
event = current_event.get()
81+
except LookupError as e:
82+
raise SerializeFailed(lang.require("nbp-uniseg", "event_missing")) from e
83+
if not bot:
84+
try:
85+
bot = current_bot.get()
86+
except LookupError as e:
87+
raise SerializeFailed(lang.require("nbp-uniseg", "bot_missing")) from e
88+
if not adapter:
89+
_adapter = bot.adapter
90+
adapter = _adapter.get_name()
91+
emj = Emoji(emoji) if isinstance(emoji, str) else emoji
92+
if fn := alter_get_exporter(adapter):
93+
try:
94+
return await fn.reaction(emj, message_id or fn.get_message_id(event), bot, event, delete=delete)
95+
except NotImplementedError:
96+
return
6097
raise SerializeFailed(lang.require("nbp-uniseg", "unsupported").format(adapter=adapter))
6198

6299

0 commit comments

Comments
 (0)