switchboard 0.1.0

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