| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359 |
- use Modern::Perl;
- use Data::Dumper;
- use Mojo::Base -strict, -signatures, -async_await;
- use Mojo::Util;
- use Scalar::Util;
- use experimental qw/switch/;
- use fsa;
- our $redis;
- our $config;
- our $client;
- our $commands;
- our $abon_client;
- use constant FAIL_ASK_SERIAL => 3;
- ##################################################
- my $rules = {
-
- logged_out => async sub
- {
- my ($fsa, $line, $info) = @_;
-
- if ($line ne "/start")
- {
- reply($info, _("Для начала работы наберите <b>/start</b>"));
- return "logged_out";
- }
- # Поступила команда /start
-
- # У нас есть uid в автомате? Это значит, что мы прочитали состояние из внешней базы
- if ($fsa->note("uid"))
- {
- reply_with($info, { button_menu => 1 }, _("Для получения списка доступных команд введите /help"));
- return "command";
- }
-
- # Проверяем, логинился ли уже пользователь в нашем боте
- my $res = eval { await $client->get_p("client", "/telegram/$info->{id}/client") };
- if ($@)
- {
- if ($@->{code} == 410)
- {
- reply($info,
- _("Вас приветствует провайдер") . " " . $config->{provider},
- _("Введите номер учетной записи или логин")
- );
-
- return "needs_login";
- }
- else
- {
- die $@;
- }
- }
- reply_with($info, { button_menu => 1 }, greet($res->{fio}), _("Для получения списка доступных команд введите /help"));
- $fsa->note(uid => $res->{uid});
- return "command";
- },
-
- needs_login => sub($fsa, $line, $info)
- {
- $fsa->note(login => $line);
- reply($info, _("Теперь введите пароль"));
- "needs_password";
- },
- needs_password => async sub
- {
- my ($fsa, $line, $info) = @_;
-
- request("deleteMessage", { chat_id=>$info->{id}, message_id=>$info->{msgid} });
-
- my $res = eval { await $client->post_p("client", "/telegram/client", {login=>$fsa->note("login"), password=>$line, telegram_id=>$info->{id}}) };
- if ($@)
- {
- $fsa->state("logged_out");
- $fsa->delete_note("login");
- die $@;
- }
- reply_with($info, { button_menu => 1 }, greet($res->{fio}), _("Для получения списка доступных команд введите /help"));
- $fsa->note(uid => $res->{uid});
- $fsa->delete_note("login");
-
- "command";
- },
- command => async sub
- {
- my ($fsa, $line, $info) = @_;
- await do_command($fsa, $line, $info);
-
- $fsa->state; # или все тот же command, или команда установила уже свое состояние
- },
-
- #### Перевод денег
-
- xfer_needs_amount => async sub
- {
- my ($fsa, $amount, $info) = @_;
- my $uid = $fsa->note("uid");
- my $to_uid = $fsa->note("xfer_to");
-
- unless($amount)
- {
- reply($info, _("Перевод денег прерван"));
- }
-
- $amount =~ s/,/./g;
- $amount =~ s/[^\d\.]//g;
- my $tmp = $amount;
- my $count = $amount =~ tr/.//;
-
- if (!$amount || $count>1)
- {
- reply($info, _("Вы ввели неправильную сумму"), _("Введите ее заново или пустую строку, если передумали пополнять"));
- return "xfer_needs_amount";
- }
-
- $fsa->note(xfer_amount => $amount);
- reply_with($info, {
- inline_menu => [[
- { text=>_("Подтвердите перевод"), callback_data=>"\x00/transfer" },
- ]]
- },
- sprintf("%.2f %s %s %d", $amount, $config->{currency}->{human}, _(" на счет абонента"), $to_uid),
- );
-
- return "command";
- },
-
- #### Заявки
-
- task_needs_descr => async sub
- {
- my ($fsa, $descr, $info) = @_;
- my $uid = $fsa->note("uid");
-
- $fsa->note(task_text => $descr);
- reply_with($info, {
- inline_menu => [[
- { text=>_("Отправить"), callback_data=>"\x00/task_post" },
- { text=>_("Отменить"), callback_data=>"\x00/task_cancel" },
- ]]
- },
- _("Перед отправкой заявки вы можете ее отредактировать средствами Телеграм")
- );
- return "command";
- },
- task_needs_comment => async sub
- {
- my ($fsa, $comment, $info) = @_;
- my $uid = $fsa->note("uid");
- my $task_id = $fsa->note("task_id");
-
- my $params = {
- text => $comment,
- "for-client" => $uid,
- };
- $fsa->state("command"); # на случай die
- my $res = await $client->post_p("task", "/task/$task_id/comment", $params);
- reply($info, "Ваш ответ добавлен");
- $fsa->delete_note("task_id");
- return "command";
- },
-
- ##### Карточка пополнения
-
- card_needs_code => sub
- {
- my ($fsa, $code, $info) = @_;
- my $uid = $fsa->note("uid");
- $fsa->state("command"); # на случай die
- $code =~ s/\D//g;
-
- unless($code)
- {
- reply($info, _("Пополнение карточкой прервано"));
- }
- unless ($code =~ /^\d{16}$/)
- {
- reply($info, _("Код карточки должен состоять из 16 цифр"), _("Введите его заново или пустую строку, если передумали пополнять"));
- return "card_needs_code";
- }
-
- my $key = "card-guess-$uid";
- my $fails = $redis->get($key) || 0;
-
- if ($fails > FAIL_ASK_SERIAL)
- {
- $fsa->note(card_code => $code);
- reply($info, _("Теперь введите номер карточки (7 цифр, можно разделять знаком'-')"));
- return "card_needs_serial";
- }
- else
- {
- pay_from_card($info, $uid, $code, "");
- return "command";
- }
- },
-
- card_needs_serial => sub
- {
- my ($fsa, $serial, $info) = @_;
- $fsa->state("command"); # на случай die
- unless($serial)
- {
- reply($info, _("Пополнение карточкой прервано"));
- }
- unless ($serial =~ /^\d{7}$/)
- {
- reply($info, _("Номер карточки должен состоять из 7 цифр"), _("Введите его заново или пустую строку, если передумали пополнять"));
- return "card_needs_serial";
- }
- pay_from_card($info, $fsa->note("uid"), $fsa->note("card_code"), $serial);
-
- return "command";
- },
-
- needs_input => async sub
- {
- my ($fsa, $input, $info) = @_;
- my $uid = $fsa->note("uid");
-
- $fsa->state("command"); # на случай die
-
- my $name = $fsa->note("input_name");
- my $sub = refpath("verify_$name") || sub { undef };
-
- my $error = await $sub->($fsa, $input, $info);
- if ($error)
- {
- reply_with($info, {
- inline_menu => [[
- { text=>_("Отмена"), callback_data=>"\x00/cancel_input" },
- ]]
- }, $error, $fsa->note("input_again"));
- return "needs_input";
- }
- else
- {
- $fsa->note($name => $input);
- $sub = refpath("use_$name") || sub { "command" };
- my $state = await $sub->($fsa, $input, $info);
- return $state;
- }
- },
- };
- sub greet($whom)
- {
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
- my $greet = $hour>=4 && $hour<=10 ? _("Доброе утро") : $hour>10 && $hour<19 ? _("Добрый день") : _("Добрый вечер");
- "$greet, $whom";
- }
- async sub pay_from_card
- {
- my ($info, $uid, $code, $serial) = @_;
- my $args = {
- code => $code,
- uid => $uid,
- domain => $config->{domain},
- source => "abonbot",
- ip => "0.0.0.0",
- };
-
- $args->{serial} = $serial if $serial;
-
- my $res = eval { await $client->post_p("card", "/redemption", $args) };
- unless($@)
- {
- return reply($info, _("Ваш счёт пополнен на " . $res->{amount} . " " . $res->{currency_short_name_ru} . ". " . _("Оплачено до ") . $res->{paid_until}));
- };
- given($@->{code})
- {
- when(400)
- {
- reply($info, _("Вы ввели неправильный номер или код карточки"));
- my $key = "card-guess-$uid";
- $redis->incr($key);
- $redis->expire($key, 24 * 3600);
- }
- when(409)
- {
- reply($info, _("Карточка уже использована"));
- }
- when(406)
- {
- reply($info, _("Карточка не активирована. Обратитесь в службу поддержки"));
- }
- default
- {
- die $@;
- }
- }
- };
- ######################
- sub make_key($id)
- {
- return "abonbot-$id";
- }
- sub save_fsa($fsa, $chatid)
- {
- my $key = make_key($chatid);
- say "saving state ", $fsa->state, Dumper $fsa->notes;
-
- my $notes = $fsa->notes;
- utf8::encode($notes->{$_}) for keys %$notes;
- $redis->del($key);
- $redis->hmset($key, %$notes);
- $redis->expire($key, 3600*24);
- }
- sub make_fsa($chatid, $from)
- {
- my $id = make_key($chatid);
- my $notes = { $redis->hgetall($id) };
-
- if (keys %$notes)
- {
- utf8::decode($notes->{$_}) for keys %$notes;
- return fsa->new($rules, $notes->{_state}, $notes);
- }
- else
- {
- return fsa->new($rules, "logged_out", {});
- }
- }
- 1;
|