net-irc 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,7 +9,7 @@ require "monitor"
9
9
  module Net; end
10
10
 
11
11
  module Net::IRC
12
- VERSION = "0.0.4"
12
+ VERSION = "0.0.5"
13
13
  class IRCException < StandardError; end
14
14
 
15
15
  require "net/irc/constants"
@@ -6,10 +6,11 @@ class Net::IRC::Client
6
6
  attr_reader :prefix, :channels
7
7
 
8
8
  def initialize(host, port, opts={})
9
- @host = host
10
- @port = port
11
- @opts = OpenStruct.new(opts)
12
- @log = @opts.logger || Logger.new($stdout)
9
+ @host = host
10
+ @port = port
11
+ @opts = OpenStruct.new(opts)
12
+ @log = @opts.logger || Logger.new($stdout)
13
+ @server_config = Message::ServerConfig.new
13
14
  @channels = {
14
15
  # "#channel" => {
15
16
  # :modes => [],
@@ -21,6 +22,8 @@ class Net::IRC::Client
21
22
 
22
23
  # Connect to server and start loop.
23
24
  def start
25
+ # reset config
26
+ @server_config = Message::ServerConfig.new
24
27
  @socket = TCPSocket.open(@host, @port)
25
28
  on_connected
26
29
  post PASS, @opts.pass if @opts.pass
@@ -66,136 +69,17 @@ class Net::IRC::Client
66
69
  @prefix = Prefix.new(m[1][/\S+$/])
67
70
  end
68
71
 
72
+ # Default RPL_ISUPPORT callback.
73
+ # This detects server's configurations.
74
+ def on_rpl_isupport(m)
75
+ @server_config.set(m)
76
+ end
77
+
69
78
  # Default PING callback. Response PONG.
70
79
  def on_ping(m)
71
80
  post PONG, @prefix ? @prefix.nick : ""
72
81
  end
73
82
 
74
- # For managing channel
75
- def on_rpl_namreply(m)
76
- type = m[1]
77
- channel = m[2]
78
- init_channel(channel)
79
-
80
- @channels.synchronize do
81
- m[3].split(/\s+/).each do |u|
82
- _, mode, nick = *u.match(/^([@+]?)(.+)/)
83
-
84
- @channels[channel][:users] << nick
85
- @channels[channel][:users].uniq!
86
-
87
- case mode
88
- when "@" # channel operator
89
- @channels[channel][:modes] << [:o, nick]
90
- when "+" # voiced (under moderating mode)
91
- @channels[channel][:modes] << [:v, nick]
92
- end
93
- end
94
-
95
- case type
96
- when "@" # secret
97
- @channels[channel][:modes] << [:s, nil]
98
- when "*" # private
99
- @channels[channel][:modes] << [:p, nil]
100
- when "=" # public
101
- end
102
-
103
- @channels[channel][:modes].uniq!
104
- end
105
- end
106
-
107
- # For managing channel
108
- def on_part(m)
109
- nick = m.prefix.nick
110
- channel = m[0]
111
- init_channel(channel)
112
-
113
- @channels.synchronize do
114
- info = @channels[channel]
115
- if info
116
- info[:users].delete(nick)
117
- info[:modes].delete_if {|u|
118
- u[1] == nick
119
- }
120
- end
121
- end
122
- end
123
-
124
- # For managing channel
125
- def on_quit(m)
126
- nick = m.prefix.nick
127
-
128
- @channels.synchronize do
129
- @channels.each do |channel, info|
130
- info[:users].delete(nick)
131
- info[:modes].delete_if {|u|
132
- u[1] == nick
133
- }
134
- end
135
- end
136
- end
137
-
138
- # For managing channel
139
- def on_kick(m)
140
- users = m[1].split(/,/)
141
-
142
- @channels.synchronize do
143
- m[0].split(/,/).each do |chan|
144
- init_channel(chan)
145
- info = @channels[chan]
146
- if info
147
- users.each do |nick|
148
- info[:users].delete(nick)
149
- info[:modes].delete_if {|u|
150
- u[1] == nick
151
- }
152
- end
153
- end
154
- end
155
- end
156
- end
157
-
158
- # For managing channel
159
- def on_join(m)
160
- nick = m.prefix.nick
161
- channel = m[0]
162
-
163
- @channels.synchronize do
164
- init_channel(channel)
165
-
166
- @channels[channel][:users] << nick
167
- @channels[channel][:users].uniq!
168
- end
169
- end
170
-
171
- # For managing channel
172
- def on_mode(m)
173
- channel = m[0]
174
- @channels.synchronize do
175
- init_channel(channel)
176
-
177
- mode = Message::ModeParser::RFC1459::Channel.parse(m)
178
- mode[:negative].each do |m|
179
- @channels[channel][:modes].delete(m)
180
- end
181
-
182
- mode[:positive].each do |m|
183
- @channels[channel][:modes] << m
184
- end
185
-
186
- @channels[channel][:modes].uniq!
187
- [mode[:negative], mode[:positive]]
188
- end
189
- end
190
-
191
- # For managing channel
192
- def init_channel(channel)
193
- @channels[channel] ||= {
194
- :modes => [],
195
- :users => [],
196
- }
197
- end
198
-
199
83
  # Do nothing.
200
84
  # This is for avoiding error on calling super.
201
85
  # So you can always call super at subclass.
@@ -0,0 +1,144 @@
1
+
2
+ module Net::IRC::Client::ChannelManager
3
+ # For managing channel
4
+ def on_rpl_namreply(m)
5
+ type = m[1]
6
+ channel = m[2]
7
+ init_channel(channel)
8
+
9
+ @channels.synchronize do
10
+ m[3].split(/\s+/).each do |u|
11
+ _, mode, nick = *u.match(/^([@+]?)(.+)/)
12
+
13
+ @channels[channel][:users] << nick
14
+ @channels[channel][:users].uniq!
15
+
16
+ op = @server_config.mode_parser.mark_to_op(mode)
17
+ if op
18
+ @channels[channel][:modes] << [op, nick]
19
+ end
20
+ end
21
+
22
+ case type
23
+ when "@" # secret
24
+ @channels[channel][:modes] << [:s, nil]
25
+ when "*" # private
26
+ @channels[channel][:modes] << [:p, nil]
27
+ when "=" # public
28
+ end
29
+
30
+ @channels[channel][:modes].uniq!
31
+ end
32
+ end
33
+
34
+ # For managing channel
35
+ def on_part(m)
36
+ nick = m.prefix.nick
37
+ channel = m[0]
38
+ init_channel(channel)
39
+
40
+ @channels.synchronize do
41
+ info = @channels[channel]
42
+ if info
43
+ info[:users].delete(nick)
44
+ info[:modes].delete_if {|u|
45
+ u[1] == nick
46
+ }
47
+ end
48
+ end
49
+ end
50
+
51
+ # For managing channel
52
+ def on_quit(m)
53
+ nick = m.prefix.nick
54
+
55
+ @channels.synchronize do
56
+ @channels.each do |channel, info|
57
+ info[:users].delete(nick)
58
+ info[:modes].delete_if {|u|
59
+ u[1] == nick
60
+ }
61
+ end
62
+ end
63
+ end
64
+
65
+ # For managing channel
66
+ def on_kick(m)
67
+ users = m[1].split(/,/)
68
+
69
+ @channels.synchronize do
70
+ m[0].split(/,/).each do |chan|
71
+ init_channel(chan)
72
+ info = @channels[chan]
73
+ if info
74
+ users.each do |nick|
75
+ info[:users].delete(nick)
76
+ info[:modes].delete_if {|u|
77
+ u[1] == nick
78
+ }
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ # For managing channel
86
+ def on_join(m)
87
+ nick = m.prefix.nick
88
+ channel = m[0]
89
+
90
+ @channels.synchronize do
91
+ init_channel(channel)
92
+
93
+ @channels[channel][:users] << nick
94
+ @channels[channel][:users].uniq!
95
+ end
96
+ end
97
+
98
+ # For managing channel
99
+ def on_nick(m)
100
+ oldnick = m.prefix.nick
101
+ newnick = m[0]
102
+
103
+ @channels.synchronize do
104
+ @channels.each do |channel, info|
105
+ info[:users].map! {|i|
106
+ (i == oldnick) ? newnick : i
107
+ }
108
+ info[:modes].map! {|m, target|
109
+ (target == oldnick) ? [m, newnick] : [m, target]
110
+ }
111
+ end
112
+ end
113
+ end
114
+
115
+ # For managing channel
116
+ def on_mode(m)
117
+ channel = m[0]
118
+ @channels.synchronize do
119
+ init_channel(channel)
120
+
121
+ mode = @server_config.mode_parser.parse(m)
122
+ mode[:negative].each do |m|
123
+ @channels[channel][:modes].delete(m)
124
+ end
125
+
126
+ mode[:positive].each do |m|
127
+ @channels[channel][:modes] << m
128
+ end
129
+
130
+ @channels[channel][:modes].uniq!
131
+ [mode[:negative], mode[:positive]]
132
+ end
133
+ end
134
+
135
+ # For managing channel
136
+ def init_channel(channel)
137
+ @channels[channel] ||= {
138
+ :modes => [],
139
+ :users => [],
140
+ }
141
+ end
142
+
143
+ end
144
+
@@ -3,7 +3,7 @@ module Net::IRC::Constants # :nodoc:
3
3
  RPL_YOURHOST = '002'
4
4
  RPL_CREATED = '003'
5
5
  RPL_MYINFO = '004'
6
- RPL_BOUNCE = '005'
6
+ RPL_ISUPPORT = '005'
7
7
  RPL_USERHOST = '302'
8
8
  RPL_ISON = '303'
9
9
  RPL_AWAY = '301'
@@ -76,7 +76,7 @@ class Net::IRC::Message
76
76
 
77
77
  # Same as params.
78
78
  def to_a
79
- @params
79
+ @params.dup
80
80
  end
81
81
 
82
82
  # If the message is CTCP, return true.
@@ -96,5 +96,7 @@ class Net::IRC::Message
96
96
  end
97
97
 
98
98
  autoload :ModeParser, "net/irc/message/modeparser"
99
+ autoload :ServerConfig, "net/irc/message/serverconfig"
100
+ autoload :ISupportModeParser, "net/irc/message/isupportmodeparser"
99
101
  end # Message
100
102
 
@@ -1,44 +1,85 @@
1
1
  class Net::IRC::Message::ModeParser
2
2
 
3
- def initialize(require_arg, definition)
4
- @require_arg = require_arg.map {|i| i.to_sym }
5
- @definition = definition
3
+ ONE_PARAM_MASK = 0
4
+ ONE_PARAM = 1
5
+ ONE_PARAM_FOR_POSITIVE = 2
6
+ NO_PARAM = 3
7
+
8
+ def initialize
9
+ @modes = {}
10
+ @op_to_mark_map = {}
11
+ @mark_to_op_map = {}
12
+
13
+ # Initialize for ircd 2.11 (RFC2812+)
14
+ set(:CHANMODES, 'beIR,k,l,imnpstaqr')
15
+ set(:PREFIX, '(ov)@+')
16
+ end
17
+
18
+ def mark_to_op(mark)
19
+ mark.empty? ? nil : @mark_to_op_map[mark.to_sym]
20
+ end
21
+
22
+ def set(key, value)
23
+ case key
24
+ when :PREFIX
25
+ if value =~ /^\(([a-zA-Z]+)\)(.+)$/
26
+ @op_to_mark_map = {}
27
+ key, value = Regexp.last_match[1], Regexp.last_match[2]
28
+ key.scan(/./).zip(value.scan(/./)) {|pair|
29
+ @op_to_mark_map[pair[0].to_sym] = pair[1].to_sym
30
+ }
31
+ @mark_to_op_map = @op_to_mark_map.invert
32
+ end
33
+ when :CHANMODES
34
+ @modes = {}
35
+ value.split(/,/).each_with_index do |s,kind|
36
+ s.scan(/./).each {|c|
37
+ @modes[c.to_sym] = kind
38
+ }
39
+ end
40
+ end
6
41
  end
7
42
 
8
43
  def parse(arg)
9
44
  params = arg.kind_of?(Net::IRC::Message) ? arg.to_a : arg.split(/\s+/)
45
+ params.shift
10
46
 
11
- ret = {
47
+ ret = {
12
48
  :positive => [],
13
49
  :negative => [],
14
50
  }
15
51
 
16
- current = nil, arg_pos = 0
17
- params[1].each_byte do |c|
18
- sym = c.chr.to_sym
19
- case sym
20
- when :+
21
- current = ret[:positive]
22
- when :-
23
- current = ret[:negative]
24
- else
25
- case
26
- when @require_arg.include?(sym)
27
- current << [sym, params[arg_pos + 2]]
28
- arg_pos += 1
29
- when @definition.key?(sym)
30
- current << [sym, nil]
52
+ current = ret[:positive]
53
+ until params.empty?
54
+ s = params.shift
55
+ s.scan(/./).each do |c|
56
+ c = c.to_sym
57
+ case c
58
+ when :+
59
+ current = ret[:positive]
60
+ when :-
61
+ current = ret[:negative]
31
62
  else
32
- # fallback, should raise exception
33
- # but not for convenience
34
- current << [sym, nil]
63
+ case @modes[c]
64
+ when ONE_PARAM_MASK,ONE_PARAM
65
+ current << [c, params.shift]
66
+ when ONE_PARAM_FOR_POSITIVE
67
+ if current.equal?(ret[:positive])
68
+ current << [c, params.shift]
69
+ else
70
+ current << [c, nil]
71
+ end
72
+ when NO_PARAM
73
+ current << [c, nil]
74
+ else
75
+ if @op_to_mark_map[c]
76
+ current << [c, params.shift]
77
+ end
78
+ end
35
79
  end
36
80
  end
37
81
  end
38
82
 
39
83
  ret
40
84
  end
41
-
42
- autoload :RFC1459, "net/irc/message/modeparser/rfc1459"
43
- autoload :Hyperion, "net/irc/message/modeparser/hyperion"
44
85
  end