pkoch-switchboard 0.1.1a
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 +73 -0
- data/Rakefile +30 -0
- data/bin/switchboard +34 -0
- data/examples/echo_bot.rb +7 -0
- data/lib/switchboard.rb +1 -0
- data/lib/switchboard/client.rb +105 -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 +41 -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 +53 -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 +307 -0
- data/lib/switchboard/ext/delegate.rb +21 -0
- data/lib/switchboard/ext/instance_exec.rb +18 -0
- data/lib/switchboard/helpers/oauth_pubsub.rb +44 -0
- data/lib/switchboard/helpers/pubsub.rb +28 -0
- data/lib/switchboard/jacks.rb +8 -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 +16 -0
- metadata +147 -0
data/History.txt
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,73 @@
|
|
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
|
+
## Docs
|
7
|
+
|
8
|
+
* [switchboard : XMPP :: curl :
|
9
|
+
HTTP](http://mojodna.net/2009/07/16/switchboard-curl-for-xmpp.html) -
|
10
|
+
command-line usage
|
11
|
+
* [Switchboard as a
|
12
|
+
Framework](http://mojodna.net/2009/07/19/switchboard-as-a-framework.html)
|
13
|
+
* [Subscribing to Wordpress.com with
|
14
|
+
Switchboard](http://mojodna.net/2009/07/21/subscribing-to-wordpress-com-with-switchboard.html)
|
15
|
+
|
16
|
+
## Mailing List
|
17
|
+
|
18
|
+
[Switchboard Google Group](http://groups.google.com/group/switchboard)
|
19
|
+
|
20
|
+
## Getting Started
|
21
|
+
|
22
|
+
Install it:
|
23
|
+
|
24
|
+
$ sudo gem install switchboard
|
25
|
+
|
26
|
+
Install optional dependencies for additional functionality.
|
27
|
+
|
28
|
+
OAuth PubSub support:
|
29
|
+
|
30
|
+
$ sudo gem install oauth
|
31
|
+
|
32
|
+
User Location (XEP-0080) support via Fire Eagle:
|
33
|
+
|
34
|
+
$ sudo gem install mojodna-fire-hydrant -s http://gems.github.com
|
35
|
+
|
36
|
+
_Note: you will need to set up a PubSub subscription to your location via Fire
|
37
|
+
Eagle for this to work._
|
38
|
+
|
39
|
+
User Tune (XEP-0118) support via iTunes (on Mac OS X):
|
40
|
+
|
41
|
+
$ sudo gem install rb-appscript
|
42
|
+
|
43
|
+
Configure it:
|
44
|
+
|
45
|
+
$ switchboard config jid jid@example.com
|
46
|
+
$ switchboard config password pa55word
|
47
|
+
|
48
|
+
_Settings will be stored in `$HOME/.switchboardrc`_
|
49
|
+
|
50
|
+
Run it:
|
51
|
+
|
52
|
+
$ switchboard <command> <args>
|
53
|
+
$ switchboard roster list
|
54
|
+
$ switchboard roster add fireeagle.com
|
55
|
+
$ ...
|
56
|
+
|
57
|
+
Subscribe to a node using OAuth, overriding default settings:
|
58
|
+
|
59
|
+
$ switchboard --jid subscriber@example.com --password pa55word \
|
60
|
+
pubsub --oauth \
|
61
|
+
--oauth-consumer-key <consumer key> \
|
62
|
+
--oauth-consumer-secret <consumer secret> \
|
63
|
+
--oauth-token <token> \
|
64
|
+
--oauth-token-secret <token secret> \
|
65
|
+
--server fireeagle.com \
|
66
|
+
--node "/api/0.1/user/<token>" \
|
67
|
+
subscribe
|
68
|
+
|
69
|
+
Publish iTunes' current track using User Tune (XEP-0118):
|
70
|
+
|
71
|
+
$ switchboard --resource switchtunes pep tune
|
72
|
+
|
73
|
+
_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", "Paulo Köch"]
|
13
|
+
s.email = ["seth@mojodna.net", "paulo.koch@gmail.com"]
|
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,34 @@
|
|
1
|
+
#!/usr/bin/env ruby -rubygems
|
2
|
+
require 'switchboard'
|
3
|
+
require 'switchboard/commands'
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
ARGV.clone.options do |opts|
|
7
|
+
# opts.banner = "Usage: example.rb [options]"
|
8
|
+
|
9
|
+
command = Switchboard::Commands::Default
|
10
|
+
command.options(opts)
|
11
|
+
|
12
|
+
cmd = []
|
13
|
+
argv = []
|
14
|
+
|
15
|
+
# force optparse into being a command parser
|
16
|
+
opts.order! do |arg|
|
17
|
+
cmd << arg
|
18
|
+
|
19
|
+
if c = Switchboard::COMMANDS[cmd * "_"]
|
20
|
+
command = c
|
21
|
+
command.options(opts)
|
22
|
+
elsif c = Switchboard::COMMANDS["_" + cmd * "_"]
|
23
|
+
command = c
|
24
|
+
else
|
25
|
+
# unrecognized, unclaimed argument; keep as ARGV
|
26
|
+
argv << arg
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# correct ARGV to match unrecognized, unclaimed arguments
|
31
|
+
ARGV.reject! { |v| !argv.include?(v) }
|
32
|
+
|
33
|
+
command
|
34
|
+
end.run!
|
data/lib/switchboard.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'switchboard/switchboard'
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Switchboard
|
2
|
+
class Client < Core
|
3
|
+
attr_reader :client, :roster, :jid
|
4
|
+
|
5
|
+
def initialize(settings = Switchboard::Settings.new, spin = true)
|
6
|
+
super(settings, spin)
|
7
|
+
|
8
|
+
# create a new JID (one should always be provided, even if it's just the
|
9
|
+
# domain component)
|
10
|
+
@jid = Jabber::JID.new(settings["jid"])
|
11
|
+
|
12
|
+
# override the resource if one was provided
|
13
|
+
@jid.resource = settings["resource"] if settings["resource"]
|
14
|
+
|
15
|
+
# instantiate the client
|
16
|
+
@client = Jabber::Client.new(@jid)
|
17
|
+
|
18
|
+
on_stream_connected do
|
19
|
+
register_roster_callbacks
|
20
|
+
|
21
|
+
# tell others that we're online
|
22
|
+
presence
|
23
|
+
|
24
|
+
defer :roster_loaded do
|
25
|
+
# wait for the roster to load
|
26
|
+
roster.wait_for_roster
|
27
|
+
|
28
|
+
# roster has now been loaded
|
29
|
+
on(:roster_loaded)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
hook \
|
35
|
+
:roster_presence, :roster_query, :roster_subscription,
|
36
|
+
:roster_subscription_request, :roster_loaded, :roster_update
|
37
|
+
|
38
|
+
protected
|
39
|
+
|
40
|
+
def auth!
|
41
|
+
if (jid.node.nil? || settings["anonymous"]) && client.supports_anonymous?
|
42
|
+
# no node (user) component in the JID, be anonymous
|
43
|
+
|
44
|
+
client.auth_anonymous
|
45
|
+
else
|
46
|
+
client.auth(settings["password"])
|
47
|
+
end
|
48
|
+
@roster = Jabber::Roster::Helper.new(client)
|
49
|
+
rescue Jabber::ClientAuthenticationFailure => e
|
50
|
+
puts "Could not authenticate as #{settings["jid"]}"
|
51
|
+
shutdown(false)
|
52
|
+
exit 1
|
53
|
+
end
|
54
|
+
|
55
|
+
def connect!
|
56
|
+
client.connect
|
57
|
+
auth!
|
58
|
+
end
|
59
|
+
|
60
|
+
def disconnect!
|
61
|
+
presence(:unavailable)
|
62
|
+
super
|
63
|
+
end
|
64
|
+
|
65
|
+
def presence(status = nil, to = nil)
|
66
|
+
presence = Jabber::Presence.new(nil, status)
|
67
|
+
presence.to = to
|
68
|
+
client.send(presence)
|
69
|
+
end
|
70
|
+
|
71
|
+
def register_roster_callbacks
|
72
|
+
# presence from someone on my roster
|
73
|
+
roster.add_presence_callback do |item, old_presence, new_presence|
|
74
|
+
on(:roster_presence, item, old_presence, new_presence)
|
75
|
+
end
|
76
|
+
|
77
|
+
# roster query completed (rarely used)
|
78
|
+
roster.add_query_callback do |query|
|
79
|
+
on(:roster_query, query)
|
80
|
+
end
|
81
|
+
|
82
|
+
# roster subscription
|
83
|
+
roster.add_subscription_callback do |item, presence|
|
84
|
+
# confirmation that we were able to subscribe to someone else
|
85
|
+
on(:roster_subscription, item, presence)
|
86
|
+
end
|
87
|
+
|
88
|
+
roster.add_subscription_request_callback do |item, presence|
|
89
|
+
# someone wants to subscribe to me!
|
90
|
+
on(:roster_subscription_request, item, presence)
|
91
|
+
end
|
92
|
+
|
93
|
+
# roster was updated (rarely used)
|
94
|
+
roster.add_update_callback do |old_item, new_item|
|
95
|
+
# roster has been updated; don't care
|
96
|
+
# puts "update: #{old_item.inspect}, #{new_item.inspect}"
|
97
|
+
on(:roster_update, old_item, new_item)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def stream
|
102
|
+
client
|
103
|
+
end
|
104
|
+
end
|
105
|
+
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,41 @@
|
|
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("-a", "--anonymous", "Connect anonymously.") { OPTIONS["anonymous"] = true }
|
12
|
+
# opts.on("-d", "--daemon", "Make server run as a daemon.") { OPTIONS["detach"] = true }
|
13
|
+
opts.on("-j", "--jid=jid", String, "Specifies the JID to use.") { |v| OPTIONS["jid"] = v }
|
14
|
+
opts.on("-r", "--resource=resource", String, "Specifies the resource to use.") { |v| OPTIONS["resource"] = v }
|
15
|
+
opts.on("-p", "--password=password", String, "Specifies the password to use.") { |v| OPTIONS["password"] = v }
|
16
|
+
# opts.on("-p", "--pidfile=path", String,
|
17
|
+
# "Specifies a pidfile to use.") { |v| OPTIONS[:pidfile] = v }
|
18
|
+
# opts.on("-v", "--[no-]verbose", "Run verbosely") { |v| OPTIONS[:verbose] = v }
|
19
|
+
|
20
|
+
opts.separator ""
|
21
|
+
|
22
|
+
opts.on_tail("-h", "--help", "Show this help message.") { puts opts; exit }
|
23
|
+
opts.on_tail("--version", "Show version.") { puts "switchboard version #{Switchboard::VERSION * "."}"; exit }
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.run!
|
27
|
+
puts self.options(OptionParser.new).help
|
28
|
+
puts
|
29
|
+
puts "Available commands:"
|
30
|
+
Switchboard.commands.each do |name, command|
|
31
|
+
puts " #{command.to_command.ljust(15)}#{command.description}"
|
32
|
+
command.options(OptionParser.new).summarize do |line|
|
33
|
+
puts " " * 16 + line
|
34
|
+
end
|
35
|
+
end
|
36
|
+
puts
|
37
|
+
puts "See '#{@options.program_name} help COMMAND' for more information on a specific command."
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|