Skip to content

Commit 335ed91

Browse files
committed
✨ version 0.8.0
add button and interaction event
1 parent bdcf184 commit 335ed91

File tree

5 files changed

+268
-3
lines changed

5 files changed

+268
-3
lines changed

nonebot/adapters/satori/adapter.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,15 @@
1414
from .config import Config, ClientInfo
1515
from .exception import ApiNotAvailable
1616
from .models import Event as SatoriEvent
17-
from .event import EVENT_CLASSES, Event, MessageEvent, LoginAddedEvent, LoginRemovedEvent, LoginUpdatedEvent
17+
from .event import (
18+
EVENT_CLASSES,
19+
Event,
20+
MessageEvent,
21+
LoginAddedEvent,
22+
InteractionEvent,
23+
LoginRemovedEvent,
24+
LoginUpdatedEvent,
25+
)
1826
from .models import (
1927
Payload,
2028
LoginStatus,
@@ -238,7 +246,7 @@ async def _loop(self, info: ClientInfo, ws: WebSocket):
238246
f"Received event for unknown bot " f"{escape_tag(event.self_id)}",
239247
)
240248
continue
241-
if isinstance(event, MessageEvent):
249+
if isinstance(event, (MessageEvent, InteractionEvent)):
242250
event = event.convert()
243251
asyncio.create_task(bot.handle_event(event))
244252
elif isinstance(payload, PongPayload):

nonebot/adapters/satori/event.py

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from .models import Event as SatoriEvent
1313
from .message import Message, RenderMessage
1414
from .models import InnerMessage as SatoriMessage
15+
from .models import ArgvInteraction, ButtonInteraction
1516
from .models import Guild, Login, Channel, ChannelType, InnerMember
1617

1718
E = TypeVar("E", bound="Event")
@@ -39,6 +40,8 @@ class EventType(str, Enum):
3940
REACTION_ADDED = "reaction-added"
4041
REACTION_REMOVED = "reaction-removed"
4142
INTERNAL = "internal"
43+
INTERACTION_BUTTON = "interaction/button"
44+
INTERACTION_COMMAND = "interaction/command"
4245

4346

4447
class Event(BaseEvent, SatoriEvent):
@@ -423,3 +426,184 @@ class InternalEvent(Event):
423426
@override
424427
def get_event_name(self) -> str:
425428
return getattr(self, "_type", "internal")
429+
430+
431+
class InteractionEvent(NoticeEvent):
432+
def convert(self) -> "InteractionEvent":
433+
raise NotImplementedError
434+
435+
@override
436+
def is_tome(self) -> bool:
437+
return True
438+
439+
440+
@register_event_class
441+
class InteractionButtonEvent(InteractionEvent):
442+
__type__ = EventType.INTERACTION_BUTTON
443+
444+
button: ButtonInteraction
445+
446+
@override
447+
def get_event_description(self) -> str:
448+
return escape_tag(f"Button interacted with button#{self.button.id}")
449+
450+
def convert(self):
451+
if self.channel and self.user and self.channel.type != ChannelType.DIRECT:
452+
return PublicInteractionButtonEvent.parse_obj(self)
453+
if self.user:
454+
return PrivateInteractionButtonEvent.parse_obj(self)
455+
return self
456+
457+
458+
class PrivateInteractionButtonEvent(InteractionButtonEvent):
459+
user: User
460+
461+
@override
462+
def get_session_id(self) -> str:
463+
return self.channel.id if self.channel else self.user.id
464+
465+
@override
466+
def get_user_id(self) -> str:
467+
return self.user.id
468+
469+
470+
class PublicInteractionButtonEvent(InteractionButtonEvent):
471+
user: User
472+
channel: Channel
473+
474+
@override
475+
def get_session_id(self) -> str:
476+
s = f"{self.channel.id}/{self.user.id}"
477+
if self.guild:
478+
s = f"{self.guild.id}/{s}"
479+
return s
480+
481+
@override
482+
def get_user_id(self) -> str:
483+
return self.user.id
484+
485+
486+
@register_event_class
487+
class InteractionCommandEvent(InteractionEvent):
488+
__type__ = EventType.INTERACTION_COMMAND
489+
490+
if TYPE_CHECKING:
491+
_message: Message
492+
original_message: Message
493+
494+
@override
495+
def get_type(self) -> str:
496+
return "message"
497+
498+
@override
499+
def get_message(self) -> Message:
500+
return self._message
501+
502+
def convert(self):
503+
if self.argv:
504+
return InteractionCommandArgvEvent.convert(self) # type: ignore
505+
return InteractionCommandMessageEvent.convert(self) # type: ignore
506+
507+
508+
class InteractionCommandArgvEvent(InteractionCommandEvent):
509+
argv: ArgvInteraction
510+
511+
@override
512+
def get_event_description(self) -> str:
513+
return escape_tag(f"Command interacted with {self.argv}")
514+
515+
@root_validator
516+
def generate_message(cls, values: Dict[str, Any]) -> Dict[str, Any]:
517+
argv: ArgvInteraction = values["argv"]
518+
cmd = argv.name
519+
if argv.arguments:
520+
cmd += " ".join(argv.arguments)
521+
values["_message"] = Message(cmd)
522+
values["original_message"] = deepcopy(values["_message"])
523+
return values
524+
525+
def convert(self):
526+
if self.channel and self.user and self.channel.type != ChannelType.DIRECT:
527+
return PublicInteractionCommandArgvEvent.parse_obj(self)
528+
if self.user:
529+
return PrivateInteractionCommandArgvEvent.parse_obj(self)
530+
return self
531+
532+
533+
class PrivateInteractionCommandArgvEvent(InteractionCommandArgvEvent):
534+
user: User
535+
536+
@override
537+
def get_session_id(self) -> str:
538+
return self.channel.id if self.channel else self.user.id
539+
540+
@override
541+
def get_user_id(self) -> str:
542+
return self.user.id
543+
544+
545+
class PublicInteractionCommandArgvEvent(InteractionCommandArgvEvent):
546+
user: User
547+
channel: Channel
548+
549+
@override
550+
def get_session_id(self) -> str:
551+
s = f"{self.channel.id}/{self.user.id}"
552+
if self.guild:
553+
s = f"{self.guild.id}/{s}"
554+
return s
555+
556+
@override
557+
def get_user_id(self) -> str:
558+
return self.user.id
559+
560+
561+
class InteractionCommandMessageEvent(InteractionCommandEvent):
562+
message: SatoriMessage
563+
to_me: bool = False
564+
reply: Optional[RenderMessage] = None
565+
566+
@root_validator
567+
def generate_message(cls, values: Dict[str, Any]) -> Dict[str, Any]:
568+
values["_message"] = Message.from_satori_element(values["message"].content)
569+
values["original_message"] = deepcopy(values["_message"])
570+
return values
571+
572+
@override
573+
def get_event_description(self) -> str:
574+
return escape_tag(f"Command interacted with {self.get_message()}")
575+
576+
def convert(self):
577+
if self.channel and self.user and self.channel.type != ChannelType.DIRECT:
578+
return PublicInteractionCommandMessageEvent.parse_obj(self)
579+
if self.user:
580+
return PrivateInteractionCommandMessageEvent.parse_obj(self)
581+
return self
582+
583+
584+
class PrivateInteractionCommandMessageEvent(InteractionCommandMessageEvent):
585+
user: User
586+
587+
@override
588+
def get_session_id(self) -> str:
589+
return self.channel.id if self.channel else self.user.id
590+
591+
@override
592+
def get_user_id(self) -> str:
593+
return self.user.id
594+
595+
596+
class PublicInteractionCommandMessageEvent(InteractionCommandMessageEvent):
597+
user: User
598+
channel: Channel
599+
600+
@override
601+
def get_session_id(self) -> str:
602+
s = f"{self.channel.id}/{self.user.id}"
603+
if self.guild:
604+
s = f"{self.guild.id}/{s}"
605+
return s
606+
607+
@override
608+
def get_user_id(self) -> str:
609+
return self.user.id

nonebot/adapters/satori/message.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,33 @@ def author(
254254
data["avatar"] = avatar
255255
return Author("author", data)
256256

257+
@staticmethod
258+
def action_button(button_id: str, display: Optional[str] = None, theme: Optional[str] = None):
259+
data = {"type": "action", "id": button_id}
260+
if display:
261+
data["display"] = display
262+
if theme:
263+
data["theme"] = theme
264+
return Button("button", data)
265+
266+
@staticmethod
267+
def link_button(url: str, display: Optional[str] = None, theme: Optional[str] = None):
268+
data = {"type": "link", "href": url}
269+
if display:
270+
data["display"] = display
271+
if theme:
272+
data["theme"] = theme
273+
return Button("button", data)
274+
275+
@staticmethod
276+
def input_button(text: str, display: Optional[str] = None, theme: Optional[str] = None):
277+
data = {"type": "input", "text": text}
278+
if display:
279+
data["display"] = display
280+
if theme:
281+
data["theme"] = theme
282+
return Button("button", data)
283+
257284
@override
258285
def is_text(self) -> bool:
259286
return False
@@ -548,6 +575,35 @@ class Author(MessageSegment):
548575
data: AuthorData = field(default_factory=dict)
549576

550577

578+
class ButtonData(TypedDict, total=False):
579+
type: str
580+
display: NotRequired[str]
581+
id: NotRequired[str]
582+
href: NotRequired[str]
583+
text: NotRequired[str]
584+
theme: NotRequired[str]
585+
586+
587+
@dataclass
588+
class Button(MessageSegment):
589+
data: ButtonData = field(default_factory=dict)
590+
591+
@override
592+
def __str__(self):
593+
attr = [f'type="{escape(self.data["type"])}"']
594+
if self.data["type"] == "action":
595+
attr.append(f'id="{escape(self.data["id"])}"')
596+
if self.data["type"] == "link":
597+
attr.append(f'href="{escape(self.data["href"])}"')
598+
if self.data["type"] == "input":
599+
attr.append(f'text="{escape(self.data["text"])}"')
600+
if "theme":
601+
attr.append(f'theme="{escape(self.data["theme"])}"')
602+
if "display" in self.data:
603+
return f'<button {" ".join(attr)}>{escape(self.data["display"])}</button>'
604+
return f'<button {" ".join(attr)} />'
605+
606+
551607
ELEMENT_TYPE_MAP = {
552608
"text": (Text, "text"),
553609
"at": (At, "at"),
@@ -610,6 +666,11 @@ def from_satori_element(cls, elements: List[Element]) -> "Message":
610666
)
611667
else:
612668
msg.append(Link("link", {"text": elem.attrs["href"]}))
669+
elif elem.type == "button":
670+
if elem.children:
671+
msg.append(Button("button", {"display": elem.children[0].attrs["text"], **elem.attrs}))
672+
else:
673+
msg.append(Button("button", {**elem.attrs}))
613674
elif elem.type in STYLE_TYPE_MAP:
614675
seg_cls, seg_type = STYLE_TYPE_MAP[elem.type]
615676
msg.append(seg_cls(seg_type, {"text": elem.children[0].attrs["text"]}))

nonebot/adapters/satori/models.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,16 @@ class Login(BaseModel, extra=Extra.allow):
8181
status: LoginStatus
8282

8383

84+
class ArgvInteraction(BaseModel):
85+
name: str
86+
arguments: list
87+
options: Any
88+
89+
90+
class ButtonInteraction(BaseModel):
91+
id: str
92+
93+
8494
class Opcode(IntEnum):
8595
EVENT = 0
8696
PING = 1
@@ -188,6 +198,8 @@ class Event(BaseModel, extra=Extra.allow):
188198
platform: str
189199
self_id: str
190200
timestamp: datetime
201+
argv: Optional[ArgvInteraction] = None
202+
button: Optional[ButtonInteraction] = None
191203
channel: Optional[Channel] = None
192204
guild: Optional[Guild] = None
193205
login: Optional[Login] = None

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "nonebot-adapter-satori"
3-
version = "0.7.0"
3+
version = "0.8.0"
44
description = "Satori Protocol Adapter for Nonebot2"
55
authors = [
66
{name = "RF-Tar-Railt",email = "rf_tar_railt@qq.com"},

0 commit comments

Comments
 (0)