概要
Discordのアクティビティ
を
SlackのStatusに反映した。
誰得なんだこれは…。
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 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'})
これらをまとめるとこんな感じ
※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
- ニックネーム
- 役職