[Python]Discordで指定時間に発言させるBOT

# 2025/06/23追記
どう考えても1日1回の処理に1分ごとの確認処理を走らせるのはリソースの無駄遣いが過ぎます。
AWSのlambdaやAzure functionsなどクラウド側において定期実行するみたいな感じでスマートに解決してね...><
ローカルならcronやタスクスケジューラでいいやんね（記事と矛盾）


# はじめに
Disocordbotを指定時間に発言させたいけど
タスクスケジューラだったりcronだったりは使いたくないなー
どうせBOTは常時動いてるんだし内部で処理してくれたほうが嬉しいよねってことで作りました。

# できること
タスクスケジューラ,cronを使わずに
DiscordのBOTに時間指定して発言させる。

# 環境
- Python3.7
- Discord.py 1.1.1

#d iscordにBOTを導入するあれこれ
いろいろ参考になる記事がいっぱいあるのでこちらを参考に
[Discord Botアカウント初期設定ガイド for Developer](https://qiita.com/1ntegrale9/items/cb285053f2fa5d0cccdf)
[Pythonで簡単なDiscord Botの作り方](https://qiita.com/PinappleHunter/items/af4ccdbb04727437477f)
[Pythonで実用Discord Bot(discordpy解説)](https://qiita.com/1ntegrale9/items/9d570ef8175cf178468f)

# ループ処理
時間判定をするために30秒置きに判定処理をループさせます。
ただ普通にtime.sleep()しちゃうとほかの処理が走らなくなっちゃうので
非同期処理のasyncio.sleep()を使おうかと思いましたが、
discord.pyにもっといいのがありました。
https://discordpy.readthedocs.io/ja/latest/ext/tasks/index.html

botを起動させる前に
このtasks.loopの処理を実行すればx秒ごとのループ実行ができる（らしい）

例えばloop()を60秒に1回実行する場合

```python:tasksloop.py
#coding:UTF-8
import discord
from discord.ext import tasks

TOKEN = "**********" #トークン
CHANNEL_ID = ********** #チャンネルID
# 接続に必要なオブジェクトを生成
client = discord.Client()

# 60秒に一回ループ
@tasks.loop(seconds=60)
async def loop():
    # botが起動するまで待つ
    await client.wait_until_ready()
    channel = client.get_channel(CHANNEL_ID)
    await channel.send('時間だよ')  

#ループ処理実行
loop.start()
# Botの起動とDiscordサーバーへの接続
client.run(TOKEN)
```

上記の場合だと60秒に一回「時間だよ」と発言する。
どうしようもなく邪魔なbotができてしまいます。

# 指定時間に発言する
上記の方法を使って毎朝7:00に「おはよう」と発言するbotにする場合

```python:ohayo-bot.py
#coding:UTF-8
import discord
from discord.ext import tasks
from datetime import datetime 

TOKEN = "**********" #トークン
CHANNEL_ID = ********** #チャンネルID
# 接続に必要なオブジェクトを生成
client = discord.Client()

# 60秒に一回ループ
@tasks.loop(seconds=60)
async def loop():
    # 現在の時刻
    now = datetime.now().strftime('%H:%M')
    if now == '07:00':
        channel = client.get_channel(CHANNEL_ID)
        await channel.send('おはよう')  

#ループ処理実行
loop.start()
# Botの起動とDiscordサーバーへの接続
client.run(TOKEN)
```

これで指定時間に発言させることができました。

#今回作りたかったもの
「グラブルの古戦場やってると団アビリティ発動時間いつだっけ？ってなってたので
通知してくれるようなBOTを作ろうと思い作成に至りました。」
を実現したものも下記に置いておきます

<details><summary>コード詳細</summary><div>

```python:danabiBot.py
#coding:UTF-8
import discord
from datetime import datetime
from discord.ext import tasks

TOKEN = "**********" #トークン
CHANNEL_ID = ********** #チャンネルID
# 接続に必要なオブジェクトを生成
client = discord.Client()


#投稿する日時
dateTimeList = [
'2019/05/19 19:00',
'2019/05/20 18:30',
'2019/05/21 18:30',
'2019/05/22 07:00',
'2019/05/23 07:00',
'2019/05/24 07:00',
'2019/05/25 07:00'
]

# 起動時に動作する処理
@client.event
async def on_ready():
    print('ready')

# 指定時間に走る処理
async def SendMessage():
    channel = client.get_channel(CHANNEL_ID)
    await channel.send('時間だよ')

# 30秒に一回ループ
@tasks.loop(seconds=30)
async def time_check():
    sleepTime = 0
    # 現在の時刻
    now = datetime.now().strftime('%Y/%m/%d %H:%M')
    if now in dateTimeList :
        print(now)
        await SendMessage()
        #該当時間だった場合は２重に投稿しないよう３０秒余計に待機
        await asyncio.sleep(30)

# メッセージ受信時に動作する処理
@client.event
async def on_message(message):
    # メッセージ送信者がBotだった場合は無視する
    if message.author.bot:
        return
    # 使用できるコマンド一覧
    if message.content == '!help':
        await message.channel.send('現在使用できるコマンドはありません')

#ループ処理
time_check.start()
# Botの起動とDiscordサーバーへの接続
client.run(TOKEN)

```
</div></details>

# さいごに
Schedulerとかも使いたかったけど、なんかあれね。うまくできなかった。
非同期処理との相性悪いとかなの?ぷろぐらむわかんない＾ｐ＾