rules.pm 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. use Modern::Perl;
  2. use Data::Dumper;
  3. use Mojo::Base -strict, -signatures, -async_await;
  4. use Mojo::Util;
  5. use Scalar::Util;
  6. use experimental qw/switch/;
  7. use fsa;
  8. our $redis;
  9. our $config;
  10. our $client;
  11. our $commands;
  12. our $abon_client;
  13. our $lex_vars;
  14. our $lex_actions;
  15. ##################################################
  16. my $rules = {
  17. logged_out => async sub
  18. {
  19. my ($fsa, $line, $info) = @_;
  20. if ($line ne "/start")
  21. {
  22. reply_with($info, { button_menu => 0 }, _("Для начала работы наберите <b>/start</b>"));
  23. return "logged_out";
  24. }
  25. # Поступила команда /start
  26. # У нас есть uid в автомате? Это значит, что мы прочитали состояние из внешней базы
  27. if ($fsa->note("[uid]"))
  28. {
  29. reply_with($info, { button_menu => 1 }, _("Для получения списка доступных команд введите /help"));
  30. return "command";
  31. }
  32. # Проверяем, логинился ли уже пользователь в нашем боте
  33. my $res = eval { await $client->get_p("client", "/telegram/$info->{id}/client") };
  34. if ($@)
  35. {
  36. if ($@->{code} == 410)
  37. {
  38. reply($info,
  39. _("Вас приветствует провайдер") . " " . $config->{provider},
  40. _("Введите номер учетной записи или логин")
  41. );
  42. return "needs_login";
  43. }
  44. else
  45. {
  46. die $@;
  47. }
  48. }
  49. reply_with($info, { button_menu => 1 }, greet($res->{fio}), _("Для получения списка доступных команд введите /help"));
  50. $fsa->note("[uid]" => $res->{uid});
  51. "command";
  52. },
  53. needs_login => sub($fsa, $line, $info)
  54. {
  55. unless ($line =~ /^[\w_]+$/)
  56. {
  57. reply($info, _("Неизвестный логин"), _("Для начала работы введите команду /start"));
  58. return "logged_out";
  59. }
  60. $fsa->note(login => $line);
  61. reply($info, _("Теперь введите пароль"));
  62. "needs_password";
  63. },
  64. needs_password => async sub
  65. {
  66. my ($fsa, $line, $info) = @_;
  67. request("deleteMessage", { chat_id=>$info->{id}, message_id=>$info->{msgid} });
  68. my $res = eval { await $client->post_p("client", "/telegram/client", {login=>$fsa->note("login"), password=>$line, telegram_id=>$info->{id}}) };
  69. if ($@)
  70. {
  71. $fsa->state("logged_out");
  72. $fsa->delete_note("login");
  73. die $@;
  74. }
  75. reply_with($info, { button_menu => 1 }, greet($res->{fio}), _("Для получения списка доступных команд введите /help"));
  76. $fsa->note("[uid]" => $res->{uid});
  77. $fsa->delete_note("login");
  78. "command";
  79. },
  80. command => async sub
  81. {
  82. my ($fsa, $line, $info) = @_;
  83. await do_command($fsa, $line, $info);
  84. $fsa->state; # или все тот же command, или команда установила уже свое состояние
  85. },
  86. #### Заявки
  87. task_needs_descr => async sub
  88. {
  89. my ($fsa, $descr, $info) = @_;
  90. my $uid = $fsa->note("[uid]");
  91. $fsa->note(task_text => $descr);
  92. $fsa->note(if_edited => "$info->{msgid}/task_text");
  93. reply_with($info, {
  94. inline_menu => [[
  95. { text=>_("Отправить"), callback_data=>"\x00/task_post" },
  96. { text=>_("Отменить"), callback_data=>"\x00/task_cancel" },
  97. ]]
  98. },
  99. _("Перед отправкой заявки вы можете ее отредактировать средствами Телеграм")
  100. );
  101. return "command";
  102. },
  103. task_needs_comment => async sub
  104. {
  105. my ($fsa, $comment, $info) = @_;
  106. my $uid = $fsa->note("[uid]");
  107. my $task_id = $fsa->note("task_id");
  108. my $params = {
  109. text => $comment,
  110. "for-client" => $uid,
  111. };
  112. $fsa->state("command"); # на случай die
  113. my $res = await $client->post_p("task", "/task/$task_id/comment", $params);
  114. reply($info, "Ваш ответ добавлен");
  115. return "command";
  116. },
  117. ###################################
  118. needs_input => async sub
  119. {
  120. my ($fsa, $input, $info) = @_;
  121. my $uid = $fsa->note("[uid]");
  122. $fsa->state("command"); # на случай die
  123. my $action = $fsa->note("input_action");
  124. if ($input eq "\x00/cancel_input")
  125. {
  126. reply_with($info, {button_menu=>1}, ucfirst($lex_actions->{$action}->{name}) . " " . $lex_actions->{$action}->{canceled});
  127. return "command";
  128. }
  129. my $var = $fsa->note("input_var");
  130. my $sub = refpath("verify_$var") || sub { undef };
  131. my $error = $sub->($fsa, $input, $info);
  132. $error = await $error if ref $error && $error->can("then");
  133. if ($error)
  134. {
  135. reply_with($info, {
  136. inline_menu => [[
  137. { text=>_("Отмена"), callback_data=>"\x00/cancel_input" },
  138. ]]
  139. }, $error, "", _("Введите заново ") . $lex_vars->{$var} . _(" или нажмите Отмена, чтобы прервать ") . $lex_actions->{$action}->{name});
  140. return "needs_input";
  141. }
  142. else
  143. {
  144. $fsa->note($var => $input);
  145. $sub = refpath("use_$var") || sub { "command" };
  146. my $state = await $sub->($fsa, $input, $info);
  147. return $state;
  148. }
  149. },
  150. };
  151. sub greet($whom)
  152. {
  153. my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
  154. my $greet = $hour>=4 && $hour<=10 ? _("Доброе утро") : $hour>10 && $hour<19 ? _("Добрый день") : _("Добрый вечер");
  155. "$greet, $whom";
  156. }
  157. ######################
  158. sub make_key($id)
  159. {
  160. return "abonbot-$id";
  161. }
  162. sub save_fsa($fsa, $chatid)
  163. {
  164. my $key = make_key($chatid);
  165. say "saving state ", $fsa->state, Dumper $fsa->notes;
  166. my $notes = $fsa->notes;
  167. utf8::encode($notes->{$_}) for keys %$notes;
  168. $redis->del($key);
  169. $redis->hmset($key, %$notes);
  170. $redis->expire($key, 3600*24);
  171. }
  172. sub make_fsa($chatid, $from)
  173. {
  174. my $id = make_key($chatid);
  175. my $notes = { $redis->hgetall($id) };
  176. if (keys %$notes)
  177. {
  178. utf8::decode($notes->{$_}) for keys %$notes;
  179. return fsa->new($rules, $notes->{"[state]"}, $notes);
  180. }
  181. else
  182. {
  183. return fsa->new($rules, "logged_out", {});
  184. }
  185. }
  186. 1;