commit 0e385f7bdb53b8717bc0d29d160876d78efaf47d Author: Nicola Zangrandi Date: Thu Feb 8 09:10:16 2024 +0100 Split from monorepo diff --git a/README.md b/README.md new file mode 100644 index 0000000..f8a7fa0 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# csbot2 + +Every end is followed by a beginning. This is something new. This is _csbot2_. + +Every info you need is in the `docs` folder: +- `csbot2.md` explains the basics of csbot2; +- `config.md` explains the configuration file and how to use it; +- `modules.md` explains the module "API" and how single modules work. \ No newline at end of file diff --git a/config.json b/config.json new file mode 100644 index 0000000..f80df08 --- /dev/null +++ b/config.json @@ -0,0 +1,37 @@ +{ + "config": { + "server": "server", + "port": 6667, + "ssl": 0, + "channel": "#chan", + "nick": "csbot2", + "password": "pass", + "masters": ["nicolapcweek94"], + "randomchars": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,.;:-_+*^@#[]{}()=?/%$£!|\\", + "quitline": "6 > 4 > 2 > 5 > 1 > 3" + }, + "apikeys": { + "trakt": "trakt API key", + "lastfm": "lastfm API key" + }, + "traktaliases": { + "nicolapcweek94": "nicolapcweek94" + }, + "lastfmaliases": { + "nicolapcweek94": "citizenwasp" + }, + "modules": [ + "autorejoin", + "trakt", + "lastfm", + "specials", + "dieroll", + "scp", + "mtg", + "emergency", + "isup", + "reddit", + "niggaradio", + "linktitles" + ] +} diff --git a/docs/config.md b/docs/config.md new file mode 100644 index 0000000..ad7e582 --- /dev/null +++ b/docs/config.md @@ -0,0 +1,30 @@ +#csbot2 docs +###configuration file + +The configuration file is the most important part of csbot2, as without it nothing works. It's designed to be as simple as possible to use from code and to write by hand. + +All the configuration is contained in a json file (`config.json`) placed in the same folder as the main `csbot2.pl` file. It contains a main hash that is then separated in 5 parts: the main configuration (hash, it contains basic options for the main module), the API keys section (hash, it contains the API keys needed to access online services from the various modules), traktaliases (hash, it contains aliases in the form "irc username": "trakt username"), lastfmaliases (hash, works exactly like traktaliases) and the modules array, containing csbot2 modules to be loaded. + +###1: main configuration +The main configuration hash contains basic informations like the server/channel to connect to, the nick to use and so on. They are: +- "server": the hostname/ip of the irc server; +- "port": the port to connect to, most server run raw tcp on 6667 and SSL on 6697; +- "ssl": 0 if the connection is raw tcp/1 if SSL is to be used; +- "channel": the channel to connect to; +- "nick": the nickname to use; +- "password": The NickServ password to use (if the nickname is registered); +- "masters": array, contains the nicknames of "master" users allowed to run important commands; +- "randomchars": not used as of 22/01/2014; +- "quitline": not used as of 22/01/2014; + +###2: api keys +This section contains the varius API keys for online services used by modules. Right now it contains just the keys for Last.fm and Trakt. + +###3: traktaliases +This section contains aliases to be used in the trakt module. They need to be in the form "irc user": "trakt user". + +###4: lastfmaliases +This section contains aliases to be used in the lastfm module. They work exactly like the ones used in the trakt module. + +###5: modules +This section contains an array listing modules to be loaded when the bot starts up. They need to be placed in the `modules` subfolder placed in the same folder as the main `csbot2.pl` file to be correctly recognized by perl. diff --git a/docs/csbot2.md b/docs/csbot2.md new file mode 100644 index 0000000..5b23e4f --- /dev/null +++ b/docs/csbot2.md @@ -0,0 +1,24 @@ +#csbot2 docs +###general introduction + +Table of contents: +1 General introduction +2 Csbot2's code explaination +3 Dependencies list + +###1: General introduction +csbot2 is beta software. You should use it accordingly and not come to me if it breaks your server (?). + +Welcome to csbot2's docs! This really short series of documents will help you understand how csbot2 works and how to write modules for it. It's a pretty simple process and modules are basically normal perl modules, free to do whatever they want. You'll get more info about it in the `modules.md` file, that explains how a module is built, what data it gets from the main module and what to do with it. It also lists the currently working modules and their dependencies. + +The `config.md` file explains the configuration file and how you should use it to customize your instance of csbot2. It's a really simple configuration process and, being written in JSON, it's easy to write and modify. Hope you like it. + +This file explains how the main module works and how to modify it if you ever need to. It also lists the dependencies you need for the main bot code to work. + +###2: Csbot2's code explaination +The first thing csbot does, of course after loading modules, is opening the config file and parsing it. This means that _the config file is required_. It needs to be named `config.json` and placed in the same folder as `csbot2.pl`. After the succesful loading of the config file it decodes the JSON in a hash variable and then proceeds to load the modules listed in the configuration, failing horribly if they don't exist or contain errors. If that works, it goes on to load the rest of the config into variables and then opens a TCP socket connecting to the specified server:port, again failing horribly if that doesn't work. Once the socket is open and working, csbot2 sends its nickname and USER information to the server and then enters the main loop, where it parses new lines received on the socket and does stuff with them (responds to PINGs, QUITs if a master asks him to, authenticates with nickserv if needed and so on). It then calls the loaded modules and waits for them to do stuff. + +That's it. Was it so hard to understand? Hope not! + +###3: Dependencies list +The main module of csbot actually requires almost no dependencies, leaving all that shit to modules. It requires perl (a decently updated version, as it uses the `say` function to avoid printing newlines as they are the bane of my existence), some modules from the standard library (`strict`, `warnings`, `diagnostics` and `IO::Socket::INET`/`IO::Socket::SSL`) and just one module from CPAN, `JSON`. It's easily installed, just run `cpan JSON` and you're done. diff --git a/docs/modules.md b/docs/modules.md new file mode 100644 index 0000000..17b65a6 --- /dev/null +++ b/docs/modules.md @@ -0,0 +1,32 @@ +#csbot2 docs +###modules API + +Csbot2 was thought to be extremely modular, which is basically the reason I switched to perl in the first place. This means it includes a very basic "API", which allows modules to get some data from the main bot module (the socket, data from the user ho sent the message and so on). Modules needs to be in the csbot2::modulename package and they only need to implement two functions: `parse` (executed for every line written on the channel) and `init` (executed just once, when the module is loaded). The parse function gets passed 6 parameters from the main loop: +- The main bot instance +- The message to be parsed +- The socket to write on +- The configuration hash +- The channel csbot2 is in +- The nick csbot2 is using + +What you do with those 6 parameters is up to you. The standard module stucture is to use ifs to parse the `$line` variable with regexes and do stuff with it. + +You can find an example module in the `barebones.pm` module. + +###Modules list +This is a list of the modules included in this repo as of 23/01/2014 and their dependencies: + +- `autorejoin.pm`: This module joins the channel csbot2 was in if it gets kicked. It has no special dependencies. +- `barebones.pm`: This module is included as an example and not used by csbot2. No special dependencies. +- `dieroll.pm`: This module rolls a die and prints the result. No dependencies. +- `emergency.pm`: This module insults users when called. No special dependencies. +- `isup.pm`: This module checks if a website is up based on the HTTP response it receives. It needs `LWP::UserAgent` to work. +- `lastfm.pm`: This module is used as a Last.fm interface. It needs `JSON`, `LWP::UserAgent` and an API key defined in the config file. +- `linktitles.pm`: This module prints the `` tag of links posted in the channel. It needs `LWP::UserAgent` and `HTML::Entities` to work. +- `mtg.pm`: This module connects to [magiccards.info](http://magicards.info) and searches for specific cards. It needs `LWP::UserAgent` to work. +- `niggaradio.pm`: This module is kept as an example since the service it connected to no longer exists. It uses the `LWP::UserAgent` module. +- `reddit.pm`: This module links to subreddits mentioned in the channel. No dependencies required. +- `rss.pm`: This module prints the first n elements of an RSS feed. It needs `XML::FeedPP` to work. +- `scp.pm`: This module waits for a SCP to be mentioned and then fetches its name and link from the scp wiki. It needs `LWP::UserAgent` and `HTML::Entities` to work. +- `specials.pm`: This module does special functions useful only to me, basically. No dependencies. +- `trakt.pm`: This module is used as a Trakt interface. It needs `JSON`, `LWP::UserAgent`, `DateTime` and an API key defined in the config file. diff --git a/src/csbot2.pl b/src/csbot2.pl new file mode 100644 index 0000000..ed151b2 --- /dev/null +++ b/src/csbot2.pl @@ -0,0 +1,133 @@ +#/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) = ("", "", ""); +} diff --git a/src/modules/4chan.pm b/src/modules/4chan.pm new file mode 100644 index 0000000..9a76b28 --- /dev/null +++ b/src/modules/4chan.pm @@ -0,0 +1,38 @@ +package csbot2::4chan; +use strict; +use warnings; +use LWP::UserAgent; +use JSON; +use feature "say"; + +sub init +{ + say "[~] 4chan module init"; +} + +sub parse +{ + my ($self, $line, $irc, $config, $channel, $nick) = @_; + if (/^:(.+?)!.+?@.+? PRIVMSG ${channel} :.*(https?:\/\/.+?4chan\.org(\S*)).*$/i) + { + my $response = LWP::UserAgent -> new -> get("https://a.4cdn.org$3.json"); + if ($response -> is_success) + { + my $json = $response -> decoded_content; + my $data = decode_json $json; + my $subject = $data -> {"posts"}[0] -> {"sub"}; + $subject = "No subject" if not $subject; + my $replies = $data -> {"posts"}[0] -> {"replies"}; + $replies = 0 if not $replies =~ /\d+/; + my $images = $data -> {"posts"}[0] -> {"images"}; + $images = 0 if not $images =~ /\d+/; + say $irc "PRIVMSG $channel :$subject, with $replies replies and $images images."; + } + else + { + say $irc "PRIVMSG $channel :You fuckhole really should give me valid URLs."; + } + } + return 0; +} +1; diff --git a/src/modules/autorejoin.pm b/src/modules/autorejoin.pm new file mode 100644 index 0000000..705a67a --- /dev/null +++ b/src/modules/autorejoin.pm @@ -0,0 +1,17 @@ +package csbot2::autorejoin; +use strict; +use warnings; +use feature "say"; + +sub init +{ + say "[~] autorejoin module init"; +} + +sub parse +{ + my ($self, $line, $irc, $config, $channel, $nick) = @_; + say $irc "JOIN $channel" if $line =~ /^:.+?!.+?@.+? KICK $channel $nick.*$/i; + return 0; +} +1; diff --git a/src/modules/barebones.pm b/src/modules/barebones.pm new file mode 100644 index 0000000..83736cc --- /dev/null +++ b/src/modules/barebones.pm @@ -0,0 +1,20 @@ +package csbot2::barebones; +use strict; +use warnings; +use feature "say"; + +sub init +{ + say "[~] barebones module init"; +} + +sub parse +{ + my ($self, $line, $irc, $config, $channel, $nick) = @_; + if (/^:(.+?)!.+?@.+? PRIVMSG ${channel} :command (.+?)$/i) + { + say $irc "PRIVMSG $channel :output."; + } + return 0; +} +1; diff --git a/src/modules/dieroll.pm b/src/modules/dieroll.pm new file mode 100644 index 0000000..82c06fa --- /dev/null +++ b/src/modules/dieroll.pm @@ -0,0 +1,25 @@ +package csbot2::dieroll; +use strict; +use warnings; +use feature "say"; + +sub init +{ + say "[~] dieroll module init"; +} + +sub parse +{ + my ($self, $line, $irc, $config, $channel, $nick) = @_; + if ($line =~ /^:(.+?)!.+?@.+? PRIVMSG ${channel} :roll (\d+)d(\d+)\s*$/i) + { + my $total = 0; + foreach (1..$2) + { + $total += int(rand($3)); + } + say $irc "PRIVMSG $channel :$total"; + } + return 0; +} +1; diff --git a/src/modules/emergency.pm b/src/modules/emergency.pm new file mode 100644 index 0000000..2a45ca0 --- /dev/null +++ b/src/modules/emergency.pm @@ -0,0 +1,20 @@ +package csbot2::emergency; +use strict; +use warnings; +use feature "say"; + +sub init +{ + say "[~] emergency module init"; +} + +sub parse +{ + my ($self, $line, $irc, $config, $channel, $nick) = @_; + if (/^:(.+?)!.+?@.+? PRIVMSG ${channel} :emergency (.+?)$/i) + { + say $irc "PRIVMSG $channel :$2: https://www.youtube.com/watch?v=8Q7FFjUpVLg"; + } + return 0; +} +1; diff --git a/src/modules/isup.pm b/src/modules/isup.pm new file mode 100644 index 0000000..771dd9a --- /dev/null +++ b/src/modules/isup.pm @@ -0,0 +1,29 @@ +package csbot2::isup; +use strict; +use warnings; +use LWP::UserAgent; +use feature "say"; + +sub init +{ + say "[~] isup module init"; +} + +sub parse +{ + my ($self, $line, $irc, $config, $channel, $nick) = @_; + if (/^:(.+?)!.+?@.+? PRIVMSG ${channel} :isup (https?:\/\/(www\.)?\S+\.\w{2,6}\S+?)\s?$/i) + { + my $res = LWP::UserAgent -> new -> get($2); + if ($res -> is_success) + { + say $irc "PRIVMSG $channel :$2 is UP."; + } + else + { + say $irc "PRIVMSG $channel :$2 is DOWN. (hint: the url must be absolute. http://www.google.com works, google.com doesn't.)"; + } + } + return 0; +} +1; diff --git a/src/modules/lastfm.pm b/src/modules/lastfm.pm new file mode 100644 index 0000000..8bc619d --- /dev/null +++ b/src/modules/lastfm.pm @@ -0,0 +1,97 @@ +package csbot2::lastfm; +use strict; +use warnings; +use JSON; +use LWP::UserAgent; +use feature "say"; + +sub init +{ + say "[~] lastfm module init"; +} + +sub parse +{ + my ($self, $line, $irc, $config, $channel, $nick) = @_; + + if (/^:(.+?)!.+?@.+? PRIVMSG ${channel} :lastfm stats (.+?)$/i) + { + lastfm_stats($2, $config -> {"apikeys"} -> {"lastfm"}, $irc, $channel); + return; + } + + if (/^:(.+?)!.+?@.+? PRIVMSG ${channel} :.* ?lastfm stats?.*$/i) + { + lastfm_stats($config -> {"lastfmaliases"} -> {$1}, $config -> {"apikeys"} -> {"lastfm"}, $irc, $channel); + return; + } + + if (/^:(.+?)!.+?@.+? PRIVMSG ${channel} :lastfm (.+?)$/i) + { + lastfm($2, $config -> {"apikeys"} -> {"lastfm"}, $irc, $channel); + return; + } + + if (/^:(.+?)!.+?@.+? PRIVMSG ${channel} :.* ?lastfm ?.*$/i) + { + lastfm($config -> {"lastfmaliases"} -> {$1}, $config -> {"apikeys"} -> {"lastfm"}, $irc, $channel); + return; + } + return 0; +} + +sub lastfm_stats +{ + my ($user, $apikey, $irc, $channel) = @_; + my $json = LWP::UserAgent -> new -> get("http://ws.audioscrobbler.com/2.0/?method=user.getinfo&api_key=" . $apikey . "&format=json&user=" . $user) -> decoded_content; + my $dati = decode_json $json; + + if ($dati -> {"message"} eq "No user with that name was found") + { + say $irc "PRIVMSG $channel :You don't want to end up dead right? So stahp before it's too late."; + return; + } + else + { + my $name = $dati -> {"user"} -> {"name"}; + my $playcount = $dati -> {"user"} -> {"playcount"}; + + $json = LWP::UserAgent -> new -> get("http://ws.audioscrobbler.com/2.0/?method=user.getbannedtracks&api_key=" . $apikey . "&format=json&user=" . $name) -> decoded_content; + $dati = decode_json $json; + my $bannedtracks = $dati -> {"bannedtracks"} -> {"\@attr"} -> {"total"}; + $bannedtracks = 0 if not $bannedtracks =~ /\d+/; + + $json = LWP::UserAgent -> new -> get("http://ws.audioscrobbler.com/2.0/?method=user.getlovedtracks&api_key=" . $apikey . "&format=json&user=" . $name) -> decoded_content; + $dati = decode_json $json; + my $lovedtracks = $dati -> {"lovedtracks"} -> {"\@attr"} -> {"total"}; + $lovedtracks = 0 if not $lovedtracks =~ /\d+/; + + $json = LWP::UserAgent -> new -> get("http://ws.audioscrobbler.com/2.0/?method=user.gettoptracks&api_key=" . $apikey . "&format=json&user=" . $name) -> decoded_content; + $dati = decode_json $json; + my $toptrackname = $dati -> {"toptracks"} -> {"track"}[0] -> {"name"}; + my $toptrackcount = $dati -> {"toptracks"} -> {"track"}[0] -> {"playcount"}; + my $toptrackartist = $dati -> {"toptracks"} -> {"track"}[0] -> {"artist"} -> {"name"}; + + say $irc "PRIVMSG $channel :" . $name . " has listened to \x02" . $playcount . "\x02 songs. He banned \x02" . $bannedtracks . "\x02 tracks and loved \x02" . $lovedtracks . "\x02."; + say $irc "PRIVMSG $channel :His top track is \x02" . $toptrackname . "\x02 by " . $toptrackartist . ". He listened to it " . $toptrackcount . " times."; + } + return; +} + +sub lastfm +{ + my ($user, $apikey, $irc, $channel) = @_; + my $json = LWP::UserAgent -> new -> get("http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&api_key=" . $apikey . "&format=json&user=" . $user) -> decoded_content; + my $dati = decode_json $json; + if ($dati -> {"message"} eq "No user with that name was found") + { + say $irc "PRIVMSG $channel :You don't want to end up dead right? So stahp before it's too late."; + } + else + { + say $irc "PRIVMSG $channel :", $dati -> {"recenttracks"} -> {"\@attr"} -> {"user"} , " last listened to \x02", $dati -> {"recenttracks"} -> {"track"}[0] -> {"name"}, "\x02 by ", $dati -> {"recenttracks"} -> {"track"}[0] -> {"artist"} -> {"#text"}, " in ", $dati -> {"recenttracks"} -> {"track"}[0] -> {"album"} -> {"#text"}, "."; + } + return; +} + +1; diff --git a/src/modules/linktitles.pm b/src/modules/linktitles.pm new file mode 100644 index 0000000..9236842 --- /dev/null +++ b/src/modules/linktitles.pm @@ -0,0 +1,31 @@ +package csbot2::linktitles; +use strict; +use warnings; +use LWP::UserAgent; +use HTML::Entities; +use feature "say"; + +sub init +{ + say "[~] linktitles module init"; +} + +sub parse +{ + my ($self, $line, $irc, $config, $channel, $nick) = @_; + if (/^:(.+?)!.+?@.+? PRIVMSG ${channel} :.*(https?:\/\/(www\.)?\S+\.\w{2,6}\S+)\b?.*$/i) + { + if ($1 ne $nick) + { + my $res = LWP::UserAgent -> new() -> get($2); + if ($res -> is_success) + { + my $html = $res -> decoded_content; + my $title = $1 if $html =~ /<title>(.*)<\/title>/i; + say $irc "PRIVMSG $channel :" . decode_entities($title); + } + } + } + return 0; +} +1; diff --git a/src/modules/mtg.pm b/src/modules/mtg.pm new file mode 100644 index 0000000..5d41d3f --- /dev/null +++ b/src/modules/mtg.pm @@ -0,0 +1,31 @@ +package csbot2::mtg; +use strict; +use warnings; +use LWP::UserAgent; +use feature "say"; + +sub init +{ + say "[~] mtg module init"; +} + +sub parse +{ + my ($self, $line, $irc, $config, $channel, $nick) = @_; + if (/^:(.+?)!.+?@.+? PRIVMSG ${channel} :mtg (.+?)$/i || /^:(.+?)!.+?@.+? PRIVMSG ${channel} :.+?\[\[(.+?)\]\].+?/i) + { + my $cardname = $2; + $cardname =~ s/\s/\+/g; + my $html = LWP::UserAgent -> new -> get("http://magiccards.info/query?q=$cardname&v=card&s=cname") -> decoded_content; + if ($html =~ /<img\ssrc="(http:\/\/magiccards.info\/scans\/\S+?\/\S+?\/\d+.jpg)"\s+alt="(.+?)" width="312" height="445" style="border: 1px solid black;">/i) + { + say $irc "PRIVMSG $channel :$2: $1"; + } + else + { + say $irc "PRIVMSG $channel :Card not found."; + } + } + return 0; +} +1; diff --git a/src/modules/niggaradio.pm b/src/modules/niggaradio.pm new file mode 100644 index 0000000..4d01334 --- /dev/null +++ b/src/modules/niggaradio.pm @@ -0,0 +1,32 @@ +package csbot2::niggaradio; +use strict; +use warnings; +use LWP::UserAgent; +use feature "say"; + +sub init +{ + say "[~] niggaradio module init"; +} + +sub parse +{ + my ($self, $line, $irc, $config, $channel, $nick) = @_; + if (/^:(.+?)!.+?@.+? PRIVMSG ${channel} :.*\bniggaradio\b.*$/i) + { + my $xml = LWP::UserAgent -> new -> get("http://niggazwithattitu.de:9000/status2.xsl"); + if ($xml -> is_success) + { + my $res = $xml -> decoded_content; + my $song = $1 if $res =~ /<fullname>(.*)<\/fullname>/i; + my $listeners = $1 if $res =~ /<listeners>(.*)<\/listeners>/i; + say $irc "PRIVMSG $channel :Niggaradio is currently playing $song with $listeners internetfags listening."; + } + else + { + say $irc "PRIVMSG $channel :Wut? something wrong happened here."; + } + } + return 0; +} +1; diff --git a/src/modules/pdffer.pm b/src/modules/pdffer.pm new file mode 100644 index 0000000..cd440a4 --- /dev/null +++ b/src/modules/pdffer.pm @@ -0,0 +1,25 @@ +package csbot2::pdffer; +use strict; +use warnings; +use LWP::Simple; +use DateTime; +use feature "say"; + +sub init +{ + say "[~] pdffer module init"; +} + +sub parse +{ + my ($self, $line, $irc, $config, $channel, $nick) = @_; + if (/^:(.+?)!.+?@.+? PRIVMSG ${channel} :url2pdf (.+?)$/i) + { + my $api = "http://do.convertapi.com/Web2Pdf?Plugins=true&Timeout=60&ConversionDelay=5&CUrl=" . $2; + my $filename = DateTime -> now . ".pdf"; + getstore($api, "../public_html/url2pdf/" . $filename); + say $irc "PRIVMSG $channel :Converted! Download at https://www.niggazwithattitu.de/u/wasp/url2pdf/$filename"; + } + return 0; +} +1; diff --git a/src/modules/reddit.pm b/src/modules/reddit.pm new file mode 100644 index 0000000..300b5e3 --- /dev/null +++ b/src/modules/reddit.pm @@ -0,0 +1,18 @@ +package csbot2::reddit; +use strict; +use warnings; +use feature "say"; + +sub init +{ + say "[~] reddit module init"; +} + +sub parse +{ + my ($self, $line, $irc, $config, $channel, $nick) = @_; + say $irc "PRIVMSG $channel :http://reddit.com/r/$1" if /^:(?:.+?)!(?:.+?)@(?:.+?) PRIVMSG ${channel} :(?:.+\s)?r\/(\w+)(\s|$)/i; + say $irc "PRIVMSG $channel :http://reddit.com/u/$1" if /^:(?:.+?)!(?:.+?)@(?:.+?) PRIVMSG ${channel} :(?:.+\s)?u\/(\w+)(\s|$)/i; + return 0; +} +1; \ No newline at end of file diff --git a/src/modules/rss.pm b/src/modules/rss.pm new file mode 100644 index 0000000..fe6fa8f --- /dev/null +++ b/src/modules/rss.pm @@ -0,0 +1,67 @@ +package csbot2::rss; +use strict; +use warnings; +use XML::FeedPP; +use feature "say"; + +my @posts; + +sub loadSource{ + my $source = shift; + @posts = (); + + my $feed; + + eval{ + my $source_url=$source; + $feed = XML::FeedPP->new($source_url); + }; + + if ($@) + { + print "hey, failed! ".$source; + return; + } + + foreach my $post ($feed->get_item()) + { + my $title = $post->title; + $title =~ s/(\t|\n)/ /g; + $title =~ s/ {2,}/ /g; + push (@posts, {title => $title, link => $post->link}); + } +} + +sub init +{ + say "[~] rss module init"; +} + +sub parse +{ + my ($self, $line, $irc, $config, $channel, $nick) = @_; + if(/^:(.+?)!.+?@.+? PRIVMSG ${channel} :rss help/i) + { + say $irc "PRIVMSG $channel :(help) rss [number of item to see] <url>"; + } + elsif (/^:(.+?)!.+?@.+? PRIVMSG ${channel} :rss ([0-9]* ?)(.+?)$/i) + { + loadSource($3); + unless (scalar @posts) + { + say $irc "PRIVMSG $channel :I heard you talkin' shit, man. Please, stop it."; + } + else + { + my ($index,$limit) = (0, 3); + $limit = int($2) if $2; + foreach my $post (@posts) + { + say $irc "PRIVMSG $channel :".$post->{"title"}." --> ".$post->{"link"} if $index < $limit; + $index++; + } + } + } + return 0; +} +1; diff --git a/src/modules/scp.pm b/src/modules/scp.pm new file mode 100644 index 0000000..a61c6ed --- /dev/null +++ b/src/modules/scp.pm @@ -0,0 +1,46 @@ +package csbot2::scp; +use strict; +use warnings; +use LWP::UserAgent; +use HTML::Entities; +use feature "say"; + +sub init +{ + say "[~] scp module init"; +} + +sub parse +{ + my ($self, $line, $irc, $config, $channel, $nick) = @_; + if (/^:(.+?)!.+?@.+? PRIVMSG ${channel} :.*scp-(\d+).*$/i) + { + my $scp = $2; + my $url = ""; + if ($scp < 1000) + { + $url = "http://www.scp-wiki.net/scp-series"; + } + else + { + if ($scp < 2000) + { + $url = "http://www.scp-wiki.net/scp-series-2"; + } + else + { + $url = "http://www.scp-wiki.net/scp-series-3"; + } + } + + my $response = LWP::UserAgent -> new -> get($url) -> decoded_content; + my @responze = split /\n/, $response; + + foreach (@responze) + { + say $irc "PRIVMSG $channel :SCP-$scp", decode_entities($1), " http://www.scp-wiki.net/scp-$scp" if /<li><a href="\/scp-$scp">SCP-$scp<\/a>( - .*)<\/li>/i; + } + } + return 0; +} +1; diff --git a/src/modules/specials.pm b/src/modules/specials.pm new file mode 100644 index 0000000..03c4169 --- /dev/null +++ b/src/modules/specials.pm @@ -0,0 +1,20 @@ +package csbot2::specials; +use strict; +use warnings; +use feature "say"; + +sub init +{ + say "[~] specials module init"; +} + +sub parse +{ + my ($self, $line, $irc, $config, $channel, $nick) = @_; + say $irc "PRIVMSG $channel :~" if $line =~ /^:(.+?)!.+?@.+? PRIVMSG ${channel} :.*tilde.*$/i; + say $irc "PRIVMSG $channel :`" if $line =~ /^:(.+?)!.+?@.+? PRIVMSG ${channel} :.*backtick.*$/i; + say $irc "PRIVMSG $channel :`~" if $line =~ /^:(.+?)!.+?@.+? PRIVMSG ${channel} :.*itsdangerous.*$/i; #to go alone, take these! + say $irc "PRIVMSG $channel :\x01ACTION headtilts\x01" if $line =~ /^:(.+?)!.+?@.+? PRIVMSG ${channel} :.*shinbo.*$/i; + return 0; +} +1; diff --git a/src/modules/trakt.pm b/src/modules/trakt.pm new file mode 100644 index 0000000..2930f04 --- /dev/null +++ b/src/modules/trakt.pm @@ -0,0 +1,104 @@ +package csbot2::trakt; +use strict; +use warnings; +use JSON; +use LWP::UserAgent; +use DateTime; +use feature "say"; + +sub init +{ + say "[~] trakt module init"; +} + +sub parse +{ + my ($self, $line, $irc, $config, $channel, $nick) = @_; + + if (/^:(.+?)!.+?@.+? PRIVMSG ${channel} :trakt stats (\S+)\s?.*$/i) + { + trakt_stats($2, $config -> {"apikeys"} -> {"trakt"}, $irc, $channel); + return; + } + + if (/^:(.+?)!.+?@.+? PRIVMSG ${channel} :.* ?trakt stats ?.*$/i) + { + trakt_stats($config -> {"traktaliases"} -> {$1}, $config -> {"apikeys"} -> {"trakt"}, $irc, $channel); + return; + } + + if (/^:(.+?)!.+?@.+? PRIVMSG ${channel} :trakt (\S+)\s?.*$/i) + { + trakt($2, $config -> {"apikeys"} -> {"trakt"}, $irc, $channel); + return; + } + + if (/^:(.+?)!.+?@.+? PRIVMSG ${channel} :.* ?trakt ?.*$/i) + { + trakt($config -> {"traktaliases"} -> {$1}, $config -> {"apikeys"} -> {"trakt"}, $irc, $channel); + return; + } + return 0; +} + +sub trakt_stats +{ + my ($user, $apikey, $irc, $channel) = @_; + my $json = LWP::UserAgent -> new -> get("http://api.trakt.tv/user/profile.json/" . $apikey . "/" . $user) -> decoded_content; + my $dati = decode_json $json; + if ($dati -> {"status"} eq "failure" && not (defined($dati -> {"username"}) && defined($dati -> {"stats"} -> {"shows"} -> {"collection"}) && defined($dati -> {"stats"} -> {"episodes"} -> {"collection"}) && defined($dati -> {"stats"} -> {"movies"} -> {"collection"}) && defined($dati -> {"stats"} -> {"episodes"} -> {"watched_unique"}) && defined($dati -> {"stats"} -> {"movies"} -> {"watched_unique"}))) + { + say $irc "PRIVMSG $channel :You don't want to end up dead right? So stahp before it's too late."; + } + else + { + say $irc "PRIVMSG $channel :" . $dati -> {"username"} . " collected \x02" . $dati -> {"stats"} -> {"shows"} -> {"collection"} . "\x02 shows (\x02" . $dati -> {"stats"} -> {"episodes"} -> {"collection"} . "\x02 episodes) and \x02" . $dati -> {"stats"} -> {"movies"} -> {"collection"} . "\x02 movies."; + say $irc "PRIVMSG $channel :He watched \x02" . $dati -> {"stats"} -> {"episodes"} -> {"watched_unique"} . "\x02 of those episodes and \x02" . $dati -> {"stats"} -> {"movies"} -> {"watched_unique"} . "\x02 movies."; + } + return; +} + +sub trakt +{ + my ($user, $apikey, $irc, $channel) = @_; + my $json = LWP::UserAgent -> new -> get("http://api.trakt.tv/user/profile.json/" . $apikey . "/" . $user) -> decoded_content; + my $dati = decode_json $json; + if (defined $dati -> {status} && $dati -> {"status"} eq "failure") + { + say $irc "PRIVMSG $channel :You don't want to end up dead right? So stahp before it's too late."; + } + else + { + my $tktnick = $dati -> {"username"}; + + if (defined $dati -> {watching} && (ref $dati -> {watching} eq ref {})) + { + if (defined $dati -> {watching} -> {show} -> {title}) + { + say $irc "PRIVMSG $channel :$tktnick is watching \x02", $dati -> {watching}-> {show} -> {title}, " S", sprintf("%02d", $dati -> {watching} -> {episode} -> {season}), "E", sprintf("%02d", $dati -> {watching} -> {episode} -> {number}), "\x02, \"", $dati -> {watching} -> {episode} -> {title}, "\"."; + return; + } + if (defined $dati -> {watching}-> {movie} -> {title}) + { + say $irc "PRIVMSG $channel :$tktnick is watching \x02", $dati -> {watching} -> {movie} -> {title}, "\x02."; + return; + } + } + elsif (defined $dati -> {watched}[0] -> {show} -> {title}) + { + my $date = DateTime -> from_epoch(epoch => $dati -> {watched}[0] -> {watched}); + say $irc "PRIVMSG $channel :$tktnick last watched \x02", $dati -> {watched}[0] -> {show} -> {title}, " S", sprintf("%02d", $dati -> {watched}[0] -> {episode} -> {season}), "E", sprintf("%02d", $dati -> {watched}[0] -> {episode} -> {number}), "\x02 on ", $date -> day_abbr, " ", $date -> month_abbr, " ", $date -> day, " ", $date -> year,"."; + } + elsif (defined $dati -> {watched}[0] -> {movie} -> {title}) + { + my $date = DateTime -> from_epoch(epoch => $dati -> {watched}[0] -> {watched}); + say $irc "PRIVMSG $channel :$tktnick last watched \x02", $dati -> {watched}[0] -> {movie} -> {title} . "\x02 on ", $date -> day_abbr, " ", $date -> month_abbr, " ", $date -> day, " ", $date -> year,"."; + } + else + { + say $irc "PRIVMSG $channel :uhm. it seems that $tktnick didn't watch a TV show or a movie lately. Use the \"Check-in\" functionality to scrobble movies and episodes while you're watching them!"; + } + } +} + +1;