Интересно будет (возможно) компьютерщикам и юниксоидам. Краткий рассказ о том, как я устроил себе отсылку записей и комментов в ЖЖ.
После возвращения к Линуксу на новом ноутбуке я решил вернуться к старому и очень удобному способу отсылать комментарии в ЖЖ, и заодно улучшить его и расширить на записи. У меня был скрипт, позволяющий мне отвечать на комментарии в ЖЖ просто путем "обычного" ответа на письма с комментами из почты. Не та HTML-формочка, которая ведет на сайт ЖЖ, нет, это уродливо и неудобно, а отвечая в тексте на текстовые письма, как будто это обычное письмо. Мой скрипт перехватывал такие ответы, форматировал их специально (например, переводил цитирование строк, на которые отвечаещь, с помощью ">", обычное для почты, в цитирование всего абзаца курсивом, итд.). А потом строил правильный запрос к серверу ЖЖ и отсылал его, притворяясь той самой HTML-формочкой из писем с комментариями в HTML-формате.
Оставалась, однако, такая проблема: для того, чтобы отправить такой комментарий, нужно быть в сети. А новый ноутбук я собираюсь носить с собой и пользоваться им, и когда нет интернета. С обычными письмами это не проблема, они просто сидят себе в очереди и ждут, пока я подключусь, мне об этом не нужно и помнить. А с таким псевдо-письмами - проблема. Очевидно, нужна какая-то система отложенных задач (неотосланный комментарий - отложенная задача), которые сами выполнятся, когда я подключусь к сети, и все сделают. Кроме того, то же желательно иметь для записей. Меня не устраивает ни один из популярных ЖЖ-клиентов. Писать текст записей проще всего, по-моему, в текстовом редакторе, а хранить черновики и недописанные записи - просто в виде файлов в отведенной для этого директории. Послать запись на сервер должно быть просто - написать что-то вроде "ljpost file", а если связи в этот момент нет - пусть оно само запомнит где надо и позже отошлет, я не хочу об этом думать.
Я решил, что система отложенных задач должна быть настолько простой, насколько это возможно, и не требовать от меня никаких действий. И написать ее должно быть очень просто.
Вот мое решение. Расскажу сначала на примере скрипта для записей ljpost, проще понять. Это небольшой (4.5kb) скрипт на перле, получает текст записи либо в виде имени файла в командной строке, либо в потоке стандартного входа. Первым делом, или почти первым делом, он проверяет, есть ли сеть (пинг на www.livejournal.com). Если нет, он создает отложенную задачу.
О таких задачах. Считаем, что всё, что находится в каталоге ~/jobs - отложенные задачи. Любая такая задача - это файл, который просто можно выполнить, и больше никаких условий нет. Т.е. попытка выполнить отложенные задачи - это всего лишь пройтись по каталогу ~/jobs и запустить в нем все файлы, которые в принципе можно запустить (executable).
Теперь обозначим вход скрипту ljpost (т.е. весь текст записи) [INPUT] для примера, и предположим, что ему нужно отложить отсылку этой записи. Он создает в каталоге ~/jobs файл со случайным именем и следующим содержимым:
#!/bin/bash
/path/to/ljpost --canfail <<'DELIM-random'
[INPUT]
DELIM-random
if [ "\$?" -eq "0" ]; then
echo "[removing \$0]"
rm \$0
exit 0
else
exit 1
fi
Смысл: мы создаем shell-скрипт, который запускает ту же программу на том же тексте, подающемся на стандартный вход. Запускаем себя с опцией --canfail, которая говорит нам, что если нам опять не удастся (например, опять нет сети), можно просто вернуть код ошибки >0 и не создавать еще одну отложенную задачу (когда же я запускаю скрипт с командной строки без этой опции, неудача заставляет его создать отложенную задачу). Если при следующем запуске мы вернем код ошибки >0, ничего не случится, останемся в списке задач; если вернем код удачной отработки =0, то shell-скрипт удалит сам себя, задача выполнена и ее больше нет.
Теперь достаточно запускать через crontab раз в полчаса, скажем, команду запуска всего, что можно, из ~/jobs, и все.
Теперь об обработке текста записи. Мы хотим в ней передать не только сам текст, но также заголовок, название коммьюнити (если не в свой журнал), current music итд. Следующая очень простая система мне подходит идеально. Текст может начинаться с любого числа контрольных строк вида: to: communityname, mood: moodname, music: current music, subj: заголовок записи. Кроме этого, если то, что после этих контрольных строк, начинается с одной строки и после нее пустая строка - это считается заголовком, а все остальное главным текстом. Т.е. если, например, просто написать запись в свой дневник с заголовком и без всяких опций, то это написать заголовок, пропустить строку, и дальше текст всей записи.
Скрипт это все понимает, правильно разделяет, строит запрос к www.livejournal.com по протоколу XMLRPC, и отсылает. Если есть ошибка, опять-таки создает отложенную задачу, чтобы позже попробовать.
О разметке в теле записи. Тело записи скрипт перед отсылкой пропускает через простую программу markdown. Markdown существует для того, чтобы быстро и удобно писать текст с разметкой, который превращается в HTML. Например, не надо писать <i> </i>, достаточно выделить кусок текста звездочками. Не надо писать <blockquote>, достаточно начать абзац со знака "> " (как в почте). Вместо HTML-тагов для построения списка, просто пишем элементы списка через звездочки с новой строки. Ссылки тоже немного удобней, чем выписывать в HTML, и многое другое. Подробности см. на его сайте. Я давно хотел попробовать им пользоваться, и вот теперь с этим моим скриптом живу уже неделю и очень доволен скоростью и удобством сочинения записей.
Наконец, о комментариях. Я изменил и упростил то, как запускается скрипт для отсылки комментариев. Раньше для него был специальный отдельный пользователь на моей машине, которому пересылались псевдо-письма ответов; теперь он просто ведет себя так же, как обычный sendmail, а моя почтовая программа для отсылки писем вызывает его, а не sendmail, если письмо идет на определенный псевдо-адрес (тут необходима определенная помощь во время получения писем с комментариями, нужно простым фильтром вытащить из них имя журнала, номер записи и комментария-"отца", составить из них псевдо-адрес и вставить в поле Reply-to:, чтобы ответы автоматически шли на него. Три строки в моем файле фильтровки входящей почты). Он обрабатывает текст комментария, удаляет из него лишнее (например, весь текст того, на что я отвечаю, если он остался полностью непроцитированным внизу, чтобы мне не надо было удалять), и, если получается, отсылает на сервер описанным выше способом, а если нет, создает отложенную задачу по тому же точно принципу, что и ljpost.
Если кому-то нужно/интересно, выложу исходники скриптов. Да, предваряя вопрос, имя пользователя/пароль в данный момент в них забиты прямо в тексте, что не есть хорошо с точки зрения секьюрити. По сути дела пароль нужен только для того, чтобы делать md5 некоторым строкам, включающим его. Намного более безопасным вариантом было бы, например, запустить простенький сервис (на том же компьютере другим пользователем, или на другом компьютере), который слушает какой-то порт, принимает по нему строку X и возвращает МД5(X+password), или другие варианты, какие там нужны. Наверное, я это сделаю в том или ином виде в ближайшие дни.