原材料の空間

チョコの原材料が語ってます

Pythonで通話時間と今までのトーク数を出してくれる簡単なDiscordbotを作った ~初期設定から導入まで全部~

Pythonの勉強がてらDiscordのbotでも作ろうと思ってやってみた時の備忘録です。

Discordはbotの開発環境がとても整備されていてとても作りやすいので初心者におすすめです。

VCチャンネルでの通話時間と今までのトーク数を出すbotを作りました。

ここではbotの登録からプログラムまでしっかり説明してます。

環境は

 ・python3.6
・Windows10

です。python3.7はDiscordbotでは使えないと聞きました。

準備としてpython3の環境構築とテキストエディタを用意しておいてください。

discord.pyのダウンロード

pipを使ってdiscord.pyをインストールします

py -3 -m pip install -U discord.py 

Botトークンを取得する

DiscordのDEVELOPER PORTALへ行きbotを登録します。

 

Create an applicationをクリックしてください。

右にいるJKは僕が作ったbotですので表示されてません。

f:id:kakao1839:20180907165410p:plain

 

名前とかアイコンとかの設定です。好きに入力してください。

できたら左欄の[bot]をクリックします。

f:id:kakao1839:20180907165600p:plain

 

AddBotをクリックしてYes,do it!

注)名前が最初のMy Applicationだと「その名前多すぎだから無理」という旨のエラーがでます。名前を変更しましょう

f:id:kakao1839:20180907170034p:plain

 

 TOKENのところにある[Copy]をクリックしてトークンをコピーします。

メモ帳とかにペーストして保存しましょう。トークンは誰にも教えないように。乗っ取られます。

TOKENを取得できたら左欄の[OAuth2]をクリックましょう。

f:id:kakao1839:20180907170325p:plain

 

[bot]にチェックをつけるとBOT PERMISSIONSというのが現れます。ここでbotにできることを設定します

とりあえず[Administrator]にチェックを入れましょう。「管理者」ってことですね。これでこのbotには何の柵もありません。

次に[SCOPES]にある[COPY]をクリックして認証URLを取得します。

f:id:kakao1839:20180907171137p:plain

 

さっきの認証URLにアクセスましょう。

追加したいサーバーを選択肢[認証]をクリックします。自分が管理者になっているサーバーにのみ追加できます。

f:id:kakao1839:20180907171619p:plain

 

以下のように表示されると成功です。

f:id:kakao1839:20180907171755p:plain

f:id:kakao1839:20180907171802p:plain

 

 

プログラムを書いていく

 

メッセージ数を数える

いよいよですね。まずはコメント数を数えるのでも書きましょうか。

こんなふうに「!talk」と送ると送信者の今までのトーク数を出してくれます。

f:id:kakao1839:20180907172119p:plain

 

import discord

client = discord.Client()

@client.event
async def on_ready():
    print('Logged in as')
    print(client.user.name)
    print(client.user.id)
    print('------')

@client.event
async def on_message(message):
    if message.content.startswith('!talk'): 
        counter = 0
        tmp = await client.send_message(message.channel, 'Calculating messages...')
        async for log in client.logs_from(message.channel, limit=100):
            if log.author == message.author:
                counter += 1

        await client.edit_message(tmp, 'You have {} messages.'.format(counter))
    elif message.content.startswith('!sleep'):
        await asyncio.sleep(5)
        await client.send_message(message.channel, 'Done sleeping')

client.run('token') #tokenにはbotのトークンを貼り付け

これだけです。動かしてみましょう。

cmdからなら $ python ファイル名.py とかで行けると思います。cmd起動は試してないので行けなかったらごめんなさい。

 解説
if message.content.startswith('!talk'):

受け取ったメッセージが!talkで始まってるかどうか判別します

ということは「!talk aiueo」でも反応します。

tmp = await client.send_message(message.channel, 'Calculating messages...')

メッセージ数の統計中、送信元のチャンネルに「Calculating messages...」と送信します。

統計は

async for log in client.logs_from(message.channel, limit=100):
            if log.author == message.author:
                counter += 1

とfor文を用いてログを辿りmessageの送信者によるメッセージの個数を数えてます。

統計が終わると先程の「Calculating messages...」を書き換え計算結果を送信します。

 

通話時間を測定する

先程のコードに付け足す形で行きたいと思います。

こんな感じです

f:id:kakao1839:20180907200025p:plain

とりあえず時間取得のためのモジュールをimportしましょう。

一番上の

import discord

の下に

import datetime

を追記してください。

また、client = discord.Client() の下に pretime_dict = {} を追記します。

 

次に、先程書いた

    elif message.content.startswith('!sleep'):
        await asyncio.sleep(5)
        await client.send_message(message.channel, 'Done sleeping')

の下に以下のコードを追記します。

@client.event
async def on_voice_state_update(before, after):
  print("ボイスチャンネルで変化がありました")
  if ((before.voice.self_mute is not after.voice.self_mute) or (before.voice.self_deaf is not after.voice.self_deaf)):
      print("ボイスチャンネルでミュート設定の変更がありました")
      return

  if (before.voice_channel is None):  # 入室時
      pretime_dict[after.name] = datetime.datetime.now()
  elif (after.voice_channel is None):  # 退出時
      duration_time = pretime_dict[before.name] - datetime.datetime.now()
      duration_time_adjust = int(duration_time.total_seconds()) * -1
      # メッセージを送りたいテキストチャンネルの名前
      reply_channel_name = "general"
      reply_channel = [channel for channel in before.server.channels if channel.name == reply_channel_name][0]
      # 送りたいメッセージ
      reply_text = after.name + "さんが[" + before.voice_channel.name + "]から抜けました。 通話時間は" + str(duration_time_adjust) + "秒でした。"

      await client.send_message(reply_channel, reply_text)
 解説
async def on_voice_state_update(before, after):
  print("ボイスチャンネルで変化がありました")

ボイスチャンネルで入退出、マイク、スピーカーミュート等の変化があった際に

 on_voice_state_update(before, after)

が呼び出されます。

if((before.voice.self_mute is notafter.voice.self_mute)or(before.voice.self_deaf is not after.voice.self_deaf)):
  print("ボイスチャンネルでミュート設定の変更がありました")
  return

今回はボイスチャットへの入退出だけなのでマイク・スピーカーのミュート字の呼び出しも消します。 .voice.self_mute でマイクミュートかどうかを確認し同様にスピーカーも.voice.self_defで行います。前後に変化があればreturnで弾きます。

  if (before.voice_channel is None):  # 入室時
      pretime_dict[after.name] = datetime.datetime.now()
  elif (after.voice_channel is None):  # 退出時
      duration_time = pretime_dict[before.name] - datetime.datetime.now()
      duration_time_adjust = int(duration_time.total_seconds()) * -1

入退出を判定しチャンネル退出時に退出時の時刻から入室時の時刻を引きます。

計算すると通話時間の結果は負の数字が出てきてしまうため最後に-1を掛けています。

      reply_channel_name = "general" # メッセージを送りたいテキストチャンネルの名前
      reply_channel = [channel for channel in before.server.channels if channel.name == reply_channel_name][0]
      # 送りたいメッセージ
      reply_text = after.name + "さんが[" + before.voice_channel.name + "]から抜けました。 通話時間は" + str(duration_time_adjust) + "秒でした。"

      await client.send_message(reply_channel, reply_text)

 で最後にDiscord上にメッセージを送信しています。

ソース全体像

import discord
import datetime

client = discord.Client()
pretime_dict = {}

@client.event
async def on_ready():
    print('Logged in as')
    print(client.user.name)
    print(client.user.id)
    print('------')

@client.event
async def on_message(message):
    if message.content.startswith('!talk'):
        counter = 0
        tmp = await client.send_message(message.channel, 'Calculating messages...')
        async for log in client.logs_from(message.channel, limit=100):
            if log.author == message.author:
                counter += 1

        await client.edit_message(tmp, 'You have {} messages.'.format(counter))
    elif message.content.startswith('!sleep'):
        await asyncio.sleep(5)
        await client.send_message(message.channel, 'Done sleeping')

@client.event
async def on_voice_state_update(before, after):
  print("ボイスチャンネルで変化がありました")
  if ((before.voice.self_mute is not after.voice.self_mute) or (before.voice.self_deaf is not after.voice.self_deaf)):
      print("ボイスチャンネルでミュート設定の変更がありました")
      return

  if (before.voice_channel is None):  # 入室時
      pretime_dict[after.name] = datetime.datetime.now()
  elif (after.voice_channel is None):  # 退出時
      duration_time = pretime_dict[before.name] - datetime.datetime.now()
      duration_time_adjust = int(duration_time.total_seconds()) * -1
      reply_channel_name = "general" # メッセージを送りたいテキストチャンネルの名前
      reply_channel = [channel for channel in before.server.channels if channel.name == reply_channel_name][0]
      # 送りたいメッセージ
      reply_text = after.name + "さんが[" + before.voice_channel.name + "]から抜けました。 通話時間は" + str(duration_time_adjust) + "秒でした。"

      await client.send_message(reply_channel, reply_text)

client.run('token') #tokenにはbotのトークンを貼り付け

 

これで終わりです。pythonに勉強がてら作ってみました。

botはなかなかおもしろいのでこれからも続けていきたいです。

 

参考

qiita.com

kantarow.hatenablog.com

qiita.com