Skip to content

Commit ccccf7f

Browse files
committed
feat: 新增消息按钮结果回调、修改频道md消息接口
1 parent aa34daf commit ccccf7f

File tree

7 files changed

+147
-43
lines changed

7 files changed

+147
-43
lines changed

botpy/api.py

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
forum,
2323
)
2424

25+
2526
class BotAPI:
2627
"""
2728
机器人相关的API接口类
@@ -198,7 +199,7 @@ async def get_delete_member(
198199
guild_id: str,
199200
user_id: str,
200201
add_blacklist: bool = False,
201-
delete_history_msg_days: int = 0
202+
delete_history_msg_days: int = 0,
202203
) -> str:
203204
"""
204205
删除频道成员
@@ -246,7 +247,7 @@ async def get_guild_members(self, guild_id: str, after: str = "0", limit: int =
246247
guild_id=guild_id,
247248
)
248249
return await self._http.request(route, params=params)
249-
250+
250251
async def get_guild_role_members(
251252
self, guild_id: str, role_id: str, start_index: str = "0", limit: int = 1
252253
) -> Dict[str, Union[List[user.Member], str]]:
@@ -270,7 +271,7 @@ async def get_guild_role_members(
270271
"GET",
271272
"/guilds/{guild_id}/roles/{role_id}/members",
272273
guild_id=guild_id,
273-
role_id=role_id
274+
role_id=role_id,
274275
)
275276
return await self._http.request(route, params=params)
276277

@@ -584,6 +585,58 @@ async def post_keyboard_message(
584585
)
585586
return await self._http.request(route, json=payload)
586587

588+
async def on_interaction_result(self, interaction_id: str, code: int):
589+
"""
590+
`on_interaction_result` 消息按钮回调结果
591+
592+
Args:
593+
interaction_id (str): 消息按钮回调事件的 ID。
594+
code (int): 回调结果 0 成功,1 操作失败,2 操作频繁,3 重复操作,4 没有权限,5 仅管理员操作
595+
596+
Returns:
597+
598+
"""
599+
payload = {"code": code}
600+
route = Route(
601+
"PUT",
602+
"/interactions/{id}",
603+
id=interaction_id,
604+
)
605+
return await self._http.request(route, json=payload)
606+
607+
async def patch_guild_message(
608+
self,
609+
channel_id: str,
610+
patch_msg_id: str,
611+
msg_id: str = None,
612+
event_id: str = None,
613+
markdown: message.MarkdownPayload = None,
614+
keyboard: message.KeyboardPayload = message.KeyboardPayload(content={}),
615+
) -> message.Message:
616+
"""
617+
修改频道markdown消息,需要先申请权限。
618+
619+
Args:
620+
channel_id (str): 您要将消息发送到的频道的 ID。
621+
patch_msg_id (str): 需要修改的消息id。
622+
msg_id (str): 您要回复的消息的 ID。您可以从 AT_CREATE_MESSAGE 事件中获取此 ID。
623+
event_id (str): 您要回复的消息的事件 ID。
624+
markdown (message.MarkdownPayload): markdown 消息的构建参数。
625+
keyboard (message.KeyboardPayload): keyboard 消息的构建参数
626+
627+
Returns:
628+
message.Message: 一个消息字典对象。
629+
"""
630+
payload = locals()
631+
payload.pop("self", None)
632+
route = Route(
633+
"PATCH",
634+
"/channels/{channel_id}/messages/{patch_msg_id}",
635+
channel_id=channel_id,
636+
patch_msg_id=patch_msg_id,
637+
)
638+
return await self._http.request(route, json=payload)
639+
587640
# 私信消息
588641
async def create_dms(self, guild_id: str, user_id: str) -> message.DmsPayload:
589642
"""
@@ -766,7 +819,7 @@ async def mute_all(self, guild_id: str, mute_end_timestamp: str = None, mute_sec
766819
"""
767820
payload = {
768821
"mute_end_timestamp": mute_end_timestamp,
769-
"mute_seconds": mute_seconds
822+
"mute_seconds": mute_seconds,
770823
}
771824
route = Route("PATCH", "/guilds/{guild_id}/mute", guild_id=guild_id)
772825
return await self._http.request(route, json=payload)
@@ -808,7 +861,7 @@ async def mute_member(
808861
"""
809862
payload = {
810863
"mute_end_timestamp": mute_end_timestamp,
811-
"mute_seconds": mute_seconds
864+
"mute_seconds": mute_seconds,
812865
}
813866
route = Route("PATCH", "/guilds/{guild_id}/members/{user_id}/mute", guild_id=guild_id, user_id=user_id)
814867
return await self._http.request(route, json=payload)
@@ -831,7 +884,7 @@ async def mute_multi_member(
831884
payload = {
832885
"mute_end_timestamp": mute_end_timestamp,
833886
"mute_seconds": mute_seconds,
834-
"user_ids": user_ids
887+
"user_ids": user_ids,
835888
}
836889
route = Route("PATCH", "/guilds/{guild_id}/mute", guild_id=guild_id)
837890
return await self._http.request(route, json=payload)
@@ -1312,7 +1365,7 @@ async def delete_thread(self, channel_id: str, thread_id: str) -> str:
13121365
"DELETE", "/channels/{channel_id}/threads/{thread_id}", channel_id=channel_id, thread_id=thread_id
13131366
)
13141367
return await self._http.request(route)
1315-
1368+
13161369
async def post_group_message(
13171370
self,
13181371
group_openid: str,
@@ -1444,5 +1497,3 @@ async def post_c2c_file(
14441497
payload.pop("self", None)
14451498
route = Route("POST", "/v2/users/{openid}/files", openid=openid)
14461499
return await self._http.request(route, json=payload)
1447-
1448-

botpy/client.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,11 @@ def _loop_exception_handler(_loop, context):
218218
coroutine = self._connection.multi_run(session_interval)
219219
if self.ret_coro:
220220
return coroutine
221-
else:
221+
elif coroutine:
222222
await coroutine
223+
else:
224+
await self.close()
225+
_log.info("[botpy] 服务意外停止!")
223226
except KeyboardInterrupt:
224227
_log.info("[botpy] 服务强行停止!")
225228
# cancel all tasks lingering

botpy/gateway.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ async def ws_connect(self):
131131
elif msg.type == WSMsgType.CLOSED or msg.type == WSMsgType.CLOSE:
132132
await self.on_closed(ws_conn.close_code, msg.extra)
133133
if ws_conn.closed:
134-
_log.debug("[botpy] ws关闭, 停止接收消息!")
134+
_log.info("[botpy] ws关闭, 停止接收消息!")
135135
break
136136

137137
async def ws_identify(self):

botpy/interaction.py

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,56 @@
11
from .api import BotAPI
2-
from .types import interaction, gateway
2+
from .types import interaction
33

44

55
class Interaction:
6-
__slots__ = ("_api", "_ctx", "id", "application_id", "type", "event_id", "data", "guild_id", "channel_id")
6+
__slots__ = (
7+
"_api",
8+
"_ctx",
9+
"id",
10+
"application_id",
11+
"type",
12+
"chat_type",
13+
"event_id",
14+
"data",
15+
"guild_id",
16+
"channel_id",
17+
"group_open_id",
18+
"timestamp",
19+
"version",
20+
)
721

822
def __init__(self, api: BotAPI, event_id, data: interaction.InteractionPayload):
923
self._api = api
1024

1125
self.id = data.get("id", None)
1226
self.type = data.get("type", None)
27+
self.chat_type = data.get("chat_type", None)
1328
self.application_id = data.get("application_id", None)
1429
self.event_id = event_id
15-
self.data = data.get("data", None)
30+
self.data = self._Data(data.get("data", {}))
1631
self.guild_id = data.get("guild_id", None)
1732
self.channel_id = data.get("channel_id", None)
33+
self.group_open_id = data.get("group_open_id", None)
34+
self.timestamp = data.get("timestamp", None)
35+
self.version = data.get("version", None)
1836

1937
def __repr__(self):
20-
return str({items: str(getattr(self, items)) for items in self.__slots__ if not items.startswith('_')})
38+
return str({items: str(getattr(self, items)) for items in self.__slots__ if not items.startswith("_")})
39+
40+
class _Data:
41+
def __init__(self, data):
42+
self.type = data.get("type", None)
43+
self.resolved = Interaction._Resolved(data.get("resolved", None))
44+
45+
def __repr__(self):
46+
return str(self.__dict__)
47+
48+
class _Resolved:
49+
def __init__(self, data):
50+
self.button_id = data.get("button_id", None)
51+
self.button_data = data.get("button_data", None)
52+
self.message_id = data.get("message_id", None)
53+
self.user_id = data.get("user_id", None)
54+
55+
def __repr__(self):
56+
return str(self.__dict__)

botpy/message.py

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class Message:
1717
"seq",
1818
"seq_in_channel",
1919
"timestamp",
20-
"event_id"
20+
"event_id",
2121
)
2222

2323
def __init__(self, api: BotAPI, event_id, data: gateway.MessagePayload):
@@ -39,7 +39,7 @@ def __init__(self, api: BotAPI, event_id, data: gateway.MessagePayload):
3939
self.event_id = event_id
4040

4141
def __repr__(self):
42-
return str({items: str(getattr(self, items)) for items in self.__slots__ if not items.startswith('_')})
42+
return str({items: str(getattr(self, items)) for items in self.__slots__ if not items.startswith("_")})
4343

4444
class _User:
4545
def __init__(self, data):
@@ -100,7 +100,7 @@ class DirectMessage:
100100
"seq_in_channel",
101101
"src_guild_id",
102102
"timestamp",
103-
"event_id"
103+
"event_id",
104104
)
105105

106106
def __init__(self, api: BotAPI, event_id, data: gateway.DirectMessagePayload):
@@ -122,7 +122,7 @@ def __init__(self, api: BotAPI, event_id, data: gateway.DirectMessagePayload):
122122
self.event_id = event_id
123123

124124
def __repr__(self):
125-
return str({items: str(getattr(self, items)) for items in self.__slots__ if not items.startswith('_')})
125+
return str({items: str(getattr(self, items)) for items in self.__slots__ if not items.startswith("_")})
126126

127127
class _User:
128128
def __init__(self, data):
@@ -171,7 +171,8 @@ class MessageAudit:
171171
"message_id",
172172
"channel_id",
173173
"guild_id",
174-
"event_id")
174+
"event_id",
175+
)
175176

176177
def __init__(self, api: BotAPI, event_id, data: gateway.MessageAuditPayload):
177178
self._api = api
@@ -183,8 +184,9 @@ def __init__(self, api: BotAPI, event_id, data: gateway.MessageAuditPayload):
183184
self.event_id = event_id
184185

185186
def __repr__(self):
186-
return str({items: str(getattr(self, items)) for items in self.__slots__ if not items.startswith('_')})
187-
187+
return str({items: str(getattr(self, items)) for items in self.__slots__ if not items.startswith("_")})
188+
189+
188190
class BaseMessage:
189191
__slots__ = (
190192
"_api",
@@ -195,7 +197,7 @@ class BaseMessage:
195197
"attachments",
196198
"msg_seq",
197199
"timestamp",
198-
"event_id"
200+
"event_id",
199201
)
200202

201203
def __init__(self, api: BotAPI, event_id, data: gateway.MessagePayload):
@@ -208,17 +210,17 @@ def __init__(self, api: BotAPI, event_id, data: gateway.MessagePayload):
208210
self.msg_seq = data.get("msg_seq", None) # 全局消息序号
209211
self.timestamp = data.get("timestamp", None)
210212
self.event_id = event_id
211-
213+
212214
def __repr__(self):
213215
return str({items: str(getattr(self, items)) for items in self.__slots__ if not items.startswith("_")})
214-
216+
215217
class _MessageRef:
216218
def __init__(self, data):
217219
self.message_id = data.get("message_id", None)
218220

219221
def __repr__(self):
220222
return str(self.__dict__)
221-
223+
222224
class _Attachments:
223225
def __init__(self, data):
224226
self.content_type = data.get("content_type", None)
@@ -231,11 +233,12 @@ def __init__(self, data):
231233

232234
def __repr__(self):
233235
return str(self.__dict__)
234-
236+
237+
235238
class GroupMessage(BaseMessage):
236239
__slots__ = (
237240
"author",
238-
"group_openid"
241+
"group_openid",
239242
)
240243

241244
def __init__(self, api: BotAPI, event_id, data: gateway.MessagePayload):
@@ -246,7 +249,7 @@ def __init__(self, api: BotAPI, event_id, data: gateway.MessagePayload):
246249
def __repr__(self):
247250
slots = self.__slots__ + super().__slots__
248251
return str({items: str(getattr(self, items)) for items in slots if not items.startswith("_")})
249-
252+
250253
class _User:
251254
def __init__(self, data):
252255
self.member_openid = data.get("member_openid", None)
@@ -278,5 +281,3 @@ def __repr__(self):
278281

279282
async def reply(self, **kwargs):
280283
return await self._api.post_c2c_message(openid=self.author.user_openid, msg_id=self.id, **kwargs)
281-
282-

botpy/robot.py

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
from botpy.types import robot
1+
import asyncio
22
import time
3+
34
import aiohttp
5+
46
from .logging import get_logger
7+
from botpy.types import robot
58

69
_log = get_logger()
710

11+
812
class Robot:
913
def __init__(self, data: robot.Robot):
1014
self._update(data)
@@ -39,17 +43,23 @@ async def check_token(self):
3943
async def update_access_token(self):
4044
session = aiohttp.ClientSession()
4145
data = None
42-
async with session.post(
43-
url="https://bots.qq.com/app/getAppAccessToken",
44-
timeout=(aiohttp.ClientTimeout(total=5)),
45-
json={
46-
"appId": self.app_id,
47-
"clientSecret": self.secret,
48-
},
49-
) as response:
50-
data = await response.json()
51-
await session.close()
52-
_log.info(f"{data}")
46+
# TODO 增加超时重试
47+
try:
48+
async with session.post(
49+
url="https://bots.qq.com/app/getAppAccessToken",
50+
timeout=(aiohttp.ClientTimeout(total=20)),
51+
json={
52+
"appId": self.app_id,
53+
"clientSecret": self.secret,
54+
},
55+
) as response:
56+
data = await response.json()
57+
except asyncio.TimeoutError as e:
58+
_log.info("[botpy] access_token TimeoutError:" + str(e))
59+
raise
60+
finally:
61+
await session.close()
62+
_log.info("[botpy] access_token expires_in " + data["expires_in"])
5363
self.access_token = data["access_token"]
5464
self.expires_in = int(data["expires_in"]) + int(time.time())
5565

0 commit comments

Comments
 (0)