| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 | 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, _("Для начала работы наберите <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, или команда установила уже свое состояние   },      #### Заявки      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;
 |