如何使用Python构建一个用于故事讲述、聊天以及促进心理健康的简单Discord机器人
当看到Discord机器人实际运行时,它们会显得相当复杂。这些机器人能够回复消息、讲述故事、记住对话内容,并且可以全天候保持在线状态。 当我最初开始研究它们的工作原理时,我认为背后肯定有大量复杂的代码在支撑着这一切。 但实际上,其基本原理其实非常简单。 从根本上说,Discord机器人只是一个Python程序,它连接到Discord平台,等待某些事件发生,然后决定该如何做出响应。一旦你理解了这个基本概念,就可以逐步为它添加各种功能,从而将一个简单的机器人发展成更有趣的东西。 在这个教程中,我们将从一个非常基础的机器人开始,逐渐将其开发成功能更强大的工具。在这个过程中,你将会学习到关于Disco
当看到Discord机器人实际运行时,它们会显得相当复杂。这些机器人能够回复消息、讲述故事、记住对话内容,并且可以全天候保持在线状态。
当我最初开始研究它们的工作原理时,我认为背后肯定有大量复杂的代码在支撑着这一切。
但实际上,其基本原理其实非常简单。
从根本上说,Discord机器人只是一个Python程序,它连接到Discord平台,等待某些事件发生,然后决定该如何做出响应。一旦你理解了这个基本概念,就可以逐步为它添加各种功能,从而将一个简单的机器人发展成更有趣的东西。
在这个教程中,我们将从一个非常基础的机器人开始,逐渐将其开发成功能更强大的工具。在这个过程中,你将会学习到关于Discord命令、事件、异步编程、用户状态管理、环境变量以及基本部署方法的知识。
在开始之前,需要先说明一点:在这个项目中,我们将在机器人中加入的心理健康支持功能并非真正的心理治疗,这个机器人也不是治疗师或医疗专业人士。它所提供的仅仅是一些建议,目的是帮助用户,在适当的时候鼓励他们向可信任的人寻求帮助。
好了,现在让我们开始编程吧!
我们将学习的内容:
我们正在开发什么
当我们完成这个机器人的开发后,它将能够执行多种指令:
!hello
!story
!chat hello!
!support 我今天压力很大
!help
例如:
用户:
!story
机器人:
你醒来时发现自己身处一座废弃的图书馆里。
面前有三扇门:
1. 一扇红色的木门
2. 一扇布满奇怪符号的金属门
3. 一条通往地下的楼梯
你会选择哪一扇门呢?
之后用户就可以继续这个故事的发展了。
对于聊天功能:
用户:
!chat 学习Python有什么好方法吗?
机器人:
试着先做一些小项目,而不仅仅是阅读教程。其实开发一个Discord机器人也是一个很有趣的项目起点。
而对于提供心理辅导的功能:
用户:
!support 我因为学业压力很大而感到非常焦虑。
机器人:
听起来你的负担确实很重。你可以尝试把任务分解成一个个小部分来完成,每完成一个部分后就休息一会儿。
我只是一个机器人,并不是心理治疗师,所以如果你需要专业的帮助,建议找你信任的人聊聊。
我们的目标并不是创造出一个具有神奇功能的机器人治疗师,而是通过这个项目来学习如何运用Discord的API、Python函数、事件处理机制、异步编程技术以及基本的对话逻辑。
你需要什么
你只需要准备几样东西就可以了:
Python(建议使用3.8及以上版本)
一个Discord账户
一个允许你添加机器人的Discord服务器
一个代码编辑器(我个人推荐VS Code或PyCharm)
`discord.py`这个库
我们还会使用Python内置的`os`模块来读取环境变量。
如果你还没有安装Python,可以从官方网站下载当前支持的版本进行安装。
然后检查Python是否能够正常运行:
python --version
你应该会看到类似这样的输出:
Python 3.x.x
创建Discord机器人
在让Python控制Discord之前,我们首先需要创建一个Discord应用程序。
请访问Discord开发者门户:https://discord.com/developers/applications
页面会显示这样的内容,在开始操作之前,你可能需要使用自己的Discord邮箱/用户名和密码登录。
点击右上角的“新建应用程序”按钮,然后为你的机器人起一个名字。在这个教程中,我们就把我们的机器人命名为`StoryBot`吧。
这个应用实际上就是你的机器人的主界面。
Discord的开发平台提供了创建和配置应用程序及机器人所需的所有工具。
创建好应用程序后,打开其机器人设置部分,然后创建机器人的用户账户。如果你愿意,还可以为自己的机器人添加自定义图标和横幅。
接下来进入令牌设置页面,点击“重置令牌”来生成令牌。请将这个令牌视为密码,绝对不要直接将其写入Python源代码中。
千万不要这样做:
bot.run("my-secret-token")
也绝对不要将令牌上传到GitHub或提交到版本控制系统中。相反,我们应该将其存储在环境变量中,具体方法我们稍后会讨论。
授予机器人读取消息的权限
我们的机器人需要能够查看那些包含指令的消息。
Discord使用一种称为网关意图的功能来控制机器人能够接收哪些类型的事件。discord.py的文档说明指出,这些意图必须在你的代码中启用;对于某些高级意图,还需要在Discord开发者门户中进行配置。
在开发者门户中,找到以下选项:
机器人
→ 高级网关意图
将其启用:
消息内容意图
设置完成后,页面应该会显示成这样的样子:
我们也会在Python代码中启用这个功能,具体步骤会在本文后面介绍。
创建项目
首先创建一个文件夹:
discord-story-bot/
在这个文件夹里,最终会包含以下文件:
discord-story-bot/
│
├── bot.py
├── requirements.txt
└── .env
其中三个重要的文件是:
bot.py:我们的Python程序requirements.txt:包含机器人所需包名的文本文件.env:本地开发时使用的秘密令牌文件
创建虚拟环境
在项目文件夹中打开终端。
运行以下命令:
python -m venv venv
然后激活该虚拟环境。
在 Windows 上:
venv\Scripts\activate
在 macOS/Linux 上:
source venv/bin/activate
虚拟环境的存在使得这个项目拥有自己独立的 Python 环境。
这样一来,为这个机器人安装的包就不会影响到其他项目所使用的包。
安装 discord.py
现在来安装 Discord 库:
pip install -U discord.py
官方的 discord.py 文档也是建议使用这种方式来安装该库的。
我们还会安装 python-dotenv,这个工具可以让我们更方便地读取本地的 .env 文件:
pip install python-dotenv
之后保存所需的依赖项:
pip freeze > requirements.txt
你的 requirements.txt 文件中应该会列出该项目所需的所有包。
创建你的第一个机器人
让我们从简单的例子开始吧。
打开 bot.py 文件:
import os
import discord
from discord.ext import commands
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.getenv("DISCORD_TOKEN")
intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot(
command_prefix="!",
intents=intents
)
@bot.event
async def on_ready():
print(f"已以 {bot.user} 的身份登录。")
@bot.command()
async def hello(ctx):
await ctx.send("Hello! 我在线呢。")
bot.run(TOKEN)
这样就已经创建了一个可以正常运行的 Discord 机器人了。
让我们一步步来分析这个代码吧。
导入所需的库
首先:
import os
os 模块允许 Python 与操作系统进行交互。
我们会用它来读取环境变量。
接下来:
import discord
这段代码用于导入 discord.py 库。
然后:
from discord.ext import commands
commands 扩展模块使得创建命令变得非常简单。
我们不需要手动检查每条消息中是否包含像 !hello 这样的指令,而是可以直接这样编写代码:
@bot.command()
async def hello(ctx):
await ctx.send("Hello!")
discord.py 的命令系统就是基于这些被标记为命令的 Python 函数构建的。
最后:
from dotenv import load_dotenv这使我们能够从`.env`文件中读取配置信息。
加载令牌
我们的机器人需要一个令牌,才能让Python程序与Discord连接。可以把这个令牌看作是一种密码,它能让我们的程序以机器人的身份进行认证。
我们不希望将这个敏感信息直接写到Python代码中,因此会将其存储在环境变量中。
首先,安装`python-dotenv`包:
pip install python-dotenv
这个包可以让Python从`.env`文件中读取配置信息。
现在,在与`bot.py`位于同一文件夹中,创建一个名为`.env`的新文件。
在`.env`文件中,添加以下内容:
DISCORD_TOKEN=你的机器人令牌
将`你的机器人令牌`替换为从Discord开发者门户复制过来的令牌值。
你的文件应该看起来像这样:
DISCORD_TOKEN=你的实际令牌值
千万不要将这个令牌分享给任何人,也不要将`.env`文件上传到GitHub上。你的机器人令牌必须被视为重要的敏感信息。
为了防止Git不小心将`.env`文件包含到代码仓库中,在项目文件夹中创建一个名为`.gitignore`的文件,并添加以下内容:
.env
venv/
__pycache__/
现在,让我们在Python代码中加载这个令牌。
在`bot.py`文件的顶部,添加以下代码:
import os
from dotenv import load_dotenv
然后添加:
load_dotenv()
这样Python就会查找`.env`文件并加载其中的配置变量。
现在我们可以获取到Discord令牌了:
TOKEN = os.getenv("DISCORD_TOKEN")
`os.getenv()`函数会查找名为“DISCORD_TOKEN”的环境变量,并返回它的值。
我们还可以检查是否真的找到了这个令牌:
if not TOKEN:
raise RuntimeError("DISCORD_TOKEN未设置。")
如果Python找不到这个令牌,程序会立即停止运行,并给出明确的错误提示,而不会在后续执行过程中出现莫名其妙的错误。
理解意图对象
还记得我们之前讨论过如何让Python程序能够读取消息内容吗?现在我们就来实现这个功能。
添加以下代码:
intents = discord.Intents.default()
intents.message_content = True
第一行创建了一组Discord默认的意图对象。
第二行告诉Discord,我们的机器人需要能够访问消息内容。
现在,在创建机器人实例时,我们需要将这些意图对象传递给它:
bot = commands.Bot(
command_prefix="!",
intents=intents
)
command_prefix="!"这一设置意味着我们的机器人能够识别以!开头的命令。
例如:
!hello
intents=intents这一部分为我们的机器人赋予了我们在前面配置的权限。
这里需要分两步进行设置:首先,Discord需要知道我们的机器人被允许接收消息内容;其次,我们的Python程序也需要向Discord表明它确实希望接收这些消息。
现在,我们的基本配置应该如下所示:
import os
import discord
from dotenv import load_dotenv
from discord.ext import commands
load_dotenv()
TOKEN = os.getenv("DISCORD_TOKEN")
if not TOKEN:
raise RuntimeError("DISCORD_TOKEN未设置。")
intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot(
command_prefix="!",
intents=intents
)
现在,我们的机器人的token已经被成功加载到了程序中,而discord.py也知道了在连接到Discord服务器时应该请求哪些功能。
什么是ctx?
在学习如何开发Discord机器人时,这一部分可能会让你感到有些困惑:
async def hello(ctx):
那么,ctx到底代表什么呢?ctx实际上指的是上下文信息。它包含了关于用户所使用的命令的各类详细数据。
例如,ctx可以告诉我们:
是谁发出了这个命令
这个命令是来自哪个服务器
它是在哪个频道中发出的
是什么消息触发了这个命令的执行
因此,当我们在代码中写到:
await ctx.send("Hello!")
这意味着:
“将这条消息发送回用户最初发出命令的那个地方。”
为什么所有的代码中都会出现async和await?
你可能会注意到:
async def hello(ctx):
以及:
await ctx.send...
Discord机器人需要花费大量时间来等待各种操作的结果。
它们需要等待的内容包括:
用户的输入消息
- Discord服务器的响应
- API请求的结果
还有其他一些需要等待的事件。
Python的异步编程特性使得机器人可以在不阻塞整个程序运行的情况下,等待这些操作的完成。
在创建你的第一个Discord机器人之前,你并不需要成为异步编程方面的专家。
目前,你可以把await理解为:
“暂停当前任务的执行,直到相关操作完成,同时让机器人继续处理其他事务。”
运行机器人
要启动机器人,请执行以下命令:
python bot.py
如果一切正常,你的终端会显示类似这样的信息:
已以StoryBot身份登录现在,请进入你的 Discord 服务器,输入 !hello,你的机器人应该会做出回应。
恭喜!你正式创建了一个 Discord 机器人了。
接下来,让我们让这个机器人变得更有趣吧。
构建故事讲述系统
首先,我们要创建一个交互式的故事讲述命令。
在 bot.py 文件的顶部,添加以下代码:
import random
然后准备一些故事元素:
story_locations = [
"一座废弃的图书馆",
"一个神秘的岛屿",
"一座未来主义城市",
"一个隐藏在地下的实验室",
"一片从未出现在地图上的森林"
]
story_items = [
"一把会发光的钥匙",
"一本古老的笔记本",
"一个奇怪的指南针",
"一个上锁的金属箱",
"一张神秘的照片"
]
story_events = [
"你听到身后有脚步声。",
"灯突然熄灭了。",
"附近有一扇隐藏的门打开了。",
"你的手机开始收到来自陌生发件人的消息。",
"你发现房间发生了变化。"
]
现在来创建这个命令:
@bot.command()
async def story(ctx):
location = random.choice(story_locations)
item = random.choice(story_items)
event = random.choice(story_events)
story_text = (
f"你醒来时发现自己身处 {location}。\n\n"
f>你身边放着 {item}。\n\n"
f>"发生了 {event},你会怎么做?"
)
await ctx.send(story_text)
现在输入 !story,可能会得到这样的结果:
你醒来时发现自己身处一座未来主义城市。
你身边放着一本古老的笔记本。
附近有一扇隐藏的门打开了。
你会怎么做?
再运行一次,你可能会得到完全不同的结果。
这是因为:
random.choice(...)
Python 会从每个列表中随机选取一个元素。
这个方法很简单,但这样一来,你的机器人就能生成数百种不同的组合了。
让故事记住用户的信息
随机生成的故事固然有趣,但如果机器人能够记住之前发生的事情,那么交互式故事就会变得更加精彩。
我们可以创建一个字典来存储每个用户的剧情信息:
user_stories = {}
这个字典会为每个用户保存他们的剧情相关数据。
例如:
用户 ID → 当前正在进行的剧情
现在我们来修改一下故事讲述命令:
@bot.command()
async def story(ctx):
user_id = ctx.author.id
location = random.choice(story_locations)
item = random.choice(story_items)
event = random.choice(story_events)
user_stories[user_id] = {
"location": location,
"item": item,
"event": event
}
await ctx.send(
f"你醒来时发现自己身处 {location}。\n\n"
f>你身边放着 {item}。\n\n"
f>"发生了 {event},你会怎么做?"
)
现在,每个用户都可以拥有自己正在使用的故事。
添加故事选择功能
让我们为用户提供更多的选择吧。
@bot.command()
async def choose(ctx, choice: str):
user_id = ctx.author.id
if user_id not in user_stories:
await ctx.send("你还没有正在使用的故事。请先尝试使用 `!story` 命令。")
return
choice = choice.lower()
if choice == "left":
response = (
"你向左走,发现了一个摆满旧地图的房间。"
"其中有一张地图上写着你的名字。"
)
elif choice == "right":
response = (
"你向右走,发现了一段通往”
“一道奇怪蓝光的楼梯。”
)
else:
response = "请尝试选择“left”或“right”。"
await ctx.send(response)
现在,用户可以输入如下命令:
!choose left
或者:
!choose right
注意以下几点:
async def choose(ctx, choice: str):
`choice` 参数接收用户输入的命令后的文本。
因此:
!choose left
在代码中会表示为:
choice = "left"
这就是命令框架如此便捷的原因之一。所谓命令框架,其实就是一套能够帮助我们更轻松地创建和管理程序中的命令的工具。在我们的例子中,`discord.py`提供了这样的命令框架,它允许我们使用像`@bot.command()`这样的装饰器,将Python函数转换成Discord命令。
我们不需要手动检查每一条消息来判断用户是否输入了`!choose`命令,`discord.py`会自动完成这项工作。它会识别该命令,提取用户的输入参数,并将这些参数传递给我们的函数。
所以当有人输入:
!choose left
`discord.py`就会知道`choose`是命令,`"left"`是参数,然后它会使用这些信息来调用我们的`choose()`函数。
添加闲聊命令
现在,让我们让这个机器人具备进行简单对话的能力吧。
我们可以将其连接到大型语言模型API上,但实际上,学习如何处理聊天命令并不一定需要人工智能的帮助。我们先从基于关键词的简单响应系统开始入手。
首先,我们会创建一个字典,其中包含一些关键词以及对应的回应内容:
chatresponses = { "hello": [ "嘿!最近怎么样?", "你好!今天过得怎么样?", "嗨!你在忙什么吗?" ], "python": [ "Python对初学者来说是非常适合的语言,因为它的语法非常容易理解。", "如果你正在学习Python,建议动手实践而不是只看教程。" ], "discord": [ "使用Discord机器人练习Python是很有趣的方式,因为你可以立刻得到反馈。", "一旦你掌握了命令和事件的相关知识,就可以制作出一些相当复杂的机器人程序了。」 ] } 可以把`chatresponses`看作是我们机器人能够讨论的几个主题。每个键,比如“python”或“discord”,都代表一个机器人可以搜索的关键词,而每个键对应的值则是一系列可能的回应内容。我们使用列表而不是单一的回应内容,这样机器人就不会每次都给出完全相同的回答。之后,我们会从这些回应中随机选择一条来回复用户。
现在我们来创建实际的`!chat`命令:
@bot.command() async def chat(ctx, *, message: str): text = message.lower() for keyword, responses in chatresponses.items(): if keyword in text: await ctx.send(random.choice(responses)) return await ctx.send( "我还在学习如何回应这类问题。" "你可以跟我聊聊Python或者Discord吧!" )这里发生了几件事情,让我们来逐一分析一下。
首先,看这一部分:
@bot.command() async def chat(ctx, *, message: str):这段代码将
chat()函数转换成了一个 Discord 命令。其中的*非常重要,因为它告诉discord.py,把命令后面的所有内容都视为一个参数。例如,如果有人输入:
!chat 我想学习 Python那么
!chat之后的整个内容就会成为message变量的值:message = "我想学习 Python"接下来是这一行:
text = message.lower()这行代码会将消息转换为小写。这样一来,
Python、python和PYTHON都会变成python。如果没有这个步骤,我们在检查关键词时可能会因为用户使用了不同的大小写而漏掉一些匹配结果。现在来看循环部分:
for keyword, responses in chatresponses.items():
.items()这个方法允许我们同时获取关键词及其对应的响应列表。在每次循环中,keyword> 会包含像"python"这样的字符串,而responses> 则会包含与该关键词相关的所有响应内容。然后我们会进行如下检查:
if keyword in text:这行代码用于判断用户输入的消息中是否包含了当前的关键词。
如果用户输入:
!chat 我想学习 Python那么转换成小写后的内容就是:
i want to learn python因为
"python"确实出现在这个字符串中,所以条件为真。此时机器人就可以随机选择一条响应内容来回复用户:
await ctx.send(random.choice(responses))
random.choice()会从响应列表中随机选取一条内容,而ctx.send()则会将这条内容发送回 Discord 频道。最后是这一行:
return这行代码表示在找到匹配的关键词后,函数就会结束执行。如果没有这个语句,即使机器人已经给出了回复,循环也会继续检查其他关键词。
但如果没有任何关键词与用户输入的内容相匹配呢?
这就是这一部分的作用:
await ctx.send( "我还在学习如何回应这类问题。" "你可以跟我聊聊 Python 或者 Discord 吧!" )如果循环结束后没有找到匹配的关键词,机器人就会发送这条备用回复信息。
例如:
!chat 我喜欢披萨这个输入中并不包含
"hello"、"python"或"discord",所以机器人没有具体的回复内容可以发送。这种方式让机器人能够在不需要人工智能模型的情况下进行对话。
添加心理健康支持功能
现在来说说这个需要更加仔细设计的功能吧。
我们在内部不会将这个功能称为“治疗指令”,而是会将其命名为:
!support在开始之前,我们需要先强调一点:这只是一个用于提供心理健康的辅助工具,并非真正的心理咨询师!
创建相关代码如下:
supportresponses = { "stress": [ "听起来这些事情确实让人难以应对。可以试着一次只处理一项任务。", "当一切看起来都令你不堪重负时,先暂停一下,专注于当前需要解决的最紧迫的问题会很有帮助。” ], "school": [ "学校的作业可能会迅速堆积起来。可以先选择其中一项任务来开始做,而不是试图一次性完成所有任务。” “如果学习带来的压力让你难以应对,与信任你的人聊聊,会让你觉得这些压力不那么难以承受。” ], "sad": [ “很遗憾你正在经历一段艰难的时光。暂时休息一下,做一些能让自己放松的事情,或者和信任你的人聊聊天,可能会有所帮助。” “你不必立刻解决所有问题。给自己一些时间,也可以考虑找信任你的人倾诉。” ] }现在来创建相应的指令:
@bot.command() async def support(ctx, *, message: str): text = message.lower() for keyword, responses in supportresponses.items(): if keyword in text: response = random.choice(responses) await ctx.send( f"{response}\n\n" "我是一个机器人,而不是心理咨询师或医疗专业人士。" "如果你需要心理支持,可以考虑找” "信任你的人聊聊。” ) return await ctx.send( "看起来有些事情正在困扰着你。” "我可以提供一些一般的心理健康建议,但我并不是心理咨询师。” "如果你需要心理支持,可以考虑找信任你的人帮忙。” )现在,人们可以输入这样的指令:
!support 我因为学校作业而感到压力很大机器人会识别到这个关键词:
school然后从中选择一条与学习相关的回复来回应用户。
我们故意将这个功能设计得相对简单一些。
对于一个真正的公开使用的机器人来说,在允许用户在使用它处理敏感问题之前,还需要进行更加严格的安全性测试、管理机制设置、隐私保护措施制定以及故障应对流程的设计。
添加帮助指令
一个好的机器人应该能够自己向用户解释它的功能。
@bot.command() async def commands_help(ctx): await ctx.send( "**可用指令:**\n" "`!hello` - 问候\n" "`!story` - 开始一个新的故事\n" "`!choose left` - 选择左边的道路\n" "`!choose right` - 选择右边的道路\n" "`!chat` - 进行随意的对话\n" "`!support ` - 获得一般的心理健康支持" ) 还有一个小问题。
Discord默认的帮助命令就是〈code>help
所以,我们没有将自己的帮助命令命名为:
async def help(ctx):而是将其命名为:
commands_help如果你想让这个命令的名称仍然是〈code>!help
@bot.command(name="help") async def commands_help(ctx): ...这样Discord就会知道:
即使Python函数的实际名称是别的,也应该使用〈code>!help
改进错误处理机制
机器人不应该因为有人输入了无效的命令就崩溃。
添加以下代码:
@bot.event async def on_command_error(ctx, error): if isinstance(error, commands.MissingRequiredArgument): await ctx.send( "你缺少某些信息。试试使用`!help`来了解这个命令的用法吧。” ) elif isinstance(error, commands.CommandNotFound): return else: print(f"错误:{error}")现在,如果有人输入:
!chat而没有给机器人发送任何消息,它也可以给出有用的提示,而不会在对话中显示令人困惑的错误信息。
整合所有代码
此时,你的〈code>bot.py文件应该看起来像这样:
import os import random import discord from discord.ext import commands from dotenv import load_dotenv load_dotenv() TOKEN = os.getenv("DISCORD_TOKEN") if not TOKEN: raise RuntimeError("DISCORD_TOKEN未设置。") intents = discord.Intents.default() intents.message_content = True bot = commands.Bot( command_prefix="!", intents=intents ) story_locations = [ "一座废弃的图书馆", "一个神秘的岛屿", "一座未来主义风格的城市", "一个隐藏在地下的实验室", "一片从未出现在地图上的森林" ] story_items = [ "一枚会发光的钥匙", "一本古老的笔记本", "一个奇怪的指南针", "一个上锁的金属箱", "一张神秘的照片" ] story_events = [ "你听到身后有脚步声。", "灯突然熄灭了。", "附近有一扇隐藏的门打开了。", "你的手机收到了一条来自陌生人的消息。", "你发现房间的布局发生了变化。" ] user_stories = {} chatresponses = { "hello": [ "嘿!最近怎么样?", "你好!今天过得怎么样?", "嗨!你在忙什么呢?" }, "python": [ "Python非常适合初学者,因为它的语法非常容易理解。", "如果你正在学习Python,不妨试着自己动手做一些东西,而不仅仅是看教程。」 ], "discord": [ "使用Discord机器人来练习Python是很有趣的方式,因为你可以立刻得到反馈。", "一旦你掌握了命令和事件的处理方法,就可以制作出一些相当复杂的机器人程序了。」 ] } supportresponses = { "stress": [ "听起来这些事情确实有点让人难以应对。试着把它们分解成一个个小任务来处理吧。", "当一切看起来都让人不堪重负的时候,暂停一下,先集中精力解决当前最紧迫的问题会很有帮助。” ], "school": [ "学校的作业可能会迅速堆积起来。可以先选择其中一项任务来开始做,而不是一下子试图完成所有作业。」 "如果学校带来的压力让你难以应对,和信任你的人聊聊,会让这种压力感觉没那么难以承受。” ], "sad": [ > “很抱歉你正在经历一段艰难的时光。短暂休息一下,做一些让自己放松的事情,或者和信任你的人聊聊,可能会对你有所帮助。” > “你不必立刻解决所有问题。给自己一些时间,也可以考虑向信任你的人寻求帮助。” ] } @bot.event async def on_ready(): print(f"已以{bot.user}的身份登录。") @bot.command() async def hello(ctx): await ctx.send("你好!我在线呢。") @bot-command() async def story(ctx): user_id = ctx.author.id location = random.choice(story_locations) item = random.choice(story_items) event = random.choice(story_events) user_stories[user_id] = { "location": location, "item": item, "event": event } await ctx.send( f“你醒来后发现自己身处{location}。\n\n” f“你旁边有一件物品:{item}。\n\n” f“接下来会发生{event}。\n\n” "你会怎么做呢?" ) @bot-command() async def choose(ctx, choice: str): user_id = ctx.author.id if user_id not in user_stories: await ctx.send( "你还没有开始任何故事。先试试使用`!story`吧。」 ) return choice = choice.lower() if choice == "left": response = ( "你向左走,发现了一个堆满旧地图的房间。其中有一张地图上写着你的名字。” ) elif choice == "right": response = ( "你向右走,看到了一段通往” "一道奇怪的蓝光的楼梯。” ) else: response = "请选择“left”或“right”。" await ctx.send(response) @bot_command() async def chat(ctx, *, message: str): text = message.lower() for keyword, responses in chatresponses.items(): if keyword in text: await ctx.send(random.choice(responses)) return await ctx.send( "我还在学习如何回应这类问题。你可以跟我聊聊Python或者Discord相关的事情哦!" ) @bot_command() async def support(ctx, *, message: str): text = message.lower() for keyword, responses in supportresponses.items(): if keyword in text: response = random.choice(responses) await ctx.send( f"{response}\n\n" "我只是一个机器人,并不是心理治疗师或医疗专业人士。" "如果你需要心理支持,建议和” "信任你的人聊聊。” ) return await ctx.send( "听起来你有些烦恼。我可以给你一些建议,但我并不是心理治疗师。" "如果你需要帮助,可以考虑向信任你的人寻求支持。” ) @bot.command(name="help") async def commands_help(ctx): await ctx.send( "**可使用的命令:**\n" "`!hello` – 问候他人\n` "`!story` – 开始一个新的故事\n` "`!choose left` – 选择左边的路径\n` "`!choose right` – 选择右边的路径\n` "`!chat` – 进行随意的对话\n" "`!support ` – 获得一般性的帮助建议" ) @bot.event async def on_command_error(ctx, error): if isinstance(error, commands.MissingRequiredArgument): await ctx.send( "你缺少某些信息。试试使用`!help`来了解这个命令的用法吧。” ) elif isinstance(error, commands.CommandNotFound): return else: print(f"错误:{error}") bot.run(TOKEN) 这些资源已经足以用来创建一个功能相当强大的初学者级 Discord 项目了。
但还有一个重要的限制存在。
我们的机器人实际上并不记得任何信息
到目前为止,我们的机器人存在一个小问题:它在关闭后并不会保留任何数据。
目前,我们是将故事信息存储在一个 Python 字典中:
user_stories = {}这种存储方式在机器人运行期间是有效的。但如果你停止程序然后再重新启动,字典中的内容就会恢复为空。
为了解决这个问题,我们需要一个能够永久保存数据的机制。这时数据库就派上了用场。
在这个项目中,我们将使用SQLite数据库。SQLite 是一种轻量级的数据库系统,它将数据存储在用户电脑上的文件中。Python 本身就已经内置了
sqlite3模块,因此我们不需要额外安装任何软件。创建数据库
首先,在
bot.py文件的开头添加以下导入语句:import sqlite3然后建立与数据库文件的连接:
db = sqlite3.connect("bot.db") cursor = db.cursor()第一行代码会检查是否存在名为
bot.db的数据库文件;如果不存在,就会创建这个文件。如果文件已经存在,SQLite 会直接打开它。第二行代码创建了一个游标对象。你可以把游标看作是 Python 程序中用来与数据库进行交互的部分。
接下来,我们需要创建一个表格来存储用户的剧情信息:
cursor.execute(""" CREATE TABLE IF NOT EXISTS user_stories ( user_id INTEGER PRIMARY KEY, location TEXT, item TEXT, event TEXT ) """) db.commit()让我们详细解释一下这段代码的含义。
cursor.execute()这条命令告诉 SQLite 执行括号内的 SQL 语句。这个 SQL 语句的第一部分是:
CREATE TABLE IF NOT EXISTS user_stories这条指令表示:只有当
user_stories表格还不存在时,才创建它。括号内定义了每行数据可以包含的字段:user_id INTEGER PRIMARY KEY, location TEXT, item TEXT, event TEXT
user_id是 Discord 用户的唯一标识符,我们将其设置为PRIMARY KEY,这样每个用户都会在数据库中拥有自己独立的记录。
location、item和event分别代表了用户当前剧情中的地点、物品以及发生的事件等信息。最后:db.commit()这条命令会将所有更改保存到数据库中。
完成这些步骤后,你的项目文件夹里应该会多出一个名为
bot.db的文件。bot.db你不需要手动打开或编辑这个文件。SQLite会为我们处理这些操作。
保存用户的故事
现在,让我们真正地将信息存储到数据库中吧。
假设我们有以下变量:
user_id = ctx.author.id location = "一座废弃的城堡" item = "一把神秘的钥匙" event = "一扇上锁的门"我们可以使用以下代码来保存这些信息:
cursor.execute( """ INSERT OR REPLACE INTO user_stories (user_id, location, item, event) VALUES (?, ?, ?, ?) """, (user_id, location, item, event) ) db.commit()这条SQL语句告诉SQLite将信息插入到
user_stories表中。其中的
?符号是用于替换实际值的占位符。这些实际值在后面单独提供:(user_id, location, item, event)这样比直接在SQL字符串中插入数值要安全得多。
INSERT OR REPLACE这个语句还意味着:如果该用户已经保存过故事,那么他们的旧信息会被新信息替换掉。取回保存的故事
保存信息只是整个过程的一半,我们还需要能够检索这些信息。
我们可以像这样在数据库中搜索用户的保存故事:
cursor.execute( """ SELECT location, item, event FROM user_stories WHERE user_id = ? """, (user_id,) ) story = cursor.fetchone()这次,我们使用
SELECT语句来从数据库中获取信息。
WHERE部分非常重要:WHERE user_id = ?它告诉SQLite找到属于这位特定Discord用户的记录。
然后:story = cursor.fetchone()这条语句会获取第一个匹配的结果。
如果该用户有保存的故事,
story变量中就会包含这些信息;如果没有,story的值将会是None。我们可以这样进行检查:
if story: location, item, event = story await ctx.send( f"你当前位于{location}。" f>你拥有{item},目前面临着{event}。" ) else: await ctx.send("你还没有保存任何故事呢!")现在,即使Python程序重新启动,机器人也能够检索之前保存的信息。
将其整合成命令
我们可以将这个功能整合成一个简单的命令,让用户能够查看自己保存的故事:
@bot.command() async def status(ctx): user_id = ctx.author.id cursor.execute( """ SELECT location, item, event FROM user_stories WHERE user_id = ? """, (user_id,) ) story = cursor.fetchone() if story: location, item, event = story await ctx.send( f"你当前位于{location}。" f>你拥有{item},目前面临着{event}。" ) else: await ctx.send( "你还没有保存任何故事呢。" "可以使用`!story`来开始保存吧!" )现在,用户可以输入如下内容:
!status机器人就会从数据库中查找相应的信息并予以回复。
这与我们最初的实现方式相比有了显著的改进。因为传统的字典只能在Python程序运行期间保存数据,而SQLite则能够让我们将这些数据保存下来,这样即使程序重新启动,这些数据依然会存在。
对于规模更大的机器人来说,未来还可以存储用户的偏好设置、故事进度、物品清单、对话记录等其他数据。但目前而言,这个简单的数据库已经足以让我们的机器人具备“记忆功能”了。
添加真正的AI聊天功能
在将我们的机器人与AI模型连接起来之前,让我们先简单了解一下Hugging Face吧。
如果你还没有使用过Hugging Face,那么它其实是一个让开发者能够查找、分享并使用机器学习模型及数据集的平台。可以把它看作是一个庞大的AI工具社区和资源库。
Hugging Face还提供了各种工具,使得Python程序能够直接与这些模型进行交互,而无需从头开始构建和训练AI模型。
对于我们的机器人来说,我们将使用Hugging Face的Inference Providers来将用户的消息发送到相应的语言模型,并接收其回复。
我们不会自己训练AI模型,而是直接使用现有的模型,并通过Python将其与我们的Discord机器人连接起来。
既然我们已经了解了Hugging Face的作用,那么接下来就让它与我们의机器人结合起来吧。
安装Hugging Face库
首先,安装
huggingface_hub模块:pip install -U huggingface_hub我们已经安装了
python-dotenv,因此仍然可以使用之前创建的.env文件来隐藏Hugging Face访问令牌的信息,避免将其直接写在源代码中。将你的Hugging Face访问令牌添加到
.env文件中:DISCORD_TOKEN=你的机器人令牌 HF_TOKEN=你的Hugging Face令牌请将
你的Hugging Face令牌替换成你实际的访问令牌。与Discord机器人的令牌一样,千万不要将这个令牌分享给他人,也不要将其上传到GitHub上。
创建Hugging Face客户端
现在,在
bot.py文件的顶部添加以下导入语句:from huggingface_hub import InferenceClient然后加载访问令牌:
HF_TOKEN = os.getenv("HF_TOKEN") if not HF_TOKEN: raise RuntimeError("HF_TOKEN未设置。")第一行代码从环境变量中获取访问令牌,if语句用于检查该令牌是否确实存在。如果不存在,Python会立即停止执行,并给出相应的错误提示,而不会在程序运行后期出现莫名其妙的故障。接下来,创建Hugging Face客户端:
client = InferenceClient( api_key=HF_TOKEN )InferenceClient正是我们的Python程序用来与Hugging Face的推理服务进行通信的工具。将AI模型连接到机器人中
现在,我们可以用一个新的命令来替换之前基于关键词的
!chat命令,这个新命令会将用户输入的消息发送给语言模型。@bot.command() async def chat(ctx, *, message: str): try: response = client.chat_completion( model="YOUR_SUPPORTED_MODEL_ID", messages=[ { "role": "system", "content": ( "你是一个友好的Discord机器人。" "请确保你的回复有帮助、简洁且符合对话风格。" ) }, { "role": "user", "content": message } ], max_tokens=200 ) answer = response.choices[0].message.content await ctx.send(answer) except Exception as error: print(f"AI出现错误:{error}") await ctx.send( "目前我无法生成回复。" "请稍后再试一次。」 )这里发生了很多复杂的操作,让我们一步步来解释一下。
我们首先使用之前已经用过的命令结构:
@bot.command() async def chat(ctx, *, message: str):这种结构用于创建
!chat命令,并将用户在该命令后输入的所有内容存储在message变量中。例如:
!chat Python是什么?这样就会得到:
message = "Python是什么?"接下来,我们使用:
try:这个语句告诉Python,我们即将执行一些可能会出错的代码。由于我们需要与外部服务进行交互,因此可能会出现模型不可用、令牌无效或连接暂时中断等问题。
然后我们调用:
response = client.chat_completion(这条语句会通过Hugging Face向语言模型发送一个聊天请求。
messages参数中包含了我们希望模型根据这些内容进行回复的对话内容。其中第一条消息的角色被设置为
"system":{ "role": "system", "content": ( "你是一个友好的Discord机器人。" "请确保你的回复有帮助、简洁且符合对话风格。」 ) }这条系统消息为模型提供了关于如何进行回复的指导。
最后,我们再提供用户实际输入的消息:
{ "role": "user", "content": message }如果用户输入的是:
!chat Python是什么?那么,
message中包含的内容是:什么是Python?因此,模型会将这些内容视为用户的输入。
我们还设置了以下参数:
max_tokens=200这个限制决定了模型每次响应时能够生成多少文本。在Discord中,保持响应内容相对简短效果较好,因为过长的文本在聊天频道中阅读起来并不方便。
你还需要将以下代码中的:
model="YOUR_SUPPORTED_MODEL_ID"替换为你正在使用的Hugging Face推理平台所提供的模型ID。根据Hugging Face的文档说明,
InferenceClient可以使用托管在Hugging Face Hub上的模型ID来完成聊天对话的生成。当请求完成后,我们需要从响应中获取实际的文本内容:
answer = response.choices[0].message.content响应中包含了模型生成的结果。
choices[0]表示第一个生成的响应,而.message.content则能获取到实际的文本内容。最后,我们将这个文本发送回Discord:
await ctx.send(answer)整个流程如下所示:
用户输入“!chat” ↓ Discord将命令发送给我们的机器人 ↓ Python接收用户的消息 ↓ Hugging Face接收到这些信息 ↓ AI模型生成响应 ↓ Python获取生成的文本 ↓ 机器人将结果发送回Discord处理AI相关错误
我们代码的最后一部分是:
except Exception as error: print(f"AI出现错误:{error}") await ctx.send( "目前我无法生成响应。请稍后再试。” )如果
try块中的代码出现问题,Python会跳转到except块执行,而不会导致整个机器人崩溃。错误信息会被打印在终端上,这样你就可以了解具体发生了什么:
print(f"AI出现错误:{error}")同时,Discord上的用户也会收到一条简单的提示信息:
目前我无法生成响应。请稍后再试。这种方式显然比让API错误导致整个机器人停止运行要好得多。
现在,你已经拥有了一个真正由AI支持的“!chat”命令了。你可以像这样使用它:
!chat 告诉我一个关于太空的有趣事实。模型会根据你的请求生成相应的回答,而不会从预先准备好的固定列表中选择答案。
需要记住的一点是,这个机器人会将用户的消息发送给外部AI服务。因此,请不要自动将私密或敏感信息发送给这些AI服务提供商。如果你让其他人也能使用这个机器人,一定要明确说明它会处理哪些信息,并避免存储或传输超出实际需要的数据量。
你也可以将这个人工智能系统与之前使用的 SQLite 数据库结合使用。例如,你可以保存一定量的对话记录,并在发送新消息时同时附上相关的之前的聊天内容。这样,机器人就能在不同的消息之间保持上下文关联,而不会把每一条消息都视为一场全新的对话。
我们如何让机器人保持在线状态?
在这里,“永远在线”这个说法需要稍作解释。
其实存在两种不同的情况。
选项 1:在您的电脑上运行它
当您运行以下命令时:
python bot.py机器人就会保持在线状态,直到该程序停止运行。
如果关闭终端窗口?
机器人就会下线。
如果关掉电脑?
机器人也会下线。
如果网络中断?
机器人依然会下线。
这种设置非常适合开发用途,但并不适合用于需要24小时不间断运行的生产环境。
选项 2:在服务器上运行它
如果希望机器人在您的电脑关闭后依然保持在线状态,那么就需要有一台始终处于开机状态的计算机来运行这个机器人程序。
这台计算机可以是一台云服务器。
您只需将项目文件上传到云服务器上,安装所需的依赖库,设置环境变量,然后运行以下命令:
python bot.py现在,程序就会在云服务器上运行,而不是在您的笔记本电脑上。
对于那些需要持续运行的应用程序来说,可以使用专门为这类任务设计的服务。例如,Render目前提供了一种名为后台工作进程的服务,这种服务可以用于运行那些不需要处理网络请求的程序。
但在实际部署之前,您应该先了解提供商当前的定价政策和服务限制。免费的托管方案并不一定适合用于需要24小时不间断运行的 Discord 机器人;而且,也不能想当然地认为任何托管平台都能提供“永久免费”的7×24小时运行环境。
“永远在线”到底意味着什么?
其实并没有什么神奇的设置方法可以让机器人真正做到“永远在线”。
只有当运行机器人的电脑或服务器持续处于正常工作状态时,机器人才能保持在线。
即便是由专业机构托管的机器人,也可能会因为以下原因而下线:
服务器维护
程序部署
程序出现故障
网络问题
托管服务中断
用户名或密码错误
API接口发生变化
计费或账户相关问题
因此,现实的目标应该是让机器人能够自动运行,并在出现故障时自动重新启动。
这就是生产环境托管服务所要帮助实现的目标。
如果您的托管服务提供商支持自动重启功能,请务必启用这项功能。
另外,您也可以在 Python 代码中设置机制,以便在缺少重要的环境变量时让程序明确地出现错误并停止运行。
if not TOKEN:
raise RuntimeError("DISCORD_TOKEN未设置。")
出现明确的错误要比那些根本无法正常运行的机器人更容易进行调试。
不要试图用一些随意的方法让机器人保持运行状态
你可能会看到一些教程,建议通过部署Web服务器并让其他服务不断向该服务器发送ping请求,来防止免费的托管实例进入睡眠状态。
但对这种做法要谨慎对待。
托管服务提供商会随时更改其免费 tier 的使用规则,试图绕过这些限制可能会违反他们的服务条款。
如果你确实需要一个能够持续运行的机器人,那就选择那些明确支持此类负载的托管方案。
例如,后台工作进程就是为持续运行而设计的。这种方式比试图让Web服务认为你的Discord机器人实际上是一个网站要合理得多。
其他功能与后续发展方向
现在你已经成功创建了一个可以正常运行的Discord机器人,接下来你可以朝着很多不同的方向继续开发这个项目。
你可以通过添加物品清单、多个章节、谜题或不同的结局,将这个故事讲述系统发展成一款更完整的游戏;也可以用Discord的斜杠命令和按钮来替代文本形式的指令,从而使机器人的交互方式更加便捷。
如果你对人工智能感兴趣,还可以通过赋予机器人不同的“性格特征”、添加有限的对话场景,或者利用AI生成故事内容,来进一步扩展这个聊天系统。
你还可以根据自己正在建立的Discord社区的需求,添加审核功能、每日故事提示或其他相应的指令。
这些都是扩展项目功能的想法,并非我们在本教程中会逐步实现的步骤。重要的是,你现在已经具备了自行尝试这些想法的基础。
可以先从实现一个简单的功能开始,弄清楚它的运作原理,然后再逐步完善它。你不需要一下子就把这个机器人发展成一个庞大的项目。
当你越深入地探索代码时,就会越清楚地了解到Python、Discord、数据库以及人工智能是如何在实际应用中协同工作的。
先在本地进行所有测试
在正式部署之前,请先进行以下测试:
!hello
!story
!choose left
!choose right
!chat hello
!chat 我想学习Python
!support 我压力很大
!help
然后再测试一些奇怪的输入指令:
!choose banana
!chat
!support
!unknowncommand
你最好是在自己坐在电脑前时发现这些错误,而不是等到三天后有人告诉你:“你的机器人从周二开始就出问题了。”
部署机器人
首先,请确保你的项目包含了以下内容:
discord-story-bot/
│
├── bot.py
├── requirements.txt
├── .gitignore
└── .python-version
.python-version文件中可以包含如下内容:
3.13
使用版本信息文件可以让你的部署环境更加稳定可靠。Render目前支持通过.python-version文件或环境变量来指定Python版本。
requirements.txt文件中应该列出你所依赖的库和模块。
discord.py
python-dotenv
在进行部署时,通常不需要使用本地的.env文件。
相反,你应该在托管服务提供的控制面板中设置如下环境变量:
DISCORD_TOKEN
这样,你的敏感信息就不会存储在代码仓库中。
启动命令
你的部署服务需要知道应该运行什么程序。
对于这个项目来说,启动命令是:
python bot.py
重要的是,这个进程不能立即退出。
Discord机器人能够持续运行,是因为bot.run(TOKEN)这一命令会建立与Discord服务器的连接,并使程序保持运行状态。
如果你的托管服务支持后台工作进程,那么对于这样的机器人来说,这种配置非常合适,因为机器人并不需要处理普通的HTTP请求。Render将后台工作进程定义为那些不会接收外部网络请求、能够持续运行的服务。
记住:一定要保密你的敏感信息
这一点值得反复强调,因为很多初学者的项目就是因为忽略了这一点而遭到攻击的。
bot.run("YOUR_REAL_TOKEN")
这条代码绝对不能被提交到代码仓库中!
.env文件也绝对不能被上传到公共代码托管平台上。
.env
千万不要将你的实际token信息粘贴到公开的GitHub问题中。
你学到了什么
你现在已经成功构建了一个Discord机器人,这个机器人运用了多种编程技巧。
<你学会了以下内容:创建一个Discord应用程序
将Python程序与Discord服务器连接起来
使用
discord.py库配置Gateway Intents
创建命令功能
使用异步函数
读取命令参数
生成随机故事内容
存储用户的临时状态信息
构建一个基本的聊天系统
实现心理健康支持功能
处理命令执行过程中可能出现的错误
确保敏感信息不会被包含在源代码中
为项目的部署做好准备工作
考虑使用持久化的托管服务
在所有这些功能背后,其架构其实出奇地简单:
用户发送指令
↓
Discord接收消息
↓
discord.py接收到相关事件
↓
Python函数被执行
↓
机器人生成响应
↓
Discord将响应显示给用户
你并不需要数千行代码才能开始使用这个工具。
你只需要有一个清晰的概念、掌握一些Python基础知识,并且愿意在遇到问题时不断进行调试即可。
结语
这个项目最棒的地方并不在于它本身创造的Discord机器人,而在于它能让你学到什么。
一旦你理解了这些基本原理,就可以将同样的方法应用到无数个项目中。
一个Discord机器人可以发展成一款游戏,也可以变成一个Web应用程序,甚至可能演变成一个规模更大的软件项目。
通过这个过程,你不仅仅是在学习Python的语法,而是在了解软件究竟是如何被一点一点构建起来的。
祝编程愉快!
相关文章
用于机械工程的Python语言
无论你是从事机器人设计、流体动力学分析,还是处理复杂的工程数据,学习编程都是提升工作效率与技术分析能力的最有效方法之一。 我们刚刚在freeCodeCamp.org的YouTube频道上推出了一门全新的综合课程,这门课程将教你如何使用Python进行工程开发,并帮助你掌握NumPy和Pandas等工具。同时,你还会学到如何在工作中运用人工智能技术。 这款课程专为机械工程师、机器人爱好者以及那些希望在日常工作中运用现代编程技术和人工智能工具的人设计。 在学习实际的工程应用之前,你会先系统地学习Python的基础知识: Python核心基础:从工程师的角度学习变量、控制结构(`if/elif/el
阅读全文
在调试BLE多点连接断开问题时,在阿里巴巴的网站上发现了音频指纹识别技术被使用的痕迹。
最近有研究发现,AliExpress利用Web Audio API,通过无声音频流来识别用户的设备类型。这种技术是通过分析各种硬件设备特有的音频处理方式来区分不同用户设备的。那些注重保护用户隐私的浏览器已经开发出了相应的应对措施,但这也暴露出现行网页标准在音频处理机制及用户隐私保护方面存在的安全漏洞。 作者:Olimpiu Pop
阅读全文
如何测试对话式人工智能:面向质量保证工程师的实用指南
当我刚开始学习对话式人工智能测试时,有一个问题一直困扰着我: 预期的结果到底在哪里? 由于我之前从事过传统的软件测试工作,所以我习惯于一种固定的流程。 需求文档会告诉我们系统应该完成什么功能。我们创建测试用例,提供输入数据,定义预期结果,执行测试,然后将实际结果与预期结果进行对比。 举个例子: 测试项 输入内容 预期结果 有效登录 正确的用户名和密码 用户成功登录 无效登录 错误的密码 显示错误提示信息 API请求 有效的请求数据 收到HTTP 200响应及预期内容 但当我开始接触对话式人工智能时,我发现同样的测试方法不再适用了。 如果我向AI系统询问“如何重置密码?”,它可能会回答:“您可以
阅读全文
如何利用WCAG 2.2标准打造更加易于访问的网站
一个网站可能看起来很专业,使用鼠标操作时也能运行得非常顺畅,但对某些用户来说,使用它仍然会遇到困难。 有时,某个表单会仅通过颜色来提示用户出现了问题;固定的页眉可能会完全覆盖当前处于键盘焦点位置的元素;登录表单可能会阻止用户从密码管理工具中复制密码并粘贴到表单中;而某个自定义按钮,用鼠标点击时可以正常使用,但当人们使用键盘操作时却毫无反应。 这些其实都是开发过程中做出的决策,并非只有在进行无障碍性审核时才会出现的问题。 《Web内容无障碍性指南》为识别和消除这些障碍提供了统一的标准。WCAG 2.2是最新的WCAG 2建议标准,世界万维网联盟也建议开发人员和各类组织在可能的情况下使用WCAG
阅读全文