nonebot / adapter-discord

NoneBot2 Discord 适配器 / Discord adapter for nonebot2
21 stars 8 forks source link

当发送图片消息时,从消息事件中获取的图片消息段中似乎不包含有效的 URL 信息 #43

Open jks15satoshi opened 1 month ago

jks15satoshi commented 1 month ago

版本信息:

Python 版本:3.11.9 NoneBot 版本:2.3.3 Discord 适配器版本:0.1.8

问题描述:

如以下示例代码:

from nonebot import on_message
from nonebot.adapters.discord.event import MessageEvent
from nonebot.internal.adapter.event import Event

matcher = on_message()

@matcher.handle()
async def handle_message_event(base_event: Event, message_event: MessageEvent) -> None:
    await matcher.send(base_event.get_message())

在 Discord 发送一条图片消息,在调试器中尝试从事件中获取消息,似乎在消息段中不包含图片的 URL 信息:

>>> base_event.get_message()
[AttachmentSegment(type='attachment', data={'attachment': AttachmentSend(filename='-_.png', description=None), 'file': None})]
>>> message_event.get_message()
[AttachmentSegment(type='attachment', data={'attachment': AttachmentSend(filename='-_.png', description=None), 'file': None})]

而直接执行此处理器会抛出异常:

10-10 11:06:48 [ERROR] nonebot | Running Matcher(type='message', module=echomod_msgrelay.core, lineno=70) failed.
Traceback (most recent call last):
  File "/home/jks15satoshi/.pyenv/versions/3.11.9/lib/python3.11/runpy.py", line 198, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/home/jks15satoshi/.pyenv/versions/3.11.9/lib/python3.11/runpy.py", line 88, in _run_code
    exec(code, run_globals)
  File "/home/jks15satoshi/.vscode-server/extensions/ms-python.debugpy-2024.10.0-linux-x64/bundled/libs/debugpy/adapter/../../debugpy/launcher/../../debugpy/__main__.py", line 39, in <module>
    cli.main()
  File "/home/jks15satoshi/.vscode-server/extensions/ms-python.debugpy-2024.10.0-linux-x64/bundled/libs/debugpy/adapter/../../debugpy/launcher/../../debugpy/../debugpy/server/cli.py", line 430, in main
    run()
  File "/home/jks15satoshi/.vscode-server/extensions/ms-python.debugpy-2024.10.0-linux-x64/bundled/libs/debugpy/adapter/../../debugpy/launcher/../../debugpy/../debugpy/server/cli.py", line 284, in run_file
    runpy.run_path(target, run_name="__main__")
  File "/home/jks15satoshi/.vscode-server/extensions/ms-python.debugpy-2024.10.0-linux-x64/bundled/libs/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 321, in run_path
    return _run_module_code(code, init_globals, run_name,
  File "/home/jks15satoshi/.vscode-server/extensions/ms-python.debugpy-2024.10.0-linux-x64/bundled/libs/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 135, in _run_module_code
    _run_code(code, mod_globals, init_globals,
  File "/home/jks15satoshi/.vscode-server/extensions/ms-python.debugpy-2024.10.0-linux-x64/bundled/libs/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 124, in _run_code
    exec(code, run_globals)
  File "/home/jks15satoshi/Documents/Git/echodev/echobot/bot.py", line 18, in <module>
    nonebot.run()
  File "/home/jks15satoshi/Documents/Git/echodev/echobot/.venv/lib/python3.11/site-packages/nonebot/__init__.py", line 335, in run
    get_driver().run(*args, **kwargs)
  File "/home/jks15satoshi/Documents/Git/echodev/echobot/.venv/lib/python3.11/site-packages/nonebot/drivers/fastapi.py", line 186, in run
    uvicorn.run(
  File "/home/jks15satoshi/Documents/Git/echodev/echobot/.venv/lib/python3.11/site-packages/uvicorn/main.py", line 579, in run
    server.run()
  File "/home/jks15satoshi/Documents/Git/echodev/echobot/.venv/lib/python3.11/site-packages/uvicorn/server.py", line 65, in run
    return asyncio.run(self.serve(sockets=sockets))
  File "/home/jks15satoshi/.pyenv/versions/3.11.9/lib/python3.11/asyncio/runners.py", line 190, in run
    return runner.run(main)
  File "/home/jks15satoshi/.pyenv/versions/3.11.9/lib/python3.11/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
  File "/home/jks15satoshi/Documents/Git/echodev/echobot/.venv/lib/python3.11/site-packages/nonebot/message.py", line 476, in check_and_run_matcher
    await _run_matcher(
> File "/home/jks15satoshi/Documents/Git/echodev/echobot/.venv/lib/python3.11/site-packages/nonebot/message.py", line 428, in _run_matcher
    await matcher.run(bot, event, state, stack, dependency_cache)
  File "/home/jks15satoshi/Documents/Git/echodev/echobot/.venv/lib/python3.11/site-packages/nonebot/internal/matcher/matcher.py", line 850, in run
    await self.simple_run(bot, event, state, stack, dependency_cache)
  File "/home/jks15satoshi/Documents/Git/echodev/echobot/.venv/lib/python3.11/site-packages/nonebot/internal/matcher/matcher.py", line 825, in simple_run
    await handler(
  File "/home/jks15satoshi/Documents/Git/echodev/echobot/.venv/lib/python3.11/site-packages/nonebot/dependencies/__init__.py", line 94, in __call__
    return await cast(Callable[..., Awaitable[R]], self.call)(**values)
  File "/home/jks15satoshi/Documents/Git/echodev/echomod/msgrelay/echomod_msgrelay/core.py", line 77, in handle_message_event
    await matcher.send(base_event.get_message())
  File "/home/jks15satoshi/Documents/Git/echodev/echobot/.venv/lib/python3.11/site-packages/nonebot/internal/matcher/matcher.py", line 566, in send
    return await bot.send(event=event, message=_message, **kwargs)
  File "/home/jks15satoshi/Documents/Git/echodev/echobot/.venv/lib/python3.11/site-packages/nonebot/adapters/discord/bot.py", line 249, in send
    return await self.create_message(
  File "/home/jks15satoshi/Documents/Git/echodev/echobot/.venv/lib/python3.11/site-packages/nonebot/internal/adapter/bot.py", line 122, in call_api
    raise exception
  File "/home/jks15satoshi/Documents/Git/echodev/echobot/.venv/lib/python3.11/site-packages/nonebot/internal/adapter/bot.py", line 97, in call_api
    result = await self.adapter._call_api(self, api, **data)
  File "/home/jks15satoshi/Documents/Git/echodev/echobot/.venv/lib/python3.11/site-packages/nonebot/adapters/discord/adapter.py", line 455, in _call_api
    return await api_handler(self, bot, **data)
  File "/home/jks15satoshi/Documents/Git/echodev/echobot/.venv/lib/python3.11/site-packages/nonebot/adapters/discord/api/handle.py", line 876, in _create_message
    return type_validate_python(MessageGet, await _request(adapter, bot, request))
  File "/home/jks15satoshi/Documents/Git/echodev/echobot/.venv/lib/python3.11/site-packages/nonebot/adapters/discord/api/request.py", line 39, in _request
    raise ActionFailed(data)
nonebot.adapters.discord.exception.ActionFailed: <ActionFailed: 400, code=50035, message=Invalid Form Body, data=None, errors={'attachments': {'0': {'id': {'_errors': [{'code': 'BASE_TYPE_REQUIRED', 'message': 'This field is required'}]}}}}>

事件信息如下(个人认为敏感的 ID 数据使用 *** 替换):

>>> base_event
GuildMessageCreateEvent(
    id=***, 
    channel_id=***,
    author=User(
        id=***, username='jks15satoshi', discriminator='0', global_name='Satoshi Jek', 
        avatar='6750c14af941fd419ca09fa72ca18f3f', bot=<UNSET>, system=<UNSET>, mfa_enabled=<UNSET>, 
        banner=<UNSET>, accent_color=<UNSET>, locale=<UNSET>, verified=<UNSET>, email=<UNSET>, 
        flags=<UNSET>, premium_type=<UNSET>, public_flags=<UserFlags: 0>, avatar_decoration=<UNSET>
    ), 
    content='', 
    timestamp=datetime.datetime(2024, 10, 10, 3, 24, 55, 511000, tzinfo=TzInfo(UTC)), 
    edited_timestamp=None, 
    tts=False, 
    mention_everyone=False, 
    mentions=[], 
    mention_roles=[], 
    mention_channels=<UNSET>, 
    attachments=[
        Attachment(
           id='***', filename='-_.png', description=<UNSET>, content_type='image/png', size=6772914, 
           url='https://cdn.discordapp.com/attachments/***/***/-_.png?ex=67089a87&is=67074907&hm=5036e7a9d7f5584696e9fc173a846cfcb383d11a2939127f19277de50a68b1e8&', 
           proxy_url='https://media.discordapp.net/attachments/***/***/-_.png?ex=67089a87&is=67074907&hm=5036e7a9d7f5584696e9fc173a846cfcb383d11a2939127f19277de50a68b1e8&', 
           height=1272, width=2158, ephemeral=<UNSET>, duration_secs=<UNSET>, waveform=<UNSET>
        )
    ], 
    embeds=[], 
    reactions=<UNSET>, 
    nonce='1293776249528778752', 
    pinned=False, 
    webhook_id=<UNSET>, 
    type=<MessageType.DEFAULT: 0>, 
    activity=<UNSET>, 
    application=<UNSET>, 
    application_id=<UNSET>, 
    message_reference=<UNSET>, 
    flags=<MessageFlag: 0>, 
    referenced_message=<UNSET>, 
    interaction=<UNSET>, 
    thread=<UNSET>, 
    components=[], 
    sticker_items=<UNSET>, 
    stickers=<UNSET>, 
    position=<UNSET>, 
    role_subscription_data=<UNSET>, 
    guild_id=***, 
    member=GuildMember(
        user=<UNSET>, nick='Satoshi Jek', avatar=None, roles=[***], 
        joined_at=datetime.datetime(2021, 1, 31, 15, 49, 57, 777000, tzinfo=TzInfo(UTC)), 
        premium_since=None, deaf=False, mute=False, flags=<GuildMemberFlags: 0>, pending=False, 
        permissions=<UNSET>, communication_disabled_until=None
    ), 
    to_me=False, 
    reply=None
)
jks15satoshi commented 1 month ago

大概看了一下源码,MessageSegment.attachment 接受一个可选的 bytes 类型参数 content 表示附件内容:

https://github.com/nonebot/adapter-discord/blob/5794c0b53b4fe1f41726fd09b4db1a5239ef965b/nonebot/adapters/discord/message.py#L46-L51

但奇怪的是 Message.from_guild_message 构造附件消息段时却似乎没有传过这个参数:

https://github.com/nonebot/adapter-discord/blob/5794c0b53b4fe1f41726fd09b4db1a5239ef965b/nonebot/adapters/discord/message.py#L438-L451

Autuamn commented 1 month ago

我想因为 AttachmentSegment 是给发送用的,不能从 url 发送文件,就没有预留传入 url 的地方

jks15satoshi commented 1 month ago

我想因为 AttachmentSegment 是给发送用的,不能从 url 发送文件,就没有预留传入 url 的地方

可以理解。不过确实会有通过 AttachmentSegment 获取到 URL 的需求(比如 Alconna 插件的 UniMessage 就是统一通过 event.get_message 获取消息内容,目前的实现就不能从消息段中拿到附件的 URL,实现跨频道或者跨平台消息转发就会有问题),所以我在想是不是应该允许接受一个 URL 参数比较合适,这样做跨平台兼容也会方便一些;至于发送带有 URL 参数的附件消息段时,在 api.utils.parse_data 或在任意构造创建消息事件前,请求 URL 获取附件的二进制数据(拍脑袋的想法,不见得合适)。