From 156800544027a88f68670a47c8fb702843173db5 Mon Sep 17 00:00:00 2001 From: Zoffix Znet Date: Tue, 26 Jul 2016 13:58:52 -0400 Subject: Move DESIGN to historical-archive so no one thinks it is current --- DESIGN/01-main.md | 961 ---------------------- DESIGN/specs-and-references.md | 17 - historical-archive/DESIGN/01-main.md | 961 ++++++++++++++++++++++ historical-archive/DESIGN/specs-and-references.md | 17 + 4 files changed, 978 insertions(+), 978 deletions(-) delete mode 100644 DESIGN/01-main.md delete mode 100644 DESIGN/specs-and-references.md create mode 100644 historical-archive/DESIGN/01-main.md create mode 100644 historical-archive/DESIGN/specs-and-references.md diff --git a/DESIGN/01-main.md b/DESIGN/01-main.md deleted file mode 100644 index 196fc1c..0000000 --- a/DESIGN/01-main.md +++ /dev/null @@ -1,961 +0,0 @@ -# TABLE OF CONTENTS -- [PURPOSE](#purpose) -- [GOALS](#goals) - - [Ease of Use](#ease-of-use) - - [Client-Generated Events](#client-generated-events) - - [Possibility of Non-Blocking Code](#possibility-of-non-blocking-code) -- [DESIGN](#design) -- [Multi-Server Interface](#multi-server-interface) -- [Client Object](#client-object) - - [`$.irc` (access from inside a plugin)](#irc-access-from-inside-a-plugin) - - [`.new`](#new) - - [`.run`](#run) - - [`.quit`](#quit) - - [`.part`](#part) - - [`.join`](#join) - - [`.send`](#send) - - [`.nick`](#nick) - - [`.emit`](#emit) - - [`.emit-custom`](#emit-custom) - - [`.channel`](#channel) - - [`.has`](#has) - - [`.topic`](#topic) - - [`.modes`](#modes) - - [`.bans`](#bans) - - [`.names`](#names) -- [Message Delivery](#message-delivery) -- [Response Constants](#response-constants) - - [`IRC_NEXT`](#irc_next) - - [`IRC_DONE`](#irc_done) -- [Message Object Interface](#message-object-interface) - - [`.nick`](#nick-1) - - [`.username`](#username) - - [`.host`](#host) - - [`.usermask`](#usermask) - - [`.reply`](#reply) -- [Convenience Events](#convenience-events) - - [`irc-to-me`](#irc-to-me) - - [`irc-addressed`](#irc-addressed) - - [`irc-mentioned`](#irc-mentioned) - - [`irc-privmsg-channel`](#irc-privmsg-channel) - - [`irc-privmsg-me`](#irc-privmsg-me) - - [`irc-notice-channel`](#irc-notice-channel) - - [`irc-notice-me`](#irc-notice-me) - - [`irc-started`](#irc-started) - - [`irc-connected`](#irc-connected) - - [`irc-mode-channel`](#irc-mode-channel) - - [`irc-mode-user`](#irc-mode-user) - - [`irc-all`](#irc-all) -- [Numeric Events](#numeric-events) -- [Named Events](#named-events) - - [`irc-nick`](#irc-nick) - - [`irc-quit`](#irc-quit) - - [`irc-join`](#irc-join) - - [`irc-part`](#irc-part) - - [`irc-mode`](#irc-mode) - - [`irc-topic`](#irc-topic) - - [`irc-invite`](#irc-invite) - - [`irc-kick`](#irc-kick) - - [`irc-privmsg`](#irc-privmsg) - - [`irc-notice`](#irc-notice) -- [Custom Events](#custom-events) - -# PURPOSE - -The purpose of IRC::Client is to serve as a fully-functional IRC -client that--unlike programs like HexChat or mIRC--provide a programmatic -interface to IRC. So, for example, to send a message to a channel, instead -of typing a message in a message box and pressing ENTER, a method is called -and given a string. - -Naturally, such an interface provides vast abilities to automate interactions -with IRC or implement a human-friendly interface, such as HexChat or mIRC. - -# GOALS - -An implementation must achieve these goals: - -## Ease of Use - -For basic use, such as a bot that responds to triggers said in channel, -the details of the IRC protocol must be as invisible as possible. Just as any -user can install HexChat and join a channel and talk, similar usability has -to be achieved by the implementation. - -As an example, a HexChat user can glance at the user list or channel topic -without explicitly issuing `NAMES` or `TOPIC` IRC commands. The implementation -should thus provide similar simplicity and provide a userlist or topic -via a convenient method rather than explicit method to send the appropriate -commands and the requirement of listening for the server response events. - -## Client-Generated Events - -The implementation must allow the users of the code to emit IRC and custom -events. For example, given plugins A and B, with A performing processing -first, plugin A can mark all `NOTICE` IRC events as handled and emit them -as `PRIVMSG` events instead. From the point of view of second plugin B, no -`NOTICE` commands ever happen (as they arrive to it as `PRIVMSG`). - -Similarly, plugin A can choose to emit custom event `FOOBAR` instead of -`PRIVMSG`, to which plugin B can choose to respond to. - -## Possibility of Non-Blocking Code - -The implementation must allow the user to perform responses to events in -a non-blocking manner if they choose to. - -# DESIGN - -The implementation consists of Core code responsible for maintaining the -state of the connected client, parsing of server messages, and sending -essential messages, as well as relating messages to and from plugins. - -The implementation distribution may also include several plugins that may -be commonly needed by users. Such plugins are not enabled by default and -the user must request their inclusion with code. - -# Multi-Server Interface - -The interface described in the rest of this document assumes a connection -to a single server. Should the client be connected to multiple-servers at -the time, issuing commands described will apply to *every* connected server. -A server must be specified to issue a command to a single server. -**Plugin authors must keep this fact in mind, when writing plugins, as -forgetting to handle multiple servers can result in unwanted behaviour.** - -The same reasoning applies to the `.new` method: attributes, such as -nicknames, usernames, etc. given without associating them with a server will -apply to ALL connected servers. Configuration for individual servers is -given via `:servers` named parameter as a list of `Pairs`. The key -is the nickname of server and must be a valid method name. It's recommended -to choose something that won't end up an actual method on the Client Object. -It's guaranteed methods starting with `s-` will always be safe to use. The -value is a list of pairs that can be accepted by the Client Object as named -parameters (except for `:servers`) that specify the configuration for that -specific server, overriding any of the non-server-specific parameters already -set. - -A possible `.new` setup may look something like this: - -```perl6 - my $irc = IRC::Client.new: - :nick # nicks to try to use on ALL servers, - :servers( - s-leliana => ( - :server, - :channels<#perl #perl6 #perl7> - ), - s-morrigan => ( - :server, - :channels<#perl #perl-help> - ), - s-alistair => ( - :nick # nick override - :server, - :channels<#perler> - ), - ), -``` - -Use of multiple servers is facilitated via server nicknames and using -them as a method call to obtain the correct Client Object. For example: - -```perl6 - $.irc.quit; # quits all servers - $.irc.s-leliana.quit; # quits only the s-leliana server - - # send a message to #perl6 channel on s-morrigan server - $.irc.s-morrigan.send: where => '#perl6', text => 'hello'; -``` - -The Message Object will also contain a `.server` method value of which -is the nickname of the server from which the message arrived. In general, -the most common way to generate messages will be using `.reply` on the Message -Object, making the multi-server paradigm completely transparent. - -# Client Object - -Client Object represents a connected IRC client and is aware of and can -manipulate its state, such as disconnecting, joining or parting a channel, -or sending messages. - -A Client Object must support the ability to connect to multiple servers. -The client object provides these methods: - -## `$.irc` (access from inside a plugin) - -```perl6 - use IRC::Client::Plugin; - unit Plugin::Foo is IRC::Client::Plugin; - - method irc-privmsg-me ($msg) { - $.irc.send: - where => '#perl6', - text => "$msg.nick() just sent me a secret! It's $msg.text()"; - } -``` - -A plugin inherits from `IRC::Client::Plugin`, which provides `$.irc` -attribute containing the Client Object, allowing the plugin to utilize all -of the methods it provides. - -## `.new` - -```perl6 - my $irc = IRC::Client.new: - ... - :plugins( - IRC::Client::Plugin::Factoid.new, - My::Plugin.new, - class :: is IRC::Client::Plugin { - method irc-privmsg-me ($msg) { $msg.repond: 'Go away!'; } - }, - ); -``` - -*Not to be used inside plugins.* -Creates a new `IRC::Client` object. Along with the usual arguments like -nick, username, server address, etc, takes `:plugins` argument that -lists the plugins to include. All messages will be propagated through plugins -in the order they are defined here. - -## `.run` - -```perl6 - $irc.run; -``` - -*Not to be used inside plugins.* -Starts the client, connecting to the server and maintaining that connection -and not returning until an explicit `.quit` is issued. If the connection -breaks, the client will attempt to reconnect. - -## `.quit` - -```perl6 - $.irc.quit; - - $.irc.quit: 'Reason'; -``` - -Disconnects from the server. Takes an option string to be given to the -server as the reson for quitting. - -## `.part` - -```per6 - $.irc.part: '#perl6'; - - $.irc.part: '#perl6', 'Leaving'; -``` - -Exits a channel. Takes two positional strings: the channel to part -and an optional parting message. Causes the client object to discard any state -kept for this channel. - -## `.join` - -```perl6 - $.irc.join '#perl6', '#perl7'; -``` - -Attempts to joins channels given as positional arguments. - -## `.send` - -```perl6 - $.irc.send: where => '#perl6', text => 'Hello, Perl 6!'; - - $.irc.send: where => 'Zoffix', text => 'Hi, Zoffie!'; - - $.irc.send: where => 'Zoffix', text => 'Notice me, senpai!', :notice; -``` - -Sends a message specified by `text` argument -either to a user or a channel specified by `:where` argument. If `Bool` -argument `:notice` is set to true, will send a *notice* instead of regular -message. - -Note that in IRC bots that respond to commands from other users a more -typical way to reply to those commands would be by calling -`.reply` method on the Message Object, rather than using `.send` method. - - -## `.nick` - -```perl6 - $.irc.nick: 'ZofBot', 'ZofBot_', 'ZofBot__'; -``` - -Attempts to change the nick of the client. Takes one or more positional -arguments that are a list of nicks to try. - -## `.emit` - -```perl6 - $.irc.emit: $msg; - - $.irc.emit: IRC::Client::Message::Privmsg.new: - nick => 'Zoffix', - text => 'Hello', - ...; - ... - method irc-privmsg ($msg) { - say "$msg.nick() said $msg.text()... or did they?"; - } -``` - -Takes an object of any of `IRC::Client::Message::*` subclass and emits it -as if it were a new event. That is, it will propagate through the plugin chain -starting at the first plugin, and not the one emiting the event, and the -plugins can't tell whether the message is self-generated or something that -came from the server. - -## `.emit-custom` - -```perl6 - $.irc.emit-custom: 'my-event', 'just', 'some', :args; -``` - -Same idea as `.emit`, except a custom event is emitted. The first positional -argument specifies the name of the event to emit. Any other arguments -given here will be passed as is to listener methods. - -## `.channel` - -```perl6 - method irc-addressed ($msg) { - if $msg.text ~~ /'kick' \s+ $=\S+/ { - $msg.reply: "I don't see $ up in here" - unless $.irc.channel($msg.channel).?has: ~$; - } - - if $msg.text ~~ /'topic' \s+ $=\S+/ { - return $msg.reply: $_ - ?? "Channel $ does not exist" - !! "Topic in $ is $_.topic()" - given $.irc.channel: ~$; - } - } -``` - -Returns an `IRC::Client::Channel` object for the channel given as positional -argument, or `False` if no such channel seems to exist. Unless our client is -currently *on* that channel, that existence is -determined with `LIST` IRC command, so there will be some false negatives, -such as when attempting to get an object for a channel with secret mode set. - -The Client Object tracks state for any of the joined channels, so some -information obtainable via the Channel Object will be cached -and retrieved from that state, whenever possible. Otherwise, a request -to the server will be generated. Return values will be empty (empty lists -or empty strings) when requests fail. The channel object provides the -following methods. - -### `.has` - -```perl6 - $.irc.channel('#perl6').has: 'Zoffix'; -``` - -Returns `True` or `False` indicating whether a user with the given nick is -present on the channel. - -### `.topic` - -```perl6 - say "Topic of the channel is " ~ $.irc.channel('#perl6').topic; -``` - -Returns the `TOPIC` of the channel. - -### `.modes` - -```perl6 - say $.irc.channel('#perl6').modes; - # ('s', 'n', 't') -``` - -Returns a list of single-letter codes for currently active channel modes -on the channel. Note, this does not include any bans. - -### `.bans` - -```perl6 - say $.irc.channel('#perl6').bans; - # ('*!spammer@*', 'warezbot!*@*') -``` - -Returns a list of currently active ban masks on the channel. - -### `.names` - -```perl6 - say $.irc.channel('#perl6').names; - # ('@Zoffix', '+zoffixs-helper', 'not-zoffix') -``` - -Returns a list of nicks present on the channel, each potentially prefixed -with a [channel membership prefix](https://www.alien.net.au/irc/chanmembers.html) - -# Message Delivery - -An event listener is defined by a method in a plugin class. The name -of the method starts with `irc-` and followed by the lowercase name of the -event. User-defined events follow the same pattern, except they start with -`irc-custom-`: - -```perl6 - use IRC::Client::Plugin; - unit Plugin::Foo is IRC::Client::Plugin; - - # Listen to PRIVMSG IRC events: - method irc-privmsg ($msg) { - return IRC_NEXT unless $msg.channel eq '#perl6'; - $msg.reply: 'Nice to meet you!'; - } - - # Listen to custom client-generated events: - method irc-custom-my-event ($some, $random, :$args) { - return IRC_NEXT unless $random > 5; - $.irc.send: where => '#perl6', text => 'Custom event triggered!'; - } -``` - -An event listener receives the event message in the form of an object. -The object must provide all the relevant information about the source -and content of the message. - -The message object, where appropriate, must provide a means to send a reply -back to the originator of the message. For example, here's a potential -implementation of `PRIVMSG` handler that receives the message object: - -```perl6 - method irc-privmsg-channel ($msg) { - return IRC_NEXT unless $msg.channel eq '#perl6'; - $msg.reply: 'Nice to meet you!'; - } -``` - -A plugin can send messages and emit events at will: - -```perl6 - method irc-connected { - Supply.interval(60).tap: { - $.irc.send: where => '#perl6', text => 'One minute passed!!'; - }; - Promise.in(60*60).then: { - $.irc.send: - where => 'Zoffix', - text => 'I lived for one hour already!', - :notice; - - $.irc.emit-custom: 'MY-EVENT', 'One hour passed!'; - } - } -``` - -# Response Constants - -Multiple plugins can listen to the same event. The event message will be -handed to each of the plugins in the sequence they are defined when the -Client Object is initialized. Each handler can use predefined response -constants to signal whether the handling of this particular event message -should stop or continue onto the next plugin. These response constants -are `IRC_NEXT` and `IRC_DONE` and are exported by `IRC::Client::Plugin`. - -## `IRC_NEXT` - -```perl6 - method irc-privmsg-channel ($msg) { - return IRC_NEXT unless $msg.channel eq '#perl6'; - .... - } -``` - -Signals that the message should continue to be passed on to any further -plugins that subscribed to handle it. - -## `IRC_DONE` - -```perl6 - method irc-privmsg-channel ($msg) { - return IRC_DONE if $msg.channel eq '#perl6'; - } - - # or just... - - method irc-privmsg-channel ($msg) {} -``` - -Signals that the message has been handled and should NOT be passed on -to any further plugins. **Note:** you don't have to explicitly return this -value; anything other than returning `IRC_NEXT` is the same as returning -`IRC_DONE`. - - -# Message Object Interface - -The message object received by all non-custom events is an event-specific -subclass of `IRC::Client::Message`. The subclass is named -`IRC::Client::Message::$NAME`, where `$NAME` is: - -* *Named* and *Convenience* events use their names without `irc-` part, with any `-` -changed to `::` and with each word written in `Title Case`. e.g. -message object for `irc-privmsg-me` is `IRC::Client::Message::Privmsg::Me` -* *Numeric* events always receive `IRC::Client::Message::Numeric` message -object, regardless of the actual number of the event. - -Along with event-specific methods -described under each event, the `IRC::Client::Message` offers the following -methods: - -## `.nick` - -```perl6 - say $msg.nick ~ " says hello"; -``` - -Contains the nickname of the sender of the message. - -## `.username` - -```perl6 - say $msg.nick ~ " has username " ~ $msg.username; -``` - -Contains the username of the sender of the message. - -## `.host` - -```perl6 - say $msg.nick ~ " is connected from " ~ $msg.host; -``` - -Hostname of sender of the message. - -## `.usermask` - -```perl6 - say $msg.usermask; -``` - -Nick, username, and host combined into a full usermask, e.g. -`Zoffix!zoffix@zoffix.com` - -## `.reply` - -```perl6 - $msg.reply: 'I love you too' - if $msg.text ~~ /'I love you'/; -``` - -Replies back to a message. For example, if we received the message as a -private message to us, the reply will be a private message back to the -user. Same for notices. For in-channel messages, `irc-addressed` -and `irc-to-me` will address the sender in return, while all other in-channel -events will not. - -**NOTE:** this method is only available for these events: - -* `irc-privmsg` -* `irc-notice` -* `irc-to-me` -* `irc-addressed` -* `irc-mentioned` -* `irc-privmsg-channel` -* `irc-privmsg-me` -* `irc-notice-channel` -* `irc-privmsg-me` - -# Convenience Events - -These sets of events do not have a corresponding IRC command defined by the -protocol and instead are offered to make listening for a specific kind -of events easier. - -## `irc-to-me` - -```perl6 - # :zoffix!zoffix@127.0.0.1 PRIVMSG zoffix2 :hello - # :zoffix!zoffix@127.0.0.1 NOTICE zoffix2 :hello - # :zoffix!zoffix@127.0.0.1 PRIVMSG #perl6 :zoffix2, hello - - method irc-to-me ($msg) { - printf "%s told us `%s` using %s\n", - .nick, .text, .how given $msg; - } -``` - -Emitted when a user sends us a message as a private message, notice, or -addresses us in a channel. The `.respond` method of the Message -Object is the most convenient way to respond back to the sender of the message. - -The `.how` method returns a `Pair` where the key is the message type used -(`PRIVMSG` or `NOTICE`) and the value is the addressee of that message -(a channel or us). - -## `irc-addressed` - -```perl6 - # :zoffix!zoffix@127.0.0.1 PRIVMSG #perl6 :zoffix2, hello - - method irc-addressed ($msg) { - printf "%s told us `%s` in channel %s\n", - .nick, .text, .channel given $msg; - } -``` - -Emitted when a user addresses us in a channel. Specifically, this means -their message starts with our nickname, followed by optional comma or colon, -followed by whitespace. That prefix will be stripped from the message. - -## `irc-mentioned` - -```perl6 - # :zoffix!zoffix@127.0.0.1 PRIVMSG #perl6 :Is zoffix2 a robot? - - method irc-mentioned ($msg) { - printf "%s mentioned us in channel %s when they said %s\n", - .nick, .channel, .text given $msg; - } -``` - -Emitted when a user mentions us in a channel. Specifically, this means -their message contains our nickname separated by a word boundary on each side. - -## `irc-privmsg-channel` - -```perl6 - # :zoffix!zoffix@127.0.0.1 PRIVMSG #perl6 :hello - - method irc-privmsg-channel ($msg) { - printf "%s said `%s` to channel %s\n", - .nick, .text, .channel given $msg; - } -``` - -Emitted when a user sends a message to a channel. - -## `irc-privmsg-me` - -```perl6 - # :zoffix!zoffix@127.0.0.1 PRIVMSG zoffix2 :hey bruh - - method irc-privmsg-me ($msg) { - printf "%s messaged us: %s\n", .nick, .text given $msg; - } -``` - -Emitted when a user sends us a private message. - -## `irc-notice-channel` - -```perl6 - # :zoffix!zoffix@127.0.0.1 NOTICE #perl6 :Notice me! - - method irc-notice-channel ($msg) { - printf "%s sent a notice `%s` to channel %s\n", - .nick, .text, .channel given $msg; - } -``` - -Emitted when a user sends a notice to a channel. - -## `irc-notice-me` - -```perl6 - # :zoffix!zoffix@127.0.0.1 NOTICE zoffix2 :did you notice me? - - method irc-notice-me ($msg) { - printf "%s sent us a notice: %s\n", .nick, .text given $msg; - } -``` - -Emitted when a user sends us a private notice. - -## `irc-started` - -```perl6 - method irc-started { - $.do-some-sort-of-init-setup; - } -``` - -Emitted when the IRC client is started. Useful for doing setup work, like -initializing database connections, etc. Note: this event will fire only once, -even if the client reconnects to the server numerous times. Note that -unlike most events, this event does *not* receive a Message Object. -**IMPORTANT:** when this event fires, there's no guarantee we even started a -connection to the server, let alone connected successfully. - -## `irc-connected` - -```perl6 - method irc-connected { - $.do-some-sort-of-per-connection-setup; - } -``` - -Similar to `irc-started`, except will be emitted every time a -*successful* connection to the server is made and we joined all -of the requested channels. That is, we'll wait to either receive the -full user list or error message for each of the channels we're joining. -Note that unlike most events, this event does *not* receive a Message Object. - -## `irc-mode-channel` - -```perl6 - # :zoffix!zoffix@127.0.0.1 MODE #perl6 +o zoffix2 - # :zoffix!zoffix@127.0.0.1 MODE #perl6 +bbb Foo!*@* Bar!*@* Ber!*@* - - method irc-mode-channel ($msg) { - printf "Nick %s with usermask %s set mode(s) %s in channel %s\n", - .nick, .usermask, .modes, .channel given $msg; - } -``` - -Emitted when IRC `MODE` command is received and it's being operated on a -channel, see `irc-mode` event for details. - -## `irc-mode-user` - -```perl6 - # :zoffix2!f@127.0.0.1 MODE zoffix2 +w - - method irc-mode-user ($msg) { - printf "Nick %s with usermask %s set mode(s) %s on user %s\n", - .nick, .usermask, .modes, .who given $msg; - } -``` - -Emitted when IRC `MODE` command is received and it's being operated on a -user, see `irc-mode` event for details. - -## `irc-all` - -```perl6 - method irc-all ($msg) { - say "Received an event: $msg.perl()"; - return IRC_NEXT; - } -``` - -Emitted for all events and is mostly useful for debugging. The type of the -message object received will depend on the type of the event that generated -the message. This event will be triggered *AFTER* all other event handlers -in the current plugin are processed. - -# Numeric Events - -Numeric IRC events can be subscribed to by defining a method with name -`irc-` followed by the numeric code of the event (e.g. `irc-001`). The -arguments of the event can be accessed via `.args` method that returns a -list of strings: - -```perl6 - method irc-004 ($msg) { - say "Here are the arguments of the RPL_MYINFO event:"; - .say for $msg.args; - } -``` - -See [this reference](https://www.alien.net.au/irc/irc2numerics.html) for -a detailed list of numerics and their arguments available in the wild. Note: -the client will emit an event for any received numeric with a 3-digit -code, regardless of whether it is listed in that reference. - -# Named Events - -## `irc-nick` - -```perl6 - # :zoffix!zoffix@127.0.0.1 NICK not-zoffix - - method irc-nick ($msg) { - printf "%s changed nickname to %s\n", .nick, .new-nick given $msg; - } -``` - -[RFC 2812, 3.1.2](https://tools.ietf.org/html/rfc2812#section-3.1.2). -Emitted when a user changes their nickname. - -## `irc-quit` - -```perl6 - # :zoffix!zoffix@127.0.0.1 QUIT :Quit: Leaving - - method irc-quit ($msg) { - printf "%s has quit (%s)\n", .nick, .reason given $msg; - } -``` - -[RFC 2812, 3.1.7](https://tools.ietf.org/html/rfc2812#section-3.1.7). -Emitted when a user quits the server. - -## `irc-join` - -```perl6 - # :zoffix!zoffix@127.0.0.1 JOIN :#perl6 - - method irc-join ($msg) { - printf "%s joined channel %s\n", .nick, .channel given $msg; - } -``` - -[RFC 2812, 3.2.1](https://tools.ietf.org/html/rfc2812#section-3.2.1). -Emitted when a user joins a channel. - -## `irc-part` - -```perl6 - # :zoffix!zoffix@127.0.0.1 PART #perl6 :Leaving - - method irc-part ($msg) { - printf "%s left channel %s (%s)\n", .nick, .channel, .reason given $msg; - } -``` - -[RFC 2812, 3.2.2](https://tools.ietf.org/html/rfc2812#section-3.2.2). -Emitted when a user leaves a channel. - -## `irc-mode` - -```perl6 - # :zoffix!zoffix@127.0.0.1 MODE #perl6 +o zoffix2 - # :zoffix!zoffix@127.0.0.1 MODE #perl6 +bbb Foo!*@* Bar!*@* Ber!*@* - # :zoffix2!f@127.0.0.1 MODE zoffix2 +w - - method irc-mode ($msg) { - if $msg.?channel { - # channel mode change - printf "%s set mode(s) %s in channel %s\n", - .nick, .modes, .channel given $msg; - } - else { - # user mode change - printf "%s set mode(s) %s on user %s\n", - .nick, .modes, .who given $msg; - } - } -``` - -[RFC 2812, 3.1.5](https://tools.ietf.org/html/rfc2812#section-3.1.5)/[RFC 2812, 3.2.3](https://tools.ietf.org/html/rfc2812#section-3.2.3). -Emitted when IRC `MODE` command is received. As the command is dual-purpose, -the message object will have either `.channel` method available -(for channel mode changes) or `.who` method (for user mode changes). See -also `irc-mode-channel` and `irc-mode-user` convenience events. - -For channel modes, the `.modes` method returns a list of `Pair` where key -is the mode set and the value is the argument for that mode (i.e. "limit", -"user", or "banmask") or an empty string if the mode takes no arguments. - -For user modes, the `.modes` method returns a list of `Str` of the modes -set. - -The received message object will be one of the subclasses of -`IRC::Client::Message::Mode` object: `IRC::Client::Message::Mode::Channel` -or `IRC::Client::Message::Mode::User`. - -## `irc-topic` - -```perl6 - # :zoffix!zoffix@127.0.0.1 TOPIC #perl6 :meow - - method irc-topic ($msg) { - printf "%s set topic of channel %s to %s\n", - .nick, .channel, .topic given $msg; - } -``` - -[RFC 2812, 3.2.4](https://tools.ietf.org/html/rfc2812#section-3.2.4). -Emitted when a user changes the topic of a channel. - -## `irc-invite` - -```perl6 - # :zoffix!zoffix@127.0.0.1 INVITE zoffix2 :#perl6 - - method irc-invite ($msg) { - printf "%s invited us to channel %s\n", .nick, .channel given $msg; - } -``` - -[RFC 2812, 3.2.7](https://tools.ietf.org/html/rfc2812#section-3.2.7). -Emitted when a user invites us to a channel. - -## `irc-kick` - -```perl6 - # :zoffix!zoffix@127.0.0.1 KICK #perl6 zoffix2 :go away - - method irc-kick ($msg) { - printf "%s kicked %s out of %s (%s)\n", - .nick, .who, .channel, .reason given $msg; - } -``` - -[RFC 2812, 3.2.8](https://tools.ietf.org/html/rfc2812#section-3.2.8). -Emitted when someone kicks a user out of a channel. - -## `irc-privmsg` - -```perl6 - # :zoffix!zoffix@127.0.0.1 PRIVMSG #perl6 :hello - # :zoffix!zoffix@127.0.0.1 PRIVMSG zoffix2 :hey bruh - - method irc-privmsg ($msg) { - if $msg.?channel { - # message sent to a channel - printf "%s said `%s` to channel %s\n", - .nick, .text, .channel given $msg; - } - else { - # private message - printf "%s messaged us: %s\n", .nick, .text given $msg; - } - } -``` - -[RFC 2812, 3.3.1](https://tools.ietf.org/html/rfc2812#section-3.3.1). -Emitted when a user sends a message either to a channel -or a private message to us. See *Convenience Events* section for a number -of more convenient ways to listen to messages. - -## `irc-notice` - -```perl6 - # :zoffix!zoffix@127.0.0.1 NOTICE #perl6 :Notice me! - # :zoffix!zoffix@127.0.0.1 NOTICE zoffix2 :did you notice me? - - method irc-notice ($msg) { - if $msg.?channel { - # notice sent to a channel - printf "%s sent a notice `%s` to channel %s\n", - .nick, .text, .channel given $msg; - } - else { - # private notice - printf "%s sent us a notice: %s\n", .nick, .text given $msg; - } - } -``` - -[RFC 2812, 3.3.2](https://tools.ietf.org/html/rfc2812#section-3.3.2). -Emitted when a user sends a notice either to a channel -or a private notice to us. See *Convenience Events* section for a number -of more convenient ways to listen to notices and messages. - -# Custom Events - -There is support for custom events. A custom event is emitted by calling -`.emit-custom` method on the Client Object and is subscribed to via -`irc-custom-*` methods: - -```perl6 - $.irc.emit-custom: 'my-event', 'just', 'some', :args; - ... - method irc-custom-my-event ($just, $some, :$args) { } -``` - -No Message Object is involved in custom events. diff --git a/DESIGN/specs-and-references.md b/DESIGN/specs-and-references.md deleted file mode 100644 index 917e606..0000000 --- a/DESIGN/specs-and-references.md +++ /dev/null @@ -1,17 +0,0 @@ - -# Specs - -* [Numerics and other awesome info](https://www.alien.net.au/irc/) -* [RFC 1459](https://tools.ietf.org/html/rfc1459) -* [RFC 2810](https://tools.ietf.org/html/rfc2810) -* [RFC 2811](https://tools.ietf.org/html/rfc2811) -* [RFC 2812](https://tools.ietf.org/html/rfc2812) -* [RFC 2813](https://tools.ietf.org/html/rfc2813) -* [WebIRC](https://irc.wiki/WebIRC) -* [CTCP SPEC](http://cpansearch.perl.org/src/HINRIK/POE-Component-IRC-6.78/docs/ctcpspec.html) -* [DCC Description](http://www.irchelp.org/irchelp/rfc/dccspec.html) -* [DCC2](https://tools.ietf.org/id/draft-smith-irc-dcc2-negotiation-00.txt) - -# Future - -IRCv3 group: http://ircv3.net/ diff --git a/historical-archive/DESIGN/01-main.md b/historical-archive/DESIGN/01-main.md new file mode 100644 index 0000000..196fc1c --- /dev/null +++ b/historical-archive/DESIGN/01-main.md @@ -0,0 +1,961 @@ +# TABLE OF CONTENTS +- [PURPOSE](#purpose) +- [GOALS](#goals) + - [Ease of Use](#ease-of-use) + - [Client-Generated Events](#client-generated-events) + - [Possibility of Non-Blocking Code](#possibility-of-non-blocking-code) +- [DESIGN](#design) +- [Multi-Server Interface](#multi-server-interface) +- [Client Object](#client-object) + - [`$.irc` (access from inside a plugin)](#irc-access-from-inside-a-plugin) + - [`.new`](#new) + - [`.run`](#run) + - [`.quit`](#quit) + - [`.part`](#part) + - [`.join`](#join) + - [`.send`](#send) + - [`.nick`](#nick) + - [`.emit`](#emit) + - [`.emit-custom`](#emit-custom) + - [`.channel`](#channel) + - [`.has`](#has) + - [`.topic`](#topic) + - [`.modes`](#modes) + - [`.bans`](#bans) + - [`.names`](#names) +- [Message Delivery](#message-delivery) +- [Response Constants](#response-constants) + - [`IRC_NEXT`](#irc_next) + - [`IRC_DONE`](#irc_done) +- [Message Object Interface](#message-object-interface) + - [`.nick`](#nick-1) + - [`.username`](#username) + - [`.host`](#host) + - [`.usermask`](#usermask) + - [`.reply`](#reply) +- [Convenience Events](#convenience-events) + - [`irc-to-me`](#irc-to-me) + - [`irc-addressed`](#irc-addressed) + - [`irc-mentioned`](#irc-mentioned) + - [`irc-privmsg-channel`](#irc-privmsg-channel) + - [`irc-privmsg-me`](#irc-privmsg-me) + - [`irc-notice-channel`](#irc-notice-channel) + - [`irc-notice-me`](#irc-notice-me) + - [`irc-started`](#irc-started) + - [`irc-connected`](#irc-connected) + - [`irc-mode-channel`](#irc-mode-channel) + - [`irc-mode-user`](#irc-mode-user) + - [`irc-all`](#irc-all) +- [Numeric Events](#numeric-events) +- [Named Events](#named-events) + - [`irc-nick`](#irc-nick) + - [`irc-quit`](#irc-quit) + - [`irc-join`](#irc-join) + - [`irc-part`](#irc-part) + - [`irc-mode`](#irc-mode) + - [`irc-topic`](#irc-topic) + - [`irc-invite`](#irc-invite) + - [`irc-kick`](#irc-kick) + - [`irc-privmsg`](#irc-privmsg) + - [`irc-notice`](#irc-notice) +- [Custom Events](#custom-events) + +# PURPOSE + +The purpose of IRC::Client is to serve as a fully-functional IRC +client that--unlike programs like HexChat or mIRC--provide a programmatic +interface to IRC. So, for example, to send a message to a channel, instead +of typing a message in a message box and pressing ENTER, a method is called +and given a string. + +Naturally, such an interface provides vast abilities to automate interactions +with IRC or implement a human-friendly interface, such as HexChat or mIRC. + +# GOALS + +An implementation must achieve these goals: + +## Ease of Use + +For basic use, such as a bot that responds to triggers said in channel, +the details of the IRC protocol must be as invisible as possible. Just as any +user can install HexChat and join a channel and talk, similar usability has +to be achieved by the implementation. + +As an example, a HexChat user can glance at the user list or channel topic +without explicitly issuing `NAMES` or `TOPIC` IRC commands. The implementation +should thus provide similar simplicity and provide a userlist or topic +via a convenient method rather than explicit method to send the appropriate +commands and the requirement of listening for the server response events. + +## Client-Generated Events + +The implementation must allow the users of the code to emit IRC and custom +events. For example, given plugins A and B, with A performing processing +first, plugin A can mark all `NOTICE` IRC events as handled and emit them +as `PRIVMSG` events instead. From the point of view of second plugin B, no +`NOTICE` commands ever happen (as they arrive to it as `PRIVMSG`). + +Similarly, plugin A can choose to emit custom event `FOOBAR` instead of +`PRIVMSG`, to which plugin B can choose to respond to. + +## Possibility of Non-Blocking Code + +The implementation must allow the user to perform responses to events in +a non-blocking manner if they choose to. + +# DESIGN + +The implementation consists of Core code responsible for maintaining the +state of the connected client, parsing of server messages, and sending +essential messages, as well as relating messages to and from plugins. + +The implementation distribution may also include several plugins that may +be commonly needed by users. Such plugins are not enabled by default and +the user must request their inclusion with code. + +# Multi-Server Interface + +The interface described in the rest of this document assumes a connection +to a single server. Should the client be connected to multiple-servers at +the time, issuing commands described will apply to *every* connected server. +A server must be specified to issue a command to a single server. +**Plugin authors must keep this fact in mind, when writing plugins, as +forgetting to handle multiple servers can result in unwanted behaviour.** + +The same reasoning applies to the `.new` method: attributes, such as +nicknames, usernames, etc. given without associating them with a server will +apply to ALL connected servers. Configuration for individual servers is +given via `:servers` named parameter as a list of `Pairs`. The key +is the nickname of server and must be a valid method name. It's recommended +to choose something that won't end up an actual method on the Client Object. +It's guaranteed methods starting with `s-` will always be safe to use. The +value is a list of pairs that can be accepted by the Client Object as named +parameters (except for `:servers`) that specify the configuration for that +specific server, overriding any of the non-server-specific parameters already +set. + +A possible `.new` setup may look something like this: + +```perl6 + my $irc = IRC::Client.new: + :nick # nicks to try to use on ALL servers, + :servers( + s-leliana => ( + :server, + :channels<#perl #perl6 #perl7> + ), + s-morrigan => ( + :server, + :channels<#perl #perl-help> + ), + s-alistair => ( + :nick # nick override + :server, + :channels<#perler> + ), + ), +``` + +Use of multiple servers is facilitated via server nicknames and using +them as a method call to obtain the correct Client Object. For example: + +```perl6 + $.irc.quit; # quits all servers + $.irc.s-leliana.quit; # quits only the s-leliana server + + # send a message to #perl6 channel on s-morrigan server + $.irc.s-morrigan.send: where => '#perl6', text => 'hello'; +``` + +The Message Object will also contain a `.server` method value of which +is the nickname of the server from which the message arrived. In general, +the most common way to generate messages will be using `.reply` on the Message +Object, making the multi-server paradigm completely transparent. + +# Client Object + +Client Object represents a connected IRC client and is aware of and can +manipulate its state, such as disconnecting, joining or parting a channel, +or sending messages. + +A Client Object must support the ability to connect to multiple servers. +The client object provides these methods: + +## `$.irc` (access from inside a plugin) + +```perl6 + use IRC::Client::Plugin; + unit Plugin::Foo is IRC::Client::Plugin; + + method irc-privmsg-me ($msg) { + $.irc.send: + where => '#perl6', + text => "$msg.nick() just sent me a secret! It's $msg.text()"; + } +``` + +A plugin inherits from `IRC::Client::Plugin`, which provides `$.irc` +attribute containing the Client Object, allowing the plugin to utilize all +of the methods it provides. + +## `.new` + +```perl6 + my $irc = IRC::Client.new: + ... + :plugins( + IRC::Client::Plugin::Factoid.new, + My::Plugin.new, + class :: is IRC::Client::Plugin { + method irc-privmsg-me ($msg) { $msg.repond: 'Go away!'; } + }, + ); +``` + +*Not to be used inside plugins.* +Creates a new `IRC::Client` object. Along with the usual arguments like +nick, username, server address, etc, takes `:plugins` argument that +lists the plugins to include. All messages will be propagated through plugins +in the order they are defined here. + +## `.run` + +```perl6 + $irc.run; +``` + +*Not to be used inside plugins.* +Starts the client, connecting to the server and maintaining that connection +and not returning until an explicit `.quit` is issued. If the connection +breaks, the client will attempt to reconnect. + +## `.quit` + +```perl6 + $.irc.quit; + + $.irc.quit: 'Reason'; +``` + +Disconnects from the server. Takes an option string to be given to the +server as the reson for quitting. + +## `.part` + +```per6 + $.irc.part: '#perl6'; + + $.irc.part: '#perl6', 'Leaving'; +``` + +Exits a channel. Takes two positional strings: the channel to part +and an optional parting message. Causes the client object to discard any state +kept for this channel. + +## `.join` + +```perl6 + $.irc.join '#perl6', '#perl7'; +``` + +Attempts to joins channels given as positional arguments. + +## `.send` + +```perl6 + $.irc.send: where => '#perl6', text => 'Hello, Perl 6!'; + + $.irc.send: where => 'Zoffix', text => 'Hi, Zoffie!'; + + $.irc.send: where => 'Zoffix', text => 'Notice me, senpai!', :notice; +``` + +Sends a message specified by `text` argument +either to a user or a channel specified by `:where` argument. If `Bool` +argument `:notice` is set to true, will send a *notice* instead of regular +message. + +Note that in IRC bots that respond to commands from other users a more +typical way to reply to those commands would be by calling +`.reply` method on the Message Object, rather than using `.send` method. + + +## `.nick` + +```perl6 + $.irc.nick: 'ZofBot', 'ZofBot_', 'ZofBot__'; +``` + +Attempts to change the nick of the client. Takes one or more positional +arguments that are a list of nicks to try. + +## `.emit` + +```perl6 + $.irc.emit: $msg; + + $.irc.emit: IRC::Client::Message::Privmsg.new: + nick => 'Zoffix', + text => 'Hello', + ...; + ... + method irc-privmsg ($msg) { + say "$msg.nick() said $msg.text()... or did they?"; + } +``` + +Takes an object of any of `IRC::Client::Message::*` subclass and emits it +as if it were a new event. That is, it will propagate through the plugin chain +starting at the first plugin, and not the one emiting the event, and the +plugins can't tell whether the message is self-generated or something that +came from the server. + +## `.emit-custom` + +```perl6 + $.irc.emit-custom: 'my-event', 'just', 'some', :args; +``` + +Same idea as `.emit`, except a custom event is emitted. The first positional +argument specifies the name of the event to emit. Any other arguments +given here will be passed as is to listener methods. + +## `.channel` + +```perl6 + method irc-addressed ($msg) { + if $msg.text ~~ /'kick' \s+ $=\S+/ { + $msg.reply: "I don't see $ up in here" + unless $.irc.channel($msg.channel).?has: ~$; + } + + if $msg.text ~~ /'topic' \s+ $=\S+/ { + return $msg.reply: $_ + ?? "Channel $ does not exist" + !! "Topic in $ is $_.topic()" + given $.irc.channel: ~$; + } + } +``` + +Returns an `IRC::Client::Channel` object for the channel given as positional +argument, or `False` if no such channel seems to exist. Unless our client is +currently *on* that channel, that existence is +determined with `LIST` IRC command, so there will be some false negatives, +such as when attempting to get an object for a channel with secret mode set. + +The Client Object tracks state for any of the joined channels, so some +information obtainable via the Channel Object will be cached +and retrieved from that state, whenever possible. Otherwise, a request +to the server will be generated. Return values will be empty (empty lists +or empty strings) when requests fail. The channel object provides the +following methods. + +### `.has` + +```perl6 + $.irc.channel('#perl6').has: 'Zoffix'; +``` + +Returns `True` or `False` indicating whether a user with the given nick is +present on the channel. + +### `.topic` + +```perl6 + say "Topic of the channel is " ~ $.irc.channel('#perl6').topic; +``` + +Returns the `TOPIC` of the channel. + +### `.modes` + +```perl6 + say $.irc.channel('#perl6').modes; + # ('s', 'n', 't') +``` + +Returns a list of single-letter codes for currently active channel modes +on the channel. Note, this does not include any bans. + +### `.bans` + +```perl6 + say $.irc.channel('#perl6').bans; + # ('*!spammer@*', 'warezbot!*@*') +``` + +Returns a list of currently active ban masks on the channel. + +### `.names` + +```perl6 + say $.irc.channel('#perl6').names; + # ('@Zoffix', '+zoffixs-helper', 'not-zoffix') +``` + +Returns a list of nicks present on the channel, each potentially prefixed +with a [channel membership prefix](https://www.alien.net.au/irc/chanmembers.html) + +# Message Delivery + +An event listener is defined by a method in a plugin class. The name +of the method starts with `irc-` and followed by the lowercase name of the +event. User-defined events follow the same pattern, except they start with +`irc-custom-`: + +```perl6 + use IRC::Client::Plugin; + unit Plugin::Foo is IRC::Client::Plugin; + + # Listen to PRIVMSG IRC events: + method irc-privmsg ($msg) { + return IRC_NEXT unless $msg.channel eq '#perl6'; + $msg.reply: 'Nice to meet you!'; + } + + # Listen to custom client-generated events: + method irc-custom-my-event ($some, $random, :$args) { + return IRC_NEXT unless $random > 5; + $.irc.send: where => '#perl6', text => 'Custom event triggered!'; + } +``` + +An event listener receives the event message in the form of an object. +The object must provide all the relevant information about the source +and content of the message. + +The message object, where appropriate, must provide a means to send a reply +back to the originator of the message. For example, here's a potential +implementation of `PRIVMSG` handler that receives the message object: + +```perl6 + method irc-privmsg-channel ($msg) { + return IRC_NEXT unless $msg.channel eq '#perl6'; + $msg.reply: 'Nice to meet you!'; + } +``` + +A plugin can send messages and emit events at will: + +```perl6 + method irc-connected { + Supply.interval(60).tap: { + $.irc.send: where => '#perl6', text => 'One minute passed!!'; + }; + Promise.in(60*60).then: { + $.irc.send: + where => 'Zoffix', + text => 'I lived for one hour already!', + :notice; + + $.irc.emit-custom: 'MY-EVENT', 'One hour passed!'; + } + } +``` + +# Response Constants + +Multiple plugins can listen to the same event. The event message will be +handed to each of the plugins in the sequence they are defined when the +Client Object is initialized. Each handler can use predefined response +constants to signal whether the handling of this particular event message +should stop or continue onto the next plugin. These response constants +are `IRC_NEXT` and `IRC_DONE` and are exported by `IRC::Client::Plugin`. + +## `IRC_NEXT` + +```perl6 + method irc-privmsg-channel ($msg) { + return IRC_NEXT unless $msg.channel eq '#perl6'; + .... + } +``` + +Signals that the message should continue to be passed on to any further +plugins that subscribed to handle it. + +## `IRC_DONE` + +```perl6 + method irc-privmsg-channel ($msg) { + return IRC_DONE if $msg.channel eq '#perl6'; + } + + # or just... + + method irc-privmsg-channel ($msg) {} +``` + +Signals that the message has been handled and should NOT be passed on +to any further plugins. **Note:** you don't have to explicitly return this +value; anything other than returning `IRC_NEXT` is the same as returning +`IRC_DONE`. + + +# Message Object Interface + +The message object received by all non-custom events is an event-specific +subclass of `IRC::Client::Message`. The subclass is named +`IRC::Client::Message::$NAME`, where `$NAME` is: + +* *Named* and *Convenience* events use their names without `irc-` part, with any `-` +changed to `::` and with each word written in `Title Case`. e.g. +message object for `irc-privmsg-me` is `IRC::Client::Message::Privmsg::Me` +* *Numeric* events always receive `IRC::Client::Message::Numeric` message +object, regardless of the actual number of the event. + +Along with event-specific methods +described under each event, the `IRC::Client::Message` offers the following +methods: + +## `.nick` + +```perl6 + say $msg.nick ~ " says hello"; +``` + +Contains the nickname of the sender of the message. + +## `.username` + +```perl6 + say $msg.nick ~ " has username " ~ $msg.username; +``` + +Contains the username of the sender of the message. + +## `.host` + +```perl6 + say $msg.nick ~ " is connected from " ~ $msg.host; +``` + +Hostname of sender of the message. + +## `.usermask` + +```perl6 + say $msg.usermask; +``` + +Nick, username, and host combined into a full usermask, e.g. +`Zoffix!zoffix@zoffix.com` + +## `.reply` + +```perl6 + $msg.reply: 'I love you too' + if $msg.text ~~ /'I love you'/; +``` + +Replies back to a message. For example, if we received the message as a +private message to us, the reply will be a private message back to the +user. Same for notices. For in-channel messages, `irc-addressed` +and `irc-to-me` will address the sender in return, while all other in-channel +events will not. + +**NOTE:** this method is only available for these events: + +* `irc-privmsg` +* `irc-notice` +* `irc-to-me` +* `irc-addressed` +* `irc-mentioned` +* `irc-privmsg-channel` +* `irc-privmsg-me` +* `irc-notice-channel` +* `irc-privmsg-me` + +# Convenience Events + +These sets of events do not have a corresponding IRC command defined by the +protocol and instead are offered to make listening for a specific kind +of events easier. + +## `irc-to-me` + +```perl6 + # :zoffix!zoffix@127.0.0.1 PRIVMSG zoffix2 :hello + # :zoffix!zoffix@127.0.0.1 NOTICE zoffix2 :hello + # :zoffix!zoffix@127.0.0.1 PRIVMSG #perl6 :zoffix2, hello + + method irc-to-me ($msg) { + printf "%s told us `%s` using %s\n", + .nick, .text, .how given $msg; + } +``` + +Emitted when a user sends us a message as a private message, notice, or +addresses us in a channel. The `.respond` method of the Message +Object is the most convenient way to respond back to the sender of the message. + +The `.how` method returns a `Pair` where the key is the message type used +(`PRIVMSG` or `NOTICE`) and the value is the addressee of that message +(a channel or us). + +## `irc-addressed` + +```perl6 + # :zoffix!zoffix@127.0.0.1 PRIVMSG #perl6 :zoffix2, hello + + method irc-addressed ($msg) { + printf "%s told us `%s` in channel %s\n", + .nick, .text, .channel given $msg; + } +``` + +Emitted when a user addresses us in a channel. Specifically, this means +their message starts with our nickname, followed by optional comma or colon, +followed by whitespace. That prefix will be stripped from the message. + +## `irc-mentioned` + +```perl6 + # :zoffix!zoffix@127.0.0.1 PRIVMSG #perl6 :Is zoffix2 a robot? + + method irc-mentioned ($msg) { + printf "%s mentioned us in channel %s when they said %s\n", + .nick, .channel, .text given $msg; + } +``` + +Emitted when a user mentions us in a channel. Specifically, this means +their message contains our nickname separated by a word boundary on each side. + +## `irc-privmsg-channel` + +```perl6 + # :zoffix!zoffix@127.0.0.1 PRIVMSG #perl6 :hello + + method irc-privmsg-channel ($msg) { + printf "%s said `%s` to channel %s\n", + .nick, .text, .channel given $msg; + } +``` + +Emitted when a user sends a message to a channel. + +## `irc-privmsg-me` + +```perl6 + # :zoffix!zoffix@127.0.0.1 PRIVMSG zoffix2 :hey bruh + + method irc-privmsg-me ($msg) { + printf "%s messaged us: %s\n", .nick, .text given $msg; + } +``` + +Emitted when a user sends us a private message. + +## `irc-notice-channel` + +```perl6 + # :zoffix!zoffix@127.0.0.1 NOTICE #perl6 :Notice me! + + method irc-notice-channel ($msg) { + printf "%s sent a notice `%s` to channel %s\n", + .nick, .text, .channel given $msg; + } +``` + +Emitted when a user sends a notice to a channel. + +## `irc-notice-me` + +```perl6 + # :zoffix!zoffix@127.0.0.1 NOTICE zoffix2 :did you notice me? + + method irc-notice-me ($msg) { + printf "%s sent us a notice: %s\n", .nick, .text given $msg; + } +``` + +Emitted when a user sends us a private notice. + +## `irc-started` + +```perl6 + method irc-started { + $.do-some-sort-of-init-setup; + } +``` + +Emitted when the IRC client is started. Useful for doing setup work, like +initializing database connections, etc. Note: this event will fire only once, +even if the client reconnects to the server numerous times. Note that +unlike most events, this event does *not* receive a Message Object. +**IMPORTANT:** when this event fires, there's no guarantee we even started a +connection to the server, let alone connected successfully. + +## `irc-connected` + +```perl6 + method irc-connected { + $.do-some-sort-of-per-connection-setup; + } +``` + +Similar to `irc-started`, except will be emitted every time a +*successful* connection to the server is made and we joined all +of the requested channels. That is, we'll wait to either receive the +full user list or error message for each of the channels we're joining. +Note that unlike most events, this event does *not* receive a Message Object. + +## `irc-mode-channel` + +```perl6 + # :zoffix!zoffix@127.0.0.1 MODE #perl6 +o zoffix2 + # :zoffix!zoffix@127.0.0.1 MODE #perl6 +bbb Foo!*@* Bar!*@* Ber!*@* + + method irc-mode-channel ($msg) { + printf "Nick %s with usermask %s set mode(s) %s in channel %s\n", + .nick, .usermask, .modes, .channel given $msg; + } +``` + +Emitted when IRC `MODE` command is received and it's being operated on a +channel, see `irc-mode` event for details. + +## `irc-mode-user` + +```perl6 + # :zoffix2!f@127.0.0.1 MODE zoffix2 +w + + method irc-mode-user ($msg) { + printf "Nick %s with usermask %s set mode(s) %s on user %s\n", + .nick, .usermask, .modes, .who given $msg; + } +``` + +Emitted when IRC `MODE` command is received and it's being operated on a +user, see `irc-mode` event for details. + +## `irc-all` + +```perl6 + method irc-all ($msg) { + say "Received an event: $msg.perl()"; + return IRC_NEXT; + } +``` + +Emitted for all events and is mostly useful for debugging. The type of the +message object received will depend on the type of the event that generated +the message. This event will be triggered *AFTER* all other event handlers +in the current plugin are processed. + +# Numeric Events + +Numeric IRC events can be subscribed to by defining a method with name +`irc-` followed by the numeric code of the event (e.g. `irc-001`). The +arguments of the event can be accessed via `.args` method that returns a +list of strings: + +```perl6 + method irc-004 ($msg) { + say "Here are the arguments of the RPL_MYINFO event:"; + .say for $msg.args; + } +``` + +See [this reference](https://www.alien.net.au/irc/irc2numerics.html) for +a detailed list of numerics and their arguments available in the wild. Note: +the client will emit an event for any received numeric with a 3-digit +code, regardless of whether it is listed in that reference. + +# Named Events + +## `irc-nick` + +```perl6 + # :zoffix!zoffix@127.0.0.1 NICK not-zoffix + + method irc-nick ($msg) { + printf "%s changed nickname to %s\n", .nick, .new-nick given $msg; + } +``` + +[RFC 2812, 3.1.2](https://tools.ietf.org/html/rfc2812#section-3.1.2). +Emitted when a user changes their nickname. + +## `irc-quit` + +```perl6 + # :zoffix!zoffix@127.0.0.1 QUIT :Quit: Leaving + + method irc-quit ($msg) { + printf "%s has quit (%s)\n", .nick, .reason given $msg; + } +``` + +[RFC 2812, 3.1.7](https://tools.ietf.org/html/rfc2812#section-3.1.7). +Emitted when a user quits the server. + +## `irc-join` + +```perl6 + # :zoffix!zoffix@127.0.0.1 JOIN :#perl6 + + method irc-join ($msg) { + printf "%s joined channel %s\n", .nick, .channel given $msg; + } +``` + +[RFC 2812, 3.2.1](https://tools.ietf.org/html/rfc2812#section-3.2.1). +Emitted when a user joins a channel. + +## `irc-part` + +```perl6 + # :zoffix!zoffix@127.0.0.1 PART #perl6 :Leaving + + method irc-part ($msg) { + printf "%s left channel %s (%s)\n", .nick, .channel, .reason given $msg; + } +``` + +[RFC 2812, 3.2.2](https://tools.ietf.org/html/rfc2812#section-3.2.2). +Emitted when a user leaves a channel. + +## `irc-mode` + +```perl6 + # :zoffix!zoffix@127.0.0.1 MODE #perl6 +o zoffix2 + # :zoffix!zoffix@127.0.0.1 MODE #perl6 +bbb Foo!*@* Bar!*@* Ber!*@* + # :zoffix2!f@127.0.0.1 MODE zoffix2 +w + + method irc-mode ($msg) { + if $msg.?channel { + # channel mode change + printf "%s set mode(s) %s in channel %s\n", + .nick, .modes, .channel given $msg; + } + else { + # user mode change + printf "%s set mode(s) %s on user %s\n", + .nick, .modes, .who given $msg; + } + } +``` + +[RFC 2812, 3.1.5](https://tools.ietf.org/html/rfc2812#section-3.1.5)/[RFC 2812, 3.2.3](https://tools.ietf.org/html/rfc2812#section-3.2.3). +Emitted when IRC `MODE` command is received. As the command is dual-purpose, +the message object will have either `.channel` method available +(for channel mode changes) or `.who` method (for user mode changes). See +also `irc-mode-channel` and `irc-mode-user` convenience events. + +For channel modes, the `.modes` method returns a list of `Pair` where key +is the mode set and the value is the argument for that mode (i.e. "limit", +"user", or "banmask") or an empty string if the mode takes no arguments. + +For user modes, the `.modes` method returns a list of `Str` of the modes +set. + +The received message object will be one of the subclasses of +`IRC::Client::Message::Mode` object: `IRC::Client::Message::Mode::Channel` +or `IRC::Client::Message::Mode::User`. + +## `irc-topic` + +```perl6 + # :zoffix!zoffix@127.0.0.1 TOPIC #perl6 :meow + + method irc-topic ($msg) { + printf "%s set topic of channel %s to %s\n", + .nick, .channel, .topic given $msg; + } +``` + +[RFC 2812, 3.2.4](https://tools.ietf.org/html/rfc2812#section-3.2.4). +Emitted when a user changes the topic of a channel. + +## `irc-invite` + +```perl6 + # :zoffix!zoffix@127.0.0.1 INVITE zoffix2 :#perl6 + + method irc-invite ($msg) { + printf "%s invited us to channel %s\n", .nick, .channel given $msg; + } +``` + +[RFC 2812, 3.2.7](https://tools.ietf.org/html/rfc2812#section-3.2.7). +Emitted when a user invites us to a channel. + +## `irc-kick` + +```perl6 + # :zoffix!zoffix@127.0.0.1 KICK #perl6 zoffix2 :go away + + method irc-kick ($msg) { + printf "%s kicked %s out of %s (%s)\n", + .nick, .who, .channel, .reason given $msg; + } +``` + +[RFC 2812, 3.2.8](https://tools.ietf.org/html/rfc2812#section-3.2.8). +Emitted when someone kicks a user out of a channel. + +## `irc-privmsg` + +```perl6 + # :zoffix!zoffix@127.0.0.1 PRIVMSG #perl6 :hello + # :zoffix!zoffix@127.0.0.1 PRIVMSG zoffix2 :hey bruh + + method irc-privmsg ($msg) { + if $msg.?channel { + # message sent to a channel + printf "%s said `%s` to channel %s\n", + .nick, .text, .channel given $msg; + } + else { + # private message + printf "%s messaged us: %s\n", .nick, .text given $msg; + } + } +``` + +[RFC 2812, 3.3.1](https://tools.ietf.org/html/rfc2812#section-3.3.1). +Emitted when a user sends a message either to a channel +or a private message to us. See *Convenience Events* section for a number +of more convenient ways to listen to messages. + +## `irc-notice` + +```perl6 + # :zoffix!zoffix@127.0.0.1 NOTICE #perl6 :Notice me! + # :zoffix!zoffix@127.0.0.1 NOTICE zoffix2 :did you notice me? + + method irc-notice ($msg) { + if $msg.?channel { + # notice sent to a channel + printf "%s sent a notice `%s` to channel %s\n", + .nick, .text, .channel given $msg; + } + else { + # private notice + printf "%s sent us a notice: %s\n", .nick, .text given $msg; + } + } +``` + +[RFC 2812, 3.3.2](https://tools.ietf.org/html/rfc2812#section-3.3.2). +Emitted when a user sends a notice either to a channel +or a private notice to us. See *Convenience Events* section for a number +of more convenient ways to listen to notices and messages. + +# Custom Events + +There is support for custom events. A custom event is emitted by calling +`.emit-custom` method on the Client Object and is subscribed to via +`irc-custom-*` methods: + +```perl6 + $.irc.emit-custom: 'my-event', 'just', 'some', :args; + ... + method irc-custom-my-event ($just, $some, :$args) { } +``` + +No Message Object is involved in custom events. diff --git a/historical-archive/DESIGN/specs-and-references.md b/historical-archive/DESIGN/specs-and-references.md new file mode 100644 index 0000000..917e606 --- /dev/null +++ b/historical-archive/DESIGN/specs-and-references.md @@ -0,0 +1,17 @@ + +# Specs + +* [Numerics and other awesome info](https://www.alien.net.au/irc/) +* [RFC 1459](https://tools.ietf.org/html/rfc1459) +* [RFC 2810](https://tools.ietf.org/html/rfc2810) +* [RFC 2811](https://tools.ietf.org/html/rfc2811) +* [RFC 2812](https://tools.ietf.org/html/rfc2812) +* [RFC 2813](https://tools.ietf.org/html/rfc2813) +* [WebIRC](https://irc.wiki/WebIRC) +* [CTCP SPEC](http://cpansearch.perl.org/src/HINRIK/POE-Component-IRC-6.78/docs/ctcpspec.html) +* [DCC Description](http://www.irchelp.org/irchelp/rfc/dccspec.html) +* [DCC2](https://tools.ietf.org/id/draft-smith-irc-dcc2-negotiation-00.txt) + +# Future + +IRCv3 group: http://ircv3.net/ -- cgit v1.1