[Python3] GithubAPI/TwitterAPIを用いて、最新のコミットログをツイートする

前提

以下の環境で実装、動作確認

要素 バージョン
debian 8.6
python 3.6.2

概要

GithubAPIを用いて、自身がGithub上のリモートリポジトリに対してpushした内容からコミットログを取得し、それに関する情報をTwitterAPIを用いてツイートしたお話。

本記事ではTwitterAPIを利用するためのコンシューマキー、アクセストークンは取得済みであることを前提としている。

同じことはGithubのWebhookを使ってできるしむしろ自然だが、その場合サーバーを用意する必要があるので、今回はあえてpull型で非効率なやり方を採用

動作イメージ

リモートリポジトリがGithub上にあるリポジトリに対して適当にコミット/プッシュ

$ git commit -m "コミットテスト"
[master e6bb9e4] コミットテスト
 1 file changed, 1 insertion(+)
$ git push origin master
Counting objects: 3, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (3/3), done.

今回実装したコマンドを実行する

$ python main.py

以下のように、リポジトリ名、コミットメッセージ、diffへのリンクがツイートされる

GithubAPIについて

GithubAPIは、名前の通りGithubの各種読み書きを行うためのAPI。
公開されているデータの取得に関しては認証無しで手軽に利用できる。

例えば、curlコマンドを用いて以下のURLに対してGETリクエストを送信すると

curl -i https://api.github.com/users/sa2knight/events

以下のように、該当ユーザのGithubでのイベントログを取得することができる

[
  {
    "id": "6516862394",
    "type": "PushEvent",
    "actor": {
      "id": 16274215,
      "login": "Sa2Knight",
      "display_login": "Sa2Knight",
      "gravatar_id": "",
      "url": "https://api.github.com/users/Sa2Knight",
      "avatar_url": "https://avatars.githubusercontent.com/u/16274215?"
    },
    "repo": {
      "id": 96615971,
      "name": "Sa2Knight/degulog2",
      "url": "https://api.github.com/repos/Sa2Knight/degulog2"
    },
    "payload": {
      "push_id": 1946817475,
      "size": 1,
      "distinct_size": 1,
      "ref": "refs/heads/master",
      "head": "4208370eb0cda0a1bab02fd3fbe5bf7e3e6f5f29",
      "before": "afd8b8647c2b810ceb731a74bad20c7388c19974",
      "commits": [
        {
          "sha": "4208370eb0cda0a1bab02fd3fbe5bf7e3e6f5f29",
          "author": {
            "email": "shingo.sasaki.0529@gmail.com",
            "name": "shingo sasaki"
          },
          "message": "バックアップ追加",
          "distinct": true,
          "url": "https://api.github.com/repos/Sa2Knight/degulog2/commits/4208370eb0cda0a1bab02fd3fbe5bf7e3e6f5f29"
        }
      ]
    },
    "public": true,
    "created_at": "2017-08-27T15:22:08Z"
  },
  {
    "id": "6516846556",
    "type": "PushEvent",
(以下省略)

ここで取得できるイベントは、Webで言う以下のような、そのユーザのリポジトリに対する操作全般の情報なので、今回はこのAPIを用いることにする

ライブラリの導入

今回はPython3を用いて実装するので、Pythonのパッケージ管理ツールであるpipを用いて、以下の二種類のライブラリを導入する。

  • requests
    — HTTPライブラリ
  • twitter
    — TwitterAPI用

TwitterAPIの利用には認証も絡んでくるので、専用のライブラリを利用する。GithubAPIは特定URLにGETするだけなのでHTTPライブラリで直接実行することに。

$ pip install requests
$ pip install twitter

ソースコード

最新版はこちら

import twitter
import requests
import json
import os

FILE_NAME      = "last_id"
EVENTS_URL     = "https://api.github.com/users/sa2knight/events"
REPOSITORY_URL = "https://api.github.com/repos"

def tweet(text):
  auth = twitter.OAuth(consumer_key=os.environ['TWITTER_CONSUMER_KEY'],
                       consumer_secret=os.environ['TWITTER_CONSUMER_SECRET'],
                       token=os.environ['TWITTER_ACCESS_TOKEN'],
                       token_secret=os.environ['TWITTER_ACCESS_SECRET'])
  t = twitter.Twitter(auth=auth)
  t.statuses.update(status=text)

def tweet_event(event):
  tweet_text = f"""
  @null
  Githubにコミットをプッシュしました。
  [{event['repository']}]
  「{event['commits'][0]['message']}」
  """.strip()
  if 1 < len(event['commits']):
    tweet_text += f"ほか{len(event['commits']) - 1}件"
  tweet_text += f"\n\n{event['commits'][0]['url']}"
  tweet(tweet_text)

def save_id(id):
  with open(FILE_NAME, 'w') as f:
    f.write(id)

def load_id():
  if os.path.exists(FILE_NAME):
    with open(FILE_NAME, 'r') as f:
      return f.readline()
  else:
    return ''

def is_new_id(id):
  return id != load_id()

def parse_commit_log(repo_name, commit):
  return {
    'url': f"https://github.com/{repo_name}/commit/{commit['sha']}",
    'message': commit['message']
  }

def get_repository_description(repo_name):
  url = f"{REPOSITORY_URL}/{repo_name}"
  response = requests.get(url)
  repository = json.loads(response.text)
  return repository['description']

def get_recent_push_event():
  response = requests.get(EVENTS_URL)
  events   = json.loads(response.text)
  recent_event = list(filter(lambda e: e['type'] == 'PushEvent', events))[0]
  repo_name    = recent_event['repo']['name']
  commits      = list(map(lambda c: parse_commit_log(repo_name, c), recent_event['payload']['commits']))
  return {
    'id':         recent_event['id'],
    'repository': get_repository_description(recent_event['repo']['name']),
    'commits':    commits,
  }

event = get_recent_push_event()
if is_new_id(event['id']):
  tweet_event(event)
  save_id(event['id'])
  print('ツイートを投稿しました')
else:
  print('ツイートは不要です')

スクリプトを実行すると概ね以下の手順でツイートされる
1. GithubAPIを叩いて、自身の最新のコミットを含んだイベントログを取得
2. 直近で取得したイベントIDをファイルに保存しておき、そのIDと比較、一致していた場合変更なしとして終了
3. イベントログを元にツイートするテキストを生成
4. TwitterAPIを用いてツイート

所感

  • 本スクリプトを15分おきぐらいにcronで実行すれば、概ねコミット内容がツイートされる
  • Webhookと違ってpull型になるので、リアルタイムにはできない
  • 単にGithubAPIが使いたくて遊んだだけで実用性はあんまり無い

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です