1
0

🐶がご飯を食べるために、discord botで時報を作った話。

Posted at

愛犬アレックスの独り言

🐶「お腹すいたわん、でも、ご主人はdiscordで通話中。困ったわん」
🐶「そうだ!discord経由で連絡すればいいわん!」
🐶「この間ご主人がくれたこのボタン、discordのbotになってるって言ってたわん!」
🐶「押してみよ!」

U・ω・U)ノ凸”ポチッ

bot「🐶<ご飯くれ!」
ワシ「あー忘れてたごめん!!」
🐶「やったー!ご飯だわん」

🐶「でも、毎日これをやるのは面倒だわん」

今日は、🐶が頑張らなくても、

  • 特定の時間
  • 特定のチャンネルに
  • 任意の曜日に
  • 任意のメッセージを送信する
    botを作るわん!

前提条件・実行環境

  • python3以上
  • discord.pyのインストール
  • disocrdでのアプリ作成・TOKENなどの取得
  • botの権限周りの設定が完了している
  • 投稿日現在は機能しているが、discordAPIのアップデートによる仕様変更の可能性あり

上記の環境設定が完了していることを前提とします。

階層構造

├── scheduler.py
└── .env

コードの内容

.env
DISCORD_BOT_TOKEN='自分のTOKEN。公開してはいけない'
DEFAULT_CHANNEL_ID='デフォルトで設定しておくchannelのid'

※.envは公開しないでください!

scheduler.py
import datetime
import schedule
import time
import discord
from dotenv import load_dotenv
import os
import asyncio

# .envファイルを読み込みます
load_dotenv()
TOKEN = os.getenv('DISCORD_BOT_TOKEN')
DEFAULT_CHANNEL_ID = intos.getenv('DEFAULT_CHANNEL_ID'))

# 基本的な設定
intents = discord.Intents.default()
intents.messages = True
client = discord.Client(intents=intents)

# botの起動と同時に走る関数
@client.event
async def on_ready():
    print(f'Logged in as {client.user}')  # ログインしたユーザーの名前を出力
    await send_message("起動しました", DEFAULT_CHANNEL_ID)

# メッセージを送るための基本的な関数
async def send_message(msg, channel_id):
    try:
        channel = client.get_channel(channel_id)
        if channel:
            await channel.send(msg)
            print(f'Message sent to channel {channel.name}')
        else:
            print(f'Channel with ID {channel_id} not found.')
    except Exception as e:
        print(f'Error sending message: {e}')

# job通知を非同期で実行
async def job(msg, channel_id):
    now = datetime.datetime.now()
    print(str(now) + " 通知したよ")
    await send_message(str(now) + " : " + msg, channel_id)

# 曜日の判定
def schedule_job(msg, weekdays, channel_id):
    now = datetime.datetime.now()
    if now.weekday() in weekdays:
        client.loop.call_soon_threadsafe(asyncio.create_task, job(msg, channel_id))

# スケジュール設定 ここの部分で個別に設定していく
schedule.every().day.at("16:13").do(lambda: schedule_job("毎日", range(7), DEFAULT_CHANNEL_ID))  # 0-6: Monday to Sunday
schedule.every().day.at("16:13").do(lambda: schedule_job("火曜日", [1], DEFAULT_CHANNEL_ID))  # 1: Tuesday
schedule.every().day.at("16:13").do(lambda: schedule_job("スケジュール", range(5), DEFAULT_CHANNEL_ID))  # 0-4: Monday to Friday

# スケジュールを実行する関数
def run_schedule():
    while True:
        schedule.run_pending()
        time.sleep(60) # 60秒に一度判定を行う

# スケジュール実行を別スレッドで行う
import threading
schedule_thread = threading.Thread(target=run_schedule)
schedule_thread.start()

# ボットの起動
client.run(TOKEN)

詳細説明

このPythonスクリプトは、Discordボットを使用して定期的なメッセージ送信を管理するものです。以下では、スクリプトの各部分について詳細に説明します。

1. ライブラリのインポートと環境変数の読み込み

import datetime
import schedule
import time
import discord
from dotenv import load_dotenv
import os
import asyncio
  • datetime, schedule, time, discord: 時間処理、スケジューリング、Discord API、環境変数の操作に必要なライブラリをインポートします。
  • load_dotenv(), os.getenv(): .env ファイルから環境変数を読み込みます。DISCORD_BOT_TOKENDEFAULT_CHANNEL_IDがここで使用されています。

2. Discordクライアントのセットアップ

# 基本的な設定
intents = discord.Intents.default()
intents.messages = True
client = discord.Client(intents=intents)
  • discord.Client インスタンスを作成し、イベントを処理するために必要な設定を行います。メッセージの送受信を有効にしています。

3. イベントハンドラーの定義

# botの起動と同時に走る関数
@client.event
async def on_ready():
    print(f'Logged in as {client.user}')  # ログインしたユーザーの名前を出力
    await send_message("起動しました", DEFAULT_CHANNEL_ID)
  • on_ready() 関数は、Discordにボットがログインし準備が完了したときに実行されます。send_message() を使用して指定されたチャンネルにメッセージを送信します。

4. メッセージ送信のための関数

# メッセージを送るための基本的な関数
async def send_message(msg, channel_id):
    try:
        channel = client.get_channel(channel_id)
        if channel:
            await channel.send(msg)
            print(f'Message sent to channel {channel.name}')
        else:
            print(f'Channel with ID {channel_id} not found.')
    except Exception as e:
        print(f'Error sending message: {e}')
  • send_message() 関数は、指定されたチャンネルIDにメッセージを非同期で送信します。チャンネルが見つからない場合やエラーが発生した場合に例外を処理します。

5. スケジュール関数と非同期ジョブ

# job通知を非同期で実行
async def job(msg, channel_id):
    now = datetime.datetime.now()
    print(str(now) + " 通知したよ")
    await send_message(str(now) + " : " + msg, channel_id)

# 曜日の判定
def schedule_job(msg, weekdays, channel_id):
    now = datetime.datetime.now()
    if now.weekday() in weekdays:
        client.loop.call_soon_threadsafe(asyncio.create_task, job(msg, channel_id))
  • job() 関数は非同期で実行され、指定されたメッセージとチャンネルIDでメッセージを送信します。
  • schedule_job() 関数は、特定の曜日にジョブをスケジュールするために使用されます。schedule.every().day.at() で指定された時間に、lambda を使用して非同期のジョブが実行されます。

6. スケジュールの設定と実行

# スケジュール設定 ここの部分で個別に設定していく
schedule.every().day.at("16:13").do(lambda: schedule_job("毎日", range(7), DEFAULT_CHANNEL_ID))  # 0-6: Monday to Sunday
schedule.every().day.at("16:13").do(lambda: schedule_job("火曜日", [1], DEFAULT_CHANNEL_ID))  # 1: Tuesday
schedule.every().day.at("16:13").do(lambda: schedule_job("スケジュール", range(5), DEFAULT_CHANNEL_ID))  # 0-4: Monday to Friday
  • schedule.every().day.at() を使用して、特定の時間にスケジュールを設定します。lambda を使用して、非同期の schedule_job() 関数を実行し、ジョブをスケジュールします。

7. スケジュール実行の管理

# スケジュールを実行する関数
def run_schedule():
    while True:
        schedule.run_pending()
        time.sleep(60) # 60秒に一度判定を行う

# スケジュール実行を別スレッドで行う
import threading
schedule_thread = threading.Thread(target=run_schedule)
schedule_thread.start()
  • run_schedule() 関数は、スケジュールされたジョブが実行されるように定期的に schedule.run_pending() を呼び出します。
  • threading.Thread を使用して、スケジュール実行をメインスレッドとは別のスレッドで行います。

8. ボットの起動

# ボットの起動
client.run(TOKEN)
  • client.run() を使用して、Discordボットを起動します。これにより、Discord APIに接続し、イベントの受信や処理が開始されます。

これらの要素が組み合わさって、このスクリプトは指定されたチャンネルに定期的にメッセージを送信する Discord ボットを実現しています。

終わりに

🐶「やっった!わざわざご主人に連絡する必要なくなった!」
🐶「明日からは空腹に耐えずに済むぜ〜」

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0