em-xmpp 0.0.10 → 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/README.md +157 -18
- data/bin/xmig +1099 -0
- data/lib/em-xmpp/connection.rb +1 -0
- data/lib/em-xmpp/context.rb +444 -38
- data/lib/em-xmpp/conversation.rb +105 -0
- data/lib/em-xmpp/entity.rb +759 -31
- data/lib/em-xmpp/handler.rb +16 -0
- data/lib/em-xmpp/helpers.rb +207 -0
- data/lib/em-xmpp/jid.rb +2 -1
- data/lib/em-xmpp/namespaces.rb +25 -0
- data/lib/em-xmpp/version.rb +1 -1
- data/samples/hello.rb +25 -4
- metadata +12 -9
data/lib/em-xmpp/handler.rb
CHANGED
@@ -43,6 +43,14 @@ module EM::Xmpp
|
|
43
43
|
ctx.bit!(:error) if iq.error?
|
44
44
|
ctx
|
45
45
|
end
|
46
|
+
on_decorator('//xmlns:pubsub', 'xmlns' => PubSub) do |ctx|
|
47
|
+
ctx.bit!(:pubsub)
|
48
|
+
ctx
|
49
|
+
end
|
50
|
+
on_decorator('//xmlns:event', 'xmlns' => PubSubEvent) do |ctx|
|
51
|
+
ctx.bit!(:pubsubevent)
|
52
|
+
ctx
|
53
|
+
end
|
46
54
|
on_decorator('//xmlns:delay', 'xmlns' => Delay) do |ctx|
|
47
55
|
ctx.bit!(:delay)
|
48
56
|
ctx
|
@@ -63,6 +71,10 @@ module EM::Xmpp
|
|
63
71
|
ctx.bit!(:command)
|
64
72
|
ctx
|
65
73
|
end
|
74
|
+
on_decorator('//xmlns:data', 'xmlns' => BoB) do |ctx|
|
75
|
+
ctx.bit!(:bob)
|
76
|
+
ctx
|
77
|
+
end
|
66
78
|
on_decorator('//xmlns:x', 'xmlns' => DataForms) do |ctx|
|
67
79
|
ctx.bit!(:dataforms)
|
68
80
|
ctx
|
@@ -79,6 +91,10 @@ module EM::Xmpp
|
|
79
91
|
ctx.bit!(:streaminitiation)
|
80
92
|
ctx
|
81
93
|
end
|
94
|
+
on_decorator('//xmlns:open | //xmlns:data | //xmlns:close', 'xmlns' => IBB) do |ctx|
|
95
|
+
ctx.bit!(:ibb)
|
96
|
+
ctx
|
97
|
+
end
|
82
98
|
on_decorator('//xmlns:query', 'xmlns' => ByteStreams) do |ctx|
|
83
99
|
ctx.bit!(:bytestreams)
|
84
100
|
ctx
|
data/lib/em-xmpp/helpers.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
|
2
2
|
require 'em-xmpp/nodes'
|
3
|
+
require 'em-xmpp/conversation'
|
3
4
|
module EM::Xmpp
|
4
5
|
module Helpers
|
5
6
|
include EM::Xmpp::Namespaces
|
@@ -16,5 +17,211 @@ module EM::Xmpp
|
|
16
17
|
|
17
18
|
Fiber.yield
|
18
19
|
end
|
20
|
+
|
21
|
+
attr_reader :conversations
|
22
|
+
|
23
|
+
def framework_ready(*args,&blk)
|
24
|
+
@conversations = {}
|
25
|
+
end
|
26
|
+
|
27
|
+
def start_conversation(ctx,key,state=nil,&blk)
|
28
|
+
EM::Xmpp::Conversation.start(ctx,state) do |conv|
|
29
|
+
conversations[key] = conv
|
30
|
+
blk.call conv
|
31
|
+
conversations.delete key
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def conversation(key)
|
36
|
+
@conversations[key]
|
37
|
+
end
|
38
|
+
|
39
|
+
DataFormState = Struct.new(:form, :answers, :current_idx, :user_input) do
|
40
|
+
def current_field
|
41
|
+
form.fields[current_idx]
|
42
|
+
end
|
43
|
+
|
44
|
+
def current_values
|
45
|
+
current_field.values
|
46
|
+
end
|
47
|
+
|
48
|
+
def current_answer
|
49
|
+
answers.fields[current_idx]
|
50
|
+
end
|
51
|
+
|
52
|
+
def current_response_values
|
53
|
+
if current_answer
|
54
|
+
current_answer.values
|
55
|
+
else
|
56
|
+
current_values
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def dataform_conversation(initial_ctx,&blk)
|
62
|
+
form = initial_ctx.env['dataform']
|
63
|
+
answers = EM::Xmpp::Context::Contexts::Dataforms::Form.new('submit',[],nil,nil)
|
64
|
+
state = DataFormState.new(form, answers, 0, nil)
|
65
|
+
|
66
|
+
start_conversation(initial_ctx,:dataform,state) do |conv|
|
67
|
+
conv.prepare_callbacks(:start, :ask, :user_answer, :confirm, :submit, :cancel, &blk)
|
68
|
+
finalize = :submit
|
69
|
+
|
70
|
+
conv.callback(:start)
|
71
|
+
|
72
|
+
catch :cancel do
|
73
|
+
fields = state.form.fields.dup
|
74
|
+
cnt = fields.size
|
75
|
+
idx = 0
|
76
|
+
until (idx + 1) > cnt
|
77
|
+
state.current_idx = idx
|
78
|
+
conv.callback(:ask)
|
79
|
+
state.user_input = conv.delay.ctx
|
80
|
+
cb_action = conv.callback(:user_answer)
|
81
|
+
state.user_input = nil
|
82
|
+
|
83
|
+
case cb_action
|
84
|
+
when :previous
|
85
|
+
idx -= 1
|
86
|
+
idx = [0,idx].max
|
87
|
+
when :next, :modified
|
88
|
+
idx += 1
|
89
|
+
when :repeat
|
90
|
+
#don't touch index
|
91
|
+
when :cancel
|
92
|
+
finalize = :cancel
|
93
|
+
throw :cancel
|
94
|
+
else
|
95
|
+
raise RuntimeError, "no such data-form action: #{cb_action}"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
conv.callback(:confirm)
|
100
|
+
state.user_input = conv.delay.ctx
|
101
|
+
end
|
102
|
+
conv.callback(finalize)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
CommandFlash = Struct.new(:level, :msg)
|
107
|
+
CommandState = Struct.new(:spec, :status, :current_idx, :flash, :last_answer, :result) do
|
108
|
+
def current_step
|
109
|
+
spec.step current_idx
|
110
|
+
end
|
111
|
+
def form
|
112
|
+
current_step.form
|
113
|
+
end
|
114
|
+
def can_complete?
|
115
|
+
spec.can_complete_command? current_idx
|
116
|
+
end
|
117
|
+
def can_prev?
|
118
|
+
spec.has_previous_command? current_idx
|
119
|
+
end
|
120
|
+
def can_next?
|
121
|
+
spec.has_next_command? current_idx
|
122
|
+
end
|
123
|
+
def finished?
|
124
|
+
spec.finished?(current_idx)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
LinearCommandsSpec = Struct.new(:steps) do
|
128
|
+
def step(idx)
|
129
|
+
steps[idx]
|
130
|
+
end
|
131
|
+
def finished?(idx)
|
132
|
+
(idx + 1) > steps.size
|
133
|
+
end
|
134
|
+
def has_previous_command?(idx)
|
135
|
+
idx > 0
|
136
|
+
end
|
137
|
+
def has_next_command?(idx)
|
138
|
+
idx + 1 < steps.size
|
139
|
+
end
|
140
|
+
def can_complete_command?(idx)
|
141
|
+
not has_next_command?(idx)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
CommandStep = Struct.new(:form) do
|
145
|
+
end
|
146
|
+
|
147
|
+
def start_command_conversation(ctx,key,sess_id,spec,&blk)
|
148
|
+
query = ctx.bit(:command)
|
149
|
+
|
150
|
+
state = CommandState.new(spec, :completed, 0, nil, nil, nil)
|
151
|
+
|
152
|
+
start_conversation(ctx,key,state) do |conv|
|
153
|
+
conv.prepare_callbacks(:start, :answer, :cancel, &blk)
|
154
|
+
|
155
|
+
conv.callback(:start)
|
156
|
+
|
157
|
+
catch :cancel do
|
158
|
+
until state.finished?
|
159
|
+
state.status = 'executing'
|
160
|
+
|
161
|
+
reply = query.reply do |iq|
|
162
|
+
iq.command(:xmlns => EM::Xmpp::Namespaces::Commands, :sessionid => sess_id, :node => query.node, :status => state.status) do |cmd|
|
163
|
+
cmd.actions do |n|
|
164
|
+
n.prev if state.can_prev?
|
165
|
+
n.complete if state.can_complete?
|
166
|
+
n.next if state.can_next?
|
167
|
+
end
|
168
|
+
build_form(cmd, state.form,'form')
|
169
|
+
cmd.note({:type => state.flash.level}, state.flash.msg) if state.flash
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
user_answer = conv.send_stanza reply
|
174
|
+
state.last_answer = user_answer
|
175
|
+
action = user_answer.ctx.bit(:command).action
|
176
|
+
|
177
|
+
case action
|
178
|
+
when 'cancel'
|
179
|
+
conv.callback(:cancel)
|
180
|
+
state.status = 'cancel'
|
181
|
+
throw :cancel
|
182
|
+
else
|
183
|
+
conv.callback(:answer)
|
184
|
+
end
|
185
|
+
end #end of until
|
186
|
+
|
187
|
+
state.status = 'completed'
|
188
|
+
end
|
189
|
+
|
190
|
+
finalizer = state.last_answer.ctx.bit(:command).reply do |iq|
|
191
|
+
iq.command(:xmlns => EM::Xmpp::Namespaces::Commands, :sessionid => sess_id, :node => query.node, :status => state.status) do |cmd|
|
192
|
+
cmd.note({:type => state.flash.level}, state.flash.msg) if state.flash
|
193
|
+
build_form(cmd, state.result,'result') if state.result
|
194
|
+
end
|
195
|
+
end
|
196
|
+
send_stanza finalizer
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def build_form(xml,form,type='submit')
|
201
|
+
xml.x(:xmlns => DataForms, :type => type) do |x|
|
202
|
+
x.title form.title if form.title
|
203
|
+
x.instructions form.instructions if form.instructions
|
204
|
+
form.fields.each do |field|
|
205
|
+
args = {'var' => field.var}
|
206
|
+
args = args.merge('type' => field.type) unless field.type.nil? or field.type.empty?
|
207
|
+
args = args.merge('label' => field.label) unless field.label.nil? or field.label.empty?
|
208
|
+
x.field(args) do |f|
|
209
|
+
(field.options||[]).each do |opt_value|
|
210
|
+
f.option do |o|
|
211
|
+
o.value opt_value
|
212
|
+
end
|
213
|
+
end
|
214
|
+
(field.values||[]).each do |value|
|
215
|
+
f.value value
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def build_submit_form(xml,form)
|
223
|
+
build_form(xml,form,'submit')
|
224
|
+
end
|
225
|
+
|
19
226
|
end
|
20
227
|
end
|
data/lib/em-xmpp/jid.rb
CHANGED
data/lib/em-xmpp/namespaces.rb
CHANGED
@@ -25,6 +25,11 @@ module EM::Xmpp
|
|
25
25
|
Roster = 'jabber:iq:roster'
|
26
26
|
#XMPP commands
|
27
27
|
Commands = "http://jabber.org/protocol/commands"
|
28
|
+
#avatar
|
29
|
+
AvatarData = "urn:xmpp:avatar:data"
|
30
|
+
AvatarMetaData = "urn:xmpp:avatar:metadata"
|
31
|
+
#attention
|
32
|
+
Attention = "urn:xmpp:attention:0"
|
28
33
|
#entity nicknames
|
29
34
|
Nick = "http://jabber.org/protocol/nick"
|
30
35
|
#entity activity
|
@@ -39,14 +44,32 @@ module EM::Xmpp
|
|
39
44
|
Delay = "urn:xmpp:delay"
|
40
45
|
#Jabber Data forms
|
41
46
|
DataForms = 'jabber:x:data'
|
47
|
+
#Pubsub
|
48
|
+
PubSub = 'http://jabber.org/protocol/pubsub'
|
49
|
+
#Pubsub#owner
|
50
|
+
PubSubOwner = 'http://jabber.org/protocol/pubsub#owner'
|
51
|
+
#Pubsub#subscribe-authorization
|
52
|
+
PubSubSubscribeAuthorization = 'http://jabber.org/protocol/pubsub#subscribe_authorization'
|
53
|
+
#Pubsub#get-pending
|
54
|
+
PubSubGetPending = 'http://jabber.org/protocol/pubsub#get-pending'
|
55
|
+
#Pubsub#event
|
56
|
+
PubSubEvent = 'http://jabber.org/protocol/pubsub#event'
|
57
|
+
#Multi user chat
|
58
|
+
Muc = 'http://jabber.org/protocol/muc'
|
42
59
|
#Multi user chat - simple user
|
43
60
|
MucUser = 'http://jabber.org/protocol/muc#user'
|
61
|
+
#Multi user chat - admin
|
62
|
+
MucAdmin = 'http://jabber.org/protocol/muc#admin'
|
44
63
|
#Multi user chat - owner
|
45
64
|
MucOwner = 'http://jabber.org/protocol/muc#owner'
|
46
65
|
#Multi user chat - roomconfig
|
47
66
|
MucRoomconfig = 'http://jabber.org/protocol/muc#roomconfig'
|
48
67
|
#Stream initiation offer
|
49
68
|
StreamInitiation = 'http://jabber.org/protocol/si'
|
69
|
+
#Bits of Binary (bob)
|
70
|
+
BoB = 'urn:xmpp:bob'
|
71
|
+
#In-Band Bytestreams
|
72
|
+
IBB = 'http://jabber.org/protocol/ibb'
|
50
73
|
#FileTransfer
|
51
74
|
FileTransfer = 'http://jabber.org/protocol/si/profile/file-transfer'
|
52
75
|
#Feature Negotiation
|
@@ -55,5 +78,7 @@ module EM::Xmpp
|
|
55
78
|
ByteStreams = 'http://jabber.org/protocol/bytestreams'
|
56
79
|
#Extension for Fast byte stream transfers
|
57
80
|
FastByteStreams = 'http://affinix.com/jabber/stream'
|
81
|
+
#xhtml-im
|
82
|
+
XhtmlIM = 'http://jabber.org/protocol/xhtml-im'
|
58
83
|
end
|
59
84
|
end
|
data/lib/em-xmpp/version.rb
CHANGED
data/samples/hello.rb
CHANGED
@@ -15,7 +15,9 @@ module RosterClient
|
|
15
15
|
attr_reader :roster
|
16
16
|
|
17
17
|
include EM::Xmpp::Helpers
|
18
|
+
|
18
19
|
def ready
|
20
|
+
super #setup helpers
|
19
21
|
puts "***** #{@jid} ready"
|
20
22
|
|
21
23
|
on_presence do |ctx|
|
@@ -37,11 +39,30 @@ module RosterClient
|
|
37
39
|
msg = ctx.bit :message
|
38
40
|
|
39
41
|
puts "**** message from #{msg.from}"
|
40
|
-
|
41
|
-
|
42
|
-
|
42
|
+
|
43
|
+
key = msg.from.to_s
|
44
|
+
|
45
|
+
conv = conversation(key)
|
46
|
+
|
47
|
+
if conv
|
48
|
+
conv.resume ctx
|
49
|
+
else
|
50
|
+
x = rand(300)
|
51
|
+
y = rand(300)
|
52
|
+
start_conversation(ctx, key) do |c|
|
53
|
+
rep = c.send_stanza(msg.reply{|xml| xml.body("how much is #{x} - #{y} ?")}, 5)
|
54
|
+
greeting = if rep.interrupted?
|
55
|
+
if rep.ctx.bit(:message).body == (x - y).to_s
|
56
|
+
"great!"
|
57
|
+
else
|
58
|
+
"wrong: #{x - y}"
|
59
|
+
end
|
60
|
+
else
|
61
|
+
"too slow, laggard"
|
62
|
+
end
|
63
|
+
self.send_stanza(msg.reply{|xml| xml.body(greeting)})
|
64
|
+
end
|
43
65
|
end
|
44
|
-
send_stanza hello
|
45
66
|
|
46
67
|
ctx #returns a ctx for subsequent handlers if any
|
47
68
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: em-xmpp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.11
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-02-26 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: eventmachine
|
16
|
-
requirement: &
|
16
|
+
requirement: &2151825840 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *2151825840
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: nokogiri
|
27
|
-
requirement: &
|
27
|
+
requirement: &2151814740 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *2151814740
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: ruby-sasl
|
38
|
-
requirement: &
|
38
|
+
requirement: &2151812280 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,11 +43,12 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *2151812280
|
47
47
|
description: XMPP client for event machine
|
48
48
|
email:
|
49
49
|
- crapooze@gmail.com
|
50
|
-
executables:
|
50
|
+
executables:
|
51
|
+
- xmig
|
51
52
|
extensions: []
|
52
53
|
extra_rdoc_files: []
|
53
54
|
files:
|
@@ -56,12 +57,14 @@ files:
|
|
56
57
|
- LICENSE
|
57
58
|
- README.md
|
58
59
|
- Rakefile
|
60
|
+
- bin/xmig
|
59
61
|
- em-xmpp.gemspec
|
60
62
|
- lib/em-xmpp.rb
|
61
63
|
- lib/em-xmpp/cert_store.rb
|
62
64
|
- lib/em-xmpp/connection.rb
|
63
65
|
- lib/em-xmpp/connector.rb
|
64
66
|
- lib/em-xmpp/context.rb
|
67
|
+
- lib/em-xmpp/conversation.rb
|
65
68
|
- lib/em-xmpp/entity.rb
|
66
69
|
- lib/em-xmpp/handler.rb
|
67
70
|
- lib/em-xmpp/helpers.rb
|