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.
Files changed (70) hide show
  1. data/History.txt +3 -0
  2. data/README.markdown +59 -0
  3. data/Rakefile +30 -0
  4. data/bin/switchboard +35 -0
  5. data/examples/echo_bot.rb +7 -0
  6. data/lib/switchboard.rb +1 -0
  7. data/lib/switchboard/client.rb +92 -0
  8. data/lib/switchboard/colors.rb +10 -0
  9. data/lib/switchboard/commands.rb +12 -0
  10. data/lib/switchboard/commands/command.rb +85 -0
  11. data/lib/switchboard/commands/config.rb +1 -0
  12. data/lib/switchboard/commands/config/config.rb +20 -0
  13. data/lib/switchboard/commands/default.rb +40 -0
  14. data/lib/switchboard/commands/disco.rb +3 -0
  15. data/lib/switchboard/commands/disco/disco.rb +13 -0
  16. data/lib/switchboard/commands/disco/info.rb +39 -0
  17. data/lib/switchboard/commands/disco/items.rb +30 -0
  18. data/lib/switchboard/commands/grep.rb +23 -0
  19. data/lib/switchboard/commands/help.rb +1 -0
  20. data/lib/switchboard/commands/help/help.rb +15 -0
  21. data/lib/switchboard/commands/last.rb +1 -0
  22. data/lib/switchboard/commands/last/last.rb +34 -0
  23. data/lib/switchboard/commands/pep.rb +3 -0
  24. data/lib/switchboard/commands/pep/location.rb +97 -0
  25. data/lib/switchboard/commands/pep/pep.rb +7 -0
  26. data/lib/switchboard/commands/pep/tune.rb +85 -0
  27. data/lib/switchboard/commands/pubsub.rb +16 -0
  28. data/lib/switchboard/commands/pubsub/affiliations.rb +34 -0
  29. data/lib/switchboard/commands/pubsub/config.rb +42 -0
  30. data/lib/switchboard/commands/pubsub/create.rb +41 -0
  31. data/lib/switchboard/commands/pubsub/delete.rb +32 -0
  32. data/lib/switchboard/commands/pubsub/info.rb +48 -0
  33. data/lib/switchboard/commands/pubsub/items.rb +40 -0
  34. data/lib/switchboard/commands/pubsub/listen.rb +20 -0
  35. data/lib/switchboard/commands/pubsub/nodes.rb +37 -0
  36. data/lib/switchboard/commands/pubsub/options.rb +40 -0
  37. data/lib/switchboard/commands/pubsub/publish.rb +44 -0
  38. data/lib/switchboard/commands/pubsub/pubsub.rb +25 -0
  39. data/lib/switchboard/commands/pubsub/purge.rb +32 -0
  40. data/lib/switchboard/commands/pubsub/retract.rb +38 -0
  41. data/lib/switchboard/commands/pubsub/subscribe.rb +34 -0
  42. data/lib/switchboard/commands/pubsub/subscriptions.rb +35 -0
  43. data/lib/switchboard/commands/pubsub/unsubscribe.rb +30 -0
  44. data/lib/switchboard/commands/register.rb +40 -0
  45. data/lib/switchboard/commands/roster.rb +5 -0
  46. data/lib/switchboard/commands/roster/add.rb +27 -0
  47. data/lib/switchboard/commands/roster/list.rb +26 -0
  48. data/lib/switchboard/commands/roster/online.rb +28 -0
  49. data/lib/switchboard/commands/roster/remove.rb +25 -0
  50. data/lib/switchboard/commands/roster/roster.rb +7 -0
  51. data/lib/switchboard/commands/unregister.rb +21 -0
  52. data/lib/switchboard/component.rb +36 -0
  53. data/lib/switchboard/core.rb +311 -0
  54. data/lib/switchboard/ext/delegate.rb +21 -0
  55. data/lib/switchboard/ext/instance_exec.rb +16 -0
  56. data/lib/switchboard/helpers/oauth_pubsub.rb +44 -0
  57. data/lib/switchboard/helpers/pubsub.rb +28 -0
  58. data/lib/switchboard/jacks.rb +7 -0
  59. data/lib/switchboard/jacks/auto_accept.rb +16 -0
  60. data/lib/switchboard/jacks/debug.rb +17 -0
  61. data/lib/switchboard/jacks/echo.rb +7 -0
  62. data/lib/switchboard/jacks/notify.rb +15 -0
  63. data/lib/switchboard/jacks/oauth_pubsub.rb +23 -0
  64. data/lib/switchboard/jacks/pubsub.rb +21 -0
  65. data/lib/switchboard/jacks/roster_debug.rb +23 -0
  66. data/lib/switchboard/settings.rb +49 -0
  67. data/lib/switchboard/switchboard.rb +12 -0
  68. data/lib/switchboard/version.rb +3 -0
  69. data/switchboard.gemspec +15 -0
  70. metadata +136 -0
@@ -0,0 +1,3 @@
1
+ == 0.1.0 2009-07-08
2
+
3
+ * Initial RubyForge release (Seth)
@@ -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._
@@ -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
@@ -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!
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby -rubygems
2
+
3
+ require 'switchboard'
4
+
5
+ switchboard = Switchboard::Client.new
6
+ switchboard.plug!(AutoAcceptJack, EchoJack, NotifyJack)
7
+ switchboard.run!
@@ -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,3 @@
1
+ require 'switchboard/commands/disco/disco'
2
+ require 'switchboard/commands/disco/items'
3
+ require 'switchboard/commands/disco/info'
@@ -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