irc 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +50 -0
- data/lib/irc.rb +17 -0
- data/lib/irc/base.rb +6 -0
- data/lib/irc/bot.rb +114 -0
- data/lib/irc/callback.rb +57 -0
- data/lib/irc/connection.rb +91 -0
- data/lib/irc/extensions.rb +33 -0
- data/lib/irc/message.rb +46 -0
- data/lib/irc/store.rb +24 -0
- metadata +53 -0
data/README.markdown
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
Usage
|
2
|
+
=====
|
3
|
+
|
4
|
+
## Quickstart
|
5
|
+
``` ruby
|
6
|
+
require 'irc'
|
7
|
+
|
8
|
+
host 'localhost'
|
9
|
+
nick 'MyBot'
|
10
|
+
channel '#MyChannel', '#OtherChannel'
|
11
|
+
|
12
|
+
# Say hi when somebody joins the channel:
|
13
|
+
on :join do
|
14
|
+
say "Hi #{nick}!"
|
15
|
+
end
|
16
|
+
|
17
|
+
# Tell the time and date:
|
18
|
+
match /^!(?:time|now)/ do
|
19
|
+
reply Time.now.strftime("it is %l:%M %P on %A, %B %-d, %Y.").gsub(/[ ]+/, ' ' )
|
20
|
+
end
|
21
|
+
|
22
|
+
start!
|
23
|
+
```
|
24
|
+
Then, `ruby newbot.rb`
|
25
|
+
|
26
|
+
|
27
|
+
## Subclass
|
28
|
+
|
29
|
+
You can also `require 'irc/base'` and subclass `IRC::Bot`:
|
30
|
+
``` ruby
|
31
|
+
require 'irc/base'
|
32
|
+
|
33
|
+
class MyBot < IRC::Bot
|
34
|
+
host 'localhost'
|
35
|
+
nick 'MyBot'
|
36
|
+
channel '#MyChannel'
|
37
|
+
|
38
|
+
start!
|
39
|
+
end
|
40
|
+
```
|
41
|
+
|
42
|
+
You can re-load the bot to update its callbacks without disconnecting:
|
43
|
+
``` ruby
|
44
|
+
# mention_match requires messages to start with the name of the bot
|
45
|
+
mention_match /reload!/ do
|
46
|
+
self.class.reset!
|
47
|
+
|
48
|
+
load __FILE__
|
49
|
+
end
|
50
|
+
```
|
data/lib/irc.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'irc/base'
|
2
|
+
|
3
|
+
class NewBot < IRC::Bot
|
4
|
+
|
5
|
+
end
|
6
|
+
|
7
|
+
module IRC
|
8
|
+
module Delegator
|
9
|
+
NewBot.public_methods.each do |method_name|
|
10
|
+
define_method method_name do |*args, &block|
|
11
|
+
NewBot.send method_name, *args, &block
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
extend IRC::Delegator
|
data/lib/irc/base.rb
ADDED
data/lib/irc/bot.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
module IRC
|
2
|
+
class Bot
|
3
|
+
attr_reader :message
|
4
|
+
|
5
|
+
def initialize message
|
6
|
+
@message = message
|
7
|
+
end
|
8
|
+
|
9
|
+
def stop
|
10
|
+
Thread.current.kill
|
11
|
+
end
|
12
|
+
|
13
|
+
def say something, recipients = nil
|
14
|
+
message.connection.privmsg something, recipients || self.class.channels
|
15
|
+
end
|
16
|
+
|
17
|
+
def reply something, mention = true
|
18
|
+
if message.middle == self.class.nick
|
19
|
+
say something, message.nick
|
20
|
+
elsif mention
|
21
|
+
say "#{message.nick}, #{something}", message.middle
|
22
|
+
else
|
23
|
+
say something, message.middle
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def store namespace = nil
|
28
|
+
@store ||= IRC::Store.store
|
29
|
+
|
30
|
+
if namespace
|
31
|
+
(@namespace ||= {})[namespace] ||= Redis::Namespace.new(namespace, redis: @store)
|
32
|
+
else
|
33
|
+
@store
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def conjugate noun, lookup = nil
|
38
|
+
lookup ||= {
|
39
|
+
'me' => nick,
|
40
|
+
'you' => self.class.nick,
|
41
|
+
nick => 'you'
|
42
|
+
}
|
43
|
+
noun.gsub /[\w]+/i, lookup
|
44
|
+
end
|
45
|
+
|
46
|
+
def method_missing method_name, *args
|
47
|
+
message.public_send method_name, *args
|
48
|
+
end
|
49
|
+
|
50
|
+
class << self
|
51
|
+
attr_accessor :nick, :host, :port, :channels
|
52
|
+
|
53
|
+
def start!
|
54
|
+
@connection ||= Connection.new @host, @port
|
55
|
+
|
56
|
+
on(:ping) { connection.pong params }
|
57
|
+
|
58
|
+
@connection.nick @nick
|
59
|
+
@connection.join @channels
|
60
|
+
|
61
|
+
@connection.listen
|
62
|
+
end
|
63
|
+
|
64
|
+
def reset!
|
65
|
+
Callback.reset!
|
66
|
+
@channels = []
|
67
|
+
end
|
68
|
+
|
69
|
+
def host _host = nil
|
70
|
+
puts " host: '#{_host}'"
|
71
|
+
@host = _host || @host
|
72
|
+
end
|
73
|
+
|
74
|
+
def port _port = nil
|
75
|
+
puts " port: '#{_port}'"
|
76
|
+
@port = _port || @port
|
77
|
+
end
|
78
|
+
|
79
|
+
def nick _nick = nil
|
80
|
+
puts " nick: '#{_nick}'" if _nick
|
81
|
+
@nick = _nick || @nick
|
82
|
+
end
|
83
|
+
|
84
|
+
def store _store, options = nil
|
85
|
+
require 'irc/store'
|
86
|
+
|
87
|
+
case _store
|
88
|
+
when :redis
|
89
|
+
IRC::Store.options = options
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def channel *_channels
|
94
|
+
_channels.each do |c|
|
95
|
+
puts "channel: '#{c}'"
|
96
|
+
(@channels ||= []) << c
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def match regex, &block
|
101
|
+
on(:privmsg, regex, &block)
|
102
|
+
end
|
103
|
+
|
104
|
+
def mention_match regex, &block
|
105
|
+
r = /^#{@nick}[,: ]*/ + regex
|
106
|
+
on(:privmsg, r, &block)
|
107
|
+
end
|
108
|
+
|
109
|
+
def on action, regex = nil, &block
|
110
|
+
Callback.add(action, regex, self, &block)
|
111
|
+
end
|
112
|
+
end # class << Bot
|
113
|
+
end # class Bot
|
114
|
+
end # module IRC
|
data/lib/irc/callback.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
module IRC
|
2
|
+
class Callback
|
3
|
+
attr_accessor :action, :regex
|
4
|
+
|
5
|
+
def initialize action, regex = nil, factory = nil, &block
|
6
|
+
@action = action
|
7
|
+
@regex = regex || //
|
8
|
+
@block = block
|
9
|
+
@factory = factory
|
10
|
+
end
|
11
|
+
|
12
|
+
def call! message
|
13
|
+
if match = @regex.match(message.content)
|
14
|
+
(class << message; self; end).send :attr_accessor, *@regex.names
|
15
|
+
@regex.names.each do |name|
|
16
|
+
message.instance_variable_set("@#{name}", match[name])
|
17
|
+
end
|
18
|
+
|
19
|
+
Thread.new do
|
20
|
+
# begin
|
21
|
+
if @factory
|
22
|
+
@factory.new(message).instance_eval(&@block)
|
23
|
+
else
|
24
|
+
puts "calling #{@block}"
|
25
|
+
@block.call(message, match)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class << self
|
32
|
+
attr_accessor :callbacks
|
33
|
+
|
34
|
+
def handle message
|
35
|
+
callbacks[message.action].each do |callback|
|
36
|
+
callback.call! message
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def add action, *args, &block
|
41
|
+
callback = new(action, *args, &block)
|
42
|
+
|
43
|
+
callbacks[action] << callback
|
44
|
+
callbacks[:all] << callback
|
45
|
+
end
|
46
|
+
|
47
|
+
def callbacks
|
48
|
+
@callbacks ||= Hash.new {|hash,key| hash[key] = []}
|
49
|
+
end
|
50
|
+
|
51
|
+
def reset!
|
52
|
+
@callbacks = nil
|
53
|
+
callbacks
|
54
|
+
end
|
55
|
+
end # class << Callback
|
56
|
+
end # class Callback
|
57
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
module IRC
|
5
|
+
class Connection
|
6
|
+
def initialize host = nil, port = 6667
|
7
|
+
@host = host
|
8
|
+
@port = port
|
9
|
+
end
|
10
|
+
|
11
|
+
def mutex
|
12
|
+
@mutex ||= Mutex.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def disconnected?
|
16
|
+
!@socket || @socket.closed?
|
17
|
+
end
|
18
|
+
|
19
|
+
def connected?
|
20
|
+
!disconnected?
|
21
|
+
end
|
22
|
+
|
23
|
+
def write *strings
|
24
|
+
# mutex do
|
25
|
+
strings.each do |string|
|
26
|
+
puts '-> ' + string
|
27
|
+
socket.print string + "\r\n"
|
28
|
+
end
|
29
|
+
# end
|
30
|
+
end
|
31
|
+
|
32
|
+
def join *channels
|
33
|
+
channels.flatten.each do |channel|
|
34
|
+
@channel = channel
|
35
|
+
write "JOIN #{channel}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def part *channels
|
40
|
+
channels.flatten.each do |channel|
|
41
|
+
write "PART #{channel}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def nick _nick, realname = nil
|
46
|
+
realname ||= _nick
|
47
|
+
write "NICK #{_nick}"
|
48
|
+
write "USER #{_nick} localhost #{@host} :#{realname}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def privmsg content, *recipients
|
52
|
+
recipients = @channel unless recipients.any?
|
53
|
+
recipients = [recipients].flatten.join(',')
|
54
|
+
|
55
|
+
msg = "PRIVMSG #{recipients} :#{content}"
|
56
|
+
write msg.slice!(0,500)
|
57
|
+
privmsg msg, recipients if msg != ''
|
58
|
+
end
|
59
|
+
|
60
|
+
def quit message = nil
|
61
|
+
write "QUIT :#{message}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def pong value
|
65
|
+
write "PONG #{value}"
|
66
|
+
end
|
67
|
+
|
68
|
+
def disconnect
|
69
|
+
quit
|
70
|
+
socket.close
|
71
|
+
end
|
72
|
+
|
73
|
+
def socket
|
74
|
+
@socket ||= TCPSocket.open(@host, @port)
|
75
|
+
end
|
76
|
+
Thread.abort_on_exception = true
|
77
|
+
|
78
|
+
def listen
|
79
|
+
unless @listening
|
80
|
+
loop do
|
81
|
+
@listening = true
|
82
|
+
socket.lines "\r\n" do |line|
|
83
|
+
message = Message.new(line, self)
|
84
|
+
Callback.handle message
|
85
|
+
end
|
86
|
+
@listening = false
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end # class Connection
|
91
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class Array
|
2
|
+
def to_sentence
|
3
|
+
case length
|
4
|
+
when 0
|
5
|
+
''
|
6
|
+
when 1
|
7
|
+
self[0].to_s
|
8
|
+
when 2
|
9
|
+
"#{self[0]} and #{self[1]}"
|
10
|
+
else
|
11
|
+
"#{self[0..-2].join(', ')}, and #{self.last}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class String
|
17
|
+
def pluralize number = 2
|
18
|
+
case number
|
19
|
+
when 0
|
20
|
+
"no #{self}s"
|
21
|
+
when 1
|
22
|
+
"1 #{self}"
|
23
|
+
else
|
24
|
+
"#{number} #{self}s"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Regexp
|
30
|
+
def +(r)
|
31
|
+
Regexp.new(source + r.source)
|
32
|
+
end
|
33
|
+
end
|
data/lib/irc/message.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
module IRC
|
2
|
+
class Message
|
3
|
+
REGEX = /^
|
4
|
+
(:
|
5
|
+
(?<prefix>
|
6
|
+
(
|
7
|
+
(?<servername>(?<nick>[a-z][a-z0-9_\-\[\]\\`\^\{\}\.\|]*))
|
8
|
+
)
|
9
|
+
(!(?<user>[a-z0-9~\.]+))?
|
10
|
+
(@(?<host>[a-z0-9\.]+))?
|
11
|
+
)
|
12
|
+
[\ ]+
|
13
|
+
)?
|
14
|
+
(?<command>[a-z]+|[0-9]{3})
|
15
|
+
(?<params>
|
16
|
+
(\ +(?<middle>[^:\r\n\ ][^\r\n\ ]*))*
|
17
|
+
(\ *:(?<trailing>.+))?
|
18
|
+
)
|
19
|
+
\r\n$
|
20
|
+
/xi
|
21
|
+
|
22
|
+
attr_reader :raw
|
23
|
+
attr_accessor :connection
|
24
|
+
attr_accessor *REGEX.names
|
25
|
+
|
26
|
+
def initialize message_string, connection
|
27
|
+
@raw = message_string
|
28
|
+
|
29
|
+
@connection = connection
|
30
|
+
@match = REGEX.match(message_string) || {}
|
31
|
+
|
32
|
+
REGEX.names.each do |name|
|
33
|
+
instance_variable_set("@#{name}", @match[name])
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
def action
|
39
|
+
@action ||= (command || '').downcase.intern
|
40
|
+
end
|
41
|
+
|
42
|
+
def content
|
43
|
+
@content ||= trailing || middle
|
44
|
+
end
|
45
|
+
end # class Message
|
46
|
+
end
|
data/lib/irc/store.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'redis'
|
2
|
+
require 'redis-namespace'
|
3
|
+
|
4
|
+
module IRC
|
5
|
+
class Store
|
6
|
+
attr_reader :store
|
7
|
+
|
8
|
+
def initialize options = {}
|
9
|
+
options = {:host => 'localhost', :port => 6379}.merge(options)
|
10
|
+
@store = Redis.new options
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_missing method_name, *args, &block
|
14
|
+
store.send method_name, *args, &block
|
15
|
+
end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
attr_accessor :options
|
19
|
+
def store
|
20
|
+
self.new options
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: irc
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jeff Peterson
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-10-02 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: a simple ruby irc (bot framework)
|
15
|
+
email: jeff@petersonj.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- README.markdown
|
21
|
+
- lib/irc.rb
|
22
|
+
- lib/irc/base.rb
|
23
|
+
- lib/irc/bot.rb
|
24
|
+
- lib/irc/callback.rb
|
25
|
+
- lib/irc/connection.rb
|
26
|
+
- lib/irc/extensions.rb
|
27
|
+
- lib/irc/message.rb
|
28
|
+
- lib/irc/store.rb
|
29
|
+
homepage: http://github.com/jeffpeterson/irc
|
30
|
+
licenses: []
|
31
|
+
post_install_message:
|
32
|
+
rdoc_options: []
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
none: false
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
requirements: []
|
48
|
+
rubyforge_project:
|
49
|
+
rubygems_version: 1.8.24
|
50
|
+
signing_key:
|
51
|
+
specification_version: 3
|
52
|
+
summary: a simple ruby irc (bot) framework
|
53
|
+
test_files: []
|