mojodna-switchboard 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +31 -0
- data/bin/switchboard +35 -0
- data/examples/election_results.rb +61 -0
- data/lib/switchboard/colors.rb +10 -0
- data/lib/switchboard/commands/command.rb +85 -0
- data/lib/switchboard/commands/config/config.rb +20 -0
- data/lib/switchboard/commands/config.rb +1 -0
- data/lib/switchboard/commands/default.rb +47 -0
- data/lib/switchboard/commands/help/help.rb +15 -0
- data/lib/switchboard/commands/help.rb +1 -0
- data/lib/switchboard/commands/pubsub/pubsub.rb +16 -0
- data/lib/switchboard/commands/pubsub/subscribe.rb +35 -0
- data/lib/switchboard/commands/pubsub/subscriptions.rb +32 -0
- data/lib/switchboard/commands/pubsub/unsubscribe.rb +31 -0
- data/lib/switchboard/commands/pubsub.rb +4 -0
- data/lib/switchboard/commands/roster/add.rb +26 -0
- data/lib/switchboard/commands/roster/list.rb +24 -0
- data/lib/switchboard/commands/roster/remove.rb +29 -0
- data/lib/switchboard/commands/roster/roster.rb +7 -0
- data/lib/switchboard/commands/roster.rb +4 -0
- data/lib/switchboard/commands.rb +6 -0
- data/lib/switchboard/core.rb +324 -0
- data/lib/switchboard/instance_exec.rb +16 -0
- data/lib/switchboard/jacks/auto_accept.rb +16 -0
- data/lib/switchboard/jacks/debug.rb +17 -0
- data/lib/switchboard/jacks/notify.rb +15 -0
- data/lib/switchboard/jacks/oauth_pubsub.rb +50 -0
- data/lib/switchboard/jacks/roster_debug.rb +23 -0
- data/lib/switchboard/jacks.rb +5 -0
- data/lib/switchboard/oauth/request_proxy/mock_request.rb +21 -0
- data/lib/switchboard/settings.rb +32 -0
- data/lib/switchboard/switchboard.rb +5 -0
- data/lib/switchboard/version.rb +3 -0
- data/lib/switchboard/xmpp4r/pubsub/helper/oauth_service_helper.rb +107 -0
- data/lib/switchboard.rb +1 -0
- data/switchboard.gemspec +14 -0
- metadata +113 -0
data/README.markdown
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Switchboard
|
2
|
+
|
3
|
+
Switchboard is both a toolkit for assembling XMPP clients as well as a set of
|
4
|
+
command-line tools for interacting with XMPP servers.
|
5
|
+
|
6
|
+
## Getting Started
|
7
|
+
|
8
|
+
Install it:
|
9
|
+
|
10
|
+
$ gem install mojodna-switchboard -s http://gems.github.com
|
11
|
+
|
12
|
+
Configure it:
|
13
|
+
|
14
|
+
$ switchboard config jid jid@example.com
|
15
|
+
$ switchboard config password pa55word
|
16
|
+
$ switchboard config oauth.consumer_key asdf
|
17
|
+
$ switchboard config oauth.consumer_secret qwerty
|
18
|
+
$ switchboard config oauth.token asdf
|
19
|
+
$ switchboard config oauth.token_secret qwerty
|
20
|
+
$ switchboard config oauth.general_token asdf
|
21
|
+
$ switchboard config oauth.general_token_secret qwerty
|
22
|
+
$ switchboard config pubsub.server fireeagle.com
|
23
|
+
|
24
|
+
_Settings will be stored in `$HOME/.switchboardrc`_
|
25
|
+
|
26
|
+
Run it:
|
27
|
+
|
28
|
+
$ switchboard <command> <args>
|
29
|
+
$ switchboard roster list
|
30
|
+
$ switchboard roster add fireeagle.com
|
31
|
+
$ ...
|
data/bin/switchboard
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'switchboard'
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
command = ARGV.clone.options do |opts|
|
6
|
+
# opts.banner = "Usage: example.rb [options]"
|
7
|
+
|
8
|
+
command = Switchboard::Commands::Default
|
9
|
+
command.options(opts)
|
10
|
+
|
11
|
+
cmd = []
|
12
|
+
argv = []
|
13
|
+
|
14
|
+
# force optparse into being a command parser
|
15
|
+
opts.order! do |arg|
|
16
|
+
cmd << arg
|
17
|
+
|
18
|
+
if c = Switchboard::COMMANDS[cmd * "_"]
|
19
|
+
command = c
|
20
|
+
command.options(opts)
|
21
|
+
elsif c = Switchboard::COMMANDS["_" + cmd * "_"]
|
22
|
+
command = c
|
23
|
+
else
|
24
|
+
# unrecognized, unclaimed argument; keep as ARGV
|
25
|
+
argv << arg
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# correct ARGV to match unrecognized, unclaimed arguments
|
30
|
+
ARGV.reject! { |v| !argv.include?(v) }
|
31
|
+
|
32
|
+
command
|
33
|
+
end
|
34
|
+
|
35
|
+
command.run!
|
@@ -0,0 +1,61 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'fire_hydrant'
|
3
|
+
require 'rexml/document'
|
4
|
+
require 'open-uri'
|
5
|
+
|
6
|
+
hydrant = FireHydrant.new(YAML.load(File.read("fire_hydrant.yml"))) do
|
7
|
+
old_votes = load_results
|
8
|
+
|
9
|
+
while(!shutdown?) do
|
10
|
+
# load the results feed
|
11
|
+
votes = load_results
|
12
|
+
diff = nil
|
13
|
+
|
14
|
+
# loop through and compare it to the old results
|
15
|
+
votes.each do |state, cand|
|
16
|
+
cand.each do |c, val|
|
17
|
+
unless old_votes[state][c] == val
|
18
|
+
diff ||= "Electoral results changed"
|
19
|
+
diff << "\n#{state} - #{c}: #{old_votes[state][c].inspect} => #{val.inspect}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
if diff
|
25
|
+
puts diff
|
26
|
+
broadcast(diff)
|
27
|
+
end
|
28
|
+
|
29
|
+
# set this as old_votes
|
30
|
+
old_votes = votes
|
31
|
+
|
32
|
+
# wait 5 minutes
|
33
|
+
puts "-----"
|
34
|
+
sleep 300
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
hydrant.jack!(AutoAcceptJack, NotifyJack)
|
39
|
+
|
40
|
+
def hydrant.load_results(url = "http://d.yimg.com/b/data/us/news/xml/elections/2008a/pres.xml")
|
41
|
+
doc = REXML::Document.new(open(url))
|
42
|
+
nodes = doc.elements.collect("//state") { |node| [node.attributes["name"].downcase, node.elements.collect("cand") { |cand| [cand.attributes["name"].downcase, cand.attributes["PopPct"].to_f, cand.attributes["PopVote"].to_i] }] }
|
43
|
+
|
44
|
+
votes = {}
|
45
|
+
nodes.each do |state, results|
|
46
|
+
votes[state] = {}
|
47
|
+
results.each do |cand, pct, vote|
|
48
|
+
votes[state][cand] = [pct, vote]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
votes
|
53
|
+
end
|
54
|
+
|
55
|
+
def hydrant.broadcast(msg)
|
56
|
+
roster.items.each do |jid, item|
|
57
|
+
client.send(Jabber::Message.new(jid, msg))
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
hydrant.run!
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class String
|
2
|
+
def red; colorize(self, "\e[1m\e[31m"); end
|
3
|
+
def green; colorize(self, "\e[1m\e[32m"); end
|
4
|
+
def dark_green; colorize(self, "\e[32m"); end
|
5
|
+
def yellow; colorize(self, "\e[1m\e[33m"); end
|
6
|
+
def blue; colorize(self, "\e[1m\e[34m"); end
|
7
|
+
def dark_blue; colorize(self, "\e[34m"); end
|
8
|
+
def purple; colorize(self, "\e[1m\e[35m"); end
|
9
|
+
def colorize(text, color_code) "#{color_code}#{text}\e[0m" end
|
10
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Switchboard
|
2
|
+
COMMANDS = {}
|
3
|
+
|
4
|
+
def self.commands(command = nil)
|
5
|
+
if command
|
6
|
+
COMMANDS.select { |k,v| k =~ /^#{command.to_command_name("_")}_/ }
|
7
|
+
else
|
8
|
+
COMMANDS.reject { |k,v| k =~ /_/ }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.hide_command(command)
|
13
|
+
COMMANDS["_#{command.to_command_name("_")}"] = unregister_command(command)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.register_command(command)
|
17
|
+
COMMANDS[command.to_command_name("_")] = command
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.unregister_command(command)
|
21
|
+
COMMANDS.delete(command.to_command_name("_"))
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
module Commands
|
26
|
+
end
|
27
|
+
|
28
|
+
class Command
|
29
|
+
def self.description(description = nil)
|
30
|
+
@description = description if description
|
31
|
+
@description
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.help
|
35
|
+
"No help is available for this command (#{self.to_command_name})."
|
36
|
+
end
|
37
|
+
|
38
|
+
# TODO consider accepting a block in subclasses
|
39
|
+
def self.options(opts)
|
40
|
+
@options = opts
|
41
|
+
@options.banner = "Usage: #{opts.program_name} [options] #{self.to_command_name} [options] [args]"
|
42
|
+
@options
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.run!
|
46
|
+
puts self.options(OptionParser.new).help
|
47
|
+
commands = Switchboard.commands(self)
|
48
|
+
|
49
|
+
if commands.any?
|
50
|
+
puts
|
51
|
+
puts "Available commands:"
|
52
|
+
commands.each do |name, command|
|
53
|
+
puts " #{command.to_command.ljust(15)}#{command.description}"
|
54
|
+
command.options(OptionParser.new).summarize do |line|
|
55
|
+
puts " " * 16 + line
|
56
|
+
end
|
57
|
+
end
|
58
|
+
puts
|
59
|
+
puts "See '#{@options.program_name} help COMMAND' for more information on a specific command."
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.to_command
|
64
|
+
self.name.gsub("Switchboard::Commands::", "").split("::").last.downcase
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.to_command_name(delimiter = " ")
|
68
|
+
self.name.gsub("Switchboard::Commands::", "").split("::").map { |c| c.downcase } * delimiter
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def self.hide!
|
74
|
+
Switchboard.hide_command(self)
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.inherited(klass)
|
78
|
+
Switchboard.register_command(klass) if klass.name =~ /^Switchboard::Commands/
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.unregister!
|
82
|
+
Switchboard.unregister_command(self)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Switchboard
|
2
|
+
module Commands
|
3
|
+
class Config < Switchboard::Command
|
4
|
+
description "Get and set global options"
|
5
|
+
|
6
|
+
def self.run!
|
7
|
+
settings = Switchboard::Settings.new
|
8
|
+
if ARGV.empty?
|
9
|
+
super
|
10
|
+
elsif ARGV.length == 1
|
11
|
+
puts settings.get(ARGV.shift)
|
12
|
+
elsif ARGV.length == 2
|
13
|
+
settings.set!(ARGV.shift, ARGV.shift)
|
14
|
+
else
|
15
|
+
puts "error: More than one value for the key #{ARGV.shift}: #{ARGV * " "}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'switchboard/commands/config/config'
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Switchboard
|
2
|
+
class Command
|
3
|
+
OPTIONS = {}
|
4
|
+
DEFAULT_OPTIONS = {
|
5
|
+
# :detach => false,
|
6
|
+
"debug" => false,
|
7
|
+
"oauth" => false,
|
8
|
+
"resource" => "switchboard"
|
9
|
+
}
|
10
|
+
end
|
11
|
+
module Commands
|
12
|
+
class Default < Switchboard::Command
|
13
|
+
unregister!
|
14
|
+
|
15
|
+
def self.options(opts)
|
16
|
+
super(opts)
|
17
|
+
|
18
|
+
opts.banner = "Usage: #{opts.program_name} [options] COMMAND [options] [args]"
|
19
|
+
|
20
|
+
# opts.on("-d", "--daemon", "Make server run as a daemon.") { OPTIONS[:detach] = true }
|
21
|
+
# opts.on("-l", "--log=path", String, "Specifies a path to log script output.") { |v| OPTIONS[:log] = v }
|
22
|
+
# opts.on("-p", "--pidfile=path", String,
|
23
|
+
# "Specifies a pidfile to use.") { |v| OPTIONS[:pidfile] = v }
|
24
|
+
# opts.on("-v", "--[no-]verbose", "Run verbosely") { |v| OPTIONS[:verbose] = v }
|
25
|
+
|
26
|
+
opts.separator ""
|
27
|
+
|
28
|
+
opts.on_tail("-h", "--help", "Show this help message.") { puts opts; exit }
|
29
|
+
opts.on_tail("--version", "Show version") { puts "switchboard version #{Switchboard::VERSION * "."}"; exit }
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.run!
|
33
|
+
puts self.options(OptionParser.new).help
|
34
|
+
puts
|
35
|
+
puts "Available commands:"
|
36
|
+
Switchboard.commands.each do |name, command|
|
37
|
+
puts " #{command.to_command.ljust(15)}#{command.description}"
|
38
|
+
command.options(OptionParser.new).summarize do |line|
|
39
|
+
puts " " * 16 + line
|
40
|
+
end
|
41
|
+
end
|
42
|
+
puts
|
43
|
+
puts "See '#{@options.program_name} help COMMAND' for more information on a specific command."
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'switchboard/commands/help/help'
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Switchboard
|
2
|
+
module Commands
|
3
|
+
class PubSub < Switchboard::Command
|
4
|
+
description "Pubsub node manipulation"
|
5
|
+
|
6
|
+
def self.help
|
7
|
+
"These are the more extensive instructions for using the pubsub command."
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.options(opts)
|
11
|
+
super(opts)
|
12
|
+
# opts.on("--oauth", "Sign requests using OAuth.") { OPTIONS[:oauth] = true }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Switchboard
|
2
|
+
module Commands
|
3
|
+
class PubSub
|
4
|
+
class Subscribe < Switchboard::Command
|
5
|
+
description "Subscribe to a pubsub node"
|
6
|
+
|
7
|
+
def self.run!
|
8
|
+
# TODO override settings with values from the command line
|
9
|
+
switchboard = Switchboard::Core.new do
|
10
|
+
# this executes in the main loop, so it doesn't really matter that this runs in a different thread
|
11
|
+
defer :subscribed do
|
12
|
+
begin
|
13
|
+
pubsub.subscribe_to("/api/0.1/user/#{settings["oauth.token"]}", oauth_consumer, oauth_token)
|
14
|
+
rescue Jabber::ServerError => e
|
15
|
+
puts e
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# define here or as hydrant.subscriptions_received
|
20
|
+
def subscribed(subscription)
|
21
|
+
if subscription.subscription == :subscribed
|
22
|
+
puts "Subscribe successful."
|
23
|
+
else
|
24
|
+
puts "Subscribe failed!".red
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
switchboard.plug!(OAuthPubSubJack)
|
30
|
+
switchboard.run!
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Switchboard
|
2
|
+
module Commands
|
3
|
+
class PubSub
|
4
|
+
class Subscriptions < Switchboard::Command
|
5
|
+
description "List pubsub subscriptions"
|
6
|
+
|
7
|
+
def self.run!
|
8
|
+
# TODO override settings with values from the command line
|
9
|
+
switchboard = Switchboard::Core.new do
|
10
|
+
# this executes in the main loop, so it doesn't really matter that this runs in a different thread
|
11
|
+
defer :subscriptions_received do
|
12
|
+
begin
|
13
|
+
pubsub.get_subscriptions_from_all_nodes(oauth_consumer, general_token)
|
14
|
+
rescue Jabber::ServerError => e
|
15
|
+
puts e
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# define here or as hydrant.subscriptions_received
|
20
|
+
def subscriptions_received(subscriptions)
|
21
|
+
puts "Subscriptions:"
|
22
|
+
puts subscriptions.collect { |subscription| "#{subscription.jid} => #{subscription.node}" } * "\n"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
switchboard.plug!(OAuthPubSubJack)
|
27
|
+
switchboard.run!
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Switchboard
|
2
|
+
module Commands
|
3
|
+
class PubSub
|
4
|
+
class Unsubscribe < Switchboard::Command
|
5
|
+
description "Unsubscribe from a pubsub node"
|
6
|
+
|
7
|
+
def self.run!
|
8
|
+
# TODO override settings with values from the command line
|
9
|
+
switchboard = Switchboard::Core.new do
|
10
|
+
# this executes in the main loop, so it doesn't really matter that this runs in a different thread
|
11
|
+
defer :unsubscribed do
|
12
|
+
begin
|
13
|
+
pubsub.unsubscribe_from("/api/0.1/user/#{settings["oauth.token"]}", oauth_consumer, oauth_token)
|
14
|
+
rescue Jabber::ServerError => e
|
15
|
+
puts e
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# define here or as hydrant.subscriptions_received
|
20
|
+
def unsubscribed(successful)
|
21
|
+
puts "Unsubscribe was successful." if successful
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
switchboard.plug!(OAuthPubSubJack)
|
26
|
+
switchboard.run!
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Switchboard
|
2
|
+
module Commands
|
3
|
+
class Roster
|
4
|
+
class Add < Switchboard::Command
|
5
|
+
description "Add a JID to your roster"
|
6
|
+
|
7
|
+
def self.run!
|
8
|
+
# TODO override settings with values from the command line
|
9
|
+
switchboard = Switchboard::Core.new do
|
10
|
+
# add the server as a contact if it wasn't already added
|
11
|
+
ARGV.each do |jid|
|
12
|
+
if roster.find(jid).empty?
|
13
|
+
puts "Adding #{jid} to my roster..."
|
14
|
+
roster.add(jid, nil, true)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
switchboard.plug!(AutoAcceptJack)
|
20
|
+
|
21
|
+
switchboard.run!
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Switchboard
|
2
|
+
module Commands
|
3
|
+
class Roster
|
4
|
+
class List < Switchboard::Command
|
5
|
+
description "List all roster items"
|
6
|
+
|
7
|
+
def self.run!
|
8
|
+
# TODO override settings with values from the command line
|
9
|
+
switchboard = Switchboard::Core.new do
|
10
|
+
if roster.items.any?
|
11
|
+
puts "#{settings["jid"]}'s roster:"
|
12
|
+
puts roster.items.keys.map { |jid| jid.to_s } * "\n"
|
13
|
+
else
|
14
|
+
puts "#{settings["jid"]}'s roster is empty."
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
switchboard.plug!(AutoAcceptJack)
|
19
|
+
switchboard.run!
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Switchboard
|
2
|
+
module Commands
|
3
|
+
class Roster
|
4
|
+
class Remove < Switchboard::Command
|
5
|
+
description "Remove a JID from your roster"
|
6
|
+
|
7
|
+
def self.options(opts)
|
8
|
+
super(opts)
|
9
|
+
# opts.on("-l", "--log=path", String, "Specifies a path to log script output.") { |v| OPTIONS[:log] = v }
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.run!
|
13
|
+
# TODO override settings with values from the command line
|
14
|
+
switchboard = Switchboard::Core.new do
|
15
|
+
ARGV.each do |jid|
|
16
|
+
if (items = roster.find(jid)).any?
|
17
|
+
item = items.values.first
|
18
|
+
puts "Removing #{item.jid.to_s} from my roster..."
|
19
|
+
item.remove
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
switchboard.run!
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|