switchboard 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|