sinatra-wechat 0.0.1
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.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/README.md +67 -0
- data/Rakefile +8 -0
- data/examples/wechat_example.rb +78 -0
- data/lib/sinatra/version.rb +5 -0
- data/lib/sinatra/wechat.rb +88 -0
- data/sinatra-wechat.gemspec +30 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/wechat_spec.rb +282 -0
- metadata +169 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f3056a6b2bb8a22c428fd91e3ed597dd54063374
|
4
|
+
data.tar.gz: a50373853d8165e3aa3e8ee10caf6a7aa747d0fa
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 336bfb4a2f8f5fd8dcc5c14555bbd0c24170dcbe5bb57906f733bddf61254343f4fbfe4ecc33b9e8f76f3b7b935beea2e664876234c00fcc607cf5a94057d7a0
|
7
|
+
data.tar.gz: 3264fb34bdbdd9bd3bb479fc0ebc5cf0793771655f5dbe29cef37e111e463e15132c761b625ee3b5236fb86a0d41f8a86a09b3d7bef6e50c866a19d29b7eea6c
|
data/.coveralls.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
service_name: travis-ci
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Lu, Jun
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# Sinatra Wechat extension
|
2
|
+
[](https://travis-ci.org/luj1985/sinatra-wechat)
|
3
|
+
|
4
|
+
This extension is used to support [Tencent Wechat](https://mp.weixin.qq.com/) development mode.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
$ gem install sinatra-wechat
|
9
|
+
|
10
|
+
# Usage
|
11
|
+
|
12
|
+
> use `disable :message_validation` to prevent message validation, otherwise need to add signature to the URL.
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
# app.rb
|
16
|
+
require 'sinatra'
|
17
|
+
require 'sinatra/wechat'
|
18
|
+
|
19
|
+
disable :message_validation
|
20
|
+
set :wechat_token, 'test-token'
|
21
|
+
|
22
|
+
wechat('/') {
|
23
|
+
text(:content => %r{\d+}) {
|
24
|
+
content_type 'application/xml'
|
25
|
+
erb :hello, :locals => request[:wechat_values]
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
__END__
|
30
|
+
@@ hello
|
31
|
+
<xml>
|
32
|
+
<ToUserName><%= from_user_name %></ToUserName>
|
33
|
+
<FromUserName><%= to_user_name %></FromUserName>
|
34
|
+
<CreateTime><%= Time.now.to_i %></CreateTime>
|
35
|
+
<MsgType>text</MsgType>
|
36
|
+
<Content><![CDATA[你好]]></Content>
|
37
|
+
</xml>
|
38
|
+
```
|
39
|
+
|
40
|
+
start server:
|
41
|
+
```sheel
|
42
|
+
$ ruby app.rb
|
43
|
+
```
|
44
|
+
|
45
|
+
Test via [cURL](http://curl.haxx.se):
|
46
|
+
|
47
|
+
```shell
|
48
|
+
$ curl -X POST --data '<xml>
|
49
|
+
<ToUserName>tousername</ToUserName>
|
50
|
+
<FromUserName>fromusername</FromUserName>
|
51
|
+
<CreateTime>1348831860</CreateTime>
|
52
|
+
<MsgType>text</MsgType>
|
53
|
+
<Content>1345 match number</Content>
|
54
|
+
<MsgId>1234567890123456</MsgId>
|
55
|
+
</xml>' http://localhost:4567
|
56
|
+
```
|
57
|
+
|
58
|
+
The response is like:
|
59
|
+
``` xml
|
60
|
+
<xml>
|
61
|
+
<ToUserName>fromusername</ToUserName>
|
62
|
+
<FromUserName>tousername</FromUserName>
|
63
|
+
<CreateTime>1405790652</CreateTime>
|
64
|
+
<MsgType>text</MsgType>
|
65
|
+
<Content><![CDATA[你好]]></Content>
|
66
|
+
</xml>
|
67
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
require 'sinatra/wechat'
|
3
|
+
require 'nokogiri'
|
4
|
+
|
5
|
+
disable :message_validation
|
6
|
+
set :wechat_token, 'test-token'
|
7
|
+
|
8
|
+
location_event_reply = proc {
|
9
|
+
content_type 'application/xml'
|
10
|
+
values = request[:wechat_values]
|
11
|
+
builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
|
12
|
+
xml.xml {
|
13
|
+
xml.ToUserName values[:from_user_name]
|
14
|
+
xml.FromUserName values[:to_user_name]
|
15
|
+
xml.CreateTime Time.now.to_i
|
16
|
+
xml.MsgType "text"
|
17
|
+
xml.Content 'This is a location response'
|
18
|
+
}
|
19
|
+
end
|
20
|
+
builder.to_xml
|
21
|
+
}
|
22
|
+
|
23
|
+
wechat('/wechat') {
|
24
|
+
location {
|
25
|
+
instance_eval &location_event_reply
|
26
|
+
}
|
27
|
+
text(:content => %r/\d+/) {
|
28
|
+
content_type 'application/xml'
|
29
|
+
erb :text_response, :locals => request[:wechat_values]
|
30
|
+
}
|
31
|
+
image(lambda { |values| values[:from_user_name] == 'test' }) {
|
32
|
+
content_type 'application/xml'
|
33
|
+
values = request[:wechat_values]
|
34
|
+
replies = [{
|
35
|
+
:title => 'artitle 1 title',
|
36
|
+
:description => 'article 1 description',
|
37
|
+
:pic_url => 'http://www.xxxx.com/yyy.jpg',
|
38
|
+
:url => 'http://www.xxxx.com/yyy.html'
|
39
|
+
}, {
|
40
|
+
:title => 'artitle 2 title',
|
41
|
+
:description => 'article 2 description',
|
42
|
+
:pic_url => 'http://zzz.com/foo.png',
|
43
|
+
:url => 'http://zzz.com/bar.html'
|
44
|
+
}]
|
45
|
+
builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
|
46
|
+
xml.xml {
|
47
|
+
xml.ToUserName values[:from_user_name]
|
48
|
+
xml.FromUserName values[:to_user_name]
|
49
|
+
xml.CreateTime Time.now.to_i
|
50
|
+
xml.MsgType "news"
|
51
|
+
xml.ArticleCount replies.length
|
52
|
+
xml.Articles {
|
53
|
+
replies.each do |reply|
|
54
|
+
xml.item {
|
55
|
+
xml.Title reply.title
|
56
|
+
xml.Description reply.description
|
57
|
+
xml.PicUrl reply.pic_url
|
58
|
+
xml.Url reply.url
|
59
|
+
}
|
60
|
+
end
|
61
|
+
}
|
62
|
+
}
|
63
|
+
end
|
64
|
+
builder.to_xml
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
|
69
|
+
__END__
|
70
|
+
@@ text_response
|
71
|
+
|
72
|
+
<xml>
|
73
|
+
<ToUserName><%= from_user_name %></ToUserName>
|
74
|
+
<FromUserName><%= to_user_name %></FromUserName>
|
75
|
+
<CreateTime><%= Time.now.to_i %></CreateTime>
|
76
|
+
<MsgType>text</MsgType>
|
77
|
+
<Content><![CDATA[你好]]></Content>
|
78
|
+
</xml>
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'blankslate'
|
3
|
+
require 'nokogiri'
|
4
|
+
|
5
|
+
module Sinatra
|
6
|
+
module Wechat
|
7
|
+
module EndpointActions
|
8
|
+
class WechatDispatcher < ::BlankSlate
|
9
|
+
def initialize
|
10
|
+
super
|
11
|
+
@message_handlers = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def method_missing(sym, *args, &block)
|
15
|
+
@message_handlers[sym] ||= []
|
16
|
+
matchers = args.collect do |v|
|
17
|
+
if v.respond_to?(:call) then lambda { |values| v.call(values) }
|
18
|
+
# for named parameters
|
19
|
+
elsif v.respond_to?(:all?) then lambda { |values| v.all? { |k,v| v === values[k]} }
|
20
|
+
else raise TypeError, "\"#{v} (#{v.class})\" is not an acceptable condition"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
matcher = lambda do |values|
|
24
|
+
matchers.all? {|m| m.call(values)}
|
25
|
+
end
|
26
|
+
@message_handlers[sym] << [ matcher, block ]
|
27
|
+
end
|
28
|
+
|
29
|
+
def route!(values)
|
30
|
+
type = values[:msg_type].to_sym
|
31
|
+
handlers = @message_handlers[type] || []
|
32
|
+
_, handler = handlers.find { |m, _| m.call(values) }
|
33
|
+
handler
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def wechat(endpoint = '/', &block)
|
38
|
+
dispatcher = WechatDispatcher.new
|
39
|
+
dispatcher.instance_eval &block
|
40
|
+
|
41
|
+
get endpoint do
|
42
|
+
halt 403 unless validate_messages
|
43
|
+
content_type 'text/plain'
|
44
|
+
params[:echostr]
|
45
|
+
end
|
46
|
+
|
47
|
+
post endpoint do
|
48
|
+
halt 403 unless validate_messages
|
49
|
+
|
50
|
+
body = request.body.read || ""
|
51
|
+
halt 501 if body.empty?
|
52
|
+
|
53
|
+
doc = Nokogiri::XML(body).root
|
54
|
+
values = doc.element_children.each_with_object(Hash.new) do |e, v|
|
55
|
+
name = e.name.gsub(/(.)([A-Z])/,'\1_\2').downcase
|
56
|
+
# rename 'Location_X' to 'location__x' then to 'location_x'
|
57
|
+
name = name.gsub(/(_{2,})/,'_')
|
58
|
+
v[name.to_sym] = e.content
|
59
|
+
end
|
60
|
+
handler = dispatcher.route!(values)
|
61
|
+
halt 501 unless handler
|
62
|
+
|
63
|
+
request[:wechat_values] = values
|
64
|
+
instance_eval(&handler)
|
65
|
+
end
|
66
|
+
self
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.registered(app)
|
71
|
+
app.extend(Wechat::EndpointActions)
|
72
|
+
|
73
|
+
app.helpers do
|
74
|
+
enable :message_validation
|
75
|
+
|
76
|
+
def validate_messages
|
77
|
+
token = settings.wechat_token || ""
|
78
|
+
raw = [token, params[:timestamp], params[:nonce]].compact.sort.join
|
79
|
+
settings.message_validation ? Digest::SHA1.hexdigest(raw) == params[:signature] : true
|
80
|
+
end
|
81
|
+
end
|
82
|
+
# expose to classic style
|
83
|
+
Delegator.delegate(:wechat)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
register Wechat
|
88
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'sinatra/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "sinatra-wechat"
|
8
|
+
spec.version = Sinatra::Wechat::VERSION
|
9
|
+
spec.authors = ["Lu, Jun"]
|
10
|
+
spec.email = ["luj1985@gmail.com"]
|
11
|
+
spec.summary = "Sinatra extension for Tencent Wechat"
|
12
|
+
spec.description = "Provide Extensible Sinatra API to support Wechat development mode"
|
13
|
+
spec.homepage = "https://github.com/luj1985/sinatra-wechat"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0") - %w[.gitignore .travis.yml]
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "bundler", "~> 1.6"
|
22
|
+
spec.add_dependency "sinatra", "~> 1.4"
|
23
|
+
spec.add_dependency "nokogiri"
|
24
|
+
spec.add_dependency "blankslate"
|
25
|
+
|
26
|
+
spec.add_development_dependency "rake"
|
27
|
+
spec.add_development_dependency "rspec"
|
28
|
+
spec.add_development_dependency "rack-test"
|
29
|
+
spec.add_development_dependency "coveralls"
|
30
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/spec/wechat_spec.rb
ADDED
@@ -0,0 +1,282 @@
|
|
1
|
+
require File.expand_path '../spec_helper.rb', __FILE__
|
2
|
+
|
3
|
+
describe Sinatra::Wechat do
|
4
|
+
include Rack::Test::Methods
|
5
|
+
|
6
|
+
it "GET should have message verification" do
|
7
|
+
|
8
|
+
def app
|
9
|
+
@instance = Sinatra.new do
|
10
|
+
set :wechat_token, 'test-token'
|
11
|
+
register Sinatra::Wechat
|
12
|
+
end
|
13
|
+
@instance.wechat { }
|
14
|
+
end
|
15
|
+
|
16
|
+
get '/'
|
17
|
+
expect(last_response.status).to eq(403)
|
18
|
+
|
19
|
+
get '/', {:timestamp => '201407191804',
|
20
|
+
:nonce => 'nonce',
|
21
|
+
:signature => '9a91a1cea1cb60b87a9abb29dae06dce14721258',
|
22
|
+
:echostr => 'echo string'}
|
23
|
+
expect(last_response.status).to eq(200)
|
24
|
+
expect(last_response.body).to eq('echo string')
|
25
|
+
|
26
|
+
@instance.disable :message_validation
|
27
|
+
get '/'
|
28
|
+
expect(last_response.status).to eq(200)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "POST should have message verification" do
|
32
|
+
def app
|
33
|
+
@instance = Sinatra.new do
|
34
|
+
set :wechat_token, 'test-token'
|
35
|
+
register Sinatra::Wechat
|
36
|
+
end
|
37
|
+
@instance.wechat {
|
38
|
+
text { 'text response' }
|
39
|
+
}
|
40
|
+
end
|
41
|
+
post '/'
|
42
|
+
expect(last_response.status).to eq(403)
|
43
|
+
|
44
|
+
body = <<-EOF
|
45
|
+
<xml>
|
46
|
+
<ToUserName>tousername</ToUserName>
|
47
|
+
<FromUserName>fromusername</FromUserName>
|
48
|
+
<CreateTime>1348831860</CreateTime>
|
49
|
+
<MsgType>text</MsgType>
|
50
|
+
<Content>This is the message content</Content>
|
51
|
+
<MsgId>1234567890123456</MsgId>
|
52
|
+
</xml>
|
53
|
+
EOF
|
54
|
+
|
55
|
+
post '/?timestamp=201407191804&nonce=nonce&signature=9a91a1cea1cb60b87a9abb29dae06dce14721258', body
|
56
|
+
expect(last_response.status).to eq(200)
|
57
|
+
expect(last_response.body).to eq('text response')
|
58
|
+
end
|
59
|
+
|
60
|
+
it "can switch wechat endpoint" do
|
61
|
+
def app
|
62
|
+
@instance = Sinatra.new do
|
63
|
+
set :wechat_token, 'test-token'
|
64
|
+
register Sinatra::Wechat
|
65
|
+
end
|
66
|
+
@instance.wechat('/wechat') {
|
67
|
+
image { 'relocated response' }
|
68
|
+
}
|
69
|
+
end
|
70
|
+
body = <<-EOF
|
71
|
+
<xml>
|
72
|
+
<ToUserName><![CDATA[toUser]]></ToUserName>
|
73
|
+
<FromUserName><![CDATA[fromUser]]></FromUserName>
|
74
|
+
<CreateTime>1348831860</CreateTime>
|
75
|
+
<MsgType><![CDATA[image]]></MsgType>
|
76
|
+
<PicUrl><![CDATA[this is a url]]></PicUrl>
|
77
|
+
<MediaId><![CDATA[media_id]]></MediaId>
|
78
|
+
<MsgId>1234567890123456</MsgId>
|
79
|
+
</xml>
|
80
|
+
EOF
|
81
|
+
|
82
|
+
post '/?timestamp=201407191804&nonce=nonce&signature=9a91a1cea1cb60b87a9abb29dae06dce14721258', body
|
83
|
+
expect(last_response.status).to eq(404)
|
84
|
+
|
85
|
+
post '/wechat?timestamp=201407191804&nonce=nonce&signature=9a91a1cea1cb60b87a9abb29dae06dce14721258', body
|
86
|
+
expect(last_response.status).to eq(200)
|
87
|
+
expect(last_response.body).to eq('relocated response')
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should accept wechat message push" do
|
92
|
+
def app
|
93
|
+
@instance = Sinatra.new do
|
94
|
+
set :wechat_token, 'test-token'
|
95
|
+
register Sinatra::Wechat
|
96
|
+
end
|
97
|
+
@instance.wechat {
|
98
|
+
text(:content => %r{regex match}) { 'regex match' }
|
99
|
+
text(lambda {|values| values[:content] == 'function match'}) { 'function match' }
|
100
|
+
text { 'default match' }
|
101
|
+
voice {
|
102
|
+
values = request[:wechat_values]
|
103
|
+
values[:msg_id]
|
104
|
+
}
|
105
|
+
location {
|
106
|
+
values = request[:wechat_values]
|
107
|
+
values[:label]
|
108
|
+
}
|
109
|
+
}
|
110
|
+
end
|
111
|
+
|
112
|
+
post '/?timestamp=201407191804&nonce=nonce&signature=9a91a1cea1cb60b87a9abb29dae06dce14721258', <<-EOF
|
113
|
+
<xml>
|
114
|
+
<ToUserName>tousername</ToUserName>
|
115
|
+
<FromUserName>fromusername</FromUserName>
|
116
|
+
<CreateTime>1348831860</CreateTime>
|
117
|
+
<MsgType>text</MsgType>
|
118
|
+
<Content>test regex match ...</Content>
|
119
|
+
<MsgId>1234567890123456</MsgId>
|
120
|
+
</xml>
|
121
|
+
EOF
|
122
|
+
expect(last_response.status).to eq(200)
|
123
|
+
expect(last_response.body).to eq('regex match')
|
124
|
+
|
125
|
+
|
126
|
+
post '/?timestamp=201407191804&nonce=nonce&signature=9a91a1cea1cb60b87a9abb29dae06dce14721258', <<-EOF
|
127
|
+
<xml>
|
128
|
+
<ToUserName>tousername</ToUserName>
|
129
|
+
<FromUserName>fromusername</FromUserName>
|
130
|
+
<CreateTime>1348831860</CreateTime>
|
131
|
+
<MsgType>text</MsgType>
|
132
|
+
<Content>function match</Content>
|
133
|
+
<MsgId>1234567890123456</MsgId>
|
134
|
+
</xml>
|
135
|
+
EOF
|
136
|
+
expect(last_response.status).to eq(200)
|
137
|
+
expect(last_response.body).to eq('function match')
|
138
|
+
|
139
|
+
|
140
|
+
post '/?timestamp=201407191804&nonce=nonce&signature=9a91a1cea1cb60b87a9abb29dae06dce14721258', <<-EOF
|
141
|
+
<xml>
|
142
|
+
<ToUserName>tousername</ToUserName>
|
143
|
+
<FromUserName>fromusername</FromUserName>
|
144
|
+
<CreateTime>1348831860</CreateTime>
|
145
|
+
<MsgType>text</MsgType>
|
146
|
+
<Content>default match</Content>
|
147
|
+
<MsgId>1234567890123456</MsgId>
|
148
|
+
</xml>
|
149
|
+
EOF
|
150
|
+
expect(last_response.status).to eq(200)
|
151
|
+
expect(last_response.body).to eq('default match')
|
152
|
+
|
153
|
+
|
154
|
+
post '/?timestamp=201407191804&nonce=nonce&signature=9a91a1cea1cb60b87a9abb29dae06dce14721258', <<-EOF
|
155
|
+
<xml>
|
156
|
+
<ToUserName><![CDATA[toUser]]></ToUserName>
|
157
|
+
<FromUserName><![CDATA[fromUser]]></FromUserName>
|
158
|
+
<CreateTime>1357290913</CreateTime>
|
159
|
+
<MsgType><![CDATA[voice]]></MsgType>
|
160
|
+
<MediaId><![CDATA[media_id]]></MediaId>
|
161
|
+
<Format><![CDATA[Format]]></Format>
|
162
|
+
<MsgId>1234567890123456</MsgId>
|
163
|
+
</xml>
|
164
|
+
EOF
|
165
|
+
expect(last_response.status).to eq(200)
|
166
|
+
expect(last_response.body).to eq('1234567890123456')
|
167
|
+
|
168
|
+
post '/?timestamp=201407191804&nonce=nonce&signature=9a91a1cea1cb60b87a9abb29dae06dce14721258', <<-EOF
|
169
|
+
<xml>
|
170
|
+
<ToUserName><![CDATA[toUser]]></ToUserName>
|
171
|
+
<FromUserName><![CDATA[fromUser]]></FromUserName>
|
172
|
+
<CreateTime>1351776360</CreateTime>
|
173
|
+
<MsgType><![CDATA[location]]></MsgType>
|
174
|
+
<Location_X>23.134521</Location_X>
|
175
|
+
<Location_Y>113.358803</Location_Y>
|
176
|
+
<Scale>20</Scale>
|
177
|
+
<Label><![CDATA[位置信息]]></Label>
|
178
|
+
<MsgId>1234567890123456</MsgId>
|
179
|
+
</xml>
|
180
|
+
EOF
|
181
|
+
expect(last_response.status).to eq(200)
|
182
|
+
expect(last_response.body).to eq('位置信息')
|
183
|
+
|
184
|
+
post '/?timestamp=201407191804&nonce=nonce&signature=9a91a1cea1cb60b87a9abb29dae06dce14721258', <<-EOF
|
185
|
+
<xml>
|
186
|
+
<MsgType>unknown</MsgType>
|
187
|
+
</xml>
|
188
|
+
EOF
|
189
|
+
expect(last_response.status).to eq(501)
|
190
|
+
end
|
191
|
+
|
192
|
+
it "should accept complex match" do
|
193
|
+
def app
|
194
|
+
@instance = Sinatra.new do
|
195
|
+
set :wechat_token, 'test-token'
|
196
|
+
register Sinatra::Wechat
|
197
|
+
end
|
198
|
+
@instance.wechat {
|
199
|
+
future(lambda {|vs| vs[:to_user_name] == 'test' }, :content => %r{future}, :create_time => '1348831860') {
|
200
|
+
'complex match'
|
201
|
+
}
|
202
|
+
range(:to_user_name => 'tesa'..'testz') { 'range match' }
|
203
|
+
}
|
204
|
+
end
|
205
|
+
|
206
|
+
post '/?timestamp=201407191804&nonce=nonce&signature=9a91a1cea1cb60b87a9abb29dae06dce14721258', <<-EOF
|
207
|
+
<xml>
|
208
|
+
<ToUserName>test</ToUserName>
|
209
|
+
<FromUserName>fromusername</FromUserName>
|
210
|
+
<CreateTime>1348831860</CreateTime>
|
211
|
+
<MsgType>future</MsgType>
|
212
|
+
<Content>Test future message type</Content>
|
213
|
+
<MsgId>1234567890123456</MsgId>
|
214
|
+
</xml>
|
215
|
+
EOF
|
216
|
+
expect(last_response.status).to eq(200)
|
217
|
+
expect(last_response.body).to eq('complex match')
|
218
|
+
|
219
|
+
|
220
|
+
|
221
|
+
post '/?timestamp=201407191804&nonce=nonce&signature=9a91a1cea1cb60b87a9abb29dae06dce14721258', <<-EOF
|
222
|
+
<xml>
|
223
|
+
<ToUserName>test</ToUserName>
|
224
|
+
<MsgType>range</MsgType>
|
225
|
+
</xml>
|
226
|
+
EOF
|
227
|
+
expect(last_response.status).to eq(200)
|
228
|
+
expect(last_response.body).to eq('range match')
|
229
|
+
end
|
230
|
+
|
231
|
+
it "should raise error when invalid condition set" do
|
232
|
+
@instance = Sinatra.new do
|
233
|
+
set :wechat_token, 'test-token'
|
234
|
+
register Sinatra::Wechat
|
235
|
+
end
|
236
|
+
expect {
|
237
|
+
@instance.wechat {
|
238
|
+
future('invalid condition') { 'complex match' }
|
239
|
+
}
|
240
|
+
}.to raise_exception
|
241
|
+
end
|
242
|
+
|
243
|
+
it "can have multiple endpoint" do
|
244
|
+
def app
|
245
|
+
@instance = Sinatra.new do
|
246
|
+
set :wechat_token, 'test-token'
|
247
|
+
register Sinatra::Wechat
|
248
|
+
end
|
249
|
+
@instance.wechat('/wechat1') {
|
250
|
+
selector = lambda do |values|
|
251
|
+
x = values[:location_x].to_f
|
252
|
+
20 < x && x < 30
|
253
|
+
end
|
254
|
+
location(selector) { 'matched location range' }
|
255
|
+
}
|
256
|
+
@instance.wechat('/wechat2') {
|
257
|
+
text { 'this is another wechat endpoint' }
|
258
|
+
}
|
259
|
+
end
|
260
|
+
|
261
|
+
post '/wechat1?timestamp=201407191804&nonce=nonce&signature=9a91a1cea1cb60b87a9abb29dae06dce14721258', <<-EOF
|
262
|
+
<xml>
|
263
|
+
<MsgType><![CDATA[location]]></MsgType>
|
264
|
+
<Location_X>23.134521</Location_X>
|
265
|
+
<Location_Y>113.358803</Location_Y>
|
266
|
+
<Scale>20</Scale>
|
267
|
+
</xml>
|
268
|
+
EOF
|
269
|
+
expect(last_response.status).to eq(200)
|
270
|
+
expect(last_response.body).to eq('matched location range')
|
271
|
+
|
272
|
+
|
273
|
+
post '/wechat2?timestamp=201407191804&nonce=nonce&signature=9a91a1cea1cb60b87a9abb29dae06dce14721258', <<-EOF
|
274
|
+
<xml>
|
275
|
+
<MsgType>text</MsgType>
|
276
|
+
</xml>
|
277
|
+
EOF
|
278
|
+
expect(last_response.status).to eq(200)
|
279
|
+
expect(last_response.body).to eq('this is another wechat endpoint')
|
280
|
+
|
281
|
+
end
|
282
|
+
end
|
metadata
ADDED
@@ -0,0 +1,169 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sinatra-wechat
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Lu, Jun
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-07-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: sinatra
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.4'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.4'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: nokogiri
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: blankslate
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rack-test
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: coveralls
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: Provide Extensible Sinatra API to support Wechat development mode
|
126
|
+
email:
|
127
|
+
- luj1985@gmail.com
|
128
|
+
executables: []
|
129
|
+
extensions: []
|
130
|
+
extra_rdoc_files: []
|
131
|
+
files:
|
132
|
+
- ".coveralls.yml"
|
133
|
+
- Gemfile
|
134
|
+
- LICENSE
|
135
|
+
- README.md
|
136
|
+
- Rakefile
|
137
|
+
- examples/wechat_example.rb
|
138
|
+
- lib/sinatra/version.rb
|
139
|
+
- lib/sinatra/wechat.rb
|
140
|
+
- sinatra-wechat.gemspec
|
141
|
+
- spec/spec_helper.rb
|
142
|
+
- spec/wechat_spec.rb
|
143
|
+
homepage: https://github.com/luj1985/sinatra-wechat
|
144
|
+
licenses:
|
145
|
+
- MIT
|
146
|
+
metadata: {}
|
147
|
+
post_install_message:
|
148
|
+
rdoc_options: []
|
149
|
+
require_paths:
|
150
|
+
- lib
|
151
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
152
|
+
requirements:
|
153
|
+
- - ">="
|
154
|
+
- !ruby/object:Gem::Version
|
155
|
+
version: '0'
|
156
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
157
|
+
requirements:
|
158
|
+
- - ">="
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '0'
|
161
|
+
requirements: []
|
162
|
+
rubyforge_project:
|
163
|
+
rubygems_version: 2.2.2
|
164
|
+
signing_key:
|
165
|
+
specification_version: 4
|
166
|
+
summary: Sinatra extension for Tencent Wechat
|
167
|
+
test_files:
|
168
|
+
- spec/spec_helper.rb
|
169
|
+
- spec/wechat_spec.rb
|