2
4

More than 3 years have passed since last update.

DiscordのアクティビティをSlackのStatusに反映する話

Last updated at Posted at 2020-05-06

概要

Discordのアクティビティ
image.png

SlackのStatusに反映した。
image.png

誰得なんだこれは…。

discord.py

今回はDiscord APIのPythonライブラリであるdiscord.pyを利用する。

参考

Tokenの取得などは偉大なる先駆者の方々が既に記事を書いているのでそちらを参照してもらいたい。

実装

基本的には自分で建てたdiscordサーバにbotを置いて、そのbotがサーバメンバーのアクティビティを取得するような形になる。
※もっと良い方法があれば教えて欲しい。

discordの各イベントハンドラに処理を記述していくことになるが、
今回は

  • bot起動時
  • メンバーのプロフィール更新時

にアクティビティを取得することとする。

それぞれのイベントハンドラは以下の通り。

# bot起動時
@client.event
async def on_ready():
     ...
# メンバーのプロフィール更新時
@client.event
async def on_member_update(before, after):
     ...

アクティビティを取得する

公式リファレンスを読む限り、discord.Memberにユーザのアクティビティを持っているとのこと。

つまり各イベントハンドラでMemberを取得できればよい。
そこで、Client.get_all_members()を使うことにした。

ただしサーバに参加しているすべてのメンバーを取得してしまうため、
discord.util関数を使って指定したメンバーのMemberのみ取得する。

member = discord.utils.get(client.get_all_members(), name='Discord上の名前')
if member.activity != None:
    print(member.activity.name)

KovaaKを起動していた場合、
image.png

実行結果
KovaaK 2.0: The Meta

となる。

アクティビティを取得する上での注意点

discord.Member.activityは様々なアクティビティを持っており、
ゲームの場合は上記で上手くいくが、Discordに登録されていないゲームやSpotify再生中などのアクティビティはもしかしたらうまく行かないかもしれない。

詳しくは下記を参照。

一応BaseActivityのメンバとSpotifyはみんなnameを持ってるから大丈夫か…な…(多分)

Slackのステータスを書き換える

Slack APIのusers.profile.setにpostリクエストを投げればできる。

  • users.profile.set  → Testerタブでいろんなリクエストをテストできるぞ!

基本的にはnameに更新したいプロファイル名、valueにプロファイルの内容を渡すことによって実現可能。

response = requests.post('https://slack.com/api/users.profile.set', 
                       data={'token': 'Slackのトークン',
                       'name': 'status_text',
                       'value': 'test',
                       'user': 'SlackのユーザID', 
                       'pretty': '1'})

これを投げると、
image.png
こんな感じになる。

これらをまとめるとこんな感じ

※Python初心者のコードなので見苦しい点あればすみません……。

import discord
import requests

DISCORD_TOKEN = '******'
SLACK_TOKEN = '******'

game = None

# Discordに接続するためのオブジェクトを生成
client = discord.Client()

# memberからアクティビティの名前を取得して変数にセット
def set_game_name(member):
    global game
    if member.activity != None:
        game = member.activity.name + 'をプレイ中'
    else:
        game = 'Not Playing game.'

# Slackのstatusを更新
def post_slack_status():
    global game
    response = request.post('https://slack.com/api/users.profile.set',
                            data={
                                 'token': SLACK_TOKEN,
                                 'profile': '{ "status_text":"' + game + '",
                                               "status_emoji": ":nick:" }',
                                 'user': '*****',
                                 'pretty': '1'})

# bot起動時
@client.event
async def on_ready():
    member = discord.utils.get(client.get_all_members(), name='*****')
    set_game_name(member)
    post_slack_status()

# メンバーのプロフィール更新時
@client.event
async def on_member_update(befor, after):
    set_game_name(after)
    post_slack_status()

# bot起動
client.run(DISCORD_TOKEN)

ね?簡単でしょ?

友人A「n分経過も出せるといい」

詳しい実装方法は後日気が向けば記載します…。
簡単に説明すると、

  • post_slack_status()のリクエストを、1分ごとに回るループ内で投げる。
  • post_slack_status()をスレッド化して別スレッドとして実行。
  • on_member_update(befor, after)が走ったら、ループのフラグをFalseにしてスレッドを終了させアクティビティを再取得後、もう一回post_slack_status()のスレッドを走らせる。

こういう感じ。
(書いてて思ったけど、on_member_update()内でスレッドの終了を待ってるときに、もう一回on_member_update()が走ったらどうなるんだろう……。イベントハンドラやっかいだ………。)

追記(2020/05/07)

on_member_update()の仕様理解不足があった。
on_member_update()

メンバーの以下が更新されたときに走るため、
activityが更新されたときのみ走るようにしよう!

  • ステータス
  • activity
  • ニックネーム
  • 役職
2
4
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
2
4