spox-mod_spox 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/CHANGELOG +5 -0
  2. data/README.rdoc +61 -1
  3. data/bin/mod_spox +1 -7
  4. data/data/mod_spox/extras/AutoKick.rb +3 -2
  5. data/data/mod_spox/extras/AutoMode.rb +2 -1
  6. data/data/mod_spox/extras/AutoRejoin.rb +5 -4
  7. data/data/mod_spox/extras/Bouncer.rb +243 -131
  8. data/data/mod_spox/extras/FloodKicker.rb +2 -1
  9. data/data/mod_spox/extras/Fortune.rb +5 -1
  10. data/data/mod_spox/extras/Karma.rb +2 -1
  11. data/data/mod_spox/extras/Logger.rb +11 -9
  12. data/data/mod_spox/extras/NickServ.rb +2 -1
  13. data/data/mod_spox/extras/PhpCli.rb +1 -1
  14. data/data/mod_spox/extras/PhpFuncLookup.rb +2 -1
  15. data/data/mod_spox/extras/RegexTracker.rb +2 -1
  16. data/data/mod_spox/extras/Roulette.rb +2 -1
  17. data/data/mod_spox/extras/Seen.rb +10 -8
  18. data/data/mod_spox/extras/Topten.rb +2 -1
  19. data/data/mod_spox/extras/Translate.rb +2 -1
  20. data/data/mod_spox/extras/Twitter.rb +1 -1
  21. data/data/mod_spox/plugins/Authenticator.rb +7 -7
  22. data/data/mod_spox/plugins/Banner.rb +6 -6
  23. data/data/mod_spox/plugins/BotNick.rb +2 -1
  24. data/data/mod_spox/plugins/Initializer.rb +4 -4
  25. data/data/mod_spox/plugins/Joiner.rb +1 -1
  26. data/data/mod_spox/plugins/PluginLoader.rb +1 -1
  27. data/data/mod_spox/plugins/Ponger.rb +8 -8
  28. data/data/mod_spox/plugins/Status.rb +1 -1
  29. data/lib/mod_spox/Bot.rb +27 -27
  30. data/lib/mod_spox/BotConfig.rb +17 -21
  31. data/lib/mod_spox/Filter.rb +29 -0
  32. data/lib/mod_spox/FilterManager.rb +63 -0
  33. data/lib/mod_spox/Helpers.rb +120 -14
  34. data/lib/mod_spox/Loader.rb +1 -1
  35. data/lib/mod_spox/MessageFactory.rb +10 -2
  36. data/lib/mod_spox/Pipeline.rb +66 -61
  37. data/lib/mod_spox/PluginManager.rb +5 -5
  38. data/lib/mod_spox/PriorityQueue.rb +21 -8
  39. data/lib/mod_spox/Sockets.rb +6 -6
  40. data/lib/mod_spox/Timer.rb +3 -3
  41. data/lib/mod_spox/Version.rb +2 -2
  42. data/lib/mod_spox/handlers/UserHost.rb +32 -0
  43. data/lib/mod_spox/handlers/Whois.rb +7 -7
  44. data/lib/mod_spox/messages/incoming/Pong.rb +11 -2
  45. data/lib/mod_spox/messages/incoming/UserHost.rb +24 -0
  46. data/lib/mod_spox/messages/internal/FilterAdd.rb +20 -0
  47. data/lib/mod_spox/messages/internal/FilterList.rb +18 -0
  48. data/lib/mod_spox/messages/internal/FilterListing.rb +22 -0
  49. data/lib/mod_spox/messages/internal/FilterRemove.rb +20 -0
  50. data/lib/mod_spox/messages/internal/Incoming.rb +15 -0
  51. data/lib/mod_spox/migrations/006_ignore.rb +21 -0
  52. data/lib/mod_spox/models/Nick.rb +11 -0
  53. data/lib/mod_spox/rfc2812.rb +2 -1
  54. data/lib/mod_spox/rfc2812_full.rb +185 -0
  55. data/tests/BotHolder.rb +7 -1
  56. data/tests/handlers/tc_Created.rb +28 -8
  57. data/tests/handlers/tc_Invite.rb +11 -9
  58. data/tests/handlers/tc_Join.rb +36 -16
  59. data/tests/handlers/tc_Kick.rb +27 -6
  60. data/tests/handlers/tc_Mode.rb +23 -13
  61. data/tests/handlers/tc_Names.rb +29 -9
  62. data/tests/handlers/tc_Nick.rb +30 -8
  63. data/tests/handlers/tc_Part.rb +30 -20
  64. data/tests/handlers/tc_Ping.rb +32 -19
  65. data/tests/handlers/tc_Pong.rb +28 -8
  66. data/tests/handlers/tc_Privmsg.rb +33 -13
  67. data/tests/handlers/tc_Quit.rb +30 -17
  68. data/tests/handlers/tc_Who.rb +8 -3
  69. data/tests/handlers/tc_Whois.rb +7 -3
  70. data/tests/lib/tc_BotConfig.rb +35 -0
  71. data/tests/lib/tc_Helpers.rb +139 -0
  72. data/tests/lib/tc_PriorityQueue.rb +31 -0
  73. metadata +17 -2
data/CHANGELOG CHANGED
@@ -1,3 +1,8 @@
1
+ 0.3.2 (Alpha status)
2
+ Core:
3
+ * New tests resulting in lots of bug fixing
4
+ * New and untested/documented message filtering
5
+
1
6
  0.3.1 Release (Alpha status)
2
7
  Core:
3
8
  * As needed class loading
data/README.rdoc CHANGED
@@ -6,6 +6,65 @@ The core of mod_spox is basically a framework for interacting with IRC. By itsel
6
6
 
7
7
  The framework provided by mod_spox allows it to quickly transform from a useless bag of bytes to something entertaining, annoying, helpful, and aggravating, occasionally all at the same time. This transformation is accomplished through the use of plugins which use the framework to accomplish feats of greatness.
8
8
 
9
+ Included with mod_spox are a variety of plugins to help make the bot a bit more interesting.
10
+
11
+ * Authenticator - Authenticate users for running commands
12
+ * Banner - Pattern matching bans. Timed bans. Ban accumulation.
13
+ * BotNick - Sends nick information to the server
14
+ * Helper - Provide help on various plugins
15
+ * Initializer - Gets the bot all ready on startup
16
+ * Joiner - Join a channel
17
+ * Nicker - Change the bot's nick
18
+ * Parter - Part a channel
19
+ * Permissions - Change permissions on triggers
20
+ * PluginLoader - Load and unload plugins
21
+ * Ponger - Send PONG replies
22
+ * Quitter - Quit
23
+ * Servers - Server configuration
24
+ * Status - Show bot status and version information
25
+ * Triggers - Show, add, remove triggers
26
+
27
+ Wow. So many amazing plugins that some how seem to make the bot even more boring than it was when it was completely useless. Fear not, for there are a variety of optional plugins designed to strip the boring and replace it with rainbows and unicorns.
28
+
29
+ * AOLSpeak - Speak in AOL. Kick in AOL.
30
+ * AutoKick - Kick/Ban based on message pattern matching
31
+ * AutoMode - Automatically set nicks +o or +v
32
+ * AutoRejoin - Rejoin channel on KICK or connection automatically
33
+ * Bash - Grab a bash.org quote
34
+ * Bullshit - Spout bullshit
35
+ * Bytes - Convert bytes to human readable
36
+ * Confess - Show confessions
37
+ * DevWatch - Report changes on a trac timeline
38
+ * DownForEveryoneOrJustMe - Check if a site is down
39
+ * EightBall - Let mod_spox answer life's most difficult questions
40
+ * FML - Assurance that life could be worse
41
+ * Fortune - Random fortunes from fortune files
42
+ * GoogleIt - Perform search for people who are unable
43
+ * Headers - Show headers from a given site
44
+ * Karma - Tally the karma of things.
45
+ * Locator - Locate a nick
46
+ * Logger - Log a channel
47
+ * LolSpeak - Translate text to LOL
48
+ * NickServ - Identify with NickServ
49
+ * PhpCli - Execute PHP code
50
+ * PhpFuncLookup - Lookup and display PHP functions
51
+ * Pinger - Send pong reply to nick
52
+ * Quotes - Store channel quotes
53
+ * RegexTracker - Track how often a pattern appears in channel
54
+ * Roulette - Ask mod_spox to shoot you
55
+ * RubyCli - Execute Ruby code
56
+ * Search - Search the web for terms
57
+ * Seen - Keep sighting log of nicks
58
+ * SlashdotHeadlineGenerator - Generate Slashdot headlines
59
+ * Slashdot - Show current Slashdot headlines
60
+ * Talk - Make mod_spox talk
61
+ * Topten - Current chattiest people
62
+ * TracTicket - Add new tickets to trac
63
+ * Translate - Translate text. Also provides automatic translation
64
+ * Twitter - Display status message of twits to channel
65
+ * UrbanDictionary - The only dictionary that matters on IRC
66
+ * Weather - Show the weather
67
+
9
68
  == Critical informations
10
69
 
11
70
  * Author: spox <spox@rubyforge.org>
@@ -15,7 +74,8 @@ The framework provided by mod_spox allows it to quickly transform from a useless
15
74
 
16
75
  == Aquiring
17
76
 
18
- * Gem: gem install mod_spox
77
+ Gem:
78
+ gem install mod_spox
19
79
 
20
80
  == Aquiring current unstable
21
81
 
data/bin/mod_spox CHANGED
@@ -60,17 +60,11 @@ opts.each do |opt, arg|
60
60
  puts '--help -h: print this help message'
61
61
  exit
62
62
  when '--version'
63
- puts "mod_spox IRC bot version: #{$BOTVERSION}"
63
+ puts "mod_spox IRC bot version: #{ModSpox.botversion}"
64
64
  puts 'http://rubyforge.org/projects/modspox'
65
65
  exit
66
66
  when '--jdbc'
67
67
  ModSpox.jdbc = true
68
- begin
69
- require 'jdbc/postgres'
70
- rescue Object => boom
71
- puts 'Failed to load JDBC Postgres gem'
72
- exit 1
73
- end
74
68
  when '--debug'
75
69
  if(arg && arg.gsub(' ', '').size > 0)
76
70
  ModSpox.logto = arg
@@ -4,6 +4,7 @@ class AutoKick < ModSpox::Plugin
4
4
 
5
5
  def initialize(pipeline)
6
6
  super
7
+ Helpers.load_message(:incoming, :Privmsg)
7
8
  group = Group.find_or_create(:name => 'autokick')
8
9
  add_sig(:sig => 'autokick list', :method => :list, :group => group, :desc => 'List active autokick rules')
9
10
  add_sig(:sig => 'autokick add (#\S+) (\d+) (\S+) (.+)', :method => :add, :group => group, :desc => 'Add new autokick rule for current channel', :req => 'public', :params => [:channel, :time, :regex, :message])
@@ -108,7 +109,7 @@ class AutoKick < ModSpox::Plugin
108
109
  def do_listen
109
110
  @map = nil
110
111
  begin
111
- @pipeline.unhook(self, :listener, :Incoming_Privmsg)
112
+ @pipeline.unhook(self, :listener, ModSpox::Messages::Incoming::Privmsg)
112
113
  rescue Object => boom
113
114
  #ignore
114
115
  end
@@ -119,7 +120,7 @@ class AutoKick < ModSpox::Plugin
119
120
  @map[record.channel_id] = [] unless @map[record.channel_id]
120
121
  @map[record.channel_id] << record.pattern
121
122
  end
122
- @pipeline.hook(self, :listener, :Incoming_Privmsg)
123
+ @pipeline.hook(self, :listener, ModSpox::Messages::Incoming::Privmsg)
123
124
  end
124
125
  end
125
126
 
@@ -14,7 +14,8 @@ class AutoMode < ModSpox::Plugin
14
14
  add_sig(:sig => 'automode list (\S+)', :method => :list, :group => @admin, :desc => 'Show list of current auto-modes for channel', :params => [:channel])
15
15
  add_sig(:sig => 'delmode (\S+)', :method => :remove, :group => @admin, :desc => 'Remove nick from any auto-modes in channel', :req => 'public', :params => [:nick])
16
16
  ModeRecord.create_table unless ModeRecord.table_exists?
17
- @pipeline.hook(self, :check_join, :Incoming_Join)
17
+ Helpers.load_message(:incoming, :Join)
18
+ @pipeline.hook(self, :check_join, ModSpox::Messages::Incoming::Join)
18
19
  end
19
20
 
20
21
  def addop(m, p)
@@ -4,10 +4,11 @@ class AutoRejoin < ModSpox::Plugin
4
4
 
5
5
  def initialize(pipeline)
6
6
  super
7
- @pipeline.hook(self, :check_kick, :Incoming_Kick)
8
- @pipeline.hook(self, :check_join, :Incoming_Join)
9
- @pipeline.hook(self, :check_part, :Incoming_Part)
10
- @pipeline.hook(self, :do_joins, :Incoming_Welcome)
7
+ [:Kick, :Join, :Part, :Welcome].each{|t| Helpers.load_message(:incoming, t)}
8
+ @pipeline.hook(self, :check_kick, ModSpox::Messages::Incoming::Kick)
9
+ @pipeline.hook(self, :check_join, ModSpox::Messages::Incoming::Join)
10
+ @pipeline.hook(self, :check_part, ModSpox::Messages::Incoming::Part)
11
+ @pipeline.hook(self, :do_joins, ModSpox::Messages::Incoming::Welcome)
11
12
  end
12
13
 
13
14
  def check_kick(message)
@@ -1,69 +1,95 @@
1
1
  require 'openssl'
2
2
 
3
+ ## config stuffs: port
4
+
3
5
  class Bouncer < ModSpox::Plugin
4
6
 
7
+ # Setup our plugin
5
8
  def initialize(pipeline)
6
9
  super
7
10
  bounce = Group.find_or_create(:name => 'bouncer')
8
- add_sig(:sig => 'bouncer port( (\d+))?', :method => :port, :group => bounce, :desc => 'Show or set bouncer port',
9
- :params => [:port])
10
- add_sig(:sig => 'bouncer (start|stop|restart)', :method => :do_service, :group => bounce, :desc => 'Start, stop, or restart bouncer')
11
+ add_sig(:sig => 'bouncer port( (\d+))?', :method => :set_port, :group => bounce, :desc => 'Show or set bouncer port', :params => [:port])
12
+ add_sig(:sig => 'bouncer (start|stop|restart)', :method => :do_service, :group => bounce, :desc => 'Start, stop, or restart bouncer', :params => [:action])
11
13
  add_sig(:sig => 'bouncer status', :method => :status, :group => bounce, :desc => 'Show current bouncer status')
12
- add_sig(:sig => 'bouncer disconnect', :method => :disconnect, :group => bounce, :desc => 'Disconnect all connected clients')
13
- add_sig(:sig => 'bouncer clients'< :method => :clients, :group => bounce, :desc => 'List clients connected to bouncer')
14
- @pipeline.hook(self, :get_msgs, :Incoming)
15
- @listener = nil
16
- @clients = []
17
- @socket = nil
18
- @processor = nil
19
- @to_server = Queue.new
20
- start_listener if Models::Config.filter(:name => 'bouncer_port').count > 0
14
+ add_sig(:sig => 'bouncer disconnect', :method => :do_disconnect, :group => bounce, :desc => 'Disconnect all connected clients')
15
+ add_sig(:sig => 'bouncer clients', :method => :clients, :group => bounce, :desc => 'List clients connected to bouncer')
16
+ add_sig(:sig => 'bouncer generate cert', :method => :certgen, :group => bounce, :desc => 'Generate new certification')
17
+ @pipeline.hook(self, :get_msgs, ModSpox::Messages::Incoming)
18
+ @spockets = Spockets::Spockets.new
19
+ @listener_thread = nil
20
+ @listener_socket = nil
21
+ @clients = {}
22
+ Helpers.load_message(:outgoing, :Raw)
23
+ Helpers.load_message(:internal, :Incoming)
24
+ @filters = {}
25
+ methods.each do |f|
26
+ f = f.to_s
27
+ z = f.slice!(0..6)
28
+ @filters[f.to_sym] = (z+f).to_sym if z == 'filter_'
29
+ end
30
+ start_listener unless port.nil?
31
+ end
32
+
33
+ # Clean up our socket stuffs
34
+ def destroy
35
+ stop_listener
36
+ disconnect
37
+ end
38
+
39
+ # m:: message
40
+ # params:: parameters
41
+ # Set/show current listening port
42
+ def set_port(m, params)
43
+ begin
44
+ if(params[:port])
45
+ params[:port] = params[:port].to_i
46
+ port(params[:port])
47
+ information m.replyto, "Bouncer port has been updated to: #{params[:port] == 0 ? 'disabled' : params[:port].to_s}"
48
+ warning m.replyto, 'Bouncer must be restarted for port to be changed' if running?
49
+ else
50
+ if(port)
51
+ information m.replyto, "Bouncer is set to listen on port: #{port}"
52
+ else
53
+ warning m.replyto, "Bouncer port has not been set"
54
+ end
55
+ end
56
+ rescue Object => boom
57
+ error m.replyto, "Failed to update bouncer port. Reason: #{boom}"
58
+ end
21
59
  end
22
60
 
61
+ # message:: ModSpox::Messages::Incoming types
62
+ # Get all incoming messages to pass on to clients
23
63
  def get_msgs(message)
24
64
  unless(@clients.empty?)
25
65
  Logger.info("Bouncer has #{@clients.size} clients to send a message to")
26
- @clients.each do |client|
66
+ @clients.keys.each do |client|
27
67
  begin
28
68
  output = message.raw_content.is_a?(Array) ? message.raw_content.join("\n") : message.raw_content + "\n"
29
- client[:connection].puts(output)
69
+ client.puts(output)
30
70
  rescue Object => boom
31
71
  Logger.warn("Bouncer encountered unexpected error. Disconnecting client. #{boom}")
32
- client[:thread].kill if client[:thread].alive?
33
- @clients.delete(client)
72
+ disconnect(client)
34
73
  end
35
74
  end
36
75
  end
37
76
  end
38
-
39
- def port(m, params)
40
- if(params[:port])
41
- if(Models::Config.filter(:name => 'bouncer_port').count > 0)
42
- reply m.replyto, "Bouncer port is set to: #{Models::Config.filter(:name => 'bouncer_port').first.value
43
- else
44
- error m.replyo, 'No port has been set for bouncer'
45
- end
46
- else
47
- parmas[:port].strip!
48
- c = Models::Config.find_or_create(:name => 'bouncer_port')
49
- c.value = params[:port]
50
- c.save
51
- information m.replyto, "Bouncer port is not set to: #{params[:port]}"
52
- end
53
- end
54
-
77
+
78
+ # m:: message
79
+ # params:: parameters
80
+ # Start/stop/restart bouncer
55
81
  def do_service(m, params)
56
82
  begin
57
83
  case params[:action]
58
84
  when 'start'
59
- if(listening?)
85
+ if(running?)
60
86
  error m.replyto, 'Bouncer is already running'
61
87
  else
62
88
  start_listener
63
89
  information m.replyto, 'Bouncer has been started'
64
90
  end
65
91
  when 'stop'
66
- if(listening?)
92
+ if(running?)
67
93
  stop_listener
68
94
  information m.replyto, 'Bouncer has been stopped'
69
95
  else
@@ -79,141 +105,227 @@ class Bouncer < ModSpox::Plugin
79
105
  end
80
106
  end
81
107
 
82
- def disconnect(m, params)
108
+ # m:: message
109
+ # params:: parameters
110
+ # Disconnect all clients from bouncer
111
+ # TODO: add individual disconnects
112
+ def do_disconnect(m, params)
83
113
  unless(@clients.empty?)
84
- @clients.each do |client|
85
- client[:connection].close unless client[:connection].closed?
86
- @clients.delete(client)
87
- end
114
+ disconnect
88
115
  information m.replyto, 'All clients have been disconnected'
89
116
  else
90
117
  warning m.replyto, 'No clients are connected to bouncer'
91
118
  end
92
119
  end
93
-
120
+
121
+ # m:: message
122
+ # params:: parameters
123
+ # Display current bot status
94
124
  def status(m, params)
95
- warning m.replyto, 'not implemented'
125
+ information m.replyto, "Status: #{running? ? 'listening' : 'stopped'}"
96
126
  end
97
-
98
- private
99
127
 
100
- def start_listener
101
- port = Models::Config.filter(:name => 'bouncer_port').first
102
- raise 'Port has not been set' unless port
103
- port = port.value.to_i
104
- @socket = OpenSSL::SSL::SSLServer.new(port)
105
- @listener = Thread.new do
106
- until(@socket.closed?)
107
- begin
108
- new_con = @socket.accept_nonblock
109
- add_client(new_con)
110
- rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINTR
111
- IO.select([@socket])
112
- retry
113
- rescue Object => boom
114
- Logger.warn "Bouncer listener encountered an error: #{boom}"
115
- end
116
- end
128
+ # m:: message
129
+ # params:: parameters
130
+ # Generate a new certificate
131
+ def certgen(m, params)
132
+ begin
133
+ create_certs
134
+ information m.replyto, 'New certificate has been generated'
135
+ rescue Object
136
+ error m.replyto, "Failed to create new certificate: #{boom}"
117
137
  end
118
138
  end
119
-
120
- def add_client
139
+
140
+ private
141
+
142
+ # Generates an OpenSSL::SSL::SSLContext
143
+ def generate_ctx
144
+ c = Models::Setting.filter(:name => 'bouncer_ssl').first
145
+ sc = nil
146
+ if(c)
147
+ cert = c.value
148
+ sc = OpenSSL::SSL::SSLContext.new
149
+ sc.key = OpenSSL::PKey::RSA.new(cert[:key])
150
+ sc.cert = OpenSSL::X509::Certificate.new(cert[:cert])
151
+ else
152
+ create_certs
153
+ sc = generate_ctx
154
+ end
155
+ return sc
121
156
  end
122
157
 
123
- class Bouncer < ModSpox::Plugin
124
-
125
- def status(message, params)
158
+ # Generates and stores new key and certificate
159
+ def create_certs
160
+ Logger.info('Generating key/cert pair for bouncer SSL')
161
+ key = OpenSSL::PKey::RSA.new(2048)
162
+ cert = OpenSSL::X509::Certificate.new
163
+ cert.not_before = Time.now
164
+ cert.not_after = Time.now + 99999999
165
+ cert.public_key = key.public_key
166
+ cert.sign(key, OpenSSL::Digest::SHA1.new)
167
+ c = Models::Setting.find_or_create(:name => 'bouncer_ssl')
168
+ c.value = {:cert => cert.to_s, :key => key.to_s}
169
+ c.save
126
170
  end
127
-
128
- private
129
-
171
+
172
+ # Starts the listener for new connections
130
173
  def start_listener
131
- port = Config[:bouncer_port]
132
- if(port)
133
- @socket = TCPServer.new(port)
174
+ raise 'Port has not been set' if port.nil?
175
+ begin
176
+ @socket = OpenSSL::SSL::SSLServer.new(TCPServer.new(port), generate_ctx)
134
177
  @listener = Thread.new do
135
- until(@socket.closed?)
178
+ until(@socket.closed?) do
136
179
  begin
137
- new_con = @socket.accept_nonblock
138
- Logger.info("BOUNCER: New connection established on bouncer")
139
- @clients << {
140
- :connection => new_con,
141
- :thread => Thread.new(new_con) do | con |
142
- begin
143
- Logger.info("CONNECTION: #{con}")
144
- until(con.closed?)
145
- Logger.info("WAITING FOR STUFF ON :#{con}")
146
- Kernel.select([con], nil, nil, nil)
147
- Logger.info("Woken up and ready to read")
148
- string = con.gets
149
- Logger.info("BOUNCER GOT MESSAGE: #{string}")
150
- if(string.empty?)
151
- raise Exception.new("EMPTY STRING")
152
- else
153
- @to_server << {:message => string, :socket => con}
154
- end
155
- end
156
- rescue Object => boom
157
- Logger.warn("THREAD BOUNCER ERROR: #{boom}")
158
- end
159
- end
160
- }
161
- rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINTR
162
- IO.select([@socket])
180
+ new_con = @socket.accept
181
+ add_client(new_con)
182
+ rescue Object => boom
183
+ Logger.warn "Bouncer listener encountered an error: #{boom}"
163
184
  retry
164
185
  end
165
186
  end
166
187
  @listener = nil
167
188
  end
168
- start_processor
169
- else
170
- Logger.warn("Error: Bouncer was not started. Failed to find set port number")
189
+ rescue Object => boom
190
+ Logger.error "Bouncer listener encountered an error: #{boom}"
191
+ raise boom
171
192
  end
172
193
  end
173
-
174
- def stop_listener
175
- @socket.close
176
- @listener.kill unless @listener.nil?
177
- @listener = nil
178
- @to_server.clear
179
- @processor.kill if !@processor.nil? && @processor.alive?
180
- @processor = nil
194
+
195
+ # socket:: SSLSocket
196
+ # Adds a new client to the bouncer
197
+ def add_client(socket)
198
+ @clients[socket] = {:nick => false, :user => false, :init => false}
199
+ @spockets.add(socket) do |string|
200
+ deliver_message(string, socket)
201
+ end
181
202
  end
182
-
183
- def start_processor
184
- @processor.kill if !@processor.nil? && @processor.alive?
185
- @processor = Thread.new do
203
+
204
+ # string:: outgoing string
205
+ # socket:: SSLSocket that sent the string
206
+ # Delivers messages from clients to the IRC
207
+ # server
208
+ def deliver_message(string, socket)
209
+ Logger.info("Processing bouncer message: #{string} for socket: #{socket}")
186
210
  begin
187
- while(@listener) do
188
- info = @to_server.pop
189
- Logger.info("Processing message: #{info[:message]}")
190
- if(info[:message] =~ /^USER\s/i)
191
- initialize_connection(info[:socket])
211
+ unless(@clients[socket][:init])
212
+ part = string.slice(0,5)
213
+ Logger.info "Part we extracted: #{part}|"
214
+ @clients[socket][:nick] = true if part == 'NICK '
215
+ @clients[socket][:user] = true if part == 'USER '
216
+ if(@clients[socket][:nick] && @clients[socket][:user])
217
+ initialize_connection(socket)
218
+ @clients[socket][:init] = true
192
219
  else
193
- @pipeline << Messages::Outgoing::Raw.new(info[:message])
220
+ Logger.warn("Got part: #{part}. Not init yet")
194
221
  end
222
+ else
223
+ filter(string, socket)
195
224
  end
196
225
  rescue Object => boom
197
- Logger.warn("BOUNCER ERROR: #{boom}")
198
- unless(@clients.empty?)
199
- @clients.each do |socket|
200
- socket[:connection].close
201
- @clients.delete(socket)
202
- end
203
- end
226
+ Logger.error("Bouncer received message but failed to pass it on. Error: #{boom} Message: #{string}")
204
227
  end
228
+ end
229
+
230
+ # Stops the listener
231
+ def stop_listener
232
+ @socket.close
233
+ @listener.kill unless @listener.nil?
234
+ @listener = nil
235
+ @socket = nil
236
+ end
237
+
238
+ # socket:: SSLSocket
239
+ # Disconnect given socket or all sockets
240
+ def disconnect(socket=nil)
241
+ sockets = socket.nil? ? @clients.keys : [socket]
242
+ sockets.each do |sock|
243
+ begin
244
+ @spockets.remove(sock)
245
+ rescue Spockets::UnknownSocket => boom
246
+ Logger.warn('Socket was already removed automatically from spockets')
247
+ end
248
+ sock.close
249
+ @clients.delete(sock)
205
250
  end
206
251
  end
207
-
208
- def listening?
252
+
253
+ # Returns if the listener is running
254
+ def running?
209
255
  return !@listener.nil?
210
256
  end
211
257
 
258
+ # connection:: SSLSocket
259
+ # Sends initialization information to client. Basically a
260
+ # 001 welcome message and JOINs for any channels the bot is in
212
261
  def initialize_connection(connection)
213
262
  # send channel info and such to client
214
263
  connection.puts(":localhost 001 #{me.nick} :Welcome to the network #{me.source}\n")
215
- Models::Channel.filter(:parked => true).each do |channel|
216
- connection.puts(":#{me.source} JOIN :#{channel.name}\n")
264
+ me.channels.each do |channel|
265
+ connection.puts(":#{me.source} JOIN :#{channel.name}")
266
+ end
267
+ end
268
+
269
+ # num:: integer
270
+ # Set/show port number for listener
271
+ def port(num=nil)
272
+ if(num.nil?)
273
+ conf = Models::Config.filter(:name => 'bouncer_port').first
274
+ return conf ? conf.value : nil
275
+ else
276
+ if(num == 0)
277
+ Models::Config.filter(:name => 'bouncer_port').destroy
278
+ else
279
+ raise 'Invalid port number' if num < 1 || num > 65535
280
+ conf = Models::Config.find_or_create(:name => 'bouncer_port')
281
+ conf.value = num
282
+ conf.save
283
+ end
284
+ end
285
+ end
286
+
287
+ # string:: Outgoing string
288
+ # socket:: SSLSocket
289
+ # f:: filter to call. Defaults to running all filters
290
+ # Filter outgoing string to be delivered to IRC server. This
291
+ # is used to stop excessive WHO/PING type messages from flooding
292
+ # the server and getting the bot kicked.
293
+ def filter(string, socket, f=:all)
294
+ if(f == :all)
295
+ @filters.each{|f| send(@filters[f], string, socket)}
296
+ else
297
+ raise NoMethodError.new unless @filters[f]
298
+ send(@filters[f], string, socket)
299
+ end
300
+ unless(string.empty?)
301
+ @pipeline << Messages::Outgoing::Raw.new(string)
302
+ Logger.warn("Modifying message-> :#{me.source} #{string}")
303
+ @pipeline << Messages::Internal::Incoming.new(":#{me.source} #{string}") if string.slice(0, 7).downcase == 'privmsg'
304
+ end
305
+ end
306
+
307
+ ## Filters are here ##
308
+
309
+ def filter_who(string, socket)
310
+ if(string.slice(0..2).downcase == 'who')
311
+ nick = string.slice(4, string.size-3)
312
+ if(nick.downcase == m.nick.downcase)
313
+ #send who info
314
+ string.delete!(string)
315
+ end
316
+ end
317
+ end
318
+
319
+ def filter_quit(string, socket)
320
+ if(string.slice(0..3).downcase == 'quit')
321
+ string.delete!(string)
322
+ end
323
+ end
324
+
325
+ def filter_ping(string, socket)
326
+ if(string.slice(0..3).downcase == 'ping')
327
+ socket.puts(":mod_spox.bouncer PONG mod_spox.bouncer :#{string.slice(5, string.size - 4)}")
328
+ string.delete!(string)
217
329
  end
218
330
  end
219
331