1/(1+e^(-ax))

ポケモンとかPCとか

Discord Botで予め準備しておいた音楽を連続再生する

Top→Discord Botの作り方 - 1/(1+e^(-ax))
prev→Discord Botで予め準備しておいた音楽を再生する - 1/(1+e^(-ax))
next→Discord BotでCogを使ってみる - 1/(1+e^(-ax))

前回作成したbgmコマンドを、!bgmと入力した回数だけ再生されるようにしてみます。

作成にあたりこちらの記事を参考にしました。→Pythonで始める録音機能付きDiscord Bot: (4) 音楽ファイルを再生する - Qiita


前回vc.play(...)で音楽ファイルを再生しましたが、これをvc.play(... , after= function)とすることで再生終了時に関数functionを呼び出すことができるのでこれを利用します。
また、今回は特定の1曲だけを再生しますが、後々のことを考えて再生にはキューを使用することにします。
キューという概念の説明については割愛します。そこまで難しいものではないので軽く調べてもらえば理解できると思います。


pythonのasyncioというライブラリにキューが実装されてるので、これを都合よく改造します。

再生キューであるAudioQueueクラス

class AudioQueue(asyncio.Queue):
    def __init__(self):
        super().__init__(0)         #再生キューの上限を設定しない

    def __getitem__(self, idx):
        return self._queue[idx]     #idx番目を取り出し

    def to_list(self):
        return list(self._queue)    #キューをリスト化

    def reset(self):
        self._queue.clear()         #キューのリセット

asyncio.Queueクラスを継承しています。
クラスがよく分からない方は、とりあえずこれで再生キューが作成できたと思ってもらって結構です。


次に、現在の再生状況を管理するAudioStatusクラスを作成します。
これには、自分が今どのボイスチャンネルにいて、再生キューがどうなっているのかといった情報を保持してもらいます。

class AudioStatus:
    def __init__(self, vc):
        self.vc = vc                                #自分が今入っているvc
        self.queue = AudioQueue()                   #再生キュー
        self.playing = asyncio.Event()
        asyncio.create_task(self.playing_task())

    #曲の追加
    async def add_audio(self, path):
        await self.queue.put(path)

    #曲の再生(再生にはffmpegが必要)    
    async def playing_task(self):
        while True:
            self.playing.clear()
            try:
                path = await asyncio.wait_for(self.queue.get(), timeout = 100)
            except asyncio.TimeoutError:
                asyncio.create_task(self.leave())
            selfpath = os.path.dirname(__file__)
            self.vc.play(discord.FFmpegPCMAudio(executable=selfpath+"/bin/ffmpeg.exe", source=path), after = self.play_next)
            await self.playing.wait()

    
    #playing_taskの中で呼び出される
    #再生が終わると次の曲を再生する
    def play_next(self, err=None):
        self.bgminfo = None
        self.playing.set()
        return
            
    #vcから切断
    async def leave(self):
        self.queue.reset()  #キューのリセット
        if self.vc:
            await self.vc.disconnect()
            self.vc = None
        return


前回グローバル変数VCで行っていたボイスチャンネルの管理もこのクラスで行うので、切断用の関数をこちらに実装します。


AudioStatusはadd_audioでキューに曲を追加することができるので、プログラムの完成版は次のようになります。
import asyncioやawaitの抜けに注意しましょう。

import discord
from discord.ext import commands
import os
import asyncio

TOKEN  = "token" #トークン
PREFIX = '!'       #prefix=接頭辞

#bgmコマンドで使う再生キュー
class AudioQueue(asyncio.Queue):
    def __init__(self):
        super().__init__(0)         #再生キューの上限を設定しない

    def __getitem__(self, idx):
        return self._queue[idx]     #idx番目を取り出し

    def to_list(self):
        return list(self._queue)    #キューをリスト化

    def reset(self):
        self._queue.clear()         #キューのリセット

#bgmコマンドで使う,現在の再生状況を管理するクラス
class AudioStatus:
    def __init__(self, vc):
        self.vc = vc                                #自分が今入っているvc
        self.queue = AudioQueue()                   #再生キュー
        self.playing = asyncio.Event()
        asyncio.create_task(self.playing_task())

    #曲の追加
    async def add_audio(self, path):
        await self.queue.put(path)

    #曲の再生(再生にはffmpegが必要)    
    async def playing_task(self):
        while True:
            self.playing.clear()
            try:
                path = await asyncio.wait_for(self.queue.get(), timeout = 100)
            except asyncio.TimeoutError:
                asyncio.create_task(self.leave())
            selfpath = os.path.dirname(__file__)
            self.vc.play(discord.FFmpegPCMAudio(executable=selfpath+"/bin/ffmpeg.exe", source=path), after = self.play_next)
            await self.playing.wait()

    
    #playing_taskの中で呼び出される
    #再生が終わると次の曲を再生する
    def play_next(self, err=None):
        self.bgminfo = None
        self.playing.set()
        return
            
    #vcから切断
    async def leave(self):
        self.queue.reset()  #キューのリセット
        if self.vc:
            await self.vc.disconnect()
            self.vc = None
        return
    
#botの作成
bot = commands.Bot(command_prefix=PREFIX)

Audio_queue = None #AudioStatusの宣言

#予め決めておいた音楽ファイルを再生する
@bot.command()
async def bgm(ctx):
    """play music"""
    global Audio_queue   #この関数内ではVCはグローバル変数のVCを指す

    if (ctx.author.voice is None):  #送信者がボイスチャンネルにいなければエラーを返す
        await send_message(ctx.send, ctx.author.mention, 'ボイスチャンネルが見つかりません')
        return

    if ((Audio_queue is None) or (Audio_queue.vc is None)):      #botがボイスチャンネルに入っていなければ
        voice_channel = ctx.author.voice.channel.id                     #送信者の入っているボイスチャンネルのID
        vc = await bot.get_channel(voice_channel).connect()             #ボイスチャンネルに入る
        Audio_queue = AudioStatus(vc)

    path = os.path.dirname(__file__)    #このファイルが置いてあるディレクトリまでのファイルパス

    #music.mp3をキューに追加
    await Audio_queue.add_audio(path+'/bgm/music.mp3')

    return

#botをボイスチャンネルから切断する
@bot.command()
async def remove(ctx):
    await Audio_queue.leave()
    return

bot.run(TOKEN)


!bgmと2回打つとmusic.mp3が2回再生されるようになれば成功です。
お疲れ様でした。


next→Discord BotでCogを使ってみる - 1/(1+e^(-ax))

Discord Botで予め準備しておいた音楽を再生する

Top→Discord Botの作り方 - 1/(1+e^(-ax))
prev→Discord Botの作り方(Botを動かす) - 1/(1+e^(-ax))
next→Discord Botで予め準備しておいた音楽を連続再生する - 1/(1+e^(-ax))

準備

音楽の再生にはpynaclというものとffmpegなるものを使用します。

pynaclのインストールはコマンドプロンプトを起動して、「pip install pynacl」と入力して実行するだけです。
ffmpegは、ここからダウンロードできるので、自分のOSに合わせてダウンロードしてください(Linuxの方はsudo apt-get ffmpegでダウンロードできたはずです)。

例えばWindows OSであれば次のようにWindowsのアイコンを選択し、「Windows builds bt BtbN」をクリックします。
f:id:Sigmoid_poke:20210302142648p:plain


今回は下のスクショで赤枠で囲まれたものをダウンロードします。
f:id:Sigmoid_poke:20210302142835p:plain


これを展開すると4つのフォルダがあるので、「bin」という名前のフォルダがあるので、これをフォルダごとbotソースコードがあるフォルダにコピーします。
f:id:Sigmoid_poke:20210302143504p:plain


更に、botソースコードがあるフォルダに「bgm」というフォルダを作成し、そこに今回再生する音楽ファイルを保存します。
botソースコードがあるフォルダ
f:id:Sigmoid_poke:20210302143710p:plain


bgmフォルダの中
f:id:Sigmoid_poke:20210302143746p:plain



今回は、discordでこの「sample.mp3」を再生することを目標にします。

プログラム

import discord
from discord.ext import commands
import os

TOKEN  = "token" #トークン
PREFIX = '!'       #prefix=接頭辞

#botの作成
bot = commands.Bot(command_prefix=PREFIX)

VC = None   #botが入っているボイスチャンネル(入っていなければNone)

#予め決めておいた音楽ファイルを再生する
@bot.command()
async def bgm(ctx):
    """play music"""
    global VC   #この関数内ではVCはグローバル変数のVCを指す

    if (ctx.author.voice is None):  #送信者がボイスチャンネルにいなければエラーを返す
        await ctx.send(f'{ctx.author.mention} ボイスチャンネルが見つかりません')
        return

    if (VC is None):    #botがボイスチャンネルに入っていなければ
        #送信者の入っているボイスチャンネルのID
        voice_channel = ctx.author.voice.channel.id
        #ボイスチャンネルに入る
        VC = await bot.get_channel(voice_channel).connect()

    #このファイルが置いてあるディレクトリまでのファイルパス
    path = os.path.dirname(__file__)

    #music.mp3を再生
    VC.play(discord.FFmpegPCMAudio(executable = path+'/bin/ffmpeg.exe', source = path+'/bgm/music.mp3'))

    return

#botをボイスチャンネルから切断する
@bot.command()
async def remove(ctx):
    global VC               #この関数内ではVCはグローバル変数のVCを指す
    if (VC is not None):        #botがボイスチャンネルに入っている場合
        await VC.disconnect()   #ボイスチャンネルから切断
        VC = None
    return

bot.run(TOKEN)

botを切断する時にボイスチャンネルを使うので、ボイスチャンネルはグローバル変数で保存しています。
再生する音楽ファイルの名前は適宜変更してください。


このbotが導入されているサーバーでボイスチャンネルに入って「!bgm」と入力すると、音楽ファイルが再生されるはずです。
このままだと連続再生等はできませんが、区切りがいいので今回はここまでにします。
お疲れ様でした。


next→Discord Botで予め準備しておいた音楽を連続再生する - 1/(1+e^(-ax))

Discord Botの作り方(Botを動かす)

Top→Discord Botの作り方 - 1/(1+e^(-ax))
prev→Discord Botの作り方(Botの導入) - 1/(1+e^(-ax))
next→Discord Botで予め準備しておいた音楽を再生する - 1/(1+e^(-ax))

テンプレートを以下に置いておくので、とりあえずコピペして保存してください。
前回取得したトークンを使います

import discord
from discord.ext import commands

TOKEN  = "token" #トークン
PREFIX = '!'           #prefix=接頭辞

#botの作成
bot = commands.Bot(command_prefix=PREFIX)

bot.run(TOKEN)

tokenを取得したトークンに置き換えてください。
これを保存して、前回同様にコマンドプロンプトで実行するとdiscordにBotが浮上します。
プログラムはコマンドプロンプト上でCtrl+Cで停止できます。


このままだとbotは何もできないので、挨拶をするbotを作ってみます。
@bot.command()
async def function():
でコマンドの登録をすることができます。

import discord
from discord.ext import commands

TOKEN  = "token" #トークン
PREFIX  = '!'       #prefix=接頭辞

#botの作成
bot = commands.Bot(command_prefix=PREFIX)

@bot.command()
async def hello(ctx):
    """send Hello"""
    await ctx.send(f'{ctx.author.mention} Hello!')
    return

bot.run(TOKEN)

ctxはメッセージの送信主やチャンネルなどの情報が勝手に入っているものと思ってもらえれば大丈夫です。
await ctx.send()でメッセージの送信を行えます。
ctx.author.mentionはメッセージの送信主へのメンションになります。
f'{value} text'は、{}で囲まれたvalueの値を文字列に変換し、textと合わせた文字列になります。

引数を取る場合は次のようにします

import discord
from discord.ext import commands

TOKEN  = "token" #トークン
PREFIX = '!'       #prefix=接頭辞

#botの作成
bot = commands.Bot(command_prefix=PREFIX)

@bot.command()
async def hello(ctx):
    """send Hello"""
    await ctx.send(f'{ctx.author.mention} Hello!')
    return

#引数を取る場合
@bot.command()
async def hello1(ctx, name):
    """send name + Hello"""
    await ctx.send(f'{ctx.author.mention} {name} Hello!')
    return

@bot.command()
async def hello2(ctx, name1, name2):
    """send name*2 + Hello"""
    await ctx.send(f'{ctx.author.mention} {name1} {name2} Hello!')
    return

@bot.command()
async def hello3(ctx, *values):
    """send name*n + Hello"""
    text = ''
    for word in values:
        text += word + '\n'
    await ctx.send(f'{ctx.author.mention} {text} Hello!')
    return

bot.run(TOKEN)


引数の数を無制限にする時にはhello3のように *(変数名) を使います
実行結果はこんな感じ
f:id:Sigmoid_poke:20210227231715p:plain


基本的なbotの作り方はこんな感じになります。
ここまでお疲れ様でした。


next→Discord Botで予め準備しておいた音楽を再生する - 1/(1+e^(-ax))

Discord Botの作り方(Botの導入)

Top→Discord Botの作り方 - 1/(1+e^(-ax))
prev→Discord Botの作り方(Pythonを触ってみる) - 1/(1+e^(-ax))
next→Discord Botの作り方(Botを動かす) - 1/(1+e^(-ax))


ここまでの動作確認が正常に動いており、discordのアカウントを所持していることを前提に進めます。
また、Botを導入することができるのは、Botの作成者が管理者権限を持っているサーバーに限ります。

botの作成


Discord Developer Portalにアクセスして、右上あたりにあるボタンから新しいアプリケーションを作ります。


・アプリケーションが作成できたら,それをクリックしてクライアントIDとクライアントシークレットを控えます(誰にも見せないこと)。

・アイコンや名前は適宜設定します。


・左のタブからBotを選択し,botを追加しTokenをメモします(これも誰にも見せない)。
 Tokenはbotのログインに使用するもので、ID(又はメアド)とパスワードが一体になったものという認識で大丈夫です。


・下のほうにBotの権限を設定する場所があるので,必要な権限にチェックを入れて下に生成された数字をメモします(今回は考えるのが面倒なので管理者権限(=8)が付与してあります)。
追記:Privileged Gateway Intentsの項目にチェックを入れないとメッセージが受け取れなくなっているようです。

botをサーバーに入れる

以下のurlを、「クライアントID」と「権限」を上で求めたクライアントIDと権限にチェックを入れて得た数字に置き換えてリンクを踏みます
https://discordapp.com/api/oauth2/authorize?client_id=「クライアントID」&permissions=「権限」&scope=bot
例:クライアントIDが「abc」,権限が「8」なら「https://discordapp.com/api/oauth2/authorize?client_id=abc&permissions=8&scope=bot
ジャンプ後にどのサーバーにbotを導入するかを聞かれるので選択し、権限を確認します。

サーバーにbotがやってくるので、discordを開いて確認します。

Botが目的のサーバーに入ったら成功です。
お疲れ様でした。


next→Discord Botの作り方(Botを動かす) - 1/(1+e^(-ax))

Discord Botの作り方(Pythonを触ってみる)

Top→Discord Botの作り方 - 1/(1+e^(-ax))
prev→Discord Botの作り方(開発環境の準備) - 1/(1+e^(-ax))
next→Discord Botの作り方(Botの導入) - 1/(1+e^(-ax))

とりあえず適当なプログラムを書いてみます。
テキストエディタ(無ければメモ帳)を開いて

print('Hello World!')

と入力し、拡張子を「.py」にして適当な場所に保存します。(今回の例ではhello.py)


保存出来たらコマンドプロンプトを起動し、「python」「 」(半角スペース)を続けて入力して、
先程作成したファイルをコマンドプロンプトドラッグアンドドロップしてEnterキーを押します。

f:id:Sigmoid_poke:20210227194340p:plain


こんな感じでHello World!と表示されれば成功です。



次に、前回インストールしたdiscord.pyが使えるかどうか確認します。
先程作成したファイルを次のように書き換えます

import discord
print('Hello World!')

実行結果は最初と変わりませんが、
discord.pyのインストールに失敗していた場合はここでエラーが表示されるはずです。
何も表示されなければ成功なので、今回はここまでです。
お疲れ様でした。


next→Discord Botの作り方(Botの導入) - 1/(1+e^(-ax))


良さげなテキストエディタが欲しい人にはVS Codeがオススメです。

Discord Botの作り方(開発環境の準備)

Top→Discord Botの作り方 - 1/(1+e^(-ax))
next→Discord Botの作り方(Pythonを触ってみる) - 1/(1+e^(-ax))

今回はPythonというプログラム用の言語を使ってBotを作成するので、ここからOSに合ったPythonインストーラーをダウンロードします。
現段階では特にバージョンの指定は無いですが、Python 2.xとPython3.xは別物なので必ずPython 3.xとなっているものをダウンロードするように気を付けましょう。
何事もなければとりあえず最新版でok。

2021/02/27現在、3.9.2が最新版らしいので筆者の環境では下画像の一番下にある Windows installer (64-bit)をダウンロードすることになります。
f:id:Sigmoid_poke:20210227181055p:plain


インストーラーを起動すると、ウィンドウの下の方に
「Add Python 3.x to PATH」
と書かれたチェックボックスがあるので、必ずチェックを入れてから「Install Now」を押すようにしましょう。

インストールが完了したら、コマンドプロンプトを起動してみます(スタートボタンを押すと出てくる「ここに入力して検索」に「cmd」と入力すると出てきます)。
Pythonの実行はこのコマンドプロンプトで行うことが多いのでタスクバーに登録しておくと便利です。

コマンドプロンプトが起動したら、「python --version」又は「python -V」と入力して、実行(Enterキー)してみてください。
f:id:Sigmoid_poke:20210227182716p:plain
画像のように、pythonのバージョンが表示されれば成功です(画像は古いスクショなので3.7.8になっていますが、ダウンロードしたバージョンが表示されるはずです)。

「'Python' は、内部コマンドまたは外部コマンド、操作可能なプログラムまたはバッチ ファイルとして認識されていません。」等と表示された場合はPathが通っていないので、詳細は割愛しますがシステム環境変数からPathを追加してください。



続けて、bot制作に必要なものをダウンロードしていきます。
コマンドプロンプトに「pip install --upgrade pip」を実行した後で「pip install -U discord.py」と入力し、実行します。

ここで何故か長文のエラーが出ることがあるらしいのですが、これに関しては後述します。
コマンドプロンプトで「pip list」を実行した際にdiscord.pyが表示されていれば成功です。

ここまでできれば開発環境の準備は一先ず完了です。
お疲れ様でした。


next→Discord Botの作り方(Pythonを触ってみる) - 1/(1+e^(-ax))


discord.pyのインストールが上手くいかない場合

C++の実行環境が足りないというエラーであればここからVisual Studio 2019のコミュニティをダウンロードして実行します。
f:id:Sigmoid_poke:20210227185328p:plain

インストール画面を進めていくと下の画像のような画面が出てくるので、赤枠で囲った「C++ によるデスクトップ開発」にチェックを入れてインストールします。
f:id:Sigmoid_poke:20210227185449p:plain

インストールが完了したらPCを一度再起動してからまたコマンドプロンプトでコマンドを打ち直してみる。

Discord Botの作り方

f:id:Sigmoid_poke:20210227175434p:plain

所属しているサークルの後輩が、有り難いことにおわりのだいちに興味を持ってくれたので簡単に作り方の説明でも

実は初代おわりのだいちはNode.jsという実行環境で作られてたけど、僕の気分で今はPythonで書かれてるので今回はPythonでDiscordのBotを作ることを目標とします。
説明に関してはWindows OSを対象とします。
何かあればtwitterまで

予定ではそれなりに長くなるのでいくつかに分割します。

Part01:開発環境の準備
Part02:Hello World!
Part03:BotをDiscordサーバーに導入してみる
Part04:Botのテンプレートの作成
Part05:Botで音楽を流してみる
Part06:Botで音楽を連続再生してみる
Part07:Cogを使ってみる
Part08:Botでファイル名を指定して音楽を再生する
Part09:リストを埋め込み形式で送信する
Part10:フォルダの階層構造を表示してみる
Part11:音楽再生Bot完成



そもそもn番煎じだしQiitaでやったほうがいいけど目を瞑ってください()。