PerfectlyNormal-Flexo 0.3.9
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/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
|