Skip to content

Commit a6d250c

Browse files
authored
✨ add bot send and exception catch
1 parent 59290e4 commit a6d250c

File tree

6 files changed

+117
-17
lines changed

6 files changed

+117
-17
lines changed

codegen/templates/event.py.jinja

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ from nonebot.utils import escape_tag
1010
from nonebot.adapters import Event as BaseEvent
1111

1212
from .message import Message
13+
from .utils import get_attr_or_item
1314

1415

1516
class Event(BaseEvent):
@@ -26,7 +27,7 @@ class Event(BaseEvent):
2627
@overrides(BaseEvent)
2728
def get_event_name(self) -> str:
2829
return self.name + (
29-
f".{action}" if (action := getattr(self.payload, "action", None)) else ""
30+
f".{action}" if (action := get_attr_or_item(self.payload, "action")) else ""
3031
)
3132

3233
@overrides(BaseEvent)
@@ -35,12 +36,12 @@ class Event(BaseEvent):
3536
f"{self.__class__.__name__} "
3637
+ (
3738
f"from sender {sender.login}"
38-
if (sender := getattr(self.payload, "sender", None))
39+
if (sender := get_attr_or_item(self.payload, "sender"))
3940
else ""
4041
)
4142
+ (
4243
f"in repository {repo.full_name}"
43-
if (repo := getattr(self.payload, "repository", None))
44+
if (repo := get_attr_or_item(self.payload, "repository"))
4445
else ""
4546
)
4647
)
@@ -51,7 +52,7 @@ class Event(BaseEvent):
5152

5253
@overrides(BaseEvent)
5354
def get_user_id(self) -> str:
54-
if sender := getattr(self.payload, "sender", None):
55+
if sender := get_attr_or_item(self.payload, "sender"):
5556
return sender.login
5657
raise ValueError("Event has no context!")
5758

nonebot/adapters/github/adapter.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
import asyncio
33
import inspect
44
from functools import partial
5-
from typing import Any, Union, Optional, cast
5+
from typing import Any, Union, Callable, Optional, cast
66

77
from nonebot.typing import overrides
8-
from githubkit.webhooks import parse, verify
8+
from githubkit.webhooks import verify
9+
from githubkit.exception import RequestFailed
910
from nonebot.drivers import (
1011
URL,
1112
Driver,
@@ -20,7 +21,9 @@
2021
from .utils import log
2122
from .event import Event, events
2223
from .bot import Bot, OAuthBot, GitHubBot
24+
from .message import Message, MessageSegment
2325
from .config import Config, OAuthApp, GitHubApp
26+
from .exception import ActionFailed, NetworkError
2427

2528

2629
class Adapter(BaseAdapter):
@@ -101,7 +104,13 @@ async def _call_api(self, bot: Bot, api: str, **data: Any) -> Any:
101104
func = getattr(func, part)
102105
if not inspect.isroutine(func) or not inspect.iscoroutinefunction(func):
103106
raise TypeError(f"{api} is invalid.")
104-
return await func(**data)
107+
108+
try:
109+
return await func(**data)
110+
except RequestFailed as e:
111+
raise ActionFailed(e.response) from None
112+
except Exception as e:
113+
raise NetworkError from e
105114

106115
@classmethod
107116
def payload_to_event(
@@ -129,3 +138,11 @@ def payload_to_event(
129138
return Event.parse_obj(
130139
{"id": event_id, "name": event_name, "payload": event_payload}
131140
)
141+
142+
@classmethod
143+
def custom_send(
144+
cls,
145+
send_func: Callable[[Bot, Event, Union[str, Message, MessageSegment]], Any],
146+
):
147+
"""自定义 Bot 的回复函数。"""
148+
Bot.send_handler = send_func

nonebot/adapters/github/bot.py

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import re
22
from typing_extensions import Self
33
from contextlib import contextmanager
4-
from typing import TYPE_CHECKING, Any, Dict, List, Union, Optional, Generator
4+
from typing import TYPE_CHECKING, Any, Dict, List, Union, Callable, Optional, Generator
55

66
from nonebot.typing import overrides
77
from githubkit.utils import UNSET, Unset
@@ -10,10 +10,10 @@
1010

1111
from nonebot.adapters import Bot as BaseBot
1212

13-
from .event import Event
14-
from .utils import APIContext
15-
from .message import MessageSegment
1613
from .config import OAuthApp, GitHubApp
14+
from .message import Message, MessageSegment
15+
from .event import Event, CommitCommentCreated
16+
from .utils import APIContext, get_attr_or_item
1717

1818
if TYPE_CHECKING:
1919
from githubkit.rest import RestNamespace
@@ -70,8 +70,47 @@ def _check_nickname(bot: "Bot", event: Event) -> None:
7070
message[0] = MessageSegment.markdown(text[m.end() :])
7171

7272

73+
async def send(
74+
bot: "Bot", event: Event, message: Union[str, Message, MessageSegment]
75+
) -> Any:
76+
msg = message if isinstance(message, Message) else Message(message)
77+
if isinstance(event, CommitCommentCreated):
78+
return await bot.rest.repos.async_create_commit_comment(
79+
owner=event.payload.repository.owner.login,
80+
repo=event.payload.repository.name,
81+
commit_sha=event.payload.comment.commit_id,
82+
body=msg.extract_plain_text(),
83+
)
84+
85+
owner: Optional[str] = None
86+
repo: Optional[str] = None
87+
if repository := get_attr_or_item(event.payload, "repository"):
88+
owner_user = get_attr_or_item(repository, "owner")
89+
owner = get_attr_or_item(owner_user, "login")
90+
repo = get_attr_or_item(repository, "name")
91+
92+
number: Optional[int] = None
93+
if issue := get_attr_or_item(event.payload, "issue"):
94+
number = get_attr_or_item(issue, "number")
95+
elif pull_request := get_attr_or_item(event.payload, "pull_request"):
96+
number = get_attr_or_item(pull_request, "number")
97+
98+
if owner and repo and number:
99+
return await bot.rest.issues.async_create_comment(
100+
owner=owner, repo=repo, issue_number=number, body=msg.extract_plain_text()
101+
)
102+
103+
raise RuntimeError(
104+
f"Cannot guess reply target for event type {event.__class__.__name__}"
105+
)
106+
107+
73108
class Bot(BaseBot):
74109

110+
send_handler: Callable[
111+
["Bot", Event, Union[str, Message, MessageSegment]], Any
112+
] = send
113+
75114
if TYPE_CHECKING:
76115
rest: RestNamespace
77116

@@ -98,8 +137,10 @@ async def handle_event(self, event: Event) -> None:
98137
await handle_event(self, event)
99138

100139
@overrides(BaseBot)
101-
async def send(self, event: Event, message):
102-
...
140+
async def send(
141+
self, event: Event, message: Union[str, Message, MessageSegment]
142+
) -> Any:
143+
return await self.__class__.send_handler(self, event, message)
103144

104145

105146
class OAuthBot(Bot):

nonebot/adapters/github/event.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@
380380
from nonebot.adapters import Event as BaseEvent
381381

382382
from .message import Message
383+
from .utils import get_attr_or_item
383384

384385

385386
class Event(BaseEvent):
@@ -396,7 +397,7 @@ def get_type(self) -> str:
396397
@overrides(BaseEvent)
397398
def get_event_name(self) -> str:
398399
return self.name + (
399-
f".{action}" if (action := getattr(self.payload, "action", None)) else ""
400+
f".{action}" if (action := get_attr_or_item(self.payload, "action")) else ""
400401
)
401402

402403
@overrides(BaseEvent)
@@ -405,12 +406,12 @@ def get_event_description(self) -> str:
405406
f"{self.__class__.__name__} "
406407
+ (
407408
f"from sender {sender.login}"
408-
if (sender := getattr(self.payload, "sender", None))
409+
if (sender := get_attr_or_item(self.payload, "sender"))
409410
else ""
410411
)
411412
+ (
412413
f"in repository {repo.full_name}"
413-
if (repo := getattr(self.payload, "repository", None))
414+
if (repo := get_attr_or_item(self.payload, "repository"))
414415
else ""
415416
)
416417
)
@@ -421,7 +422,7 @@ def get_message(self) -> Message:
421422

422423
@overrides(BaseEvent)
423424
def get_user_id(self) -> str:
424-
if sender := getattr(self.payload, "sender", None):
425+
if sender := get_attr_or_item(self.payload, "sender"):
425426
return sender.login
426427
raise ValueError("Event has no context!")
427428

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""OneBot v11 错误类型。
2+
3+
FrontMatter:
4+
sidebar_position: 8
5+
description: onebot.v11.exception 模块
6+
"""
7+
8+
from githubkit.exception import RequestFailed
9+
from nonebot.exception import AdapterException
10+
from nonebot.exception import ActionFailed as BaseActionFailed
11+
from nonebot.exception import NetworkError as BaseNetworkError
12+
from nonebot.exception import NoLogException as BaseNoLogException
13+
from nonebot.exception import ApiNotAvailable as BaseApiNotAvailable
14+
15+
16+
class GitHubAdapterException(AdapterException):
17+
def __init__(self):
18+
super().__init__("GitHub")
19+
20+
21+
class NetworkError(BaseNetworkError, GitHubAdapterException):
22+
"""网络错误。"""
23+
24+
25+
class ApiNotAvailable(BaseApiNotAvailable, GitHubAdapterException):
26+
pass
27+
28+
29+
class ActionFailed(RequestFailed, BaseActionFailed, GitHubAdapterException):
30+
def __repr__(self) -> str:
31+
return (
32+
f"<ActionFailed: {self.response.raw_request.method} "
33+
f"{self.response.raw_request.url}, status_code: {self.response.status_code}>"
34+
)

nonebot/adapters/github/utils.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import re
2+
import contextlib
23
from typing import TYPE_CHECKING, Any, Tuple
34

45
from nonebot.utils import logger_wrapper
@@ -13,6 +14,11 @@ def escape(content: str) -> str:
1314
return re.sub(r"\\|`|\*|_|{|}|\[|\]|\(|\)|#|\+|-|\.|!", r"\\\1", content)
1415

1516

17+
def get_attr_or_item(obj: Any, attr: str) -> Any:
18+
with contextlib.suppress(TypeError):
19+
return getattr(obj, attr, None) or obj[attr]
20+
21+
1622
class APIContext:
1723
__slots__ = ("bot", "parts")
1824

0 commit comments

Comments
 (0)