percy 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/percy.rb CHANGED
@@ -1,556 +1,21 @@
1
- require 'rubygems'
2
1
  require 'pathname'
3
2
  $:.unshift Pathname.new(__FILE__).dirname.expand_path
4
- require 'eventmachine'
5
- require 'ostruct'
6
- require 'timeout'
7
- require 'thread'
8
- autoload :PercyLogger, 'percylogger'
9
-
3
+ require 'percy/irc'
4
+ require 'percy/formatting'
10
5
  Thread.abort_on_exception = true
11
6
 
12
- class Connection < EventMachine::Connection
13
- include EventMachine::Protocols::LineText2
14
-
15
- def connection_completed
16
- Percy.raw "NICK #{Percy.config.nick}"
17
- Percy.raw "USER #{Percy.config.nick} 0 * :#{Percy.config.username}"
18
- Percy.raw "PASS #{Percy.config.password}" if Percy.config.password
19
- end
20
-
21
- def unbind
22
- Percy.connected = false
23
- Percy.traffic_logger.info('-- Percy disconnected') if Percy.traffic_logger
24
- puts "#{Time.now.strftime('%d.%m.%Y %H:%M:%S')} -- Percy disconnected"
25
-
26
- if Percy.config.reconnect
27
- Percy.traffic_logger.info("-- Reconnecting in #{Percy.config.reconnect_interval} seconds") if Percy.traffic_logger
28
- puts "#{Time.now.strftime('%d.%m.%Y %H:%M:%S')} -- Reconnecting in #{Percy.config.reconnect_interval} seconds"
29
-
30
- EventMachine::add_timer(Percy.config.reconnect_interval) do
31
- reconnect Percy.config.server, Percy.config.port
32
- end
33
- else
34
- EventMachine::stop_event_loop
35
- end
36
- end
37
-
38
- def receive_line(line)
39
- Percy.parse line
40
- end
41
- end
42
-
43
- class Percy
44
- class << self
45
- attr_reader :config
46
- attr_accessor :traffic_logger, :connected
47
- end
48
-
49
- VERSION = 'Percy 1.3.0 (http://github.com/tbuehlmann/percy)'
50
-
51
- @config = OpenStruct.new({:server => 'localhost',
52
- :port => 6667,
53
- :nick => 'Percy',
54
- :username => 'Percy',
55
- :verbose => true,
56
- :logging => false,
57
- :reconnect => true,
58
- :reconnect_interval => 30})
59
-
60
- # helper variables for getting server return values
61
- @observers = 0
62
- @temp_socket = []
63
-
64
- @connected = false
65
-
66
- # user methods
67
- @events = Hash.new []
68
- @listened_types = [:connect, :channel, :query, :join, :part, :quit, :nickchange, :kick] # + 3-digit numbers
69
-
70
- # observer synchronizer
71
- @mutex_observer = Mutex.new
72
-
73
- def self.configure(&block)
74
- block.call(@config)
75
-
76
- # set the percy root directory
77
- Object::const_set(:PERCY_ROOT, Pathname.new($0).dirname.expand_path)
78
-
79
- # logger
80
- if @config.logging
81
- @traffic_logger = PercyLogger.new(Pathname.new(PERCY_ROOT).join('logs'), 'traffic.log')
82
- @error_logger = PercyLogger.new(Pathname.new(PERCY_ROOT).join('logs'), 'error.log')
83
- end
84
- end
85
-
86
- # raw IRC messages
87
- def self.raw(message)
88
- @connection.send_data "#{message}\r\n"
89
- @traffic_logger.info(">> #{message}") if @traffic_logger
90
- puts "#{Time.now.strftime('%d.%m.%Y %H:%M:%S')} >> #{message}" if @config.verbose
91
- end
92
-
93
- # send a message
94
- def self.message(recipient, message)
95
- self.raw "PRIVMSG #{recipient} :#{message}"
96
- end
97
-
98
- # send a notice
99
- def self.notice(recipient, message)
100
- self.raw "NOTICE #{recipient} :#{message}"
101
- end
102
-
103
- # set a mode
104
- def self.mode(recipient, option)
105
- self.raw "MODE #{recipient} #{option}"
106
- end
107
-
108
- # kick a user
109
- def self.kick(channel, user, reason)
110
- if reason
111
- self.raw "KICK #{channel} #{user} :#{reason}"
112
- else
113
- self.raw "KICK #{channel} #{user}"
114
- end
115
- end
116
-
117
- # perform an action
118
- def self.action(recipient, message)
119
- self.raw "PRIVMSG #{recipient} :\001ACTION #{message}\001"
120
- end
121
-
122
- # set a topic
123
- def self.topic(channel, topic)
124
- self.raw "TOPIC #{channel} :#{topic}"
125
- end
126
-
127
- # joining a channel
128
- def self.join(channel, password = nil)
129
- if password
130
- self.raw "JOIN #{channel} #{password}"
131
- else
132
- self.raw "JOIN #{channel}"
133
- end
134
- end
135
-
136
- # parting a channel
137
- def self.part(channel, message)
138
- if msg
139
- self.raw "PART #{channel} :#{message}"
140
- else
141
- self.raw 'PART'
142
- end
143
- end
144
-
145
- # quitting
146
- def self.quit(message = nil)
147
- if message
148
- self.raw "QUIT :#{message}"
149
- else
150
- self.raw 'QUIT'
151
- end
152
-
153
- @config.reconnect = false # so Percy does not reconnect after the socket has been closed
154
- end
7
+ module Percy
8
+ VERSION = 'Percy 1.4.0 (http://github.com/tbuehlmann/percy)'
155
9
 
156
- def self.nick
157
- @config.nick
158
- end
159
-
160
- # returns all users on a specific channel as array: ['Foo', 'bar', 'The_Librarian']
161
- def self.users_on(channel)
162
- actual_length = self.add_observer
163
- self.raw "NAMES #{channel}"
164
- channel = Regexp.escape(channel)
165
-
166
- begin
167
- Timeout::timeout(30) do # try 30 seconds to retrieve the users of <channel>
168
- start = actual_length
169
- ending = @temp_socket.length
170
- users = []
171
-
172
- loop do
173
- for line in start..ending do
174
- case @temp_socket[line]
175
- when /^:\S+ 353 .+ #{channel} :/i
176
- users << $'.split(' ')
177
- when /^:\S+ 366 .+ #{channel}/i
178
- return users.flatten.uniq.map { |element| element.gsub(/[!@%+]/, '') } # removing all modes
179
- end
180
- end
181
-
182
- sleep 0.25
183
- start = ending
184
- ending = @temp_socket.length
185
- end
186
- end
187
- rescue Timeout::Error
188
- return []
189
- ensure
190
- self.remove_observer
191
- end
192
- end
193
-
194
- # returns all users on a specific channel as array (with status): ['@Foo', '+bar', 'The_Librarian', '!Frank']
195
- def self.users_with_status_on(channel)
196
- actual_length = self.add_observer
197
- self.raw "NAMES #{channel}"
198
- channel = Regexp.escape(channel)
199
-
200
- begin
201
- Timeout::timeout(30) do # try 30 seconds to retrieve the users of <channel>
202
- start = actual_length
203
- ending = @temp_socket.length
204
- users = []
205
-
206
- loop do
207
- for line in start..ending do
208
- case @temp_socket[line]
209
- when /^:\S+ 353 .+ #{channel} :/i
210
- users << $'.split(' ')
211
- when /^:\S+ 366 .+ #{channel}/i
212
- return users.flatten.uniq
213
- end
214
- end
215
-
216
- sleep 0.25
217
- start = ending
218
- ending = @temp_socket.length
219
- end
220
- end
221
- rescue Timeout::Error
222
- return []
223
- ensure
224
- self.remove_observer
225
- end
226
- end
227
-
228
- # get the channel limit of a channel
229
- def self.channel_limit(channel)
230
- actual_length = self.add_observer
231
- self.raw "MODE #{channel}"
232
-
233
- begin
234
- Timeout::timeout(10) do # try 10 seconds to retrieve l mode of <channel>
235
- start = actual_length
236
- ending = @temp_socket.length
237
- channel = Regexp.escape(channel)
238
-
239
- loop do
240
- for line in start..ending do
241
- if @temp_socket[line] =~ /^:\S+ 324 \S+ #{channel} .*l.* (\d+)/
242
- return $1.to_i
243
- end
244
- end
245
-
246
- sleep 0.25
247
- start = ending
248
- ending = @temp_socket.length
249
- end
250
- end
251
- rescue Timeout::Error
252
- return false
253
- ensure
254
- self.remove_observer
255
- end
256
- end
257
-
258
- # check whether an user is online
259
- def self.is_online(nick)
260
- actual_length = self.add_observer
261
- self.raw "WHOIS #{nick}"
262
-
263
- begin
264
- Timeout::timeout(10) do
265
- start = actual_length
266
- ending = @temp_socket.length
267
- nick = Regexp.escape(nick)
268
-
269
- loop do
270
- for line in start..ending do
271
- if @temp_socket[line] =~ /^:\S+ 311 \S+ (#{nick}) /i
272
- return $1
273
- elsif @temp_socket[line] =~ /^:\S+ 401 \S+ #{nick} /i
274
- return false
275
- end
276
- end
277
-
278
- sleep 0.25
279
- start = ending
280
- ending = @temp_socket.length
281
- end
282
- end
283
- rescue Timeout::Error
284
- return false
285
- ensure
286
- self.remove_observer
287
- end
288
- end
289
-
290
- # on method
291
- def self.on(type = :channel, match = //, &block)
292
- unless @listened_types.include?(type) || type =~ /^\d\d\d$/
293
- raise ArgumentError, "#{type} is not a supported type"
294
- end
295
-
296
- @events[type] = [] if @events[type].empty? # @events' default value is [], but it's not possible to add elements to it (weird!)
297
- case type
298
- when :channel || :query
299
- @events[type] << {:match => match, :proc => block}
300
- else
301
- @events[type] << block
302
- end
303
- end
304
-
305
- # connect!
306
- def self.connect
307
- @traffic_logger.info('-- Starting Percy') if @traffic_logger
308
- puts "#{Time.now.strftime('%d.%m.%Y %H:%M:%S')} -- Starting Percy"
309
-
310
- EventMachine::run do
311
- @connection = EventMachine::connect(@config.server, @config.port, Connection)
312
- end
313
- end
314
-
315
- private
316
-
317
- # add observer
318
- def self.add_observer
319
- @mutex_observer.synchronize do
320
- @observers += 1
321
- end
322
-
323
- return @temp_socket.length - 1 # so the loop knows where to begin to search for patterns
324
- end
325
-
326
- # remove observer
327
- def self.remove_observer
328
- @mutex_observer.synchronize do
329
- @observers -= 1 # remove observer
330
- @temp_socket = [] if @observers == 0 # clear @temp_socket if no observers are active
331
- end
332
- end
333
-
334
- # parses incoming traffic (types)
335
- def self.parse_type(type, env = nil)
336
- case type
337
- when /^\d\d\d$/
338
- if @events[type]
339
- @events[type].each do |block|
340
- Thread.new do
341
- begin
342
- block.call(env)
343
- rescue => e
344
- if @error_logger
345
- @error_logger.error(e.message)
346
- e.backtrace.each do |line|
347
- @error_logger.error(line)
348
- end
349
- end
350
- end
351
- end
352
- end
353
- end
354
-
355
- # :connect
356
- if type =~ /^376|422$/
357
- @events[:connect].each do |block|
358
- Thread.new do
359
- begin
360
- unless @connected
361
- @connected = true
362
- block.call
363
- end
364
- rescue => e
365
- if @error_logger
366
- @error_logger.error(e.message)
367
- e.backtrace.each do |line|
368
- @error_logger.error(line)
369
- end
370
- end
371
- end
372
- end
373
- end
374
- end
375
-
376
- when :channel
377
- @events[type].each do |method|
378
- if env[:message] =~ method[:match]
379
- Thread.new do
380
- begin
381
- method[:proc].call(env)
382
- rescue => e
383
- if @error_logger
384
- @error_logger.error(e.message)
385
- e.backtrace.each do |line|
386
- @error_logger.error(line)
387
- end
388
- end
389
- end
390
- end
391
- end
392
- end
393
-
394
- when :query
395
- # version respones
396
- if env[:message] == "\001VERSION\001"
397
- self.notice env[:nick], "\001VERSION #{VERSION}\001"
398
- end
399
-
400
- # time response
401
- if env[:message] == "\001TIME\001"
402
- self.notice env[:nick], "\001TIME #{Time.now.strftime('%a %b %d %H:%M:%S %Y')}\001"
403
- end
404
-
405
- # ping response
406
- if env[:message] =~ /\001PING (\d+)\001/
407
- self.notice env[:nick], "\001PING #{$1}\001"
408
- end
409
-
410
- @events[type].each do |method|
411
- if env[:message] =~ method[:match]
412
- Thread.new do
413
- begin
414
- method[:proc].call(env)
415
- rescue => e
416
- if @error_logger
417
- @error_logger.error(e.message)
418
- e.backtrace.each do |line|
419
- @error_logger.error(line)
420
- end
421
- end
422
- end
423
- end
424
- end
425
- end
426
-
427
- when :join
428
- @events[type].each do |block|
429
- Thread.new do
430
- begin
431
- block.call(env)
432
- rescue => e
433
- if @error_logger
434
- @error_logger.error(e.message)
435
- e.backtrace.each do |line|
436
- @error_logger.error(line)
437
- end
438
- end
439
- end
440
- end
441
- end
442
-
443
- when :part
444
- @events[type].each do |block|
445
- Thread.new do
446
- begin
447
- block.call(env)
448
- rescue => e
449
- if @error_logger
450
- @error_logger.error(e.message)
451
- e.backtrace.each do |line|
452
- @error_logger.error(line)
453
- end
454
- end
455
- end
456
- end
457
- end
458
-
459
- when :quit
460
- @events[type].each do |block|
461
- Thread.new do
462
- begin
463
- block.call(env)
464
- rescue => e
465
- if @error_logger
466
- @error_logger.error(e.message)
467
- e.backtrace.each do |line|
468
- @error_logger.error(line)
469
- end
470
- end
471
- end
472
- end
473
- end
474
-
475
- when :nickchange
476
- @events[type].each do |block|
477
- Thread.new do
478
- begin
479
- block.call(env)
480
- rescue => e
481
- if @error_logger
482
- @error_logger.error(e.message)
483
- e.backtrace.each do |line|
484
- @error_logger.error(line)
485
- end
486
- end
487
- end
488
- end
489
- end
490
-
491
- when :kick
492
- @events[type].each do |block|
493
- Thread.new do
494
- begin
495
- block.call(env)
496
- rescue => e
497
- if @error_logger
498
- @error_logger.error(e.message)
499
- e.backtrace.each do |line|
500
- @error_logger.error(line)
501
- end
502
- end
503
- end
504
- end
505
- end
506
- end
507
- end
508
-
509
- # parsing incoming traffic
510
- def self.parse(message)
511
- @traffic_logger.info("<< #{message.chomp}") if @traffic_logger
512
- puts "#{Time.now.strftime('%d.%m.%Y %H:%M:%S')} << #{message.chomp}" if @config.verbose
513
-
514
- case message.chomp
515
- when /^PING \S+$/
516
- self.raw message.chomp.gsub('PING', 'PONG')
517
-
518
- when /^:\S+ (\d\d\d) /
519
- self.parse_type($1, :params => $')
520
-
521
- when /^:(\S+)!(\S+)@(\S+) PRIVMSG #(\S+) :/
522
- self.parse_type(:channel, :nick => $1, :user => $2, :host => $3, :channel => "##{$4}", :message => $')
523
-
524
- when /^:(\S+)!(\S+)@(\S+) PRIVMSG \S+ :/
525
- self.parse_type(:query, :nick => $1, :user => $2, :host => $3, :message => $')
526
-
527
- when /^:(\S+)!(\S+)@(\S+) JOIN :*(\S+)$/
528
- self.parse_type(:join, :nick => $1, :user => $2, :host => $3, :channel => $4)
529
-
530
- when /^:(\S+)!(\S+)@(\S+) PART (\S+)/
531
- self.parse_type(:part, :nick => $1, :user => $2, :host => $3, :channel => $4, :message => $'.sub(' :', ''))
532
-
533
- when /^:(\S+)!(\S+)@(\S+) QUIT/
534
- self.parse_type(:quit, :nick => $1, :user => $2, :host => $3, :message => $'.sub(' :', ''))
535
-
536
- when /^:(\S+)!(\S+)@(\S+) NICK :/
537
- self.parse_type(:nickchange, :nick => $1, :user => $2, :host => $3, :new_nick => $')
538
-
539
- when /^:(\S+)!(\S+)@(\S+) KICK (\S+) (\S+) :/
540
- self.parse_type(:kick, :nick => $1, :user => $2, :host => $3, :channel => $4, :victim => $5, :reason => $')
541
- end
542
-
543
- if @observers > 0
544
- @temp_socket << message.chomp
545
- end
546
- end
10
+ # set the percy root directory
11
+ Object::const_set(:PERCY_ROOT, Pathname.new($0).dirname.expand_path)
547
12
  end
548
13
 
549
14
  def delegate(*methods)
550
15
  methods.each do |method|
551
16
  eval <<-EOS
552
17
  def #{method}(*args, &block)
553
- Percy.send(#{method.inspect}, *args, &block)
18
+ Percy::IRC.send(#{method.inspect}, *args, &block)
554
19
  end
555
20
  EOS
556
21
  end
data/percy.gemspec CHANGED
@@ -1,10 +1,10 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'percy'
3
- s.version = '1.3.0'
3
+ s.version = '1.4.0'
4
4
  s.summary = '(DSL, EventMachine) IRC Bot Framework inspired by isaac'
5
5
  s.description = 'Percy is an IRC Bot framework inspired by isaac with various changes.'
6
6
  s.homepage = 'http://github.com/tbuehlmann/percy'
7
- s.date = '04.03.2010'
7
+ s.date = '10.03.2010'
8
8
 
9
9
  s.author = 'Tobias Bühlmann'
10
10
  s.email = 'tobias.buehlmann@gmx.de'
@@ -16,7 +16,10 @@ Gem::Specification.new do |s|
16
16
  'examples/github_blog.rb',
17
17
  'examples/is_online_checker.rb',
18
18
  'lib/percy.rb',
19
- 'lib/percylogger.rb',
19
+ 'lib/percy/connection.rb',
20
+ 'lib/percy/formatting.rb',
21
+ 'lib/percy/irc.rb',
22
+ 'lib/percy/percylogger.rb',
20
23
  'percy.gemspec']
21
24
 
22
25
  s.has_rdoc = false
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 1
7
- - 3
7
+ - 4
8
8
  - 0
9
- version: 1.3.0
9
+ version: 1.4.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - "Tobias B\xC3\xBChlmann"
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-03-04 00:00:00 +01:00
17
+ date: 2010-03-10 00:00:00 +01:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -46,7 +46,10 @@ files:
46
46
  - examples/github_blog.rb
47
47
  - examples/is_online_checker.rb
48
48
  - lib/percy.rb
49
- - lib/percylogger.rb
49
+ - lib/percy/connection.rb
50
+ - lib/percy/formatting.rb
51
+ - lib/percy/irc.rb
52
+ - lib/percy/percylogger.rb
50
53
  - percy.gemspec
51
54
  has_rdoc: true
52
55
  homepage: http://github.com/tbuehlmann/percy
data/lib/percylogger.rb DELETED
@@ -1,71 +0,0 @@
1
- class PercyLogger
2
- require 'pathname'
3
- require 'thread'
4
- autoload :FileUtils, 'fileutils'
5
-
6
- DEBUG = 0
7
- INFO = 1
8
- WARN = 2
9
- ERROR = 3
10
- FATAL = 4
11
- UNKNOWN = 5
12
- LEVEL = ['DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL', 'UNKNOWN']
13
-
14
- attr_accessor :level, :time_format, :file
15
-
16
- def initialize(dirpath = Pathname.new($0).dirname.expand_path.join('logs'), filename = 'log.log', level = DEBUG, time_format = '%d.%m.%Y %H:%M:%S')
17
- @dirpath = dirpath
18
- @pathname = dirpath.join(filename)
19
- @level = level
20
- @time_format = time_format
21
- @mutex = Mutex.new
22
-
23
- unless @pathname.exist?
24
- unless @dirpath.directory?
25
- FileUtils.mkdir_p @dirpath
26
- end
27
-
28
- File.new(@pathname, 'w+')
29
- end
30
-
31
- @file = File.open(@pathname, 'a+')
32
- @file.sync = true
33
- end
34
-
35
- def write(severity, message)
36
- begin
37
- if severity >= @level
38
- @mutex.synchronize do
39
- @file.puts "#{LEVEL[severity]} #{Time.now.strftime(@time_format)} #{message}"
40
- end
41
- end
42
- rescue => e
43
- puts e.message
44
- puts e.backtrace.join('\n')
45
- end
46
- end
47
-
48
- def debug(message)
49
- write DEBUG, message
50
- end
51
-
52
- def info(message)
53
- write INFO, message
54
- end
55
-
56
- def warn(message)
57
- write WARN, message
58
- end
59
-
60
- def error(message)
61
- write ERROR, message
62
- end
63
-
64
- def fatal(message)
65
- write FATAL, message
66
- end
67
-
68
- def unknown(message)
69
- write UNKNOWN, message
70
- end
71
- end