Discord机器人实现涩图功能
上篇文章我们构建了一个最基础的机器人,接下来就得给他加点功能了
作为一个聊天机器人最重要的功能,那当然是涩图!
我们的需求是调用一个指令 /涩图
就可以获取一张涩图,而且还可以带点可选的参数
调用API
这里使用lolicon API(十分感谢API作者)
API可以传入几个参数,我这里就给用户提供三个参数: r18
tags
exclude_AI
,使用POST方式请求
先写一个最基础的请求类
def RequestLolicon(tags: list[str], r18: bool, excludeAI: bool) -> dict:
params = {
'r18': r18.as_integer_ratio()[0],
'excludeAI': excludeAI.as_integer_ratio()[0]
}
if len(tags) > 0:
params['tag'] = tags
result = requests.get("https://api.lolicon.app/setu/v2", params=params, timeout=5, proxies={ 'https': None } )
return result.json() if result.json()['data'] else None
这段代码写了一个基础的请求图片数据的函数,传入三个参数,返回结果。
下载图片
我想让图片直接下载到本地,然后上传到discord的CDN上,所以需要一个图片下载功能
先写一个简单的下载图片函数
def Download(pid: str, url: str) -> str:
ext = url.split('.')[-1]
r = requests.get(url, timeout=30, proxies={ 'https': None })
os.makedirs('./cache/setu/', exist_ok=True)
with open(f'./cache/setu/{pid}.{ext}', 'wb') as img:
img.write(r.content)
return f'{pid}.{ext}'
这个函数传入两个参数 pid
url
其中 url
是我们通过上一个调用API之后的结果获得的,因为这里只是个工具函数,所以不需要去关心API返回的数据。
pid
也是返回的数据,用于文件名
这个函数会把图片下载到 ./cache/setu/{pid}.{ext}
这个地方,其中 ext
根据下载 url
的后缀定
整合
接下来就是整合这两个函数,并且和Discord交互
async def HandleRequest(ctx: SlashContext, args, r18, excludeAI):
tags = args.split(',')
tags = args.split(',')
data = RequestLolicon(tags, r18, excludeAI)
if data is None:
await ctx.send('未找到与tag所匹配的图片')
else:
pid = str(data['data'][0]['pid'])
url = data['data'][0]['urls']['original']
filename = Download(pid, url)
file = File(
file = f'./cache/setu/{filename}',
file_name = filename,
description = f'PID: {pid}'
)
await ctx.send(f'PID: {pid}', file=file)
os.remove(f'./cache/setu/{filename}')
这段代码接受一个 SlashContext
参数,是python库 discord-py-interactions
提供给我们的,就是指令的上下文
这四个参数是我们在注册指令的地方传入的
这个函数的大意就是整合请求,下载图片,上传图片,发送消息,删除本地图片为一体
注册Discord Slash指令
现在开始注册Discord的Slash指令
scopes = [XXX]
@interactions.slash_command(
name = '涩图',
description = "来张涩图",
scopes = scopes,
nsfw = True
)
@interactions.slash_option(
name = 'tags',
description = "指定图片的tag (可选, 用逗号分隔)",
required = False,
opt_type = interactions.OptionType.STRING
)
@interactions.slash_option(
name = 'r18',
description = "图片是否为r18 (可选,默认为true)",
required = False,
opt_type = interactions.OptionType.BOOLEAN
)
@interactions.slash_option(
name = 'exclude_ai',
description = "是否排除AI生成的图片 (可选,默认为true)",
required = False,
opt_type = interactions.OptionType.BOOLEAN
)
async def setu(ctx: interactions.SlashContext, tags: str = "", r18 = True, exclude_ai = True):
await ctx.defer()
await Setu.HandleRequest(ctx, tags, r18, exclude_ai)
这里有一些东西需要说明:
scopes : 指令的作用域,一个数组,里面存的是你要启用指令的服务器ID。
如果你不设置这个的话,discord默认你在所有服务器里启用,但是指令生效需要1个小时左右,如果你设置了瞬间就会注册好
如果你设置正确的服务器ID,但是还是没有注册成功,那就有可能是你权限没开
服务器ID的获取方式是
打开开发者模式
->右键服务器名
->复制服务器ID
,具体方法如下:指令注册Slash Command
@interactions.slash_command( name = '指令名', description = "指令简介", scopes = scopes, nsfw = True )
注册一个基本指令的注解
指令参数Slash Option
@interactions.slash_option( name = 'tags', description = "指定图片的tag (可选, 用逗号分隔)", required = False, opt_type = interactions.OptionType.STRING )
name代表这个参数的名字,description就是简介
required
是指定这个参数是不是必要的,如果不必要,那么在函数的参数声明里必须给它一个默认值,比如下面代码的tags = ""
opt_type
就是参数的类型,这个类型在interactions.OptionType
这个枚举里面函数内部:
await ctx.defer() await Setu.HandleRequest(ctx, tags, r18, exclude_ai)
await ctx.defer()
这个代码是让机器人显示一个正在响应
的文字,通常一些耗时操作需要用到这个指令,正好我们下载图片挺耗时的,就加上这个
测试
以上步骤没问题的话,这个功能应该就能正常实现了,成果图如下: