PerfectlyNormal-Flexo 0.3.9
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +348 -0
- data/README +5 -0
- data/Rakefile +21 -0
- data/bin/flexo +3 -0
- data/bin/mkflexorc +99 -0
- data/flexo.gemspec +53 -0
- data/lib/caselesshash.rb +113 -0
- data/lib/daemonize.rb +59 -0
- data/lib/flexo/client.rb +30 -0
- data/lib/flexo/config.rb +79 -0
- data/lib/flexo/constants.rb +15 -0
- data/lib/flexo/data.rb +104 -0
- data/lib/flexo/dispatcher.rb +134 -0
- data/lib/flexo/errors.rb +29 -0
- data/lib/flexo/event.rb +31 -0
- data/lib/flexo/events/join.rb +13 -0
- data/lib/flexo/events/kick.rb +29 -0
- data/lib/flexo/events/mode.rb +23 -0
- data/lib/flexo/events/nick.rb +14 -0
- data/lib/flexo/events/notice.rb +12 -0
- data/lib/flexo/events/part.rb +14 -0
- data/lib/flexo/events/ping.rb +16 -0
- data/lib/flexo/events/pong.rb +14 -0
- data/lib/flexo/events/privmsg.rb +46 -0
- data/lib/flexo/events/quit.rb +12 -0
- data/lib/flexo/events/reply.rb +45 -0
- data/lib/flexo/events/topic.rb +18 -0
- data/lib/flexo/events/unknown.rb +11 -0
- data/lib/flexo/handler.rb +30 -0
- data/lib/flexo/logger.rb +86 -0
- data/lib/flexo/manager.rb +152 -0
- data/lib/flexo/numerics.rb +207 -0
- data/lib/flexo/plugin.rb +69 -0
- data/lib/flexo/pluginmanager.rb +106 -0
- data/lib/flexo/sender.rb +601 -0
- data/lib/flexo/server.rb +130 -0
- data/lib/flexo/trigger.rb +44 -0
- data/plugins/auth.rb +256 -0
- data/plugins/control.rb +36 -0
- data/plugins/dbus.rb +39 -0
- data/plugins/pstore.rb +34 -0
- data/plugins/svnlookup.rb +63 -0
- metadata +93 -0
data/lib/daemonize.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Shamelessly downloaded and included in Flexo
|
2
|
+
# from http://grub.ath.cx/daemonize/
|
3
|
+
# Not my own work at all.
|
4
|
+
module Daemonize
|
5
|
+
VERSION = "0.1.2"
|
6
|
+
|
7
|
+
# Try to fork if at all possible retrying every 5 sec if the
|
8
|
+
# maximum process limit for the system has been reached
|
9
|
+
def safefork
|
10
|
+
tryagain = true
|
11
|
+
|
12
|
+
while tryagain
|
13
|
+
tryagain = false
|
14
|
+
begin
|
15
|
+
if pid = fork
|
16
|
+
return pid
|
17
|
+
end
|
18
|
+
rescue Errno::EWOULDBLOCK
|
19
|
+
sleep 5
|
20
|
+
tryagain = true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# This method causes the current running process to become a daemon
|
26
|
+
# If closefd is true, all existing file descriptors are closed
|
27
|
+
def daemonize(oldmode=0, closefd=false)
|
28
|
+
srand # Split rand streams between spawning and daemonized process
|
29
|
+
safefork and exit # Fork and exit from the parent
|
30
|
+
|
31
|
+
# Detach from the controlling terminal
|
32
|
+
unless sess_id = Process.setsid
|
33
|
+
raise 'Cannot detach from controlled terminal'
|
34
|
+
end
|
35
|
+
|
36
|
+
# Prevent the possibility of acquiring a controlling terminal
|
37
|
+
if oldmode.zero?
|
38
|
+
trap 'SIGHUP', 'IGNORE'
|
39
|
+
exit if pid = safefork
|
40
|
+
end
|
41
|
+
|
42
|
+
Dir.chdir "/" # Release old working directory
|
43
|
+
File.umask 0000 # Insure sensible umask
|
44
|
+
|
45
|
+
if closefd
|
46
|
+
# Make sure all file descriptors are closed
|
47
|
+
ObjectSpace.each_object(IO) do |io|
|
48
|
+
unless [STDIN, STDOUT, STDERR].include?(io)
|
49
|
+
io.close rescue nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
STDIN.reopen "/dev/null" # Free file descriptors and
|
55
|
+
STDOUT.reopen File.expand_path("~/flexo-err.log"), "a+" # point them somewhere sensible
|
56
|
+
STDERR.reopen STDOUT # STDOUT/STDERR should go to a logfile
|
57
|
+
return oldmode ? sess_id : 0 # Return value is mostly irrelevant
|
58
|
+
end
|
59
|
+
end
|
data/lib/flexo/client.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# dirty haxx
|
2
|
+
$:.push 'lib'
|
3
|
+
|
4
|
+
require 'daemonize'
|
5
|
+
require 'flexo/manager'
|
6
|
+
|
7
|
+
module Flexo
|
8
|
+
# This is the 'main' class of Flexo.
|
9
|
+
# The main file just creates an instance of this class, and then
|
10
|
+
# everything goes automatically. Additionally, it contains tons of
|
11
|
+
# convenience functions, so plugins don't neccessarily have to do everything
|
12
|
+
# so complicated.
|
13
|
+
class Client
|
14
|
+
include Daemonize
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@nickname = nil
|
18
|
+
|
19
|
+
begin
|
20
|
+
@manager = Flexo::Manager.instance
|
21
|
+
@manager.setup
|
22
|
+
rescue InvalidConfigurationException => e
|
23
|
+
$stderr.puts "Invalid configuration from Flexo::Client"
|
24
|
+
exit 1
|
25
|
+
end
|
26
|
+
|
27
|
+
daemonize
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/flexo/config.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Flexo
|
4
|
+
# Configuration-related functions.
|
5
|
+
# Able to read settings from a file, look them up, change them, and save
|
6
|
+
# them back to the file while the bot is running.
|
7
|
+
class Config
|
8
|
+
attr_reader :configpath
|
9
|
+
attr_reader :configfile
|
10
|
+
|
11
|
+
alias path configpath
|
12
|
+
alias file configfile
|
13
|
+
|
14
|
+
# Reads the configuration file into an array
|
15
|
+
def initialize
|
16
|
+
@configpath = File.expand_path("~/.flexo")
|
17
|
+
@configfile = "#{@configpath}/config.yaml"
|
18
|
+
@config = {}
|
19
|
+
reload(:first)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Very basic validity-test. Just checks if we have *anything*
|
23
|
+
# stored in the configuration.
|
24
|
+
def valid?
|
25
|
+
return !@config.empty?
|
26
|
+
end
|
27
|
+
|
28
|
+
# Lookup-function. Lets us use Config as an array,
|
29
|
+
# for convenience. so instead of Config.lookup('key'),
|
30
|
+
# we can use Config['key']
|
31
|
+
def [](key)
|
32
|
+
if @config[key]
|
33
|
+
return @config[key]
|
34
|
+
end
|
35
|
+
|
36
|
+
return nil
|
37
|
+
end
|
38
|
+
|
39
|
+
# Same as [], lets you use this class as an array for saving
|
40
|
+
# values as well. Plugins also have access to this class, and
|
41
|
+
# can store their own permanent values here.
|
42
|
+
#
|
43
|
+
# Flexo would preferably use a "namespace", and plugins use their own
|
44
|
+
# namespace, using keys like core.nicks instead of just making a mess.
|
45
|
+
# This would also prevent collisions
|
46
|
+
def []=(key, value)
|
47
|
+
@config[key] = value
|
48
|
+
return @config[key]
|
49
|
+
end
|
50
|
+
|
51
|
+
# Reloads configuration from file, resetting changes made since the
|
52
|
+
# last write.
|
53
|
+
def reload(first = false)
|
54
|
+
begin
|
55
|
+
Dir["#{@configpath}/*.yaml"].each do |file|
|
56
|
+
tmp = YAML.load_file(file)
|
57
|
+
next if tmp.class != Hash && tmp.class != Array
|
58
|
+
@config.merge!(tmp)
|
59
|
+
end
|
60
|
+
rescue
|
61
|
+
if first == :first
|
62
|
+
Flexo::Logger.error "No configuration found. "
|
63
|
+
Flexo::Logger.error "Try mkflexorc for an automated tool to help "
|
64
|
+
Flexo::Logger.error "you create a configuration for Flexo.\n"
|
65
|
+
exit
|
66
|
+
else
|
67
|
+
Flexo::Logger.warn "Configuration has disappeared! Aborting reload."
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Saves the current configuration to a file.
|
73
|
+
def write
|
74
|
+
File.open(@configfile, 'w') do |f|
|
75
|
+
f.puts @config.to_yaml
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Flexo
|
2
|
+
# A collection of various constants
|
3
|
+
module Constants
|
4
|
+
LOG_DEBUG2 = 4
|
5
|
+
LOG_DEBUG = 3
|
6
|
+
LOG_INFO = 2
|
7
|
+
LOG_WARN = 1
|
8
|
+
LOG_ERROR = 0
|
9
|
+
|
10
|
+
CHANPREFIX = ['#', '&', '+', '!']
|
11
|
+
|
12
|
+
FLEXO_VERSION = '0.3.9'
|
13
|
+
FLEXO_RELEASE = 'Bendless Love'
|
14
|
+
end
|
15
|
+
end
|
data/lib/flexo/data.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
module Flexo
|
2
|
+
# This is a handy objecty representation
|
3
|
+
# of an IRC message. Each line is broken into
|
4
|
+
# tiny little pieces, made available to the rest
|
5
|
+
# of Flexo through accessors (only readers).
|
6
|
+
class Data
|
7
|
+
attr_reader :raw
|
8
|
+
attr_reader :message
|
9
|
+
attr_reader :origin
|
10
|
+
attr_reader :params
|
11
|
+
attr_reader :string
|
12
|
+
attr_reader :hostmask
|
13
|
+
attr_reader :numeric
|
14
|
+
|
15
|
+
# Calls parse() and fills up all the variables
|
16
|
+
# with sensible data
|
17
|
+
def initialize(raw)
|
18
|
+
@manager = Flexo::Manager.instance
|
19
|
+
data = parse(raw)
|
20
|
+
|
21
|
+
if data == nil
|
22
|
+
@manager.logger.error("Unable to parse\n#{raw}")
|
23
|
+
return
|
24
|
+
end
|
25
|
+
|
26
|
+
@raw = raw
|
27
|
+
@message = data[:message]
|
28
|
+
@origin = data[:origin]
|
29
|
+
@params = data[:params]
|
30
|
+
@string = data[:string]
|
31
|
+
@hostmask = parse_hostmask(@origin) if @origin
|
32
|
+
@numeric = @message =~ /^\d+$/ ? true : false
|
33
|
+
end
|
34
|
+
|
35
|
+
def numeric?
|
36
|
+
@numeric
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
@raw
|
41
|
+
end
|
42
|
+
|
43
|
+
def inspect # :nodoc:
|
44
|
+
"#<#{self.class}:#{(@raw.size > 40 ? @raw[0..40] + '...' : @raw).inspect}>"
|
45
|
+
end
|
46
|
+
|
47
|
+
# Does the actual parsing and splitting and trickery
|
48
|
+
# with the received line
|
49
|
+
def parse(line)
|
50
|
+
m = line.lstrip.chomp.match(/^(?::(\S+) )?(\S+)(?: ([^:].*?[^:]))?(?: :(.*?))?$/)
|
51
|
+
{:origin => m[1], :message => m[2], :params => m[3] ? m[3].split(' ') : [], :string => m[4] || ''} if m
|
52
|
+
end
|
53
|
+
|
54
|
+
# This parses the hostmask, creating an instance of
|
55
|
+
# Flexo::Data::Hostmask.
|
56
|
+
def parse_hostmask(mask)
|
57
|
+
m = mask.match(/^([^!\s]+)(?:(?:!([^@\s]+))?@(\S+))?$/)
|
58
|
+
|
59
|
+
if m
|
60
|
+
host = Hostmask.new(m[1], m[2], m[3])
|
61
|
+
return host
|
62
|
+
end
|
63
|
+
|
64
|
+
return nil
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# For added fun, we have a small, separate class
|
69
|
+
# for hostmasks. And, being very creative, we call it...
|
70
|
+
# Data::Hostmask!
|
71
|
+
class Data::Hostmask
|
72
|
+
attr_reader :nickname
|
73
|
+
attr_reader :hostname
|
74
|
+
attr_reader :username
|
75
|
+
attr_reader :hostmask
|
76
|
+
|
77
|
+
alias :nick :nickname
|
78
|
+
alias :from :nickname
|
79
|
+
alias :by :nickname
|
80
|
+
|
81
|
+
alias :host :hostname
|
82
|
+
alias :ip :hostname
|
83
|
+
|
84
|
+
alias :user :username
|
85
|
+
alias :ident :username
|
86
|
+
|
87
|
+
alias :mask :hostmask
|
88
|
+
|
89
|
+
# The only function in this class.
|
90
|
+
# When instanciated, this populates some instance variables
|
91
|
+
# with sensible values, and then allow us to access those using
|
92
|
+
# attr_readers
|
93
|
+
def initialize(nickname, username, hostname)
|
94
|
+
@nickname = nickname
|
95
|
+
@username = username
|
96
|
+
@hostname = hostname
|
97
|
+
@hostmask = "#{@nickname}!#{@username}@#{@hostname}".sub(/!?@?$/, '')
|
98
|
+
end
|
99
|
+
|
100
|
+
def inspect
|
101
|
+
"#<#{self.class}:#{@hostmask}>"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'flexo/event'
|
2
|
+
require 'flexo/events/privmsg'
|
3
|
+
require 'flexo/events/notice'
|
4
|
+
require 'flexo/events/reply'
|
5
|
+
require 'flexo/events/unknown'
|
6
|
+
require 'flexo/events/mode'
|
7
|
+
require 'flexo/events/ping'
|
8
|
+
require 'flexo/events/pong'
|
9
|
+
require 'flexo/events/join'
|
10
|
+
require 'flexo/events/part'
|
11
|
+
require 'flexo/events/quit'
|
12
|
+
require 'flexo/events/kick'
|
13
|
+
require 'flexo/events/nick'
|
14
|
+
require 'flexo/events/topic'
|
15
|
+
|
16
|
+
module Flexo
|
17
|
+
# Dispatcher receives data from Flexo::Server, analyzes it, and creates
|
18
|
+
# relevant events (see Flexo::Event), which is then broadcast
|
19
|
+
# to all classes and plugins wanting to use it for something.
|
20
|
+
#
|
21
|
+
# This class also manages subscriptions for events, so all plugins
|
22
|
+
# that want to listen for an event, can use subscribe and unsubscribe
|
23
|
+
# to manage things.
|
24
|
+
class Dispatcher
|
25
|
+
# Save a reference to Flexo::Manager, set up the dispatching-queue,
|
26
|
+
# and get ready for working.
|
27
|
+
def initialize
|
28
|
+
@manager = Flexo::Manager.instance
|
29
|
+
@queue = Queue.new
|
30
|
+
@handlers = {}
|
31
|
+
@triggers = []
|
32
|
+
|
33
|
+
@manager.thread do
|
34
|
+
while event = @queue.shift
|
35
|
+
next unless @handlers.key?(event.class)
|
36
|
+
@handlers[event.class].each { |h|
|
37
|
+
@manager.logger.debug2("Running handler #{h} for #{event.class}")
|
38
|
+
h.call(event)
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Does the handling of incoming lines.
|
45
|
+
# First, we use Flexo::Data to get
|
46
|
+
# anything meaningful out of the line received.
|
47
|
+
# Then, we create a subclass of Flexo::Event, which
|
48
|
+
# is pushed onto the dispatching-queue for further processing.
|
49
|
+
def receive(line)
|
50
|
+
@manager.logger.debug2("Received \"#{line.chomp}\"")
|
51
|
+
data = Data.new(line)
|
52
|
+
event = case data.message
|
53
|
+
when /^\d+$/ then Flexo::Events::ReplyEvent.new @manager, data
|
54
|
+
when 'JOIN' then Flexo::Events::JoinEvent.new @manager, data
|
55
|
+
when 'KICK' then Flexo::Events::KickEvent.new @manager, data
|
56
|
+
when 'MODE' then Flexo::Events::ModeEvent.new @manager, data
|
57
|
+
when 'NICK' then Flexo::Events::NickEvent.new @manager, data
|
58
|
+
when 'NOTICE' then Flexo::Events::NoticeEvent.new @manager, data
|
59
|
+
when 'PART' then Flexo::Events::PartEvent.new @manager, data
|
60
|
+
when 'PING' then Flexo::Events::PingEvent.new @manager, data
|
61
|
+
when 'PONG' then Flexo::Events::PongEvent.new @manager, data
|
62
|
+
when 'PRIVMSG' then Flexo::Events::PrivmsgEvent.new @manager, data
|
63
|
+
when 'QUIT' then Flexo::Events::QuitEvent.new @manager, data
|
64
|
+
when 'TOPIC' then Flexo::Events::TopicEvent.new @manager, data
|
65
|
+
else Flexo::Events::UnknownEvent.new @manager, data
|
66
|
+
end
|
67
|
+
|
68
|
+
@queue << event
|
69
|
+
end
|
70
|
+
|
71
|
+
# Registers a callback function for a specific event.
|
72
|
+
def subscribe(event, &block)
|
73
|
+
if event.is_a?(Symbol) || event.kind_of?(String)
|
74
|
+
if (e = event.to_s) =~ /^(?:RPL|ERR)_/i
|
75
|
+
event = Flexo::Events::ReplyEvent[e.upcase]
|
76
|
+
else
|
77
|
+
match = e.match(/^(.+?)(?:_?event)?$/i)
|
78
|
+
event = Flexo::Events.const_get("#{match[1].capitalize}Event")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
if event.is_a? Flexo::Events::ReplyEvent::Numeric
|
83
|
+
numeric = event.numeric
|
84
|
+
event = Flexo::Events::ReplyEvent
|
85
|
+
else
|
86
|
+
numeric = false
|
87
|
+
end
|
88
|
+
|
89
|
+
handler = Handler.new(numeric, &block)
|
90
|
+
@manager.mutex do
|
91
|
+
@handlers[event] ||= []
|
92
|
+
@handlers[event] << handler
|
93
|
+
end
|
94
|
+
|
95
|
+
return handler
|
96
|
+
end
|
97
|
+
|
98
|
+
# Removes an event handler.
|
99
|
+
# Known to be broken.
|
100
|
+
# DO NOT USE.
|
101
|
+
# See bug #28
|
102
|
+
def unsubscribe(handler)
|
103
|
+
@manager.logger.debug("Going to remove handler #{handler}")
|
104
|
+
events = []
|
105
|
+
@manager.mutex do
|
106
|
+
@handlers.each { |e,handlers|
|
107
|
+
if handlers.delete(handler)
|
108
|
+
events << e
|
109
|
+
@manager.logger.debug("Adding #{e} to removal queue")
|
110
|
+
end
|
111
|
+
}
|
112
|
+
events.each { |e|
|
113
|
+
@manager.logger.debug("Removing #{e}")
|
114
|
+
@handlers.delete(e)
|
115
|
+
}
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Easy way to add a trigger.
|
120
|
+
# See Flexo::Trigger for documentation
|
121
|
+
def add_trigger(pattern, &block)
|
122
|
+
trigger = Flexo::Trigger.new(pattern, &block)
|
123
|
+
@manager.mutex { @triggers << trigger }
|
124
|
+
trigger
|
125
|
+
end
|
126
|
+
|
127
|
+
# Easy way to remove a trigger.
|
128
|
+
# Must be passed a Flexo::Trigger object
|
129
|
+
def remove_trigger(trigger)
|
130
|
+
trigger.unsubscribe
|
131
|
+
@manager.mutex { @triggers.delete(trigger) }
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
data/lib/flexo/errors.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
module Flexo
|
2
|
+
# Our base-class for errors and exceptions
|
3
|
+
class Error < StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
# Raised when the configuration is invalid, and there's no way to recover
|
7
|
+
class InvalidConfigurationException < Error
|
8
|
+
end
|
9
|
+
|
10
|
+
# Raised when a plugin is missing a required dependency
|
11
|
+
class PluginDependencyError < Error
|
12
|
+
end
|
13
|
+
|
14
|
+
# Default error for plugins
|
15
|
+
class PluginError < Error
|
16
|
+
end
|
17
|
+
|
18
|
+
# Exception raised when a plugin is missing a name
|
19
|
+
class PluginNameError < PluginError
|
20
|
+
end
|
21
|
+
|
22
|
+
# Raised when there's a name conflict
|
23
|
+
class PluginConflictError < PluginError
|
24
|
+
end
|
25
|
+
|
26
|
+
# Trying to load, but could not be found (404)
|
27
|
+
class PluginNotFoundError < PluginError
|
28
|
+
end
|
29
|
+
end
|
data/lib/flexo/event.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module Flexo
|
2
|
+
# Base class for all events generated by Flexo.
|
3
|
+
# Nothing of interest happening here, actually.
|
4
|
+
# Most of the fun stuff is taken care of by the subclasses
|
5
|
+
class Event
|
6
|
+
attr_reader :data
|
7
|
+
attr_reader :nickname
|
8
|
+
|
9
|
+
alias nick nickname
|
10
|
+
alias from nickname
|
11
|
+
alias by nickname
|
12
|
+
alias who nickname
|
13
|
+
|
14
|
+
# Create the event, setting some variables
|
15
|
+
def initialize(manager, data)
|
16
|
+
@manager = manager
|
17
|
+
@data = data
|
18
|
+
@manager.logger.debug("Creating instance of #{self.class}")
|
19
|
+
end
|
20
|
+
|
21
|
+
# Checks the nickname in the hostmask
|
22
|
+
# to see if you're the one that made this event trigger,
|
23
|
+
# or if it was someone else.
|
24
|
+
def me?
|
25
|
+
end
|
26
|
+
|
27
|
+
alias is_me? me?
|
28
|
+
alias from_me? me?
|
29
|
+
alias by_me? me?
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Flexo
|
2
|
+
module Events
|
3
|
+
class KickEvent < Event
|
4
|
+
attr_reader :channel
|
5
|
+
attr_reader :kicker
|
6
|
+
attr_reader :victim
|
7
|
+
attr_reader :reason
|
8
|
+
|
9
|
+
alias kicked_by kicker
|
10
|
+
|
11
|
+
def initialize(*args)
|
12
|
+
super
|
13
|
+
@kicker = @data.hostmask.nick
|
14
|
+
@channel = @data.params[0]
|
15
|
+
@victim = @data.params[1]
|
16
|
+
@reason = @data.string
|
17
|
+
end
|
18
|
+
|
19
|
+
def kicked_me?
|
20
|
+
@victim == @manager.nickname
|
21
|
+
end
|
22
|
+
|
23
|
+
def rejoin
|
24
|
+
sleep rand*5
|
25
|
+
@manager.sender.join(@channel)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Flexo
|
2
|
+
module Events
|
3
|
+
class ModeEvent < Event
|
4
|
+
attr_reader :modes
|
5
|
+
|
6
|
+
def initialize(*args)
|
7
|
+
super
|
8
|
+
@channelmode = Flexo::Constants::CHANPREFIX.include?(@data.params[0][0])
|
9
|
+
@modes = @data.params[1..-1].join(' ')
|
10
|
+
end
|
11
|
+
|
12
|
+
def usermode?
|
13
|
+
!@channelmode
|
14
|
+
end
|
15
|
+
|
16
|
+
def channelmode?
|
17
|
+
@channelmode
|
18
|
+
end
|
19
|
+
|
20
|
+
alias chanmode? channelmode?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Flexo
|
2
|
+
module Events
|
3
|
+
# A little special event. This one is identical to Flexo::Events::PrivmsgEvent,
|
4
|
+
# so see that one for documentation.
|
5
|
+
class NoticeEvent < PrivmsgEvent
|
6
|
+
# We default to not replying to a channel for these.
|
7
|
+
def reply(target, to_channel=false)
|
8
|
+
super
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Flexo
|
2
|
+
module Events
|
3
|
+
# Private messages, which, in fact, isn't neccessarily private.
|
4
|
+
# When the target of a privmsg is a channel, it's displayed to the
|
5
|
+
# entire channel. Which isn't very private at all.
|
6
|
+
class PrivmsgEvent < Event
|
7
|
+
attr_reader :sender
|
8
|
+
attr_reader :target
|
9
|
+
attr_reader :text
|
10
|
+
alias from sender
|
11
|
+
|
12
|
+
def initialize(*args)
|
13
|
+
super
|
14
|
+
@sender = @data.hostmask.nick if @data.hostmask
|
15
|
+
@target = @data.params[0]
|
16
|
+
@ctcp = (m = @data.string.match(/^\x01(.+?)\x01$/)) ? true : false
|
17
|
+
@text = @ctcp ? m[1] : @data.string
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns true if this was a CTCP-message, and false if not.
|
21
|
+
def ctcp?
|
22
|
+
return @ctcp
|
23
|
+
end
|
24
|
+
|
25
|
+
# Were you the target of the message?
|
26
|
+
def to_me?
|
27
|
+
return @target == @manager.nickname # FIXME: Should this be stored here?
|
28
|
+
end
|
29
|
+
|
30
|
+
# Sent to a channel, or was it actually a private message?
|
31
|
+
def to_channel?
|
32
|
+
return Flexo::Constants::CHANPREFIX.include?(@target[0..0])
|
33
|
+
end
|
34
|
+
|
35
|
+
# Sends a quick reply to the message.
|
36
|
+
# If to_channel is false, it's sent to the person who sent the
|
37
|
+
# message, whether he sent it to a channel, or private.
|
38
|
+
# If it was sent to you, this will send a direct reply
|
39
|
+
# no matter what to_channel is set to.
|
40
|
+
def reply(text, to_channel=true)
|
41
|
+
target = (to_channel && to_channel?) ? @target : @sender
|
42
|
+
@manager.sender.privmsg(target, text)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|