aboutsummaryrefslogtreecommitdiff
path: root/lib/IRC/Client/Handler.rakumod
diff options
context:
space:
mode:
Diffstat (limited to 'lib/IRC/Client/Handler.rakumod')
-rw-r--r--lib/IRC/Client/Handler.rakumod174
1 files changed, 174 insertions, 0 deletions
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 ∈ <PRIVMSG NOTICE> }
+) {
+ 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