【问题标题】:Need to await a function inside a sync function需要在同步函数中等待一个函数
【发布时间】:2020-12-31 17:18:59
【问题描述】:

我已经尝试解决这个问题 2 天了,阅读了 StackOverflow 中的 50 个问题和大量有关 Python 的文档。我不知道还能尝试什么。

我的代码

import discord
import re
from datetime import datetime
import mysql.connector
from discord.ext import commands, tasks
from discord.utils import get
import atexit
from pynput import keyboard
import asyncio
from asgiref.sync import async_to_sync, sync_to_async

# The currently active modifiers
current = set()

# The key combination to check
COMBINATIONS_MUTE = [
    {keyboard.Key.shift, keyboard.KeyCode(char='a')},
    {keyboard.Key.shift, keyboard.KeyCode(char='A')}
]


COMBINATIONS_UNMUTE = [
    {keyboard.Key.shift, keyboard.KeyCode(char='b')},
    {keyboard.Key.shift, keyboard.KeyCode(char='B')}
]



GUILD_ID=guild_id
CHANNELS = ""
GUILD = ""
VOICE_CHANNEL = ""

client = commands.Bot(command_prefix = ".")

def get_channel(name):
    global CHANNELS
    for channel in CHANNELS:
        if (channel.name == name) and (channel.type.name == "voice"):
            return channel
    return False

def get_voice_channels():
    global CHANNELS
    channels = []
    for channel in CHANNELS:
        if (channel.type.name == "voice"):
            channels.append(channel)
    return channels

async def mute_all():
    global VOICE_CHANNELS
    print("mute all")
    for channel in VOICE_CHANNELS:
        for member in channel.members:
            await member.edit(mute=True)

async def unmute_all():
    global VOICE_CHANNELS
    print("unmute all")
    for channel in VOICE_CHANNELS:
        for member in channel.members:
            await member.edit(mute=False)


def on_press(key):
    if any([key in COMBO for COMBO in COMBINATIONS_MUTE]) and not key in current:
        current.add(key)
        if any(all(k in current for k in COMBO) for COMBO in COMBINATIONS_MUTE):
            asyncio.run(mute_all())
    elif any([key in COMBO for COMBO in COMBINATIONS_UNMUTE]):
        current.add(key)
        if any(all(k in current for k in COMBO) for COMBO in COMBINATIONS_UNMUTE):
            asyncio.run(unmute_all())

def on_release(key):
    if any([key in COMBO for COMBO in COMBINATIONS_MUTE]):
        current.remove(key)
    elif any([key in COMBO for COMBO in COMBINATIONS_UNMUTE]):
        current.remove(key)


@client.event
async def on_ready():
    global GUILD
    global CHANNELS
    global VOICE_CHANNELS
    global GUILD_ID

    GUILD = client.get_guild(GUILD_ID)

    CHANNELS = GUILD.channels

    VOICE_CHANNELS = get_voice_channels()
    print("bot ready")
    loop = asyncio.get_event_loop()
    with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
        listener.join()

client.run('token')

基本上应该做的是:

  1. 当我按下 Shift+A 时,每个语音通道中的所有人都静音。
  2. 当我按 Shitf+B 时,将每个语音频道中的所有人取消静音。

问题是我需要等待member.edit(),但我不能,因为我不能等待mute_all(),也不能等待unmute_all(),因为on_press(key) 不是异步的,我不能让它异步,因为keyboard_listener 不会让我这么做的。

我尝试过的(没有成功)

  1. 在member.edit() 上使用asyncio.run()。
  2. 在mute_all() 上使用asyncio.run()。
  3. 使on_press(key)异步。
  4. 在member.edit() 上使用async_to_sync()。
  5. 在mute_all() 上使用async_to_sync()。

我不知道还能尝试什么。

【问题讨论】:

    标签: python discord.py


    【解决方案1】:

    keyboard.Listener 不是异步的,所以它会阻塞on_ready 中的事件循环,你需要在执行器中初始化监听器,这种情况下我们可以使用默认的。

    这是我为了让这部分工作而进行的所有更改:

    def keyboard_listener():
        with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
            listener.join()
    
    @client.event
    async def on_ready():
        global GUILD
        global CHANNELS
        global VOICE_CHANNELS
        global GUILD_ID
    
        GUILD = client.get_guild(GUILD_ID)
    
        CHANNELS = GUILD.channels
    
        VOICE_CHANNELS = get_voice_channels()
        print("bot ready")
    
        loop.run_in_executor(None, keyboard_listener)
    
    
    loop = asyncio.get_event_loop()
    

    然后我用loop.create_task替换了你所有的asyncio.run

    如果我的说明不清楚,请随时提出问题。

    从 async 到 sync 再回到 async 是很恶心的,但是嘿它有效,所以我猜它并不愚蠢

    我还稍微更改了您的 mute_all 和 unmute_all,因为您当前的实施只会影响机器人最初连接时频道中的语音用户。这是我的更改示例:

    async def mute_all():
        print("mute all")
        for channel in client.get_guild(GUILD_ID).channels:
            if not channel.type.name == "voice":
                continue
            for member in channel.members:
                await member.edit(mute=True)
    

    【讨论】:

    • 您好,您的回答有效!抱歉耽搁了,我大学和工作都很忙。感谢您的帮助。
    【解决方案2】:

    试试这两个

    #1st Way
    loop = asyncio.get_event_loop()
    loop.run_until_complete(async_function())
    
    #2nd Way
    asyncio.run_coroutine_threadsafe(async_function(), client.loop)
    

    【讨论】:

    • 第一个选项给出There is no current event loop in thread 'Thread-2'.。 (我用您提供的代码替换了asyncio.run())。第二个选项不执行该功能,不进行打印,也没有任何反应。
    【解决方案3】:

    如果您有事件循环,则可以使用 loop.create_task 调用异步函数。

    discord.py 有一个可以使用的事件循环,即Client.loop。

    def on_press(key):
        if any([key in COMBO for COMBO in COMBINATIONS_MUTE]) and not key in current:
            current.add(key)
            if any(all(k in current for k in COMBO) for COMBO in COMBINATIONS_MUTE):
                client.loop.create_task(mute_all())
        elif any([key in COMBO for COMBO in COMBINATIONS_UNMUTE]):
            current.add(key)
            if any(all(k in current for k in COMBO) for COMBO in COMBINATIONS_UNMUTE):
                client.loop.create_task(unmute_all())
    

    【讨论】:

    • 你好,当我使用create_task() 时没有任何反应,没有错误,没有输出。
    • 我已经用我的答案测试了你的代码,只有在某些情况下组合键会静音,我永远无法取消静音。我在无法删除键时遇到错误。所以也许它需要一些工作,或者它不适用于discord.py。
    • 嗨,静音/取消静音代码没有任何问题,添加键盘侦听器时出现问题,因为我无法使 on_press 函数异步。我还读到,在使用 create_task() 之后,我需要等待创建的任务,而我不能在非异步函数中执行此任务。
    猜你喜欢
    • 2021-10-02
    • 1970-01-01
    • 2021-10-16
    • 1970-01-01
    • 1970-01-01
    • 2020-01-28
    • 2021-10-16
    • 2020-10-20
    • 2017-01-15
    相关资源
    最近更新 更多