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
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module Flexo
|
4
|
+
module Events
|
5
|
+
# A numeric reply of some kind.
|
6
|
+
class ReplyEvent < Event
|
7
|
+
attr_reader :mynickname
|
8
|
+
attr_reader :numeric
|
9
|
+
attr_reader :text
|
10
|
+
attr_reader :reply
|
11
|
+
|
12
|
+
alias mynick mynickname
|
13
|
+
alias struct reply
|
14
|
+
|
15
|
+
def initialize(*args)
|
16
|
+
super
|
17
|
+
@mynickname = @data.params[0]
|
18
|
+
@numeric = @data.message.to_i
|
19
|
+
@text = @data.string
|
20
|
+
@is_error = @numeric >= 400
|
21
|
+
@reply = Struct.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.[](reply)
|
25
|
+
n = (reply =~ /^\d+$/ || reply.kind_of?(Integer)) ? reply.to_i : Numerics.const_get(reply)
|
26
|
+
Numeric.new(n)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# This open structure is used to construct detailed objects about a specific
|
31
|
+
# reply, thus contains information specific to the received response.
|
32
|
+
class ReplyEvent::Struct < OpenStruct
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returned by ReplyEvent.[] and used by Dispatcher to be able to subscribe to
|
36
|
+
# specific reply events.
|
37
|
+
class ReplyEvent::Numeric
|
38
|
+
attr_reader :numeric # :nodoc:
|
39
|
+
|
40
|
+
def initialize(numeric) # :nodoc:
|
41
|
+
@numeric = numeric
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Flexo
|
2
|
+
module Events
|
3
|
+
class TopicEvent < Event
|
4
|
+
attr_reader :channel
|
5
|
+
attr_reader :topic
|
6
|
+
|
7
|
+
def initialize(*args)
|
8
|
+
super
|
9
|
+
@channel = @data.params[0]
|
10
|
+
@topic = @data.string
|
11
|
+
end
|
12
|
+
|
13
|
+
def cleared?
|
14
|
+
@topic.empty?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Flexo
|
2
|
+
# As seen in Flexo::Dispatcher.
|
3
|
+
# This is the class that's spawned to handle
|
4
|
+
# a subscription to an event.
|
5
|
+
#
|
6
|
+
# Each call to Flexo::Dispatcher.subscribe
|
7
|
+
# creates an instance of Flexo::Handler, which
|
8
|
+
# is called when the event fires.
|
9
|
+
#
|
10
|
+
# Also, to unsubscribe, the original handle-object
|
11
|
+
# is required. So don't throw it away.
|
12
|
+
class Handler
|
13
|
+
# This just stores some instance variables
|
14
|
+
def initialize(numeric, &block)
|
15
|
+
@manager = Flexo::Manager.instance
|
16
|
+
@numeric = numeric
|
17
|
+
@block = block
|
18
|
+
end
|
19
|
+
|
20
|
+
# While this function does the actuall
|
21
|
+
# calling of the block. So when this function,
|
22
|
+
# aptly named 'call', is called, the block passed
|
23
|
+
# as block when creating this instance, is executed.
|
24
|
+
def call(event)
|
25
|
+
if !@numeric || event.class == ReplyEvent && @numeric == event.numeric
|
26
|
+
@manager.thread { @block.call(event) }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/flexo/logger.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Flexo
|
4
|
+
# Having a little log of what's going on might not
|
5
|
+
# be such a bad idea. This provides methods
|
6
|
+
# for outputting information to a logfile
|
7
|
+
class Logger
|
8
|
+
attr_reader :logfile
|
9
|
+
attr_accessor :loglevel
|
10
|
+
|
11
|
+
# Open a logfile for writing.
|
12
|
+
# We want to open the logger first, so
|
13
|
+
# instead of reading values from the config when
|
14
|
+
# initializing, we set them to defaults, and then,
|
15
|
+
# after the config has been loaded, override them.
|
16
|
+
def initialize
|
17
|
+
@logfile = File.expand_path('~/flexo.log')
|
18
|
+
@loglevel = 3
|
19
|
+
@handle = STDERR
|
20
|
+
end
|
21
|
+
|
22
|
+
# If we change the logfile, we should also reopen the handle
|
23
|
+
def logfile=(file)
|
24
|
+
file = File.expand_path(file)
|
25
|
+
if File.writable?(file)
|
26
|
+
@handle = File.open(file, 'a+')
|
27
|
+
info "Switched logfile to #{file}"
|
28
|
+
else
|
29
|
+
# Little fallback to make sure we have somewhere to log
|
30
|
+
@handle = STDERR
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Quick and dirty hack to find the textual representation
|
35
|
+
# of a log level based on the numeric value
|
36
|
+
def const_to_text(i)
|
37
|
+
hit = ""
|
38
|
+
|
39
|
+
Flexo::Constants.constants.each do |const|
|
40
|
+
if Flexo::Constants.const_get(const) == i
|
41
|
+
hit = const
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
return hit.split('_')[-1]
|
46
|
+
end
|
47
|
+
|
48
|
+
# Writes a line into the log
|
49
|
+
def log(level, line)
|
50
|
+
i = level
|
51
|
+
level = const_to_text(level)
|
52
|
+
|
53
|
+
if(i <= @loglevel)
|
54
|
+
Thread.new do
|
55
|
+
@handle.print "#{level.to_s.upcase} #{Time.now} #{line}\n"
|
56
|
+
@handle.flush
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Convenience function for debug-messages
|
62
|
+
def debug(line)
|
63
|
+
log(Flexo::Constants::LOG_DEBUG, line)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Convenience for more-verbose-than-debug-messages
|
67
|
+
def debug2(line)
|
68
|
+
log(Flexo::Constants::LOG_DEBUG2, line)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Convenience for information
|
72
|
+
def info(line)
|
73
|
+
log(Flexo::Constants::LOG_INFO, line)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Convenience for warnings
|
77
|
+
def warn(line)
|
78
|
+
log(Flexo::Constants::LOG_WARN, line)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Convenience for errors
|
82
|
+
def error(line)
|
83
|
+
log(Flexo::Constants::LOG_ERROR, line)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'singleton'
|
3
|
+
require 'flexo/errors'
|
4
|
+
require 'flexo/config'
|
5
|
+
require 'flexo/constants'
|
6
|
+
require 'flexo/data'
|
7
|
+
require 'flexo/dispatcher'
|
8
|
+
require 'flexo/event'
|
9
|
+
require 'flexo/handler'
|
10
|
+
require 'flexo/logger'
|
11
|
+
require 'flexo/numerics'
|
12
|
+
require 'flexo/pluginmanager'
|
13
|
+
require 'flexo/plugin'
|
14
|
+
require 'flexo/sender'
|
15
|
+
require 'flexo/server'
|
16
|
+
require 'flexo/trigger'
|
17
|
+
|
18
|
+
module Flexo
|
19
|
+
# One class to rule them all.
|
20
|
+
# And in the darkness bind them.
|
21
|
+
#
|
22
|
+
# This singleton-class creates an
|
23
|
+
# instance of each other class that's needed
|
24
|
+
# and manages threads, mutexes and other things
|
25
|
+
# that need sharing between classes. Each
|
26
|
+
# class can just save a reference to Flexo::Manager
|
27
|
+
# and get access to everything they need.
|
28
|
+
class Manager
|
29
|
+
include Singleton
|
30
|
+
|
31
|
+
def initialize
|
32
|
+
end
|
33
|
+
|
34
|
+
# Create everything
|
35
|
+
def setup
|
36
|
+
return if @setup == true
|
37
|
+
@setup = true
|
38
|
+
|
39
|
+
@config = Flexo::Config.new
|
40
|
+
raise InvalidConfigurationException unless @config.valid?
|
41
|
+
|
42
|
+
@nickname = nil
|
43
|
+
@threadqueue = Queue.new
|
44
|
+
@threadgroup = ThreadGroup.new
|
45
|
+
@threadmax = 100
|
46
|
+
@mutex = Mutex.new
|
47
|
+
@pluginmutex = Mutex.new
|
48
|
+
|
49
|
+
@logger = Flexo::Logger.new
|
50
|
+
@logger.loglevel = @config['log.level']
|
51
|
+
@logger.logfile = @config['log.file']
|
52
|
+
|
53
|
+
@logger.debug "Creating Dispatcher"
|
54
|
+
@dispatcher = Flexo::Dispatcher.new
|
55
|
+
|
56
|
+
@logger.debug "Creating PluginManager"
|
57
|
+
@pluginmanager = Flexo::PluginManager.new(@pluginmutex)
|
58
|
+
@pluginmanager.load_plugin('control')
|
59
|
+
@pluginmanager.load_plugin('auth')
|
60
|
+
|
61
|
+
@logger.debug "Creating Sender"
|
62
|
+
@sender = Flexo::Sender.new
|
63
|
+
|
64
|
+
@logger.debug "Creating Server"
|
65
|
+
@server = Flexo::Server.new
|
66
|
+
@server.setup
|
67
|
+
|
68
|
+
@logger.debug "Created all Flexo-classes. Going to create threads"
|
69
|
+
manager = Thread.new do
|
70
|
+
loop do
|
71
|
+
if @threadqueue.size < @threadmax
|
72
|
+
@threadgroup.add(@threadqueue.shift.wakeup)
|
73
|
+
else
|
74
|
+
sleep 0.1
|
75
|
+
end
|
76
|
+
end
|
77
|
+
@threadgroup.add manager
|
78
|
+
end
|
79
|
+
|
80
|
+
@logger.info "Setting up default handlers"
|
81
|
+
dispatcher.subscribe(:PING) { |ping| ping.pong }
|
82
|
+
dispatcher.subscribe(Flexo::Events::PrivmsgEvent) { |msg|
|
83
|
+
return unless msg.ctcp?
|
84
|
+
|
85
|
+
case msg.text.split(' ')[0].upcase
|
86
|
+
when 'VERSION'
|
87
|
+
l = "\001VERSION Flexo #{Flexo::Constants::FLEXO_VERSION} "
|
88
|
+
l += "(#{Flexo::Constants::FLEXO_RELEASE})\001"
|
89
|
+
@sender.notice(msg.data.hostmask.nickname, l)
|
90
|
+
when 'PING'
|
91
|
+
arg = msg.text.split(' ')[1]
|
92
|
+
cmd = "\001PING #{arg}\001"
|
93
|
+
@sender.notice(msg.data.hostmask.nickname, cmd)
|
94
|
+
when 'QUIT'
|
95
|
+
if msg.text.split(' ')[1] == 'lololz'
|
96
|
+
@server.disconnect
|
97
|
+
exit
|
98
|
+
end
|
99
|
+
end
|
100
|
+
}
|
101
|
+
|
102
|
+
@logger.debug "Sleeping a while to allow logging into the server"
|
103
|
+
sleep 15
|
104
|
+
|
105
|
+
@logger.info "Joining channels"
|
106
|
+
@config['core.channels'].each do |channel|
|
107
|
+
@sender.join(channel[:channel], channel[:key])
|
108
|
+
end
|
109
|
+
|
110
|
+
@logger.debug "Joining server-thread"
|
111
|
+
@server.thread.join
|
112
|
+
end
|
113
|
+
|
114
|
+
# Spawns a thread to handle the block
|
115
|
+
def thread(&block)
|
116
|
+
thread = Thread.new { sleep; block.call }
|
117
|
+
@threadqueue << thread
|
118
|
+
return thread
|
119
|
+
end
|
120
|
+
|
121
|
+
def mutex
|
122
|
+
@mutex.synchronize { yield }
|
123
|
+
end
|
124
|
+
|
125
|
+
def pluginmutex
|
126
|
+
@pluginmutex.synchronize { yield }
|
127
|
+
end
|
128
|
+
|
129
|
+
def dispatcher
|
130
|
+
return @dispatcher
|
131
|
+
end
|
132
|
+
|
133
|
+
def logger
|
134
|
+
return @logger
|
135
|
+
end
|
136
|
+
|
137
|
+
def config
|
138
|
+
return @config
|
139
|
+
end
|
140
|
+
|
141
|
+
def sender
|
142
|
+
return @sender
|
143
|
+
end
|
144
|
+
|
145
|
+
def server
|
146
|
+
return @server
|
147
|
+
end
|
148
|
+
|
149
|
+
def nickname; return @nickname; end
|
150
|
+
def nickname=(nick); @nickname = nick; nick; end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
module Flexo
|
2
|
+
# This module contains a long list of constants.
|
3
|
+
# It maps numeric IRC codes to their text equivalents
|
4
|
+
#
|
5
|
+
# (Everything is stolen from http://tools.ietf.org/html/rfc2812#section-5)
|
6
|
+
module Numerics
|
7
|
+
RPL_WELCOME = 001 # "Welcome to the Internet Relay Network
|
8
|
+
# <nick>!<user>@<host>"
|
9
|
+
RPL_YOURHOST = 002 # "Your host is <servername>, running version <ver>"
|
10
|
+
RPL_CREATED = 003 # "This server was created <date>"
|
11
|
+
RPL_MYINFO = 004 # "<servername> <version> <available user modes>
|
12
|
+
# <available channel modes>"
|
13
|
+
RPL_BOUNCE = 005 # "Try server <server>, port <port>"
|
14
|
+
# Sent to suggest an alternative server
|
15
|
+
# Often when the current server is full
|
16
|
+
|
17
|
+
RPL_TRACELINK = 200 # "Link <version & debug level> <destination>
|
18
|
+
# <next server> V<protocol version>
|
19
|
+
# <link uptime in seconds> <backstream sendq>
|
20
|
+
# <upstream sendq>"
|
21
|
+
RPL_TRACECONNECTING = 201 # "Try. <class> <server>"
|
22
|
+
RPL_TRACEHANDSHAKE = 202 # "H.S. <class> <server>"
|
23
|
+
RPL_TRACEUNKNOWN = 203 # "???? <class> [<client IP address in dot form>]"
|
24
|
+
RPL_TRACEOPERATOR = 204 # "Oper <class> <nick>"
|
25
|
+
RPL_TRACEUSER = 205 # "User <class> <nick>"
|
26
|
+
RPL_TRACESERVER = 206 # "Serv <class> <int>S <int>C <server>
|
27
|
+
# <nick!user|*!*>@<host|server> V<protocol version>"
|
28
|
+
RPL_TRACESERVICE = 207 # "Service <class> <name> <type> <active type>"
|
29
|
+
RPL_TRACENEWTYPE = 208 # "<newtype> 0 <client name>"
|
30
|
+
RPL_TRACECLASS = 209 # "Class <class> <count>"
|
31
|
+
RPL_TRACERECONNECT = 210 # Unused.
|
32
|
+
RPL_TRACELOG = 261 # "File <logfile> <debuglevel>"
|
33
|
+
RPL_TRACEEND = 262 # "<server name> <version & debug level> :End of TRACE"
|
34
|
+
|
35
|
+
RPL_STATSLINKINFO = 211 # "<linkname> <sendq> <sent messages>
|
36
|
+
# <sent Kbytes> <received messages>
|
37
|
+
# <received Kbytes> <time open>"
|
38
|
+
RPL_STATSCOMMANDS = 212 # "<command> <count> <byte count> <remote count>"
|
39
|
+
RPL_ENDOFSTATS = 219 # "<stats letter> :End of STATS report"
|
40
|
+
RPL_STATSUPTIME = 242 # ":server up %d days %d:%02d:%02d"
|
41
|
+
RPL_STATSOLINE = 243 # "O <hostmask> * <name>"
|
42
|
+
RPL_UMODEIS = 221 # "<user mode string>"
|
43
|
+
RPL_SERVLIST = 234 # "<name> <server> <mask> <type> <hopcount> <info>"
|
44
|
+
RPL_SERVLISTEND = 235 # "<mask> <type> :End of service listing"
|
45
|
+
RPL_LUSERCLIENT = 251 # ":There are <integer> users and <integer>
|
46
|
+
# services on <integer> servers"
|
47
|
+
RPL_LUSEROP = 252 # "<integer> :operator(s) online"
|
48
|
+
RPL_LUSERUNKNOWN = 253 # "<integer> :unknown connection(s)"
|
49
|
+
RPL_LUSERCHANNELS = 254 # "<integer> :channels formed"
|
50
|
+
RPL_LUSERME = 255 # ":I have <integer> clients and <integer> servers"
|
51
|
+
RPL_ADMINME = 256 # "<server> :Administrative info"
|
52
|
+
RPL_ADMINLOC1 = 257 # ":<admin info>"
|
53
|
+
RPL_ADMINLOC2 = 258 # ":<admin info>"
|
54
|
+
RPL_ADMINEMAIL = 259 # ":<admin info>"
|
55
|
+
RPL_TRYAGAIN = 263 # "<command> :Please wait a while and try again."
|
56
|
+
|
57
|
+
RPL_USERHOST = 302 # ":*1<reply> *( " " <reply> )"
|
58
|
+
RPL_ISON = 303 # ":*1<nick> *( " " <nick> )"
|
59
|
+
RPL_AWAY = 301 # "<nick> :<away message>"
|
60
|
+
RPL_UNAWAY = 305 # ":You are no longer marked as being away"
|
61
|
+
RPL_NOWAWAY = 306 # ":You have been marked as being away"
|
62
|
+
RPL_WHOISUSER = 311 # "<nick> <user> <host> * :<real name>"
|
63
|
+
RPL_WHOISSERVER = 312 # "<nick> <server> :<server info>"
|
64
|
+
RPL_WHOISOPERATOR = 313 # "<nick> :is an IRC operator"
|
65
|
+
RPL_WHOISIDLE = 317 # "<nick> <integer> :seconds idle"
|
66
|
+
RPL_ENDWHOIS = 318 # "<nick> :End of WHOIS list"
|
67
|
+
RPL_WHOISCHANNELS = 319 # "<nick> :*( ( "@" / "+" ) <channel> " " )"
|
68
|
+
RPL_WHOWASUSER = 314 # "<nick> <user> <host> * :<real name>"
|
69
|
+
RPL_ENDWHOWAS = 369 # "<nick> :End of WHOWAS"
|
70
|
+
RPL_LISTSTART = 321 # Obsolete. Not used.
|
71
|
+
RPL_LIST = 322 # "<channel> <# visible> :<topic>"
|
72
|
+
RPL_LISTEND = 323 # ":End of LIST"
|
73
|
+
RPL_UNIQOPIS = 325 # <channel> <nickname>"
|
74
|
+
RPL_CHANNELMODEIS = 324 # "<channel> <mode> <mode params>"
|
75
|
+
RPL_NOTOPIC = 331 # "<channel> :No topic is set"
|
76
|
+
RPL_TOPIC = 332 # "<channel> :<topic>"
|
77
|
+
RPL_INVITING = 341 # "<channel> <nick>"
|
78
|
+
RPL_SUMMONING = 342 # "<user> :Summoning user to IRC"
|
79
|
+
RPL_INVITELIST = 346 # "<channel> <invitemask>"
|
80
|
+
RPL_ENDOFINVITELIST = 347 # "<channel> :End of channel invite list"
|
81
|
+
RPL_EXCEPTLIST = 348 # "<channel> <exceptionmask>"
|
82
|
+
RPL_ENDOFEXCEPTLIST = 349 # "<channel> :End of channel exception list"
|
83
|
+
RPL_VERSION = 351 # "<version>.<debuglevel> <server> :<comments>"
|
84
|
+
RPL_WHOREPLY = 352 # "<channel> <user> <host> <server> <nick>
|
85
|
+
# ( "H" / "G" > ["*"] [ ( "@" / "+" ) ]
|
86
|
+
# :<hopcount> <real name>"
|
87
|
+
RPL_ENDOFWHO = 315 # "<name> :End of WHO list"
|
88
|
+
RPL_NAMREPLY = 353 # ( "=" / "*" / "@" ) <channel>
|
89
|
+
# :[ "@" / "+" ] <nick> *( " " [ "@" / "+" ] <nick> )
|
90
|
+
# - "@" is used for secret channels, "*" for private
|
91
|
+
# channels, and "=" for others (public channels).
|
92
|
+
RPL_ENDOFNAMES = 366 # "<channel> :End of NAMES list"
|
93
|
+
RPL_LINKS = 364 # "<mask> <server> :<hopcount> <server info>"
|
94
|
+
RPL_ENDOFLINKS = 365 # "<mask> :End of LINKS list"
|
95
|
+
RPL_BANLIST = 367 # "<channel> <banmask>"
|
96
|
+
RPL_ENDOFBANLIST = 368 # "<channel> :End of channel ban list"
|
97
|
+
RPL_INFO = 371 # ":<string>"
|
98
|
+
RPL_ENFOFINFO = 374 # ":End of INFO list"
|
99
|
+
RPL_MOTDSTART = 375 # ":- <server> Message of the day - "
|
100
|
+
RPL_MOTD = 372 # ":- <text>"
|
101
|
+
RPL_ENDOFMOTD = 376 # ":End of MOTD command"
|
102
|
+
RPL_YOUREOPER = 381 # ":You are now an IRC operator"
|
103
|
+
RPL_REHASHING = 382 # "<config file> :Rehashing"
|
104
|
+
RPL_YOURESERVICE = 383 # "You are service <servicename>"
|
105
|
+
RPL_TIME = 391 # "<server> :<server's local time>"
|
106
|
+
RPL_USERSSTART = 392 # ":UserID Terminal Host"
|
107
|
+
RPL_USERS = 393 # ":<username> <ttyline> <hostname>"
|
108
|
+
RPL_ENDOFUSERS = 394 # ":End of users"
|
109
|
+
RPL_NOUSERS = 395 # ":Nobody logged in"
|
110
|
+
|
111
|
+
ERR_NOSUCHNICK = 401 # "<nickname> :No such nick/channel"
|
112
|
+
ERR_NOSUCHSERVER = 402 # "<server name> :No such server"
|
113
|
+
ERR_NOSUCHCHANNEL = 403 # "<channel name> :No such channel"
|
114
|
+
ERR_CANNOTSENDTOCHAN = 404 # "<channel name> :Cannot send to channel"
|
115
|
+
ERR_TOOMANYCHANNELS = 405 # "<channel name> :You have joined too many channels"
|
116
|
+
ERR_WASNOSUCHNICK = 406 # "<nickname> :There was no such nickname"
|
117
|
+
ERR_TOOMANYTARGETS = 407 # <target> :<error code> recipients. <abort message>"
|
118
|
+
# - Returned to a client which is attempting to send a
|
119
|
+
# PRIVMSG/NOTICE using the user@host destination format
|
120
|
+
# and for a user@host which has several occurrences.
|
121
|
+
# - Returned to a client which trying to send a
|
122
|
+
# PRIVMSG/NOTICE to too many recipients.
|
123
|
+
# - Returned to a client which is attempting to JOIN a safe
|
124
|
+
# channel using the shortname when there are more than one
|
125
|
+
# such channel.
|
126
|
+
ERR_NOSUCHSERVICE = 408 # "<service name> :No such service"
|
127
|
+
ERR_NOORIGIN = 409 # ":No origin specified"
|
128
|
+
# - PING or PONG message missing the
|
129
|
+
# originator parameter.
|
130
|
+
ERR_NORECIPIENT = 411 # ":No recipient given (<command>)"
|
131
|
+
ERR_NOTEXTTOSEND = 412 # ":No text to send"
|
132
|
+
ERR_NOTOPLEVEL = 413 # "<mask> :No toplevel domain specified"
|
133
|
+
ERR_WILDTOPLEVEL = 414 # "<mask> :Wildcard in toplevel domain"
|
134
|
+
ERR_BADMASK = 415 # "<mask> :Bad server/host mask"
|
135
|
+
ERR_UNKNOWNCOMMAND = 421 # "<command> :Unknown command"
|
136
|
+
ERR_NOMOTD = 422 # ":MOTD File is missing"
|
137
|
+
ERR_NOADMININFO = 423 # "<server> :No administrative info available"
|
138
|
+
ERR_FILEERROR = 424 # ":File error doing <file op> on <file>"
|
139
|
+
ERR_NONICKNAMEGIVEN = 431 # ":No nickname given"
|
140
|
+
ERR_ERRONEUSNICKNAME = 432 # "<nick> :Erroneous nickname"
|
141
|
+
ERR_NICKNAMEINUSE = 433 # "<nick> :Nickname is already in use"
|
142
|
+
ERR_NICKCOLLISION = 436 # "<nick> :Nickname collision KILL from <user>@<host>"
|
143
|
+
ERR_UNAVAILRESOURCE = 437 # "<nick/channel> :Nick/channel is temporarily unavailable"
|
144
|
+
ERR_USERNOTINCHANNEL = 441 # "<nick> <channel> :They aren't on that channel"
|
145
|
+
ERR_NOTONCHANNEL = 442 # "<channel> :You're not on that channel"
|
146
|
+
ERR_USERONCHANNEL = 443 # "<user> <channel> :is already on channel"
|
147
|
+
ERR_NOLOGIN = 444 # "<user> :User not logged in"
|
148
|
+
ERR_SUMMONDISABLED = 445 # ":SUMMON has been disabled"
|
149
|
+
ERR_USERSDISABLED = 446 # ":USERS has been disabled"
|
150
|
+
ERR_NOTREGISTERED = 451 # ":You have not registered"
|
151
|
+
ERR_NEEDMOREPARAMS = 461 # "<command> :Not enough parameters"
|
152
|
+
ERR_ALREADYREGISTRED = 462 # ":Unauthorized command (already registered)"
|
153
|
+
ERR_NOPERMFORHOST = 463 # ":Your host isn't among the privileged"
|
154
|
+
ERR_PASSWDMISMATCH = 464 # ":Password incorrect"
|
155
|
+
ERR_YOUREBANNEDCREEP = 465 # ":You are banned from this server"
|
156
|
+
ERR_YOUWILLBEBANNED = 466 # Sent by a server to inform that access will soon be denied
|
157
|
+
ERR_KEYSET = 467 # "<channel> :Channel key already set"
|
158
|
+
ERR_CHANNELISFULL = 471 # "<channel> :Cannot join channel (+l)"
|
159
|
+
ERR_UNKNOWNMODE = 472 # "<char> :is unknown mode char to me for <channel>"
|
160
|
+
ERR_INVITEONLYCHAN = 473 # "<channel> :Cannot join channel (+i)"
|
161
|
+
ERR_BANNEDFROMCHAN = 474 # "<channel> :Cannot join channel (+b)"
|
162
|
+
ERR_BADCHANNELKEY = 475 # "<channel> :Cannot join channel (+k)"
|
163
|
+
ERR_BADCHANMASK = 476 # "<channel> :Bad Channel Mask"
|
164
|
+
ERR_NOCHANMODES = 477 # "<channel> :Channel doesn't support modes"
|
165
|
+
ERR_BANLISTFULL = 478 # "<channel> <char> :Channel list is full"
|
166
|
+
ERR_NOPRIVILEGES = 481 # ":Permission Denied - You're not an IRC operator"
|
167
|
+
ERR_CHANOPPRIVSNEEDED = 482 # "<channel> :You're not channel operator"
|
168
|
+
ERR_CANTKILLSERVER = 483 # ":You can't kill a server!"
|
169
|
+
ERR_RESTRICTED = 484 # ":Your connection is restricted!"
|
170
|
+
ERR_UNIQOPPRIVSNEEDED = 485 # ":You're not the original channel operator"
|
171
|
+
ERR_NOOPERHOST = 491 # ":No O-lines for your host"
|
172
|
+
ERR_UMODEUNKNOWNFLAG = 501 # ":Unknown MODE flag"
|
173
|
+
ERR_USERSDONTMATCH = 502 # ":Cannot change mode for other users
|
174
|
+
|
175
|
+
# The following numerics are either:
|
176
|
+
# 1. no longer in use
|
177
|
+
# 2. reserved for future planned use
|
178
|
+
# 3. in current use but part of a non-generic 'feature'
|
179
|
+
# of the current IRC server
|
180
|
+
RPL_SERVICEINFO = 231
|
181
|
+
RPL_ENDOFSERVICES = 232
|
182
|
+
RPL_SERVICE = 233
|
183
|
+
RPL_NONE = 300
|
184
|
+
RPL_WHOISCHANOP = 316
|
185
|
+
RPL_KILLDONE = 361
|
186
|
+
RPL_CLOSING = 362
|
187
|
+
RPL_CLOSEEND = 363
|
188
|
+
RPL_INFOSTART = 373
|
189
|
+
RPL_MYPORTIS = 384
|
190
|
+
|
191
|
+
RPL_STATSCLINE = 213
|
192
|
+
RPL_STATSNLINE = 214
|
193
|
+
RPL_STATSILINE = 215
|
194
|
+
RPL_STATSKLINE = 216
|
195
|
+
RPL_STATSQLINE = 217
|
196
|
+
RPL_STATSYLINE = 218
|
197
|
+
RPL_STATSVLINE = 240
|
198
|
+
RPL_STATSLLINE = 241
|
199
|
+
RPL_STATSHLINE = 244
|
200
|
+
RPL_STATSSLINE = 244
|
201
|
+
RPL_STATSPING = 246
|
202
|
+
RPL_STATSBLINE = 247
|
203
|
+
RPL_STATSDLINE = 250
|
204
|
+
|
205
|
+
ERR_NOSERVICEHOST = 492
|
206
|
+
end
|
207
|
+
end
|
data/lib/flexo/plugin.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
module Flexo
|
2
|
+
# Base class of all plugins.
|
3
|
+
# All plugins wanting to be loaded by
|
4
|
+
# Flexo must subclass this class or face the
|
5
|
+
# wrath of Flexo::PluginManager.
|
6
|
+
#
|
7
|
+
# If the plugin requires persistent storage,
|
8
|
+
# keeping it as a YAML-file like ~/.flexo/<plugin>.yaml
|
9
|
+
# would make Flexo::Config load it when starting, and
|
10
|
+
# making them available to the plugin through a simple
|
11
|
+
# call to @manager.config[key]. But that requires plugins
|
12
|
+
# to make their own namespace, preferably something like
|
13
|
+
# plugins.<pluginname>.key to prevent collisions.
|
14
|
+
class Plugin
|
15
|
+
def self.const_missing(const)
|
16
|
+
super unless [:NAME, :VERSION, :SUMMARY, :DEPENDS].include?(const)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Variable, mutex, check.
|
20
|
+
def initialize(plugins, *args)
|
21
|
+
@manager = Flexo::Manager.instance
|
22
|
+
@plugins = plugins
|
23
|
+
@manager.logger.debug "Created #{self.class}, with #{@plugins.to_a}"
|
24
|
+
end
|
25
|
+
|
26
|
+
# Overriding method_missing to let plugins use commands
|
27
|
+
# from other plugins. If the plugin tries to use a function
|
28
|
+
# that doesn't exist, check the list of loaded plugins
|
29
|
+
# if any of those respond to the function.
|
30
|
+
def method_missing(method_name, *args, &block)
|
31
|
+
@manager.logger.debug "Method #{method_name} not found in #{self.class}"
|
32
|
+
@plugins.each do |plugin_name, plugin|
|
33
|
+
@manager.logger.debug " Checking in #{plugin_name}"
|
34
|
+
if plugin.respond_to?(method_name)
|
35
|
+
@manager.logger.debug " Found #{method_name} in #{plugin_name}"
|
36
|
+
return plugin.__send__(method_name, *args, &block)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
@manager.logger.debug " Method #{method_name} not found in any loaded plugins"
|
41
|
+
super
|
42
|
+
end
|
43
|
+
|
44
|
+
# Synchronize mutex
|
45
|
+
def mutex
|
46
|
+
@manager.pluginmutex { yield }
|
47
|
+
end
|
48
|
+
|
49
|
+
# Convenience. See Flexo::Dispatcher.subscribe
|
50
|
+
def subscribe(event, &block)
|
51
|
+
@manager.dispatcher.subscribe(event, &block)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Convenience. See Flexo::Dispatcher.unsubscribe
|
55
|
+
def unsubscribe(handler)
|
56
|
+
@manager.dispatcher.unsubscribe(handler)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Convenience. See Flexo::Dispatcher.add_trigger
|
60
|
+
def add_trigger(pattern, &block)
|
61
|
+
@manager.dispatcher.add_trigger(pattern, &block)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Convenience. See Flexo::Dispatcher.remove_trigger
|
65
|
+
def remove_trigger(trigger)
|
66
|
+
@manager.dispatcher.remove_trigger(trigger)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|