cinch 1.0.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/README.md +25 -44
  2. data/examples/basic/autovoice.rb +1 -1
  3. data/examples/basic/join_part.rb +0 -4
  4. data/examples/plugins/autovoice.rb +2 -5
  5. data/examples/plugins/google.rb +1 -2
  6. data/examples/plugins/hooks.rb +36 -0
  7. data/examples/plugins/lambdas.rb +35 -0
  8. data/examples/plugins/last_nick.rb +24 -0
  9. data/examples/plugins/multiple_matches.rb +1 -10
  10. data/examples/plugins/own_events.rb +37 -0
  11. data/examples/plugins/timer.rb +22 -0
  12. data/examples/plugins/url_shorten.rb +1 -1
  13. data/lib/cinch.rb +50 -1
  14. data/lib/cinch/ban.rb +5 -2
  15. data/lib/cinch/bot.rb +360 -193
  16. data/lib/cinch/cache_manager.rb +15 -0
  17. data/lib/cinch/callback.rb +6 -0
  18. data/lib/cinch/channel.rb +150 -96
  19. data/lib/cinch/channel_manager.rb +26 -0
  20. data/lib/cinch/constants.rb +6 -4
  21. data/lib/cinch/exceptions.rb +9 -0
  22. data/lib/cinch/irc.rb +197 -82
  23. data/lib/cinch/logger/formatted_logger.rb +8 -8
  24. data/lib/cinch/logger/zcbot_logger.rb +37 -0
  25. data/lib/cinch/mask.rb +17 -3
  26. data/lib/cinch/message.rb +14 -7
  27. data/lib/cinch/message_queue.rb +8 -4
  28. data/lib/cinch/mode_parser.rb +56 -0
  29. data/lib/cinch/pattern.rb +45 -0
  30. data/lib/cinch/plugin.rb +129 -34
  31. data/lib/cinch/rubyext/string.rb +4 -4
  32. data/lib/cinch/syncable.rb +8 -0
  33. data/lib/cinch/user.rb +68 -13
  34. data/lib/cinch/user_manager.rb +60 -0
  35. metadata +17 -35
  36. data/Rakefile +0 -66
  37. data/lib/cinch/PLANNED +0 -4
  38. data/spec/bot_spec.rb +0 -5
  39. data/spec/channel_spec.rb +0 -5
  40. data/spec/cinch_spec.rb +0 -5
  41. data/spec/irc_spec.rb +0 -5
  42. data/spec/message_spec.rb +0 -5
  43. data/spec/plugin_spec.rb +0 -5
  44. data/spec/spec.opts +0 -2
  45. data/spec/spec_helper.rb +0 -8
  46. data/spec/user_spec.rb +0 -5
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- Cinch - An IRC Bot Building Framework
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/injekt/cinch.git
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 [here](http://doc.injekt.net/cinch).
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 **many**) are great, but we see so little of them take
69
- advantage of the awesome Object Oriented Interface which most Ruby programmers will have
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 it's advantage. Rather than having to pass around
73
- a reference to a channel or a user, to another method, which then passes it to
74
- another method (by which time you're confused about what's going on) -- Cinch provides
75
- an OOP interface for even the simpliest of tasks, making your code simple and easy
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 that
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
- We have listened to your requests and implemented a bot-wide key/value store. You can
91
- now store data and use it across your handlers. Here's an example:
92
-
93
- configure do |c|
94
- store[:friends] = []
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 feature
110
- many people have bugged us about for a long time. It's finally here, and it's
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 it's own branch, and send
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 &lt;darix [at] nordisch.org&gt; (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.
@@ -22,7 +22,7 @@ bot = Cinch::Bot.new do
22
22
  end
23
23
  end
24
24
 
25
- on :channel, /^!autovoice (on|off)/ do |m, option|
25
+ on :channel, /^!autovoice (on|off)$/ do |m, option|
26
26
  @autovoice = option == "on"
27
27
 
28
28
  m.reply "Autovoice is now #{@autovoice ? 'enabled' : 'disabled'}"
@@ -10,10 +10,6 @@ bot = Cinch::Bot.new do
10
10
  @admin = "injekt"
11
11
  end
12
12
 
13
- on :connect do
14
- bot.join "#cinch"
15
- end
16
-
17
13
  helpers do
18
14
  def is_admin?(user)
19
15
  true if user.nick == @admin
@@ -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
@@ -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
@@ -25,7 +25,7 @@ end
25
25
  bot = Cinch::Bot.new do
26
26
  configure do |c|
27
27
  c.server = "irc.freenode.org"
28
- c.channels = ["#cinch"]
28
+ c.channels = ["#cinch-bots"]
29
29
  end
30
30
  end
31
31
 
@@ -1,7 +1,7 @@
1
1
  require 'cinch/bot'
2
2
 
3
3
  module Cinch
4
- VERSION = '1.0.2'
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 -&gt; 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
@@ -4,15 +4,18 @@ module Cinch
4
4
  # @return [Mask, String]
5
5
  attr_reader :mask
6
6
 
7
- # @return [String]
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 =~ /^\$/