
Думаю, многим приходила в голову идея автоматизировать ту или иную последовательность действий, чтобы заняться чем-то действительно важным и грандиозным, например, сходить в душ или поесть. Эта история о реализации одной такой идеи.
СОДЕРЖАНИЕ
ОБОЗНАЧЕНИЯ И СОКРАЩЕНИЯ
ВВЕДЕНИЕ
1. ОПИСАНИЕ СЕРВИСОВ И СИСТЕМ
2. ЗАДАЧА ПРОЕКТА
3. ОПИСАНИЕ ПРОЕКТА
3.1. Средства для получения состояния лицевого счета
3.2. Средства для работы с ЯД
3.3. Средства для работы с Твиттером
4. РАБОТА И ЛОГИКА
ЗАКЛЮЧЕНИЕ
СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ
ОБОЗНАЧЕНИЯ И СОКРАЩЕНИЯ
ЛИСА — личный интернет-сервис абонента;
ЯД — Яндекс.Деньги;
Выпить ЯДу — получить денежные средства на Яндекс-кошелек;
БД — база данных.
ВВЕДЕНИЕ
Мне, как и многим, то и дело приходят мысли о том, что в нашей жизни есть множество вещей, которые чрезмерно сложны и запутаны, они требуют упрощения. Примерно такая мысль мне и пришла, когда, в очередной раз, я получил текст от своего мобильного оператора, о том, что баланс приближается к нулю и, в очередной раз, я пошел на ЯД, прошел авторизацию, нажал повторить платеж, подтвердил, указал платежный пароль, дождался выполнения, вышел из ЯД. Как-то многовато действий, вам не кажется? Мне показалось именно так, да и вообще, очень это утомительный процесс, нужно найти более простой способ, который бы мне подходил.
Хочется отметить, процесс так меня захватил, что теперь не уверен, — сэкономил ли я на всем этом время или проще было оставить все как есть? Однако, человек я такой, увлекаюсь во всякие интересные занятия быстро, поэтому большее удовольствия принес сам проц��сс реализации, чем конечный результат.
1. ОПИСАНИЕ СЕРВИСОВ И СИСТЕМ
ЛИСА— сервис, который позволяет абонентам Компании «Мотив» управлять текущим состоянием своего лицевого счета и пользоваться услугами при помощи сети интернет.
ЯД — электронная платёжная система. Многие магазины и компании реализуют у себя поддержку ЯД, что позволяет расплачиваться за товары и услуги легко и просто. «Мотив», как раз, одна из тех компаний, где реализована возможность пополнения баланса через ЯД.
Твиттер — система, позволяющая пользователям отправлять короткие текстовые заметки (до 140 символов), используя веб-интерфейс, SMS, средства мгновенного обмена сообщениями или сторонние программы-клиенты. Отличительной особенностью Твиттера является публичная доступность размещённых сообщений, что роднит его с блогами.
Вернуться к содержанию →
2. ЗАДАЧА ПРОЕКТА
Реализация системы пополнения баланса мобильного телефона с кошелька ЯД является основной задачей проекта, пополнение баланса должно происходить без какой-либо заданной периодичности. Мобильный счет должен пополняться только тогда, когда это необходимо.
Система должна включать в себя:
- Средства для получения состояния лицевого счета;
- Средства для работы с ЯД;
- Средства для работы с Твиттером.
Вернуться к содержанию →
3. ОПИСАНИЕ ПРОЕКТА
Проект размещен на площадке Мастерхост, хостинг получен бесплатно, в рамках программы поддержки студентов. Потребовалось обратиться в техническую поддержку, для включения curl и получения информации по поводу планировщика, так как на windows-тарифах данная услуга не предоставлялась. Было предложено добавить запись в crontab на unix-сервере, на что я и согласился. Я передал ссылку на скрипт и указал периодичность в 10 минут, после чего меня предупредили, — если скрипт будет создавать большую нагрузку, его придется отключить.
Вернуться к содержанию →
3.1. Средства для получения состояния лицевого счета
В первой версии состояние мобильного счета приходилось получать непосредственно через сайт ЛИСЫ, что занимало достаточно продолжительно время и не всегда давало результат, порой длительность операции достигала 10 секунд, что было просто неприемлемо.

Рисунок 3.1 — Сайт сервиса ЛИСА.
После длительного и беспощадного общения с технической поддержкой Компании «Мотив», мне стало известно, что в ближайшее время будет введен «Гаджет баланса», который и предназначен для отображения текущего баланса лицевого счета. Помимо этого, я получил информацию о том, что никакого внешнего интерфейса у сервиса не планируется, который, к слову, мне уже был не нужен, у меня была уверенность, что всю информацию я смогу достать из гаджета.

Рисунок 3.2 — Информация о «Гаджете баланса» и кнопка для его формирования.
Ранее мне не приходилось работать с гаджетами для Windows 7 или Vista, после установки\переустановки операционной системы я сразу отключал все это дело, так как не видел в этом необходимости, а на домашнем компьютере у меня старая-добрая Windows XP.

Рисунок 3.3 — Внутреннее строение «Гаджета баланса».
В каталоге «motiv_balance.gadget\js» находятся файлы:
- balance.js — основная логика гаджета;
- jquery.js — распространенная javascript-библиотека;
- settings.js — реализация функций чтение и сохранение настроек гаджета.
ожидается ответ, далее передается в функцию showResponse(), которая и отвечает за обработку и отображение результата._req = new ActiveXObject("Microsoft.XMLHTTP"); /*don't change this sequence! it invokes non-ordering requests: xz why*/ _req.open("GET", url, true);
Как вы уже заметили, обращение к сайту оператора представляет собой не что иное, как обычный GET-запрос, который состоит из нескольких параметров:
- uid — уникальный идентификатор, привязанный к номеру абонента;
- b — тип запроса данных. Может принимать два значения: «a» — упрощенный отчёт (только состояние лицевого счета) или «b» — расширенный (состояние лицевого счета + остатки по абонементам\пакетам услуг, например, количество купленных SMS или MMS). Не уверен, что всё это работает, данное значение, мистическим образом, зависит от System.Gadget.docked;
- a — случайное число.
В ответ получаем либо информацию о текущем состоянии счета, например:b = System.Gadget.docked ? 'a' : 'b'; a = Math.round(Math.random() * 10000); /*it should be global: no ideas why*/ var url = "https://gadget.motivtelecom.ru/?uid=UID&b="+b+"&a=" + a;
3=RUB:80р.45к.где 3 — это номер версии гаджета, на момент написания статьи (var VERSION=3;), либо извещение об ошибке
3=ERR
Вооружившись этими знаниями, я написал простейшую функцию, которая cURL-ом отправляет запрос.
Время, требуемое на выполнение запроса и обработку результата, с 10 секунд уменьшилось до 0.26 секунды.$req_lisa = array( CURLOPT_HEADER => 0, CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_TIMEOUT => 10, CURLOPT_SSL_VERIFYPEER => 0, CURLOPT_SSL_VERIFYHOST => 0, CURLOPT_RETURNTRANSFER => TRUE, CURLOPT_URL => LISA_URL ); $ci = curl_init(); curl_setopt_array ($ci, $req_lisa); $result_lisa = curl_exec($ci);
Вернуться к содержанию →
3.2. Средства для работы с ЯД
Для работы с ЯДом требуется:
- Изучить документацию;
- Зарегистрировать приложение (получить client_id);
- Получить временный токен (сроком менее 1 минуты);
- Получить токен авторизации (сроком на 1 год).

Рисунок 3.4 — Информация о зарегистрированном приложении.
Полученный токен авторизации ограничен 1 магазином (Компания «Мотив») и лимитом «1 тысяча рублей в неделю» с возможностью просмотра информации о текущем счете:
Теперь в нашем распоряжении есть методы:scope=payment.to-pattern("1122").limit(7,1000) account-info
- account-info — информация о состоянии счета, данный метод мною не использовался;
- request-payment — запрос платежа;
- process-payment — подтверждение платежа;
pattern_id=1122&PROPERTY1=908&PROPERTY2=9089XXX&sum=%s
и отправляем request-payment запрос на ЯД, если все верно, то получаем json-строку со значением «success» параметра status и желанный request_id, все остальное для нас, отличное от «success», равносильно неудаче и может зависеть от разных факторов, таких как: технические проблемы на стороне Яндекса, технические проблемы на стороне магазина или что-то еще.
Если всё в полном порядке, то отправляем уже process-payment запрос, содержащий request_id, тем самым мы подтверждаем платеж:
В случае успешного или неудавшегося платежа, мы так же получаем json-строку с параметром status. При значении «success» параметра status в ответ приходит номер платежа и информация о состоянии счета.$yad = json_decode($result, 1); if ( $yad['status'] == 'success' ) { $pay_fields = sprintf($process_fields, $yad['request_id']); curl_setopt($ci, CURLOPT_URL, YM_PROC_URL); curl_setopt($ci, CURLOPT_POSTFIELDS, $pay_fields); $result_pay = curl_exec($ci); curl_close($ci); return json_decode($result_pay, 1); }
Вернуться к содержанию →
3.3. Средства для работы с Твиттером
Для работы была выбрана готовая библиотека, требовалось лишь зарегистрировать свое приложение на сайте для разработчиков и получить все необходимые параметры для работы:
- Consumer key
- Consumer secret
- Access token
- Access token secret

Мне показалось, что будет очень уныло и не круто, если в твиттере будут публиковаться одни и те же твиты, поэтому я набросал набор фраз, которыми сейчас оперирует мое приложение-бот:
Работает библиотека предельно просто:function GetRandomTwit() { $music_bands = array ( 'Queen', 'Coldplay', 'Muse', 'Justice', 'Duft Punk', 'Radiohead', 'Adele', 'Beatles', 'Red Hot Chili Peppers', 'Foo Fighters', 'Killers', 'Gorillaz' ); $rand_music_band = $music_bands[rand(0, sizeof($music_bands)-1)]; $twi_mask = array ( 'На счету Лео, совершенно точно, есть %1$s #motiv #motivtelecom #wtf', 'У Лео на телефоне: %1$s #motiv', 'Баланс изменился, теперь составляет %1$s #motiv', '%1$s если быть точным #motiv', '#motiv хватит списывать деньги! На счету осталось %1$s', 'Мы принимаем Яндекс.Деньги, вот они %3$s. Остаток: %1$s #motivtelecom', '%1$s и постоянно убывает, что делать? #motiv', 'А баланс-то изменился, теперь там %1$s #motiv', 'Переслушивал дискографию '.$rand_music_band.' и вдруг... на счету осталось уже %1$s #motiv', 'БАЦ! И на счету уже %1$s #motiv #motivtelecom #whyyyyyy', 'Иногда, #motiv списывает деньги просто так, возможно, это происходит прямо сейчас ;) Остаток %2$s руб.', 'Зуб даю, списали случайно, Лео никуда не звонил! Ну вот, теперь уже %1$s', 'Я не спец в финансовом деле, но знаю, что на счету Лео %1$s #motivtelecom', 'Y U SO GREEDY? %2$s rub #motiv #motivtelecom', '#motiv у меня ВСЁ записано! Баланс %1$s #motivtelecom #yusogreedy', 'Если бы знал, что ботам Лео ничего не платит, никогда бы у него не стал работать! Остаток %2$s руб.', 'Сбор средств на GPRS-трафик ЯД: %3$s. Остаток на счету: %1$s #motiv', 'Дааа, жаль у тебя не безлимит :( %2$s руб. #motiv #motivtelecom', 'Я бот-меломан, сейчас слушаю '.$rand_music_band.', а на счету %2$s руб. #motiv #'.str_replace(' ','', strtolower($rand_music_band)), 'А ведь раньше я работал почтовым роботом.. до чего я докатился! :( А баланс уже %2$s руб.', '#motiv дадите спокойно послушать альбом '.$rand_music_band.' или нет? Ок, %2$s руб на счету.', 'И снова здравствуйте! На счету %1$s #motivtelecom', 'Отправил, за счет Лео, СМС другу - спам-боту %) Теперь на счету %1$s #motiv', 'Отправляйте мне свои предложение, в поле Примечание, на ЯД %3$s :) На счету %2$s руб.', 'Я образованный бот, почему я должен это делать? Ок, Лео, на твоем счету %2$s руб. #motiv', 'Работа - не волк, поэтому сообщу о балансе в следующий раз. #motiv', 'BREAKING NEWS! Balance: %2$s rub.', 'Я свободно владею несколькими языками, а должен заниматься этим! Лео, на твоем счету %2$s руб.' ); return ($twi_mask[rand(0, sizeof($twi_mask)-1)]); }
$twitter = new Twitter(TWT_CON_KEY, TWT_CON_SEC, TWT_ACC_TOK, TWT_ACC_TOK_SEC); $twitter->send(sprintf(GetRandomTwit(), GetSumWithPost($current_bal_value), $current_bal_value, YM_WALLET));
Вернуться к содержанию →
4. РАБОТА И ЛОГИКА
Главная задумка приложения — сделать плату своевременной, мне не нужно, чтобы баланс пополнялся каждый день на 50 или 100 рублей, ведь я могу просто не пользоваться в этот день телефоном и это можно было реализовать на ЯД. Дело оставалось за логикой работы, ведь все компоненты уже есть и работают.
Опустив лишние проверки, все сводится к понятному виду:
1. Проверяю текущий баланс, чтобы знать когда пополнять, надо всегда быть в курсе сколько сейчас денег на счету;
2. Определяю разницу между эталонным значением баланса (100 рублей) и текущим;
2.1. Разница больше заданного значения (20 рублей):
2.1.1. произвожу оплату;
2.1.2. пишу новое значение в БД;
2.1.3. пишу в Твиттер.
2.2. Разница меньше заданного значения (20 рублей):
2.2.1. Сравниваю текущее значение с последним, записанным в БД, если разница положительная, значит баланс пополнился:
2.2.1.1. пишу новое значение в БД;
2.2.1.2. пишу в Твиттер, что кто-то из вне пополнил баланс.
2.2.2. Сравниваю текущее значение с последним, записанным в БД, если разница отрицательная и меньше заданного значения (-5 рублей):
2.2.2.1. пишу новое значение в БД;
2.2.2.2. пишу в Твиттер, что баланс изменился.
Есть некоторые оговорки, которые пришлось учитывать:
— компания «Мотив» не принимает платежи менее 10 рублей — это означает, что разница между текущим состоянием счета и эталонным заданным значением должна составлять, как минимум, 10 рублей, чтобы произвести пополнение;
— сумма в 20 рублей взята для того, чтобы часто не писать в Твиттер о пополнении счета;
— разница в 5 рублей, о которой упоминалось выше, взята не просто так, ранее ограничения не было вообще и приложение сообщало, посредством Твитт��р, каждые 10 минут о том, что баланс изменился на 7 копеек, а то и еще меньше — это сподвигло меня поставить некий ограничитель, сначала он был равен 1 рублю, но и этого оказалось мало, теперь ограничитель равен 5 рублям и меня ничто больше не беспокоит;
— если разница между текущим значением и эталонным оставляет более 100 рублей, то начинаем подозревать что-то неладное и пополняем баланс частями, сначала 100 рублей, потом оставшиеся деньги, по такому же принципу.
Будет достаточно времени, чтобы ответить на вопросы: «Куда делись деньги? Почему такой большой минус? Кто виноват?», выпить чашечку чая и, в конце концов, отключить бота.$pay_bot = (floor(BAL_LEVEL-$current_bal_value)>BAL_PAY_LEVEL_MAX)?BAL_PAY_LEVEL_MAX:(floor(BAL_LEVEL-$current_bal_value));
Отмечу, что на значение BAL_UP_SUM увеличивается любой платеж. Сейчас значение BAL_UP_SUM равно 1, вы видите, что выше все расчеты применяются с округлением в меньшую сторону, чтобы выйти за пределы 99 рублей с копейками следует добавить 1 рубль. Так же это значение можно увеличивать для того, чтобы приложение не докучало своей постоянно писаниной в Твиттер.$pay_bot = (floor(BAL_LEVEL-$current_bal_value)>BAL_PAY_LEVEL_MAX)?BAL_PAY_LEVEL_MAX:(floor(BAL_LEVEL-$current_bal_value)); if ( $pay_bot >= BAL_PAY_LEVEL ) { $pay_bot += BAL_UP_SUM; $ym_pay_state = YandexMoneyPay($pay_bot); if ( $ym_pay_state['status'] == YM_PAY_STAT ) { $twitter = new Twitter(TWT_CON_KEY, TWT_CON_SEC, TWT_ACC_TOK, TWT_ACC_TOK_SEC); $twitter->send(sprintf($plus_pay, GetSumWithPost($pay_bot), GetSumWithPost($current_bal_value+$pay_bot), GetSumWithPost($ym_pay_state['balance']))); } } else { $pay_user = floor($current_bal_value - $last_bal_value); $bal_diff = $last_bal_value - $current_bal_value; if ( $pay_user > 0 or $bal_diff >= BAL_CHANGE_LEVEL ) { $twitter = new Twitter(TWT_CON_KEY, TWT_CON_SEC, TWT_ACC_TOK, TWT_ACC_TOK_SEC); if ( $pay_user > 0 ) $twitter->send(sprintf($user_pay, GetSumWithPost($pay_user), GetSumWithPost($current_bal_value))); elseif ( $bal_diff >= BAL_CHANGE_LEVEL ) $twitter->send(sprintf(GetRandomTwit(), GetSumWithPost($current_bal_value), $current_bal_value, YM_WALLET)); } }
Вернуться к содержанию →
ЗАКЛЮЧЕНИЕ
Во время написания столкнулся с некоторыми вопросами, на которые так и не нашел ответ.
Я долго не мог понять, почему не могу сделать перевод на мобильный счет через ЯД, хотя в ответе на request-payment-запрос приходит значение «success». Оказалось, что приведенный в документации пример:
pattern_id=2904&phone-prefix=921&phone-number=9538ХХХ&sum=300.00
с содержанием полей phone-prefix и phone-number, не обязателен для каждого магазина, у Мотива эти параметры носят имена PROPERTY1 и PROPERTY2, у МТС или Мегафон эти поля тоже могут носить другие названия. Поля для Мотива мне подсказали модераторы клуба API ЯД, что и странно. Раз в ЯД знаю значения этих полей, почему просто, во время платежа, не могут переназначить со своих имен параметров на параметры магазина?
И еще один вопрос, он напрямую связан с первым. Когда я отправлял запрос с неверными именами полей мне, почему-то, приходило значение «success», так же приходил параметр request_id, по которому нужно подтверждать платеж. Такие платежи, конечно, не проходили, но почему в status мне приходил «success»? В руководстве разработчика ясно написано, что «success» — это успешное выполнение.
Буду рад, если кто-то ответит на эти вопросы.
Хочется выразить благодарность:
- Сотруднику компании «Мотив» — Марине Neumann;
- Модераторам клуба API Яндекс.Денег — Лине и hh;
- Сотрудникам компании Мастерхост.
Вернуться к содержанию →
СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ
1. API Яндекс.Денег. Руководство разработчика // материалы сайта api.yandex.ru/money/doc/dg/.
2. Клуб для разработчиков, использующих API Яндекс.Денег // материалы сайта clubs.ya.ru/moneyapi/.
3. Twitter. Сайт для разработчиков // материалы сайта dev.twitter.com.
4. ЛИСА. Гаджет баланса // материалы сайта lisa.motivtelecom.ru.
5. Материалы сайта ru.wikipedia.org.
