quarry
использует twisted
, и чтобы использовать его с asyncio
, вам нужно будет установить AsyncioReactor
в начале вашего приложения.
import asyncio
from twisted.internet import asyncioreactor
asyncioreactor.install(asyncio.get_event_loop())
После этого вы необходимо только выполнить bot.start()
, поскольку quarry
зависит от работающего реактора, и просто запустить событие l oop. Вы не можете await reactor.run()
, потому что это не сопрограмма, поэтому просто избавьтесь от этой функции.
Обновление: добавлен пример и объяснение
У меня был шанс go вместо discord.py
кода немного и обнаружил, что discord.Client._connect()
- это в основном «код блокировки», если вы await
это, но только если у вас есть другая задача, ожидающая завершения bot.start()
в том же async def
. Другими словами, это бесконечный опросчик l oop, который никогда ничего не возвращает, поэтому не может пройти дальше точки await
. Чтобы избежать этого, вы можете создать Task
. Этот пример «должен» работать (я не пробовал его на реальном сервере, так как у меня нет клиента Minecraft под рукой). Он запускает сервер, а затем, когда клиенты пытаются подключиться, они получают сообщение о том, что сервер Minecraft не работает, а затем загружаются. Когда пользователь пытается подключиться, на канал также отправляется сообщение Discord.
import asyncio
from twisted.internet import asyncioreactor
asyncioreactor.install(asyncio.get_event_loop())
import discord
from quarry.net.server import ServerFactory, ServerProtocol
from twisted.internet import endpoints, reactor
async def start():
loop = asyncio.get_event_loop()
minecraft_server = DowntimeFactory()
minecraft_server_host = "0.0.0.0"
minecraft_server_port = 25565
discord_bot_token = "APP_TOKEN"
discord_channel_id = 0 # CHANNEL ID
# Start a quarry server
quarry_server = endpoints.TCP4ServerEndpoint(
reactor,
port=minecraft_server_port,
interface=minecraft_server_host,
)
try:
await quarry_server.listen(minecraft_server).asFuture(loop)
except Exception as err:
print(err)
loop.stop()
# Start Discord poller
discord_client = discord.Client(loop=loop)
loop.create_task(discord_client.start(discord_bot_token))
@discord_client.event
async def on_ready():
"""
After the Discord client connects, set the discord_channel attribute in the
quarry factory so that server can access Discord functionality.
"""
discord_channel = discord_client.get_channel(discord_channel_id)
if discord_channel:
minecraft_server.discord_channel = discord_channel
await discord_channel.send("hello world")
else:
print("[!] channel no found")
loop.stop()
class DowntimeProtocol(ServerProtocol):
def packet_login_start(self, buff):
buff.discard()
self.close(self.factory.motd)
# The Discord channel might not have been set up yet
if self.factory.discord_channel:
self.factory.loop.create_task(
self.factory.discord_channel.send(
"Someone is trying to access the Minecraft server!"
)
)
class DowntimeFactory(ServerFactory):
motd = "Down for maintenance"
loop = asyncio.get_event_loop()
discord_channel = None
protocol = DowntimeProtocol
asyncio.get_event_loop().create_task(start())
try:
asyncio.get_event_loop().run_forever()
except KeyboardInterrupt:
pass