| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 | #!/usr/bin/perl# Бот-исполнитель запросов техподдержки# Ю. Жиловец, 11 января 2023 годаuse Modern::Perl;use utf8;use EV;use AnyEvent;use Mojolicious::Lite;use Mojo::UserAgent;use Data::Dumper;use Promises qw/deferred collect/;use Mojo::JSON qw/j/;use NetAddr::IP;use HTML::Restrict;use FindBin qw/$Bin/;use lib "$Bin/lib";use lib "$Bin/modules";use darsan_auth;use darsan_client;use commands;my $NAME = "djinn";my $confdir = "config/".app->mode;use rabbit_async_rec;plugin yaml_config => {  file      => "$confdir/$NAME.cfg",  stash_key => 'config',};our $config = app->config;app->secrets(["Marsz, Marsz, Dabrowski"]);my $log = new Mojo::Log;# https://core.telegram.org/bots/api#formatting-optionsmy $html_strip = HTML::Restrict->new(rules => {  b   => [],  strong => [],  i => [],  em => [],  u => [],  ins => [],  s => [],  strike => [],  del => [],  a => [qw/href/],  code => [qw/class/],  pre => [],});my $auth   = darsan_auth->as_server($config->{darsan}->{auth}, "system", "$confdir/system.private");our $client = darsan_client->new($auth, $config->{darsan}->{servers});my $term;my $int;my $hup;Mojo::IOLoop->next_tick(sub {    $term = AnyEvent->signal(signal => "TERM", cb => \&terminate);  $int = AnyEvent->signal(signal => "INT", cb => \&terminate);  $hup = AnyEvent->signal(signal => "HUP", cb => \&terminate);});                            ##########################my $ua = new Mojo::UserAgent;$ua->max_redirects(5);###########################=cuthook before_dispatch => sub{  my $c = shift;  say $c->req->to_string;};        hook after_dispatch => sub{  my $c = shift;  say $c->res->to_string;};#=cut###############################################get "/health" => sub{  shift->render(text => "Djinn OK");};post "/:token" => sub{  my $c = shift;  $c->render(text=>"ok");    unless ($c->param("token") eq $config->{token})  {    return $c->render(status=>401, text=>"Request from unknown URL");  }    my $body = j($c->req->body);  my $m = $body->{message} || $body->{edited_message};  my $chatid = $m->{chat}->{id};  if ($m->{chat}->{type} ne "supergroup")  {    return notify($chatid, "Общение с ботом возможно только в чате");  }    my $from = $m->{from};  my $cmd = $m->{text};  my $msgid = $m->{message_id};    return unless substr($cmd, 0, 1) eq "/"; # Бот не должен мешать общению, даже если его добавили админом    do_command($cmd, $chatid, {msgid=>$msgid, from=>$from});};##################################sub terminate{  request("setWebhook", {url=>""})->then(sub  {    exit(0);  })->catch(sub  {    $log->error(Dumper @_);  });}sub request{  my $action = shift;  my $params = shift;  my $deferred = deferred;  $ua->post("https://api.telegram.org/bot$config->{token}/$action" => form => $params => sub  {    my ($ua, $tx) = @_;    my $resp = $tx->result;             if ($resp->is_error)    {       my $err = {};       $err->{code} = $resp->code;       $err->{url} = $tx->req->url->to_string;       $err->{body} = $resp->body;       $err->{body} = j($err->{body}) if $resp->headers->content_type eq "application/json";       $deferred->reject($err);     }     else     {       my $body = $resp->body;       $body = j($body) if $resp->headers->content_type eq "application/json";       $deferred->resolve($body);     }   });    return $deferred->promise;}sub notify{  my $chatid = shift;  my $message = shift;  my $rest = shift || {};    my $params = {    chat_id => $chatid,    text => $message,    disable_web_page_preview => 1,  };    $params->{parse_mode} ||= "HTML";  $params->{reply_to_message_id} = $rest->{reply_to} if $rest->{reply_to};  $params->{disable_notification} = 1 if $rest->{silent};  my $disable_error_handler = delete $params->{disable_error_handler};    if ($params->{parse_mode} eq "HTML")  {    $params->{text} = $html_strip->process($params->{text});  }    my $promise = request("sendMessage", $params);  unless ($disable_error_handler)  {    $promise = $promise->catch(sub    {      $log->error(Dumper $params,@_);    });  }    return $promise;}sub do_command{  my $cmd = shift;  my $chatid = shift;  my $rest = shift;    local($Data::Dumper::Terse) = 1;    my ($c,@args) = split(/ /,$cmd);  $c =~ s|^/||;  $c =~ s/\@MolDjinnBot$//;  my $sub = reference("command_$c");  unless ($sub)  {    return notify($chatid, "Неизвестная команда. Введите <b>/help</b>, чтобы увидеть список команд", $rest);  }    eval {    $sub->($c, \@args, $chatid, $rest);  };    if ($@)  {    my $msg = ref $@ eq "HASH" ? Dumper($@) : $@;    $log->error("$cmd from $chatid: $msg");    notify($chatid, "Ошибка при выполнении команды $cmd: $msg");    return;  }}sub refpath{  my $name = shift;  $name =~ tr/.-/_/;  $name =~ s|/|::|g;  return reference($name);}        sub reference{  my $name = shift;  return exists(&{$name}) ? \&{$name} : undef;}######################################$log->info("Started (".app->mode.")");request("setWebhook",{url=>""})->then(sub{  $log->info("Webhook to $config->{webhook}");  return request("setWebhook", {url=>"$config->{webhook}/$config->{token}"});})->catch(sub{  $log->error(Dumper @_);});app->start;
 |