実現したいこと
sleepで待機しているプログラムを途中でやめさせることはできるのでしょうか?
ディスコードで「こんにちは」と送信されたら20秒後に「こんにちは」と返すようにしているのですが
「こんにちは」と送信した人がそのメッセージを消すと「こんにちは」と返さないようにしたいです?
該当のソースコード
#メッセージが送信されたら async def on_message(message: discord.Message): # botを対象からはずす if message.author.bot: return #「こんにちは」と送信されたら if message.content == "こんにちは": await asyncio.sleep(20) #「こんにちは」を投稿する await message.channel.send("こんにちは")
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

回答2件
0
asyncio.sleep の代わりに asyncio.Event.wait / asyncio.wait_for を使いましょう
- wait()
内部フラグが真になるまでブロックします。
- set()
内部フラグを真に設定します。それを待っていたすべてのコルーチンが再開します。wait() を呼び出していたコルーチンへのブロックが解除されます。
asyncioのEvent.waitは timeout 引数を取らないので、
wait_for により timeout することで、sleep と同等の待機をします。
タイムアウト時は asyncio.TimeoutError 例外が投げられる点に注意。
python
1 2import asyncio 3 4 5# threading.Event と同じような wait に timeout を設定できるようにする場合 6class Event(asyncio.Event): 7 async def wait(self, timeout=None): 8 try: 9 await asyncio.wait_for(super().wait(), timeout) 10 except asyncio.TimeoutError: 11 pass 12 13 14 15async def main(): 16 from time import time 17 18 start_time = time() 19 20 event = asyncio.Event() 21 loop = asyncio.get_running_loop() 22 23 # 3秒後に解除 24 loop.call_later(3, event.set) 25 26 # 5秒間スリープ 27 try: 28 event.clear() 29 await asyncio.wait_for(event.wait(), 5) 30 except asyncio.TimeoutError: 31 pass 32 print(time() - start_time) 33 34 ev = Event() 35 loop.call_later(3, ev.set) 36 await ev.wait(timeout=5) 37 38 39if __name__ == '__main__': 40 asyncio.run(main()) 41 42``' 43 44sleepしたい場所で wait_for/event.wait 45キャンセルしたい場合は、event.set を呼び出し、 46event.is_set により、キャンセルされたかどうかを判別できます。 47 48質問のコードに適応させるには、メッセージのキャンセルを受ける側の関数への 49event オブジェクトの受け渡しに工夫が必要になると思います。 50関数外にuser毎に辞書に格納する、等。
投稿2022/12/08 14:25
総合スコア8817
0
ベストアンサー
asyncio.sleepを直接キャンセルするような処理は、sleepを使用している全ての処理に影響を与えるため、あまり推奨されません。
識別子を入れる等して回避する方法はありますが複雑になります。
https://stackoverflow.com/questions/37209864/interrupt-all-asyncio-sleep-currently-executing
よりシンプルには、下記のようにasyncio の wait_for と Event を使う方法があります。
-
メッセージID に対応したイベントごとに20秒間待ち、その間に削除された場合は、そのメッセージに対する返答をキャンセルします。
-
20秒の間に削除されなかった場合はタイムアウトとなり、返答されることになります。
python
1import discord 2import asyncio 3from asyncio import wait_for, Event 4 5TOKEN = 'discord botのトークン' 6intents = discord.Intents.default() 7intents.message_content = True 8client = discord.Client(intents=intents) 9events = {} # メッセージIDに対応するイベントを格納する辞書 10 11@client.event 12async def on_message_delete(message): 13 if message.id in events: 14 # 削除されたメッセージに対応する event を set。 15 events[message.id].set() 16 print("メッセージが削除されました") 17 18 19@client.event 20async def on_message(message): 21 # botを対象からはずす 22 if message.author.bot: 23 return 24 #「こんにちは」と送信されたら 25 if message.content == "こんにちは": 26 try: 27 # 20秒の間、削除を待つ。 28 events[message.id] = Event() 29 await wait_for(events[message.id].wait(), timeout=20) 30 except asyncio.exceptions.TimeoutError: 31 # 20秒の間にメッセージが削除されなかった場合、botに返事させる。 32 await message.channel.send('こんにちは') 33 finally: 34 events.pop(message.id) 35 36 37client.run(TOKEN)
投稿2022/12/08 14:23
編集2022/12/08 14:46
退会済みユーザー
総合スコア0
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

退会済みユーザー
2022/12/08 14:53 編集
2022/12/08 15:11

あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2022/12/08 15:18