Compare commits

...

2 commits

Author SHA1 Message Date
00967c7637 added Env::Util library to cpanfile 2025-01-17 01:31:48 +01:00
9837b3c5e8 # CsBot3 - Matrix Client
- Added CsBot3::Matrix library in src/lib
- Added dotenv support
- Added login, room joining, and event listening functions
2025-01-17 01:29:26 +01:00
6 changed files with 357 additions and 147 deletions

66
.gitignore vendored Normal file
View file

@ -0,0 +1,66 @@
# Created by https://www.toptal.com/developers/gitignore/api/perl,visualstudiocode,dotenv
# Edit at https://www.toptal.com/developers/gitignore?templates=perl,visualstudiocode,dotenv
### dotenv ###
.env
### Perl ###
!Build/
.last_cover_stats
/META.yml
/META.json
/MYMETA.*
*.o
*.pm.tdy
*.bs
# Devel::Cover
cover_db/
# Devel::NYTProf
nytprof.out
# Dist::Zilla
/.build/
# Module::Build
_build/
Build
Build.bat
# Module::Install
inc/
# ExtUtils::MakeMaker
/blib/
/_eumm/
/*.gz
/Makefile
/Makefile.old
/MANIFEST.bak
/pm_to_blib
/*.zip
# Carton
local/
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
# End of https://www.toptal.com/developers/gitignore/api/perl,visualstudiocode,dotenv

View file

@ -20,18 +20,5 @@
"lastfmaliases": {
"nicolapcweek94": "citizenwasp"
},
"modules": [
"autorejoin",
"trakt",
"lastfm",
"specials",
"dieroll",
"scp",
"mtg",
"emergency",
"isup",
"reddit",
"niggaradio",
"linktitles"
]
"modules": []
}

5
cpanfile Normal file
View file

@ -0,0 +1,5 @@
#cpanfile
requires 'Module::Reload';
requires 'ENV::Util';
requires 'JSON';

72
csbot3.pl Normal file
View file

@ -0,0 +1,72 @@
#/usr/bin/env perl
use strict;
use warnings;
use diagnostics;
use JSON;
use Module::Reload;
use threads;
use feature "say";
use Cwd 'abs_path';
use File::Basename;
use ENV::Util;
BEGIN {
ENV::Util::load_dotenv('.env');
my $script_dir = dirname(abs_path($0));
push @INC, "$script_dir/src/lib";
}
use CsBot3::Matrix;
# my $jsonconf = "";
# open(my $config, "<", "config.json");
# foreach (<$config>)
# {
# $jsonconf .= $_;
# }
# close $config;
# $config = decode_json $jsonconf;
# if ($@) {
# die("Error parsing JSON: $@");
# }
#
# my $modules = $config -> {"modules"};
#
# foreach (@$modules)
# {
# my $fullname = "csbot2::$_";
# eval "use modules::$_";
# die("Cannot load $_ : $@") if $@;
# #say $_;
# $fullname -> init();
# }
#
# $|++; # enable autoflushing
#
# my $irc;
# my $server = $config -> {"config"} -> {"server"};
# my $port = $config -> {"config"} -> {"port"};
# my $nick = $config -> {"config"} -> {"nick"};
# my $channels = $config -> {"config"} -> {"channels"};
# my $password = $config -> {"config"} -> {"password"};
# my $masters = $config -> {"config"} -> {"masters"};
# my $ignore = $config -> {"config"} -> {"ignore"};
# my $version = "0.2.6 now with slightly better module loading!";
my $matrix = CsBot3::Matrix::new($ENV{MATRIX_SERVER});
my %credentials = (
type => "m.login.password",
user => $ENV{MATRIX_USER},
password => $ENV{MATRIX_PASSWORD},
);
my $room = $ENV{MATRIX_DEFAULT_ROOM};
my $login = CsBot3::Matrix::login(\%credentials);
if($login) {
CsBot3::Matrix::join_room($room);
CsBot3::Matrix->read_events();
}

View file

@ -1,133 +0,0 @@
#/usr/bin/env perl
use strict;
use warnings;
use diagnostics;
use JSON;
use Module::Reload;
use threads;
use feature "say";
my $jsonconf = "";
open(my $config, "<", "config.json");
foreach (<$config>)
{
$jsonconf .= $_;
}
close $config;
$config = decode_json $jsonconf;
my $modules = $config -> {"modules"};
foreach (@$modules)
{
my $fullname = "csbot2::$_";
eval "use modules::$_";
die("Cannot load $_ : $@") if $@;
#say $_;
$fullname -> init();
}
$|++; # enable autoflushing
my $irc;
my $server = $config -> {"config"} -> {"server"};
my $port = $config -> {"config"} -> {"port"};
my $nick = $config -> {"config"} -> {"nick"};
my $channels = $config -> {"config"} -> {"channels"};
my $password = $config -> {"config"} -> {"password"};
my $masters = $config -> {"config"} -> {"masters"};
my $ignore = $config -> {"config"} -> {"ignore"};
my $version = "0.2.6 now with slightly better module loading!";
if ($config -> {"config"} -> {"ssl"} == 1)
{
eval "use IO::Socket::SSL";
$irc = IO::Socket::SSL -> new (
PeerAddr => $server,
PeerPort => $port,
Proto => "tcp"
) or die "Couldn't connect to IRC: $!";
}
else
{
eval "use IO::Socket::INET";
$irc = IO::Socket::INET -> new (
PeerAddr => $server,
PeerPort => $port,
Proto => "tcp"
) or die "Couldn't connect to IRC: $!";
}
my ($nick_s, $user_s, $host, $chan) = ("", "", "", "");
# USER non è nick!!
say $irc "USER ", $nick, " 0 * :CounterStrikeBot strikes again";
say $irc "NICK ", $nick;
while (<$irc>)
{
print;
($nick_s, $user_s, $host, $chan) = ($1, $2, $3, $4) if /^:([^\s]+)!~?([^\s]+)@([^\s]+) PRIVMSG ([^\s]+)/;
say $irc "QUIT :bb madafackas" if $nick_s ~~ $masters and /^[^\s]+ PRIVMSG ${chan} :gtfo.*\b${nick}\b/i;
say $irc "PRIVMSG $chan :$version" if /^:(.+?)!.+?@.+? PRIVMSG ${chan} :.*\b?${nick} version.*$/i;
say $irc "PONG :", $1 if /^PING :(.+)$/i;
next if $nick_s ~~ $ignore;
if (/^:[^\s]+ (?:422|376)/) {
say $irc "PRIVMSG NickServ :identify ", $password;
foreach my $ch (@$channels)
{
say $irc "JOIN ", $ch;
}
}
if (/^:(.+?)!.+?@.+? PRIVMSG ${chan} :moduleload (\w+?)\s+?$/i)
{
if ($nick_s ~~ $masters and not $2 ~~ $modules)
{
my $fullname = "csbot2::$2";
eval "use modules::$2; $fullname -> init()";
if ($@)
{
say $irc "PRIVMSG $chan :Cannot load $2 : $@";
}
else
{
push(@$modules, $2);
say $irc "PRIVMSG $chan :Module $2 successfully loaded";
}
}
else
{
say $irc "PRIVMSG $chan :You are not authorized to do that // The module is already loaded";
}
}
if (/^:(.+?)!.+?@.+? PRIVMSG ${chan} :moduleunload (\w+?)\s+?$/i)
{
if ($nick_s ~~ $masters and $2 ~~ $modules)
{
my $fullname = "csbot2::$2";
eval "no modules::$2; Module::Reload -> check;";
die("Cannot unload $2 : $@") if $@;
@$modules = grep {$_ !~ $2} @$modules;
say $irc "PRIVMSG $chan :Module $2 successfully unloaded";
}
}
say $irc "PRIVMSG ${chan} :Loaded modules: " . join(", ", @$modules) if /^:(.+?)!.+?@.+? PRIVMSG ${chan} :modulelist/i;
foreach my $name (@$modules)
{
my $mod = "csbot2::$name";
#threads->create (sub { $mod->parse ($_, $irc, $config, $chan, $nick); })->detach;
$mod->parse ($_, $irc, $config, $chan, $nick);
}
($nick_s, $user_s, $host) = ("", "", "");
}

213
src/lib/CsBot3/Matrix.pm Normal file
View file

@ -0,0 +1,213 @@
package CsBot3::Matrix;
use JSON qw(encode_json decode_json);
#use JSON::MaybeXS qw(encode_json decode_json);
use HTTP::Request;
use HTTP::Headers;
use LWP::UserAgent;
use URI;
our $user = "";
our $tnx_id = 0;
our $server;
our $access_token = "";
our %protocols_list = (
'http' => 'http://',
'https' => 'https://'
);
our $protocol = 'https';
our %routes = (
login => '/_matrix/client/r0/login',
join_room => '/_matrix/client/r0/join',
sync => '/_matrix/client/r0/sync',
room_send_message => '/_matrix/client/r0/rooms'
);
our $request_headers = ['Content-Type' => 'application/json; charset=UTF-8'];
our $lwp = LWP::UserAgent->new();
$lwp->agent('CsMatrix');
our @rooms = ();
sub new {
my ($server) = @_;
$CsBot3::Matrix::server = $server;
}
sub login {
my ($credentials) = @_;
$CsBot3::Matrix::user = $credentials->{user};
my $request_url = get_request_url('login');
my $body = encode_json($credentials);
my $login_request = HTTP::Request->new(
'POST',
$request_url,
$CsBot3::Matrix::request_headers,
$body
) or undef;
my $login_response = $CsBot3::Matrix::lwp->request($login_request) or undef;
if(!defined $login_response) {
return 0;
}
$CsBot3::Matrix::access_token = decode_json($login_response->{_content})->{access_token};
push(@$CsBot3::Matrix::request_headers, 'Authorization', "Bearer $CsBot3::Matrix::access_token");
print "[OK] - Login as $CsBot3::Matrix::user\n";
return 1;
}
sub join_room {
my ($room) = @_;
my $request_url = get_request_url('join_room');
$request_url .= "/$room";
$request_url .= "?access_token=$CsBot3::Matrix::access_token";
my $room_join_request = HTTP::Request->new(
'POST',
$request_url,
$CsBot3::Matrix::request_headers,
encode_json({})
) or undef;
my $room_join_response = $CsBot3::Matrix::lwp->request($room_join_request) or undef;
if(!defined $room_join_response) {
return 0;
}
push(@CsBot3::Matrix::rooms, $room);
print "[OK] - Join room $room\n";
return 1;
}
sub read_events {
my ($channel) = @_;
my $request_url = get_request_url('sync');
my $initial_sync_request = HTTP::Request->new(
'GET',
$request_url,
$CsBot3::Matrix::request_headers,
encode_json({})
) or undef;
my $initial_sync_response = $CsBot3::Matrix::lwp->request($initial_sync_request) or undef;
if(!defined $initial_sync_response) {
return 0;
}
my $json = JSON->new->utf8->allow_nonref->allow_blessed->convert_blessed->pretty(1);
my $content = decode_json($initial_sync_response->{_content})->{next_batch};
long_poll_event(next_batch);
return 1;
}
sub get_request_url {
my ($request_type) = @_;
my $proto = $protocols_list{$CsBot3::Matrix::protocol};
my $route = $CsBot3::Matrix::routes{$request_type};
if(defined $routes{$request_type}) {
return "$proto$server$route";
}
return undef;
}
sub long_poll_event {
my ($since) = @_;
my $timeout = 300;
my $request_url;
while (1) {
print "[SYNC] - Events\n";
$request_url = get_request_url('sync');
$request_url .= "?since=$since&timeout=$timeout";
$CsBot3::Matrix::lwp->timeout($timeout);
my $initial_sync_request = HTTP::Request->new(
'GET',
$request_url,
$CsBot3::Matrix::request_headers,
encode_json({})
) or undef;
my $initial_sync_response = $CsBot3::Matrix::lwp->request($initial_sync_request) or undef;
if ($initial_sync_response->is_success && $initial_sync_response->{_content}) {
my $json_response = decode_json($initial_sync_response->{_content});
my $next_batch = $json_response->{next_batch};
foreach my $room (@CsBot3::Matrix::rooms) {
if(defined $json_response->{rooms}->{join}->{$room}->{timeline}->{events}) {
$events = $json_response->{rooms}->{join}->{$room}->{timeline}->{events};
foreach my $event (@$events) {
if(
$event->{type} eq "m.room.message" &&
time() - $event->{origin_server_ts} <= 1000 &&
defined $CsBot3::Matrix::user &&
$event->{sender} ne $CsBot3::Matrix::user
) {
send_message($room);
}
}
}
}
$since = $next_batch;
}
}
return 1;
}
sub send_message {
my ($room) = @_;
my $request_url = get_request_url('room_send_message');
$request_url .= "/$room/send/m.room.message/$CsBot3::Matrix::tnx_id";
$CsBot3::Matrix::tnx_id += 1;
my $body = {
"msgtype" => "m.text",
"body" => "Message received."
};
my $room_send_message_request = HTTP::Request->new(
'PUT',
$request_url,
$CsBot3::Matrix::request_headers,
encode_json($body)
) or undef;
my $room_send_message_response = $CsBot3::Matrix::lwp->request($room_send_message_request) or undef;
if(!defined $room_send_message_response) {
return 0;
}
return 1;
}
1;