- PVSM.RU - https://www.pvsm.ru -

Кросспостинг из Twitter в ВКонтакте с помощью роутера

Здравствуйте, меня зовут Евгений, и я алкоголик люблю социальные сети. В силу отсутствия каких-либо намеков на писательский талант я предпочитаю Twitter — его ограничение на 140 символов избавляет от необходимости придумывать что-то длинное. К тому же, только для твиттера есть нормальный java-клиент, которым я раньше пользовался на мобильном телефоне.
С другой стороны, бóльшая часть моя знакомых использует ВКонтакте, да и мне, честно говоря, нравится слушать там музыку и читать новости. Очевидно, что писать самому в две разных соцсети неудобно, нужно это автоматизировать — использовать кросспостинг (перенос постов).
Далее небольшое описание моего не совсем обычного способа кросспостинга.

Еще немного предыстории

Ранее для переноса записей я использовал [1] IFTTT. Это было не слишком удобно — посты переносились раз в 15 минут, спецсимволы энкодились (" превращалось в "), ссылки переносились сокращенными (t.co) — Вконтакте ругался при попытке перейти по этим ссылкам. Однако из-за недавних изменений в Twitter API, IFTTT был вынужден [2] отключить «рецепты», которые читают твиты. Мне пришлось искать новый путь. Внезапно™ я узнал, что у Вконтакта есть официальный способ импортировать твиты, но для этого приходится использовать хэштег #vk. После пары дней тестирования, оказалось что работает это не всегда (видимо опять срабатывали новые ограничения Twitter API на количество запросов), плюс оставалась проблема с ссылками.
Конечно, самым простым способом было бы использование скрипта, который будет переносить записи. На хабре неоднократно [3] писали [4] о разных способах кросспостинга, но все эти способы упирались в отсутствие у меня своего сервера. Покупать же платный хостинг [5] (пусть и недорогой) для одного простенького скрипта смысла мало.
Но вот недавно, ползая по интернету в поисках новой версии прошивки для своего роутера, меня осенило, что на базе моего Zyxel Keenetic можно сделать небольшой веб-сервер! Еще перед покупкой я читал о таких возможностях роутера, но потом это как-то вылетело из моей головы. И вот я приступил к реализации задуманного проекта — кросспостинга из Twitter в Вконтакте с помощью роутера.

Москва не сразу строилась

Для начала немного расскажу о возможностях Zyxel Keenetic. Вкратце, на роутер можно быстро, легко и безопасно устанавливать различные готовые модули (php, lighttpd, dlna, transmission, perl, python и др). Полее подробно можно почитать на форуме [6].
Перед началом проекта я сформировал список хотелок. Скрипт должен был уметь:

  • переносить твиты сразу после их появления либо при запуске скрипта через cron
  • не переносить ответы, ретвиты и упоминания
  • разворачивать сокращенные ссылки t.co в тексте твита
  • по возможности копировать приложенные картинки (например, Instagram)

Cначала надо было выбрать язык скрипта. Для роутера есть модули Perl и Python, и с этими языками я был одинаково незнаком. С Twitter API и Вконтакте API я так же никогда не сталкивался, поэтому в первую очередь меня интересовали готовые примеры работы с ними. К счастью, таких скриптов в сети было найдено достаточно. Как-то случайно я выбрал Perl:)
Затем я приступил непосредственно к документации и примерам по Twitter API. Как вы знаете, есть два типа — Streaming и REST. В последних изменениях в API явно проглядывается желание Twitter'а перевести разработчиков на Streaming API. Поэтому было решено использовать именно Streaming, чтобы не иметь проблем в будущем.

Так как я делал скрипт только для себя, то я не стал делать получение access token, а взял его сразу со страницы настроек моего Twitter-приложения. За пару часов с помощью perl-модуля [7] было написано простенькое консольное приложение, выводившие твиты. На моем компьютере все работало отлично. Для проверки на роутере установил все нужные пакеты (как мне казалось), запустил скрипт, и… ничего. Не работает. Я установил все имеющиеся пакеты для Perl, не помогло. Путем гугления по тексту ошибки выяснил, что скорее всего, проблема из-за отсутствия какого-то perl-модуля, в результате скрипт не может правильно работать с SSL. В итоге было решено оставить Perl, и попробовать Python.

Perl умер, да здравствует Python

Скачал PyCharm [8], стал изучать синтаксис. Если честно, после .NET был в шоке от использования отступов в качестве обозначения блоков:) Разобрался в синтаксисе, нашел модуль tweepy [9], и довольно быстро мой скрипт вывода твитов был переписан на Python. Устанавливаю пакеты python на роутер, запускаю скрипт — о, чудо! Работает! Осталось дописать побочный функционал и запись постов в Вконтакте.
Для начала разберемся с функционалом.
Разворачивание ссылки. Если твит содержит ссылку, то Twitter возвращает нам JSON, в котором есть элемент "entities", содержащий "urls". Выглядит это примерно так:

"entities":
{
	"hashtags":[],
	"user_mentions":[],
	"urls":[{
		"indices":[0,21],
		"display_url":"dev.twitter.com/terms/display-u2026",
		"url":"https://t.co/Ed4omjYs",
		"expanded_url":"https://dev.twitter.com/terms/display-guidelines"
	}]
}

В тексте твита содержится "url", поэтому для разворачивания ссылки нужно просто в тексте заменить значение "url" на значение "expanded_url".
Получение изображения. Если к твиту приложена картинка, то в "entities" добавится элемент "media", вот так:

"entities":
{
	"hashtags":[],
	"user_mentions":[],
	"urls":[],
	"media":[{
		"type":"photo",
		"media_url":"http://p.twimg.com/A7kqLpACEAAUlwz.png",
		"indices":[0,20],
		"sizes":
		{
			"large":{"resize":"fit","h":454,"w":584},
			"small":{"resize":"fit","h":264,"w":340},
			"thumb":{"resize":"crop","h":150,"w":150},
			"medium":{"resize":"fit","h":454,"w":584}
		},
		"display_url":"pic.twitter.com/XzDoEpH9",
		"media_url_https":"https://p.twimg.com/A7kqLpACEAAUlwz.png",
		"url":"http://t.co/XzDoEpH9",
		"expanded_url":"http://twitter.com/TestTwVK/status/268292032273977344/photo/1",
		"id":268292032278171648,
		"id_str":"268292032278171648"
	}]
},

Прямая ссылка на изображение содержится в элементе "media_url". Мне пришлось сохранять картинку во временный файл на локальном диске, не нашел способа загрузить его в ВК напрямую с сервера твиттера.

Посмотрим на получивший код для работы с Twitter.
Сначала авторизуемся и начинаем читать стрим

url = "https://userstream.twitter.com/1.1/user.json"
param = {"delimited":"length", "with":"user"}
header = {}
auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_key, access_secret)
auth.apply_auth(url, "POST", header,param)
logging.info('Twitter authorization successful')
#запрос твитов
req = urllib2.Request(url)
req.add_header("Authorization", header["Authorization"])
r = urllib2.urlopen(req, urllib.urlencode(param), 90)

Читаем ответы, если это твит — обрабатываем его

while True:
    #получаем длину твита
    length = ""
    while True:
        c = r.read(1)
        if c == "n": break
        if c == "": raise Exception
        length += c
    length = length.strip()
    if not length.isdigit(): continue
    #читаем ответ указанной длины
    tweet = json.loads(r.read(int(length)))
    #если это твит - запускаем его обработку
    if "user" in tweet and "text" in tweet and "created_at" in tweet and "id" in tweet:
        handleTweet(tweet)

Метод обработки твита:

#получаем текст твита
text = tweet["text"]
#проверяем, что это не ответ, ретвит или упоминание
if not text.startswith('@') and not "@zzeneg" in text:
    #получаем ссылки в твите
    urls = tweet["entities"]["urls"]
    #заменяем сокращенные ссылки
    for url in urls:
        text = text.replace(url["url"], url["expanded_url"])
        #если ссылка ведет на EyeEm - получаем id фото
     #если приложена картинка - убираем ее из текста
     if "media" in tweet["entities"]:
        photo = tweet["entities"]["media"][0]
        text = text.replace(photo["url"], "")
Работа с ВКонтакте

У ВК немного другой механизм авторизации, и просто так получить access token не получится.
Сначала нужно создать desktop-приложение и взять его id. Затем сформировать ссылку вида https://oauth.vk.com/authorize?client_id={ID}&scope=wall,photos,offline&redirect_uri=http://oauth.vk.com/blank.html&display=page&response_type=token, где {ID} — это ID приложения. В данной ссылке так же указаны разрешения для приложения — wall для постинга на стену, photos для заливки изображения и offline для того чтобы полученный токен был вечным. Копируем данную ссылку в браузер. ВК спросит нас о предоставлении прав данному приложению, а затем перенаправит на URL, в котором будет содержаться токен.

Для работы с методами VK API используется вот такая обертка. Код не мой, но я совершенно забыл откуда его взял — прошу прощения, что не указываю автора.

def vkMethod(method, data={}):
    url = 'https://api.vk.com/method/%s.json' % (method)
    data.update({'access_token': vkToken})
    response = requests.post(url, data).json
    if 'error' in response:
        print 'VK API error: %s' % (response['error']['error_msg'])
    return response

Здесь vkToken — полученный токен для ВК, method — имя метода из списка методов VK API, data — какие-то данные для метода.

Чтобы прикрепить картинку к посту на стене, нужно загрузить ее в альбом пользователя. Для фотографий на стене в ВК есть специальный альбом и специальные методы для работы с ним. Сначала получаем ссылку для загрузки фото, затем сохраняем картинку. В итоге получился такой метод:

def uploadPhoto(fileUrl):
    #получаем url для загрузки фото
    response = vkMethod('photos.getWallUploadServer')
    uploadUrl = response['response']['upload_url']
    #сохраняем фото локально
    urllib.urlretrieve(fileUrl,'temp.jpg')
    #загружаем фото в ВК
    files = {'photo': open('temp.jpg', 'rb')}
    response = requests.post(uploadUrl, files = files).json
    #добавляем фото в альбом
    response = vkMethod('photos.saveWallPhoto', response)
    #возвращаем id фото
    return response['response'][0]['id']

Данный метод возвращает ID загруженной картинки, который можно указать непосредственно при создании сообщения на стене:

attachments = uploadPhoto(photo["media_url"])
vkMethod('wall.post', {'message': text,'attachments':attachments})

При этом, если attachments будет пустой строкой — то сообщение все равно появится на стене, без вложений.

Работа с EyeEm

Я использую EyeEm (это аналог Instagram, но с нормальными Privacy Terms), и недавно подумал, что фото оттуда хорошо бы загружать в ВКонтакте. К счастью, у EyeEm есть очень простой и понятный API, и буквально в две строчки в скрипт можно добавить поддержку загрузки фоток из него.
Метод получения ссылки на фото по ее ID

def getEyeEmPhotoUrlById(photoId):
    url = "https://www.eyeem.com/api/v2/photos/{0}?access_token={1}".format(photoId, eyeEmToken)
    response = requests.get(url).json
    return response['photo']['photoUrl']

Теперь находим ссылку на фото в твите, получаем ее url и так же загружаем в ВК

if url["expanded_url"].startswith("http://www.eyeem.com/p/"):
    eyeEmId = url["expanded_url"].replace("http://www.eyeem.com/p/", "")
    photoUrl = getEyeEmPhotoUrlById(eyeEmId)
    attachments = uploadPhoto(photoUrl)
Установка скрипта на роутер

Готовый скрипт можно скачать с github [10]. Токены в нем, естественно, изменены:) Нужно будет получить свои.
Теперь настравиваем роутер для поддержки сторонних пакетов по инструкциям на вики [11]. Приведу здесь краткую инструкция для Windows:

  1. Форматируем флэшку в NTFS, создаем на ней папки system/bin
  2. Вставляем флэшку в роутер, открываем ее в проводнике и копируем туда файл ext_init.sh [12]
  3. Перемонтируем диск — через веб-интерфейс отключаем диск, перевтыкаем флэшку. Ждем пока в логах роутера появится запись "dropbear[4017] Running in background"
  4. Подключаемся по SSH (root/zyxel) и выполняем finish_install.sh. Ждем окончания создания swap.

Роутер готов к установке opkg пакетов. Их список можно посмотреть на сайте [13] либо командой opkg list. Устанавливаем python и необходимые модули:
Затем, устанавливаем python с помощью команды:

  1. Основные пакеты с помощью команд opkg install python и opkg install python-openssl
  2. Модули tweepy [9] и requests [14] простым копированием папки в systemusrlibpython2.7. Новая версия requests у меня не заработала, попробуйте либо ветку cache [15], либо из архива [16].
  3. Для активации cron переименовываем файл K02cron в папке systemetcinit.d в S02cron
  4. Настраиваем запуск скрипта в cron. В файл systemetccrontabsroot добавляем строку
    */15 * * * * killall -9 "python" ; /media/DISK_A1/system/usr/bin/python  /media/DISK_A1/system/root/TwVk.py

    Теперь cron в 0/15/30/45 минут каждого часа будет убивать скрипт и запускать его заново.Это очень криво, но стандартная для cron команда для действия после перезагрузки @reboot не заработала. Ну и плюс скрипт тоже не совсем идеален, перезапуск ему не повредит. Если кто-то сможет предложить идею получше — милости прошу.

  5. Ну и наконец копируем скрипт в systemroot и опять перемонтируем диск.
Заключение

Данный скрипт работает у меня уже около полугода (статья писалась ооочень долго), большинство ошибок я исправил. Мою стену в ВК стало гораздо удобнее читать из-за развернутых ссылок и прикрепленных фоток. Я доволен. Планирую создать еще пару скриптов для Twitter на основе роутера, например твиттер-бота.

Надеюсь, данная статья кому-нибудь пригодится. Хотя бы чтобы начать использовать имеющийся роутер не только по его прямому предназначению:)

Я честно постарался исправить все грамматические ошибки и опечатки, но если Вы что-то заметите — пишите в личку.
Так же я еще раз напоминаю, что мои знания в Linux и Python недалеко ушли от нуля, и я с удовольствием выслушаю Ваши подсказки по улучшению скрипта.

Благодарности

  • zyxmon [17], человек и пароход форум. Его вклад в сообщество Zyxel невозможно переоценить.
  • kethlin_mil [18], своей девушке, без нее я бы не разобрался с linux, cron и Perl.

Автор: zzeneg

Источник [19]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/vkontakte/31709

Ссылки в тексте:

[1] использовал: http://zzeneg.ru/post/eksport-zapisei-iz-twitter-v-vkontakte.aspx

[2] был вынужден: http://techcrunch.com/2012/09/20/ifttt-is-the-latest-service-to-be-affected-by-twitters-api-constraints-will-remove-triggers/

[3] неоднократно: http://habrahabr.ru/post/131107/

[4] писали: http://habrahabr.ru/post/149093/

[5] хостинг: https://www.reg.ru/?rlink=reflink-717

[6] форуме: http://forum.zyxmon.org/forum6-marshrutizatory-zyxel-keenetic.html

[7] perl-модуля: http://search.cpan.org/~mmims/Net-Twitter-Lite-0.11002/

[8] PyCharm: http://www.jetbrains.com/pycharm/

[9] tweepy: https://github.com/tweepy/tweepy

[10] github: https://github.com/zzeneg/TwVk

[11] вики: http://code.google.com/p/zyxel-keenetic-packages/wiki/Welcome

[12] ext_init.sh: http://zyxel-keenetic-packages.googlecode.com/files/ext_init.sh-r2.zip

[13] сайте: http://code.google.com/p/zyxel-keenetic-packages/source/browse/#svn%2Fbinary-packages-r2

[14] requests: https://github.com/kennethreitz/requests

[15] cache: https://github.com/kennethreitz/requests/tree/cache

[16] из архива: http://zzeneg.ru/uploads/requests.rar

[17] zyxmon: http://habrahabr.ru/users/zyxmon/

[18] kethlin_mil: http://habrahabr.ru/users/kethlin_mil/

[19] Источник: http://habrahabr.ru/post/158285/