【问题标题】:Discord bot does not accept commands when other command is running当其他命令正在运行时,Discord bot 不接受命令
【发布时间】:2020-10-04 05:38:28
【问题描述】:

我正在开发一个不和谐的机器人,它可以找到 spotify 播放列表并将曲目的 youtube 对应物排队。我有一个正在运行的循环,它使用曲目名称列表来搜索 youtube 上的视频,然后它会抓取顶部结果并发送到播放它的异步函数。但是,当机器人处于此循环中时,它不接受其他命令。我可以让它与其他命令同时运行吗?以下是我的应用程序的所有代码

from bs4 import BeautifulSoup
import urllib.request
import os
import requests
import base64
import json
import time
import discord
import shutil
from discord.utils import get
from discord import FFmpegPCMAudio
import youtube_dl
import asyncio
from asgiref.sync import async_to_sync

from discord.ext import commands
from dotenv import load_dotenv

load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')

bot = commands.Bot(command_prefix='s- ')

accessToken = ""

clientIDSECRET = os.getenv('SPOTIFY_ID')+':'+os.getenv('SPOTIFY_SECRET')

base64Auth = base64.b64encode(clientIDSECRET.encode("utf-8")).decode('ascii')
trackStrings = []

def pretty_print_POST(req):
    print('{}\n{}\r\n{}\r\n\r\n{}'.format(
        '-----------START-----------',
        req.method + ' ' + req.url,
        '\r\n'.join('{}: {}'.format(k, v) for k, v in req.headers.items()),
        req.body,
    ))

@bot.event
async def on_ready():    
    payload={'grant_type':'client_credentials'}
    headers = {'Authorization':f'Basic {base64Auth}'}
    req = requests.Request('POST', "https://accounts.spotify.com/api/token", data = payload, headers = headers)
    prep = req.prepare()
    pretty_print_POST(prep)
    s = requests.Session()
    response = s.send(prep)
    global accessToken
    accessToken = json.loads(response.content)["access_token"]
    print(accessToken)

@bot.command(pass_context=True, aliases=['sp'])
async def spotlist(ctx, userName, playlistName = "", shuffle=False, limit=100, offset=0):
    print(limit)
    if limit > 100 or limit < 0:
        await ctx.send(f'Limit out of bounds! It needs to be between 0 and 100.')
        return
    playlistId = ""
    headers = {'Authorization':f'Bearer {accessToken}'}
    if playlistName == "id":
        playlistId = userName #Username assumed to be a playlist id instead
    else:
        playlists = json.loads(requests.get(f'https://api.spotify.com/v1/users/{userName}/playlists', headers=headers).content)
        for playlist in playlists["items"]:
            if playlist["name"] == playlistName:
                playlistId = playlist["id"]
    nextURL = f'https://api.spotify.com/v1/playlists/{playlistId}/tracks?offset={offset}&limit={limit}'
    while nextURL != None:
        trackResponse = requests.get(nextURL, headers = headers)
        tracks = json.loads(trackResponse.content)
        if(tracks["total"] <= offset):
            await ctx.send(f'Offset (third argument) is too large! Your playlist is {tracks["total"]} long.')
            return
        for track in tracks["items"]:
            trackStrings.append(track["track"]["name"] + " " + track["track"]["artists"][0]["name"])
        nextURL = tracks["next"]
        if(limit != 100):
            break

    for trackString in trackStrings:
        try:
            await play(ctx, await SearchVid(trackString))
        except:
            print("couldn't find song")

@bot.command(pass_context=True, aliases=['j', 'joi'])
async def join(ctx):
    channel = ctx.message.author.voice.channel
    voice = get(bot.voice_clients, guild=ctx.guild)

    if voice and voice.is_connected():
        await voice.move_to(channel)
    else:
        voice = await channel.connect()

@bot.command(pass_context=True, aliases=['l', 'lea'])
async def leave(ctx):
    voice = get(bot.voice_clients, guild=ctx.guild)

    if voice and voice.is_connected():
        await voice.disconnect()
    else:
        await ctx.send("Don't think I am in a voice channel")


async def playSong(ctx):   
    voice = get(bot.voice_clients, guild=ctx.guild)
    DIR = os.path.abspath(os.path.realpath("Queue"))

    try:
        first_file = os.listdir(DIR)[0]
    except:
        print("No more queued song(s)\n")
        return

    song_path = os.path.abspath(os.path.realpath("Queue") + "\\" + first_file)
    async def func(x):
        os.remove(song_path)
        await playSong(ctx)
    try:
        voice.play(discord.FFmpegPCMAudio(song_path), after=async_to_sync(func))
        await ctx.send(f"playing {first_file}")
        voice.source = discord.PCMVolumeTransformer(voice.source)
        voice.source.volume = 0.07
    except:
        print("song already playing")
    still_q = len(os.listdir(DIR))
    print(f"Songs still in queue: {still_q}")


@bot.command(pass_context=True, aliases=['p', 'pla'])
async def play(ctx, url: str = ""): 

    await join(ctx) 

    Queue_infile = os.path.isdir("./Queue")
    if Queue_infile is True:
        DIR = os.path.abspath(os.path.realpath("Queue"))
        try:
            _ = os.listdir(DIR)[0]
        except:
            print("No more queued song(s)\n")
            await queue(ctx, url)
            await playSong(ctx)
        else:
            await queue(ctx, url)
            await playSong(ctx)
    else:
        return

@bot.command(pass_context=True, aliases=['s'])
async def skip(ctx):
    await stop(ctx)

@bot.command(pass_context=True, aliases=['pa', 'pau'])
async def pause(ctx):

    voice = get(bot.voice_clients, guild=ctx.guild)

    if voice and voice.is_playing():
        print("Music paused")
        voice.pause()
        await ctx.send("Music paused")
    else:
        print("Music not playing failed pause")
        await ctx.send("Music not playing failed pause")


@bot.command(pass_context=True, aliases=['r', 'res'])
async def resume(ctx):

    voice = get(bot.voice_clients, guild=ctx.guild)

    if voice and voice.is_paused():
        print("Resumed music")
        voice.resume()
        await ctx.send("Resumed music")
    else:
        print("Music is not paused")
        await ctx.send("Music is not paused")

async def stop(ctx):

    voice = get(bot.voice_clients, guild=ctx.guild)

    if voice and voice.is_playing():
        voice.stop()
    else:
        print("No music playing failed to stop")


async def SearchVid(textToSearch):
    print(textToSearch)
    query = urllib.parse.quote(textToSearch)
    url = "https://www.youtube.com/results?search_query=" + query
    response = urllib.request.urlopen(url)
    html = response.read()
    soup = BeautifulSoup(html, 'html.parser')
    for vid in soup.findAll(attrs={'class':'yt-uix-tile-link'}):
        if not vid['href'].startswith("https://googleads.g.doubleclick.net/"):
            return 'https://www.youtube.com' + vid['href']

async def queue(ctx, url: str):
    Queue_infile = os.path.isdir("./Queue")
    if Queue_infile is False:
        os.mkdir("Queue")
    DIR = os.path.abspath(os.path.realpath("Queue"))
    q_num = len(os.listdir(DIR))

    queue_path = os.path.abspath(os.path.realpath("Queue") + f"\\{q_num} %(title)s.%(ext)s")
    print(queue_path)
    ydl_opts = {
        'format': 'bestaudio/best',
        'outtmpl': queue_path,
        'postprocessors': [{
            'key': 'FFmpegExtractAudio',
            'preferredcodec': 'mp3',
            'preferredquality': '192',
        }],
    }

    with youtube_dl.YoutubeDL(ydl_opts) as ydl:
        print("Downloading audio now\n")
        print(url)
        ydl.download([url])
        name = os.listdir(DIR)[-1]
        await ctx.send("Adding song " + str(name) + " to the queue")

    print("Song added to queue\n")

bot.run(TOKEN)

【问题讨论】:

    标签: python asynchronous urllib discord.py youtube-dl


    【解决方案1】:

    您正在使用阻塞库。 Discord.py 是一个异步库,您需要使用不会阻塞代码的东西。在Frequently Asked Questions 中,您可以看到使用requests 库的示例,这是阻塞的,您应该使用aiohttp 来发出异步请求。

    【讨论】:

      【解决方案2】:

      我肯定会建议使用模块线程。我目前没有足够的时间阅读您的代码并将其放入,但我可能稍后会编辑此答案。 参考: https://docs.python.org/3/library/threading.html

      线程允许您一次运行多个函数。

      【讨论】:

      • 谢谢,我会调查的。
      • 这是一个糟糕的建议。为什么在代码异步时使用线程?您需要在代码中使同步操作异步执行。
      猜你喜欢
      • 2021-04-16
      • 1970-01-01
      • 2018-03-04
      • 1970-01-01
      • 1970-01-01
      • 2021-10-14
      • 2020-10-30
      • 2020-12-15
      • 2021-08-28
      相关资源
      最近更新 更多