mojodna-switchboard 0.0.1
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/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
|