فهرست منبع

Merge pull request #22 from Troglodyne-Internet-Widgets/ICP-21

Icp 21
Andy Baugh 5 سال پیش
والد
کامیت
64c989e4cf

+ 1 - 0
LICENSE-IMAGES.md

@@ -3,3 +3,4 @@ Adding this file here, as some of the imagery used in the Schema modules were no
 * IRC: Released under the [Creative Commons Attribution-Share Alike 3.0 Unported license](https://github.com/fabianalexisinostroza/Antu/blob/master/LICENSE "CC-SA 3.0 Unported"). [Source](https://commons.wikimedia.org/wiki/File:Antu_irc.svg "Wikimedia")
 * Slack: Released under the terms of [Slack's Branding Guidelines](https://slack.com/brand-guidelines "Slack's Branding Guidelines"). [Source](https://brandfolder.com/slack "Slack Icons @ BrandFolder")
 * Discord: Released under the terms of [Discord's Branding Guidelines](https://discordapp.com/branding "Discord's Branding Guidelines"). [Source](https://discordapp.com/assets/f8389ca1a741a115313bede9ac02e2c0.svg)
+* Telegram: Released under the [GNU General Public License, v3](https://github.com/zhukov/webogram/blob/master/LICENSE). [Source](https://github.com/zhukov/webogram/blob/master/app/img/icons/icon.svg)

+ 4 - 1
Makefile

@@ -43,7 +43,10 @@ depend-irc:
 depend-xmpp:
 	perl -MNet::XMPP -MMozilla::CA -e 'exit 0;' || sudo cpan -i Net::XMPP Mozilla::CA
 
-depend-all: depend-xmpp depend-irc
+depend-telegram:
+	perl -MWWW::Telegram::BotAPI -e 'exit 0;' || sudo cpan -i WWW::Telegram::BotAPI
+
+depend-all: depend-xmpp depend-irc depend-telegram
 
 depend-test:
 	perl -MTest::More -MTest::Fatal -MTest::MockModule -MTest::Deep -MConfig::Simple -MHTTP::Tiny::UA -MHTTP::Tiny::UA::Response -e 'exit 0;' || sudo cpan -i Test::More Test::Fatal Test::MockModule Test::Deep Config::Simple HTTP::Tiny::UA HTTP::Tiny::UA::Response

+ 142 - 0
lib/Cpanel/iContact/Provider/Schema/Telegram.pm

@@ -0,0 +1,142 @@
+package Cpanel::iContact::Provider::Schema::Telegram;
+
+use strict;
+use warnings;
+
+# Name is always uc(MODULE)
+
+=encoding utf-8
+
+=head1 NAME
+
+Cpanel::iContact::Provider::Schema::Telegram - Schema for the Telegram iContact module
+
+=head1 SYNOPSIS
+
+    use Cpanel::iContact::Provider::Schema::Telegram;
+
+    my $settings = Cpanel::iContact::Provider::Schema::Telegram::get_settings();
+
+    my $config = Cpanel::iContact::Provider::Schema::Telegram::get_config();
+
+
+=head1 DESCRIPTION
+
+Provide settings and configuration for the HipChat iContact module.
+
+=cut
+
+=head2 get_settings
+
+Provide config data for TweakSettings that will be saved in
+/etc/wwwacct.conf.shadow
+
+=over 2
+
+=item Input
+
+=over 3
+
+None
+
+=back
+
+=item Output
+
+=over 3
+
+A hashref that can be injected into Whostmgr::TweakSettings::Basic's %Conf
+with the additional help and label keys that are used in the display of the
+tweak settings.
+
+=back
+
+=back
+
+=cut
+
+sub get_settings {
+    my $help = <<HALP;
+<strong>Telegram Bot Token:</strong><br>
+Token created for authenticating as the bot you created for sending cPanel & WHM Notifications to users in Telegram.
+<br>In order to create a token for your bot, please message the <a href="https://core.telegram.org/bots#6-botfather">BotFather</a> in Telegram and it will make one for you.
+HALP
+    my $halp = <<HELP;
+<strong>Telegram Destinations:</strong><br>
+The group(s)/user(s) you wish your Telegram bot to notify, separated by comma.<br>
+Public chat rooms can be mentioned by their '\@' name, but individual users, groups or private chat rooms <i>must</i> be referenced by numeric ID.<br>
+Getting these numeric IDs can be a rather involved process, so I have created a video tutorial for doing so <a href="https://troglodyne.net">here</a>.<br><br>
+<i>Example</i>: \@bogusPublicChannel,123456789
+HELP
+    return {
+        'CONTACTTELEGRAM' => {
+            'name'     => 'Telegram Destinations',
+            'shadow'   => 0,
+            'type'     => 'text',
+            'checkval' => sub {
+                my $value = shift();
+                $value =~ s/^\s+|\s+$//g; # Trim
+
+                return $value if $value eq q{};
+
+                return $value;
+            },
+            'label' => 'Telegram Destinations',
+            'help'  => $halp,
+        },
+        'TELEGRAMBOTTOKEN' => {
+            'name'     => 'Telegram Bot Token',
+            'label'    => 'Telegram Bot Token',
+            'help'     => $help,
+            'shadow'   => 1,
+            'type'     => 'password',
+            'checkval' => sub {
+                my $value = shift();
+                $value =~ s/^\s+|\s+$//g; # Trim
+
+                return $value if $value eq q{};
+                return $value;
+            },
+        },
+    };
+}
+
+=head2 get_config
+
+Provide configuration for the module.
+
+=over 2
+
+=item Input
+
+=over 3
+
+None
+
+=back
+
+=item Output
+
+=over 3
+
+A hash ref containing the following key values:
+
+  default_level:    The iContact default contact level (All)
+  display_name:     The name displayed on the Contact Manager page in WHM.
+
+=back
+
+=back
+
+=cut
+
+sub get_config {
+    return {
+        'default_level' => 'All',
+        'display_name'  => 'Telegram',
+        'icon' =>
+          'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22620%22%20height%3D%22620%22%20viewBox%3D%220%200%20620%20620%22%3E%3ClinearGradient%20id%3D%22a%22%20gradientUnits%3D%22userSpaceOnUse%22%20x1%3D%22311.167%22%20y1%3D%22603.333%22%20x2%3D%22311.167%22%20y2%3D%2219%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%231D93D2%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%2338B0E3%22%2F%3E%3C%2FlinearGradient%3E%3Ccircle%20fill%3D%22url%28%23a%29%22%20stroke%3D%22%23FFF%22%20stroke-width%3D%2228%22%20stroke-miterlimit%3D%2210%22%20cx%3D%22311.167%22%20cy%3D%22311.167%22%20r%3D%22292.167%22%2F%3E%3Cpath%20fill%3D%22%23C8DAEA%22%20d%3D%22M220.76%20338.848l35.362%2097.88s4.42%209.157%209.157%209.157%2075.146-73.252%2075.146-73.252l78.304-151.24-196.707%2092.197-1.264%2025.258z%22%2F%3E%3Cpath%20fill%3D%22%23A9C6D8%22%20d%3D%22M267.646%20363.95l-6.788%2072.146s-2.842%2022.102%2019.26%200%2043.257-39.152%2043.257-39.152%22%2F%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M221.398%20342.34l-72.734-23.7s-8.683-3.526-5.894-11.525c.575-1.65%201.736-3.052%205.21-5.473%2016.12-11.234%20298.324-112.667%20298.324-112.667s7.97-2.683%2012.677-.898c2.153.816%203.527%201.737%204.685%205.104.42%201.226.663%203.83.63%206.42-.022%201.87-.252%203.6-.42%206.316-1.72%2027.732-53.145%20234.705-53.145%20234.705s-3.076%2012.113-14.103%2012.525c-4.02.15-8.9-.664-14.734-5.683-21.635-18.612-96.42-68.87-112.944-79.924-.93-.622-1.198-1.433-1.355-2.222-.233-1.165%201.034-2.61%201.034-2.61s130.217-115.75%20133.68-127.895c.268-.94-.74-1.405-2.105-1-8.65%203.183-158.58%2097.858-175.126%20108.313-.968.61-3.682.217-3.682.217z%22%2F%3E%3C%2Fsvg%3E',
+    };
+}
+
+1;

+ 124 - 0
lib/Cpanel/iContact/Provider/Telegram.pm

@@ -0,0 +1,124 @@
+package Cpanel::iContact::Provider::Telegram;
+
+use strict;
+use warnings;
+
+use parent 'Cpanel::iContact::Provider';
+
+use Try::Tiny;
+
+=encoding utf-8
+
+=head1 NAME
+
+Cpanel::iContact::Provider::Telegram - Backend for the Telegram iContact module
+
+=head1 SYNOPSIS
+
+    use Cpanel::iContact::Provider::Telegram;
+
+    my $notifier = Cpanel::iContact::Provider::Telegram->new();
+    $notifier->send();
+
+
+=head1 DESCRIPTION
+
+Provide backend accessor for the Telegram iContact module.
+
+=cut
+
+=head2 send
+
+Sends off the notification over to your Telegram channel/user
+
+=over 2
+
+=item Input
+
+=over 3
+
+None
+
+=back
+
+=item Output
+
+=over 3
+
+Truthy value on success, exception on failure.
+
+=back
+
+=back
+
+=cut
+
+sub send {
+    my ($self) = @_;
+
+    my $args_hr    = $self->{'args'};
+    my $contact_hr = $self->{'contact'};
+
+    my @missing = grep { !defined $self->{'contact'}{$_} } qw{TELEGRAMBOTTOKEN};
+    die "Kit not complete! Missing: " . join( ", ", @missing ) if scalar( @missing );
+
+    my @errs;
+
+    # Telegram max message length is 4096 chars.
+    # As such , truncate at 4092, add ellipsis (3 chars).
+    # Why not 4093? I want to avoid fencepost errors.
+    # Also, mojibake worries... oof
+    require Encode;
+    my $subject      = Encode::decode_utf8( $args_hr->{'subject'}, $Encode::FB_QUIET );
+    my $body         = Encode::decode_utf8( ${$args_hr->{'text_body'}}, $Encode::FB_QUIET );
+    my $message = substr( "$subject\n$body", 0, 4092 );
+    $message .= '...' if length $message == 4092;
+
+    # Disgusting, but whatever. We are about to have some fun here boyos
+    # First, gotta load our libs
+    # Second, the mojo that comes with cP is titanic.
+    # Mojo you install from cpan won't work with cP binaries
+    # Disaster all around.
+    # Get around it by forcing the module into LWP mode, lol
+    push @INC, '/usr/local/share/perl5';
+    require WWW::Telegram::BotAPI;
+    my $api = WWW::Telegram::BotAPI->new(
+        token     => $self->{'contact'}{'TELEGRAMBOTTOKEN'},
+        force_lwp => 1,
+    );
+
+    # Test the auth. Will die if it fails.
+    $api->getMe();
+
+
+    # Send it
+    foreach my $destination ( @{ $args_hr->{'to'} } ) {
+        try {
+            $api->sendMessage({
+                'chat_id'    => $destination,
+                'text'       => $message,
+            });
+        }
+        catch {
+            require Cpanel::Exception;
+            push(
+                @errs,
+                Cpanel::Exception::create(
+                    'ConnectionFailed',
+                    'The system failed to send the message to “[_1]” due to an error: [_2]',
+                    [ $destination, $_ ]
+                )
+            );
+        };
+    }
+
+    if (@errs) {
+
+        # Module should already be loaded above
+        die Cpanel::Exception::create( 'Collection', [ exceptions => \@errs ] );
+    }
+
+    return 1;
+}
+
+1;

+ 30 - 0
t/Cpanel-iContact-Provider-Schema-Telegram.t

@@ -0,0 +1,30 @@
+use strict;
+use warnings;
+
+use Cwd qw{abs_path};
+use File::Basename qw{dirname};
+use lib abs_path( dirname(__FILE__) . "/../lib" );
+
+use Test::More;
+use Test::Deep;
+
+use Cpanel::iContact::Provider::Schema::Telegram ();
+
+plan tests => 2;
+
+subtest "Settings getter method performs as expected" => sub {
+    my $model = [ 'CONTACTTELEGRAM', 'TELEGRAMBOTTOKEN' ];
+    my $settings = Cpanel::iContact::Provider::Schema::Telegram::get_settings();
+    is_deeply( [ sort keys( %{$settings} ) ], $model, "Settings returned look OK so far" );
+    foreach my $key (@$model) {
+        is(
+            $settings->{$key}{'checkval'}->('123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11'),
+            '123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11', "Valid values passed to checkval subroutine of '$key' assumed GreatSuccess"
+        ) if ref $settings->{$key}{'checkval'} eq 'CODE';
+    }
+    is( $settings->{'CONTACTTELEGRAM'}{'checkval'}->('    '), '', "Invalid values passed to checkval subroutine of 'CONTACTTELEGRAM' silently got smashed" );
+    is( eval { $settings->{'TELEGRAMBOTTOKEN'}{'checkval'}->(' NeenerNeenerNotAToken') }, undef, "Invalid values passed to checkval subroutine of 'CONTACTTELEGRAM' engaged Maximum NO" );
+};
+
+my $config_model = { 'default_level' => 'All', 'display_name' => 'Telegram', 'icon' => ignore() };
+cmp_deeply( Cpanel::iContact::Provider::Schema::Telegram::get_config(), $config_model, "Config getter method return looks OK" );

+ 61 - 0
t/Cpanel-iContact-Provider-Telegram.t

@@ -0,0 +1,61 @@
+use strict;
+use warnings;
+
+use Cwd qw{abs_path};
+use File::Basename qw{dirname};
+use lib abs_path( dirname(__FILE__) . "/../lib" );
+
+use Test::More;
+use Test::Fatal;
+use Test::MockModule ();
+use Config::Simple   ();
+
+use Cpanel::HTTP::Client              ();
+use Cpanel::HTTP::Client::Response    ();
+use Cpanel::iContact::Provider::Telegram ();
+
+plan tests => 2;
+
+# First, let's mock out the parent, and other stuff we wouldn't wanna do in a unit test
+subtest "Provider bits work as expected ('unit' test)" => sub {
+    my $text_scalar = 'lol, jk';
+    my $send_args   = { 'subject' => "[test.host.tld] YOUR COMIC BOOKS ARE DYING!!!1", 'text_body' => \$text_scalar, 'to' => [ 'SalinasPunishmentRoom', '@cPSaurus' ] };
+    my $contact_cfg = { 'TELEGRAMBOTTOKEN' => '420SWAGYOLO69696969' };
+    my $ua_mocker = Test::MockModule->new("WWW::Telegram::BotAPI");
+
+    # Mock has to be used instead of redefine due to AUTOLOAD
+    $ua_mocker->mock( 'getMe' => sub {}, 'sendMessage' => sub {} );
+
+    isa_ok( my $spammer = Cpanel::iContact::Provider::Telegram->new(), "Cpanel::iContact::Provider::Telegram" );
+    $spammer->{'contact'} = $contact_cfg;
+    is( exception { $spammer->send() }, undef, "send doesn't throw on GreatSuccess" );
+    $ua_mocker->mock( 'getMe' => sub { die "401 Unauthorized" } );
+    isnt( exception { $spammer->send() }, undef, "send throws whenever anything goes wrong" );
+};
+
+subtest "Can send a message to somewhere (systems level/integration test)" => sub {
+    SKIP: {
+        my $conf_file = abs_path( dirname(__FILE__) . "/../.telegramtestrc" );
+        skip "Skipping functional testing, needful not supplied", 1 if !$ENV{'AUTHOR_TESTS'} || !-f $conf_file;
+        my $test_conf = Config::Simple->import_from($conf_file);
+        my $text_body = "This is a test of Cpanel::iContact::Provider::Telegram. Please Ignore";
+        my %args = (
+            'to'        => [ $test_conf->param('CONTACTTELEGRAM') ],
+            'subject'   => 'My Super cool test notification',
+            'text_body' => \$text_body,
+        );
+
+        {
+            no warnings qw{redefine once};
+            local *Cpanel::iContact::Provider::Telegram::new = sub {
+                return bless {
+                    'contact' => { 'TELEGRAMBOTTOKEN' => $test_conf->param('TELEGRAMBOTTOKEN') },
+                    'args'    => \%args,
+                }, $_[0];
+            };
+            my $spammer = Cpanel::iContact::Provider::Telegram->new();
+            my $ex;
+            is( $ex = exception { $spammer->send() }, undef, "Didn't fail to send notification using full functional test" ) || diag explain $ex;
+        }
+    }
+};