aboutsummaryrefslogtreecommitdiff
path: root/historical-archive
diff options
context:
space:
mode:
authorZoffix Znet <cpan@zoffix.com>2016-07-26 13:58:52 -0400
committerZoffix Znet <cpan@zoffix.com>2016-07-26 13:58:52 -0400
commit156800544027a88f68670a47c8fb702843173db5 (patch)
tree78a8c1e03a46cc2a0dd2ec3e46e16d56a49cf6bf /historical-archive
parentb44c8425ae1d8cde9907723e99960fa4a72be38a (diff)
Move DESIGN to historical-archive so no one thinks it is current
Diffstat (limited to 'historical-archive')
-rw-r--r--historical-archive/DESIGN/01-main.md961
-rw-r--r--historical-archive/DESIGN/specs-and-references.md17
2 files changed, 978 insertions, 0 deletions
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<ZofBot ZofBot_ ZofBot__> # nicks to try to use on ALL servers,
+ :servers(
+ s-leliana => (
+ :server<irc.freenode.net>,
+ :channels<#perl #perl6 #perl7>
+ ),
+ s-morrigan => (
+ :server<irc.perl.org>,
+ :channels<#perl #perl-help>
+ ),
+ s-alistair => (
+ :nick<Party Party_ Party__> # nick override
+ :server<irc.perl6.pary>,
+ :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+ $<nick>=\S+/ {
+ $msg.reply: "I don't see $<nick> up in here"
+ unless $.irc.channel($msg.channel).?has: ~$<nick>;
+ }
+
+ if $msg.text ~~ /'topic' \s+ $<channel>=\S+/ {
+ return $msg.reply: $_
+ ?? "Channel $<channel> does not exist"
+ !! "Topic in $<channel> is $_.topic()"
+ given $.irc.channel: ~$<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/