実現したいこと
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("こんにちは")
以下のような質問にはグッドを送りましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
グッドが多くついた質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
下記のような質問は推奨されていません。
- 間違っている
- 質問になっていない投稿
- スパムや攻撃的な表現を用いた投稿
適切な質問に修正を依頼しましょう。
回答2件
1
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
総合スコア8534
1
ベストアンサー
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総合スコア7759
良いと思った回答にはグッドを送りましょう。
グッドが多くついた回答ほどページの上位に表示されるので、他の人が素晴らしい回答を見つけやすくなります。
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
このような回答には修正を依頼しましょう。
回答へのコメント
2022/12/08 15:11
関連した質問
解決済
回答2
クリップ0
更新
2023/01/24
JavaScript forとsetTimeoutと配列を組み合わせた謎の挙動について
解決済
回答3
クリップ0
更新
2023/01/19
整数 Nが与えられたときの2^Nを10で割った余りを計算しなさい
解決済
回答3
クリップ0
更新
2023/01/26
JSで変数に非同期で取得した値を格納したい。
解決済
回答2
クリップ0
更新
2023/01/25
あるAPIを実行してから次のAPIを実行したい
解決済
回答2
クリップ1
更新
2023/01/22
最速で配列からデータを取り出し、正規表現で検索したい。
解決済
回答2
クリップ0
更新
2023/01/26
c言語にて総当たり戦の各チーム名と点数状況の表示
受付中
回答2
クリップ0
更新
2023/01/27
file system access apiで読み込んだディレクトリのハンドルを保存したい
解決済
回答1
クリップ0
更新
2023/01/28
DiscodeのBOT自身以外の発言の取得の際に、一部のデータが空になる
同じタグがついた質問を見る
Discordは、ゲーマー向けのボイスチャットアプリです。チャット・通話がブラウザ上で利用可能で、個人専用サーバーも開設できます。通話中でも音楽を流したり、PC画面を共有できるなど多機能な点が特徴です。
Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。
良いと思った回答にはグッドを送りましょう。
グッドが多くついた回答ほどページの上位に表示されるので、他の人が素晴らしい回答を見つけやすくなります。
下記のような回答は推奨されていません。
このような回答には修正を依頼しましょう。
2022/12/08 15:18