percy 1.3.0 → 1.4.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/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