isaac 0.2.2 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/README.rdoc +7 -14
  2. data/isaac.gemspec +3 -2
  3. data/lib/isaac.rb +116 -75
  4. metadata +6 -4
@@ -45,6 +45,13 @@ If you want to match private messages use the +on :private+ event:
45
45
  msg nick, "Login successful!"
46
46
  end
47
47
 
48
+ You can also pass the RegExp captures as block arguments:
49
+
50
+ on :channel, /catch this: (.*) and this: (.*)/ do |first, last|
51
+ # `first` will contain the first regexp capture,
52
+ # `last` the second.
53
+ end
54
+
48
55
  === Defining helpers
49
56
  Helpers should not be defined in the top level, but instead using the +helpers+-constructor:
50
57
 
@@ -67,20 +74,6 @@ Errors, as specified by RFC 1459, can be reacted upon as well. If you e.g. try t
67
74
 
68
75
  Available variables: +nick+ and +channel+.
69
76
 
70
- === Send commands from outside an event (not implemented in Shaft atm)
71
- You might want to send messages, join channels etc. without it strictly being the result of an on()-event, e.g. send a message every time a RSS feed is updated or whatever. You can use +Isaac.execute+ for that, and all your normal commands, +msg+, +join+, +topic+ etc. will be available:
72
-
73
- class K
74
- def smoke(brand)
75
- Isaac.execute { msg "harryjr", "you should smoke #{brand} cigarettes" }
76
- end
77
- end
78
-
79
- on :connect do
80
- k = K.new
81
- k.smoke("Lucky Strike")
82
- end
83
-
84
77
  == Contribute
85
78
  The source is hosted at GitHub: http://github.com/ichverstehe/isaac
86
79
 
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "isaac"
3
- s.version = "0.2.2"
4
- s.date = "2009-02-23"
3
+ s.version = "0.2.5"
4
+ s.date = "2009-04-25"
5
5
  s.summary = "The smallish DSL for writing IRC bots"
6
6
  s.email = "harry@vangberg.name"
7
7
  s.homepage = "http://github.com/ichverstehe/isaac"
@@ -16,3 +16,4 @@ Gem::Specification.new do |s|
16
16
  s.rdoc_options = ["--main", "README.rdoc"]
17
17
  s.extra_rdoc_files = ["LICENSE", "README.rdoc"]
18
18
  end
19
+
@@ -20,53 +20,29 @@ module Isaac
20
20
  instance_eval(&b) if block_given?
21
21
  end
22
22
 
23
- def start
24
- puts "========================================="
25
- puts "Connecting to #{@config.server}:#{@config.port}"
26
- @irc = IRC.new(self, @config)
27
- @irc.connect
28
- puts "========================================="
23
+ def configure(&b)
24
+ b.call(@config)
29
25
  end
30
26
 
31
- def on(event, match=//, &b)
27
+ def on(event, match=//, &block)
32
28
  match = match.to_s if match.is_a? Integer
33
- (@events[event] ||= []) << [Regexp.new(match), b]
29
+ (@events[event] ||= []) << [Regexp.new(match), block]
34
30
  end
35
31
 
36
32
  def helpers(&b)
37
- instance_eval &b
38
- end
39
-
40
- def configure(&b)
41
- b.call(@config)
42
- end
43
-
44
- def dispatch(event, env={})
45
- self.nick, self.userhost, self.channel, self.error =
46
- env[:nick], env[:userhost], env[:channel], env[:error]
47
- self.message = env[:message] || ""
48
-
49
- event = @events[event] && @events[event].detect do |regexp,_|
50
- message.match(regexp)
51
- end
52
-
53
- if event
54
- regexp, block = *event
55
- self.match = message.match(regexp).captures
56
- catch(:halt) { instance_eval(&block) }
57
- end
33
+ instance_eval(&b)
58
34
  end
59
35
 
60
36
  def halt
61
37
  throw :halt
62
38
  end
63
39
 
64
- def raw(m)
65
- @irc.message(m)
40
+ def raw(command)
41
+ @irc.message(command)
66
42
  end
67
43
 
68
- def msg(recipient, m)
69
- raw("PRIVMSG #{recipient} :#{m}")
44
+ def msg(recipient, text)
45
+ raw("PRIVMSG #{recipient} :#{text}")
70
46
  end
71
47
 
72
48
  def join(*channels)
@@ -80,6 +56,44 @@ module Isaac
80
56
  def topic(channel, text)
81
57
  raw("TOPIC #{channel} :#{text}")
82
58
  end
59
+
60
+ def start
61
+ puts "Connecting to #{@config.server}:#{@config.port}" unless @config.environment == :test
62
+ @irc = IRC.new(self, @config)
63
+ @irc.connect
64
+ end
65
+
66
+ def dispatch(event, env={})
67
+ self.nick, self.userhost, self.channel, self.error =
68
+ env[:nick], env[:userhost], env[:channel], env[:error]
69
+ self.message = env[:message] || ""
70
+
71
+ if handler = find(event, message)
72
+ regexp, block = *handler
73
+ self.match = message.match(regexp).captures
74
+ invoke block
75
+ end
76
+ end
77
+
78
+ private
79
+ def find(type, message)
80
+ if events = @events[type]
81
+ events.detect {|regexp,_| message.match(regexp)}
82
+ end
83
+ end
84
+
85
+ def invoke(block)
86
+ mc = class << self; self; end
87
+ mc.send :define_method, :__isaac_event_handler, &block
88
+
89
+ bargs = case block.arity <=> 0
90
+ when -1; match
91
+ when 0; []
92
+ when 1; match[0..block.arity-1]
93
+ end
94
+
95
+ catch(:halt) { __isaac_event_handler(*bargs) }
96
+ end
83
97
  end
84
98
 
85
99
  class IRC
@@ -87,56 +101,44 @@ module Isaac
87
101
  @bot, @config = bot, config
88
102
  @transfered = 0
89
103
  @registration = []
90
- @lock = false
91
- @queue = []
92
104
  end
93
105
 
94
106
  def connect
95
107
  @socket = TCPSocket.open(@config.server, @config.port)
96
- message "PASSWORD #{@config.password}" if @config.password
108
+ @queue = Queue.new(@socket, @bot.config.server)
109
+ message "PASS #{@config.password}" if @config.password
97
110
  message "NICK #{@config.nick}"
98
111
  message "USER #{@config.nick} 0 * :#{@config.realname}"
99
- @lock = true
112
+ @queue.lock
100
113
 
101
- # This should probably be somewhere else..
102
- if @config.environment == :test
103
- Thread.start {
104
- while line = @socket.gets
105
- parse line
106
- end
107
- }
108
- else
109
- while line = @socket.gets
110
- parse line
111
- end
114
+ while line = @socket.gets
115
+ parse line
112
116
  end
113
117
  end
114
118
 
115
119
  def parse(input)
116
120
  puts "<< #{input}" if @bot.config.verbose
117
- case input
118
- when /^:\S+ 00([1-4])/
119
- @registration << $1.to_i
121
+ case input.chomp
122
+ when /(^:\S+ )?00([1-4])/
123
+ @registration << $2.to_i
120
124
  if registered?
121
- @lock = false
125
+ @queue.unlock
122
126
  @bot.dispatch(:connect)
123
- continue_queue
124
127
  end
125
- when /^:(\S+)!\S+ PRIVMSG \S+ :?\001VERSION\001/
126
- message "NOTICE #{$1} :\001VERSION #{@bot.config.version}\001"
128
+ when /(^:(\S+)!\S+ )?PRIVMSG \S+ :?\001VERSION\001/
129
+ message "NOTICE #{$2} :\001VERSION #{@bot.config.version}\001"
127
130
  when /^PING (\S+)/
128
- @transfered, @lock = 0, false
131
+ @queue.unlock
129
132
  message "PONG #{$1}"
130
- when /^:(\S+)!(\S+) PRIVMSG (\S+) :?(.*)/
131
- env = { :nick => $1, :userhost => $2, :channel => $3, :message => $4 }
133
+ when /(^:(\S+)!(\S+) )?PRIVMSG (\S+) :?(.*)/
134
+ env = { :nick => $2, :userhost => $3, :channel => $4, :message => $5 }
132
135
  type = env[:channel].match(/^#/) ? :channel : :private
133
136
  @bot.dispatch(type, env)
134
- when /^:\S+ ([4-5]\d\d) \S+ (\S+)/
135
- env = {:error => $1.to_i, :message => $1, :nick => $2, :channel => $2}
137
+ when /(^:\S+ )?([4-5]\d\d) \S+ (\S+)/
138
+ env = {:error => $2.to_i, :message => $2, :nick => $3, :channel => $3}
136
139
  @bot.dispatch(:error, env)
137
- when /^:\S+ PONG/
138
- @transfered, @lock = 0, false
139
- continue_queue
140
+ when /(^:\S+ )?PONG/
141
+ @queue.unlock
140
142
  end
141
143
  end
142
144
 
@@ -146,21 +148,60 @@ module Isaac
146
148
 
147
149
  def message(msg)
148
150
  @queue << msg
149
- continue_queue
151
+ end
152
+ end
153
+
154
+ class Queue
155
+ def initialize(socket, server)
156
+ # We need server for pinging us out of an excess flood
157
+ @socket, @server = socket, server
158
+ @queue, @lock, @transfered = [], false, 0
159
+ end
160
+
161
+ def lock
162
+ @lock = true
163
+ end
164
+
165
+ def unlock
166
+ @lock, @transfered = false, 0
167
+ invoke
168
+ end
169
+
170
+ def <<(message)
171
+ @queue << message
172
+ invoke
173
+ end
174
+
175
+ private
176
+ def message_to_send?
177
+ !@lock && !@queue.empty?
178
+ end
179
+
180
+ def transfered_after_next_send
181
+ @transfered + @queue.first.size + 2 # the 2 is for \r\n
182
+ end
183
+
184
+ def exceed_limit?
185
+ transfered_after_next_send > 1472
186
+ end
187
+
188
+ def lock_and_ping
189
+ lock
190
+ @socket.print "PING :#{@server}\r\n"
191
+ end
192
+
193
+ def next_message
194
+ @queue.shift.to_s.chomp + "\r\n"
150
195
  end
151
196
 
152
- def continue_queue
153
- # <= 1472 allows for \n
154
- while !@lock && msg = @queue.shift
155
- if (@transfered + msg.size) < 1472
156
- @socket.puts msg
157
- puts ">> #{msg}" if @bot.config.verbose
158
- @transfered += msg.size + 1
197
+ def invoke
198
+ while message_to_send?
199
+ if exceed_limit?
200
+ lock_and_ping; break
159
201
  else
160
- @queue.unshift(msg)
161
- @lock = true
162
- @socket.puts "PING :#{@bot.config.server}"
163
- break
202
+ @transfered = transfered_after_next_send
203
+ @socket.print next_message
204
+ # puts ">> #{msg}" if @bot.config.verbose
164
205
  end
165
206
  end
166
207
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: isaac
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Harry Vangberg
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-02-23 00:00:00 +01:00
12
+ date: 2009-04-25 00:00:00 +02:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -29,6 +29,8 @@ files:
29
29
  - lib/isaac.rb
30
30
  has_rdoc: true
31
31
  homepage: http://github.com/ichverstehe/isaac
32
+ licenses: []
33
+
32
34
  post_install_message:
33
35
  rdoc_options:
34
36
  - --main
@@ -50,9 +52,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
50
52
  requirements: []
51
53
 
52
54
  rubyforge_project: isaac
53
- rubygems_version: 1.3.1
55
+ rubygems_version: 1.3.3
54
56
  signing_key:
55
- specification_version: 2
57
+ specification_version: 3
56
58
  summary: The smallish DSL for writing IRC bots
57
59
  test_files: []
58
60