/adapter-discord

NoneBot2 Discord 适配器 / Discord adapter for nonebot2

Primary LanguagePython

nonebot-adapter-discord

NoneBot-Adapter-Discord

✨ Discord 协议适配 ✨

安装

通过 nb adapter install nonebot-adapter-discord 安装本适配器。

或在 nb create 创建项目时选择 Discord 适配器。

可通过 pip install git+https://github.com/nonebot/adapter-discord.git@master 安装开发中版本。

由于 Discord 文档存在部分表述不清的地方,并且结构复杂,存在很多 partial object, 需要更多实际测试以找出问题,欢迎提出 ISSUE 反馈。

配置

修改 NoneBot 配置文件 .env 或者 .env.*

Driver

参考 driver 配置项,添加 ForwardDriver 支持。

如:

DRIVER=~httpx+~websockets

DISCORD_BOTS

配置机器人帐号,如:

DISCORD_BOTS='
[
  {
    "token": "xxx",
    "intent": {
      "guild_messages": true,
      "direct_messages": true
    },
    "application_commands": {"*": ["*"]}
  }
]
'

# application_commands的{"*": ["*"]}代表将全部应用命令注册为全局应用命令
# {"admin": ["123", "456"]}则代表将admin命令注册为id是123、456服务器的局部命令,其余命令不注册

DISCORD_COMPRESS

是否启用数据压缩,默认为 False,如:

DISCORD_COMPRESS=True

DISCORD_API_VERSION

Discord API 版本,默认为 10,如:

DISCORD_API_VERSION=10

DISCORD_API_TIMEOUT

Discord API 超时时间,默认为 30 秒,如:

DISCORD_API_TIMEOUT=15.0

DISCORD_HANDLE_SELF_MESSAGE

是否处理自己发送的消息,默认为 False,如:

DISCORD_HANDLE_SELF_MESSAGE=True

DISCORD_PROXY

代理设置,默认无,如:

DISCORD_PROXY='http://127.0.0.1:6666'

插件示例

以下是一个简单的插件示例,展示各种消息段:

import datetime

from nonebot import on_command
from nonebot.params import CommandArg

from nonebot.adapters.discord import Bot, MessageEvent, MessageSegment, Message
from nonebot.adapters.discord.api import *

matcher = on_command('send')


@matcher.handle()
async def ready(bot: Bot, event: MessageEvent, msg: Message = CommandArg()):
    # 调用discord的api
    self_info = await bot.get_current_user()  # 获取机器人自身信息
    user = await bot.get_user(user_id=event.user_id)  # 获取指定用户信息
    ...
    # 各种消息段
    msg = msg.extract_plain_text()
    if msg == 'mention_me':
        # 发送一个提及你的消息
        await matcher.finish(MessageSegment.mention_user(event.user_id))
    elif msg == 'time':
        # 发送一个时间,使用相对时间(RelativeTime)样式
        await matcher.finish(MessageSegment.timestamp(datetime.datetime.now(),
                                                      style=TimeStampStyle.RelativeTime))
    elif msg == 'mention_everyone':
        # 发送一个提及全体成员的消息
        await matcher.finish(MessageSegment.mention_everyone())
    elif msg == 'mention_channel':
        # 发送一个提及当前频道的消息
        await matcher.finish(MessageSegment.mention_channel(event.channel_id))
    elif msg == 'embed':
        # 发送一个嵌套消息,其中包含a embed标题,nonebot logo描述和来自网络url的logo图片
        await matcher.finish(MessageSegment.embed(
            Embed(title='a embed',
                  type=EmbedTypes.image,
                  description='nonebot logo',
                  image=EmbedImage(
                      url='https://v2.nonebot.dev/logo.png'))))
    elif msg == 'attachment':
        # 发送一个附件,其中包含来自本地的logo.png图片
        with open('logo.png', 'rb') as f:
            await matcher.finish(MessageSegment.attachment(file='logo.png',
                                                           content=f.read()))
    elif msg == 'component':
        # 发送一个复杂消息,其中包含一个当前时间,一个字符串选择菜单,一个用户选择菜单和一个按钮
        time_now = MessageSegment.timestamp(datetime.datetime.now())
        string_select = MessageSegment.component(
            SelectMenu(type=ComponentType.StringSelect,
                       custom_id='string select',
                       placeholder='select a value',
                       options=[
                           SelectOption(label='A', value='a'),
                           SelectOption(label='B', value='b'),
                           SelectOption(label='C', value='c')]))
        select = MessageSegment.component(SelectMenu(
            type=ComponentType.UserInput,
            custom_id='user_input',
            placeholder='please select a user'))
        button = MessageSegment.component(
            Button(label='button',
                   custom_id='button',
                   style=ButtonStyle.Primary))
        await matcher.finish('now time:' + time_now + string_select + select + button)
    else:
        # 发送一个文本消息
        await matcher.finish(MessageSegment.text(msg))

以下是一个 Discord 斜杠命令的插件示例:

import asyncio
from typing import Optional

from nonebot.adapters.discord.api import (
    IntegerOption,
    NumberOption,
    StringOption,
    SubCommandOption,
    User,
    UserOption,
)
from nonebot.adapters.discord.commands import (
    CommandOption,
    on_slash_command,
)

matcher = on_slash_command(
    name="permission",
    description="权限管理",
    options=[
        SubCommandOption(
            name="add",
            description="添加",
            options=[
                StringOption(
                    name="plugin",
                    description="插件名",
                    required=True,
                ),
                IntegerOption(
                    name="priority",
                    description="优先级",
                    required=False,
                ),
            ],
        ),
        SubCommandOption(
            name="remove",
            description="移除",
            options=[
                StringOption(name="plugin", description="插件名", required=True),
                NumberOption(name="time", description="时长", required=False),
            ],
        ),
        SubCommandOption(
            name="ban",
            description="禁用",
            options=[
                UserOption(name="user", description="用户", required=False),
            ],
        ),
    ],
)


@matcher.handle_sub_command("add")
async def handle_user_add(
    plugin: CommandOption[str], priority: CommandOption[Optional[int]]
):
    await matcher.send_deferred_response()
    await asyncio.sleep(2)
    await matcher.edit_response(f"你添加了插件 {plugin},优先级 {priority}")
    await asyncio.sleep(2)
    fm = await matcher.send_followup_msg(
        f"你添加了插件 {plugin},优先级 {priority} (新消息)"
    )
    await asyncio.sleep(2)
    await matcher.edit_followup_msg(
        fm.id, f"你添加了插件 {plugin},优先级 {priority} (新消息修改后)"
    )


@matcher.handle_sub_command("remove")
async def handle_user_remove(
    plugin: CommandOption[str], time: CommandOption[Optional[float]]
):
    await matcher.send(f"你移除了插件 {plugin},时长 {time}")


@matcher.handle_sub_command("ban")
async def handle_admin_ban(user: CommandOption[User]):
    await matcher.finish(f"你禁用了用户 {user.username}")