Невозможно одновременно использовать discord.py и карьер - PullRequest
0 голосов
/ 30 мая 2020

Я пытаюсь создать бота Discord, используя discord.py, который использует библиотеку quarry для взаимодействия с сервером minecraft.

К сожалению, как только я запускаю бота Discord, используя bot.run(), я не могу запустите реактор для карьера с помощью reactor.run().

Я просмотрел, включая aiostream и asyncio, но не могу найти решения. Я также изучил витой, поскольку карьер использует это.

РЕДАКТИРОВАТЬ: Включая текущий код.

import asyncio
from twisted.internet import asyncioreactor

asyncioreactor.install(asyncio.get_event_loop())

from quarry.net.client import SpawningClientProtocol, ClientFactory
from quarry.net.auth import Profile
from discord.ext import commands

profile = Profile.from_credentials("email", "password")
bot = commands.Bot(command_prefix=">")

async def start():
    loop = asyncio.get_event_loop()

    loop.create_task(bot.start("token"))

    client = MinecraftBotFactory(profile)

    client.connect("creative.starlegacy.net", 25565)

class MinecraftBotProtocol(SpawningClientProtocol):
    pass

class MinecraftBotFactory(ClientFactory):
    protocol = MinecraftBotProtocol

asyncio.get_event_loop().create_task(start())
asyncio.get_event_loop().run_forever()

Ответы [ 2 ]

0 голосов
/ 01 июня 2020

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
0 голосов
/ 30 мая 2020

bot.run() - это блокирующий вызов, означающий, что он останавливает выполнение программы, пока оно не будет выполнено. Попробуйте альтернативу, например await bot.start():

import asyncio
from discord.ext import tasks

async def start_bot():
    await bot.start('my_token_goes_here')
@tasks.loop(count=1)
async def login_quarry():
    # login to quarry here #
login_quarry.start()


# put any code you need BEFORE this:
asyncio.get_event_loop().run_until_complete(start_bot())

В python 3.7 и выше есть более простая альтернатива:

import asyncio
from discord.ext import tasks

async def start_bot():
    await bot.start('my_token_goes_here')
@tasks.loop(count=1)
async def login_quarry():
    # login to quarry here #
login_quarry.start()


# put any code you need BEFORE this:
asyncio.run(start_bot())
...