tyler-uppercut 0.6.2 → 0.7.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.
- data/README.textile +26 -0
- data/VERSION.yml +2 -2
- data/examples/basic_agent.rb +28 -0
- data/lib/uppercut/agent.rb +69 -22
- data/spec/agent_spec.rb +100 -0
- data/spec/jabber_stub.rb +22 -4
- data/spec/spec_helper.rb +4 -0
- metadata +3 -2
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
@@ -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
|
data/lib/uppercut/agent.rb
CHANGED
@@ -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
|
-
|
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
|
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
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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,
|
98
|
-
|
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
|
-
|
102
|
-
|
103
|
-
|
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
|
-
|
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(
|
142
|
-
|
143
|
-
self.send(
|
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
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.
|
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-
|
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:
|