wei-backend 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +21 -0
- data/Gemfile.lock +90 -0
- data/README.md +23 -0
- data/Rakefile +23 -0
- data/examples/waiting_bus/ai_bang_client.rb +17 -0
- data/examples/waiting_bus/app.rb +25 -0
- data/examples/waiting_bus/bus_helper.rb +19 -0
- data/examples/waiting_bus/config.ru +12 -0
- data/examples/waiting_bus/config/development.yml +7 -0
- data/examples/waiting_bus/config/production.yml +7 -0
- data/examples/waiting_bus/config/test.yml +7 -0
- data/examples/waiting_bus/spec/ai_bang_client_spec.rb +22 -0
- data/examples/waiting_bus/spec/bus_helper_spec.rb +19 -0
- data/examples/waiting_bus/spec/fixtures/bus_lines_response.json +44 -0
- data/lib/wei-backend.rb +3 -0
- data/lib/wei-backend/base.rb +76 -0
- data/lib/wei-backend/main.rb +23 -0
- data/lib/wei-backend/utils.rb +12 -0
- data/lib/wei-backend/version.rb +3 -0
- data/lib/wei-backend/wei-templates/image_text.haml +13 -0
- data/lib/wei-backend/wei-templates/text.haml +6 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/wei_backend_spec.rb +52 -0
- data/spec/wei_backend_utils_spec.rb +7 -0
- data/spec/weixin_message_handler_spec.rb +23 -0
- metadata +86 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 89c1731dc45e222e2af055b01d36d25ad34cfa71
|
4
|
+
data.tar.gz: 0d958cec757c981191d472fc29aebdac9bc42c43
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bd00720f925a9843733ac78cd680b55c2846a6d9d72555d7a3e7e40b814cb12ae468494bdb37c7d8eec5d177bd137e4b8f5db04ada1fa247616ec6be3dd34f16
|
7
|
+
data.tar.gz: 8c680f884c42ab2c0840bb53ef53b093566f95dbb2d761aacae32755c7ac9f2cfa8c93cf6989b0c81be0a506cb03402626413c27cae3f685f887a6724d8a623f
|
data/Gemfile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
source 'http://rubygems.org'
|
2
|
+
|
3
|
+
gem 'json'
|
4
|
+
gem 'haml'
|
5
|
+
gem 'sinatra'
|
6
|
+
gem 'nokogiri'
|
7
|
+
gem 'capistrano'
|
8
|
+
gem 'httparty'
|
9
|
+
gem 'thin'
|
10
|
+
gem 'hash_validator'
|
11
|
+
|
12
|
+
group :test do
|
13
|
+
gem 'rspec'
|
14
|
+
gem 'rspec-html-matchers'
|
15
|
+
gem 'rack-test'
|
16
|
+
end
|
17
|
+
|
18
|
+
group :development do
|
19
|
+
gem 'rerun'
|
20
|
+
gem 'shotgun'
|
21
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
capistrano (3.0.1)
|
5
|
+
i18n
|
6
|
+
rake (>= 10.0.0)
|
7
|
+
sshkit (>= 0.0.23)
|
8
|
+
daemons (1.1.9)
|
9
|
+
diff-lcs (1.2.5)
|
10
|
+
eventmachine (1.0.3)
|
11
|
+
ffi (1.9.3)
|
12
|
+
haml (4.0.4)
|
13
|
+
tilt
|
14
|
+
hash_validator (0.2.7)
|
15
|
+
httparty (0.12.0)
|
16
|
+
json (~> 1.8)
|
17
|
+
multi_xml (>= 0.5.2)
|
18
|
+
i18n (0.6.5)
|
19
|
+
json (1.8.1)
|
20
|
+
listen (1.0.3)
|
21
|
+
rb-fsevent (>= 0.9.3)
|
22
|
+
rb-inotify (>= 0.9)
|
23
|
+
rb-kqueue (>= 0.2)
|
24
|
+
mini_portile (0.5.2)
|
25
|
+
multi_xml (0.5.5)
|
26
|
+
net-scp (1.1.2)
|
27
|
+
net-ssh (>= 2.6.5)
|
28
|
+
net-ssh (2.7.0)
|
29
|
+
nokogiri (1.6.0)
|
30
|
+
mini_portile (~> 0.5.0)
|
31
|
+
rack (1.5.2)
|
32
|
+
rack-protection (1.5.1)
|
33
|
+
rack
|
34
|
+
rack-test (0.6.2)
|
35
|
+
rack (>= 1.0)
|
36
|
+
rake (10.1.0)
|
37
|
+
rb-fsevent (0.9.3)
|
38
|
+
rb-inotify (0.9.2)
|
39
|
+
ffi (>= 0.5.0)
|
40
|
+
rb-kqueue (0.2.0)
|
41
|
+
ffi (>= 0.5.0)
|
42
|
+
rerun (0.8.2)
|
43
|
+
listen (~> 1.0.3)
|
44
|
+
rspec (2.14.1)
|
45
|
+
rspec-core (~> 2.14.0)
|
46
|
+
rspec-expectations (~> 2.14.0)
|
47
|
+
rspec-mocks (~> 2.14.0)
|
48
|
+
rspec-core (2.14.7)
|
49
|
+
rspec-expectations (2.14.4)
|
50
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
51
|
+
rspec-html-matchers (0.4.3)
|
52
|
+
nokogiri (>= 1.4.4)
|
53
|
+
rspec (>= 2.0.0)
|
54
|
+
rspec-mocks (2.14.4)
|
55
|
+
shotgun (0.9)
|
56
|
+
rack (>= 1.0)
|
57
|
+
sinatra (1.4.4)
|
58
|
+
rack (~> 1.4)
|
59
|
+
rack-protection (~> 1.4)
|
60
|
+
tilt (~> 1.3, >= 1.3.4)
|
61
|
+
sshkit (1.2.0)
|
62
|
+
net-scp (>= 1.1.2)
|
63
|
+
net-ssh
|
64
|
+
term-ansicolor
|
65
|
+
term-ansicolor (1.2.2)
|
66
|
+
tins (~> 0.8)
|
67
|
+
thin (1.6.1)
|
68
|
+
daemons (>= 1.0.9)
|
69
|
+
eventmachine (>= 1.0.0)
|
70
|
+
rack (>= 1.0.0)
|
71
|
+
tilt (1.4.1)
|
72
|
+
tins (0.13.1)
|
73
|
+
|
74
|
+
PLATFORMS
|
75
|
+
ruby
|
76
|
+
|
77
|
+
DEPENDENCIES
|
78
|
+
capistrano
|
79
|
+
haml
|
80
|
+
hash_validator
|
81
|
+
httparty
|
82
|
+
json
|
83
|
+
nokogiri
|
84
|
+
rack-test
|
85
|
+
rerun
|
86
|
+
rspec
|
87
|
+
rspec-html-matchers
|
88
|
+
shotgun
|
89
|
+
sinatra
|
90
|
+
thin
|
data/README.md
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
微信公众平台后台框架
|
2
|
+
========
|
3
|
+
## Ruby version:
|
4
|
+
**ruby-2.0.0-p247**
|
5
|
+
|
6
|
+
可以使用[rvm](http://rvm.io)安装:
|
7
|
+
|
8
|
+
rvm install ruby-2.0.0-p247
|
9
|
+
|
10
|
+
## 如何定制
|
11
|
+
* 启动
|
12
|
+
|
13
|
+
git clone https://github.com/charleyw/weixin-sinatra.git
|
14
|
+
bundle install
|
15
|
+
rake dev:start
|
16
|
+
* 所有的业务逻辑可以查看`lib/wei_xin_request_handler`
|
17
|
+
|
18
|
+
访问`http://localhost:9393?echostr=test`,页面会显示**echostr**的值
|
19
|
+
|
20
|
+
## 如何部署
|
21
|
+
git clone https://github.com/charleyw/weixin-sinatra.git
|
22
|
+
bundle install
|
23
|
+
rake prod:start
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
namespace :dev do
|
2
|
+
desc 'start application in local env'
|
3
|
+
task :start do
|
4
|
+
sh 'bundle exec shotgun config.ru'
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
namespace :prod do
|
9
|
+
desc 'start production'
|
10
|
+
task :start do
|
11
|
+
sh 'bundle exec thin -d -p 80 start'
|
12
|
+
end
|
13
|
+
|
14
|
+
desc 'stop production'
|
15
|
+
task :stop do
|
16
|
+
sh 'bundle exec thin stop'
|
17
|
+
end
|
18
|
+
|
19
|
+
desc 'restart production'
|
20
|
+
task :restart do
|
21
|
+
sh 'bundle exec thin restart'
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
class AiBangClient
|
5
|
+
def initialize(api_base_url, api_key)
|
6
|
+
%w(lines transfer stats).each do |category|
|
7
|
+
instance_variable_set("@#{category}_api_url", api_base_url + "/#{category}?app_key=#{api_key}&alt=json")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
def bus_lines(city, query)
|
11
|
+
encoded_city = URI.encode city
|
12
|
+
encoded_query = URI.encode query
|
13
|
+
api_url = "#{@lines_api_url}&city=#{encoded_city}&q=#{encoded_query}"
|
14
|
+
result = HTTParty.get(api_url).parsed_response["lines"]
|
15
|
+
result.has_key?('line') ? result['line'] : []
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
require './ai_bang_client'
|
4
|
+
require './bus_helper'
|
5
|
+
require_relative '../../lib/wei-backend'
|
6
|
+
|
7
|
+
CONFIG = YAML.load_file("./config/#{ENV['RACK_ENV']}.yml")
|
8
|
+
|
9
|
+
on_text do
|
10
|
+
aibang_client = AiBangClient.new CONFIG[:ai_bang_api], CONFIG[:ai_bang_api_key]
|
11
|
+
bus_helper = BusHelper.new aibang_client
|
12
|
+
bus_helper.bus_lines_running_time(params[:Content])
|
13
|
+
end
|
14
|
+
|
15
|
+
on_event do
|
16
|
+
if params[:Content].eql? 'subscribe'
|
17
|
+
CONFIG[:subscribe_message]
|
18
|
+
else
|
19
|
+
CONFIG[:unsubscribe_message]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
on_voice do
|
24
|
+
puts 'on voice message'
|
25
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class BusHelper
|
2
|
+
def initialize(ai_bang_client)
|
3
|
+
@ai_bang_client = ai_bang_client
|
4
|
+
end
|
5
|
+
|
6
|
+
def bus_lines_running_time(query)
|
7
|
+
lines = query.scan(/\w*\d+/)
|
8
|
+
bus_num = lines[0].strip if lines.length > 0
|
9
|
+
city = query.sub(/#{bus_num}.*/, '').strip
|
10
|
+
city = "西安" if city.empty?
|
11
|
+
bus_lines_results = @ai_bang_client.bus_lines(city, bus_num)
|
12
|
+
result = "";
|
13
|
+
bus_lines_results.each do |line|
|
14
|
+
running_time = line["info"].scan(/\d{1,2}[::]\d{1,2}-{1,2}\d{1,2}[::]\d{1,2}/)[0]
|
15
|
+
result += line["name"] + " " + running_time + "\n\n" if !running_time.nil?
|
16
|
+
end
|
17
|
+
result
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require_relative '../ai_bang_client'
|
3
|
+
|
4
|
+
class MockResponse
|
5
|
+
def parsed_response
|
6
|
+
JSON.parse IO.read('spec/fixtures/bus_lines_response.json')
|
7
|
+
end
|
8
|
+
end
|
9
|
+
module HTTParty
|
10
|
+
def self.get(*args, &block)
|
11
|
+
MockResponse.new
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe 'aibang api client' do
|
16
|
+
|
17
|
+
it 'should bus lines as json object when search for line 6' do
|
18
|
+
aibang_client = AiBangClient.new 'http://localhost/bus', 'api_key'
|
19
|
+
aibang_client.bus_lines("city", "query").to_s.should include "6\\u8DEF(\\u6021\\u56ED\\u8DEF\\u5317\\u53E3-\\u706B\\u8F66\\u7AD9\\u897F)"
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require_relative '../ai_bang_client'
|
3
|
+
require_relative '../bus_helper'
|
4
|
+
|
5
|
+
describe "bus helper" do
|
6
|
+
|
7
|
+
it "should return bus running time when user search for line 6 running time and city is xi'an" do
|
8
|
+
aibang_client = double(AiBangClient, :bus_lines => (JSON.parse IO.read('spec/fixtures/bus_lines_response.json'))["lines"]["line"])
|
9
|
+
bus_helper = BusHelper.new aibang_client
|
10
|
+
bus_helper.bus_lines_running_time("西安6路").should include "6\u8def(\u706b\u8f66\u7ad9\u897f-\u6021\u56ed\u8def\u5317\u53e3) 6:00-20:30"
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should return bus running time when user search for line k700 running time and city is xi'an" do
|
14
|
+
aibang_client = double(AiBangClient, :bus_lines => (JSON.parse IO.read('spec/fixtures/bus_lines_response.json'))["lines"]["line"])
|
15
|
+
bus_helper = BusHelper.new aibang_client
|
16
|
+
bus_helper.bus_lines_running_time("西安k700路").should include "6\u8def(\u706b\u8f66\u7ad9\u897f-\u6021\u56ed\u8def\u5317\u53e3) 6:00-20:30"
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
{
|
2
|
+
"result_num": "6",
|
3
|
+
"web_url": "http:\/\/bus.aibang.com",
|
4
|
+
"wap_url": "http:\/\/wap.aibang.com",
|
5
|
+
"lines": {
|
6
|
+
"line": [{
|
7
|
+
"name": "6\u8def(\u6021\u56ed\u8def\u5317\u53e3-\u706b\u8f66\u7ad9\u897f)",
|
8
|
+
"info": "\u5e02\u533a\u7ebf\u8def; \u706b\u8f66\u7ad9\u897f-\u6021\u56ed\u8def\u5317\u53e3 6:00-20:30,\u6021\u56ed\u8def\u5317\u53e3\u2014\u706b\u8f66\u7ad9\u897f 6:00-20:30; 1\u5143\u4e00\u7968\u5236 \u666e\u901a\u53610.5\u5143 \u5b66\u751f\u53610.3\u5143\u3002",
|
9
|
+
"stats": "\u6021\u56ed\u8def\u5317\u53e3;\u60a6\u56ed\u8def\u5317\u53e3;\u9526\u4e1a\u4e8c\u8def;\u4e2d\u5174\u901a\u8baf;\u5357\u4e09\u73af;\u9526\u4e1a\u8def;\u7701\u6e38\u6cf3\u4e2d\u5fc3;\u897f\u4e07\u8def\u53e3;\u6728\u5854\u5be8;\u5e02\u5efa\u56db\u516c\u53f8;\u7535\u5b50\u4e8c\u8def\u897f\u53e3;\u7535\u5b50\u5546\u57ce;\u6c99\u4e95\u6751;\u897f\u659c\u4e03\u8def;\u516c\u4ea4\u4e94\u516c\u53f8;\u897f\u659c\u516d\u8def;\u592a\u767d\u8def\u7acb\u4ea4;\u8fb9\u5bb6\u6751;\u9ec4\u96c1\u6751;\u5f20\u5bb6\u6751;\u542b\u5149\u95e8;\u5c0f\u5357\u95e8(\u4e34\u65f6\u53d6\u6d88);\u6731\u96c0\u95e8(\u4e34\u65f6\u53d6\u6d88);\u5357\u95e8(\u4e34\u65f6\u53d6\u6d88);\u949f\u697c;\u5317\u5927\u8857;\u5317\u95e8;\u897f\u95f8\u53e3\u5357\u53e3;\u706b\u8f66\u7ad9\u897f",
|
10
|
+
"stat_xys": "",
|
11
|
+
"xys": ""
|
12
|
+
}, {
|
13
|
+
"name": "6\u8def(\u706b\u8f66\u7ad9\u897f-\u6021\u56ed\u8def\u5317\u53e3)",
|
14
|
+
"info": "\u5e02\u533a\u7ebf\u8def; \u706b\u8f66\u7ad9\u897f-\u6021\u56ed\u8def\u5317\u53e3 6:00-20:30,\u6021\u56ed\u8def\u5317\u53e3\u2014\u706b\u8f66\u7ad9\u897f 6:00-20:30; 1\u5143\u4e00\u7968\u5236 \u666e\u901a\u53610.5\u5143 \u5b66\u751f\u53610.3\u5143\u3002",
|
15
|
+
"stats": "\u706b\u8f66\u7ad9\u897f;\u897f\u95f8\u53e3\u5357\u53e3;\u5317\u95e8;\u5317\u5927\u8857;\u949f\u697c;\u5357\u95e8(\u4e34\u65f6\u53d6\u6d88);\u6731\u96c0\u95e8(\u4e34\u65f6\u53d6\u6d88);\u5c0f\u5357\u95e8(\u4e34\u65f6\u53d6\u6d88);\u542b\u5149\u95e8;\u5f20\u5bb6\u6751;\u9ec4\u96c1\u6751;\u8fb9\u5bb6\u6751;\u592a\u767d\u8def\u7acb\u4ea4;\u897f\u659c\u516d\u8def;\u516c\u4ea4\u4e94\u516c\u53f8;\u897f\u659c\u4e03\u8def;\u6c99\u4e95\u6751;\u7535\u5b50\u5546\u57ce;\u7535\u5b50\u4e8c\u8def\u897f\u53e3;\u5e02\u5efa\u56db\u516c\u53f8;\u6728\u5854\u5be8;\u897f\u4e07\u8def\u53e3;\u7701\u6e38\u6cf3\u4e2d\u5fc3;\u9526\u4e1a\u8def;\u5357\u4e09\u73af;\u4e2d\u5174\u901a\u8baf;\u9526\u4e1a\u4e8c\u8def;\u60a6\u56ed\u8def\u5317\u53e3;\u6021\u56ed\u8def\u5317\u53e3",
|
16
|
+
"stat_xys": "",
|
17
|
+
"xys": ""
|
18
|
+
}, {
|
19
|
+
"name": "\u6e386(\u5510\u82d1-\u706b\u8f66\u7ad9)",
|
20
|
+
"info": "\u65c5\u6e38\u7ebf\u8def; \u706b\u8f66\u7ad9\u4e1c\u5e7f\u573a--\u5510\u82d1 6:40--19:30; \u8d77\u6b655\u89d2\uff0c5\u89d2\u8fdb\u4f4d\uff0c\u5168\u7a0b3.5\u5143\u3002",
|
21
|
+
"stats": "\u5510\u82d1;\u6797\u5e26\u8def\u91c7\u6458\u56ed;\u66f2\u6c5f\u751f\u6001\u82b1\u56ed;\u9ec4\u6e20\u5934\u6751;\u77f3\u7f8a\u519c\u5e84;\u5b5f\u6751;\u7406\u5de5\u5927\u66f2\u6c5f\u6821\u533a;\u9752\u9f99\u5bfa;\u94c1\u7089\u5e99;\u65b0\u7586\u4e09\u6240;\u89c2\u97f3\u5e99;\u5317\u6c60\u5934;\u897f\u5f71\u8def;\u5e02\u59d4\u515a\u6821;\u5927\u96c1\u5854;\u7fe0\u534e\u8def;\u5c0f\u5be8;\u5927\u5174\u5584\u5bfa(\u53d6\u6d88);\u957f\u5b89\u7acb\u4ea4(\u897f\u5b89\u97f3\u4e50\u5b66\u9662);\u7701\u4f53\u80b2\u573a(\u9655\u897f\u7701\u56fe\u4e66\u9986;\u8349\u573a\u5761;\u5357\u7a0d\u95e8;\u5357\u95e8;\u6587\u660c\u95e8;\u548c\u5e73\u95e8(\u53d6\u6d88);\u534e\u590f\u94f6\u884c;\u5927\u5dee\u5e02;\u6c11\u4e50\u56ed;\u4e94\u8def\u53e3;\u706b\u8f66\u7ad9",
|
22
|
+
"stat_xys": "",
|
23
|
+
"xys": ""
|
24
|
+
}, {
|
25
|
+
"name": "\u6e386(\u706b\u8f66\u7ad9-\u5510\u82d1)",
|
26
|
+
"info": "\u65c5\u6e38\u7ebf\u8def; \u706b\u8f66\u7ad9\u4e1c\u5e7f\u573a--\u5510\u82d1 6:40--19:30; \u8d77\u6b655\u89d2\uff0c5\u89d2\u8fdb\u4f4d\uff0c\u5168\u7a0b3.5\u5143\u3002",
|
27
|
+
"stats": "\u706b\u8f66\u7ad9;\u4e94\u8def\u53e3;\u6c11\u4e50\u56ed;\u5927\u5dee\u5e02;\u534e\u590f\u94f6\u884c;\u548c\u5e73\u95e8;\u6587\u660c\u95e8;\u5357\u95e8;\u5357\u7a0d\u95e8;\u8349\u573a\u5761;\u7701\u4f53\u80b2\u573a(\u9655\u897f\u7701\u56fe\u4e66\u9986;\u957f\u5b89\u7acb\u4ea4(\u897f\u5b89\u97f3\u4e50\u5b66\u9662);\u5927\u5174\u5584\u5bfa(\u53d6\u6d88);\u5c0f\u5be8;\u7fe0\u534e\u8def;\u5927\u96c1\u5854;\u5927\u96c1\u5854\u5357\u5e7f\u573a;\u96c1\u5f15\u8def;\u5e02\u59d4\u515a\u6821;\u897f\u5f71\u8def;\u5317\u6c60\u5934;\u89c2\u97f3\u5e99;\u65b0\u7586\u4e09\u6240;\u94c1\u7089\u5e99;\u9752\u9f99\u5bfa;\u7406\u5de5\u5927\u66f2\u6c5f\u6821\u533a;\u5b5f\u6751;\u77f3\u7f8a\u519c\u5e84;\u9ec4\u6e20\u5934\u6751;\u66f2\u6c5f\u751f\u6001\u82b1\u56ed;\u6797\u5e26\u8def\u91c7\u6458\u56ed;\u5510\u82d1",
|
28
|
+
"stat_xys": "",
|
29
|
+
"xys": ""
|
30
|
+
}, {
|
31
|
+
"name": "\u673a\u573a\u5927\u5df46\u53f7\u7ebf(\u54b8\u9633\u673a\u573a-\u5f69\u8679\u5bbe\u9986)",
|
32
|
+
"info": "\u673a\u573a:09:00-21:00;\u5f69\u8679\u5bbe\u9986:07:00-19:00;\u8d39\u7528:15\u5143+1\u5143\uff08\u71c3\u6cb9\u9644\u52a0\u8d39\uff09\/\u4eba;",
|
33
|
+
"stats": "\u54b8\u9633\u673a\u573a;\u54b8\u9633\u706b\u8f66\u7ad9;\u6e2d\u57ce\u4e2d\u5b66;\u54b8\u9633\u5e02\u653f\u5e9c;\u6c11\u751f\u5546\u53a6;\u5f69\u8679\u5bbe\u9986",
|
34
|
+
"stat_xys": "",
|
35
|
+
"xys": ""
|
36
|
+
}, {
|
37
|
+
"name": "\u673a\u573a\u5927\u5df46\u53f7\u7ebf(\u5f69\u8679\u5bbe\u9986-\u54b8\u9633\u673a\u573a)",
|
38
|
+
"info": "\u673a\u573a:09:00-21:00;\u5f69\u8679\u5bbe\u9986:07:00-19:00;\u8d39\u7528:15\u5143+1\u5143\uff08\u71c3\u6cb9\u9644\u52a0\u8d39\uff09\/\u4eba;",
|
39
|
+
"stats": "\u5f69\u8679\u5bbe\u9986;\u6c11\u751f\u5546\u53a6;\u54b8\u9633\u5e02\u653f\u5e9c;\u6e2d\u57ce\u4e2d\u5b66;\u54b8\u9633\u706b\u8f66\u7ad9;\u54b8\u9633\u673a\u573a",
|
40
|
+
"stat_xys": "",
|
41
|
+
"xys": ""
|
42
|
+
}]
|
43
|
+
}
|
44
|
+
}
|
data/lib/wei-backend.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
module WeiBackend
|
2
|
+
class MessageDispatcher
|
3
|
+
attr_accessor :params
|
4
|
+
|
5
|
+
def on message_type, params
|
6
|
+
@params = params
|
7
|
+
results = send(:"handle_#{message_type.downcase}_message")
|
8
|
+
create_model results
|
9
|
+
end
|
10
|
+
|
11
|
+
def create_model data
|
12
|
+
data.is_a?(Hash) || data.is_a?(Array) ? image_text_message(data) : text_message(data)
|
13
|
+
end
|
14
|
+
|
15
|
+
def text_message(data)
|
16
|
+
{
|
17
|
+
:format => 'text',
|
18
|
+
:model => {:content => data}.merge(account_info)
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def image_text_message model
|
23
|
+
{
|
24
|
+
:format => 'image_text',
|
25
|
+
:model => {
|
26
|
+
:article_count => model.is_a?(Array) ? model.length : 1,
|
27
|
+
:articles => model.is_a?(Array) ? model : [model]
|
28
|
+
}.merge(account_info)
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def account_info
|
33
|
+
{
|
34
|
+
:myAccount => params[:ToUserName],
|
35
|
+
:userAccount => params[:FromUserName],
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.on_text &block
|
40
|
+
define_method(:handle_text_message, &block)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.on_event &block
|
44
|
+
define_method(:handle_event_message, &block)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.on_voice &block
|
48
|
+
define_method(:handle_voice_message, &block)
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.on_location &block
|
52
|
+
define_method(:handle_location_message, &block)
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
module Delegator
|
59
|
+
def self.delegate(*methods)
|
60
|
+
methods.each do |method_name|
|
61
|
+
define_method(method_name) do |*args, &block|
|
62
|
+
Delegator.target.send(method_name, *args, &block)
|
63
|
+
end
|
64
|
+
private method_name
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
delegate :on_text, :on_event, :on_voice, :on_location
|
69
|
+
|
70
|
+
class << self
|
71
|
+
attr_accessor :target
|
72
|
+
end
|
73
|
+
|
74
|
+
self.target = MessageDispatcher
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
get '/' do
|
5
|
+
params[:echostr]
|
6
|
+
end
|
7
|
+
|
8
|
+
post '/' do
|
9
|
+
request.body.rewind
|
10
|
+
weixin_params = WeiBackend::Utils.parse_params request.body.read
|
11
|
+
handler = WeiBackend::MessageDispatcher.new
|
12
|
+
results = handler.on weixin_params[:MsgType], weixin_params
|
13
|
+
|
14
|
+
haml results[:format].to_sym, :views => File.dirname(__FILE__)+'/wei-templates', :locals => results[:model]
|
15
|
+
end
|
16
|
+
|
17
|
+
helpers do
|
18
|
+
def cdata content
|
19
|
+
"<![CDATA[#{content}]]>"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
extend WeiBackend::Delegator
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module WeiBackend
|
2
|
+
module Utils
|
3
|
+
def self.parse_params(request_body)
|
4
|
+
doc = Nokogiri::XML::Document.parse request_body
|
5
|
+
result = {}
|
6
|
+
doc.at_css('xml').element_children.each do |child|
|
7
|
+
result[child.name.to_sym] = child.child.text
|
8
|
+
end
|
9
|
+
result
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
%xml
|
2
|
+
%ToUserName= cdata userAccount
|
3
|
+
%FromUserName= cdata myAccount
|
4
|
+
%CreateTime= cdata Time.now.to_i
|
5
|
+
%MsgType= cdata 'news'
|
6
|
+
%ArticleCount= article_count
|
7
|
+
%Articles
|
8
|
+
- articles.each do |articles|
|
9
|
+
%item
|
10
|
+
%Title= cdata articles[:title]
|
11
|
+
%Description= cdata articles[:description]
|
12
|
+
%PicUrl= cdata articles[:picture_url]
|
13
|
+
%Url= cdata articles[:url]
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
ENV['RACK_ENV'] = 'test'
|
2
|
+
|
3
|
+
require 'rack/test'
|
4
|
+
require 'rack'
|
5
|
+
require 'rack-protection'
|
6
|
+
require 'rspec'
|
7
|
+
require 'rspec-html-matchers'
|
8
|
+
require './lib/wei-backend'
|
9
|
+
|
10
|
+
TEXT_MESSAGE_REQUEST='<xml><ToUserName><![CDATA[toUser]]></ToUserName><FromUserName>'+
|
11
|
+
'<![CDATA[fromUser]]></FromUserName><CreateTime>1348831860</CreateTime><MsgType><![CDATA[text]]></MsgType>'+
|
12
|
+
'<Content><![CDATA[this is a test]]></Content><MsgId>1234567890123456</MsgId></xml>'
|
13
|
+
|
14
|
+
EVENT_MESSAGE_REQUEST='<xml><ToUserName><![CDATA[toUser]]></ToUserName>'+
|
15
|
+
'<FromUserName><![CDATA[fromUser]]></FromUserName><CreateTime>123456789</CreateTime>'+
|
16
|
+
'<MsgType><![CDATA[event]]></MsgType><Event><![CDATA[subscribe]]></Event></xml>'
|
17
|
+
|
18
|
+
PARSED_PARAMS={
|
19
|
+
:ToUserName => 'toUser',
|
20
|
+
:FromUserName => 'fromUser',
|
21
|
+
:CreateTime => '1348831860',
|
22
|
+
:MsgType => 'text',
|
23
|
+
:Content => 'this is a test',
|
24
|
+
:MsgId => '1234567890123456'
|
25
|
+
}
|
26
|
+
|
27
|
+
def app
|
28
|
+
Sinatra::Application
|
29
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
describe 'app' do
|
3
|
+
include Rack::Test::Methods
|
4
|
+
|
5
|
+
it 'should echo string when request with echostr parameter' do
|
6
|
+
get '/?echostr=test'
|
7
|
+
last_response.body.should include 'test'
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should return user defined text message when receive a text request message' do
|
11
|
+
WeiBackend::MessageDispatcher.on_text do
|
12
|
+
'hello world'
|
13
|
+
end
|
14
|
+
|
15
|
+
post '/', TEXT_MESSAGE_REQUEST, 'CONTENT_TYPE' => 'text/xml'
|
16
|
+
last_response.body.should include '<ToUserName><![CDATA[fromUser]]></ToUserName>'
|
17
|
+
last_response.body.should include '<Content><![CDATA[hello world]]></Content>'
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should return user defined news message when receive a text request message' do
|
21
|
+
WeiBackend::MessageDispatcher.on_text do
|
22
|
+
{:title => 'title', :description => 'desc', :picture_url => 'pic url', :url => 'url'}
|
23
|
+
end
|
24
|
+
|
25
|
+
post '/', TEXT_MESSAGE_REQUEST, 'CONTENT_TYPE' => 'text/xml'
|
26
|
+
last_response.body.should include '<ToUserName><![CDATA[fromUser]]></ToUserName>'
|
27
|
+
last_response.body.should include '<Title><![CDATA[title]]></Title>'
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should return news message when receive a text request message and user defined a multi-news' do
|
31
|
+
WeiBackend::MessageDispatcher.on_text do
|
32
|
+
[{:title => 'title', :description => 'desc', :picture_url => 'pic url', :url => 'url'},
|
33
|
+
{:title => 'title1', :description => 'desc1', :picture_url => 'pic url1', :url => 'url1'}]
|
34
|
+
end
|
35
|
+
|
36
|
+
post '/', TEXT_MESSAGE_REQUEST, 'CONTENT_TYPE' => 'text/xml'
|
37
|
+
last_response.body.should include '<ToUserName><![CDATA[fromUser]]></ToUserName>'
|
38
|
+
last_response.body.should include '<Title><![CDATA[title]]></Title>'
|
39
|
+
last_response.body.should include '<Title><![CDATA[title1]]></Title>'
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should echo event request message when receive a event message' do
|
43
|
+
WeiBackend::MessageDispatcher.on_event do
|
44
|
+
'hello event'
|
45
|
+
end
|
46
|
+
|
47
|
+
post '/', EVENT_MESSAGE_REQUEST, 'CONTENT_TYPE' => 'text/xml'
|
48
|
+
last_response.body.should include '<ToUserName><![CDATA[fromUser]]></ToUserName>'
|
49
|
+
last_response.body.should include '<Content><![CDATA[hello event]]></Content>'
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
require_relative '../lib/wei-backend/base'
|
3
|
+
|
4
|
+
describe 'weixin message handler' do
|
5
|
+
it 'should call text message handler when invoke handle with text message' do
|
6
|
+
WeiBackend::MessageDispatcher.any_instance.should_receive(:handle_text_message) { 'results' }
|
7
|
+
dispatcher = WeiBackend::MessageDispatcher.new
|
8
|
+
dispatcher.on('text', PARSED_PARAMS).should == {:format => 'text', :model => {:content => 'results', :myAccount=>"toUser", :userAccount=>"fromUser"}}
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should return text format result when model is a string' do
|
12
|
+
dispatcher = WeiBackend::MessageDispatcher.new
|
13
|
+
dispatcher.params=PARSED_PARAMS
|
14
|
+
dispatcher.create_model('text results').should == {:format => 'text', :model => {:content => 'text results', :myAccount=>"toUser", :userAccount=>"fromUser"}}
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should return image text format result when model is a hash' do
|
18
|
+
dispatcher = WeiBackend::MessageDispatcher.new
|
19
|
+
dispatcher.params=PARSED_PARAMS
|
20
|
+
dispatcher.create_model({:url => "http://adc/"}).should == {:format=>"image_text", :model=>{:article_count=>1, :articles=>[{:url=>"http://adc/"}], :myAccount=>"toUser", :userAccount=>"fromUser"}}
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: wei-backend
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Wang Chao
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-12-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: sinatra
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.4.4
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.4.4
|
27
|
+
description: wei-backend is a DSL for quickly creating weixin open platform backend
|
28
|
+
system.
|
29
|
+
email: cwang8023@gmail.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- Gemfile
|
35
|
+
- Gemfile.lock
|
36
|
+
- README.md
|
37
|
+
- Rakefile
|
38
|
+
- examples/waiting_bus/ai_bang_client.rb
|
39
|
+
- examples/waiting_bus/app.rb
|
40
|
+
- examples/waiting_bus/bus_helper.rb
|
41
|
+
- examples/waiting_bus/config.ru
|
42
|
+
- examples/waiting_bus/config/development.yml
|
43
|
+
- examples/waiting_bus/config/production.yml
|
44
|
+
- examples/waiting_bus/config/test.yml
|
45
|
+
- examples/waiting_bus/spec/ai_bang_client_spec.rb
|
46
|
+
- examples/waiting_bus/spec/bus_helper_spec.rb
|
47
|
+
- examples/waiting_bus/spec/fixtures/bus_lines_response.json
|
48
|
+
- lib/wei-backend.rb
|
49
|
+
- lib/wei-backend/base.rb
|
50
|
+
- lib/wei-backend/main.rb
|
51
|
+
- lib/wei-backend/utils.rb
|
52
|
+
- lib/wei-backend/version.rb
|
53
|
+
- lib/wei-backend/wei-templates/image_text.haml
|
54
|
+
- lib/wei-backend/wei-templates/text.haml
|
55
|
+
- spec/spec_helper.rb
|
56
|
+
- spec/wei_backend_spec.rb
|
57
|
+
- spec/wei_backend_utils_spec.rb
|
58
|
+
- spec/weixin_message_handler_spec.rb
|
59
|
+
homepage:
|
60
|
+
licenses:
|
61
|
+
- MIT
|
62
|
+
metadata: {}
|
63
|
+
post_install_message:
|
64
|
+
rdoc_options: []
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - '>='
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
requirements: []
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 2.0.6
|
80
|
+
signing_key:
|
81
|
+
specification_version: 4
|
82
|
+
summary: Best DSL for weixin development
|
83
|
+
test_files:
|
84
|
+
- spec/wei_backend_spec.rb
|
85
|
+
- spec/wei_backend_utils_spec.rb
|
86
|
+
- spec/weixin_message_handler_spec.rb
|