<p align="center">
<a href="https://nonebot.dev/"><img src="https://raw.githubusercontent.com/nonebot/adapter-discord/master/assets/logo.png" width="200" height="200" alt="nonebot-adapter-discord"></a>
</p>
<div align="center">
# NoneBot-Adapter-Discord
_✨ Discord 协议适配 ✨_
</div>
## 安装
通过 `nb adapter install nonebot-adapter-discord` 安装本适配器。
或在 `nb create` 创建项目时选择 `Discord` 适配器。
可通过 `pip install git+https://github.com/nonebot/adapter-discord.git@master` 安装开发中版本。
由于 [Discord 文档](https://discord.com/developers/docs/intro)存在部分表述不清的地方,并且结构复杂,存在很多 `partial object`,
需要更多实际测试以找出问题,欢迎提出 ISSUE 反馈。
## 配置
修改 NoneBot 配置文件 `.env` 或者 `.env.*`。
### Driver
参考 [driver](https://v2.nonebot.dev/docs/tutorial/configuration#driver) 配置项,添加 `ForwardDriver` 支持。
如:
```dotenv
DRIVER=~httpx+~websockets
```
### DISCORD_BOTS
配置机器人帐号,如:
```dotenv
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`,如:
```dotenv
DISCORD_COMPRESS=True
```
### DISCORD_API_VERSION
Discord API 版本,默认为 `10`,如:
```dotenv
DISCORD_API_VERSION=10
```
### DISCORD_API_TIMEOUT
Discord API 超时时间,默认为 `30` 秒,如:
```dotenv
DISCORD_API_TIMEOUT=15.0
```
### DISCORD_HANDLE_SELF_MESSAGE
是否处理自己发送的消息,默认为 `False`,如:
```dotenv
DISCORD_HANDLE_SELF_MESSAGE=True
```
### DISCORD_PROXY
代理设置,默认无,如:
```dotenv
DISCORD_PROXY='http://127.0.0.1:6666'
```
## 插件示例
以下是一个简单的插件示例,展示各种消息段:
```python
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 斜杠命令的插件示例:
```python
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}")
```
Raw data
{
"_id": null,
"home_page": "https://github.com/nonebot/adapter-discord",
"name": "nonebot-adapter-discord",
"maintainer": null,
"docs_url": null,
"requires_python": "<4.0,>=3.9",
"maintainer_email": null,
"keywords": "nonebot, discord, bot",
"author": "CMHopeSunshine",
"author_email": "277073121@qq.com",
"download_url": "https://files.pythonhosted.org/packages/46/fc/2834ee3648ea7dae98417ff022bc7c1fe9a00160a07f9f9c0619541d3709/nonebot_adapter_discord-0.1.8.tar.gz",
"platform": null,
"description": "<p align=\"center\">\n <a href=\"https://nonebot.dev/\"><img src=\"https://raw.githubusercontent.com/nonebot/adapter-discord/master/assets/logo.png\" width=\"200\" height=\"200\" alt=\"nonebot-adapter-discord\"></a>\n</p>\n\n<div align=\"center\">\n\n# NoneBot-Adapter-Discord\n\n_\u2728 Discord \u534f\u8bae\u9002\u914d \u2728_\n\n</div>\n\n## \u5b89\u88c5\n\n\u901a\u8fc7 `nb adapter install nonebot-adapter-discord` \u5b89\u88c5\u672c\u9002\u914d\u5668\u3002\n\n\u6216\u5728 `nb create` \u521b\u5efa\u9879\u76ee\u65f6\u9009\u62e9 `Discord` \u9002\u914d\u5668\u3002\n\n\u53ef\u901a\u8fc7 `pip install git+https://github.com/nonebot/adapter-discord.git@master` \u5b89\u88c5\u5f00\u53d1\u4e2d\u7248\u672c\u3002\n\n\u7531\u4e8e [Discord \u6587\u6863](https://discord.com/developers/docs/intro)\u5b58\u5728\u90e8\u5206\u8868\u8ff0\u4e0d\u6e05\u7684\u5730\u65b9\uff0c\u5e76\u4e14\u7ed3\u6784\u590d\u6742\uff0c\u5b58\u5728\u5f88\u591a `partial object`\uff0c\n\u9700\u8981\u66f4\u591a\u5b9e\u9645\u6d4b\u8bd5\u4ee5\u627e\u51fa\u95ee\u9898\uff0c\u6b22\u8fce\u63d0\u51fa ISSUE \u53cd\u9988\u3002\n\n## \u914d\u7f6e\n\n\u4fee\u6539 NoneBot \u914d\u7f6e\u6587\u4ef6 `.env` \u6216\u8005 `.env.*`\u3002\n\n### Driver\n\n\u53c2\u8003 [driver](https://v2.nonebot.dev/docs/tutorial/configuration#driver) \u914d\u7f6e\u9879\uff0c\u6dfb\u52a0 `ForwardDriver` \u652f\u6301\u3002\n\n\u5982\uff1a\n\n```dotenv\nDRIVER=~httpx+~websockets\n```\n\n### DISCORD_BOTS\n\n\u914d\u7f6e\u673a\u5668\u4eba\u5e10\u53f7\uff0c\u5982\uff1a\n\n```dotenv\nDISCORD_BOTS='\n[\n {\n \"token\": \"xxx\",\n \"intent\": {\n \"guild_messages\": true,\n \"direct_messages\": true\n },\n \"application_commands\": {\"*\": [\"*\"]}\n }\n]\n'\n\n# application_commands\u7684{\"*\": [\"*\"]}\u4ee3\u8868\u5c06\u5168\u90e8\u5e94\u7528\u547d\u4ee4\u6ce8\u518c\u4e3a\u5168\u5c40\u5e94\u7528\u547d\u4ee4\n# {\"admin\": [\"123\", \"456\"]}\u5219\u4ee3\u8868\u5c06admin\u547d\u4ee4\u6ce8\u518c\u4e3aid\u662f123\u3001456\u670d\u52a1\u5668\u7684\u5c40\u90e8\u547d\u4ee4\uff0c\u5176\u4f59\u547d\u4ee4\u4e0d\u6ce8\u518c\n```\n\n### DISCORD_COMPRESS\n\n\u662f\u5426\u542f\u7528\u6570\u636e\u538b\u7f29\uff0c\u9ed8\u8ba4\u4e3a `False`\uff0c\u5982\uff1a\n\n```dotenv\nDISCORD_COMPRESS=True\n```\n\n### DISCORD_API_VERSION\n\nDiscord API \u7248\u672c\uff0c\u9ed8\u8ba4\u4e3a `10`\uff0c\u5982\uff1a\n\n```dotenv\nDISCORD_API_VERSION=10\n```\n\n### DISCORD_API_TIMEOUT\n\nDiscord API \u8d85\u65f6\u65f6\u95f4\uff0c\u9ed8\u8ba4\u4e3a `30` \u79d2\uff0c\u5982\uff1a\n\n```dotenv\nDISCORD_API_TIMEOUT=15.0\n```\n\n### DISCORD_HANDLE_SELF_MESSAGE\n\n\u662f\u5426\u5904\u7406\u81ea\u5df1\u53d1\u9001\u7684\u6d88\u606f\uff0c\u9ed8\u8ba4\u4e3a `False`\uff0c\u5982\uff1a\n\n```dotenv\nDISCORD_HANDLE_SELF_MESSAGE=True\n```\n\n### DISCORD_PROXY\n\n\u4ee3\u7406\u8bbe\u7f6e\uff0c\u9ed8\u8ba4\u65e0\uff0c\u5982\uff1a\n\n```dotenv\nDISCORD_PROXY='http://127.0.0.1:6666'\n```\n\n## \u63d2\u4ef6\u793a\u4f8b\n\n\u4ee5\u4e0b\u662f\u4e00\u4e2a\u7b80\u5355\u7684\u63d2\u4ef6\u793a\u4f8b\uff0c\u5c55\u793a\u5404\u79cd\u6d88\u606f\u6bb5\uff1a\n\n```python\nimport datetime\n\nfrom nonebot import on_command\nfrom nonebot.params import CommandArg\n\nfrom nonebot.adapters.discord import Bot, MessageEvent, MessageSegment, Message\nfrom nonebot.adapters.discord.api import *\n\nmatcher = on_command('send')\n\n\n@matcher.handle()\nasync def ready(bot: Bot, event: MessageEvent, msg: Message = CommandArg()):\n # \u8c03\u7528discord\u7684api\n self_info = await bot.get_current_user() # \u83b7\u53d6\u673a\u5668\u4eba\u81ea\u8eab\u4fe1\u606f\n user = await bot.get_user(user_id=event.user_id) # \u83b7\u53d6\u6307\u5b9a\u7528\u6237\u4fe1\u606f\n ...\n # \u5404\u79cd\u6d88\u606f\u6bb5\n msg = msg.extract_plain_text()\n if msg == 'mention_me':\n # \u53d1\u9001\u4e00\u4e2a\u63d0\u53ca\u4f60\u7684\u6d88\u606f\n await matcher.finish(MessageSegment.mention_user(event.user_id))\n elif msg == 'time':\n # \u53d1\u9001\u4e00\u4e2a\u65f6\u95f4\uff0c\u4f7f\u7528\u76f8\u5bf9\u65f6\u95f4\uff08RelativeTime\uff09\u6837\u5f0f\n await matcher.finish(MessageSegment.timestamp(datetime.datetime.now(),\n style=TimeStampStyle.RelativeTime))\n elif msg == 'mention_everyone':\n # \u53d1\u9001\u4e00\u4e2a\u63d0\u53ca\u5168\u4f53\u6210\u5458\u7684\u6d88\u606f\n await matcher.finish(MessageSegment.mention_everyone())\n elif msg == 'mention_channel':\n # \u53d1\u9001\u4e00\u4e2a\u63d0\u53ca\u5f53\u524d\u9891\u9053\u7684\u6d88\u606f\n await matcher.finish(MessageSegment.mention_channel(event.channel_id))\n elif msg == 'embed':\n # \u53d1\u9001\u4e00\u4e2a\u5d4c\u5957\u6d88\u606f\uff0c\u5176\u4e2d\u5305\u542ba embed\u6807\u9898\uff0cnonebot logo\u63cf\u8ff0\u548c\u6765\u81ea\u7f51\u7edcurl\u7684logo\u56fe\u7247\n await matcher.finish(MessageSegment.embed(\n Embed(title='a embed',\n type=EmbedTypes.image,\n description='nonebot logo',\n image=EmbedImage(\n url='https://v2.nonebot.dev/logo.png'))))\n elif msg == 'attachment':\n # \u53d1\u9001\u4e00\u4e2a\u9644\u4ef6\uff0c\u5176\u4e2d\u5305\u542b\u6765\u81ea\u672c\u5730\u7684logo.png\u56fe\u7247\n with open('logo.png', 'rb') as f:\n await matcher.finish(MessageSegment.attachment(file='logo.png',\n content=f.read()))\n elif msg == 'component':\n # \u53d1\u9001\u4e00\u4e2a\u590d\u6742\u6d88\u606f\uff0c\u5176\u4e2d\u5305\u542b\u4e00\u4e2a\u5f53\u524d\u65f6\u95f4\uff0c\u4e00\u4e2a\u5b57\u7b26\u4e32\u9009\u62e9\u83dc\u5355\uff0c\u4e00\u4e2a\u7528\u6237\u9009\u62e9\u83dc\u5355\u548c\u4e00\u4e2a\u6309\u94ae\n time_now = MessageSegment.timestamp(datetime.datetime.now())\n string_select = MessageSegment.component(\n SelectMenu(type=ComponentType.StringSelect,\n custom_id='string select',\n placeholder='select a value',\n options=[\n SelectOption(label='A', value='a'),\n SelectOption(label='B', value='b'),\n SelectOption(label='C', value='c')]))\n select = MessageSegment.component(SelectMenu(\n type=ComponentType.UserInput,\n custom_id='user_input',\n placeholder='please select a user'))\n button = MessageSegment.component(\n Button(label='button',\n custom_id='button',\n style=ButtonStyle.Primary))\n await matcher.finish('now time:' + time_now + string_select + select + button)\n else:\n # \u53d1\u9001\u4e00\u4e2a\u6587\u672c\u6d88\u606f\n await matcher.finish(MessageSegment.text(msg))\n```\n\n\u4ee5\u4e0b\u662f\u4e00\u4e2a Discord \u659c\u6760\u547d\u4ee4\u7684\u63d2\u4ef6\u793a\u4f8b\uff1a\n\n```python\nimport asyncio\nfrom typing import Optional\n\nfrom nonebot.adapters.discord.api import (\n IntegerOption,\n NumberOption,\n StringOption,\n SubCommandOption,\n User,\n UserOption,\n)\nfrom nonebot.adapters.discord.commands import (\n CommandOption,\n on_slash_command,\n)\n\nmatcher = on_slash_command(\n name=\"permission\",\n description=\"\u6743\u9650\u7ba1\u7406\",\n options=[\n SubCommandOption(\n name=\"add\",\n description=\"\u6dfb\u52a0\",\n options=[\n StringOption(\n name=\"plugin\",\n description=\"\u63d2\u4ef6\u540d\",\n required=True,\n ),\n IntegerOption(\n name=\"priority\",\n description=\"\u4f18\u5148\u7ea7\",\n required=False,\n ),\n ],\n ),\n SubCommandOption(\n name=\"remove\",\n description=\"\u79fb\u9664\",\n options=[\n StringOption(name=\"plugin\", description=\"\u63d2\u4ef6\u540d\", required=True),\n NumberOption(name=\"time\", description=\"\u65f6\u957f\", required=False),\n ],\n ),\n SubCommandOption(\n name=\"ban\",\n description=\"\u7981\u7528\",\n options=[\n UserOption(name=\"user\", description=\"\u7528\u6237\", required=False),\n ],\n ),\n ],\n)\n\n\n@matcher.handle_sub_command(\"add\")\nasync def handle_user_add(\n plugin: CommandOption[str], priority: CommandOption[Optional[int]]\n):\n await matcher.send_deferred_response()\n await asyncio.sleep(2)\n await matcher.edit_response(f\"\u4f60\u6dfb\u52a0\u4e86\u63d2\u4ef6 {plugin}\uff0c\u4f18\u5148\u7ea7 {priority}\")\n await asyncio.sleep(2)\n fm = await matcher.send_followup_msg(\n f\"\u4f60\u6dfb\u52a0\u4e86\u63d2\u4ef6 {plugin}\uff0c\u4f18\u5148\u7ea7 {priority} (\u65b0\u6d88\u606f)\"\n )\n await asyncio.sleep(2)\n await matcher.edit_followup_msg(\n fm.id, f\"\u4f60\u6dfb\u52a0\u4e86\u63d2\u4ef6 {plugin}\uff0c\u4f18\u5148\u7ea7 {priority} (\u65b0\u6d88\u606f\u4fee\u6539\u540e)\"\n )\n\n\n@matcher.handle_sub_command(\"remove\")\nasync def handle_user_remove(\n plugin: CommandOption[str], time: CommandOption[Optional[float]]\n):\n await matcher.send(f\"\u4f60\u79fb\u9664\u4e86\u63d2\u4ef6 {plugin}\uff0c\u65f6\u957f {time}\")\n\n\n@matcher.handle_sub_command(\"ban\")\nasync def handle_admin_ban(user: CommandOption[User]):\n await matcher.finish(f\"\u4f60\u7981\u7528\u4e86\u7528\u6237 {user.username}\")\n```\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Discord adapter for nonebot2",
"version": "0.1.8",
"project_urls": {
"Documentation": "https://github.com/nonebot/adapter-discord",
"Homepage": "https://github.com/nonebot/adapter-discord",
"Repository": "https://github.com/nonebot/adapter-discord"
},
"split_keywords": [
"nonebot",
" discord",
" bot"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "80af67716a868ee65c4dd7ab0f0cd24181dce6c856786efd3837c2274afb884d",
"md5": "b026e7c0d8fe5933105ea8dc002c212c",
"sha256": "d063bf524f6a75c5c123f2d04227e0ec62c2433f56b28fb92fa5eb2aebef1c16"
},
"downloads": -1,
"filename": "nonebot_adapter_discord-0.1.8-py3-none-any.whl",
"has_sig": false,
"md5_digest": "b026e7c0d8fe5933105ea8dc002c212c",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4.0,>=3.9",
"size": 78480,
"upload_time": "2024-06-23T14:42:27",
"upload_time_iso_8601": "2024-06-23T14:42:27.537266Z",
"url": "https://files.pythonhosted.org/packages/80/af/67716a868ee65c4dd7ab0f0cd24181dce6c856786efd3837c2274afb884d/nonebot_adapter_discord-0.1.8-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "46fc2834ee3648ea7dae98417ff022bc7c1fe9a00160a07f9f9c0619541d3709",
"md5": "f75867840fd9760fcd8b6f73fd7ffbbc",
"sha256": "5d3a7a8e0ab23b7ae84551b479c40c5d09733b15d09538d64765c5af54721781"
},
"downloads": -1,
"filename": "nonebot_adapter_discord-0.1.8.tar.gz",
"has_sig": false,
"md5_digest": "f75867840fd9760fcd8b6f73fd7ffbbc",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4.0,>=3.9",
"size": 75182,
"upload_time": "2024-06-23T14:42:29",
"upload_time_iso_8601": "2024-06-23T14:42:29.321012Z",
"url": "https://files.pythonhosted.org/packages/46/fc/2834ee3648ea7dae98417ff022bc7c1fe9a00160a07f9f9c0619541d3709/nonebot_adapter_discord-0.1.8.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-06-23 14:42:29",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "nonebot",
"github_project": "adapter-discord",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "nonebot-adapter-discord"
}