Время социализироваться, madru в Twitter и LiveJournal

С сегодняшнего дня настроен кросспостинг в LiveJournal и в Twitter. При публикации любого поста на madru.ru сразу появляется анонс на LiveJournal и через полчаса на Twitter.
Посты, имеющие твиты, теперь показывают количество комментариев на Twitterе.
Этот пост собственно и был тестовым на момент публикации в ЖЖ.
В подвале сайта теперь стоит две иконки, по клику можно перейти на микроблог и в живой журнал. Постить там мы не будем принципиально, задача стоит в увеличении аудитории madru. Но это не значит что сливаться в социалки будет мусор — будем экспортировать полноценный анонс, правда с первым твитом как-то не вышло :)
Как оказалось, реализация такого функционала не простая. Для того чтобы не зависеть от сторонних библиотек, использовался FeedBurner для Twitter'а и xmlrpc для ЖЖ.
Про FeedBurner и твиттер писать не буду, функциональность там стандартная, никаких изменений в движке своего сайта делать не нужно. Так что Гугл вам в помощь. Но про LiveJournal напишу.
Что нам нужно?
- При публикации нового поста отправляем его анонс в ЖЖ.
- При правке существующего поста, правим его анонс в ЖЖ.
- При удалении поста — удаляем анонс из ЖЖ.
Вроде бы все несложно, да? :)
Итак, для работы с ЖЖ читаем сначала хелп: http://www.livejournal.com/doc/server/ljp.csp.xml-rpc.protocol.html. Видим два основных метода, которые могут нам понадобиться:
- postevent: отправка нового поста в ЖЖ;
- editevent: правка поста, который уже находится в ЖЖ;
Посредством этих двух методов можно связать практически любой движок вашего персонального блога с ЖЖ. Я не рассматриваю плагины для распространенных CMS, речь идет об уникальном движке и написании небольшой функции для работы с кросспостингом.
Во-первых, нам понадобится библиотека xmlrpc.
Во-вторых, пишем небольшую функцию. Отмечу сразу, как человек, не пытающийся изобретать велосипед, за основу я брал аналог, но правил под себя. Поскольку от исходного варианта осталось лишь несколько комментариев и оригинальных строк кода - публикую свой вариант.
function livejournal_posting($from_cms)
{
$errorPrefix = 'LiveJournal: ';
if ((!$from_cms or !$from_cms['title'] or !$from_cms['announcement'] or !$from_cms['uri'] or !$from_cms['data']) and !$from_cms['itemid'])
return (array($errorPrefix.'неверна структура передаваемых данных!', false));
else
{
//подключаем библиотеку
require_once("путь до библиотеки/xmlrpc/lib/xmlrpc.inc");
//выставляем внутреннюю кодировку,
//чтоб не было проблем с перекодированием
$GLOBALS['xmlrpc_internalencoding'] = 'UTF-8';
//логин и пароль к ЖЖ
$u_name = 'тут пишем логин';
$u_pass = 'тут пишем пароль';
//обращаемся к www.livejournal.com/interface/xmlrpc
$lj = new xmlrpc_client('/interface/xmlrpc','www.livejournal.com',80);
//кодировка клиента
$lj->request_charset_encoding = 'UTF-8';
//чтоб возвращал в виде php-переменных
$lj->return_type = 'phpvals';
//если нужен дебаг
//$lj->setDebug(3);
//получаем chellange
$chellange = $lj->send(
new xmlrpcmsg('LJ.XMLRPC.getchallenge'));
if($chellange->faultCode())
return (array($errorPrefix.'невозможно получить chellange: '.$chellange->faultString(), false));
$c = $chellange->value();
//собираем данные для поста
$data = array();
//имя пользователя
$data['username'] = new xmlrpcval($u_name,'string');
//метод аутентификации: clear, cookie или challenge
$data['auth_method'] = new xmlrpcval('challenge','string');
//строка с challenge
$data['auth_challenge'] = new xmlrpcval($c['challenge'],'string');
//шифруем пароль
$data['auth_response'] = new xmlrpcval(md5($c['challenge'] . md5($u_pass)),'string');
//версия протокола 0 или 1
//если используется 1, то все данные должны
//быть в кодировке UTF-8
$data['ver'] = new xmlrpcval('1','string');
$data['itemid'] = new xmlrpcval($from_cms['itemid'],'string');
if (!$from_cms['title'] && !$from_cms['announcement'] && !$from_cms['uri'] && !$from_cms['data'] && $from_cms['itemid'])
{
// не формируем элементы поста в $data, чтобы ЖЖ удалил указанный пост
}
else
{
//символ перевода строк \n или \r\n
$data['lineendings']=new xmlrpcval("\r\n",'string');
//название поста в UTF-8
$data['subject'] = new xmlrpcval(htmlspecialchars(strip_tags($from_cms['title'])),'string');
//текст поста в UTF-8
if ($from_cms['announcement'])
$data['event'] = new xmlrpcval($from_cms['announcement']."\r\n
<p><a href="\"".$from_cms['uri']."\"">читать полностью</a></p>",'string');
else
$data['event'] = '';
//дата
$data['day'] = new xmlrpcval(date('d', $from_cms['data']),'string');
$data['mon'] = new xmlrpcval(date('m', $from_cms['data']),'string');
$data['year'] = new xmlrpcval(date('Y', $from_cms['data']),'string');
$data['hour'] = new xmlrpcval(date('H', $from_cms['data']),'string');
$data['min'] = new xmlrpcval(date('i', $from_cms['data']),'string');
//доступ к посту публичный
$data['security'] = new xmlrpcval('public','string');
// определяем не задним ли числом добавляется запись в жж
$older = false; // по умолчанию нет
if ($from_cms['data'] < date("U"))
$older = true;
//некоторые мета-данные
$data['props'] = new xmlrpcval(array(
//true, если пост в отформатирован в html
'opt_preformatted' => new xmlrpcval(true,'boolean'),
//true, если запись добавляем задним числом
'opt_backdated' => new xmlrpcval($older,'boolean'),
//список тегов через запятую в UTF-8
'taglist' => new xmlrpcval($from_cms['labels'],'string'),
),'struct');
unset ($older);
}
$d = new xmlrpcval($data,'struct');
if (!$from_cms['itemid'])
{
// новый пост в lj
$result = $lj->send(new xmlrpcmsg('LJ.XMLRPC.postevent', array($d)),0,'http11');
}
else
{
// правка поста в lj или удаление
$result = $lj->send(new xmlrpcmsg('LJ.XMLRPC.editevent', array($d)),0,'http11');
}
//если произошла ошибка, то сообщаем об ошибке
if($result->faultCode())
return (array($errorPrefix.'ошибка постинга: '.$result->faultString(), false));
//если все нормально, то сервер вернет
//структуру с 3-мя переменными:
//itemid - идентификатор поста
//url - URL-адрес поста
//anum - аутентификационный номер,
//созданный для этой записи
$p_data = $result->value();
return (array('', $p_data['itemid']));
}
}
В кратце - функция принимает набор данных от скрипта публикации поста:
- 'title' - заголовок поста в ЖЖ
- 'announcement' - текст поста в ЖЖ (для нас это анонс поста в нашем блоге)
- 'uri' - адрес нашего поста в нашем блоге, чтобы с анонса в ЖЖ поставить ссылку (она прописана внутри функции)
- 'data' - дата поста. ВАЖНО! Её нужно передавать в функцию в формате Unix-метки
- 'labels' - через запятую без пробелов указываем теги поста (метки)
- 'itemid' - идентификатор поста в ЖЖ. При постинге новых постов его вернет функция, для правки старых — его придется хранить в БД вместе с постом в вашем блоге.
На выходе функции всего два параметра:
- Текст ошибки (если она есть)
- 'itemid', тоже если есть. Для удаленного поста из ЖЖ вернется пустое поле.
Все что остается - правильно вызвать функцию в скрипте постинга вашего блога (или любой новостной ленты) и обработать ошибки. Как-то так.
Здорово здорово! Давно пора!
А что имеется ввиду под анонсом? Абзац текста, который виден в ленте? А весь текст поста можно будет прочитать после перехода по ссылке на madru?
Вылезло несколько косяков и сразу наметился план по грандиозным улучшениям. Анонс будет уходить в том виде, в котором захочет админ. Да, весь пост — по ссылке на madru.