telegram.pl 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. #!/usr/bin/perl
  2. # Бот и рассыльщик сообщений для Телеграм
  3. # Ю. Жиловец, 6 ноября 2016 года
  4. # 257663755 - это я
  5. use Modern::Perl;
  6. use utf8;
  7. use EV;
  8. use AnyEvent;
  9. use Mojolicious::Lite;
  10. use Mojo::UserAgent;
  11. use Data::Dumper;
  12. use Promises qw/deferred/;
  13. use Mojo::JSON qw/j/;
  14. my $NAME = "telegram";
  15. my $confdir = "config/".app->mode;
  16. use FindBin qw/$Bin/;
  17. use lib "$Bin/lib";
  18. use rabbit_async;
  19. plugin yaml_config => {
  20. file => "$confdir/$NAME.cfg",
  21. stash_key => 'config',
  22. };
  23. our $config = app->config;
  24. app->secrets(["Marsz, Marsz, Dabrowski"]);
  25. my $log = new Mojo::Log(path=>$config->{log});
  26. my $term;
  27. my $int;
  28. my $hup;
  29. my $rabbit;
  30. Mojo::IOLoop->next_tick(sub
  31. {
  32. $term = AnyEvent->signal(signal => "TERM", cb => \&terminate);
  33. $int = AnyEvent->signal(signal => "INT", cb => \&terminate);
  34. $hup = AnyEvent->signal(signal => "HUP", cb => \&terminate);
  35. $rabbit = new rabbit_async($config->{rabbit}, sub
  36. {
  37. $rabbit->listen_queue($config->{queue}, $config->{bind}, \&incoming_message);
  38. });
  39. });
  40. ##########################
  41. my $ua = new Mojo::UserAgent;
  42. $ua->max_redirects(5);
  43. ##########################
  44. #=cut
  45. hook before_dispatch => sub
  46. {
  47. my $c = shift;
  48. say $c->req->to_string;
  49. };
  50. hook after_dispatch => sub
  51. {
  52. my $c = shift;
  53. say $c->res->to_string;
  54. };
  55. #=cut
  56. ##########################
  57. post "/:token" => sub
  58. {
  59. my $c = shift;
  60. unless ($c->param("token") eq $config->{token})
  61. {
  62. return $c->render(status=>401, text=>"Request from unknown URL");
  63. }
  64. my $body = j($c->req->body);
  65. my $m = $body->{message};
  66. my $chatid = $m->{chat}->{id};
  67. my $from = $m->{from}->{first_name} . " " . $m->{from}->{last_name} . " (" . $m->{from}->{username} . ")";
  68. my $cmd = $m->{text};
  69. my $msgid = $m->{message_id};
  70. $c->render(text=>"ok");
  71. do_command($cmd, $chatid, {msgid=>$msgid, from=>$from});
  72. };
  73. sub incoming_message
  74. {
  75. my $m = shift;
  76. my $body = $m->{content};
  77. $log->debug($m->{routing_key}." ".Dumper($m->{content})) if $config->{debug};
  78. my $rk = $m->{routing_key};
  79. $rk =~ s/\./::/g;
  80. my $sub = reference($rk);
  81. unless ($sub)
  82. {
  83. $log->error("Unknown message: ".$rk);
  84. $rabbit->reject($m);
  85. return;
  86. }
  87. eval {
  88. $sub->($body,$m);
  89. };
  90. if ($@)
  91. {
  92. $log->error($@);
  93. $rabbit->reject($m);
  94. }
  95. else
  96. {
  97. $rabbit->ack($m);
  98. $log->debug("acknowledged") if $config->{debug};
  99. }
  100. }
  101. sub notify::telegram::send
  102. {
  103. my $body = shift;
  104. notify($body->{to}, $body->{message});
  105. }
  106. ############################
  107. sub command::help
  108. {
  109. my $cmd = shift;
  110. my $args = shift;
  111. my $chatid = shift;
  112. my $rest = shift;
  113. notify($chatid,"*/my_id* Узнать свой идентификатор в Телеграме", $rest);
  114. }
  115. sub command::my_id
  116. {
  117. my $cmd = shift;
  118. my $args = shift;
  119. my $chatid = shift;
  120. my $rest = shift;
  121. notify($chatid,"Ваш идентификатор в Телеграме: *$chatid*\nУстановите его в личном кабинете", $rest);
  122. }
  123. #sub command::credit
  124. #sub command::balance
  125. ############################
  126. sub do_command
  127. {
  128. my $cmd = shift;
  129. my $chatid = shift;
  130. my $rest = shift;
  131. my ($c,@args) = split(/ /,$cmd);
  132. $c =~ s|^/||;
  133. my $sub = reference("command::$c");
  134. unless ($sub)
  135. {
  136. return notify($chatid, "Неизвестная команда. Введите */help*, чтобы увидеть список команд", $rest);
  137. }
  138. eval {
  139. $sub->($c, \@args, $chatid, $rest);
  140. };
  141. if ($@)
  142. {
  143. $log->error("$cmd from $chatid [$rest->{from}]: $@");
  144. notify($chatid, "Ошибка при выполнении команды", $rest);
  145. return;
  146. }
  147. }
  148. sub terminate
  149. {
  150. request("setWebhook",{url=>""})->then(sub
  151. {
  152. exit(0);
  153. })->catch(sub
  154. {
  155. say Dumper @_;
  156. });
  157. }
  158. sub request
  159. {
  160. my $action = shift;
  161. my $params = shift;
  162. my $deferred = deferred;
  163. $ua->post("https://api.telegram.org/bot$config->{token}/$action" => form => $params => sub
  164. {
  165. my ($ua, $tx) = @_;
  166. my $resp = $tx->success;
  167. unless ($resp)
  168. {
  169. my $err = $tx->error;
  170. $err->{code} ||= 500;
  171. $err->{url} = $tx->req->url->to_string;
  172. $err->{body} = $tx->res->body;
  173. $err->{body} = j($err->{body}) if $tx->res->headers->content_type eq "application/json";
  174. $deferred->reject($err);
  175. }
  176. else
  177. {
  178. my $body = $resp->body;
  179. $body = j($body) if $resp->headers->content_type eq "application/json";
  180. $deferred->resolve($body);
  181. }
  182. });
  183. return $deferred->promise;
  184. }
  185. sub notify
  186. {
  187. my $chatid = shift;
  188. my $message = shift;
  189. my $rest = shift || {};
  190. my $params = {
  191. chat_id => $chatid,
  192. text => $message,
  193. parse_mode => "Markdown",
  194. };
  195. $params->{reply_to_message_id} = $rest->{msgid} if $rest->{msgid};
  196. return request("sendMessage", $params);
  197. }
  198. # убирать его при окончании работы
  199. # команды
  200. sub refpath
  201. {
  202. my $name = shift;
  203. $name =~ tr/.-/_/;
  204. $name =~ s|/|::|g;
  205. return reference($name);
  206. }
  207. sub reference
  208. {
  209. my $name = shift;
  210. return exists(&{$name}) ? \&{$name} : undef;
  211. }
  212. ##################################
  213. $log->info("Started (".app->mode.")");
  214. request("setWebhook",{url=>""})->then(sub
  215. {
  216. $log->info("Webhook to $config->{webhook}");
  217. return request("setWebhook",{url=>"$config->{webhook}/$config->{token}"});
  218. })->catch(sub
  219. {
  220. $log->error(Dumper @_);
  221. });
  222. app->start;