Skip to content

Commit d6e6eb9

Browse files
committed
增加获取群内的成员信息和机器人信息方法,具体可查阅开发文档或示例插件
1 parent 26e1e69 commit d6e6eb9

33 files changed

Lines changed: 287 additions & 588 deletions

PLUGIN_DEVELOPMENT.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -536,12 +536,19 @@ ok, result = await event.sender.force_wakeup(user_id, "强制召回")
536536
# 生成分享链接
537537
url = await event.sender.get_share_link(callback_data='my_data')
538538

539-
# 获取图片尺寸 (URL 或 bytes)
540-
width, height = await event.sender.get_image_size("https://...")
539+
# 获取图片尺寸 (URL/bytes/本地路径, 返回 {'width', 'height', 'px'} 或 None)
540+
size = await event.sender.get_image_size("https://...")
541541

542542
# 手动上传媒体文件 (返回 file_info)
543543
file_info = await event.sender.upload_media(event, file_bytes, file_type=1)
544544
# file_type: 1=图片, 2=视频, 3=语音, 4=文件
545+
546+
# 查询单个群成员详情 (返回 dict 或 None, 含 member_openid/username/member_role 等)
547+
member = await event.sender.get_group_member(group_id, user_id)
548+
549+
# 查询机器人自身在某群的成员信息 (返回 dict 或 None)
550+
bot_member = await event.sender.get_bot_member(group_id)
551+
is_admin = bot_member and bot_member.get('member_role') in ('admin', 'owner')
545552
```
546553

547554
---

core/base/config.py

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,7 @@ def config_dir(self):
9393
# ------ 读取 ------
9494

9595
def get(self, name, key=None, default=None):
96-
"""获取配置值
97-
98-
Args:
99-
name: 配置文件名(不含 .yaml), 如 'settings', 'bot', 'extension/redis'
100-
key: 点号分隔路径, 如 'server.port', None 返回整个 dict
101-
default: 默认值
102-
"""
96+
"""获取配置值: name=配置文件名, key=点号路径 (None 返回整个 dict)"""
10397
self._maybe_reload(name)
10498
with self._rw_lock:
10599
data = self._cache.get(name, {})
@@ -134,8 +128,7 @@ def get_bot_setting(self, appid, key, default=None):
134128
if val is not None:
135129
self._bot_setting_cache[cache_key] = val
136130
return val
137-
# 负缓存: 命中内置默认值的键也缓存, 避免每条消息重复 key.split('.') + 字典遍历
138-
# (默认值与传入 default 无关, 仅对 _BOT_DEFAULTS 中存在的键缓存)
131+
# 负缓存: 仅对 _BOT_DEFAULTS 中存在的键缓存默认值
139132
dft = _BOT_DEFAULTS.get(key, _MISSING)
140133
if dft is not _MISSING:
141134
self._bot_setting_cache[cache_key] = dft
@@ -145,11 +138,7 @@ def get_bot_setting(self, appid, key, default=None):
145138
# ------ 热加载 ------
146139

147140
def _resolve_path(self, name):
148-
"""解析配置文件名 -> 绝对路径 (带缓存)
149-
150-
查找顺序: name.yaml > name.yml > name.example.yaml(自动复制) > name.yaml(占位)
151-
当实际配置文件不存在时, 自动从 .example.yaml 复制生成, 避免示例文件被本地修改污染版本控制.
152-
"""
141+
"""解析配置文件名为绝对路径 (带缓存): .yaml > .yml > .example.yaml 自动复制 > 占位"""
153142
cached = self._path_cache.get(name)
154143
if cached:
155144
return cached

core/base/pip_helper.py

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,4 @@
1-
"""pip 依赖安装辅助 — 插件/模块共用
2-
3-
设计目标 (对齐 nonebot 的"框架自装依赖", 且不挑环境):
4-
· 用 `sys.executable -m pip` 安装 → 天然装进"当前跑 bot 的这个 Python", 虚拟环境里也对;
5-
· site-packages 不可写时 (无 root / venv 属主为 root), 自动 `--target` 装到可写的
6-
.pydeps 目录并注入 sys.path, 无需 root, venv 内外均适用;
7-
· 多镜像兜底 (配置源失败自动换官方源重试);
8-
· pip 跑在独立单线程池里, 与事件循环 / DNS / Web 隔离, 装依赖不阻塞收发消息。
9-
"""
1+
"""pip 依赖安装辅助 — 插件/模块共用, 自动适配环境并多镜像兜底"""
102

113
import asyncio
124
import contextlib
@@ -24,9 +16,7 @@
2416

2517
log = get_logger(FRAMEWORK, '依赖安装')
2618

27-
# 当运行 bot 的 Python 环境 site-packages 不可写时 (无 root / venv 属主为 root, pip 报
28-
# Errno 13 Permission denied), 依赖改装到这个可写目录并注入 sys.path: 免 root、
29-
# venv 内外都能让插件 import 到 (venv 里 --user 会被 pip 拒绝, 故用 --target)。
19+
# site-packages 不可写时, 依赖 --target 装到此可写目录并注入 sys.path
3020
_ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
3121
_DEPS_DIR = os.path.join(_ROOT_DIR, '.pydeps')
3222

@@ -40,8 +30,7 @@ def _ensure_deps_dir_on_path():
4030
if os.path.isdir(_DEPS_DIR):
4131
_ensure_deps_dir_on_path()
4232

43-
# 独立单线程池: pip 是重 IO/CPU 的阻塞操作, 放这里跑绝不占用 asyncio 默认执行器
44-
# (默认执行器还兼着 DNS 解析、Web/统计查询等, 被 pip 占满会连带卡住整个面板)。
33+
# 独立单线程池: pip 阻塞操作不占用 asyncio 默认执行器
4534
_EXECUTOR = None
4635
_EXECUTOR_LOCK = threading.Lock()
4736

@@ -153,11 +142,7 @@ def all_requirements_met(req_path):
153142

154143

155144
def _mirror_attempts():
156-
"""返回按优先级排列的镜像参数列表; '' 表示官方源 (不带 -i)。
157-
158-
支持 settings.pip.mirrors (列表) 做多镜像兜底; 否则用 settings.pip.mirror
159-
(单个), 并追加官方源兜底。
160-
"""
145+
"""返回按优先级排列的镜像参数列表, '' 表示官方源"""
161146
mirrors = cfg.get('settings', 'pip.mirrors', None)
162147
if isinstance(mirrors, (list, tuple)) and mirrors:
163148
seq = [str(m).strip() for m in mirrors if str(m).strip()]

core/bot/event.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,6 @@ async def _on_event(self, event):
238238

239239
if et == GROUP_MESSAGE_CREATE and event.group_id:
240240
self._record_full_access_group(bot, event.group_id)
241-
if event.is_at_self and event.bot_member_role in ('admin', 'owner'):
242-
self._record_bot_admin(bot, event.group_id)
243241

244242
# 全量群 @全体成员 跳过
245243
if et == GROUP_MESSAGE_CREATE and event.is_at_all:
@@ -275,14 +273,6 @@ def _record_full_access_group(self, bot, group_id):
275273
(group_id, ts),
276274
)
277275

278-
def _record_bot_admin(self, bot, group_id):
279-
"""记录机器人在该群为管理员"""
280-
ts = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
281-
bot.log_service.db_queue(
282-
'INSERT OR REPLACE INTO group_bot_admin (group_id, updated_at) VALUES (?, ?)',
283-
(group_id, ts),
284-
)
285-
286276
def get_full_access_groups(self):
287277
"""从 data.db 拉取所有全量群记录"""
288278
bot = next(iter(self._bots.values()), None)

core/message/_http.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ async def _request(self, method, endpoint, **kwargs):
8282
return False, {'message': str(e), 'code': -1}
8383
return False, {'message': 'max retries', 'code': -1}
8484

85+
async def get_json(self, endpoint, **kwargs):
86+
return await self._request('GET', endpoint, **kwargs)
87+
8588
async def post_json(self, endpoint, payload):
8689
return await self._request('POST', endpoint, json=payload)
8790

core/message/bot_openid.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
"""Bot OpenID 缓存 — 记录各 appid 机器人的艾特 id, 持久化到 JSON。
2-
3-
全量环境下机器人真实 id (mentions[].id) 与 content 里 <@id> 的虚拟 id 可能不一致。
4-
首次「仅艾特机器人」时把两个来源的 id 都记下并标记 done; 之后直接按缓存移除, 不再判断。
5-
"""
1+
"""Bot OpenID 缓存 — 记录各 appid 机器人的艾特 id, 持久化到 JSON"""
62

73
import json
84
import os
@@ -65,6 +61,14 @@ def add(appid, openid):
6561
_save()
6662

6763

64+
def first_id(appid):
65+
"""取该 appid 的第一个 openid (按字典序), 用于请求群成员接口"""
66+
e = _data.get(appid)
67+
if not e or not e['ids']:
68+
return ''
69+
return sorted(e['ids'])[0]
70+
71+
6872
def is_done(appid):
6973
"""该 appid 是否已记录齐全, 无需再判断"""
7074
e = _data.get(appid)

core/message/event.py

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,6 @@ class Event:
207207
'sharer_id',
208208
'scene_param',
209209
'mentions',
210-
'bot_member_role',
211210
'is_at_self',
212211
'is_at_other_bot',
213212
'is_at_other_user',
@@ -261,7 +260,6 @@ def __init__(self):
261260
self.sharer_id = None
262261
self.scene_param = None
263262
self.mentions = []
264-
self.bot_member_role = ''
265263
self.is_at_self = False
266264
self.is_at_other_bot = False
267265
self.is_at_other_user = False
@@ -406,16 +404,10 @@ def needs_msg_id(self):
406404
def needs_event_id(self):
407405
return self.event_type in _EVENT_ID_TYPES
408406

409-
# ==================== 交互回调 (op12 ACK) ====================
410-
# 插件可在处理 INTERACTION_CREATE 时调用 event.set_callback_code(n) 自定义
411-
# 返回给客户端的状态码 (随 op12 ACK 一起发送, 不额外发 REST 请求)。
412-
# 未设置则框架在分发结束 / 超时后用默认 code 兜底。
407+
# ==================== 交互回调 (op12 ACK) — 插件可用 set_callback_code 自定义状态码 ====================
413408

414409
def start_ack_countdown(self):
415-
"""交互事件开始处理: 创建 ACK future 并启动默认超时定时器。
416-
417-
由传输层 (webhook / websocket) 在分发插件前调用。
418-
"""
410+
"""创建 ACK future 并启动超时定时器 (由传输层在分发前调用)"""
419411
loop = asyncio.get_running_loop()
420412
if self._ack_future is None:
421413
self._ack_future = loop.create_future()
@@ -462,9 +454,7 @@ async def wait_ack_code(self):
462454
return _DEFAULT_CALLBACK_CODE
463455
return await self._ack_future
464456

465-
# ==================== 发送代理 ====================
466-
# event.reply(...) → sender.reply(event, ...)
467-
# event.send_to_group(...) → sender.send_to_group(...)
457+
# ==================== 发送代理 (event.reply → sender.reply) ====================
468458

469459
@property
470460
def sender(self):

core/message/event_router.py

Lines changed: 0 additions & 64 deletions
This file was deleted.

core/message/keyboard.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,7 @@
55

66

77
def build_keyboard(button_rows, appid=None, *, font_size=None, style=None):
8-
"""按钮行列表 → QQ InlineKeyboard 结构
9-
10-
小按钮: 通过键盘级样式 content.style.font_size 控制按钮大小 (对应官方
11-
botgo CustomKeyboard.Style.FontSize), 取值 small / middle / large,
12-
small 即「小按钮」。两种用法:
13-
- 关键字: build_keyboard(rows, appid, font_size='small')
14-
或 reply(buttons=rows, button_font_size='small')
15-
- dict 包装: build_keyboard({'rows': rows, 'font_size': 'small'})
16-
"""
8+
"""按钮行列表 → QQ InlineKeyboard 结构, font_size 控制按钮大小 (small/middle/large)"""
179
button_enter_to_send = cfg.get_bot_setting(appid, 'message.button_enter_to_send', False) if appid else False
1810

1911
# 顶层 dict 包装: 携带键盘级样式, 同时兼容 rows/buttons/btns 取行

core/message/parsers.py

Lines changed: 0 additions & 56 deletions
This file was deleted.

0 commit comments

Comments
 (0)