spox-mod_spox 0.3.1 → 0.3.2
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.
- data/CHANGELOG +5 -0
- data/README.rdoc +61 -1
- data/bin/mod_spox +1 -7
- data/data/mod_spox/extras/AutoKick.rb +3 -2
- data/data/mod_spox/extras/AutoMode.rb +2 -1
- data/data/mod_spox/extras/AutoRejoin.rb +5 -4
- data/data/mod_spox/extras/Bouncer.rb +243 -131
- data/data/mod_spox/extras/FloodKicker.rb +2 -1
- data/data/mod_spox/extras/Fortune.rb +5 -1
- data/data/mod_spox/extras/Karma.rb +2 -1
- data/data/mod_spox/extras/Logger.rb +11 -9
- data/data/mod_spox/extras/NickServ.rb +2 -1
- data/data/mod_spox/extras/PhpCli.rb +1 -1
- data/data/mod_spox/extras/PhpFuncLookup.rb +2 -1
- data/data/mod_spox/extras/RegexTracker.rb +2 -1
- data/data/mod_spox/extras/Roulette.rb +2 -1
- data/data/mod_spox/extras/Seen.rb +10 -8
- data/data/mod_spox/extras/Topten.rb +2 -1
- data/data/mod_spox/extras/Translate.rb +2 -1
- data/data/mod_spox/extras/Twitter.rb +1 -1
- data/data/mod_spox/plugins/Authenticator.rb +7 -7
- data/data/mod_spox/plugins/Banner.rb +6 -6
- data/data/mod_spox/plugins/BotNick.rb +2 -1
- data/data/mod_spox/plugins/Initializer.rb +4 -4
- data/data/mod_spox/plugins/Joiner.rb +1 -1
- data/data/mod_spox/plugins/PluginLoader.rb +1 -1
- data/data/mod_spox/plugins/Ponger.rb +8 -8
- data/data/mod_spox/plugins/Status.rb +1 -1
- data/lib/mod_spox/Bot.rb +27 -27
- data/lib/mod_spox/BotConfig.rb +17 -21
- data/lib/mod_spox/Filter.rb +29 -0
- data/lib/mod_spox/FilterManager.rb +63 -0
- data/lib/mod_spox/Helpers.rb +120 -14
- data/lib/mod_spox/Loader.rb +1 -1
- data/lib/mod_spox/MessageFactory.rb +10 -2
- data/lib/mod_spox/Pipeline.rb +66 -61
- data/lib/mod_spox/PluginManager.rb +5 -5
- data/lib/mod_spox/PriorityQueue.rb +21 -8
- data/lib/mod_spox/Sockets.rb +6 -6
- data/lib/mod_spox/Timer.rb +3 -3
- data/lib/mod_spox/Version.rb +2 -2
- data/lib/mod_spox/handlers/UserHost.rb +32 -0
- data/lib/mod_spox/handlers/Whois.rb +7 -7
- data/lib/mod_spox/messages/incoming/Pong.rb +11 -2
- data/lib/mod_spox/messages/incoming/UserHost.rb +24 -0
- data/lib/mod_spox/messages/internal/FilterAdd.rb +20 -0
- data/lib/mod_spox/messages/internal/FilterList.rb +18 -0
- data/lib/mod_spox/messages/internal/FilterListing.rb +22 -0
- data/lib/mod_spox/messages/internal/FilterRemove.rb +20 -0
- data/lib/mod_spox/messages/internal/Incoming.rb +15 -0
- data/lib/mod_spox/migrations/006_ignore.rb +21 -0
- data/lib/mod_spox/models/Nick.rb +11 -0
- data/lib/mod_spox/rfc2812.rb +2 -1
- data/lib/mod_spox/rfc2812_full.rb +185 -0
- data/tests/BotHolder.rb +7 -1
- data/tests/handlers/tc_Created.rb +28 -8
- data/tests/handlers/tc_Invite.rb +11 -9
- data/tests/handlers/tc_Join.rb +36 -16
- data/tests/handlers/tc_Kick.rb +27 -6
- data/tests/handlers/tc_Mode.rb +23 -13
- data/tests/handlers/tc_Names.rb +29 -9
- data/tests/handlers/tc_Nick.rb +30 -8
- data/tests/handlers/tc_Part.rb +30 -20
- data/tests/handlers/tc_Ping.rb +32 -19
- data/tests/handlers/tc_Pong.rb +28 -8
- data/tests/handlers/tc_Privmsg.rb +33 -13
- data/tests/handlers/tc_Quit.rb +30 -17
- data/tests/handlers/tc_Who.rb +8 -3
- data/tests/handlers/tc_Whois.rb +7 -3
- data/tests/lib/tc_BotConfig.rb +35 -0
- data/tests/lib/tc_Helpers.rb +139 -0
- data/tests/lib/tc_PriorityQueue.rb +31 -0
- metadata +17 -2
data/CHANGELOG
CHANGED
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
|
-
|
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: #{
|
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,
|
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,
|
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
|
-
|
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
|
-
|
8
|
-
@pipeline.hook(self, :
|
9
|
-
@pipeline.hook(self, :
|
10
|
-
@pipeline.hook(self, :
|
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 => :
|
9
|
-
|
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 => :
|
13
|
-
add_sig(:sig => 'bouncer clients'
|
14
|
-
|
15
|
-
@
|
16
|
-
@
|
17
|
-
@
|
18
|
-
@
|
19
|
-
@
|
20
|
-
|
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
|
69
|
+
client.puts(output)
|
30
70
|
rescue Object => boom
|
31
71
|
Logger.warn("Bouncer encountered unexpected error. Disconnecting client. #{boom}")
|
32
|
-
client
|
33
|
-
@clients.delete(client)
|
72
|
+
disconnect(client)
|
34
73
|
end
|
35
74
|
end
|
36
75
|
end
|
37
76
|
end
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
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(
|
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(
|
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
|
-
|
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
|
-
|
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
|
-
|
125
|
+
information m.replyto, "Status: #{running? ? 'listening' : 'stopped'}"
|
96
126
|
end
|
97
|
-
|
98
|
-
private
|
99
127
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
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
|
-
|
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
|
-
|
124
|
-
|
125
|
-
|
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
|
-
|
129
|
-
|
171
|
+
|
172
|
+
# Starts the listener for new connections
|
130
173
|
def start_listener
|
131
|
-
|
132
|
-
|
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.
|
138
|
-
|
139
|
-
|
140
|
-
|
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
|
-
|
169
|
-
|
170
|
-
|
189
|
+
rescue Object => boom
|
190
|
+
Logger.error "Bouncer listener encountered an error: #{boom}"
|
191
|
+
raise boom
|
171
192
|
end
|
172
193
|
end
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
@
|
178
|
-
@
|
179
|
-
|
180
|
-
|
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
|
-
|
184
|
-
|
185
|
-
|
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
|
-
|
188
|
-
|
189
|
-
Logger.info
|
190
|
-
|
191
|
-
|
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
|
-
|
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.
|
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
|
-
|
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
|
-
|
216
|
-
connection.puts(":#{me.source} JOIN :#{channel.name}
|
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
|
|