| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561 |
- use Modern::Perl;
- use utf8;
- use Mojo::Base -strict, -async_await, -signatures;
- use Data::Dumper;
- use experimental qw/switch/;
- our $client;
- our $abon_client;
- our $config;
- our $redis;
- sub __
- {
- @_;
- }
- ##############################################
- our $commands = [
- {command=>"help", name=>__("Помощь"), description=>__("Список доступных команд")},
- {command=>"info", name=>__("Информация"), description=>__("Информация о пользователе")},
- {command=>"balance", name=>__("Баланс"), description=>__("Проверка баланса"), main=>1, icon=>"\x{1F45B}"},
- {command=>"service", name=>__("Сервисы"), description=>__("Подключенные сервисы")},
- {command=>"credit", name=>__("Кредит"), description=>__("Установка кредита"), main=>1, icon=>"\x{1FAF0}"},
- {command=>"card", name=>__("Карта пополнения"), description=>__("Оплата карточкой пополнения"), main=>1, icon=>"\x{1F4B3}"},
- {command=>"payberry", name=>__("Payberry"), description=>__("Оплата через Payberry"), main=>1, icon=>"\x{1F17F}\x{FE0F}"},
- {command=>"transfer", name=>__("Перевод"), description=>__("Перевод денег")},
- {command=>"new_task", name=>__("Новая заявка"), description=>__("Новая заявка")},
- {command=>"tasks", name=>__("Заявки"), description=>__("Открытые заявки"), main=>1, icon=>"\x{2692}\x{FE0F}"},
- {command=>"support", name=>__("Техподдержка"), description=>__("Связь с техподдержкой")},
- {command=>"logout", name=>__("Выход"), description=>__("Выход")},
- ];
- #############################
- our $lex_actions = {
- xfer => {
- name => __("перевод денег"),
- canceled => __("отменён"),
- },
- card => {
- name => __("пополнение карточкой"),
- canceled => __("отмененo"),
- },
- };
- our $lex_vars = {
- xfer_to => __("номер личного счета"),
- xfer_amount => __("сумму"),
- card_code => __("код карточки"),
- card_serial => __("номер карточки"),
- };
- ##############################################
- sub command_help($fsa, $info)
- {
- my @list = map { "<b>/$_->{command}</b> " . _($_->{description}) } @$commands;
- reply($info, join("\n", @list));
- }
- async sub command_logout
- {
- my ($fsa, $info) = @_;
-
- my $uid = $fsa->note("[uid]");
- await $client->delete_p("client", "/client/$uid/telegram");
- reply_with($info, {button_menu => 0}, _("Благодарим за использование нашего бота"));
- $fsa->delete_note("[uid]");
- $fsa->state("logged_out");
- }
- async sub command_balance
- {
- my ($fsa, $info) = @_;
- my $uid = $fsa->note("[uid]");
- my $money = await $abon_client->get_p($info, "client", "/client/$uid/money?human=1");
- my $cur = $money->{human};
-
- my @lines = (
- sprintf("<u>%s:</u> <b>%.2f $cur</b> (%s %.2f $cur + %s %.2f $cur) ",
- _("Ваш баланс"), $money->{balance}, ("депозит"), $money->{deposit}, _("кредит"), $money->{credit}),
- );
-
- push @lines, sprintf("<u>%s:</u> %s", _("Оплачено включительно до"), format_date($money->{last_day})) if $money->{last_day} ne "-";
- push @lines, sprintf("<u>%s:</u> %d%%", _("Скидка"), $money->{reduction}) if $money->{reduction};
- push @lines, sprintf("<u>%s:</u> <b>%.2f $cur</b> %s (%s)",
- _("Последнее снятие"), $money->{last_withdrawal}->{sum}, format_time($money->{last_withdrawal}->{date}), $money->{last_withdrawal}->{comment})
- if $money->{last_withdrawal}->{sum};
- push @lines, sprintf("<u>%s:</u> <b>%.2f $cur</b> %s", _("Последний платеж"), $money->{last_payment}->{sum}, format_time($money->{last_payment}->{date}))
- if $money->{last_payment}->{sum};
- for (keys %{ $money->{accounts} })
- {
- push @lines, sprintf("<u>%s:</u> <b>%.2f $cur</b>", $_, $money->{accounts}->{$_});
- }
-
- reply($info, @lines);
- }
- async sub command_info
- {
- my ($fsa, $info) = @_;
- my $uid = $fsa->note("[uid]");
- my $client = await $abon_client->get_p($info, "client", "/client/$uid");
- reply($info,
- sprintf("<u>%s</u>: %d", _("Номер учетной записи"), $client->{uid}),
- sprintf("<u>%s</u>: %s", _("Логин"), $client->{login}),
- sprintf("<u>%s</u>: %s", _("ФИО"), $client->{fio}),
- sprintf("<u>%s</u>: %s", _("Адрес"), $client->{address}),
- sprintf("<u>%s</u>: %s", _("Телефон"), $client->{phone}),
- );
- }
- async sub command_credit
- {
- my ($fsa, $info) = @_;
- my $uid = $fsa->note("[uid]");
- my $money = await $abon_client->get_p($info, "client", "/client/$uid/money?human=1");
- if ($money->{credit} > 0)
- {
- return reply($info, sprintf("%s <b>%.2f %s</b>. %s", _("У вас уже установлен кредит"), $money->{credit}, $money->{human},
- _("Продлевать кредит повторно до оплаты нельзя. При следующей оплате кредит будет погашен")));
- }
-
- reply_with($info, {
- inline_menu => [[
- { text=>_("Я согласен с условиями"), callback_data=>"\x00/set-credit" },
- ]],
- },
- _("Вы можете самостоятельно установить кредит на два дня"),
- _("<b>Ограничения</b>: только для физических лиц, продлевать кредит повторно до оплаты нельзя. При следующей оплате кредит будет погашен"),
- );
- }
- async sub callback_set_credit
- {
- my ($fsa, $info) = @_;
- my $uid = $fsa->note("[uid]");
- my $res = await $abon_client->post_p($info, "client", "/client/$uid/credit", {human=>1});
- return reply($info, _("Кредит не имеет смысла для бесплатных тарифных планов")) if $res->{credit} == 0;
-
- await reply_with($info, {button_menu=>1},
- sprintf("%s <b>%.2f %s</b>", _("Установлен кредит "), $res->{credit}, $res->{human}),
- "",
- );
-
- command_balance($fsa, $info);
- }
- async sub command_service
- {
- my ($fsa, $info) = @_;
- my $uid = $fsa->note("[uid]");
-
- my $res = await $abon_client->get_p($info, "client", "/client/$uid/service?human=1&as-array=1");
- my @list = map { sprintf("<u>%s:</u> %s (%s '%s')", $_->{name_ru}, format_wd($_->{tariff}, $_->{human}), _("тариф"), $_->{tariff}->{name_ru}) }
- grep { !$_->{disabled} } @$res;
-
- reply($info, @list);
- };
- ######################################
- async sub command_transfer
- {
- my ($fsa, $info) = @_;
- reply($info, _("Введите номер личного счета пользователя, которому вы хотите перевести деньги со своего собственного счета"));
-
- return needs_input($fsa, "xfer", "xfer_to");
- }
- async sub verify_xfer_to
- {
- my ($fsa, $target_uid, $info) = @_;
- my $uid = $fsa->note("[uid]");
-
- return _("Номер личного счета должен состоять из цифр") unless $target_uid =~ /^\d+$/;
- return _("Нельзя перевести деньги себе самому") if $uid==$target_uid;
-
- my $res = eval { await $abon_client->get_p($info, "client", "/client/$target_uid") };
- if ($@)
- {
- die $@ unless $@->{code} == 404;
- return _("Абонент") . " $target_uid " . _("не найден");
- }
- return _("Абонент") . " $target_uid " . _("отключен") if $res->{disabled};
-
- $fsa->note("xfer_fio" => $res->{fio});
- return undef;
- }
- async sub use_xfer_to
- {
- my ($fsa, $target_uid, $info) = @_;
- my $uid = $fsa->note("[uid]");
-
- my $nick = $fsa->note("xfer_fio");
- my @fio = split(/\s+/, $nick);
- if (@fio)
- {
- my $f = shift(@fio);
- $nick = join(" ", (substr($f, 0, 1) . ".", @fio));
- }
-
- $fsa->note(xfer_nick => $nick);
-
- reply(
- $info, _("Получатель денег: ") . $nick,
- _("Теперь введите сумму, которую хотите перевести"),
- );
- return needs_input($fsa, "xfer", "xfer_amount");
- }
- sub verify_xfer_amount
- {
- my ($fsa, $amount, $info) = @_;
- my $uid = $fsa->note("[uid]");
-
- $amount =~ s/,/./g;
- $amount =~ s/[^\d\.]//g;
- my $tmp = $amount;
- my $count = $amount =~ tr/.//;
-
- return _("Вы ввели неправильную сумму") if !$amount || $count>1;
- }
- async sub use_xfer_amount
- {
- my ($fsa, $amount, $info) = @_;
-
- my $uid = $fsa->note("[uid]");
- my $to_uid = $fsa->note("xfer_to");
- my $nick = $fsa->note("xfer_nick");
-
- reply_with($info, {
- inline_menu => [[
- { text=>_("Подтвердите перевод"), callback_data=>"\x00/transfer" },
- ]]
- },
- sprintf("%.2f %s %s %d (%s)", $amount, $config->{currency}->{human}, _(" на счет абонента"), $to_uid, $nick)
- );
-
- return "command";
- }
- async sub callback_transfer
- {
- my ($fsa, $info) = @_;
- my $uid = $fsa->note("[uid]");
-
- my $to_uid = $fsa->note("xfer_to");
- my $amount = $fsa->note("xfer_amount");
- return unless $to_uid && $amount;
-
- my $res = await $abon_client->post_p($info, "client", "/client/$uid/money/to/$to_uid", {
- amount => $amount,
- ip => "0.0.0.0",
- via => "abonbot",
- currency => $config->{currency}->{name},
- });
-
- reply_with($info, {button_menu=>1}, _("Деньги успешно переведены"));
- command_balance($fsa, $info);
- }
- ##########################################
- async sub command_new_task
- {
- my ($fsa, $info) = @_;
- my $uid = $fsa->note("[uid]");
-
- my $res = await $abon_client->get_p($info, "task", "/task?created_by_client=$uid&list=new,work");
-
- if ($res->{total})
- {
- return reply($info, _("Создание новой заявки невозможно, пока не будут решены уже открытые"));
- }
- reply($info, _("Изложите вашу проблему"));
- $fsa->state("task_needs_descr");
- };
- async sub callback_task_post
- {
- my ($fsa, $info) = @_;
- my $uid = $fsa->note("[uid]");
-
- my $descr = $fsa->note("task_text");
- return unless $descr;
- my $params = {
- description => $descr,
- "for-client" => $uid,
- client => $uid,
- type => "client-issue",
- list => "new",
- task_attr => {
- source => "telegram"
- },
- };
- my $res = await $client->post_json_p("task", "/task", $params);
- reply_with($info, {button_menu=>1}, _("Ваша заявка размещена"));
- command_tasks($fsa, $info);
- }
- async sub callback_task_cancel
- {
- my ($fsa, $info) = @_;
- $fsa->delete_note("task_text");
- $fsa->delete_note("if_edited");
- reply_with($info, {button_menu=>1}, _("Заявка отменена"));
- }
- async sub command_tasks
- {
- my ($fsa, $info) = @_;
- my $uid = $fsa->note("[uid]");
-
- my $res = await $abon_client->get_p($info, "task", "/task?created_by_client=$uid&list=new,work&sort=id&with_comments_for_client=1");
-
- return reply($info, _("Открытых заявок нет")) unless $res->{total};
-
- my @str = map {
- _("Заявка") . " <b>$_->{number}</b>\n$_->{description}\n<i>" . _("Комментариев") . " " . $_->{total_comments} . ", " . _(" не прочитано") . " " . $_->{unread_comments} . "</i>"
- } @{$res->{data}};
-
- my $menu = [[
- map {
- { text => _("Комментарии к заявке") . " " . $_->{number}, callback_data => "\x00/task $_->{entity}" }
- } @{$res->{data}}
- ]];
-
- reply_with($info, {inline_menu=>$menu}, @str);
- };
- async sub callback_task
- {
- my ($fsa, $info, $task_id) = @_;
- my $uid = $fsa->note("[uid]");
-
- my $res = await $client->get_p("task", "/task/$task_id?with_comments_for_client=1");
-
- my @str = map {
- format_timestamp($_->{created}) . " " . "<b>" . ($_->{created_by}->[0] eq "worker" ? _("Оператор") : _("Вы")) . ":</b>\n"
- . $_->{text}
- } @{ $res->{comments} };
-
- my $menu = [[
- {text => _("Добавить комментарий"), callback_data => "\x00comment $task_id"},
- ]];
-
- unless (@str)
- {
- $str[0] = "<i>Комментариев пока нет</i>";
- }
-
- reply_with($info, {inline_menu=>$menu}, @str);
- $client->post_p("task", "/task/$task_id/comment/read", {for_client=>$uid});
- }
- async sub callback_comment
- {
- my ($fsa, $info, $task_id) = @_;
-
- $fsa->note(task_id => $task_id);
- $fsa->state("task_needs_comment");
- reply($info, _("Введите текст вашего ответа"));
- }
- ##################################################
- async sub command_payberry
- {
- my ($fsa, $info) = @_;
- my $uid = $fsa->note("[uid]");
- my $menu = [[
- {text => _("Payberry"), url => $config->{pay}->{payberry_url} . "?acc=$uid"}
- ]];
-
- reply_with($info, {inline_menu=>$menu}, _("Для оплаты перейдите по ссылке"));
- }
- #####################################
- use constant FAIL_BLOCK => 10;
- use constant FAIL_ASK_SERIAL => 3;
- async sub command_card
- {
- my ($fsa, $info) = @_;
- my $uid = $fsa->note("[uid]");
-
- my $key = "card-guess-$uid";
- my $failed = $redis->get($key) || 0;
- if ($failed > FAIL_BLOCK)
- {
- return reply($info, _("Вы ввели неправильный код слишком много раз. Пополнение карточкой заблокировано на сутки"));
- }
-
- my $res = await $client->get_p("client", "/client/$uid");
- if ($res->{disabled})
- {
- return reply($info, _("Пополнение счета недоступно отключенным пользователям"));
- }
- reply($info, _("Введите код карточки пополнения (16 цифр, можно разделять их знаком '-')"));
- return needs_input($fsa, "card", "card_code");
- }
- sub verify_card_code($fsa, $code, $info)
- {
- $code =~ s/\D//g;
- return _("Код карточки должен состоять из 16 цифр") unless $code =~ /^\d{16}$/;
- return undef;
- }
- async sub use_card_code
- {
- my ($fsa, $code, $info) = @_;
- my $uid = $fsa->note("[uid]");
- my $key = "card-guess-$uid";
- my $fails = $redis->get($key) || 0;
-
- if ($fails > FAIL_ASK_SERIAL)
- {
- reply($info, _("Теперь введите номер карточки (7 цифр, можно разделять знаком'-')"));
- return needs_input($fsa, "card", "card_serial");
- }
- else
- {
- pay_from_card($info, $uid, $code, "");
- return "command";
- }
- }
-
- sub verify_card_serial($fsa, $serial, $info)
- {
- $serial =~ s/\D//g;
- return _("Номер карточки должен состоять из 7 цифр") unless $serial =~ /^\d{7}$/;
- return undef;
- }
- async sub use_card_serial
- {
- my ($fsa, $serial, $info) = @_;
- pay_from_card($info, $fsa->note("[uid]"), $fsa->note("card_code"), $serial);
- }
- async sub pay_from_card
- {
- my ($info, $uid, $code, $serial) = @_;
-
- my $args = {
- code => $code =~ s/\D//gr,
- uid => $uid,
- domain => $config->{domain},
- source => "abonbot",
- ip => "0.0.0.0",
- };
-
- $args->{serial} = $serial =~ s/\D//gr if $serial;
- my $res = eval { await $client->post_p("card", "/redemption", $args) };
- unless($@)
- {
- return reply_with($info, {button_menu=>1}, _("Ваш счёт пополнен на " . $res->{amount} . " " . $res->{currency_short_name_ru} . ". " . _("Оплачено включительно до ") . $res->{paid_until}));
- };
- given($@->{code})
- {
- when(400)
- {
- reply_with($info, {button_menu=>1}, _("Вы ввели неправильный номер или код карточки."), _("Пополнение прервано"));
- my $key = "card-guess-$uid";
- $redis->incr($key);
- $redis->expire($key, 24 * 3600);
- }
- when(409)
- {
- reply_with($info, {button_menu=>1}, _("Карточка уже использована."), _("Пополнение прервано"));
- }
- when(406)
- {
- reply_with($info, {button_menu=>1}, _("Карточка не активирована. Обратитесь в службу поддержки."), _("Пополнение прервано"));
- }
- default
- {
- die $@;
- }
- }
- };
- #####################################################
- sub command_support
- {
- my ($fsa, $info) = @_;
- return reply($info, _("Телефоны техподдержки:"), @{$config->{support_phones}});
- }
- ##################################################
- sub needs_input($fsa, $action, $var)
- {
- $fsa->delete_note($var);
- $fsa->note(input_var => $var);
- $fsa->note(input_action => $action);
-
- $fsa->state("needs_input");
- return "needs_input";
- }
- ##################################################
- sub format_wd($rec, $cur)
- {
- return _("бесплатно") if $rec->{dayly} == 0 && $rec->{monthly} == 0;
- my $m = sprintf("<b>%.2f $cur</b> %s", $rec->{monthly}, _("в месяц")) if $rec->{monthly} != 0;
- my $d = sprintf("<b>%.2f $cur</b> %s", $rec->{dayly}, _("в день")) if $rec->{dayly} != 0;
- return ("$m + $d") if $m && $d;
- return $m if $m;
- return $d if $d;
- }
- sub parse_error
- {
- my $e = shift;
- return $e unless ref $e;
-
- return "$e->{code} $e->{message} $e->{body}";
- }
- 1;
- # локализация
|