aboutsummaryrefslogtreecommitdiff
path: root/README.md
blob: f05c83ec0083933292b5293698c7c54d553d66b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# irsc

[![travis-ci.org](https://travis-ci.org/tilpner/irsc.svg?branch=master)](https://travis-ci.org/tilpner/irsc)
[![crates.io](http://meritbadge.herokuapp.com/irsc)](https://crates.io/crates/irsc)

## Overview

This library is supposed to be a thin layer over the IRC protocol, doing all the network IO and event parsing for you.

## Features

- Somewhat complete implementation of [RFC2812](http://tools.ietf.org/html/rfc2812)
- Some CTCP support
- SSL for connections
- Callback and Event-Stream API
- Colors/bolding/etc.

### Planned

- Higher-level wrapper, directly aimed at writing bots
- Lots of tests
- More documentation (yeah, sure)

## Example

Compiles and tested with `rustc 1.3.0-nightly (912ab64a0 2015-06-25)` and `8537b61f38fd3976d47b153f3548f77896e42eb6` of this library.

Run with

    cargo run --example 02 --features ssl

and join [#botzoo on irc.mozilla.org](http://irc.lc/mozilla/botzoo).

```rust
extern crate irsc;
extern crate openssl;
extern crate env_logger;

use irsc::*;
use irsc::Command::*;
use irsc::Reply::*;

use openssl::ssl::{ Ssl, SslContext, SslMethod };

// Here should be the constants of this bot, like the nickname or description,
// to avoid repetition. However, for better readability, the constants have been
// inlined below.

fn main() {
    // If the environment variable RUST_LOG is set to "info",
    // irsc will log incoming and outgoing data in raw form.
    // Documentation: http://rust-lang.github.io/log/env_logger/#enabling-logging
    env_logger::init().ok().expect("Failed to initialise env_logger");

    let mut s = OwnedClient::new();
    // Try to use Tlsv1 to connect. This might fail, depending on your version of
    // OpenSSL. This example does not try with other methods on failure.
    let ssl = Ssl::new(&SslContext::new(SslMethod::Tlsv1).unwrap()).unwrap();

    // Connect using the newly constructed Ssl configuration.
    // If Ssl is not desired, use .connect(...) without the ssl argument.
    s.connect_ssl("irc.mozilla.org", 6697, ssl);

    // Send the USER and NICK message in one go:
    // register(nick, user, description, [password])
    s.register("irsc02", "irsc", "Example bot 02", None);

    // The client must be accessible from anywhere when using the Event API.
    // .into_shared() will convert the previously owned client into a wrapper.
    let mut shared = s.into_shared();

    // `shared.commands()` will return a carboxyl stream of tuples of the form
    // (SharedClient, Message, Command), representing the incoming Commands.
    // Event streams are not lazy, but they must still be alive when the events happen,
    // in order to process them. To keep them alive (prevent dropping), we assing them
    // to local variables. Make sure to not ignore (variable name `_`) them, as that
    // drops them as well. The leading underscore will avoid unused-variable warnings.
    let _a = shared.commands()
        .map(|(mut cl, msg, c)| {
            // PRIVMSGs are the most common way to talk, they are used in queries but also
            // to talk in channels (despite the "PRIV"). This if-let makes sure we only handle
            // these PRIVMSG events for now, and allows us to access PRIVMSG-specific information,
            // like the addressee of the message and its content.
            if let PRIVMSG(to, content) = c {
                // The ident is unique to the origin of this message, and can be used
                // to retrieve the nickname of the sender. This example assumes all
                // hostmasks are well formed and always present.
                let from = msg.ident().unwrap();
                // This example will echo the input if the bot was mentioned.
                // Example:
                //   tilpner told me: irsc - Foo!
                // if "tilpner" wrote "irsc - Foo!" before.
                let response = format!("{} told me: {}", from.nickname, color::bold(&content));

                // Only send to global channels, to prevent recursion when we are pm'ed.
                // Technically, there are other prefixes than '#', but ignoring them is fine here.
                // Also, we only reply if we were mentioned at the start of the message.
                if to.starts_with("#") && content.starts_with("irsc") {
                    // `to` is not the nick who mentioned us, but the channel we were mentioned in,
                    // This will send our `response` to that channel.
                    cl.msg(&to, &response);
                }
            }
        });

    // `shared.replies()` will return a carboxyl stream of tuples of the form
    // (SharedClient, Message, Reply), representing the incoming Replies.
    // Again, make sure to keep the mapped stream alive.
    let _b = shared.replies()
        .map(|(mut cl, _msg, r)| {
            // Logging into the IRC server might take some seconds. Some libraries solve
            // this by having a fixed timeout after they logged in, before they continue
            // to e.g. join channels or identify with the services.
            // There is a more precise way, namely waiting for the RPL_WELCOME event (001),
            // that is sent by the IRCd after we've connected successfully.
            if let RPL_WELCOME(_) = r {
                // After we've connected successfully, we join a channel
                // without providing a password.
                cl.join("#botzoo", None);
            }
        });

    // Dedicate this thread to listening and event processing.
    // This method will only return after the connection has been closed or an
    // error was encountered, which is why it should either listen in a new thread,
    // or all necessary setup must be done prior to calling this method.
    shared.listen_with_events();
}
```