failirc 0.0.1

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.
Files changed (48) hide show
  1. data/bin/failbot +162 -0
  2. data/bin/failircd +61 -0
  3. data/lib/failirc.rb +25 -0
  4. data/lib/failirc/client.rb +227 -0
  5. data/lib/failirc/client/channel.rb +98 -0
  6. data/lib/failirc/client/channels.rb +85 -0
  7. data/lib/failirc/client/client.rb +59 -0
  8. data/lib/failirc/client/clients.rb +43 -0
  9. data/lib/failirc/client/dispatcher.rb +209 -0
  10. data/lib/failirc/client/dispatcher/connectiondispatcher.rb +410 -0
  11. data/lib/failirc/client/dispatcher/event.rb +113 -0
  12. data/lib/failirc/client/dispatcher/eventdispatcher.rb +203 -0
  13. data/lib/failirc/client/module.rb +103 -0
  14. data/lib/failirc/client/modules/Base.rb +361 -0
  15. data/lib/failirc/client/modules/Logger.rb +89 -0
  16. data/lib/failirc/client/server.rb +89 -0
  17. data/lib/failirc/client/user.rb +66 -0
  18. data/lib/failirc/client/users.rb +100 -0
  19. data/lib/failirc/errors.rb +339 -0
  20. data/lib/failirc/extensions.rb +124 -0
  21. data/lib/failirc/mask.rb +117 -0
  22. data/lib/failirc/modes.rb +54 -0
  23. data/lib/failirc/responses.rb +360 -0
  24. data/lib/failirc/server.rb +266 -0
  25. data/lib/failirc/server/channel.rb +122 -0
  26. data/lib/failirc/server/channels.rb +84 -0
  27. data/lib/failirc/server/client.rb +100 -0
  28. data/lib/failirc/server/clients.rb +54 -0
  29. data/lib/failirc/server/dispatcher.rb +219 -0
  30. data/lib/failirc/server/dispatcher/connectiondispatcher.rb +477 -0
  31. data/lib/failirc/server/dispatcher/event.rb +113 -0
  32. data/lib/failirc/server/dispatcher/eventdispatcher.rb +196 -0
  33. data/lib/failirc/server/link.rb +50 -0
  34. data/lib/failirc/server/links.rb +49 -0
  35. data/lib/failirc/server/module.rb +103 -0
  36. data/lib/failirc/server/modules/Base.rb +2545 -0
  37. data/lib/failirc/server/modules/Cloaking.rb +170 -0
  38. data/lib/failirc/server/modules/Firewall.rb +104 -0
  39. data/lib/failirc/server/modules/Netlog.rb +67 -0
  40. data/lib/failirc/server/modules/Roulette.rb +78 -0
  41. data/lib/failirc/server/modules/TinyURL.rb +98 -0
  42. data/lib/failirc/server/modules/Translate.rb +62 -0
  43. data/lib/failirc/server/modules/WordFilter.rb +144 -0
  44. data/lib/failirc/server/user.rb +72 -0
  45. data/lib/failirc/server/users.rb +103 -0
  46. data/lib/failirc/sslutils.rb +74 -0
  47. data/lib/failirc/utils.rb +53 -0
  48. metadata +107 -0
@@ -0,0 +1,203 @@
1
+ # failirc, a fail IRC library.
2
+ #
3
+ # Copyleft meh. [http://meh.doesntexist.org | meh.ffff@gmail.com]
4
+ #
5
+ # This file is part of failirc.
6
+ #
7
+ # failirc is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Affero General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # failirc is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Affero General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Affero General Public License
18
+ # along with failirc. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ require 'failirc/utils'
21
+ require 'failirc/client/dispatcher/event'
22
+
23
+ module IRC
24
+
25
+ class Client
26
+
27
+ class Dispatcher
28
+
29
+ class EventDispatcher
30
+ attr_reader :client, :dispatcher, :handling, :aliases, :events
31
+
32
+ def initialize (dispatcher)
33
+ @client = dispatcher.client
34
+ @dispatcher = dispatcher
35
+ @handling = ThreadSafeHash.new
36
+
37
+ @aliases = newAliases
38
+ @events = newEvents
39
+ end
40
+
41
+ def newAliases
42
+ Hash[
43
+ :input => {},
44
+ :output => {},
45
+ ]
46
+ end
47
+
48
+ def newEvents
49
+ Hash[
50
+ :pre => [],
51
+ :post => [],
52
+ :default => [],
53
+
54
+ :custom => {},
55
+
56
+ :input => {},
57
+ :output => {}
58
+ ]
59
+ end
60
+
61
+ def handle (what, chain, deep, server)
62
+ if chain != :input || deep
63
+ return
64
+ end
65
+
66
+ if what == :start
67
+ @handling[server.socket] = true
68
+ else
69
+ @handling.delete(server.socket)
70
+ end
71
+ end
72
+
73
+ def dispatch (chain, server, string, deep=false)
74
+ if !server
75
+ return
76
+ end
77
+
78
+ handle(:start, chain, deep, server)
79
+
80
+ event = Event.new(self, chain, server, string)
81
+ result = string
82
+
83
+ @events[:pre].each {|callback|
84
+ event.special = :pre
85
+
86
+ if callback.call(event, server, string) == false
87
+ handle(:stop, chain, deep, server)
88
+ return false
89
+ end
90
+ }
91
+
92
+ if !event.types.empty?
93
+ event.special = nil
94
+
95
+ event.callbacks.each {|callback|
96
+ begin
97
+ if callback.call(server, string) == false
98
+ handle(:stop, chain, deep, server)
99
+ return false
100
+ end
101
+ rescue Exception => e
102
+ self.debug e
103
+ end
104
+ }
105
+ elsif chain == :input
106
+ @events[:default].each {|callback|
107
+ event.special = :default
108
+
109
+ if callback.call(event, server, string) == false
110
+ handle(:stop, chain, deep, server)
111
+ return false
112
+ end
113
+ }
114
+ end
115
+
116
+ @events[:post].each {|callback|
117
+ event.special = :post
118
+
119
+ if callback.call(event, server, string) == false
120
+ handle(:stop, chain, deep, server)
121
+ return false
122
+ end
123
+ }
124
+
125
+ handle(:stop, chain, deep, server)
126
+
127
+ return result
128
+ end
129
+
130
+ def execute (event, *args)
131
+ if @events[:custom][event]
132
+ @events[:custom][event].each {|callback|
133
+ begin
134
+ if callback.method.call(*args) == false
135
+ return false
136
+ end
137
+ rescue Exception => e
138
+ self.debug e
139
+ end
140
+ }
141
+ end
142
+ end
143
+
144
+ def alias (chain, symbol, regex)
145
+ if !regex
146
+ @aliases[chain].delete(symbol)
147
+ elsif !regex.class == Regexp
148
+ raise 'You have to alias to a Regexp.'
149
+ else
150
+ @aliases[chain][symbol] = regex
151
+ end
152
+ end
153
+
154
+ def register (chain, type, callback, priority=0)
155
+ if !type
156
+ events = @events[chain]
157
+
158
+ if !events
159
+ events = @events[chain] = []
160
+ end
161
+ else
162
+ if @aliases[chain]
163
+ if @aliases[chain][type]
164
+ type = @aliases[chain][type]
165
+ end
166
+ end
167
+
168
+ if !@events[chain]
169
+ @events[chain] = {}
170
+ end
171
+
172
+ events = @events[chain][type]
173
+
174
+ if !events
175
+ events = @events[chain][type] = []
176
+ end
177
+ end
178
+
179
+ if !callback
180
+ events.clear
181
+ elsif callback.is_a?(Array)
182
+ callback.each {|callback|
183
+ register(chain, type, callback)
184
+ }
185
+ else
186
+ if callback.is_a?(Event::Callback)
187
+ events.push(callback)
188
+ else
189
+ events.push(Event::Callback.new(callback, priority))
190
+ end
191
+ end
192
+
193
+ events.sort! {|a, b|
194
+ a.priority <=> b.priority
195
+ }
196
+ end
197
+ end
198
+
199
+ end
200
+
201
+ end
202
+
203
+ end
@@ -0,0 +1,103 @@
1
+ # failirc, a fail IRC library.
2
+ #
3
+ # Copyleft meh. [http://meh.doesntexist.org | meh.ffff@gmail.com]
4
+ #
5
+ # This file is part of failirc.
6
+ #
7
+ # failirc is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Affero General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # failirc is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Affero General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Affero General Public License
18
+ # along with failirc. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ require 'failirc/utils'
21
+
22
+ module IRC
23
+
24
+ class Client
25
+
26
+ class Module
27
+ attr_reader :client
28
+
29
+ def initialize (client)
30
+ @client = client
31
+
32
+ if @aliases
33
+ if @aliases[:input]
34
+ @aliases[:input].each {|key, value|
35
+ @client.dispatcher.alias(:input, key, value)
36
+ }
37
+ end
38
+
39
+ if @aliases[:output]
40
+ @aliases[:output].each {|key, value|
41
+ @client.dispatcher.alias(:output, key, value)
42
+ }
43
+ end
44
+ end
45
+
46
+ if @events
47
+ if @events[:pre]
48
+ @client.dispatcher.register(:pre, nil, @events[:pre])
49
+ end
50
+
51
+ if @events[:post]
52
+ @client.dispatcher.register(:post, nil, @events[:post])
53
+ end
54
+
55
+ if @events[:default]
56
+ @client.dispatcher.register(:default, nil, @events[:default])
57
+ end
58
+
59
+ if @events[:custom]
60
+ @events[:custom].each {|key, value|
61
+ @client.dispatcher.register(:custom, key, value)
62
+ }
63
+ end
64
+
65
+ if @events[:input]
66
+ @events[:input].each {|key, value|
67
+ @client.dispatcher.register(:input, key, value)
68
+ }
69
+ end
70
+
71
+ if @events[:output]
72
+ @events[:output].each {|key, value|
73
+ @client.dispatcher.register(:output, key, value)
74
+ }
75
+ end
76
+ end
77
+
78
+ begin
79
+ rehash
80
+ rescue NameError
81
+ rescue Exception => e
82
+ self.debug e
83
+ end
84
+ end
85
+
86
+ def finalize
87
+ if @aliases
88
+ @aliases.each_key {|key|
89
+ @client.dispatcher.alias(key, nil)
90
+ }
91
+ end
92
+
93
+ if @events
94
+ @events.each_key {|key|
95
+ @client.dispatcher.register(key, nil)
96
+ }
97
+ end
98
+ end
99
+ end
100
+
101
+ end
102
+
103
+ end
@@ -0,0 +1,361 @@
1
+ # failirc, a fail IRC library.
2
+ #
3
+ # Copyleft meh. [http://meh.doesntexist.org | meh.ffff@gmail.com]
4
+ #
5
+ # This file is part of failirc.
6
+ #
7
+ # failirc is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Affero General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # failirc is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Affero General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Affero General Public License
18
+ # along with failirc. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ require 'failirc/utils'
21
+ require 'failirc/mask'
22
+ require 'failirc/errors'
23
+ require 'failirc/responses'
24
+
25
+ require 'failirc/client/module'
26
+
27
+ require 'failirc/client/channel'
28
+ require 'failirc/client/user'
29
+ require 'failirc/client/client'
30
+
31
+ module IRC
32
+
33
+ class Client
34
+
35
+ module Modules
36
+
37
+ class Base < Module
38
+ @@version = '0.0.1'
39
+
40
+ @@supported = {}
41
+
42
+ def self.version
43
+ @@version
44
+ end
45
+
46
+ def self.supported
47
+ @@supported
48
+ end
49
+
50
+ def description
51
+ "Base-#{Base.version}"
52
+ end
53
+
54
+ def initialize (client)
55
+ @aliases = {
56
+ :input => {
57
+ :NUMERIC => /^:([^ ]+)\s+(\d{3})\s+(.+)/,
58
+
59
+ :PING => /^PING( |$)/i,
60
+
61
+ :JOIN => /^:.+?\s+JOIN\s+:./i,
62
+
63
+ :PRIVMSG => /^:.+?\s+PRIVMSG\s+.+?\s+:/i,
64
+ },
65
+ }
66
+
67
+ @events = {
68
+ :custom => {
69
+ :connect => self.method(:connect),
70
+ :numeric => self.method(:received_numeric),
71
+
72
+ :join => self.method(:join),
73
+ :who => self.method(:who),
74
+
75
+ :message => self.method(:send_message),
76
+
77
+ :quit => self.method(:do_quit),
78
+ },
79
+
80
+ :input => {
81
+ :NUMERIC => self.method(:numeric),
82
+
83
+ :PING => self.method(:pong),
84
+
85
+ :JOIN => self.method(:joined),
86
+
87
+ :PRIVMSG => self.method(:message),
88
+ },
89
+ }
90
+
91
+ super(client)
92
+ end
93
+
94
+ module Utils
95
+ module Server
96
+ @@defaultSupportedModes = [
97
+ { :mode => 'o', :status => '@' },
98
+ { :mode => 'h', :status => '%' },
99
+ { :mode => 'v', :status => '+' }
100
+ ]
101
+
102
+ def self.supportedModes
103
+ if !Base.supported['PREFIX']
104
+ return @@defaultSupportedModes
105
+ else
106
+ match = Base.supported['PREFIX'].match(/\((.*?)\)(.*)$/)
107
+
108
+ if !match
109
+ return @@defaultSupportedModes
110
+ else
111
+ modes = match[1].split
112
+ statuses = match[2].split
113
+ result = []
114
+
115
+ 1.upto(modes.length) {|f|
116
+ result.push({
117
+ :mode => modes.shift,
118
+ :status => statuses.shift,
119
+ })
120
+ }
121
+
122
+ return result
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+ module Channel
129
+ def self.isValid (string)
130
+ string.match(/^[&#+!][^ ,:\a]{0,50}$/) ? true : false
131
+ end
132
+ end
133
+ end
134
+
135
+ def connect (server)
136
+ if server.password
137
+ server.send :raw, "PASS #{server.password}"
138
+ end
139
+
140
+ server.send :raw, "NICK #{client.nick}"
141
+ server.send :raw, "USER #{client.user} * * :#{client.realName}"
142
+ end
143
+
144
+ def pong (server, string)
145
+ match = string.match(/PING\s+(.*)$/)
146
+
147
+ if match
148
+ server.send :raw, "PONG #{match[1]}"
149
+ end
150
+ end
151
+
152
+ def numeric (server, string)
153
+ match = string.match(/^:[^ ]+\s+(\d{3})\s+.+?(\s+(.*))?$/)
154
+
155
+ if match
156
+ client.dispatcher.execute :numeric, server, match[1].to_i, match[3]
157
+ end
158
+ end
159
+
160
+ def received_numeric (server, number, message)
161
+ case number
162
+
163
+ # stuff supported by the server
164
+ when 5
165
+ message.split(/\s+/).each {|support|
166
+ support = support.split(/=/)
167
+
168
+ Base.supported[support[0]] = support[1]
169
+ }
170
+
171
+ # end of MOTD or no MOTD, hence concluded connection.
172
+ when 376, 422
173
+ client.dispatcher.execute :connected, server
174
+
175
+ # WHO reply
176
+ when 352
177
+ match = message.match(/(.+?)\s+(.+?)\s+(.+?)\s+.+?\s+(.+?)\s+.+?\s+:\d+\s+(.+)$/)
178
+
179
+ if !match
180
+ return
181
+ end
182
+
183
+ channel = match[1]
184
+ mask = Mask.new(match[4], match[2], match[3])
185
+ realName = match[5]
186
+
187
+ if !server.channels[channel]
188
+ server.channels[channel] = Channel.new(server, channel)
189
+ end
190
+
191
+ channel = server.channels[channel]
192
+
193
+ if mask.nick == server.nick
194
+ return
195
+ end
196
+
197
+ if !server.clients[mask.nick] || server.clients[mask.nick].mask != mask
198
+ server.clients[mask.nick] = Client.new(server, mask)
199
+ server.clients[mask.nick].realName = realName
200
+ end
201
+
202
+ channel.add(server.clients[mask.nick])
203
+
204
+ end
205
+ end
206
+
207
+ def join (server, channel, password=nil)
208
+ text = "JOIN #{channel}"
209
+
210
+ if password
211
+ text << " #{password}"
212
+ end
213
+
214
+ server.send :raw, text
215
+ client.execute :who, server, channel
216
+ end
217
+
218
+ def joined (server, string)
219
+ match = string.match(/^:(.+?)\s+JOIN\s+:(.+)$/i)
220
+
221
+ if !match
222
+ return
223
+ end
224
+
225
+ mask = Mask.parse(match[1])
226
+ channel = match[2]
227
+
228
+ if mask.nick == server.nick
229
+ if !server.channels[channel]
230
+ server.channels[channel] = Channel.new(server, channel)
231
+ end
232
+ else
233
+ if !server.clients[mask.nick] || server.clients[mask.nick].mask != mask
234
+ server.clients[mask.nick] = Client.new(server, mask)
235
+ end
236
+
237
+ if server.channels[channel]
238
+ server.channels[channel].add(server.clients[mask.nick])
239
+ end
240
+ end
241
+ end
242
+
243
+ def who (server, channel)
244
+ server.send :raw, "WHO #{channel}"
245
+ end
246
+
247
+ def message (server, string)
248
+ match = string.match(/:(.+?)\s+PRIVMSG\s+(.+)\s+:(.*)$/i)
249
+
250
+ if !match
251
+ return
252
+ end
253
+
254
+ from = Mask.parse(match[1])
255
+
256
+ if !server.clients[from.nick] || server.clients[from.nick] != from
257
+ from = server.clients[from.nick] = Client.new(server, from)
258
+ else
259
+ from = server.clients[from.nick]
260
+ end
261
+
262
+ to = match[2]
263
+
264
+ if Utils::Channel::isValid(to)
265
+ to = server.channels[to]
266
+ from = to.user from
267
+ else
268
+ to = client
269
+ end
270
+
271
+ message = match[3]
272
+
273
+ if match = message.match(/^\x01([^ ]*)( (.*?))?\x01$/)
274
+ client.execute :ctcp, server, :message, from, to, match[1], match[3]
275
+ else
276
+ client.execute :message, server, :input, from, to, message
277
+ end
278
+ end
279
+
280
+ def send_message (server, chain, from, to, message)
281
+ if chain != :output
282
+ return
283
+ end
284
+
285
+ if to.is_a?(Channel)
286
+ name = to.name
287
+ elsif to.is_a?(Client)
288
+ name = to.nick
289
+ else
290
+ name = to.to_s
291
+ end
292
+
293
+ server.send :raw, "PRIVMSG #{name} :#{message}"
294
+ end
295
+
296
+ def notice (server, string)
297
+ match = string.match(/:(.+?)\s+NOTICE\s+(.+)\s+:(.*)$/i)
298
+
299
+ if !match
300
+ return
301
+ end
302
+
303
+ from = Mask.parse(match[1])
304
+
305
+ if !server.clients[from.nick] || server.clients[from.nick] != from
306
+ from = server.clients[from.nick] = Client.new(server, from)
307
+ else
308
+ from = server.clients[from.nick]
309
+ end
310
+
311
+ to = match[2]
312
+
313
+ if Utils::Channel::isValid(to)
314
+ to = server.channels[to]
315
+ from = to.user from
316
+ else
317
+ to = client
318
+ end
319
+
320
+ message = match[3]
321
+
322
+ if match = message.match(/^\x01([^ ]*)( (.*?))?\x01$/)
323
+ client.execute :ctcp, server, :notice, from, to, match[1], match[3]
324
+ else
325
+ client.execute :message, server, :input, from, to, message
326
+ end
327
+ end
328
+
329
+
330
+ def send_notice (server, chain, from, to, message, level=nil)
331
+ if chain != :output
332
+ return
333
+ end
334
+
335
+ if to.is_a?(Channel)
336
+ name = "#{level}#{to.name}"
337
+ elsif to.is_a?(Client)
338
+ name = to.nick
339
+ end
340
+
341
+ server.send :raw, "NOTICE #{name} :#{message}"
342
+ end
343
+
344
+ def send_ctcp (server, kind, chain, from, to, type, message, level=nil)
345
+
346
+ end
347
+
348
+ def do_quit (server, message)
349
+ if message
350
+ server.send :raw, "QUIT #{message}"
351
+ else
352
+ server.send :raw, "QUIT"
353
+ end
354
+ end
355
+ end
356
+
357
+ end
358
+
359
+ end
360
+
361
+ end