use Modern::Perl; use utf8; use Mojo::Base -strict, -async_await, -signatures; use Data::Dumper; 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{1FA99}"}, {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{2764}\x{FE0F}\x{200D}\x{1FA79}"}, {command=>"support", name=>__("Техподдержка"), description=>__("Связь с техподдержкой")}, {command=>"logout", name=>__("Выход"), description=>__("Выход")}, ]; ############################################## sub command_help($fsa, $info) { my @list = map { "/$_->{command} " . _($_->{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($info, _("Благодарим за использование нашего бота")); $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("%s: %.2f $cur (%s %.2f $cur + %s %.2f $cur) ", _("Ваш баланс"), $money->{balance}, ("депозит"), $money->{deposit}, _("кредит"), $money->{credit}), ); push @lines, sprintf("%s: %s", _("Оплачено до"), format_date($money->{last_day})) if $money->{last_day} ne "-"; push @lines, sprintf("%s: %d%%", _("Скидка"), $money->{reduction}) if $money->{reduction}; push @lines, sprintf("%s: %.2f $cur %s (%s)", _("Последнее снятие"), $money->{last_withdrawal}->{sum}, format_time($money->{last_withdrawal}->{date}), $money->{last_withdrawal}->{comment}) if $money->{last_withdrawal}->{sum}; push @lines, sprintf("%s: %.2f $cur %s", _("Последний платеж"), $money->{last_payment}->{sum}, format_time($money->{last_payment}->{date})) if $money->{last_payment}->{sum}; for (keys %{ $money->{accounts} }) { push @lines, sprintf("%s: %.2f $cur", $_, $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("%s: %d", _("Номер учетной записи"), $client->{uid}), sprintf("%s: %s", _("Логин"), $client->{login}), sprintf("%s: %s", _("ФИО"), $client->{fio}), sprintf("%s: %s", _("Адрес"), $client->{address}), sprintf("%s: %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 %.2f %s", _("У вас уже установлен кредит"), $money->{credit}, $money->{human})); } reply_with($info, { inline_menu => [[ { text=>_("Я согласен с условиями"), callback_data=>"\x00/set-credit" }, ]], }, _("Вы можете самостоятельно установить кредит на два дня"), _("Ограничения: только для физических лиц, продлевать кредит повторно до оплаты нельзя. При следующей оплате кредит будет погашен"), ); } 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($info, sprintf("%s %.2f %s", _("Установлен кредит "), $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("%s: %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, _("Введите номер личного счета пользователя, которому вы хотите перевести деньги со своего собственного счета")); $fsa->delete_note("xfer_to"); $fsa->delete_note("xfer_amount"); $fsa->delete_note("xfer_fio"); return needs_input( fsa => $fsa, name => "xfer_to", item => _("номер личного счета"), process => _("перевод денег"), ); } 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 = await $abon_client->get_p($info, "client", "/client/$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)); } reply( $info, _("Получатель денег: ") . $nick, _("Теперь введите сумму, которую хотите перевести"), ); return needs_input( fsa => $fsa, name => "xfer_amount", item => _("сумму"), process => _("перевод денег"), ); } 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"); reply_with($info, { inline_menu => [[ { text=>_("Подтвердите перевод"), callback_data=>"\x00/transfer" }, ]] }, sprintf("%.2f %s %s %d", $amount, $config->{currency}->{human}, _(" на счет абонента"), $to_uid), ); return "command"; } async sub callback_transfer { my ($fsa, $info) = @_; my $uid = $fsa->note("uid"); my $to_uid = $fsa->delete_note("xfer_to"); my $amount = $fsa->delete_note("xfer_amount"); $fsa->delete_note("xfer_fio"); unless ($to_uid && $amount) { return reply($info, _("Произошла внутренняя ошибка")); } my $res = $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($info, _("Деньги успешно переведены")); 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 $params = { description => $fsa->note("task_text"), "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($info, "Заявка размещена"); $fsa->delete_note("task_text"); command_tasks($fsa, $info); } async sub callback_task_cancel { say 666; my ($fsa, $info) = @_; my $uid = $fsa->note("uid"); say "aaa", Dumper $fsa; reply($info, _("Заявка отменена")); } 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"); return reply($info, _("Открытых заявок нет")) unless $res->{total}; my @str = map { _("Заявка") . " $_->{number}\n$_->{description}\n" . _("Комментариев") . " " . $_->{total_comments} . ", " . _(" не прочитано") . " " . $_->{unread_comments} . "" } @{$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=1"); my @str = map { format_timestamp($_->{created}) . " " . "" . ($_->{created_by}->[0] eq "worker" ? _("Оператор") : _("Вы")) . ":\n" . $_->{text} } grep { $_->{visibleToUser} } @{ $res->{comments} }; my $menu = [[ {text => _("Добавить комментарий"), callback_data => "\x00comment $task_id"}, ]]; 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, _("Введите текст вашего ответа")); } ################################################## use constant FAIL_BLOCK => 10; 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}, _("Для оплаты перейдите по ссылке")); } 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 цифр, можно разделять их знаком '-')")); $fsa->delete_note("card_code"); $fsa->delete_note("card_serial"); $fsa->state("card_needs_code"); } sub command_support { my ($fsa, $info) = @_; return reply($info, _("Телефоны техподдержки:"), @{$config->{support_phones}}); } ################################################## sub needs_input(%args) { my $name = $args{name}; my $fsa = $args{fsa}; $fsa->delete_note($name); $fsa->note(input_name => $name); $fsa->note(input_again => _("Введите заново ") . $args{item} . _(" или нажмите Отмена, чтобы прервать ") . $args{process}); $fsa->note(input_process => $args{process}); $fsa->state("needs_input"); return "needs_input"; } sub cancel_input { my ($fsa, $info) = @_; $fsa->delete_note($fsa->delete_note("input_name")); $fsa->delete_note("input_again"); reply($info, ucfirst($fsa->delete_note("input_process")) . " " . _("отменён")); $fsa->state("command"); } ################################################## sub format_wd($rec, $cur) { return _("бесплатно") if $rec->{dayly} == 0 && $rec->{monthly} == 0; my $m = sprintf("%.2f $cur %s", $rec->{monthly}, _("в месяц")) if $rec->{monthly} != 0; my $d = sprintf("%.2f $cur %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; # локализация