Это связано с тем, что клиентский модуль discord нуждается в контроле один раз в минуту или около того.
Это означает, что любая функция, которая крадет контроль более чем на определенное время, заставляет клиента разборки вводить недопустимое состояние (которое будет проявляться как исключение через некоторое время, возможно, при следующем вызове метода клиентом).
Для обеспечения того, чтобы клиент модуля discord мог выполнить ping-сервер разборки, вы должны использовать истинное многопоточное решение.
Одним из решений является разгрузка всей тяжелой обработки на отдельный процесс (отдельный поток не будет выполняться, поскольку Python имеет глобальную блокировку интерпретатора) и использовать бот-дисконт как тонкий слой, задачей которого является заполнение рабочих очередей.
Связанные чтения: https://discordpy.readthedocs.io/en/latest/faq.html#what-does-blocking-mean
Пример решения ... это WAY выходит за рамки этой проблемы, но я уже код в основном написан. Если бы у меня было больше времени, я бы написать короче решение :)
2 частей, разногласие взаимодействие и сервер обработки:
Это диссонанс слушателя.
import discord
import re
import asyncio
import traceback
import websockets
import json
# Call a function on other server
async def call(methodName, *args, **kwargs):
async with websockets.connect('ws://localhost:9001/meow') as websocket:
payload = json.dumps({"method":methodName, "args":args, "kwargs": kwargs})
await websocket.send(payload)
#...
resp = await websocket.recv()
#...
return resp
client = discord.Client()
tok = open("token.dat").read()
@client.event
async def on_ready():
print('Logged in as')
print(client.user.name)
print(client.user.id)
print('------')
@client.event
async def on_error(event, *args, **kwargs):
print("Error?")
@client.event
async def on_message(message):
try:
if message.author.id == client.user.id:
return
m = re.match("(\w+) for (\d+).*?", message.content)
if m:
g = m.groups(1)
methodName = g[0]
someNumber = int(g[1])
response = await call(methodName, someNumber)
if response:
await client.send_message(message.channel, response[0:2000])
except Exception as e:
print (e)
print (traceback.format_exc())
client.run(tok)
Это рабочий сервер для обработки тяжелых запросов. Вы можете синхронизировать эту часть или асинхронно.
Я решил использовать некоторую магию, называемую websocket, для отправки данных из одного процесса python в другой. Но вы можете использовать все, что хотите. Вы можете сделать один сценарий для записи файлов в каталог, а другой скрипт может, например, прочитать файлы и обработать их.
import tornado
import tornado.websocket
import tornado.httpserver
import json
import asyncio
import inspect
import time
class Handler:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def consume(self, text):
return "You said {0} and I say hiya".format(text)
async def sweeps(self, len):
await asyncio.sleep(len)
return "Slept for {0} seconds asynchronously!".format(len)
def sleeps(self, len):
time.sleep(len)
return "Slept for {0} seconds synchronously!".format(len)
class MyService(Handler, tornado.websocket.WebSocketHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def stop(self):
Handler.server.stop()
def open(self):
print("WebSocket opened")
def on_message(self, message):
print (message)
j = json.loads(message)
methodName = j["method"]
args = j.get("args",())
method = getattr(self, methodName)
if inspect.iscoroutinefunction(method):
loop = asyncio.get_event_loop()
task = loop.create_task(method(*args))
task.add_done_callback(lambda res: self.write_message(res.result()))
future = asyncio.ensure_future(task)
elif method:
resp = method(*args)
self.write_message(resp)
def on_close(self):
print("WebSocket closed")
application = tornado.web.Application([
(r'/meow', MyService),
])
if __name__ == "__main__":
from tornado.platform.asyncio import AsyncIOMainLoop
AsyncIOMainLoop().install()
http_server = tornado.httpserver.HTTPServer(application)
Handler.server = http_server
http_server.listen(9001)
asyncio.get_event_loop().run_forever()
Теперь, если запустить оба процесс в отдельных сценариях Python, и сказать бот «сон на 100», он будет спать в течение 100 секунд счастливо! Функция asyncio функционирует как рабочая очередь make-shift, и вы можете должным образом отделить прослушиватель от внутренней обработки, запустив их в виде отдельных скриптов python.
Теперь, независимо от того, как долго ваши функции выполняются в части «сервер», клиентская часть никогда не будет предотвращена для проверки сервера раздора.
Изображение не удалось загрузить, но ... так или иначе, это как сказать боту спать и ответить ... обратите внимание, что сон синхронный. http://i.imgur.com/N4ZPPbB.png
Благодарим вас за понимание в работе раздора. К сожалению, я не эксперт в потоковом и асинхронном программировании. Не могли бы вы объяснить немного больше, что такое «тонкий слой» и как его реализовать? – shrx
Существует много способов, и обсуждение выходит за рамки этого вопроса imo. Я просмотрю свой личный код (это довольно плохо ... потому что я написал ot) и посмотрю, могу ли я извлечь некоторые фрагменты и идеи для вас, хотя :) – JamEnergy
Благодарим вас за подробное объяснение с примером. – shrx