Pull to refresh

Пишем бота для Twitter на основе GitHub API

Reading time 5 min
Views 21K
Доброго времени суток, уважаемое Хабрасообщество.

Сегодня пятница, а это значит, что можно отвлечься от серьезных проектов и отдохнуть. Например, прочитав очередную статью для начинающих, посвященную разработке простого twitter-бота на python, уведомляющего о нескольких видах GitHub-активности.

Если вы подозреваете, что ничего нового из этой статьи не узнаете, то можете просто посмотреть код проекта на GitHub. Остальных же приглашаю под кат, чтобы узнать больше про библиотеки для взаимодействия с GitHub API v3 и ознакомиться с процессом написания бота.




Замечу, что на Хабрахабре легко найти руководства по написанию ботов для Twitter ([1], [2], [3], ...) и сложно — по API для GitHub. Это, пожалуй, основная причина появления этой заметки. Кроме того, перед вами всего лишь простой пример использования огромных возможностей, которые нам доступны.

GitHub API

В настоящий момент актуальная версия API — третья (поддержка первых двух была отменена в июне этого года). Для работы с ней посредством python существует как минимум шесть библиотек, которые очень похожи. Я выбирал библиотеку, основываясь на наличии удобной документации и отсутствии большого количества зависимостей. Наиболее приятной показалась github3.py.

Twitter API

От Twitter'a требовалось очень немного — нужно было уметь авторизовываться, получать новые упоминания (mentions) и отправлять сообщения. Токен можно было получить вручную (он выдается один раз и не нуждается в обновлении). Поэтому я остановился на библиотеке oauth, портированной под третий python.

Разработка бота

Предполагалось, что бот будет работать следующим образом:
  • каждые N секунд обновлять сообщения, в которых его упомянули
  • находить среди них новые
  • формировать из сообщений команды
  • выполнять команды
  • сообщать о результатах выполнения пользователям

Структура проекта выглядит следующим образом:


Папка oauth содержит библиотеку для авторизации, в файле twitter.py методы для оправки сообщений и обновления ленты, github.py, по сути, является самим ботом, а в id.dat будет храниться номер последнего обработанного сообщения (на случай каких-то сбоев в работе). Config.py понадобится для хранения ключей и паролей.

Для работы с Twitter API нужно создать свое приложение. Авторизуемся на dev.twitter.com, нажмем «Create an app», заполним форму и создадим приложением. Чтобы отправлять сообщения, нужно включить режим «Read and write» в разделе «Settings». Осталось получить ключи («Create my access token» на вкладке «Details») и можно приступать.

twitter.py

Ссылки на полный код будут в конце статьи, здесь только краткие выдержки.

Сначала научим нашего бота авторизовываться в Twitter, отсылать запросы и сохранять номер последнего сообщения.
HOME_TIMELINE_URL = 'http://api.twitter.com/1/statuses/home_timeline.json'
UPDATE_URL = 'http://api.twitter.com/1/statuses/update.json'
MENTIONS_URL = 'http://api.twitter.com/1/statuses/mentions.json'
FILENAME = 'id.dat'  # File with the number of the last solved problem

Для обновления ленты достаточно установить соединение:
def _get_connection(self):
    self.connection = http.client.HTTPConnection('twitter.com')

И отправить POST-запрос:
oauth_request = oauth.OAuthRequest.from_consumer_and_token(
            self.consumer, token=self.access_token, http_method='POST',
            http_url=UPDATE_URL, parameters=params)
oauth_request.sign_request(self.signature_method, self.consumer,
            self.access_token)
self.connection.request(oauth_request.http_method, UPDATE_URL,
            headers=oauth_request.to_header(),
            body=self._to_query_string(params))

А для получения упоминания — GET-запрос:
oauth_request =\
            oauth.OAuthRequest.from_consumer_and_token(self.consumer,
                            token=self.access_token, http_method='GET',
                            http_url=MENTIONS_URL, parameters=params)
oauth_request.sign_request(self.signature_method,
                                        self.consumer, self.access_token)
self.connection.request(oauth_request.http_method,
                    MENTIONS_URL + '?' + self._to_query_string(params),
                    headers=oauth_request.to_header())

Для сохранения информации можно воспользоваться библиотекой pickle:
def _save(self, data):
    path = os.path.dirname(__file__)
    path = os.path.join(path, FILENAME)
    log_file = open(path, 'wb')
    pickle.dump(data, log_file)
    log_file.close()

В принципе, для нашей задачи этого будет достаточно. Теперь создадим файл config.py, в котором будем хранить полученные нами ключи для Twitter API и логин/пароль пользователя GitHub (на случай, если нашему боту это понадобится):
import twitter
secret = twitter.SecretKeys('consumer_key',
            'consumer_secret',
            'auth_key',
            'auth_secret')
LOGIN = 'github_login'
PASSWORD = 'github_password'

И допишем оболочку для их хранения:
class SecretKeys:
    keys = {}

    def __init__(self, consumer_key, consumer_secret, auth_key, auth_secret):
        self.keys = {'consumer_key': consumer_key,
                     'consumer_secret': consumer_secret,
                     'auth_key': auth_key,
                     'auth_secret': auth_secret}


github.py

От самого бота требуется немного больше. Он будет загружать список новых сообщений, формировать из них список задач, выполнять задачи и уведомлять об их результатах пользователей.
Для начала нам понадобятся следующие библиотеки:
from github3 import login
import twitter  # twitter.py
import time
import os
import pickle
from config import secret, LOGIN, PASSWORD

Номер последнего сообщения будем брать из файла:
def get_data(filename):
    with open(filename, 'rb') as file:
        return pickle.load(file)

Список новых задач получим от twitter.py:
list_of_problems = api.get_new_mentions(idx)

И будем идти по нему, формируя новые команды:
for problem in list_of_problems:
        problem = form_problem(problem)

Функция form_problem() будет искать, что именно хотел от бота пользователь и формировать словарь с параметрами:
if command in commands:
        command = commands.index(command)
        user = problem['user']['screen_name']
        return {'command': command, 'user': user, 'params': params}

Затем нужно будет выполнить каждую из задач:
def solve(problem, gh):
    functions = {0: get_last_commit,
                 1: get_list_of_contributors,
                 2: get_count_of_open_issues,
                 3: get_count_of_commits,
                 4: get_count_of_repos,
                 5: subscribe_on_commits,
                 6: help,
                 7: unsubscribe_from_commits}
    return functions[problem['command']](gh, problem['params'],
                                         problem['user'])

Отправить пользователю результаты:
api.post_update('@%s %s.' % (problem['user'], result))

И сделать небольшую паузу:
time.sleep(10)

В принципе, этого достаточно для первой версии. Сами функции полностью основаны на GitHub API и документации к github3.py. Например, вывод последнего коммита в каком-либо репозитории можно реализовать так:
def get_last_commit(gh, params, user):
    pattern = 'Last commit in "%s" was "%s" by %s'
    repository = gh.repository(params[0], params[1])
    last_commit = repository.list_commits()[0]
    return pattern % (repository.name, last_commit.commit.message,
                    last_commit.commit.author.name)

С точки зрения GitHub API, это GET-запрос (/repos/:user/:repo/commits), в ответ на который сервер вернет список всех коммитов в репозитории с довольно обширной информацией:


Пожалуй, это все. Для запуска бот нужно просто запустить файл github.py, предварительно заполнив config.py. В настоящий момент GitToTweet поддерживает следующие команды:
  • Вывести последний коммит в репозитории: get last commit, ,
    Вывести список участников: get list of contributors, ,
    Вывести список открытых проблем: get count of open issues, ,
    Вывести список коммитов в репозитории: get count of commits, ,
    Вывести количество публичных репозиториев пользователя: get count of repos, ,
    Подписаться на новые коммиты в репозитории: subscribe me, ,
    Отписаться от новых коммитов в репозитории: unsubscribe me, ,
    Вывести пример использования с ссылкой на github: help


    А выглядит это примерно так:
    @GitToTweet get last commit, django, django

    @%username% The last commit in "django" was "Fixed #18847 - Updated for media examples to use static.example.com. Thanks Jamie Curle…


    Ссылки

    Исходный код проекта на GitHub:
    github.com/valzevul/GitToTweet

    Документация по GitHub API v3:
    developer.github.com

    Документация по Twitter API:
    dev.twitter.com/docs

    Пример работы бота в Twitter можно посмотреть здесь:
    twitter.com/GitToTweet

    Спасибо за внимание.
Tags:
Hubs:
+20
Comments 13
Comments Comments 13

Articles