use Modern::Perl; use Data::Dumper; use Mojo::Base -strict, -signatures; use FSA::Rules; our $redis; our $config; our $client; our $commands; our $kb_menu; my %main = ( logged_out => { rules => [ logged_out => sub($state, $line, $chatid, $rest) { if ($line ne "/start") { notify($chatid, _("Для начала работы наберите /start"), $rest); return 1; } return undef; }, dummy => sub($state, $line, $chatid, $rest) { my $res = $client->get("client", "/telegram/$chatid/client"); if (my $err = $client->error) { if ($err->{code}==410) { reply($rest, _("Вас приветствует провайдер") . " " . $config->{provider}); reply($rest, _("Введите номер учетной записи или логин")); $state->result("ask_login"); } else { report($chatid, $err); $state->result("error"); } } else { $state->result("logined"); $state->notes(uid => $res->{uid}); } return undef; }, needs_login => sub($state, $line, $chatid, $rest) { return $state->result eq "ask_login"; }, logged_out => sub($state, $line, $chatid, $rest) { return $state->result eq "error"; }, command => 1, ], }, needs_login => { rules => [ needs_password => sub($state, $login, $chatid, $rest) { $state->notes(login => $login); reply($rest, _("Теперь введите пароль")); 1; }, ], }, needs_password => { rules => [ logged_out => sub($state, $password, $chatid, $rest) { my $login = $state->notes("login"); my $res = $client->post("client", "/telegram/client", {login=>$login, password=>$password, telegram_id=>$chatid}); $state->notes(login => undef); if (my $err = $client->error) { report($chatid, $err); return 1; } my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); my $greet = $hour>=4 && $hour<=10 ? _("Доброе утро") : $hour>10 && $hour<6 ? _("Добрый день") : _("Добрый вечер"); reply($rest, "$greet, " . $res->{fio}); $state->notes(uid => $res->{uid}); return undef; }, command => 1, ], }, command => { rules => [ command => sub($state, $line, $chatid, $rest) { do_command($line, $chatid, $rest); 1; } ], }, dummy => { }, ); our $fsa = FSA::Rules->new(%main); sub report($chatid, $err) { if ($err->{code}>=400 && $err->{code}<500 && ref $err->{body} eq "HASH") { notify($chatid, $err->{body}->{text_ru}); } else { my $code = int(rand(10000)); say STDERR "====== $code"; say STDERR Dumper $err; notify($chatid, _("Ошибка авторизации. Сообщите в службу технической поддержки код") . " $code"); } } ###################### sub save_fsa_state($id) { my $ks = make_key($id, "state"); my $kn = make_key($id, "notes"); $redis->set($ks, $fsa->curr_state->name); $redis->expire($ks, 3600*24); $fsa->notes(chatid => undef); say "saving state ", $fsa->curr_state->name, Dumper map { ($_, $fsa->notes->{$_}) } grep { defined $fsa->notes->{$_} } keys %{ $fsa->notes }; $redis->hmset($kn, map { ($_, $fsa->notes->{$_}) } grep { defined $fsa->notes->{$_} } keys %{ $fsa->notes }); $redis->expire($kn, 3600*24); } sub restore_fsa_state($chat, $from) { my $state = $redis->get(make_key($chat, "state")); set_fsa_state($state, $chat, $from); } sub set_fsa_state($state, $chat, $from) { if ($state) { my $notes = { $redis->hgetall(make_key($chat, "notes")) }; $fsa->notes($_ => $notes->{$_}) for keys %$notes; $fsa->notes(chatid => $chat); $fsa->curr_state($state); say "restore ", $state, Dumper make_key($chat, "notes"), $notes; } else { $fsa->reset; $fsa->notes(chatid => $chat); $fsa->notes(lang => $from->{language_code}); $fsa->start; } } sub process_input($line, $chatid, $rest) { say "*** current state = ", $fsa->curr_state->name; $fsa->switch($line, $chatid, $rest); if (defined (my $new_state = $fsa->notes("new_state"))) { say "*** mandatory switching to $new_state"; set_fsa_state($new_state, $chatid, $rest); $fsa->notes(new_state => undef); } say "*** Switched to ", $fsa->curr_state->name; } sub set_new_state($name) { $fsa->notes(new_state => $name); } 1;