net-irc 0.0.4 → 0.0.5

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.
@@ -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