使用Subprocess避免长时间运行的任务断开discord.py bot的连接? [英] using Subprocess to avoid long-running task from disconnecting discord.py bot?

查看:129
本文介绍了使用Subprocess避免长时间运行的任务断开discord.py bot的连接?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我为Discord服务器创建了一个机器人,该机器人针对给定的subreddit转到Reddit API,并根据您输入的subreddit在Discord聊天中发布当天的前10名结果。它无视自我张贴,实际上只张贴图片和GIF。 Discord消息命令将如下所示: =获取有趣的www新闻编程,将每个子reddit的结果发布,因为它们是从Reddit API(PRAW)获取的。这项工作没有问题。我知道该漫游器可以访问API并发布到不和谐的功能。

I created a bot for my Discord server, that goes to the Reddit API for a given subreddit, and posts the Top 10 results for the Day in the Discord chat, based on the subreddit(s) that you input. It disregards self posts, and really only posts pictures and GIFs. The Discord message command would look something like this: =get funny awww news programming, posting the results for each subreddit as it gets them from the Reddit API (PRAW). THIS WORKS WITH NO PROBLEM. I know that the bot's ability to hit the API and post to discord works.

我添加了另一个命令 = getshuffled 它将来自子reddit的所有结果放在一个很大的列表中,然后在发布前对其进行混洗。最多约50个subreddits的要求,这确实非常有效。

I added another command =getshuffled which puts all of the results from the subreddits in a large list, and then shuffles them before posting. This works really well with a request of up to ~50 subreddits.

这是我需要的帮助:

因为它可能是这样的大量结果,来自100多个subreddits的1000多个结果,该机器人在出现非常大的请求时崩溃。根据我从问题中获得的帮助昨天,我知道出了什么问题。该机器人正在启动,正在与我的Discord服务器通信,当我向它传递一个长请求时,它在Reddit API调用完成时停止与服务器通信太长时间,并且Discord连接失败。

Because it can be such a large list of results, 1000+ results from 100+ subreddits, the bot is crashing on really big requests. Based on what help I got from my question yesterday, I understand what is going wrong. The bot is starting, it is talking to my Discord server, and when I pass it a long request, it stops talking to the server for too long while the Reddit API call is done, and it the Discord connection fails.

因此,我认为需要做的是为代码提供一个子进程,该子进程进入Reddit API并提取结果,(我认为会让不和谐的连接保持运行状态),然后在完成后将这些结果传回bot...。

So, what I think I need to do, is have a subprocess for the code that goes to the Reddit API and pulls the results, (which I think will let the discord connection stay running), and then pass those results BACK to the bot when it is finished....

或者...这是Asyncio要做的事情可以自己处理...

Or... this is something that Asyncio can handle on its own...

正如我所知,我在处理子流程调用时遇到了困难。

I'm having a hard time with the subprocess call, as I knew I would.

基本上,我要么需要这个子过程技巧的帮助,要么需要知道我是否是白痴,Asyncio可以为我处理所有这些工作。我认为这只是那些我不知道我不知道的实例之一。

Basically, I either need help with this subprocess trickery, or need to know if I'm being an idiot and Asyncio can handle all of this for me. I think this is just one of those "I don't know what I don't know" instances.

因此,回顾一下:该机器人可以在少量的情况下正常工作subreddits被改组。它通过发送的args(属于subreddits),获取每个帖子的信息,然后在发布不和谐的链接之前重新整理。问题在于,当它是约50+个更大的子集合时。为了使它能够以更大的数量工作,我需要让Reddit呼叫不阻止主要的不和谐连接,这就是为什么我试图建立一个子流程。

So to recap: The bot worked fine with smaller amounts of subreddits being shuffled. It goes through the args sent (which are subreddits), grabbing info for each post, and then shuffling before posting the links to discord. The problem is when it is a larger set of subreddits of ~ 50+. In order to get it to work with the larger amount, I need to have the Reddit call NOT block the main discord connection, and that's why I'm trying to make a subprocess.

Python版本为3.6,Discord.py版本为0.16.12
该机器人在PythonAnywhere上托管和运行

Python version is 3.6 and Discord.py version is 0.16.12 This bot is hosted and running on PythonAnywhere

代码:

from redditBot_auth import reddit

import discord
import asyncio
from discord.ext.commands import Bot
#from discord.ext import commands
import platform
import subprocess
import ast

client = Bot(description="Pulls posts from Reddit", command_prefix="=", pm_help = False)

@client.event
async def on_ready():
    return await client.change_presence(game=discord.Game(name='Getting The Dank Memes')) 

def is_number(s):
    try:
        int(s)
        return True
    except:
        pass

def show_title(s):
    try:
        if s == 'TITLES':
            return True
    except:
        pass

async def main_loop(*args, shuffled=False):
    print(type(args))

    q=10

    #This takes a integer value argument from the input string.
    #It sets the number variable,
    #Then deletes the number from the arguments list.
    title = False
    for item in args:
        if is_number(item):
            q = item
            q = int(q)
            if q > 15:
                q=15
            args = [x for x in args if not is_number(x)]

        if show_title(item):
            title = True
            args = [x for x in args if not show_title(x)]

    number_of_posts = q * len(args)
    results=[]

    TESTING = False #If this is turned to True, the subreddit of each post will be posted. Will use defined list of results


    if shuffled == False: #If they don't want it shuffled

        for item in args:
            #get subreddit results
            #post links into Discord as it gets them
            #The code for this works

    else: #if they do want it shuffled
        output = subprocess.run(["python3.6", "get_reddit.py", "*args"])
        results = ast.literal_eval(output.decode("ascii"))
        # ^^ this is me trying to get the results back from the other process.

。这是我的get_reddit.py文件:

. This is my get_reddit.py file:

#THIS CODE WORKS, JUST NEED TO CALL THE FUNCTION AND RETURN RESULTS
#TO THE MAIN_LOOP FUNCTION

from redditBot_auth import reddit
import random

def is_number(s):
    try:
        int(s)
        return True
    except:
        pass

def show_title(s):
    try:
        if s == 'TITLES':
            return True
    except:
        pass

async def get_results(*args, shuffled=False):

    q=10

    #This takes a integer value argument from the input string.
    #It sets the number variable,
    #Then deletes the number from the arguments list.
    title = False
    for item in args:
        if is_number(item):
            q = item
            q = int(q)
            if q > 15:
                q=15
            args = [x for x in args if not is_number(x)]

        if show_title(item):
            title = True
            args = [x for x in args if not show_title(x)]

    results=[]

    TESTING = False #If this is turned to True, the subreddit of each post will be posted. Will use defined list of results.
    NoGrabResults = False

    #This pulls the data and creates a list of links for the bot to post

    if NoGrabResults == False:
        for item in args:
            try:
                #get the posts
                #put them in results list    

            except Exception as e:
                #handle error
                pass

        try:
            #print('____SHUFFLED___')
            random.shuffle(results)
            random.shuffle(results)
            random.shuffle(results)

        except:
            #error stuff

        print(results)
#I should be able to read that print statement for the results, 
#and then use that in the main bot function to post the results.

@client.command()
async def get(*args, brief="say '=get' followed by a list of subreddits", description="To get the 10 Top posts from a subreddit, say '=get' followed by a list of subreddits:\n'=get funny news pubg'\n would get the top 10 posts for today for each subreddit and post to the chat."):
    #sr = '+'.join(args)
    await main_loop(*args)

#THIS POSTS THE POSTS RANDOMLY   
@client.command()
async def getshuffled(*args, brief="say '=getshuffled' followed by a list of subreddits", description="Does the same thing as =get, but grabs ALL of the posts and shuffles them, before posting."):

    await main_loop(*args, shuffled=True)


client.run('my ID')

更新:根据建议,我有该命令通过ThreadPoolExecutor传递,如下所示:

UPDATE: Following advice, I had the command passed through a ThreadPoolExecutor as shown:

async def main(*args, shuffled):

    if shuffled==True:

        with concurrent.futures.ThreadPoolExecutor() as pool:
            results = await asyncio.AbstractEventLoop().run_in_executor(
                executor=pool, func=await main_loop(*args, shuffled=True))
            print('custom thread pool', results)

但这在脚本执行时仍会导致错误尝试与Discord通话:

but this still results in errors when the script tries to talk to Discord:

ERROR:asyncio:Task was destroyed but it is pending!
task: <Task pending coro=<Client._run_event() running at /home/GageBrk/.local/lib/python3.6/site-packages/discord/client.py:307> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7f28acd8db28>()]>>
Event loop is closed
Destination must be Channel, PrivateChannel, User, or Object. Received NoneType
Destination must be Channel, PrivateChannel, User, or Object. Received NoneType
Destination must be Channel, PrivateChannel, User, or Object. Received NoneType
...

它可以正确发送结果,但仍然不和谐

It is sending the results correctly, but discord is still losing connection.

推荐答案

praw 依赖于请求库,这是同步的,意味着代码正在阻塞。如果阻塞代码执行的时间太长,这可能会导致您的机器人冻结。

praw relies on the requests library, which is synchronous meaning that the code is blocking. This can cause your bot to freeze if the blocking code takes too long to execute.

要解决此问题,可以创建一个单独的线程来处理阻塞代码。下面是一个示例。请注意 blocking_function 如何使用 time.sleep 来阻止10分钟(600秒)。这应该足以冻结僵尸并最终使其崩溃。但是,由于使用 run_in_executor 该函数位于其自己的线程中,因此该漫游器继续正常运行。

To get around this, a separate thread can be created that handles the blocking code. Below is an example of this. Note how blocking_function will use time.sleep to block for 10 minutes (600 seconds). This should be more than enough to freeze and eventually crash the bot. However, since the function is in it's own thread using run_in_executor, the bot continues to operate as normal.

import time
import asyncio
from discord.ext import commands
from concurrent.futures import ThreadPoolExecutor

def blocking_function():
    print('entering blocking function')
    time.sleep(600)
    print('sleep has been completed')
    return 'Pong'

client = commands.Bot(command_prefix='!')

@client.event
async def on_ready():
    print('client ready')

@client.command()
async def ping():
    loop = asyncio.get_event_loop()
    block_return = await loop.run_in_executor(ThreadPoolExecutor(), blocking_function)
    await client.say(block_return)

client.run('token')

这篇关于使用Subprocess避免长时间运行的任务断开discord.py bot的连接?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆