switchboard 0.1.0
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/History.txt +3 -0
- data/README.markdown +59 -0
- data/Rakefile +30 -0
- data/bin/switchboard +35 -0
- data/examples/echo_bot.rb +7 -0
- data/lib/switchboard.rb +1 -0
- data/lib/switchboard/client.rb +92 -0
- data/lib/switchboard/colors.rb +10 -0
- data/lib/switchboard/commands.rb +12 -0
- data/lib/switchboard/commands/command.rb +85 -0
- data/lib/switchboard/commands/config.rb +1 -0
- data/lib/switchboard/commands/config/config.rb +20 -0
- data/lib/switchboard/commands/default.rb +40 -0
- data/lib/switchboard/commands/disco.rb +3 -0
- data/lib/switchboard/commands/disco/disco.rb +13 -0
- data/lib/switchboard/commands/disco/info.rb +39 -0
- data/lib/switchboard/commands/disco/items.rb +30 -0
- data/lib/switchboard/commands/grep.rb +23 -0
- data/lib/switchboard/commands/help.rb +1 -0
- data/lib/switchboard/commands/help/help.rb +15 -0
- data/lib/switchboard/commands/last.rb +1 -0
- data/lib/switchboard/commands/last/last.rb +34 -0
- data/lib/switchboard/commands/pep.rb +3 -0
- data/lib/switchboard/commands/pep/location.rb +97 -0
- data/lib/switchboard/commands/pep/pep.rb +7 -0
- data/lib/switchboard/commands/pep/tune.rb +85 -0
- data/lib/switchboard/commands/pubsub.rb +16 -0
- data/lib/switchboard/commands/pubsub/affiliations.rb +34 -0
- data/lib/switchboard/commands/pubsub/config.rb +42 -0
- data/lib/switchboard/commands/pubsub/create.rb +41 -0
- data/lib/switchboard/commands/pubsub/delete.rb +32 -0
- data/lib/switchboard/commands/pubsub/info.rb +48 -0
- data/lib/switchboard/commands/pubsub/items.rb +40 -0
- data/lib/switchboard/commands/pubsub/listen.rb +20 -0
- data/lib/switchboard/commands/pubsub/nodes.rb +37 -0
- data/lib/switchboard/commands/pubsub/options.rb +40 -0
- data/lib/switchboard/commands/pubsub/publish.rb +44 -0
- data/lib/switchboard/commands/pubsub/pubsub.rb +25 -0
- data/lib/switchboard/commands/pubsub/purge.rb +32 -0
- data/lib/switchboard/commands/pubsub/retract.rb +38 -0
- data/lib/switchboard/commands/pubsub/subscribe.rb +34 -0
- data/lib/switchboard/commands/pubsub/subscriptions.rb +35 -0
- data/lib/switchboard/commands/pubsub/unsubscribe.rb +30 -0
- data/lib/switchboard/commands/register.rb +40 -0
- data/lib/switchboard/commands/roster.rb +5 -0
- data/lib/switchboard/commands/roster/add.rb +27 -0
- data/lib/switchboard/commands/roster/list.rb +26 -0
- data/lib/switchboard/commands/roster/online.rb +28 -0
- data/lib/switchboard/commands/roster/remove.rb +25 -0
- data/lib/switchboard/commands/roster/roster.rb +7 -0
- data/lib/switchboard/commands/unregister.rb +21 -0
- data/lib/switchboard/component.rb +36 -0
- data/lib/switchboard/core.rb +311 -0
- data/lib/switchboard/ext/delegate.rb +21 -0
- data/lib/switchboard/ext/instance_exec.rb +16 -0
- data/lib/switchboard/helpers/oauth_pubsub.rb +44 -0
- data/lib/switchboard/helpers/pubsub.rb +28 -0
- data/lib/switchboard/jacks.rb +7 -0
- data/lib/switchboard/jacks/auto_accept.rb +16 -0
- data/lib/switchboard/jacks/debug.rb +17 -0
- data/lib/switchboard/jacks/echo.rb +7 -0
- data/lib/switchboard/jacks/notify.rb +15 -0
- data/lib/switchboard/jacks/oauth_pubsub.rb +23 -0
- data/lib/switchboard/jacks/pubsub.rb +21 -0
- data/lib/switchboard/jacks/roster_debug.rb +23 -0
- data/lib/switchboard/settings.rb +49 -0
- data/lib/switchboard/switchboard.rb +12 -0
- data/lib/switchboard/version.rb +3 -0
- data/switchboard.gemspec +15 -0
- metadata +136 -0
data/History.txt
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,59 @@
|
|
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
|
+
$ sudo gem install mojodna-switchboard -s http://gems.github.com
|
11
|
+
|
12
|
+
Install optional dependencies for additional functionality.
|
13
|
+
|
14
|
+
OAuth PubSub support:
|
15
|
+
|
16
|
+
$ sudo gem install oauth
|
17
|
+
|
18
|
+
User Location (XEP-0080) support via Fire Eagle:
|
19
|
+
|
20
|
+
$ sudo gem install mojodna-fire-hydrant -s http://gems.github.com
|
21
|
+
|
22
|
+
_Note: you will need to set up a PubSub subscription to your location via Fire
|
23
|
+
Eagle for this to work._
|
24
|
+
|
25
|
+
User Tune (XEP-0118) support via iTunes (on Mac OS X):
|
26
|
+
|
27
|
+
$ sudo gem install rb-appscript
|
28
|
+
|
29
|
+
Configure it:
|
30
|
+
|
31
|
+
$ switchboard config jid jid@example.com
|
32
|
+
$ switchboard config password pa55word
|
33
|
+
|
34
|
+
_Settings will be stored in `$HOME/.switchboardrc`_
|
35
|
+
|
36
|
+
Run it:
|
37
|
+
|
38
|
+
$ switchboard <command> <args>
|
39
|
+
$ switchboard roster list
|
40
|
+
$ switchboard roster add fireeagle.com
|
41
|
+
$ ...
|
42
|
+
|
43
|
+
Subscribe to a node using OAuth, overriding default settings:
|
44
|
+
|
45
|
+
$ switchboard --jid subscriber@example.com --password pa55word \
|
46
|
+
pubsub --oauth \
|
47
|
+
--oauth-consumer-key <consumer key> \
|
48
|
+
--oauth-consumer-secret <consumer secret> \
|
49
|
+
--oauth-token <token> \
|
50
|
+
--oauth-token-secret <token secret> \
|
51
|
+
--server fireeagle.com \
|
52
|
+
--node "/api/0.1/user/<token>" \
|
53
|
+
subscribe
|
54
|
+
|
55
|
+
Publish iTunes' current track using User Tune (XEP-0118):
|
56
|
+
|
57
|
+
$ switchboard --resource switchtunes pep tune
|
58
|
+
|
59
|
+
_You can do this using a JID that is already online._
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require File.join("lib", "switchboard", "version")
|
2
|
+
|
3
|
+
desc "Generate the gemspec"
|
4
|
+
task :gemspec do
|
5
|
+
gemspec =<<-EOF
|
6
|
+
# this file is automatically generated
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = "switchboard"
|
9
|
+
s.version = "#{Switchboard::VERSION * "."}"
|
10
|
+
s.summary = "XMPP toolkit."
|
11
|
+
s.description = "A toolkit for assembling XMPP clients and interacting with XMPP servers."
|
12
|
+
s.authors = ["Seth Fitzsimmons"]
|
13
|
+
s.email = ["seth@mojodna.net"]
|
14
|
+
|
15
|
+
s.files = #{Dir.glob("**/*").select { |f| File.file?(f) }.inspect}
|
16
|
+
s.executables = ["switchboard"]
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.add_dependency("xmpp4r", ">=", "0.5")
|
20
|
+
end
|
21
|
+
EOF
|
22
|
+
|
23
|
+
open("switchboard.gemspec", "w") do |f|
|
24
|
+
f << gemspec
|
25
|
+
end
|
26
|
+
|
27
|
+
puts "gemspec successfully created."
|
28
|
+
end
|
29
|
+
|
30
|
+
task :default => :gemspec
|
data/bin/switchboard
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rubygems'
|
3
|
+
require 'switchboard'
|
4
|
+
require 'switchboard/commands'
|
5
|
+
require 'optparse'
|
6
|
+
|
7
|
+
ARGV.clone.options do |opts|
|
8
|
+
# opts.banner = "Usage: example.rb [options]"
|
9
|
+
|
10
|
+
command = Switchboard::Commands::Default
|
11
|
+
command.options(opts)
|
12
|
+
|
13
|
+
cmd = []
|
14
|
+
argv = []
|
15
|
+
|
16
|
+
# force optparse into being a command parser
|
17
|
+
opts.order! do |arg|
|
18
|
+
cmd << arg
|
19
|
+
|
20
|
+
if c = Switchboard::COMMANDS[cmd * "_"]
|
21
|
+
command = c
|
22
|
+
command.options(opts)
|
23
|
+
elsif c = Switchboard::COMMANDS["_" + cmd * "_"]
|
24
|
+
command = c
|
25
|
+
else
|
26
|
+
# unrecognized, unclaimed argument; keep as ARGV
|
27
|
+
argv << arg
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# correct ARGV to match unrecognized, unclaimed arguments
|
32
|
+
ARGV.reject! { |v| !argv.include?(v) }
|
33
|
+
|
34
|
+
command
|
35
|
+
end.run!
|
data/lib/switchboard.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'switchboard/switchboard'
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Switchboard
|
2
|
+
class Client < Core
|
3
|
+
attr_reader :client, :roster
|
4
|
+
|
5
|
+
def initialize(settings = Switchboard::Settings.new, spin = true)
|
6
|
+
super(settings, spin)
|
7
|
+
|
8
|
+
# TODO jid may already have a resource, so account for that
|
9
|
+
@client = Jabber::Client.new([settings["jid"], settings["resource"]] * "/")
|
10
|
+
|
11
|
+
on_stream_connected do
|
12
|
+
register_roster_callbacks
|
13
|
+
|
14
|
+
# tell others that we're online
|
15
|
+
presence
|
16
|
+
|
17
|
+
defer :roster_loaded do
|
18
|
+
# wait for the roster to load
|
19
|
+
roster.wait_for_roster
|
20
|
+
|
21
|
+
# roster has now been loaded
|
22
|
+
on(:roster_loaded)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
hook \
|
28
|
+
:roster_presence, :roster_query, :roster_subscription,
|
29
|
+
:roster_subscription_request, :roster_loaded, :roster_update
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def auth!
|
34
|
+
client.auth(settings["password"])
|
35
|
+
@roster = Jabber::Roster::Helper.new(client)
|
36
|
+
rescue Jabber::ClientAuthenticationFailure => e
|
37
|
+
puts "Could not authenticate as #{settings["jid"]}"
|
38
|
+
shutdown(false)
|
39
|
+
exit 1
|
40
|
+
end
|
41
|
+
|
42
|
+
def connect!
|
43
|
+
client.connect
|
44
|
+
auth!
|
45
|
+
end
|
46
|
+
|
47
|
+
def disconnect!
|
48
|
+
presence(:unavailable)
|
49
|
+
super
|
50
|
+
end
|
51
|
+
|
52
|
+
def presence(status = nil, to = nil)
|
53
|
+
presence = Jabber::Presence.new(nil, status)
|
54
|
+
presence.to = to
|
55
|
+
client.send(presence)
|
56
|
+
end
|
57
|
+
|
58
|
+
def register_roster_callbacks
|
59
|
+
# presence from someone on my roster
|
60
|
+
roster.add_presence_callback do |item, old_presence, new_presence|
|
61
|
+
on(:roster_presence, item, old_presence, new_presence)
|
62
|
+
end
|
63
|
+
|
64
|
+
# roster query completed (rarely used)
|
65
|
+
roster.add_query_callback do |query|
|
66
|
+
on(:roster_query, query)
|
67
|
+
end
|
68
|
+
|
69
|
+
# roster subscription
|
70
|
+
roster.add_subscription_callback do |item, presence|
|
71
|
+
# confirmation that we were able to subscribe to someone else
|
72
|
+
on(:roster_subscription, item, presence)
|
73
|
+
end
|
74
|
+
|
75
|
+
roster.add_subscription_request_callback do |item, presence|
|
76
|
+
# someone wants to subscribe to me!
|
77
|
+
on(:roster_subscription_request, item, presence)
|
78
|
+
end
|
79
|
+
|
80
|
+
# roster was updated (rarely used)
|
81
|
+
roster.add_update_callback do |old_item, new_item|
|
82
|
+
# roster has been updated; don't care
|
83
|
+
# puts "update: #{old_item.inspect}, #{new_item.inspect}"
|
84
|
+
on(:roster_update, old_item, new_item)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def stream
|
89
|
+
client
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -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,12 @@
|
|
1
|
+
require 'switchboard/commands/command'
|
2
|
+
require 'switchboard/commands/config'
|
3
|
+
require 'switchboard/commands/default'
|
4
|
+
require 'switchboard/commands/disco'
|
5
|
+
require 'switchboard/commands/grep'
|
6
|
+
require 'switchboard/commands/help'
|
7
|
+
require 'switchboard/commands/last'
|
8
|
+
require 'switchboard/commands/pep'
|
9
|
+
require 'switchboard/commands/pubsub'
|
10
|
+
require 'switchboard/commands/register'
|
11
|
+
require 'switchboard/commands/roster'
|
12
|
+
require 'switchboard/commands/unregister'
|
@@ -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 @@
|
|
1
|
+
require 'switchboard/commands/config/config'
|
@@ -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,40 @@
|
|
1
|
+
module Switchboard
|
2
|
+
module Commands
|
3
|
+
class Default < Switchboard::Command
|
4
|
+
unregister!
|
5
|
+
|
6
|
+
def self.options(opts)
|
7
|
+
super(opts)
|
8
|
+
|
9
|
+
opts.banner = "Usage: #{opts.program_name} [options] COMMAND [options] [args]"
|
10
|
+
|
11
|
+
# opts.on("-d", "--daemon", "Make server run as a daemon.") { OPTIONS[:detach] = true }
|
12
|
+
opts.on("-j", "--jid=jid", String, "Specifies the JID to use.") { |v| OPTIONS["jid"] = v }
|
13
|
+
opts.on("-r", "--resource=resource", String, "Specifies the resource to use.") { |v| OPTIONS["resource"] = v }
|
14
|
+
opts.on("-p", "--password=password", String, "Specifies the password to use.") { |v| OPTIONS["password"] = v }
|
15
|
+
# opts.on("-p", "--pidfile=path", String,
|
16
|
+
# "Specifies a pidfile to use.") { |v| OPTIONS[:pidfile] = v }
|
17
|
+
# opts.on("-v", "--[no-]verbose", "Run verbosely") { |v| OPTIONS[:verbose] = v }
|
18
|
+
|
19
|
+
opts.separator ""
|
20
|
+
|
21
|
+
opts.on_tail("-h", "--help", "Show this help message.") { puts opts; exit }
|
22
|
+
opts.on_tail("--version", "Show version") { puts "switchboard version #{Switchboard::VERSION * "."}"; exit }
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.run!
|
26
|
+
puts self.options(OptionParser.new).help
|
27
|
+
puts
|
28
|
+
puts "Available commands:"
|
29
|
+
Switchboard.commands.each do |name, command|
|
30
|
+
puts " #{command.to_command.ljust(15)}#{command.description}"
|
31
|
+
command.options(OptionParser.new).summarize do |line|
|
32
|
+
puts " " * 16 + line
|
33
|
+
end
|
34
|
+
end
|
35
|
+
puts
|
36
|
+
puts "See '#{@options.program_name} help COMMAND' for more information on a specific command."
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Switchboard
|
2
|
+
module Commands
|
3
|
+
class Disco < Switchboard::Command
|
4
|
+
description "Service discovery"
|
5
|
+
|
6
|
+
def self.options(opts)
|
7
|
+
super(opts)
|
8
|
+
opts.on("--node=node", String, "Specifies the node to query.") { |v| OPTIONS["disco.node"] = v }
|
9
|
+
opts.on("--target=target", String, "Specifies the target to query.") { |v| OPTIONS["disco.target"] = v }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'xmpp4r/discovery'
|
2
|
+
|
3
|
+
module Switchboard
|
4
|
+
module Commands
|
5
|
+
class Disco
|
6
|
+
class Info < Switchboard::Command
|
7
|
+
description "Basic service discovery"
|
8
|
+
|
9
|
+
def self.run!
|
10
|
+
switchboard = Switchboard::Client.new do
|
11
|
+
helper = Jabber::Discovery::Helper.new(client)
|
12
|
+
resp = helper.get_info_for(settings["disco.target"], settings["disco.node"])
|
13
|
+
|
14
|
+
if resp.identities.any? || resp.features.any?
|
15
|
+
puts "Discovery Info for #{settings["disco.target"]}#{settings["disco.node"] ? " (#{settings["disco.node"]})" : ""}"
|
16
|
+
|
17
|
+
if resp.identities.any?
|
18
|
+
puts "Identities:"
|
19
|
+
resp.identities.each do |identity|
|
20
|
+
puts " #{identity.category}/#{identity.type}: #{identity.iname ? identity.iname : "n/a"}"
|
21
|
+
end
|
22
|
+
puts
|
23
|
+
end
|
24
|
+
|
25
|
+
puts "Features:" if resp.features.any?
|
26
|
+
resp.features.each do |feature|
|
27
|
+
puts " #{feature}"
|
28
|
+
end
|
29
|
+
else
|
30
|
+
puts "No information was discoverable for #{settings["disco.target"]}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
switchboard.run!
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|