Discord.py 为Urban Dictionary、OpenWeatherMap API命令添加用户输入参数的求助
你现在写的$urban命令没法正常响应带搜索词的调用,核心问题有三个:
- 函数签名没留接收位,用户输在命令后面的搜索词根本没被框架捕获到
- 发起API请求的时候没把搜索词作为查询参数传出去,相当于一直给接口发空请求
- 解析接口返回JSON的时候字典键名是空字符串,根本取不到有效数据
第一步:让框架捕获用户输入的完整搜索词
Discord.py 扩展的命令解析规则是:会把用户在命令名后输入的文本按空格拆分,依次匹配函数签名里的位置参数。
因为网络词条经常带空格(比如“hot dog”“zero click”),没必要拆成多个参数,只需要在ctx参数后面加一个带*标记的字符串参数,就能把命令后面的所有剩余文本整合成一个完整字符串传给函数。
比如把原来的函数定义:async def urban(self, ctx):
改成:async def urban(self, ctx, *, search_term: str):
这里的*是关键标记,作用是告诉框架:从这个位置开始,不管后面的输入有没有空格,全部合并成一个字符串赋值给search_term。哪怕用户输入$urban fluffy cat wearing hat,整段fluffy cat wearing hat都会被完整捕获作为搜索词,不会被拆成单个单词。
如果用户没输任何搜索词直接发命令,框架会自动触发缺参报错,你也可以后续加错误处理自定义提示文案,比如引导用户“请输入要查询的词条,格式为$urban 要搜的词”。
第二步:把捕获的搜索词传给第三方API
你之前的代码里,session.get只传了URL和请求头,没有传查询参数,Urban Dictionary的接口自然不知道你要搜什么。
按照接口要求,需要传入的查询参数是term,对应的值就是刚才捕获到的search_term。你不需要手动拼URL的查询串,只要把参数组装成字典,传给get方法的params参数就行,库会自动帮你拼接到请求URL后面,还会自动处理空格、特殊符号的转义,避免请求出错。
这部分的代码修改:
# 组装接口需要的查询参数字典 querystring = {"term": search_term} # 发请求的时候把params传进去 async with session.get(url, headers=headers, params=querystring) as response:
第三步:修正接口返回值的解析逻辑
原来的代码里写r['']是完全取不到值的——这个Urban Dictionary接口返回的JSON结构里,所有匹配的词条结果都存在list字段下,是一个列表,最相关的结果在列表的第一个位置。
你需要先判断接口有没有返回有效结果,避免空结果触发KeyError报错,再从结果里取对应字段构建embed:
r = await response.json() # 先检查有没有匹配的结果 if not r.get("list"): await ctx.send(f"没找到和「{search_term}」相关的释义哦") return # 取最相关的第一条结果 top_result = r["list"][0]
原来的embed里空的取值,全部替换成对应字段:标题用top_result["word"]做词条名,释义用top_result["definition"],还可以加上接口返回的示例、点赞数这类信息让内容更完整。另外提醒一句,新版Discord.py里作者头像的属性是ctx.author.avatar.url,原来的avatar_url已经是弃用的写法了,写上去会报属性不存在的错。
改完的可运行代码
@commands.command( name="urban", description="Allows the user to get a definition from Urban Dictionary", aliases=['urbandict'] ) async def urban(self, ctx, *, search_term: str): url = "https://mashape-community-urban-dictionary.p.rapidapi.com/define" headers = { 'x-rapidapi-key': self.bot.quote_api_key, 'x-rapidapi-host': "mashape-community-urban-dictionary.p.rapidapi.com" } querystring = {"term": search_term} async with ClientSession() as session: async with session.get(url, headers=headers, params=querystring) as response: r = await response.json() if not r.get("list"): await ctx.send(f"没有找到关于「{search_term}」的网络释义~") return top_result = r["list"][0] embed = discord.Embed( title=f"搜索词条:{top_result['word']}", description=top_result["definition"] ) if top_result.get("example"): # 截断过长的示例文本,避免超出embed的字符限制 embed.add_field( name="用法示例", value=top_result["example"][:1024], inline=False ) embed.add_field(name="点赞数", value=top_result["thumbs_up"]) embed.set_author( name=ctx.author.display_name, icon_url=ctx.author.avatar.url ) await ctx.send(embed=embed)
通用逻辑参考:这套写法完全可以复用在OpenWeatherMap这类其他第三方API上——只需要在命令参数位把用户输入的地点名捕获下来,组装成接口要求的参数字典(比如OpenWeatherMap需要的
q=地点名、appid=你的密钥),传给请求方法的params参数就行,不用手动拼接复杂的URL。
内容的提问来源于stack exchange,提问作者Jango DarkWind

