From b6da79a0bc1289f2a6064a6b3ffcd0c2333f2c97 Mon Sep 17 00:00:00 2001 From: Patrick Spek Date: Wed, 5 May 2021 11:03:32 +0200 Subject: Initial commit --- lib/IRC/Client/Handler.rakumod | 174 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 lib/IRC/Client/Handler.rakumod (limited to 'lib/IRC/Client/Handler.rakumod') diff --git a/lib/IRC/Client/Handler.rakumod b/lib/IRC/Client/Handler.rakumod new file mode 100644 index 0000000..c966099 --- /dev/null +++ b/lib/IRC/Client/Handler.rakumod @@ -0,0 +1,174 @@ +#! /usr/bin/env false + +use v6.d; + +use IRC::Client::Message; +use IRC::Client::Plugin; + +use Log; + +#| This class handles incoming messages, formats a context from them and +#| dispatches it on to any registered plugin that can handle it. +unit class IRC::Client::Handler; + +#| Handle numeric commands. +multi method handle ( + $event where { $_.command ~~ / \d ** 3/ }, +) { + my @events; + + # Special cases for IRC::Client + @events.append('irc-connected' => $event.irc) if $event.command eq '001'; + @events.append('irc-ready' => $event.irc) if $event.command eq '376'; + @events.append('irc-ready' => $event.irc) if $event.command eq '422'; + + # Regular events, handles in the regular way + @events.append("irc-n{$event.command}" => $event); + @events.append('irc-numeric' => $event); + @events.append('irc-all' => $event); + + self.dispatch(@events, $event.irc); +} + +#| Handle PRIVMSG and NOTICE commands. +multi method handle ( + $event where { $_.command ∈ } +) { + my @events; + my $stripped; + + # If the message starts with the name of the client, it should dispatch + # an irc-addressed event. + if ($event.params[*-1] ~~ / ^ ( "{$event.irc.nick}" <[:;,]> <.ws> ) /) { + $stripped = $event.set-param(*-1, $event.params[*-1].substr($0.chars)); + } + + + # A private message to the client should dispatch an irc-to-me event, + # optionally stripping away the client's current nickname from the + # start. + if ($event.params[0] eq $event.irc.nick) { + @events.append('irc-to-me' => $stripped // $event); + } + + # A message to a public channel prefixed with the client's current + # nickname is both an irc-to-me event, and an irc-addressed event. + if ($event.params[0] ne $event.irc.nick && $stripped) { + @events.append('irc-to-me' => $stripped); + @events.append('irc-addressed' => $stripped); + } + + @events.append("irc-{$event.command.fc}-me" => $event) if $event.params[0] eq $event.irc.nick; + @events.append('irc-mentioned' => $event) if $event.params[*-1].words ∋ $event.irc.nick; + @events.append("irc-{$event.command.fc}-channel" => $event) if $event.params[0] ~~ / ^ <[#&]> /; + @events.append("irc-{$event.command.fc}" => $event); + @events.append('irc-all' => $event); + + self.dispatch(@events, $event.irc); +} + +#| Handle MODE commands. +multi method handle ( + $event where { $_.command eq 'MODE' } +) { + my @events; + + @events.append('irc-mode-me' => $event) if $event.params[0] eq $event.irc.nick; + @events.append('irc-mode-channel' => $event) if $event.params[0] ~~ / ^ <[#&]> /; + @events.append('irc-mode' => $event); + @events.append('irc-all' => $event); + + self.dispatch(@events, $event.irc); +} + +#| Handle all IRC commands. Note that the order in which events are appended +#| does matter, the most "narrow" event should always come first, getting +#| broader as we go down the list. +multi method handle ( + $event, +) { + my @events; + + @events.append("irc-{$event.command.fc}" => $event); + @events.append('irc-all' => $event); + + self.dispatch(@events, $event.irc); +} + +#| Dispatch the message to all plugins. +method dispatch ( + @events, + $client, #= IRC::Client, but can't `use` this due to circular references +) { + my @plugins = $client.plugins; + + # Loop over all the plugins, and dispatch to each of them on a seperate + # thread. This allows plugins to take their sweet time, while not being + # in any other plugins' way. + for $client.plugins -> $plugin { + start { + CATCH { + default { + my $exception = $_; + .emergency($exception.gist.lines.map(*.trim-trailing).join("\n")) with $Log::instance; + } + } + + self.dispatch-plugin(@events, $plugin) + } + } +} + +#| Dispatch the message to all plugin methods that can handle them. +method dispatch-plugin ( + @events, + IRC::Client::Plugin $plugin, +) { + for @events -> $event { + my $method = $event.key; + my $payload = $event.value; + + # Check for available methods to handle this payload. + my @methods = $plugin.^can($method) + .map(*.candidates) + .flat + .grep(*.cando(\($plugin, $payload))) + ; + + # Nothing to do if nothing was found. + next unless @methods; + + .debug("Dispatching to {$plugin.^name}.$method") with $Log::instance; + + my $response = $plugin."$method"($payload); + + next unless $payload ~~ IRC::Client::Message; + + # Depending on the return value of the method, something can be + # done. + given ($response) { + when Str { + $payload.reply($response); + last; + } + } + } +} + +=begin pod + +=NAME IRC::Client::Handler +=AUTHOR Patrick Spek <~tyil/raku-devel@lists.sr.ht> +=VERSION 0.0.0 + +=head1 Synopsis + +=head1 Description + +=head1 Examples + +=head1 See also + +=end pod + +# vim: ft=perl6 noet -- cgit v1.1