commands.rb 8.76 KB
Newer Older
1
#!/usr/bin/env ruby
2 3 4

# Class to handle user commands
class Commands
5
	alias_method :loadfile, :load
6 7 8 9 10 11 12
	def initialize( status, config, output, irc, timer, console = 0 )
		@status		= status
		@config		= config
		@output		= output
		@irc		= irc
		@timer		= timer
		@console	= console
13

Cool Fire's avatar
Cool Fire committed
14
		# List of protected commands for plugins
15
		@protected	= [ "kicked", "noticed", "messaged", "joined", "parted", "quited", "unload", "alias" ]
Cool Fire's avatar
Cool Fire committed
16 17

		# Hashtables to keep flood statistics
Cool Fire's avatar
Cool Fire committed
18
		@timeout	= {}
Cool Fire's avatar
Cool Fire committed
19
		@lastcmd	= {}
20 21 22 23 24 25
	end

	# Shorthands
	def con
		return( @console == 1 )
	end
Cool Fire's avatar
Cool Fire committed
26

27 28 29 30
	def cmc
		return @config.command
	end

Cool Fire's avatar
Cool Fire committed
31
	def sanitize( input, downcase = 0, spaces = 0 )
32 33 34 35
		input.gsub!( /[^a-zA-Z0-9 -]/, "" )
		if( downcase == 1 )
			input.downcase!
		end
Cool Fire's avatar
Cool Fire committed
36 37 38
		if( spaces == 1 )
		input.gsub!( /[\s]/, "" )
		end
39 40
	end

41
	# Inital command parsing & function calling
Cool Fire's avatar
Cool Fire committed
42 43 44 45
	def process( nick, user, host, from, msg, skipflood = false )

		# Do command throttling if desireable and threading allows it
		if( @status.threads && @config.threads && @config.antiflood && !con && !skipflood )
Cool Fire's avatar
Cool Fire committed
46 47 48
			
			# Check if this user has ever sent a command before
			if( !@lastcmd[ user+host ].nil? )
Cool Fire's avatar
Cool Fire committed
49

Cool Fire's avatar
Cool Fire committed
50 51 52 53
				# Up the timeout if needed
				if( @lastcmd[ user+host ] + @config.floodtime > Time.now.to_i )
					@timeout[ user+host ] += @config.floodtime
				end
Cool Fire's avatar
Cool Fire committed
54

Cool Fire's avatar
Cool Fire committed
55 56 57 58
				# Update values so they go back down over time
				@timeout[ user+host ] -= ( Time.now.to_i - @lastcmd[ user+host ] )
				if( @timeout[ user+host ] < 1 )
					@timeout[ user+host ] = 0
Cool Fire's avatar
Cool Fire committed
59
				end
Cool Fire's avatar
Cool Fire committed
60
				@lastcmd[ user+host ] = Time.now.to_i
Cool Fire's avatar
Cool Fire committed
61
			else
Cool Fire's avatar
Cool Fire committed
62 63 64
				@lastcmd[ user+host ] = Time.now.to_i
				@timeout[ user+host ] = 0
			end
Cool Fire's avatar
Cool Fire committed
65

Cool Fire's avatar
Cool Fire committed
66 67
			# Delay or drop command
			if( @timeout[ user+host ] > 0 && @timeout[ user+host ] < @config.floodcut )
Cool Fire's avatar
Cool Fire committed
68
				@irc.notice( nick, "Delaying by #{@timeout[ user+host ]} seconds. If delay goes over #{@config.floodcut} seconds the command will be dropped.", true )
Cool Fire's avatar
Cool Fire committed
69 70 71
				sleep( @timeout[ user+host ] )
			elsif ( @timeout[ user+host ] > @config.floodcut )
					Thread.exit
Cool Fire's avatar
Cool Fire committed
72 73
			end
		end
Cool Fire's avatar
Cool Fire committed
74

75 76 77 78 79 80
		# Check for aliases
		if( @status.checkplugin( "aliases" ) )
			plugin = @status.getplugin( "aliases" )
			msg = plugin.alias( msg )
		end

Cool Fire's avatar
Cool Fire committed
81 82
		cmd, rest = msg.split(' ', 2)

83
		if( !cmd.nil? && !cmd.empty? )
84
			sanitize( cmd, 1 )
85

86
			begin
87
				# Calls to local methods
88 89
				eval( "self.#{cmd}( nick, user, host, from, msg )" )
			rescue NoMethodError
90 91 92 93 94 95
				# See if we have a plugin loaded by this name.
				if( @status.checkplugin( cmd ) )
					# Get plugin
					plugin = @status.getplugin( cmd )

					# Parse function call
96
					if( !rest.nil? && !rest.empty? )
Cool Fire's avatar
Cool Fire committed
97
							function, _arguments = rest.split(' ', 2)
98 99
							sanitize( function )

100 101
						# See if such a method exists in this plugin and isn't protected, if so, call it
						if( plugin.respond_to?( function ) && !@protected.include?( function ) )
Cool Fire's avatar
Cool Fire committed
102
							eval( "plugin.#{function}( nick, user, host, from, msg, _arguments, con )" )
103 104 105 106 107 108
						else
							# Call default method with the function as argument
							if( plugin.respond_to?( "main" ) )
								plugin.main( nick, user, host, from, msg, rest, con )
							end
						end
109
					else
110 111 112 113
						# Call default method
						if( plugin.respond_to?( "main" ) )
							plugin.main( nick, user, host, from, msg, rest, con )
						end
114 115
					end
				else
Cool Fire's avatar
Cool Fire committed
116 117
					if( cmd != "core" )
						# Try to call as command from core plugin
Cool Fire's avatar
Cool Fire committed
118
						process(nick, user, host, from, "core " + msg , true )
Cool Fire's avatar
Cool Fire committed
119
					end
120 121 122 123
				end
			end
		end
	end
124 125

	# Quit command
Cool Fire's avatar
Cool Fire committed
126
	def quit( _nick, _user, host, _from, msg )
127
		if( @config.auth( host, con ) )
128 129 130 131
			if( con )		
				@output.cbad( "This will also stop the bot, are you sure? [y/N]: " )
				STDOUT.flush
				ans = STDIN.gets.chomp
132
			end
133
			if( ans =~ /^y$/i || !con )
Cool Fire's avatar
Cool Fire committed
134
				_cmd, message = msg.split( ' ', 2 )
135

136
				if( message == nil )
Cool Fire's avatar
Cool Fire committed
137
					@irc.quit( @config.nick + " was instructed to quit.", true )
138
				else
Cool Fire's avatar
Cool Fire committed
139
					@irc.quit( message, true )
140 141
				end

142 143 144
				@output.std( "Received quit command.\n" )

				@status.reconnect( 0 )
145 146 147 148 149 150
				@irc.disconnect
				Process.exit
			else
				if( con )
					@output.cinfo( "Continuing" )
				end
Cool Fire's avatar
Cool Fire committed
151
			end
152 153 154
		end
	end

155
	# Load modules
Cool Fire's avatar
Cool Fire committed
156
	def load( nick, _user, host, _from, msg, auto = false )
157
		if( @config.auth( host, con ) )
Cool Fire's avatar
Cool Fire committed
158
			_cmd, plugin = msg.split( ' ', 2 )
159 160
			if( plugin != nil )
				# Clean variable
Cool Fire's avatar
Cool Fire committed
161
				sanitize( plugin, 1, 1 )
162 163 164 165 166 167

				# Check if plugin isn't loaded already
				if( !@status.checkplugin( plugin ) )
					# Check file exists
					if( FileTest.file?( @config.plugindir + "/" + plugin + ".rb" ) )

Cool Fire's avatar
Cool Fire committed
168
						object = nil
169 170
						# Check syntax & load
						begin
171
							# Try to load the plugin
172
							eval( "loadfile './#{@config.plugindir}/#{plugin}.rb'" )
173
							@output.debug( "Load was successful.\n" )
174 175

							# Try to create an object
176
							eval( "object = #{plugin.capitalize}.new( @status, @config, @output, @irc, @timer )" )
177 178 179 180 181 182 183
							@output.debug( "Object was created.\n" )

							# Push to @plugins
							eval( "@status.addplugin( plugin, object )" )
							@output.debug( "Object was pushed to plugin hash.\n" )

							if( con )
Cool Fire's avatar
Cool Fire committed
184
								if( !auto )
Cool Fire's avatar
Cool Fire committed
185 186
									@output.cgood( "Plugin " + plugin + " loaded.\n" )
								end
187 188 189 190 191 192 193
							else
								@irc.notice( nick, "Plugin " + plugin + " loaded." )
							end
						rescue Exception => e
							if( con )
								@output.cbad( "Failed to load plugin:\n" )
								@output.cinfo( e.to_s + "\n" )
Cool Fire's avatar
Cool Fire committed
194
								@output.debug( "Working on object: #{object.inspect}" )
195 196 197 198 199 200 201
							else
								@irc.notice( nick, "Failed to load plugin: " + e.to_s )
							end
						end
					else
						# Not found
						if( con )
202
							@output.cbad( "Plugin " + plugin + " not found.\n" )
203
						else
204
							@irc.notice( nick, "Plugin " + plugin + " not found." )
205 206 207 208 209 210 211 212 213 214 215
						end
					end
				else
					if( con )
						@output.cbad( "Plugin " + plugin + " is already loaded.\n" )
					else
						@irc.notice( nick, "Plugin " + plugin + " is already loaded." )
					end
				end
			else
				if( con )
216
					@output.info( "Usage: " + cmc + "load plugin" )
217
				else
218
					@irc.notice( nick, "Usage: " + cmc + "load plugin" )
219 220 221 222 223
				end				
			end
		end
	end

224
	# Unload module
Cool Fire's avatar
Cool Fire committed
225
	def unload( nick, _user, host, _from, msg )
226
		if( @config.auth( host, con ) )
Cool Fire's avatar
Cool Fire committed
227
			_cmd, plugin = msg.split( ' ', 2 )
228 229
			if( plugin != nil )
				# Clean variable
Cool Fire's avatar
Cool Fire committed
230
				sanitize( plugin, 1, 1 )
231 232 233 234

				# Check if plugin is loaded
				if( @status.checkplugin( plugin ) )
					begin
235 236 237 238 239 240 241 242 243
						# See if plugin wants to receive notification of unloading
						if( @status.getplugin( plugin ).respond_to?( "unload" ) )
							if( @status.getplugin( plugin ).unload )
								@output.debug( "Plugin #{plugin} finised 'unload' routine.\n" )
							else
								@output.debug( "Plugin #{plugin} could not successfully complete 'unload' routine.\n" )
							end
						end

244 245 246 247 248
						# Remove @plugins
						eval( "@status.delplugin( plugin )" )
						@output.debug( "Object was removed from plugin hash.\n" )

						if( con )
249
							@output.cgood( "Plugin #{plugin} unloaded.\n" )
250
						else
251
							@irc.notice( nick, "Plugin #{plugin} unloaded." )
252 253 254 255 256 257 258 259 260 261 262
						end
					rescue Exception => e
						if( con )
							@output.cbad( "Failed to unload plugin:\n" )
							@output.cinfo( e.to_s + "\n" )
						else
							@irc.notice( nick, "Failed to unload plugin: " + e.to_s )
						end
					end
				else
					if( con )
263
						@output.cbad( "Plugin #{plugin} is not loaded.\n" )
264
					else
265
						@irc.notice( nick, "Plugin #{plugin} is not loaded." )
266 267 268 269
					end
				end
			else
				if( con )
270
					@output.info( "Usage: #{cmc} unload plugin" )
271
				else
272 273
					@irc.notice( nick, "Usage: #{cmc} unload plugin" )
				end
274 275 276 277
			end
		end
	end

278
	# Meta function to reload modules
279 280
	def reload( nick, user, host, from, msg )
		unload( nick, user, host, from, msg )
Cool Fire's avatar
Cool Fire committed
281
		load( nick, user, host, from, msg )
282 283
	end

284
	# Meta funcion to load autoload modules
Cool Fire's avatar
Cool Fire committed
285
	def autoload( _nick = nil, _user = nil, _host = nil, _from = nil, _msg = nil )
286
		@config.autoload.each do |mod|
Cool Fire's avatar
Cool Fire committed
287
			load( nil, nil, nil, nil, "dummy " + mod, true )
288 289 290 291
		end
	end

	# Function to get list of loaded modules
Cool Fire's avatar
Cool Fire committed
292
	def loaded( nick, _user, _host, _from, _msg )
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
		if( con )
			@output.c( "Loaded plugins: " )
			@status.plugins.each_key do |plugin_name|
				@output.c( plugin_name + " " )
			end
			@output.c( "\n" )
		else
			tmp_list = ""
			@status.plugins.each_key do |plugin_name|
				tmp_list = tmp_list +  plugin_name + " "
			end

			@irc.notice( nick, "Loaded plugins: " + tmp_list )
			tmp_list = nil
		end
	end
309 310

	# Function to list available modules
Cool Fire's avatar
Cool Fire committed
311
	def available( nick, _user, _host, _from, _msg )
312
		contents = Dir.entries("./" + @config.plugindir + "/" )
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
		plugs = Array.new
		contents.entries.each do |file|
			if( file =~ /\.rb$/i )
				file.gsub!( /\.rb$/i, "" )
				plugs.push( file )
			end
		end

		if( con )
			@output.c( "Available plugins: " )
			plugs.each do |p|
				@output.c( p + " " )
			end
			@output.c( "\n" )
		else
			output = "Available plugins: "
			plugs.each do |p|
				output = output + p + " "
			end
			@irc.notice( nick, output )
			output = nil
		end
	end
336
end