【discord.py】VCの寝落ち対策!指定時間に自動退出するBotの作り方

DiscordBot

「ボイスチャット(VC)で友達と話していて、ついつい寝落ちしてしまった…」

そんな悩みを解決する、指定した時間後に自動でVCから退出させてくれるDiscord Botの作り方を、ソースコード付きで解説します。

自分で時間を設定するだけでなく、他のメンバーのタイマーを設定する機能もあるので、サーバーの管理者にも便利なBotです。

どんな機能が使えるの?

  • /leave [分]
    自分自身に退出タイマーを設定します。[分]で指定した時間後に、自動でVCから退出します。
  • /cancel
    設定した退出タイマーをキャンセルします。
  • /remaining
    タイマーの残り時間を確認します。
  • /forceleave [対象者] [分]
    サーバー内の他のメンバーに対して退出タイマーを設定します。

準備するもの

  • Python環境
    PCにPythonがインストールされている必要があります。
  • discord.pyライブラリ
  • BOTトークン
    Discord Developer Portalで自分のBotを作成し、トークンを取得します。

Python環境の構築、BOTトークンの取得方法は以下の記事で紹介しています。

実行ファイルの作成

今回はWindows11環境でのセットアップ解説です。
エクスプローラーを開き、任意のフォルダを作りましょう。
ユーザーフォルダの直下がおすすめです。

フォルダを開いて、新規作成>テキストドキュメントを作成します。
ファイル名もわかりやすいもので構いません。(今回はvcTimerとしています)

ダブルクリックをするとメモ帳が開きます。
以下のソースコードをコピー&ペーストしてください。
一番下にあるbot.run('あなたのBOTトークン') の部分を、ご自身で取得したトークンに書き換えて保存してください。

import discord
from discord.ext import commands
from discord import app_commands
import asyncio
import time

intents = discord.Intents.default()
intents.guilds = True
intents.voice_states = True
intents.messages = True
intents.message_content = True

bot = commands.Bot(command_prefix=commands.when_mentioned_or("/"), intents=intents)

timers = {}
time_tracker = {}
auto_leaving = set()

@bot.event
async def on_ready():
    print(f'ログイン完了: {bot.user.name}')
    print(f'参加中のサーバー数: {len(bot.guilds)}')
    await bot.tree.sync()
    print("スラッシュコマンドが同期されました。")

@bot.tree.command(name="leave", description="指定した分後にVCから退出します。")
async def leave(interaction: discord.Interaction, minutes: int):
    member = interaction.user
    if interaction.guild is None:
        for guild in bot.guilds:
            member_in_guild = guild.get_member(member.id)
            if member_in_guild and member_in_guild.voice:
                if guild.me.guild_permissions.move_members:
                    if member.id in timers:
                        timers[member.id].cancel()

                    await interaction.response.send_message(
                        f"{member.mention} さん、{minutes}分後に {guild.name} のVCから退出します。"
                    )
                    timers[member.id] = asyncio.create_task(schedule_leave(member_in_guild, minutes, interaction))
                    time_tracker[member.id] = time.time() + (minutes * 60)
                    return
                else:
                    await interaction.response.send_message("BOTに『メンバーを移動』する権限がありません。")
                    return
        await interaction.response.send_message("どのサーバーにもVC接続が見つかりませんでした。\nサーバーに『VC退出タイマー』が参加している必要があります。")
        return

    if member.voice:
        if interaction.guild.me.guild_permissions.move_members:
            if member.id in timers:
                timers[member.id].cancel()

            await interaction.response.send_message(f"{member.mention} さん、{minutes}分後にVCから退出します。")
            timers[member.id] = asyncio.create_task(schedule_leave(member, minutes, interaction))
            time_tracker[member.id] = time.time() + (minutes * 60)
        else:
            await interaction.response.send_message("BOTに『メンバーを移動』する権限がありません。")
    else:
        await interaction.response.send_message("VCに接続してからコマンドを使用してください。")

@bot.tree.command(name="cancel", description="設定されたVC退出タイマーをキャンセルします。")
async def cancel(interaction: discord.Interaction):
    if interaction.user.id in timers:
        timers[interaction.user.id].cancel()
        del timers[interaction.user.id]
        del time_tracker[interaction.user.id]
        await interaction.response.send_message("退出タイマーをキャンセルしました。")
    else:
        await interaction.response.send_message("設定されている退出タイマーはありません。")

@bot.tree.command(name="remaining", description="設定されたVC退出タイマーの残り時間を確認します。")
async def remaining(interaction: discord.Interaction):
    if interaction.user.id in time_tracker:
        remaining_time = time_tracker[interaction.user.id] - time.time()
        await interaction.response.send_message(f"{interaction.user.mention} さん、残り時間: {int(remaining_time // 60)} 分 {int(remaining_time % 60)} 秒")
    else:
        await interaction.response.send_message("現在、設定されている退出タイマーはありません。")

@bot.tree.command(name="forceleave", description="他のメンバーを指定してVC退出タイマーを設定します(グループ内のみ)")
@app_commands.describe(
    target="タイマーを設定したいメンバー",
    minutes="何分後に退出させるか(0で即時)",
    force="(上書きしたいときだけ)既存タイマーを強制上書き"
)
async def forceleave(interaction: discord.Interaction, target: discord.Member, minutes: int, force: bool = False):
    if interaction.guild is None:
        await interaction.response.send_message("このコマンドはサーバー内でのみ使用できます。", ephemeral=True)
        return

    if not target.voice or target.voice.channel is None:
        await interaction.response.send_message(f"{target.display_name} さんはVCに接続していません。", ephemeral=True)
        return

    if not interaction.guild.me.guild_permissions.move_members:
        await interaction.response.send_message("BOTに『メンバーを移動』する権限がありません。", ephemeral=True)
        return

    if target.id in timers and not force:
        await interaction.response.send_message(f"{target.display_name} さんはすでにタイマーを設定済みです。", ephemeral=True)
        return

    if target.id in timers:
        timers[target.id].cancel()

    if minutes == 0:
        await target.move_to(None)
        await interaction.response.send_message(f"{target.mention} さんをVCから退出させました。")
        try:
            await target.send(f"{interaction.guild.name} で {interaction.user.display_name} さんがあなたをVCから退出させました。")
        except discord.Forbidden:
            pass
        return

    await interaction.response.send_message(f"{target.mention} さんを {minutes}分後にVCから退出させます。")
    timers[target.id] = asyncio.create_task(schedule_force_leave(target, minutes, interaction, interaction.user))
    time_tracker[target.id] = time.time() + (minutes * 60)

async def schedule_leave(member, minutes, interaction):
    try:
        await asyncio.sleep(minutes * 60)
        auto_leaving.add(member.id)
        await member.move_to(None)
        channel = interaction.channel
        await channel.send(f"{member.mention} さんをVCから退出させました。")
        del timers[member.id]
        del time_tracker[member.id]
    except asyncio.CancelledError:
        channel = interaction.channel
        await channel.send(f"{member.mention} さんのVC退出タイマーがキャンセルされました。")
    finally:
        auto_leaving.discard(member.id)

async def schedule_force_leave(member, minutes, interaction, caller):
    try:
        await asyncio.sleep(minutes * 60)
        auto_leaving.add(member.id)
        await member.move_to(None)
        channel = interaction.channel
        await channel.send(f"{member.mention} さんをVCから退出させました。")
        try:
            await member.send(f"{interaction.guild.name} で {caller.display_name} さんがあなたをVCから退出させました。")
        except discord.Forbidden:
            pass
        del timers[member.id]
        del time_tracker[member.id]
    except asyncio.CancelledError:
        channel = interaction.channel
        await channel.send(f"{member.mention} さんのVC退出タイマーがキャンセルされました。")
    finally:
        auto_leaving.discard(member.id)

@bot.event
async def on_voice_state_update(member, before, after):
    if member.id in auto_leaving:
        return

    if member.id in timers:
        if before.channel is not None and after.channel is None:
            timers[member.id].cancel()
            del timers[member.id]
            del time_tracker[member.id]
            try:
                await member.send("VCを手動で退出したため、タイマーがキャンセルされました。")
            except discord.Forbidden:
                pass

bot.run('あなたのBOTトークン')
GitHub - ryota-0531/Discord-VCTimer
Contribute to ryota-0531/Discord-VCTimer development by creating an account on GitHub.

エクスプローラーに戻り、
表示>表示>ファイル名拡張子にチェックを入れてください。

名前の変更でtxt部分をpyに変えてください。
これでPythonのファイルになります。

discord.pyライブラリのインストール

画面左下のWindowsスタートボタンを押し、キーボードで「cmd」と入力します。表示された「コマンド プロンプト」をクリックして起動してください。

黒い画面が表示されたら、以下のコマンドを入力し、Enterキーを押します。

pip install discord.py

ファイルの実行

いよいよファイルの実行です。

先ほど作成したpyファイルをダブルクリックで実行しましょう!

「スラッシュコマンドが同期されました。」と表示されたら成功です!

Botをサーバーに追加する

Discord Developer PortalでBotを選択したページで表示される、APPLICATION IDというものを控えてください。

以下のURLのAPPLICATION IDの箇所を控えた数字と置き換えてください。

https://discord.com/oauth2/authorize?client_id=APPLICATION ID&permissions=3214336&integration_type=0&scope=bot+applications.commands

できたURLにアクセスするとDiscordでBot追加のページが立ち上がります。

指示に従ってサーバーに追加をしてください。

Botを使ってみよう

利用できるコマンドは以下の4つです。

  • /leave minutes:
    サーバーでもBotとのDMでも利用できます。
    DMで送った場合は現在参加しているVCを自動で特定してセットします。
  • /cancel
    サーバーでもBotとのDMでも利用できます。
    セットした退出タイマーをキャンセルします。
  • /remaining
    サーバーでもBotとのDMでも利用できます。
    セットした退出タイマーの残り時間を応答します。
  • /forceleave target: minutes:
    サーバーでのみ利用できます。
    target:で指定したユーザーにminutes:で指定した時間のタイマーを設定します。
    「サーバーの管理者権限がない人が寝落ちしている人を落としたい」といった場合に利用できます。
    追加オプションでforceをTrueにすることで、現在設定されているものを上書きしてセットできます。

セットするminutesを0にすることで瞬時に退出させることも可能です。

まとめ

お疲れ様でした!これで、VCでの寝落ちの心配が減りますね!!
他のBotもぜひご覧ください!

コメント