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
data/lib/flexo/server.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
require 'socket'
|
3
|
+
require 'thread'
|
4
|
+
|
5
|
+
module Flexo
|
6
|
+
# The class that does all the socket-works.
|
7
|
+
# Using Sender, it sends commands to the server, and, using Dispatcher,
|
8
|
+
# gets the responses sent back into Flexo for further processing.
|
9
|
+
class Server
|
10
|
+
attr_reader :thread
|
11
|
+
attr_reader :server
|
12
|
+
attr_reader :nickname
|
13
|
+
attr_reader :monitor
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@manager = Flexo::Manager.instance
|
17
|
+
@socket = nil
|
18
|
+
@server = nil
|
19
|
+
@monitor = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def setup
|
23
|
+
connect
|
24
|
+
|
25
|
+
@thread = @manager.thread do
|
26
|
+
begin
|
27
|
+
while line = @socket.gets
|
28
|
+
@manager.dispatcher.receive(line)
|
29
|
+
end
|
30
|
+
disconnect
|
31
|
+
rescue Errno::ETIMEDOUT
|
32
|
+
@manager.logger.warn "Connection lost. Reconnecting"
|
33
|
+
connect
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Set up a thread to check the connection once
|
38
|
+
# in a while. See bug #15
|
39
|
+
@monitor = Thread.new do
|
40
|
+
while true
|
41
|
+
sleep 45
|
42
|
+
check_connection
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def debug(line) # :nodoc:
|
48
|
+
@manager.logger.debug(line)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Called by Sender.quote. This function just writes the
|
52
|
+
# prepared line to the socket.
|
53
|
+
def quote(data)
|
54
|
+
@socket.print("#{data.chomp}\r\n")
|
55
|
+
end
|
56
|
+
|
57
|
+
# Does the connecting, looping through the defined
|
58
|
+
# servers, trying the next one if it fails.
|
59
|
+
def connect
|
60
|
+
debug "Starting to connect!"
|
61
|
+
@manager.config['core.server'].each do |server|
|
62
|
+
debug "Connecting to #{server[:host]}:#{server[:port]}"
|
63
|
+
timeout(30) do
|
64
|
+
err = 0
|
65
|
+
begin
|
66
|
+
@socket = TCPSocket.new(server[:host], server[:port])
|
67
|
+
rescue Errno::ECONNREFUSED => h
|
68
|
+
msg = "Connection to #{server[:host]} at port #{server[:port]} was refused"
|
69
|
+
err = 1
|
70
|
+
rescue Errno::EHOSTUNREACH => h
|
71
|
+
msg = "#{server[:host]} is unreachable!"
|
72
|
+
err = 1
|
73
|
+
rescue Timeout::Error => h
|
74
|
+
msg = "Connection to #{server[:host]} timed out"
|
75
|
+
err = 1
|
76
|
+
rescue Errno::EADDRNOTAVAIL => h
|
77
|
+
msg = "Unable to assign requested address"
|
78
|
+
err = 2
|
79
|
+
end
|
80
|
+
|
81
|
+
if(err != 0)
|
82
|
+
@socket = nil
|
83
|
+
@manager.logger.warn(msg)
|
84
|
+
if err > 1 # Critical error!
|
85
|
+
@manager.logger.error("Critical errors encountered!")
|
86
|
+
exit 1
|
87
|
+
end
|
88
|
+
else
|
89
|
+
@manager.logger.info("Connected to #{server[:host]}")
|
90
|
+
login
|
91
|
+
return
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def check_connection
|
98
|
+
if(@socket == nil || @socket.closed?)
|
99
|
+
@manager.logger.error("Not connected to a server. Trying again.")
|
100
|
+
connect
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# This function goes through the login-process
|
105
|
+
def login
|
106
|
+
config = @manager.config
|
107
|
+
|
108
|
+
# First, we add a handler to fix things
|
109
|
+
# if our nickname is taken
|
110
|
+
nicktaken = @manager.dispatcher.subscribe(Flexo::Events::ReplyEvent) do |event|
|
111
|
+
if event.numeric == 433
|
112
|
+
nick = config['core.nick'].shift
|
113
|
+
quote "NICK #{nick}"
|
114
|
+
@manager.logger.info("Nickname taken, trying #{nick}")
|
115
|
+
@manager.nickname = nick
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
nick = config['core.nick'].shift
|
120
|
+
@manager.sender.nick nick
|
121
|
+
@manager.sender.user config['core.username'], 0, config['core.realname']
|
122
|
+
@manager.nickname = nick
|
123
|
+
end
|
124
|
+
|
125
|
+
# Disconnects from the server
|
126
|
+
def disconnect
|
127
|
+
@socket.close unless @socket.closed?
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Flexo
|
2
|
+
# A Trigger is a special type of handler, designed
|
3
|
+
# to trigger an action when a certain Flexo::Events::PrivmsgEvent
|
4
|
+
# is receieved. The PrivmsgEvent is matched against a regexp, and,
|
5
|
+
# if it matches, the action is triggered.
|
6
|
+
class Trigger
|
7
|
+
attr_reader :pattern
|
8
|
+
attr_reader :prefix
|
9
|
+
|
10
|
+
# Creates a trigger.
|
11
|
+
#
|
12
|
+
# _pattern_ is a string or a regular expression to match
|
13
|
+
# against the message. Any group matches will be passed
|
14
|
+
# as arguments to the block
|
15
|
+
def initialize(pattern, &block)
|
16
|
+
@manager = Flexo::Manager.instance
|
17
|
+
@pattern = Regexp.new(pattern)
|
18
|
+
@prefix = @manager.config['core.trigger_prefix']
|
19
|
+
@block = block
|
20
|
+
@handler = @manager.dispatcher.subscribe(Flexo::Events::PrivmsgEvent, &self)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Invokes the trigger
|
24
|
+
def call(privmsg)
|
25
|
+
if(privmsg.text[0...@prefix.size] == @prefix)
|
26
|
+
text = privmsg.text[@prefix.size..-1]
|
27
|
+
elsif(privmsg.to_me? && privmsg.text[0...@prefix.length] == @prefix)
|
28
|
+
text = privmsg.text[@prefix.size..-1]
|
29
|
+
elsif(privmsg.to_me?)
|
30
|
+
text = privmsg.text
|
31
|
+
else
|
32
|
+
return
|
33
|
+
end
|
34
|
+
|
35
|
+
m = text.match(@pattern)
|
36
|
+
@block.call(privmsg, *m[1..-1]) if m
|
37
|
+
end
|
38
|
+
|
39
|
+
# Turns the Trigger into a Proc
|
40
|
+
def to_proc
|
41
|
+
proc { |event| call(event) }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/plugins/auth.rb
ADDED
@@ -0,0 +1,256 @@
|
|
1
|
+
module Flexo
|
2
|
+
module Plugins
|
3
|
+
# A plugin for managing a list of users and their privileges
|
4
|
+
# within Flexo. The usage of this plugin allows other plugins
|
5
|
+
# to add restricted commands that require a certain access level.
|
6
|
+
#
|
7
|
+
# There's no way to "log in", but each time a user tries to run
|
8
|
+
# a restricted command, their current nickname and hostmask
|
9
|
+
# is matched against the database, and if found, the command
|
10
|
+
# executes.
|
11
|
+
class AuthPlugin < Flexo::Plugin
|
12
|
+
NAME = 'auth'
|
13
|
+
VERSION = '0.1.3'
|
14
|
+
SUMMARY = 'Access control list for managing users and their permissions.'
|
15
|
+
DEPENDS = ['pstore', 'ruby-digest/md5']
|
16
|
+
|
17
|
+
attr_accessor :default_level
|
18
|
+
|
19
|
+
def initialize(*args)
|
20
|
+
super
|
21
|
+
@manager.logger.debug "Loaded AuthPlugin"
|
22
|
+
@default_level = 0
|
23
|
+
|
24
|
+
add_trigger(/^user register ([a-zA-Z][a-zA-Z0-9_-]*) (\S+)$/, &method(:cmd_register))
|
25
|
+
add_trigger(/^user addmask ([a-zA-Z][a-zA-Z0-9_-]*) (\S+)$/, &method(:cmd_addmask))
|
26
|
+
|
27
|
+
add_restricted_trigger(/^user change ([a-zA-Z][a-zA-Z0-9_-]*) (\d{1,3})$/, :level => 256, &method(:cmd_changelevel))
|
28
|
+
add_restricted_trigger(/^user remove ([a-zA-Z][a-zA-Z0-9_-]*)$/, :level => 256, &method(:cmd_remove))
|
29
|
+
add_trigger(/^user lookup ([a-zA-Z][a-zA-Z0-9_-]*)$/, &method(:cmd_lookup))
|
30
|
+
|
31
|
+
pstore_open("#{@manager.config.path}/user.db")
|
32
|
+
pstore_transaction do |pstore|
|
33
|
+
pstore[:users] ||= Hash.new
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns the number of users currently registered
|
38
|
+
# in the database.
|
39
|
+
def usercount
|
40
|
+
pstore_transaction do |pstore|
|
41
|
+
return pstore[:users].length
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Similar to Flexo::Dispatcher.add_trigger, except this
|
46
|
+
# also takes an access level argument to determine
|
47
|
+
# whether the user is allowed to run the command or not.
|
48
|
+
def add_restricted_trigger(pattern, opts = {}, &block)
|
49
|
+
opts = { :level => 0 }.merge(opts)
|
50
|
+
add_trigger(pattern) do |privmsg,*args|
|
51
|
+
@manager.logger.debug "Trying to run a restricted method"
|
52
|
+
level = opts[:level].to_i
|
53
|
+
user = get_user_from_hostmask(privmsg.from, privmsg.data.hostmask.hostname)
|
54
|
+
|
55
|
+
if user && user[:level] < level
|
56
|
+
@manager.logger.debug " Not authorized for that"
|
57
|
+
privmsg.reply("Not authorized.", false)
|
58
|
+
elsif !user && level > 0
|
59
|
+
@manager.logger.debug " But not registered"
|
60
|
+
privmsg.reply("Need to register.", false)
|
61
|
+
else
|
62
|
+
@manager.logger.debug " And running the command!"
|
63
|
+
block.call(privmsg,*args)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Finds a user in the database
|
69
|
+
# Returns nil if not found
|
70
|
+
def get_user_from_hostmask(nick, host)
|
71
|
+
@manager.logger.debug "AuthPlugin: Trying to find #{nick}"
|
72
|
+
user = nil
|
73
|
+
|
74
|
+
pstore_transaction do |pstore|
|
75
|
+
user = pstore[:users].find do |name,user|
|
76
|
+
user[:hostmasks].any? do |dbnick,dbhost|
|
77
|
+
dbnick.downcase == nick.downcase &&\
|
78
|
+
dbhost.downcase == host.downcase
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
return user[1] if user
|
84
|
+
return nil
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
# Convenience function for sending a privmsg to a user
|
90
|
+
def msg(target, msg)
|
91
|
+
target.reply(msg)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Checks if a given username is registered.
|
95
|
+
def username_exists?(username)
|
96
|
+
found = false
|
97
|
+
@manager.logger.debug "Going to see if #{username} exists"
|
98
|
+
pstore_transaction do |pstore|
|
99
|
+
pstore[:users].keys.any? { |dbname|
|
100
|
+
@manager.logger.debug " Checking #{dbname} against #{username}"
|
101
|
+
found = (dbname.downcase == username.downcase)
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
@manager.logger.debug "Found == #{found}"
|
106
|
+
return found
|
107
|
+
end
|
108
|
+
|
109
|
+
# Checks if a given hostmask is registered.
|
110
|
+
def hostmask_exists?(nick, mask)
|
111
|
+
@manager.logger.debug "Auth: Host: Checking if #{nick} and #{mask} exists"
|
112
|
+
found = false
|
113
|
+
|
114
|
+
pstore_transaction do |pstore|
|
115
|
+
pstore[:users].any? do |name,user|
|
116
|
+
user[:hostmasks].any? do |dbnick,dbmask|
|
117
|
+
found = true if (dbnick.downcase == nick.downcase && dbmask.downcase == mask.downcase)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
@manager.logger.debug "Auth: Host: Returning #{found}"
|
123
|
+
return found
|
124
|
+
end
|
125
|
+
|
126
|
+
# Returns true if the username/password combination
|
127
|
+
# matches, and false if it does not.
|
128
|
+
def is_correct?(user, pass)
|
129
|
+
c = pstore_transaction do |pstore|
|
130
|
+
pstore[:users][user.downcase][:password] == Digest::MD5.hexdigest(pass).to_s
|
131
|
+
end
|
132
|
+
|
133
|
+
return c
|
134
|
+
end
|
135
|
+
|
136
|
+
# Creates a new user in the database
|
137
|
+
# Not meant to be called directly, but to be used
|
138
|
+
# from a running Flexo-instance, using the register-command.
|
139
|
+
def register(user, pass, nick, host, level)
|
140
|
+
user.downcase!
|
141
|
+
tmpuser = {
|
142
|
+
:username => user,
|
143
|
+
:password => Digest::MD5.hexdigest(pass).to_s,
|
144
|
+
:hostmasks => [[nick, host]],
|
145
|
+
:level => level.to_i
|
146
|
+
}
|
147
|
+
|
148
|
+
pstore_transaction do |pstore|
|
149
|
+
pstore[:users][user] = tmpuser
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Adds a nickname/hostmask combo to a given user
|
154
|
+
def addmask(user, nick, mask)
|
155
|
+
pstore_transaction do |pstore|
|
156
|
+
pstore[:users][user.downcase][:hostmasks] << [nick, mask]
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Triggered when a user tries to register via a privmsg
|
161
|
+
def cmd_register(privmsg, username, password)
|
162
|
+
@manager.logger.debug "AuthPlugin: Going to register #{username}"
|
163
|
+
p = privmsg
|
164
|
+
return notify_cannot_register_in_public(p) if privmsg.to_channel?
|
165
|
+
return notify_username_taken(p) if username_exists?(username)
|
166
|
+
return notify_hostmask_taken(p) if hostmask_exists?(privmsg.from, privmsg.data.mask.host)
|
167
|
+
|
168
|
+
master = (usercount == 0 ? true : false)
|
169
|
+
register(username, password, privmsg.sender, privmsg.data.hostmask.hostname, (master ? 256 : @default_level.to_i))
|
170
|
+
|
171
|
+
@manager.logger.debug "AuthPlugin: Successfully registered #{username}"
|
172
|
+
privmsg.reply("You have been successfully registered.", false)
|
173
|
+
privmsg.reply("You have been added as a master!", false) if master
|
174
|
+
end
|
175
|
+
|
176
|
+
# Triggered when a user wants to add a new hostmask to themselves
|
177
|
+
def cmd_addmask(privmsg, username, password)
|
178
|
+
@manager.logger.debug "AuthPlugin: Adding a mask to #{username}"
|
179
|
+
p = privmsg
|
180
|
+
return notify_cannot_register_in_public(p) if privmsg.to_channel?
|
181
|
+
return notify_incorrect_information(p) if (!username_exists?(username) || !is_correct?(username, password))
|
182
|
+
return notify_hostmask_taken(p) if hostmask_exists?(privmsg.sender, privmsg.data.hostmask.hostname)
|
183
|
+
|
184
|
+
addmask(username, privmsg.from, privmsg.data.hostmask.hostname)
|
185
|
+
msg(privmsg, "Successfully added the new mask to your account")
|
186
|
+
end
|
187
|
+
|
188
|
+
# Triggers when an admin want to change the level of a user
|
189
|
+
def cmd_changelevel(privmsg, username, level)
|
190
|
+
return notify_user_not_found(privmsg) if !username_exists?(username)
|
191
|
+
|
192
|
+
pstore_transaction do |pstore|
|
193
|
+
pstore[:users][username.downcase][:level] = level.to_i
|
194
|
+
end
|
195
|
+
|
196
|
+
msg(privmsg, "Successfully changed access level of user")
|
197
|
+
end
|
198
|
+
|
199
|
+
# Triggers when an admin deletes a user
|
200
|
+
def cmd_remove(privmsg, username)
|
201
|
+
@manager.logger.debug "AuthPlugin: Going to remove #{username}"
|
202
|
+
return notify_user_not_found(privmsg) if !username_exists?(username)
|
203
|
+
|
204
|
+
pstore_transaction do |pstore|
|
205
|
+
pstore[:users].delete(username.downcase)
|
206
|
+
end
|
207
|
+
|
208
|
+
privmsg.reply("Successfully deleted user")
|
209
|
+
@manager.logger.debug "AuthPlugin: Removed #{username}"
|
210
|
+
end
|
211
|
+
|
212
|
+
# Triggered when a user wants to lookup another user
|
213
|
+
def cmd_lookup(privmsg, username)
|
214
|
+
@manager.logger.debug "AuthPlugin: Going to lookup #{username}"
|
215
|
+
if !username_exists?(username)
|
216
|
+
@manager.logger.debug "AuthPlugin: #{username} not found!"
|
217
|
+
return notify_user_not_found(privmsg)
|
218
|
+
end
|
219
|
+
|
220
|
+
level = 0
|
221
|
+
|
222
|
+
pstore_transaction do |pstore|
|
223
|
+
level = pstore[:users][username.downcase][:level]
|
224
|
+
end
|
225
|
+
|
226
|
+
msg(privmsg, "#{username} has a level of #{level.to_s}")
|
227
|
+
end
|
228
|
+
|
229
|
+
# Convenience function for errors
|
230
|
+
def notify_cannot_register_in_public(privmsg)
|
231
|
+
@manager.logger.debug("Can't register in public!")
|
232
|
+
privmsg.reply "You cannot perform this in public! Try with a privmsg"
|
233
|
+
end
|
234
|
+
|
235
|
+
def notify_username_taken(privmsg)
|
236
|
+
@manager.logger.debug("Username taken!")
|
237
|
+
privmsg.reply "That username is already taken"
|
238
|
+
end
|
239
|
+
|
240
|
+
def notify_hostmask_taken(privmsg)
|
241
|
+
@manager.logger.debug("Hostnake taken!")
|
242
|
+
privmsg.reply "That hostmask is already registered to a different user."
|
243
|
+
end
|
244
|
+
|
245
|
+
def notify_incorrect_information(privmsg)
|
246
|
+
@manager.logger.debug("Incorrect information!")
|
247
|
+
privmsg.reply "Username or password is incorrect."
|
248
|
+
end
|
249
|
+
|
250
|
+
def notify_user_not_found(privmsg)
|
251
|
+
@manager.logger.debug("User not found")
|
252
|
+
privmsg.reply "User not found"
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
data/plugins/control.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module Flexo
|
2
|
+
module Plugins
|
3
|
+
# Allows a user to control Flexo through commands
|
4
|
+
# on IRC. Requires Flexo::Plugins::AuthPlugin in order
|
5
|
+
# to maintain the user database and authenticate users,
|
6
|
+
# as well as for adding the restricted commands
|
7
|
+
class ControlPlugin < Flexo::Plugin
|
8
|
+
NAME = 'Control'
|
9
|
+
VERSION = '0.1.0'
|
10
|
+
SUMMARY = 'Control a Flexo instance through IRC'
|
11
|
+
DEPENDS = ['auth']
|
12
|
+
|
13
|
+
def initialize(*args)
|
14
|
+
super
|
15
|
+
|
16
|
+
@manager.logger.debug("ControlPlugin started")
|
17
|
+
add_trigger(/^ctrl quit$/, &method(:cmd_quit))
|
18
|
+
add_trigger(/^ctrl realquit$/, &method(:cmd_realquit))
|
19
|
+
add_trigger(/^ctrl join (#\S+)$/, &method(:cmd_join))
|
20
|
+
end
|
21
|
+
|
22
|
+
def cmd_join(privmsg, channel)
|
23
|
+
@manager.sender.join(channel)
|
24
|
+
end
|
25
|
+
|
26
|
+
def cmd_quit(privmsg)
|
27
|
+
@manager.logger.debug("Trying to quit")
|
28
|
+
privmsg.reply("No!")
|
29
|
+
end
|
30
|
+
|
31
|
+
def cmd_realquit(privmsg)
|
32
|
+
@manager.sender.quit("Leaving!")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/plugins/dbus.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
module Flexo
|
2
|
+
module Plugins
|
3
|
+
class DBusPlugin < Flexo::Plugin
|
4
|
+
NAME = "DBus"
|
5
|
+
VERSION = "0.1.0"
|
6
|
+
SUMMARY = "Adds the possibility for remote-controlling Flexo via DBus"
|
7
|
+
DEPENDS = ['ruby-dbus']
|
8
|
+
|
9
|
+
def initialize(*args)
|
10
|
+
super
|
11
|
+
|
12
|
+
@bus = DBus::SessionBus.instance
|
13
|
+
@service = @bus.request_service("org.northblue.Flexo.service")
|
14
|
+
@dbusflexo = Flexo::Plugins::DBusPlugin::Flexo.new(@manager,
|
15
|
+
"/org/northblue/Flexo/instance")
|
16
|
+
@service.export(@dbusflexo)
|
17
|
+
|
18
|
+
Thread.new do
|
19
|
+
main = DBus::Main.new
|
20
|
+
main << @bus
|
21
|
+
main.run
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class DBusPlugin::Flexo < ::DBus::Object
|
27
|
+
def initialize(path, manager)
|
28
|
+
super path
|
29
|
+
@manager = manager
|
30
|
+
end
|
31
|
+
|
32
|
+
dbus_interface "org.northblue.Flexo.interface" do
|
33
|
+
dbus_method :flexo_get_nick do
|
34
|
+
puts "My nick is #{@manager.nickname}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/plugins/pstore.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
module Flexo
|
2
|
+
module Plugins
|
3
|
+
class PStorePlugin < Flexo::Plugin
|
4
|
+
NAME = 'pstore'
|
5
|
+
VERSION = '0.1.0'
|
6
|
+
SUMMARY = 'A plugin that provides permanent storage for other plugins'
|
7
|
+
DEPENDS = ['ruby-pstore']
|
8
|
+
|
9
|
+
def initialize(*args)
|
10
|
+
super
|
11
|
+
@pstore = nil
|
12
|
+
@manager = Flexo::Manager.instance
|
13
|
+
end
|
14
|
+
|
15
|
+
# Opens a database.
|
16
|
+
def pstore_open(file)
|
17
|
+
begin
|
18
|
+
@pstore = PStore.new(file)
|
19
|
+
rescue
|
20
|
+
raise PluginError("PStore: Unable to open database")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Convenience function for utilizing the mutex, creating
|
25
|
+
# a transaction on our PStore-object, and running a code
|
26
|
+
# block against it.
|
27
|
+
def pstore_transaction(&block)
|
28
|
+
@manager.logger.debug "PStore: Starting transaction"
|
29
|
+
mutex { @pstore.transaction(&block) }
|
30
|
+
@manager.logger.debug "PStore: Transaction complete"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
begin
|
2
|
+
require 'svn/info'
|
3
|
+
rescue LoadError
|
4
|
+
raise PluginDependencyError, "Ruby-bindings for Subversion not found"
|
5
|
+
end
|
6
|
+
|
7
|
+
module Flexo
|
8
|
+
module Plugins
|
9
|
+
# SVN Lookup -- Registers a command to view commit
|
10
|
+
# messages for a subversion repository.
|
11
|
+
# Requires Flexo to be running at the same machine
|
12
|
+
# as the repositories is hosted, since, for now,
|
13
|
+
# this plugin requires read-access to the filesystem.
|
14
|
+
class SvnLookupPlugin < Flexo::Plugin
|
15
|
+
NAME = 'SvnLookup'
|
16
|
+
VERSION = '0.1.0'
|
17
|
+
SUMMARY = 'Replies to !svn <project> [<revision>] with information about the commit'
|
18
|
+
DEPENDS = []
|
19
|
+
|
20
|
+
def initialize(*args)
|
21
|
+
super
|
22
|
+
@manager.config['plugins.svn.repositories'] ||= '/var/svn/repositories'
|
23
|
+
@path = @manager.config['plugins.svn.repositories']
|
24
|
+
|
25
|
+
add_trigger(/^svn (\S+) (\d+)?/, &method(:cmd_lookup))
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def lookup(privmsg, project, revision = 'latest')
|
31
|
+
@manager.logger.debug "Requested information for #{project} (#{revision})"
|
32
|
+
begin
|
33
|
+
if revision == 'latest'
|
34
|
+
revision = Svn::Repos.open("#{@path}/#{project}").fs.youngest_rev
|
35
|
+
end
|
36
|
+
|
37
|
+
@info = Svn::Info.new("#{@path}/#{project}", revision)
|
38
|
+
rescue => e
|
39
|
+
raise PluginError, "#{e.message}\n#{e.backtrace}"
|
40
|
+
end
|
41
|
+
|
42
|
+
author = @info.author
|
43
|
+
files_added = @info.added_files.length
|
44
|
+
files_updated = @info.updated_files.length
|
45
|
+
files_deleted = @info.deleted_files.length
|
46
|
+
dirs_changed = @info.changed_dirs.length
|
47
|
+
|
48
|
+
str_changedirs = (dirs_changed < 5 ? "in #{@info.changed_dirs.join(', ')}" : 'in the repository')
|
49
|
+
str_changed = []
|
50
|
+
str_changed << "#{files_added.to_s} added" if files_added > 0
|
51
|
+
str_changed << "#{files_updated.to_s} updated" if files_updated > 0
|
52
|
+
str_changed << "#{files_deleted.to_s} deleted" if files_deleted > 0
|
53
|
+
str_changed = (str_changed.length > 0 ? changes.join(', ') : '')
|
54
|
+
str_changelog = @info.log
|
55
|
+
|
56
|
+
str_full = "[#{project}] #{str_changed} #{str_changedirs}."
|
57
|
+
str_full += " r#{revision} commited by #{author}: #{str_changelog}"
|
58
|
+
|
59
|
+
privmsg.reply str_full
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: PerfectlyNormal-Flexo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.9
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Per Christian B. Viken
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-10-14 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: perchr@northblue.org
|
18
|
+
executables:
|
19
|
+
- flexo
|
20
|
+
- mkflexorc
|
21
|
+
extensions: []
|
22
|
+
|
23
|
+
extra_rdoc_files: []
|
24
|
+
|
25
|
+
files:
|
26
|
+
- README
|
27
|
+
- Rakefile
|
28
|
+
- LICENSE
|
29
|
+
- flexo.gemspec
|
30
|
+
- lib/daemonize.rb
|
31
|
+
- lib/caselesshash.rb
|
32
|
+
- lib/flexo/server.rb
|
33
|
+
- lib/flexo/data.rb
|
34
|
+
- lib/flexo/errors.rb
|
35
|
+
- lib/flexo/plugin.rb
|
36
|
+
- lib/flexo/numerics.rb
|
37
|
+
- lib/flexo/handler.rb
|
38
|
+
- lib/flexo/client.rb
|
39
|
+
- lib/flexo/sender.rb
|
40
|
+
- lib/flexo/pluginmanager.rb
|
41
|
+
- lib/flexo/logger.rb
|
42
|
+
- lib/flexo/constants.rb
|
43
|
+
- lib/flexo/events/kick.rb
|
44
|
+
- lib/flexo/events/join.rb
|
45
|
+
- lib/flexo/events/privmsg.rb
|
46
|
+
- lib/flexo/events/unknown.rb
|
47
|
+
- lib/flexo/events/reply.rb
|
48
|
+
- lib/flexo/events/ping.rb
|
49
|
+
- lib/flexo/events/pong.rb
|
50
|
+
- lib/flexo/events/topic.rb
|
51
|
+
- lib/flexo/events/mode.rb
|
52
|
+
- lib/flexo/events/quit.rb
|
53
|
+
- lib/flexo/events/notice.rb
|
54
|
+
- lib/flexo/events/part.rb
|
55
|
+
- lib/flexo/events/nick.rb
|
56
|
+
- lib/flexo/config.rb
|
57
|
+
- lib/flexo/trigger.rb
|
58
|
+
- lib/flexo/manager.rb
|
59
|
+
- lib/flexo/dispatcher.rb
|
60
|
+
- lib/flexo/event.rb
|
61
|
+
- plugins/pstore.rb
|
62
|
+
- plugins/auth.rb
|
63
|
+
- plugins/control.rb
|
64
|
+
- plugins/dbus.rb
|
65
|
+
- plugins/svnlookup.rb
|
66
|
+
has_rdoc: true
|
67
|
+
homepage: http://eastblue.org/dev/flexo/
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: "0"
|
78
|
+
version:
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: "0"
|
84
|
+
version:
|
85
|
+
requirements: []
|
86
|
+
|
87
|
+
rubyforge_project:
|
88
|
+
rubygems_version: 1.2.0
|
89
|
+
signing_key:
|
90
|
+
specification_version: 2
|
91
|
+
summary: A simple, extensible IRC bot based on Flux
|
92
|
+
test_files: []
|
93
|
+
|