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; our $lex_vars; our $lex_actions; ################################################## my $rules = { logged_out => async sub { my ($fsa, $line, $info) = @_; if ($line ne "/start") { reply($info, _("Для начала работы наберите /start")); 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, или команда установила уже свое состояние }, #### Заявки task_needs_descr => async sub { my ($fsa, $descr, $info) = @_; my $uid = $fsa->note("[uid]"); $fsa->note(task_text => $descr); $fsa->note(if_edited => "$info->{msgid}/task_text"); 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, "Ваш ответ добавлен"); return "command"; }, ################################### needs_input => async sub { my ($fsa, $input, $info) = @_; my $uid = $fsa->note("[uid]"); $fsa->state("command"); # на случай die my $action = $fsa->note("input_action"); if ($input eq "\x00/cancel_input") { reply_with($info, {button_menu=>1}, ucfirst($lex_actions->{$action}->{name}) . " " . $lex_actions->{$action}->{canceled}); return "command"; } my $var = $fsa->note("input_var"); my $sub = refpath("verify_$var") || sub { undef }; my $error = $sub->($fsa, $input, $info); $error = await $error if ref $error && $error->can("then"); if ($error) { reply_with($info, { inline_menu => [[ { text=>_("Отмена"), callback_data=>"\x00/cancel_input" }, ]] }, $error, "", _("Введите заново ") . $lex_vars->{$var} . _(" или нажмите Отмена, чтобы прервать ") . $lex_actions->{$action}->{name}); return "needs_input"; } else { $fsa->note($var => $input); $sub = refpath("use_$var") || 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"; } ###################### 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;