簡體   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