tyler-uppercut 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile CHANGED
@@ -47,6 +47,32 @@ Then to actually put the Agent to work...
47
47
  This creates a new BasicAgent instance and sends it _listen_ to make it start accepting messages.
48
48
  Note that by default when an agent is listening, it ignores all errors. (In the future, I'll have it log them.)
49
49
 
50
+ There are also event callbacks for agents. These events are based on XMPP presence messages. So, as of this writing,
51
+ the list of allowed callbacks is: signon, signoff, subscribe, unsubscribe, subscription_approval, subscription_denial,
52
+ status_change, and status_message_change.
53
+
54
+ You can use these callbacks as follows:
55
+
56
+ <pre>
57
+ <code>
58
+ class CoolAgent < Uppercut::Agent
59
+ on :subscribe do |c|
60
+ c.send 'Hey dude, thanks for subscribing!'
61
+ end
62
+
63
+ on :signoff do |c|
64
+ puts "LOG: #{c.to} signed off."
65
+ end
66
+ end
67
+ </code>
68
+ </pre>
69
+
70
+ Some callbacks only work if the user allows the agent to subscribe to their presence notifications. This happens
71
+ automatically when a user subscribes to your agent. (Depending on their client) they'll be presented with a choice
72
+ to authorize or deny the subscription. You can catch their answer with the 'subscription_approval' and 'subscription_denial'
73
+ callbacks. Without their approving the subscription, your 'signon', 'signoff', 'status_change', and 'status_message_change'
74
+ callbacks will not be fired by them. I suggest using the 'subscription_denial' callback to inform them of that.
75
+
50
76
 
51
77
  h2. Making a Notifier
52
78
 
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
- patch: 2
2
+ patch: 0
3
3
  major: 0
4
- minor: 6
4
+ minor: 7
@@ -0,0 +1,28 @@
1
+ class BasicNotifier < Uppercut::Notifier
2
+ notifier :basic do |n,data|
3
+ n.to 'tyler@codehallow.com'
4
+ n.send 'Hey kid.'
5
+ end
6
+ end
7
+
8
+ class BasicAgent < Uppercut::Agent
9
+ command 'date' do |m|
10
+ m.send `date`
11
+ end
12
+
13
+ command /^cat (.*)/ do |m,rest|
14
+ m.send File.read(rest)
15
+ end
16
+
17
+ command 'report' do |m|
18
+ m.send 'Hostname: ' + `hostname`
19
+ m.send 'Running as: ' + ENV['USER']
20
+ end
21
+
22
+ command 'dangerous' do |c|
23
+ c.send "Are you sure?!"
24
+ c.wait_for do |reply|
25
+ c.send %w(yes y).include?(reply.downcase) ? "Okay! Done boss!" : "Cancelled!"
26
+ end
27
+ end
28
+ end
@@ -1,5 +1,8 @@
1
1
  class Uppercut
2
2
  class Agent < Base
3
+ VALID_CALLBACKS = [:signon, :signoff, :subscribe, :unsubscribe, :subscription_approval,
4
+ :subscription_denial, :status_change, :status_message_change]
5
+
3
6
  class << self
4
7
  # Define a new command for the agent.
5
8
  #
@@ -29,7 +32,8 @@ class Uppercut
29
32
  # end
30
33
  #
31
34
  def on(type, &block)
32
- define_method("__on_#{type.to_s}__") { |conversation| block[conversation] }
35
+ raise 'Not a valid callback' unless VALID_CALLBACKS.include?(type)
36
+ define_method("__on_#{type.to_s}") { |conversation| block[conversation] }
33
37
  end
34
38
 
35
39
  private
@@ -76,34 +80,70 @@ class Uppercut
76
80
  # Calling listen fires off a new Thread whose sole purpose is to listen
77
81
  # for new incoming messages and then fire off a new Thread which dispatches
78
82
  # the message to the proper handler.
79
- def listen(debug=false)
83
+ def listen
80
84
  connect unless connected?
81
85
 
82
86
  @listen_thread = Thread.new {
83
87
  @client.add_message_callback do |message|
84
- next if message.body.nil?
85
- next unless allowed_roster_includes?(message.from)
86
-
87
- Thread.new do
88
- begin
89
- dispatch(message)
90
- rescue => e
91
- log e
92
- raise if debug
93
- end
88
+ log_and_continue do
89
+ next if message.body.nil?
90
+ next unless allowed_roster_includes?(message.from)
91
+ dispatch message
94
92
  end
95
93
  end
94
+
96
95
  @roster ||= Jabber::Roster::Helper.new(@client)
97
- @roster.add_presence_callback do |item, oldp, newp|
98
- dispatch_presence(item, newp)
96
+ @roster.add_presence_callback do |item, old_presence, new_presence|
97
+ # Callbacks:
98
+ # post-subscribe initial stuff (oldp == nil)
99
+ # status change: (oldp.show != newp.show)
100
+ # status message change: (oldp.status != newp.status)
101
+
102
+ log_and_continue do
103
+ if old_presence.nil? && new_presence.type == :unavailable
104
+ dispatch_presence :signoff, new_presence
105
+ elsif old_presence.nil?
106
+ # do nothing, we don't care
107
+ elsif old_presence.type == :unavailable && new_presence
108
+ dispatch_presence :signon, new_presence
109
+ elsif old_presence.show != new_presence.show
110
+ dispatch_presence :status_change, new_presence
111
+ elsif old_presence.status != new_presence.status
112
+ dispatch_presence :status_message_change, new_presence
113
+ end
114
+ end
99
115
  end
100
116
  @roster.add_subscription_request_callback do |item,presence|
101
- next unless allowed_roster_includes?(presence.from)
102
- @roster.accept_subscription(presence.from)
103
- dispatch_presence(item, presence)
117
+ # Callbacks:
118
+ # someone tries to subscribe (presence.type == 'subscribe')
119
+
120
+ log_and_continue do
121
+ case presence.type
122
+ when :subscribe
123
+ next unless allowed_roster_includes?(presence.from)
124
+ @roster.accept_subscription(presence.from)
125
+ @roster.add(presence.from, nil, true)
126
+ dispatch_presence :subscribe, presence
127
+ end
128
+ end
104
129
  end
105
130
  @roster.add_subscription_callback do |item, presence|
106
- dispatch_presence(item, presence)
131
+ # Callbacks:
132
+ # user allows agent to subscribe to them (presence.type == 'subscribed')
133
+ # user denies agent subscribe request (presence.type == 'unsubscribed')
134
+ # user unsubscribes from agent (presence.type == 'unsubscribe')
135
+
136
+ log_and_continue do
137
+ case presence.type
138
+ when :subscribed
139
+ dispatch_presence :subscription_approval, presence
140
+ when :unsubscribed
141
+ # if item.subscription != :from, it's not a denial... it's just an unsub
142
+ dispatch_presence(:subscription_denial, presence) if item.subscription == :from
143
+ when :unsubscribe
144
+ dispatch_presence :unsubscribe, presence
145
+ end
146
+ end
107
147
  end
108
148
  sleep
109
149
  }
@@ -126,10 +166,17 @@ class Uppercut
126
166
  @redirects[contact].push block
127
167
  end
128
168
 
129
- attr_accessor :allowed_roster
169
+ attr_accessor :allowed_roster, :roster
130
170
 
131
171
  private
132
172
 
173
+ def log_and_continue
174
+ yield
175
+ rescue => e
176
+ log e
177
+ raise if @debug
178
+ end
179
+
133
180
  def dispatch(msg)
134
181
  bare_from = msg.from.bare
135
182
  block = @redirects[bare_from].respond_to?(:shift) && @redirects[bare_from].shift
@@ -138,9 +185,9 @@ class Uppercut
138
185
  self.methods.grep(/^__uc/).sort.detect { |m| send(m,msg) != :no_match }
139
186
  end
140
187
 
141
- def dispatch_presence(item, presence)
142
- handler_method = "__on_#{presence.type.to_s}__"
143
- self.send(handler_method, Conversation.new(presence.from, self)) if respond_to?(handler_method)
188
+ def dispatch_presence(type, presence)
189
+ handler = "__on_#{type}"
190
+ self.send(handler, Conversation.new(presence.from, self), presence) if respond_to?(handler)
144
191
  end
145
192
 
146
193
  def __ucDefault(msg)
data/spec/agent_spec.rb CHANGED
@@ -124,6 +124,96 @@ describe Uppercut::Agent do
124
124
  @agent.should_receive(:dispatch)
125
125
  @agent.client.receive_message("foo@bar.com","test")
126
126
  end
127
+
128
+ describe 'presence callbacks' do
129
+ it 'processes :signon presence callback' do
130
+ @agent.listen
131
+ @agent.should_receive :__on_signon
132
+ new_presence = Jabber::Presence.new(nil,nil)
133
+ old_presence = Jabber::Presence.new(nil,nil)
134
+ old_presence.type = :unavailable
135
+
136
+ @agent.roster.receive_presence(Jabber::Roster::Helper::RosterItem.new, old_presence, new_presence)
137
+ end
138
+
139
+ it 'processes :signoff presence callback' do
140
+ @agent.listen
141
+ @agent.should_receive :__on_signoff
142
+ presence = Jabber::Presence.new(nil,nil)
143
+ presence.type = :unavailable
144
+
145
+ @agent.roster.receive_presence(Jabber::Roster::Helper::RosterItem.new, nil, presence)
146
+ end
147
+
148
+ it 'processes :status_change presence callback' do
149
+ @agent.listen
150
+ @agent.should_receive :__on_status_change
151
+
152
+ old_presence = Jabber::Presence.new(nil,nil)
153
+ new_presence = Jabber::Presence.new(nil,nil)
154
+ new_presence.show = :away
155
+
156
+ @agent.roster.receive_presence(Jabber::Roster::Helper::RosterItem.new, old_presence, new_presence)
157
+ end
158
+
159
+ it 'processes :status_message_change presence callback' do
160
+ @agent.listen
161
+ @agent.should_receive :__on_status_message_change
162
+
163
+ old_presence = Jabber::Presence.new(nil,nil)
164
+ old_presence.status = 'chicka chicka yeaaaaah'
165
+
166
+ new_presence = Jabber::Presence.new(nil,nil)
167
+ new_presence.status = 'thom yorke is the man'
168
+
169
+ @agent.roster.receive_presence(Jabber::Roster::Helper::RosterItem.new, old_presence, new_presence)
170
+ end
171
+
172
+ it 'processes :subscribe presence callback' do
173
+ @agent.listen
174
+ @agent.should_receive :__on_subscribe
175
+ @agent.roster.should_receive :add
176
+ @agent.roster.should_receive :accept_subscription
177
+
178
+ presence = Jabber::Presence.new(nil,nil)
179
+ presence.type = :subscribe
180
+
181
+ @agent.roster.receive_subscription_request(Jabber::Roster::Helper::RosterItem.new, presence)
182
+ end
183
+
184
+ it 'processes :subscription_approval presence callback' do
185
+ @agent.listen
186
+ @agent.should_receive :__on_subscription_approval
187
+
188
+ presence = Jabber::Presence.new(nil,nil)
189
+ presence.type = :subscribed
190
+
191
+ @agent.roster.receive_subscription(Jabber::Roster::Helper::RosterItem.new, presence)
192
+ end
193
+
194
+ it 'processes :subscription_denial presence callback' do
195
+ @agent.listen
196
+ @agent.should_receive :__on_subscription_denial
197
+
198
+ presence = Jabber::Presence.new(nil,nil)
199
+ presence.type = :unsubscribed
200
+
201
+ item = Jabber::Roster::Helper::RosterItem.new
202
+ item.subscription = :from
203
+
204
+ @agent.roster.receive_subscription(item, presence)
205
+ end
206
+
207
+ it 'processes :unsubscribe presence callback' do
208
+ @agent.listen
209
+ @agent.should_receive :__on_unsubscribe
210
+
211
+ presence = Jabber::Presence.new(nil,nil)
212
+ presence.type = :unsubscribe
213
+
214
+ @agent.roster.receive_subscription(Jabber::Roster::Helper::RosterItem.new, presence)
215
+ end
216
+ end
127
217
  end
128
218
 
129
219
  describe :stop do
@@ -153,6 +243,16 @@ describe Uppercut::Agent do
153
243
  @agent.should_not be_listening
154
244
  end
155
245
  end
246
+
247
+ describe :dispatch_presence do
248
+ it 'calls the correct callback' do
249
+ @agent.listen
250
+ @agent.should_receive(:__on_subscribe)
251
+
252
+ presence = Jabber::Presence.new(nil,nil)
253
+ @agent.send(:dispatch_presence, :subscribe, presence)
254
+ end
255
+ end
156
256
 
157
257
  describe :dispatch do
158
258
  it "calls the first matching command" do
data/spec/jabber_stub.rb CHANGED
@@ -46,13 +46,10 @@ class Jabber
46
46
  end
47
47
 
48
48
  class Presence
49
- attr_accessor :from
49
+ attr_accessor :from, :type, :show, :status
50
50
 
51
51
  def initialize(a,b)
52
52
  end
53
-
54
- def from
55
- end
56
53
  end
57
54
 
58
55
  class Message
@@ -64,12 +61,19 @@ class Jabber
64
61
 
65
62
  class Roster
66
63
  class Helper
64
+ class RosterItem
65
+ attr_accessor :subscription
66
+ end
67
+
67
68
  def initialize(client)
68
69
  @client = client
69
70
  end
70
71
 
71
72
  def accept_subscription(a)
72
73
  end
74
+
75
+ def add(a)
76
+ end
73
77
 
74
78
  attr_reader :on_subscription_request
75
79
  def add_subscription_request_callback(&block)
@@ -83,6 +87,20 @@ class Jabber
83
87
  def add_subscription_callback(&block)
84
88
  @on_subscription = block
85
89
  end
90
+
91
+ # TESTING HELPER METHODS
92
+
93
+ def receive_presence(item, old_presence, new_presence)
94
+ @on_presence[item, old_presence, new_presence]
95
+ end
96
+
97
+ def receive_subscription(item, presence)
98
+ @on_subscription[item, presence]
99
+ end
100
+
101
+ def receive_subscription_request(item, presence)
102
+ @on_subscription_request[item, presence]
103
+ end
86
104
  end
87
105
  end
88
106
 
data/spec/spec_helper.rb CHANGED
@@ -35,6 +35,10 @@ class TestAgent < Uppercut::Agent
35
35
  c.send 'Hooray!'
36
36
  end
37
37
  end
38
+
39
+ Uppercut::Agent::VALID_CALLBACKS.each do |cb|
40
+ on(cb) { }
41
+ end
38
42
  end
39
43
 
40
44
  class TestNotifier < Uppercut::Notifier
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tyler-uppercut
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tyler McMullen
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-12-02 00:00:00 -08:00
12
+ date: 2008-12-27 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -44,6 +44,7 @@ files:
44
44
  - spec/jabber_stub.rb
45
45
  - spec/notifier_spec.rb
46
46
  - spec/spec_helper.rb
47
+ - examples/basic_agent.rb
47
48
  has_rdoc: false
48
49
  homepage: http://github.com/tyler/uppercut
49
50
  post_install_message: