cinch 1.0.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +25 -44
- data/examples/basic/autovoice.rb +1 -1
- data/examples/basic/join_part.rb +0 -4
- data/examples/plugins/autovoice.rb +2 -5
- data/examples/plugins/google.rb +1 -2
- data/examples/plugins/hooks.rb +36 -0
- data/examples/plugins/lambdas.rb +35 -0
- data/examples/plugins/last_nick.rb +24 -0
- data/examples/plugins/multiple_matches.rb +1 -10
- data/examples/plugins/own_events.rb +37 -0
- data/examples/plugins/timer.rb +22 -0
- data/examples/plugins/url_shorten.rb +1 -1
- data/lib/cinch.rb +50 -1
- data/lib/cinch/ban.rb +5 -2
- data/lib/cinch/bot.rb +360 -193
- data/lib/cinch/cache_manager.rb +15 -0
- data/lib/cinch/callback.rb +6 -0
- data/lib/cinch/channel.rb +150 -96
- data/lib/cinch/channel_manager.rb +26 -0
- data/lib/cinch/constants.rb +6 -4
- data/lib/cinch/exceptions.rb +9 -0
- data/lib/cinch/irc.rb +197 -82
- data/lib/cinch/logger/formatted_logger.rb +8 -8
- data/lib/cinch/logger/zcbot_logger.rb +37 -0
- data/lib/cinch/mask.rb +17 -3
- data/lib/cinch/message.rb +14 -7
- data/lib/cinch/message_queue.rb +8 -4
- data/lib/cinch/mode_parser.rb +56 -0
- data/lib/cinch/pattern.rb +45 -0
- data/lib/cinch/plugin.rb +129 -34
- data/lib/cinch/rubyext/string.rb +4 -4
- data/lib/cinch/syncable.rb +8 -0
- data/lib/cinch/user.rb +68 -13
- data/lib/cinch/user_manager.rb +60 -0
- metadata +17 -35
- data/Rakefile +0 -66
- data/lib/cinch/PLANNED +0 -4
- data/spec/bot_spec.rb +0 -5
- data/spec/channel_spec.rb +0 -5
- data/spec/cinch_spec.rb +0 -5
- data/spec/irc_spec.rb +0 -5
- data/spec/message_spec.rb +0 -5
- data/spec/plugin_spec.rb +0 -5
- data/spec/spec.opts +0 -2
- data/spec/spec_helper.rb +0 -8
- data/spec/user_spec.rb +0 -5
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Cinch -
|
1
|
+
Cinch - The IRC Bot Building Framework
|
2
2
|
=====================================
|
3
3
|
|
4
4
|
Description
|
@@ -17,8 +17,6 @@ If you'd like to test your own Cinch experiments you can do so in the
|
|
17
17
|
[irc.freenode.org](irc://irc.freenode.org/cinch-bots). For general
|
18
18
|
support, join [#cinch](irc://irc.freenode.org/cinch).
|
19
19
|
|
20
|
-
This original document can be found [here](http://doc.injekt.net/cinch).
|
21
|
-
|
22
20
|
Installation
|
23
21
|
------------
|
24
22
|
|
@@ -32,7 +30,7 @@ You can install the latest Cinch gem using RubyGems
|
|
32
30
|
|
33
31
|
Alternatively you can check out the latest code directly from Github
|
34
32
|
|
35
|
-
git clone http://github.com/
|
33
|
+
git clone http://github.com/cinchrb/cinch.git
|
36
34
|
|
37
35
|
Example
|
38
36
|
-------
|
@@ -61,54 +59,37 @@ Features
|
|
61
59
|
|
62
60
|
### Documentation
|
63
61
|
|
64
|
-
Cinch provides a documented API, which is online for your viewing pleasure
|
62
|
+
Cinch provides a documented API, which is online for your viewing pleasure
|
63
|
+
[here](http://rubydoc.info/github/cinchrb/cinch/master/).
|
65
64
|
|
66
65
|
### Object Oriented
|
67
66
|
|
68
|
-
Many IRC bots (and there are, so
|
69
|
-
advantage of the awesome Object Oriented Interface which most Ruby
|
70
|
-
become accustomed to and grown to love.
|
67
|
+
Many IRC bots (and there are, **so** many) are great, but we see so little of
|
68
|
+
them take advantage of the awesome Object Oriented Interface which most Ruby
|
69
|
+
programmers will have become accustomed to and grown to love.
|
71
70
|
|
72
|
-
Well, Cinch uses this functionality to
|
73
|
-
a reference to a channel or a user, to another method, which then
|
74
|
-
another method (by which time you're confused about what's
|
75
|
-
an OOP interface for even the simpliest of tasks,
|
76
|
-
to comprehend.
|
71
|
+
Well, Cinch uses this functionality to its advantage. Rather than having to
|
72
|
+
pass around a reference to a channel or a user, to another method, which then
|
73
|
+
passes it to another method (by which time you're confused about what's
|
74
|
+
going on) -- Cinch provides an OOP interface for even the simpliest of tasks,
|
75
|
+
making your code simple and easy to comprehend.
|
77
76
|
|
78
77
|
### Threaded
|
79
78
|
|
80
|
-
Unlike a lot of popular IRC frameworks, Cinch is threaded. But wait, don't let
|
81
|
-
scare you. It's totally easy to grasp.
|
82
|
-
|
83
|
-
Each of Cinch's plugins and handlers are executed in their own personal thread. This
|
84
|
-
means the main thread can stay focused on what it does best, providing non-blocking
|
85
|
-
reading and writing to an IRC server. This will prevent your bot from locking up
|
86
|
-
when one of your plugins starts doing some intense operations. Damn that's handy.
|
87
|
-
|
88
|
-
### Key/Value Store
|
79
|
+
Unlike a lot of popular IRC frameworks, Cinch is threaded. But wait, don't let
|
80
|
+
that scare you. It's totally easy to grasp.
|
89
81
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
end
|
96
|
-
|
97
|
-
on :message, /^add friend (.+)$/ do |m, friend|
|
98
|
-
store[:friends] << friend
|
99
|
-
end
|
100
|
-
|
101
|
-
on :message /^get friends$/ do |m|
|
102
|
-
m.reply "Your friends are: #{store[:friends].join(', ')}"
|
103
|
-
end
|
104
|
-
|
105
|
-
Neat, right?
|
82
|
+
Each of Cinch's plugins and handlers are executed in their own personal thread.
|
83
|
+
This means the main thread can stay focused on what it does best, providing
|
84
|
+
non-blocking reading and writing to an IRC server. This will prevent your bot
|
85
|
+
from locking up when one of your plugins starts doing some intense operations.
|
86
|
+
Damn that's handy.
|
106
87
|
|
107
88
|
### Plugins
|
108
89
|
|
109
|
-
That's right folks, Cinch provides a modular based plugin system. This is a
|
110
|
-
many people have bugged us about for a long time. It's finally here,
|
111
|
-
as awesome as you had hoped!
|
90
|
+
That's right folks, Cinch provides a modular based plugin system. This is a
|
91
|
+
feature many people have bugged us about for a long time. It's finally here,
|
92
|
+
and it's as awesome as you had hoped!
|
112
93
|
|
113
94
|
This system allows you to create feature packed plugins without interfering with
|
114
95
|
any of the Cinch internals. Everything in your plugin is self contained, meaning
|
@@ -180,13 +161,13 @@ Please note that although we very much appreciate all of your efforts, Cinch
|
|
180
161
|
will not accept patches in aid of Ruby 1.8 compatibility. We have no intention
|
181
162
|
of supporting Ruby versions below 1.9.1.
|
182
163
|
|
183
|
-
Fork the project, implement your awesome feature in
|
164
|
+
Fork the project, implement your awesome feature in its own branch, and send
|
184
165
|
a pull request to one of the Cinch collaborators. We'll be more than happy
|
185
166
|
to check it out.
|
186
167
|
|
187
|
-
Just remember, no specs, no cookies!
|
188
|
-
|
189
168
|
### Contributors
|
190
169
|
- darix <darix [at] nordisch.org> (wrote the message splitting algorithm)
|
191
170
|
- robgleeson (thanks for testing, contributing a lot of ideas,
|
192
171
|
discussing design decisions etc)
|
172
|
+
- Emil Loer (http://github.com/thedjinn) for improving the handling of
|
173
|
+
unexpected disconnects and reconnects.
|
data/examples/basic/autovoice.rb
CHANGED
data/examples/basic/join_part.rb
CHANGED
@@ -9,7 +9,7 @@ require 'cinch'
|
|
9
9
|
class Autovoice
|
10
10
|
include Cinch::Plugin
|
11
11
|
listen_to :join
|
12
|
-
match /autovoice (on|off)
|
12
|
+
match /autovoice (on|off)$/
|
13
13
|
|
14
14
|
def listen(m)
|
15
15
|
unless m.user.nick == bot.nick
|
@@ -28,13 +28,10 @@ bot = Cinch::Bot.new do
|
|
28
28
|
configure do |c|
|
29
29
|
c.nick = "cinch_autovoice"
|
30
30
|
c.server = "irc.freenode.org"
|
31
|
+
c.channels = ["#cinch-bots"]
|
31
32
|
c.verbose = true
|
32
33
|
c.plugins.plugins = [Autovoice]
|
33
34
|
end
|
34
|
-
|
35
|
-
on :connect do
|
36
|
-
bot.join "#cinch"
|
37
|
-
end
|
38
35
|
end
|
39
36
|
|
40
37
|
bot.start
|
data/examples/plugins/google.rb
CHANGED
@@ -14,10 +14,9 @@ class Google
|
|
14
14
|
title = res.text
|
15
15
|
link = res.at('a')[:href]
|
16
16
|
desc = res.at("./following::div").children.first.text
|
17
|
+
CGI.unescape_html "#{title} - #{desc} (#{link})"
|
17
18
|
rescue
|
18
19
|
"No results found"
|
19
|
-
else
|
20
|
-
CGI.unescape_html "#{title} - #{desc} (#{link})"
|
21
20
|
end
|
22
21
|
|
23
22
|
def execute(m, query)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'cinch'
|
3
|
+
|
4
|
+
class HooksDemo
|
5
|
+
include Cinch::Plugin
|
6
|
+
|
7
|
+
hook :pre, method: :generate_random_number
|
8
|
+
def generate_random_number(m)
|
9
|
+
# Hooks are called in the same thread as the handler and thus
|
10
|
+
# using thread local variables is possible.
|
11
|
+
Thread.current[:rand] = Kernel.rand
|
12
|
+
end
|
13
|
+
|
14
|
+
hook :post, method: :cheer
|
15
|
+
def cheer(m)
|
16
|
+
m.reply "Yay, I successfully ran a command…"
|
17
|
+
end
|
18
|
+
|
19
|
+
match "rand"
|
20
|
+
def execute(m)
|
21
|
+
m.reply "Random number: " + Thread.current[:rand].to_s
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
bot = Cinch::Bot.new do
|
26
|
+
configure do |c|
|
27
|
+
c.nick = "cinch_hooks"
|
28
|
+
c.server = "irc.freenode.org"
|
29
|
+
c.channels = ["#cinch-bots"]
|
30
|
+
c.verbose = true
|
31
|
+
c.plugins.plugins = [HooksDemo]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
bot.start
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'cinch'
|
2
|
+
|
3
|
+
class DirectAddressing
|
4
|
+
include Cinch::Plugin
|
5
|
+
|
6
|
+
# Note: the lambda will be executed in the context it has been
|
7
|
+
# defined in, in this case the class DirectAddressing (and not an
|
8
|
+
# instance of said class).
|
9
|
+
#
|
10
|
+
# The reason we are using a lambda is that the bot's nick can change
|
11
|
+
# and the prefix has to be up to date.
|
12
|
+
prefix lambda{ |m| m.bot.nick + ": " }
|
13
|
+
|
14
|
+
match "hello", method: :greet
|
15
|
+
def greet(m)
|
16
|
+
m.reply "Hello to you, too."
|
17
|
+
end
|
18
|
+
|
19
|
+
match "rename", method: :rename
|
20
|
+
def rename(m)
|
21
|
+
@bot.nick += "_"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
bot = Cinch::Bot.new do
|
26
|
+
configure do |c|
|
27
|
+
c.nick = "cinch_lambda"
|
28
|
+
c.server = "irc.freenode.org"
|
29
|
+
c.channels = ["#cinch-bots"]
|
30
|
+
c.verbose = true
|
31
|
+
c.plugins.plugins = [DirectAddressing]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
bot.start
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'cinch'
|
2
|
+
|
3
|
+
class Nickchange
|
4
|
+
include Cinch::Plugin
|
5
|
+
listen_to :nick
|
6
|
+
|
7
|
+
def listen(m)
|
8
|
+
# This will send a PM to the user who changed his nick and inform
|
9
|
+
# him of his old nick.
|
10
|
+
m.reply "Your old nick was: #{m.user.last_nick}" ,true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
bot = Cinch::Bot.new do
|
15
|
+
configure do |c|
|
16
|
+
c.nick = "cinch_nickchange"
|
17
|
+
c.server = "irc.freenode.org"
|
18
|
+
c.channels = ["#cinch-bots"]
|
19
|
+
c.verbose = true
|
20
|
+
c.plugins.plugins = [Nickchange]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
bot.start
|
@@ -1,11 +1,5 @@
|
|
1
1
|
require 'cinch'
|
2
2
|
|
3
|
-
# Give this bot ops in a channel and it'll auto voice
|
4
|
-
# visitors
|
5
|
-
#
|
6
|
-
# Enable with !autovoice on
|
7
|
-
# Disable with !autovoice off
|
8
|
-
|
9
3
|
class MultiCommands
|
10
4
|
include Cinch::Plugin
|
11
5
|
match /command1 (.+)/, method: :command1
|
@@ -29,13 +23,10 @@ bot = Cinch::Bot.new do
|
|
29
23
|
configure do |c|
|
30
24
|
c.nick = "cinch_multi"
|
31
25
|
c.server = "irc.freenode.org"
|
26
|
+
c.channels = ["#cinch-bots"]
|
32
27
|
c.verbose = true
|
33
28
|
c.plugins.plugins = [MultiCommands]
|
34
29
|
end
|
35
|
-
|
36
|
-
on :connect do
|
37
|
-
bot.join "#dominikh"
|
38
|
-
end
|
39
30
|
end
|
40
31
|
|
41
32
|
bot.start
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'cinch'
|
2
|
+
|
3
|
+
class RandomNumberGenerator
|
4
|
+
def initialize(bot)
|
5
|
+
@bot = bot
|
6
|
+
end
|
7
|
+
|
8
|
+
def start
|
9
|
+
while true
|
10
|
+
sleep 5 # pretend that we are waiting for some kind of entropy
|
11
|
+
@bot.dispatch(:random_number, nil, Kernel.rand)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class DoSomethingRandom
|
17
|
+
include Cinch::Plugin
|
18
|
+
|
19
|
+
listen_to :random_number
|
20
|
+
def listen(m, number)
|
21
|
+
Channel("#cinch-bots").send "I got a random number: #{number}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
bot = Cinch::Bot.new do
|
26
|
+
configure do |c|
|
27
|
+
c.nick = "cinch_events"
|
28
|
+
c.server = "irc.freenode.org"
|
29
|
+
c.channels = ["#cinch-bots"]
|
30
|
+
c.verbose = true
|
31
|
+
c.plugins.plugins = [DoSomethingRandom]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
Thread.new { RandomNumberGenerator.new(bot).start }
|
37
|
+
bot.start
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'cinch'
|
2
|
+
|
3
|
+
class TimedPlugin
|
4
|
+
include Cinch::Plugin
|
5
|
+
|
6
|
+
timer 5, method: :timed
|
7
|
+
def timed
|
8
|
+
Channel("#cinch-bots").send "5 seconds have passed"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
bot = Cinch::Bot.new do
|
13
|
+
configure do |c|
|
14
|
+
c.nick = "cinch_timer"
|
15
|
+
c.server = "irc.freenode.org"
|
16
|
+
c.channels = ["#cinch-bots"]
|
17
|
+
c.verbose = true
|
18
|
+
c.plugins.plugins = [TimedPlugin]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
bot.start
|
data/lib/cinch.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'cinch/bot'
|
2
2
|
|
3
3
|
module Cinch
|
4
|
-
VERSION = '1.0
|
4
|
+
VERSION = '1.1.0'
|
5
5
|
|
6
6
|
# @return [String]
|
7
7
|
# @todo Handle mIRC color codes more gracefully.
|
@@ -9,4 +9,53 @@ module Cinch
|
|
9
9
|
def self.filter_string(string)
|
10
10
|
string.gsub(/[\x00-\x1f]/, '')
|
11
11
|
end
|
12
|
+
|
13
|
+
# @api private
|
14
|
+
def self.encode_incoming(string, encoding)
|
15
|
+
string = string.dup
|
16
|
+
if encoding == :irc
|
17
|
+
# If incoming text is valid UTF-8, it will be interpreted as
|
18
|
+
# such. If it fails validation, a CP1252 -> UTF-8 conversion
|
19
|
+
# is performed. This allows you to see non-ASCII from mIRC
|
20
|
+
# users (non-UTF-8) and other users sending you UTF-8.
|
21
|
+
#
|
22
|
+
# (from http://xchat.org/encoding/#hybrid)
|
23
|
+
string.force_encoding("UTF-8")
|
24
|
+
if !string.valid_encoding?
|
25
|
+
string.force_encoding("CP1252").encode!("UTF-8", {:invalid => :replace, :undef => :replace})
|
26
|
+
end
|
27
|
+
else
|
28
|
+
string.force_encoding(encoding).encode!({:invalid => :replace, :undef => :replace})
|
29
|
+
string = string.chars.select { |c| c.valid_encoding? }.join
|
30
|
+
end
|
31
|
+
|
32
|
+
return string
|
33
|
+
end
|
34
|
+
|
35
|
+
# @api private
|
36
|
+
def self.encode_outgoing(string, encoding)
|
37
|
+
string = string.dup
|
38
|
+
if encoding == :irc
|
39
|
+
# If your text contains only characters that fit inside the CP1252
|
40
|
+
# code page (aka Windows Latin-1), the entire line will be sent
|
41
|
+
# that way. mIRC users should see it correctly. XChat users who
|
42
|
+
# are using UTF-8 will also see it correctly, because it will fail
|
43
|
+
# UTF-8 validation and will be assumed to be CP1252, even by older
|
44
|
+
# XChat versions.
|
45
|
+
#
|
46
|
+
# If the text doesn't fit inside the CP1252 code page, (for eaxmple if you
|
47
|
+
# type Eastern European characters, or Russian) it will be sent as UTF-8. Only
|
48
|
+
# UTF-8 capable clients will be able to see these characters correctly
|
49
|
+
#
|
50
|
+
# (from http://xchat.org/encoding/#hybrid)
|
51
|
+
begin
|
52
|
+
string.encode!("CP1252")
|
53
|
+
rescue Encoding::UndefinedConversionError
|
54
|
+
end
|
55
|
+
else
|
56
|
+
string.encode!(encoding, {:invalid => :replace, :undef => :replace})
|
57
|
+
end
|
58
|
+
|
59
|
+
return string
|
60
|
+
end
|
12
61
|
end
|
data/lib/cinch/ban.rb
CHANGED
@@ -4,15 +4,18 @@ module Cinch
|
|
4
4
|
# @return [Mask, String]
|
5
5
|
attr_reader :mask
|
6
6
|
|
7
|
-
# @return [
|
7
|
+
# @return [User]
|
8
8
|
attr_reader :by
|
9
9
|
|
10
10
|
# @return [Time]
|
11
11
|
attr_reader :created_at
|
12
12
|
|
13
|
-
# @return [Boolean]
|
13
|
+
# @return [Boolean] whether this is an extended ban (as used by for example Freenode)
|
14
14
|
attr_reader :extended
|
15
15
|
|
16
|
+
# @param [String] mask The mask
|
17
|
+
# @param [User] by The user who created the ban
|
18
|
+
# @param [Time] at The time at which the ban was created
|
16
19
|
def initialize(mask, by, at)
|
17
20
|
@by, @created_at = by, at
|
18
21
|
if mask =~ /^\$/
|