繁体   English   中英

将 discord 机器人暴露给 API(Flask,FASTAPI)

[英]Expose discord bot to API (Flask, FASTAPI)

我正在构建一个 discord 机器人来接收来自多个系统和程序的命令。 我想将我的 discord 机器人的某些操作暴露给 REST 端点,然后在一个地方执行所述操作。

import uvicorn
from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional
from discord.ext import commands

app = FastAPI()

TOKEN = 'MY_TOKEN'

bot = commands.Bot(command_prefix='>')

class Item(BaseModel):
    name: str
    description: Optional[str] = None
    price: float
    tax: Optional[float] = None

@app.get("/")
def hello():
    return {"message":"Hello"}

@app.post("/items/")
async def create_item(item: Item):
    await send_message()
    return item

@bot.event
async def on_ready():
    print(f'{bot.user.name} has connected to Discord!')

async def send_message():
    user = await bot.fetch_user(USER_ID)
    await user.send('👀')

if __name__ == "__main__":
    bot.run('BOT_TOKEN')
    uvicorn.run(app, host='0.0.0.0')

当我尝试运行它时,我只看到机器人处于活动状态。 我对 python 有点新,但我是一名资深程序员。 这是由于python“缺乏”多线程吗? 还是端口使用?

最终目标是调用“/items/”端点并在 discord 上查看发送给我的消息

编辑

我尝试了所有的答案并想出了一些我自己的答案。 问题是多线程。 我对此感到沮丧,最后只是将这件作品移至 Node.js。 它在技术上并不能解决这个问题,但比导航 python 多线程要容易得多。

server.js:

var express = require('express');
var app = express();
const Discord = require('discord.js');
const client = new Discord.Client();

app.get('/listUsers', function (req, res) {

    dm_user();

    res.send('hello');
})

client.on('ready', () => {
  console.log(`Logged in as ${client.user.tag}!`);
});

client.on('message', msg => {
  if (msg.content === 'ping') {
    msg.reply('pong');
  }
});

async function dm_user(id){
    var my_user = await client.users.fetch('USER_ID');
    console.log(my_user);
}

var server = app.listen(8081, function () {
   var host = server.address().address
   var port = server.address().port
   console.log("Example app listening at http://%s:%s", host, port)
   client.login('TOKEN');
})

根据 discord.py 文档bot.run()是“从您那里抽象出事件循环初始化的阻塞调用”。 他们还说,如果我们想要更多地控制循环,我们可以使用 start() 协程而不是 run()。 所以现在我们应该创建一个任务来调用这个协程,我们知道discord.pyFastAPI都是异步应用程序。 要启动 FastAPI 应用程序,您需要一个 ASGI 服务器来处理它。 在这种情况下,我们使用Uvicorn 到目前为止,我们已经运行了 FastAPI 应用程序,现在我们需要启动我们的 discord 机器人。 根据 FastAPI 文档,我们可以使用启动/关闭事件,在主 API 启动之前调用 bot.start() 协程。

这是一个应用程序示例,该应用程序具有 API 端点,用于向不和谐的用户发送消息:

import asyncio
import discord
import uvicorn

from config import TOKEN, USER_ID
from fastapi import FastAPI

app = FastAPI()
bot = discord.Client()

@app.on_event("startup")
async def startup_event(): #this fucntion will run before the main API starts
    asyncio.create_task(bot.start(TOKEN))
    await asyncio.sleep(4) #optional sleep for established connection with discord
    print(f"{bot.user} has connected to Discord!")

@app.get("/")
async def root(msg: str): #API endpoint for sending a message to a discord's user
    user = await send_message(msg)
    return {"Message": f"'{msg}' sent to {user}"}

async def send_message(message):
    user = await bot.fetch_user(USER_ID)
    await user.send(message)
    return user #for optional log in the response of endpoint

if __name__ == "__main__":
    uvicorn.run(app, host="localhost", port=5000)

用 Python 3.7.4 测试

您没有从您的send_message function 中返回任何内容。 像这样的事情应该做得很好。

@app.post("/items/")
async def create_item(item: Item):
    msg = await send_message()
    return msg


async def send_message():
    user = await bot.fetch_user(USER_ID)
    return await user.send('👀')

代码bot.run(...)一直运行,它会阻止下一行开始 API。 您必须在单独的线程或进程中运行其中之一。

我试图在thread中运行bot

if __name__ == "__main__":
    import threading 

    print('Starting bot')
    t = threading.Thread(target=bot.start, args=(TOKEN,))
    t.start()
    
    print('Starting API')
    uvicorn.run(app, host='0.0.0.0')

但它给我的信息是bot应该在主线程中运行。

但是我在Python 中同时发现了问题 Discord bot 和 bottle并基于它创建了适合我的代码

if __name__ == "__main__":
    import asyncio
    
    print('Starting bot')
    bot_app = bot.start(TOKEN)
    bot_task = asyncio.ensure_future(bot_app)
    
    print('Starting API')
    uvicorn.run(app, host='0.0.0.0')

但我不确定这是否是优雅的方法,因为uvicorn间接运行ayncio


完整版本

import uvicorn
from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional
from discord.ext import commands

app = FastAPI()

#import os
#TOKEN = os.getenv("DISCORD_TOKEN")
TOKEN = 'MY_TOKEN'

bot = commands.Bot(command_prefix='>')

class Item(BaseModel):
    name: str
    description: Optional[str] = None
    price: float
    tax: Optional[float] = None

@app.get("/")
def hello():
    return {"message":"Hello"}

@app.post("/items/")
async def create_item(item: Item):
    await send_message()
    return item

@bot.event
async def on_ready():
    print(f'{bot.user.name} has connected to Discord!')

async def send_message():
    user = await bot.fetch_user(USER_ID)
    await user.send('👀')

if __name__ == "__main__":
    import asyncio
        
    print('Starting bot')
    bot_app = bot.start(TOKEN)
    bot_task = asyncio.ensure_future(bot_app)
        
    print('Starting API')
    uvicorn.run(app, host='0.0.0.0')

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM