Fis*_*key 1 subprocess python-3.x python-asyncio discord.py
我为我的 Discord 服务器创建了一个机器人,它转到给定 subreddit 的 Reddit API,并根据您输入的 subreddit 在 Discord 聊天中发布当天的前 10 个结果。它无视自己的帖子,实际上只发布图片和 GIF。Discord 消息命令看起来像这样:=get funny awww news programming发布每个 subreddit 的结果,因为它从 Reddit API (PRAW) 获取它们。这没有问题。我知道 bot 能够访问 API 并发布到 discord 中。
我添加了另一个命令=getshuffled,它将来自 subreddits 的所有结果放在一个大列表中,然后在发布之前将它们打乱。这对于最多约 50 个 subreddits 的请求非常有效。
这是我需要帮助的:
因为它可以是如此大的结果列表,来自 100 多个 subreddit 的 1000 多个结果,机器人在非常大的请求上崩溃。根据我昨天从我的问题中得到的帮助,我明白出了什么问题。机器人正在启动,它正在与我的 Discord 服务器通信,当我向它传递一个很长的请求时,它在 Reddit API 调用完成后停止与服务器通信的时间太长,并且 Discord 连接失败。
因此,我认为我需要做的是为转到 Reddit API 并提取结果的代码创建一个子进程(我认为这将使不和谐连接保持运行),然后将这些结果传回给机器人当它完成时......
或者...这是 Asyncio 可以自行处理的事情...
正如我所知道的那样,我在子流程调用方面遇到了困难。
基本上,我要么需要关于这个子流程技巧的帮助,要么需要知道我是否是个白痴,而 Asyncio 可以为我处理所有这些。我认为这只是“我不知道我不知道什么”的例子之一。
总结一下:该机器人运行良好,改组了少量 subreddit。它通过发送的参数(它们是 subreddits),获取每个帖子的信息,然后在将链接发布到不和谐之前进行洗牌。问题是当它是一个更大的大约 50+ 的 subreddit 集时。为了让它与更大的数量一起工作,我需要让 Reddit 调用 NOT 阻止主要的不和谐连接,这就是我试图创建一个子进程的原因。
Python 版本是 3.6,Discord.py 版本是 0.16.12 这个机器人在 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.
Run Code Online (Sandbox Code Playgroud)
. 这是我的 get_reddit.py 文件:
#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.
Run Code Online (Sandbox Code Playgroud)
.
@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')
Run Code Online (Sandbox Code Playgroud)
更新:按照建议,我让命令通过 ThreadPoolExecutor 传递,如下所示:
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)
Run Code Online (Sandbox Code Playgroud)
但是当脚本尝试与 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
...
Run Code Online (Sandbox Code Playgroud)
它正确发送结果,但不和谐仍然失去连接。
praw依赖于requests库,它是同步的,意味着代码是阻塞的。如果阻塞代码执行时间过长,这可能会导致您的机器人冻结。
为了解决这个问题,可以创建一个单独的线程来处理阻塞代码。下面是一个例子。请注意blocking_function将如何time.sleep用于阻止 10 分钟(600 秒)。这应该足以冻结并最终使机器人崩溃。但是,由于该函数在它自己的线程中使用run_in_executor,机器人将继续正常运行。
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')
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3377 次 |
| 最近记录: |