ハローラスク

absent-minded

Tweepyを使ってTwitterの垢名を最近聴いた曲のタイトルにする

f:id:HelloRusk:20180310014913p:plain

最近は, メインで使っている@8ma8Xとは別に, 日々の生活のメモを@7ma7Xというアカウントに書いているのだが, 使っていたアカウント名が気に入らなくなったので変えたいと思った. しかし, 特に新しいものが思いつかなかったので, いっそのこと曲名にしようと考えた.
今回は, @8ma8Xの直近の#nowplayingのツイートから曲名を抽出し, それを@7ma7Xのアカウント名にする, ということを, PythonのTweepyというライブラリを使ってやっていく.

Tweepyとは

TweepyはTwitterAPIを操作するPythonのライブラリである. これを使うと, Twitterのツイートやフォロー, 検索などの機能が簡単に使える. さらに, Streaming APIにも対応していて, リアルタイムのツイートやフォローに反応することもできる(らしい)(Streaming APIは今年の6月に廃止されて別のものに変わるらしい(http://www.itmedia.co.jp/news/articles/1712/20/news101.html)).

Tweepyの使い方は公式のレファレンスを見ればだいたい分かる.

Tweepy Documentation — tweepy 3.6.0 documentation

また, Tweepyを使って色々やっている人のブログがかなり参考になる.

kivantium.hateblo.jp

APIキーの取得

取得の仕方は「Twitter APIキー」でググれば沢山出てくるので説明しない.
とにかく@7ma7XのAPIキーとしてConsumer Key, Consumer Secret, Access Token, Access Token Secretというのが貰えるのでそれを1つずつ

XXXXXXXXXXXXXXXXXXXXXXXXX
YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW

みたいな感じで控えてmemo.txtとして保存しておく.

プログラムを書く

さっきのmemo.txtと同じディレクトリに

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tweepy

def main():
    api = authenticate()
    tweet_text = search_nowplaying_tweet(api)
    song_title = extract_title(tweet_text)
    api.update_profile(song_title)


def authenticate():
    with open('memo.txt') as f:
        key_list = f.read().split('\n')
    CONSUMER_KEY = key_list[0]
    CONSUMER_SECRET = key_list[1]
    ACCESS_TOKEN = key_list[2]
    ACCESS_TOKEN_SECRET = key_list[3]

    auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
    auth.set_access_token(ACCESS_TOKEN, ACCESS_TOKEN_SECRET)

    return tweepy.API(auth)


def search_nowplaying_tweet(api):
    for status in tweepy.Cursor(api.user_timeline, screen_name='8ma8X').items():
        if ("#nowplaying" in status.text) & (" - " in status.text):
            return status.text


def extract_title(text):
    if " (" in text:
        index = text.find(" (") if text.find(" (") <= 50 else 50
    else:
        index = text.find(" - ") if text.find(" - ") <= 50 else 50

    return text[:index]


if __name__ == '__main__':
    main()

みたいなpythonのプログラムを書く.
説明を加えておくと,
authenticate()memo.txtからキーを読み取って認証を行い, APIを返す.

search_nowplaying_tweet()で@8ma8Xのツイートを遡っていき, "#nowplaying"と" - "の両方を含むツイートが出てきたらそのツイートを返す.
この中に出てくるtweepy.Cursorがミソ. 普通にapi.user_timelineだけだと20件までしか表示してくれないのだが, tweepy.Cursorを使うと, ちょうど昔のツイートを遡る時どんどん下にスクロールしていくのと同じように, ツイートをどんどん昔まで遡ってくれる. ちなみに, statusというのはTweet Objectと呼ばれるjsonファイルで, この中に, あるツイートに関する全情報が含まれている. statusには.text(ツイート内容)の他に.created_at(ツイート日時), .user(ツイートした人)など様々なアトリビュートが備えられている(https://developer.twitter.com/en/docs/tweets/data-dictionary/overview/tweet-objectを参照するとよい).
なお, " - "というのは, 自分の場合#nowplayingのツイートのスタイルが「曲名 - アーティスト名 #nowplaying」なので" - "を使って絞っている.

extract_title()は「曲名 - アーティスト名 #nowplaying」から「曲名」を取り出す.
工夫した点として,

  • ユーザー名は50文字以下なので50より大きくならないようにしている.
  • 例えば BEYOND THE STARLIGHT (M@STER VERSION) を聴いた後ユーザー名がそのまま「BEYOND THE STARLIGHT (M@STER VERSION) 」になるのが嫌なので, " ("以下を含めない. ちなみにこれを空白のない"("とすると, 例えば S(mile)ING! を聴いた後ユーザー名が「S」になってしまうので良くない.
    (もしかしたら" ("で開始する内容でも重要な内容が含まれている曲名があるかもしれないが...)

最後にmain()では以上のようにして取り出された曲名をユーザー名にする.
これを実行するとユーザー名が変わる.

起動時に自動で実行させる

とはいえ, いちいち手動でこれを実行させるのは面倒なので自動実行させることにした. 自分はmacOSを使っているので以下はmacOS向けの内容である.
プログラムを自動で実行させるのはlaunchdというのを使えばよいらしいのでそれでやってみることにした. http://macwiki.osdn.jp/wiki/index.php/コマンド/launchctl などを参考に試行錯誤した結果以下のようにやると上手くいくことが分かった.

  1. さっき作ったpythonのプログラムの名前をnowplaying_to_username.pyにするとして, pythonの置き場の絶対パスnowplaying_to_username.py絶対パスをそれぞれ
/XXX/bin/python /YYY/nowplaying_to_username.py

という風に並べて書いてシェルスクリプトとして保存する. ここではtest.shという名前をつけておこう.
2. chmod +x test.shでこれを直接実行できるようにする.
3. また, nowplaying_to_username.pyの中のopen('memo.txt')の部分も絶対パスに変えておく(相対パスのままでは呼び出せなかった).
4. 以下のようなプロパティリスト.plistファイルを作る.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>nowplaying.username</string>
    <key>ProgramArguments</key>
    <array>
      <string>/XXX/test.sh</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/XXX/log.out</string>
    <key>StandardErrorPath</key>
    <string>/XXX/log.err</string>
</dict>
</plist>

プロパティリストではファイル名とラベルの名前を合わせるのが通例であるそうだからnowplaying.username.plistと名前をつけておく. ~/Library/LaunchAgents/に置く必要があるらしいのでそうしておく.
5. launchctl load nowplaying.username.plist. 完.

launchctl listで登録されているプロパティリスト一覧を表示できる. また, 実際に上手くいくかどうかを調べるにはいちいち再起動する必要はなく, launchctl start nowplaying.usernameでよい.
ちなみにlaunchctlではユーザーがログインした際に実行されるジョブを扱えるが, これをsudo launchctlにするとOS起動時に実行されるジョブを扱えるらしい.


なお, 実際に運用する上でnowplaying_to_username.pyに以下のようなマイナーチェンジも行った.

  • ログイン直後だとネットワークが確立されておらずTwitterにアクセスできない可能性があるので, timeモジュールをimportした上でmain()の前にtime.sleep(600)を書いて10分寝かせる.
  • datetimeモジュールをimportした上でprint(datetime.datetime.now())main()の後に書く. これにより, プログラムが上手くいった場合日付時刻がプロパティリストファイルに書いた/XXX/log.outに記録される.
テスト

PC起動前

f:id:HelloRusk:20180310012940p:plain:w250 f:id:HelloRusk:20180310012953p:plain:w250

PC起動後

f:id:HelloRusk:20180310013011p:plain:w250

発見された問題点

急ごしらえで作ったので問題点があった.

  • みかこしの Piña colada & Caipirinha*1という曲を聴いた後にユーザー名を確認したところ「Piña colada &amp; Caipirinha」になっていたケースがあった. すなわち「&」が「&amp;」になってしまっている.

正直, 「そもそも聴いている曲のタイトルをユーザー名に変えられるアプリを作ればよくね?」という感じがある. いつかSwiftやろうかな...

*1:ブラウザだと見づらいかもしれないが, 「&」は「アンド」です