diff --git a/src/notification/telegram/mode/alert.pm b/src/notification/telegram/mode/alert.pm index 16c863a153..3c167c79a2 100644 --- a/src/notification/telegram/mode/alert.pm +++ b/src/notification/telegram/mode/alert.pm @@ -1,5 +1,5 @@ # -# Copyright 2024 Centreon (http://www.centreon.com/) +# Copyright 2026-Now Centreon (http://www.centreon.com/) # # Centreon is a full-fledged industry-strength solution that meets # the needs in IT infrastructure and application monitoring for @@ -26,6 +26,7 @@ use strict; use warnings; use centreon::plugins::http; use JSON::XS; +use URI::Encode; my %telegram_icon_host = ( up => "\x{2705}", @@ -61,6 +62,8 @@ sub new { "link-url:s" => { name => 'link_url' }, "centreon-url:s" => { name => 'centreon_url' }, "centreon-token:s" => { name => 'centreon_token' }, + "action-links" => { name => 'action_links' }, + "legacy" => { name => 'legacy' }, "timeout:s" => { name => 'timeout' } }); @@ -84,6 +87,12 @@ sub check_options { $self->{output}->add_option_msg(short_msg => "You need to specify --host-name option."); $self->{output}->option_exit(); } + if (defined($self->{option_results}->{action_links})) { + if (!defined($self->{option_results}->{centreon_url}) || $self->{option_results}->{centreon_url} eq '') { + $self->{output}->add_option_msg(short_msg => "Please set --centreon-url option when using --action-links"); + $self->{output}->option_exit(); + } + } foreach (('graph_url', 'link_url')) { if (defined($self->{option_results}->{$_})) { @@ -94,8 +103,131 @@ sub check_options { $self->{http}->set_options(%{$self->{option_results}}); } +# Build the URL path pointing to the new Resources Status page (/monitoring/resources), +# with a pre-filled JSON filter for the host and/or service. +sub build_resource_status_filters { + my ($self, %options) = @_; + + my $data_format = URI::Encode->new({ encode_reserved => 1 }); + + my $raw_resource_status_filters = { + "id" => "", + "name" => "New+filter", + "criterias" => [ + { + "name" => "resource_types", + "object_type" => undef, + "type" => "multi_select", + "value" => [ + { + "id" => "service", + "name" => "Service" + } + ] + }, + { + "name" => "states", + "object_type" => undef, + "type" => "multi_select", + "value" => [] + }, + { + "name" => "statuses", + "object_type" => undef, + "type" => "multi_select", + "value" => [] + }, + { + "name" => "status_types", + "object_type" => undef, + "type" => "multi_select", + "value" => [] + }, + { + "name" => "host_groups", + "object_type" => "host_groups", + "type" => "multi_select", + "value" => [] + }, + { + "name" => "service_groups", + "object_type" => "service_groups", + "type" => "multi_select", + "value" => [] + }, + { + "name" => "monitoring_servers", + "object_type" => "monitoring_servers", + "type" => "multi_select", + "value" => [] + }, + { + "name" => "search", + "object_type" => undef, + "type" => "text", + "value" => sprintf( + 's.description:%s h.name:%s', + defined($self->{option_results}->{service_description}) ? $self->{option_results}->{service_description} : '', + defined($self->{option_results}->{host_name}) ? $self->{option_results}->{host_name} : '' + ) + }, + { + "name" => "sort", + "object_type" => undef, + "type" => "array", + "value" => [ "status_severity_code", "asc" ] + } + ] + }; + + my $link_url_path = '/monitoring/resources?filter='; + my $encoded_filters = JSON::XS->new->utf8->encode($raw_resource_status_filters); + my $encoded_data_for_uri = $data_format->encode($encoded_filters); + $link_url_path .= $encoded_data_for_uri; + + return $link_url_path; +} + +sub build_action_links { + my ($self, %options) = @_; + + return unless defined($self->{option_results}->{action_links}); + + my $resource_type = (defined($self->{option_results}->{service_description}) + && $self->{option_results}->{service_description} ne '') + ? 'service' : 'host'; + + my $uri = URI::Encode->new({ encode_reserved => 0 }); + my $link_url_path; + + if (defined($self->{option_results}->{legacy})) { + # Legacy: redirect to deprecated Centreon monitoring pages + $link_url_path = '/main.php?p=2020'; + if ($resource_type eq 'service') { + $link_url_path .= '1&o=svc&host_search=' . $self->{option_results}->{host_name} + . '&search=' . $self->{option_results}->{service_description}; + } else { + $link_url_path .= '2&o=svc&host_search=' . $self->{option_results}->{host_name}; + } + } else { + # Default: redirect to the new Resources Status page + $link_url_path = $self->build_resource_status_filters(); + } + + my $link_uri_encoded = $uri->encode($self->{option_results}->{centreon_url}) . $link_url_path; + $self->{action_link_url} = $link_uri_encoded; + + # Graph link (service only) : always points to the performance graph page + if ($resource_type eq 'service') { + my $graph_url_path = '/main.php?p=204&mode=0&svc_id=' + . $self->{option_results}->{host_name} . ';' + . $self->{option_results}->{service_description}; + $self->{action_graph_url} = $uri->encode($self->{option_results}->{centreon_url} . $graph_url_path); + } +} + sub host_message { - my ($self, %options) = @_; + my ($self, %options) = @_; if (defined($self->{option_results}->{host_state}) && $self->{option_results}->{host_state} ne '') { if (defined($telegram_icon_host{lc($self->{option_results}->{host_state})})) { @@ -113,7 +245,11 @@ sub host_message { if (defined($self->{option_results}->{host_output}) && $self->{option_results}->{host_output} ne '') { $self->{message} .= "\n " . $self->{option_results}->{host_output}; } - if (defined($self->{option_results}->{link_url}) && $self->{option_results}->{link_url} ne '') { + + # --action-links takes priority over the manual --link-url + if (defined($self->{action_link_url}) && $self->{action_link_url} ne '') { + $self->{message} .= "\n {action_link_url} . "\">Link"; + } elsif (defined($self->{option_results}->{link_url}) && $self->{option_results}->{link_url} ne '') { $self->{message} .= "\n {option_results}->{link_url} . "\">Link"; } } @@ -135,12 +271,19 @@ sub service_message { $self->{message} .= ' alert'; } if (defined($self->{option_results}->{service_output}) && $self->{option_results}->{service_output} ne '') { - $self->{message} .= "\n ". $self->{option_results}->{service_output}; + $self->{message} .= "\n " . $self->{option_results}->{service_output}; } - if (defined($self->{option_results}->{link_url}) && $self->{option_results}->{link_url} ne '') { + + # --action-links takes priority over the manual --link-url / --graph-url + if (defined($self->{action_link_url}) && $self->{action_link_url} ne '') { + $self->{message} .= "\n {action_link_url} . "\">Link"; + } elsif (defined($self->{option_results}->{link_url}) && $self->{option_results}->{link_url} ne '') { $self->{message} .= "\n {option_results}->{link_url} . "\">Link"; } - if (defined($self->{option_results}->{graph_url}) && $self->{option_results}->{graph_url} ne '') { + + if (defined($self->{action_graph_url}) && $self->{action_graph_url} ne '') { + $self->{message} .= "\n {action_graph_url} . "\">Graph"; + } elsif (defined($self->{option_results}->{graph_url}) && $self->{option_results}->{graph_url} ne '') { $self->{message} .= "\n {option_results}->{graph_url} . "\">Graph"; } } @@ -148,6 +291,9 @@ sub service_message { sub set_payload { my ($self, %options) = @_; + # Build action links (Resources Status or legacy pages) before composing the message + $self->build_action_links(); + if (defined($self->{option_results}->{service_description}) && $self->{option_results}->{service_description} ne '') { $self->service_message(); } else { @@ -161,9 +307,9 @@ sub format_payload { my $json = JSON::XS->new->utf8; my $payload = { - chat_id =>$self->{option_results}->{chat_id}, + chat_id => $self->{option_results}->{chat_id}, parse_mode => 'HTML', - text => $self->{message} + text => $self->{message} }; eval { $self->{payload_str} = $json->encode($payload); @@ -174,20 +320,20 @@ sub format_payload { } } - sub run { my ($self, %options) = @_; $self->{http}->add_header(key => 'Content-Type', value => 'application/json'); - $self->{http}->add_header(key => 'Accept', value => 'application/json'); + $self->{http}->add_header(key => 'Accept', value => 'application/json'); $self->set_payload(); $self->format_payload(); my $url_path = '/bot' . $self->{option_results}->{bot_token} . $self->{option_results}->{url_path}; my $response = $self->{http}->request( - url_path => $url_path, - method => 'POST', query_form_post => $self->{payload_str} + url_path => $url_path, + method => 'POST', + query_form_post => $self->{payload_str} ); my $decoded; @@ -229,6 +375,10 @@ Use Telegram CLI for getting Chat ID Telegram Bot Token (Check Telegram Doc for Creating Bot) https://core.telegram.org/bots#3-how-do-i-create-a-bot +=item B<--host-name> + +Specify host server name for the alert (required). + =item B<--host-state> Specify host server state for the alert. @@ -237,10 +387,6 @@ Specify host server state for the alert. Specify host server output message for the alert. -=item B<--host-name> - -Specify host server name for the alert (required). - =item B<--service-description> Specify service description name for the alert. @@ -253,21 +399,51 @@ Specify service state for the alert. Specify service output message for the alert. +=item B<--action-links> + +Only to be used with Centreon. + +Automatically generate and add links to the notification message pointing to the +Centreon Resources Status page (C) with a pre-filled filter +for the notified host/service. + +Requires C<--centreon-url> to be set. + +When combined with C<--legacy>, links will point to the deprecated monitoring pages +(C) instead. + =item B<--centreon-url> -Specify the centreon url macro (could be used in link-url and graph-url option). +Specify the Centreon interface URL (to be used with C<--action-links>). + +Syntax: C<--centreon-url='https://mycentreon.mydomain.local/centreon'> + +=item B<--legacy> + +Only to be used with Centreon together with C<--action-links>. + +Redirect to the deprecated Centreon resource status pages (C / +C) instead of the new Resources Status page. =item B<--centreon-token> -Specify the centreon token for autologin macro (could be used in link-url and graph-url option). +Specify the centreon token for auto-login macro (could be used in link-url and graph-url option). =item B<--graph-url> -Specify the graph url (example: %{centreon_url}/include/views/graphs/generateGraphs/generateImage.php?username=myuser&token=%{centreon_token}&hostname=%{host_name}&service=%{service_description}). +Specify a custom graph url. + +Example: C<%{centreon_url}/include/views/graphs/generateGraphs/generateImage.php?username=myuser&token=%{centreon_token}&hostname=%{host_name}&service=%{service_description}> + +Ignored when C<--action-links> is set (the graph link is then built automatically). =item B<--link-url> -Specify the link url (example: %{centreon_url}/main.php?p=20201&o=svc&host_search=%{host_name}&svc_search=%{service_description}) +Specify a custom link url. + +Example: C<%{centreon_url}/monitoring/resources> + +Ignored when C<--action-links> is set (the link is then built automatically). =item B<--timeout> @@ -275,4 +451,4 @@ Threshold for HTTP timeout. =back -=cut +=cut \ No newline at end of file