weixin_rails_middleware 1.2.2 → 1.2.3
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 +4 -4
- data/app/controllers/weixin_rails_middleware/weixin_controller.rb +13 -1
- data/config/routes.rb +1 -1
- data/lib/generators/templates/weixin_controller.rb +49 -45
- data/lib/weixin_rails_middleware.rb +4 -1
- data/lib/weixin_rails_middleware/adapter/multiple_public_account.rb +18 -20
- data/lib/weixin_rails_middleware/adapter/single_public_account.rb +17 -19
- data/lib/weixin_rails_middleware/adapter/weixin_adapter.rb +53 -55
- data/lib/weixin_rails_middleware/helpers/reply_weixin_message_helper.rb +16 -4
- data/lib/weixin_rails_middleware/version.rb +1 -1
- metadata +2 -17
- data/lib/weixin_rails_middleware/adapter.rb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c8b512a640cd72d5b60a38fc9ecd29cc36c045c1
|
4
|
+
data.tar.gz: e9ce1f55233f8fa3e7c9ab84276001eac83d6d8b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e95b2b9e4c0668bef289e2f2dd1b9f5b125e11725bf0fcdfcca423f433a1f204319ac829a1438a4f0dd813142fdc81ce7ad92b4d7088f06c252834473f89ee20
|
7
|
+
data.tar.gz: e750d71896700712fd8609987f33086155f1bd46d204b8763ca91e971f5b569cbae972f82b171ca5e033634c48e3f32e465bf55bcad53c2d13446360b6599399
|
@@ -5,6 +5,7 @@ module WeixinRailsMiddleware
|
|
5
5
|
skip_before_filter :verify_authenticity_token
|
6
6
|
before_filter :initialize_adapter, :check_weixin_legality, only: [:index, :reply]
|
7
7
|
before_filter :set_weixin_public_account, :set_weixin_message, only: :reply
|
8
|
+
before_filter :set_keyword, only: :reply
|
8
9
|
|
9
10
|
def index
|
10
11
|
end
|
@@ -15,7 +16,7 @@ module WeixinRailsMiddleware
|
|
15
16
|
protected
|
16
17
|
|
17
18
|
def initialize_adapter
|
18
|
-
@weixin_adapter ||=
|
19
|
+
@weixin_adapter ||= WexinAdapter.init_with(params)
|
19
20
|
end
|
20
21
|
|
21
22
|
def check_weixin_legality
|
@@ -36,5 +37,16 @@ module WeixinRailsMiddleware
|
|
36
37
|
@weixin_message ||= Message.factory(request.body.read)
|
37
38
|
end
|
38
39
|
|
40
|
+
def set_keyword
|
41
|
+
@keyword = @weixin_message.Content || # 文本消息
|
42
|
+
@weixin_message.EventKey || # 事件推送
|
43
|
+
@weixin_message.Recognition # 接收语音识别结果
|
44
|
+
end
|
45
|
+
|
46
|
+
# http://apidock.com/rails/ActionController/Base/default_url_options
|
47
|
+
def default_url_options(options={})
|
48
|
+
{ weichat_id: @weixin_message.FromUserName }
|
49
|
+
end
|
50
|
+
|
39
51
|
end
|
40
52
|
end
|
data/config/routes.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
# 1:
|
3
|
-
# @
|
4
|
-
#
|
5
|
-
# @weixin_public_account
|
2
|
+
# 1, @weixin_message: 获取微信所有参数.
|
3
|
+
# 2, @weixin_public_account: 如果配置了public_account_class选项,则会返回当前实例,否则返回nil.
|
4
|
+
# 3, @keyword: 目前微信只有这三种情况存在关键字: 文本消息, 事件推送, 接收语音识别结果
|
6
5
|
WeixinRailsMiddleware::WeixinController.class_eval do
|
7
|
-
before_filter :set_keyword, only: :reply
|
8
6
|
|
9
7
|
def reply
|
10
8
|
render xml: send("response_#{@weixin_message.MsgType}_message", {})
|
@@ -31,9 +29,9 @@ WeixinRailsMiddleware::WeixinController.class_eval do
|
|
31
29
|
# <PicUrl><![CDATA[this is a url]]></PicUrl>
|
32
30
|
# <MediaId><![CDATA[media_id]]></MediaId>
|
33
31
|
def response_image_message(options={})
|
34
|
-
@pic_url = @weixin_message.PicUrl
|
35
32
|
@media_id = @weixin_message.MediaId # 可以调用多媒体文件下载接口拉取数据。
|
36
|
-
|
33
|
+
@pic_url = @weixin_message.PicUrl # 也可以直接通过此链接下载图片, 建议使用carrierwave.
|
34
|
+
reply_image_message(generate_image(@media_id))
|
37
35
|
end
|
38
36
|
|
39
37
|
# <Title><![CDATA[公众平台官网链接]]></Title>
|
@@ -46,40 +44,14 @@ WeixinRailsMiddleware::WeixinController.class_eval do
|
|
46
44
|
reply_text_message("回复链接信息")
|
47
45
|
end
|
48
46
|
|
49
|
-
def response_event_message(options={})
|
50
|
-
event_type = @weixin_message.Event
|
51
|
-
case event_type
|
52
|
-
when "subscribe" # 关注公众账号
|
53
|
-
if @keyword.present?
|
54
|
-
# 扫描带参数二维码事件: 1. 用户未关注时,进行关注后的事件推送
|
55
|
-
return reply_text_message("扫描带参数二维码事件: 1. 用户未关注时,进行关注后的事件推送, keyword: #{@keyword}")
|
56
|
-
end
|
57
|
-
reply_text_message("关注公众账号")
|
58
|
-
when "unsubscribe" # 取消关注
|
59
|
-
reply_text_message("取消关注")
|
60
|
-
when "SCAN" # 扫描带参数二维码事件: 2用户已关注时的事件推送
|
61
|
-
reply_text_message("扫描带参数二维码事件: 2用户已关注时的事件推送, keyword: #{@keyword}")
|
62
|
-
when "LOCATION" # 上报地理位置事件
|
63
|
-
@lat = @weixin_message.Latitude
|
64
|
-
@lgt = @weixin_message.Longitude
|
65
|
-
@precision = @weixin_message.Precision
|
66
|
-
reply_text_message("Your Location: #{@lat}, #{@lgt}, #{@precision}")
|
67
|
-
when "CLICK" # 点击菜单拉取消息时的事件推送
|
68
|
-
reply_text_message("你点击了: #{@keyword}")
|
69
|
-
when "VIEW" # 点击菜单跳转链接时的事件推送
|
70
|
-
reply_text_message("你点击了: #{@keyword}")
|
71
|
-
else
|
72
|
-
reply_text_message("处理无法识别的事件")
|
73
|
-
end
|
74
|
-
|
75
|
-
end
|
76
|
-
|
77
47
|
# <MediaId><![CDATA[media_id]]></MediaId>
|
78
48
|
# <Format><![CDATA[Format]]></Format>
|
79
49
|
def response_voice_message(options={})
|
80
50
|
@media_id = @weixin_message.MediaId # 可以调用多媒体文件下载接口拉取数据。
|
81
51
|
@format = @weixin_message.Format
|
82
|
-
|
52
|
+
# 如果开启了语音翻译功能,@keyword则为翻译的结果
|
53
|
+
# reply_text_message("回复语音信息: #{@keyword}")
|
54
|
+
reply_voice_message(generate_voice(@media_id))
|
83
55
|
end
|
84
56
|
|
85
57
|
# <MediaId><![CDATA[media_id]]></MediaId>
|
@@ -91,15 +63,47 @@ WeixinRailsMiddleware::WeixinController.class_eval do
|
|
91
63
|
reply_text_message("回复视频信息")
|
92
64
|
end
|
93
65
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
@weixin_message.EventKey || # 事件推送
|
98
|
-
@weixin_message.Recognition # 接收语音识别结果
|
66
|
+
def response_event_message(options={})
|
67
|
+
event_type = @weixin_message.Event
|
68
|
+
send("reply_#{event_type.downcase}_event")
|
99
69
|
end
|
100
70
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
71
|
+
private
|
72
|
+
|
73
|
+
# 关注公众账号
|
74
|
+
def reply_subscribe_event
|
75
|
+
if @keyword.present?
|
76
|
+
# 扫描带参数二维码事件: 1. 用户未关注时,进行关注后的事件推送
|
77
|
+
return reply_text_message("扫描带参数二维码事件: 1. 用户未关注时,进行关注后的事件推送, keyword: #{@keyword}")
|
78
|
+
end
|
79
|
+
reply_text_message("关注公众账号")
|
80
|
+
end
|
81
|
+
|
82
|
+
# 取消关注
|
83
|
+
def reply_unsubscribe_event
|
84
|
+
Rails.logger.info("取消关注")
|
85
|
+
end
|
86
|
+
|
87
|
+
# 扫描带参数二维码事件: 2. 用户已关注时的事件推送
|
88
|
+
def reply_scan_event
|
89
|
+
reply_text_message("扫描带参数二维码事件: 2. 用户已关注时的事件推送, keyword: #{@keyword}")
|
90
|
+
end
|
91
|
+
|
92
|
+
def reply_location_event # 上报地理位置事件
|
93
|
+
@lat = @weixin_message.Latitude
|
94
|
+
@lgt = @weixin_message.Longitude
|
95
|
+
@precision = @weixin_message.Precision
|
96
|
+
reply_text_message("Your Location: #{@lat}, #{@lgt}, #{@precision}")
|
97
|
+
end
|
98
|
+
|
99
|
+
# 点击菜单拉取消息时的事件推送
|
100
|
+
def reply_click_event
|
101
|
+
reply_text_message("你点击了: #{@keyword}")
|
102
|
+
end
|
103
|
+
|
104
|
+
# 点击菜单跳转链接时的事件推送
|
105
|
+
def reply_view_event
|
106
|
+
Rails.logger.info("你点击了: #{@keyword}")
|
107
|
+
end
|
108
|
+
|
105
109
|
end
|
@@ -4,11 +4,14 @@ require "weixin_rails_middleware/models/message"
|
|
4
4
|
require "weixin_rails_middleware/models/reply_message"
|
5
5
|
require "weixin_rails_middleware/helpers/reply_weixin_message_helper"
|
6
6
|
require "weixin_rails_middleware/helpers/unique_token_helper"
|
7
|
-
require "weixin_rails_middleware/adapter"
|
8
7
|
require "weixin_rails_middleware/helpers/auto_generate_weixin_token_secret_key"
|
9
8
|
|
10
9
|
module WeixinRailsMiddleware
|
11
10
|
|
11
|
+
autoload(:WexinAdapter, "weixin_rails_middleware/adapter/weixin_adapter")
|
12
|
+
autoload(:SinglePublicAccount, "weixin_rails_middleware/adapter/single_public_account")
|
13
|
+
autoload(:MultiplePublicAccount, "weixin_rails_middleware/adapter/multiple_public_account")
|
14
|
+
|
12
15
|
DEFAULT_TOKEN_COLUMN_NAME = "weixin_token".freeze
|
13
16
|
DEFAULT_WEIXIN_SECRET_KEY = "weixin_secret_key".freeze
|
14
17
|
|
@@ -1,30 +1,28 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
module WeixinRailsMiddleware
|
3
|
-
|
4
|
-
class MultiplePublicAccount < WexinAdapter
|
3
|
+
class MultiplePublicAccount < WexinAdapter
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
# check the token from Weixin Service is exist in local store.
|
12
|
-
def is_weixin_secret_key_valid?
|
13
|
-
current_weixin_public_account.present?
|
14
|
-
end
|
5
|
+
def check_weixin_legality
|
6
|
+
return render_authorize_result(404) if !is_weixin_secret_key_valid?
|
7
|
+
super
|
8
|
+
end
|
15
9
|
|
16
|
-
|
17
|
-
|
18
|
-
|
10
|
+
# check the token from Weixin Service is exist in local store.
|
11
|
+
def is_weixin_secret_key_valid?
|
12
|
+
current_weixin_public_account.present?
|
13
|
+
end
|
19
14
|
|
20
|
-
|
21
|
-
|
22
|
-
|
15
|
+
def current_weixin_token
|
16
|
+
current_weixin_public_account.try(DEFAULT_TOKEN_COLUMN_NAME)
|
17
|
+
end
|
23
18
|
|
24
|
-
|
25
|
-
|
26
|
-
|
19
|
+
def current_weixin_public_account
|
20
|
+
self.class.token_model_class.where("#{DEFAULT_WEIXIN_SECRET_KEY}" => weixin_secret_key).first
|
21
|
+
end
|
27
22
|
|
23
|
+
def error_msg
|
24
|
+
"#{__FILE__}:#{__LINE__}: RecordNotFound - Couldn't find #{self.class.token_model} with weixin_secret_key=#{weixin_secret_key}"
|
28
25
|
end
|
26
|
+
|
29
27
|
end
|
30
28
|
end
|
@@ -1,29 +1,27 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
module WeixinRailsMiddleware
|
3
|
-
|
4
|
-
class SinglePublicAccount < WexinAdapter
|
3
|
+
class SinglePublicAccount < WexinAdapter
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
def is_weixin_secret_key_valid?
|
12
|
-
weixin_secret_key == self.class.weixin_secret_string
|
13
|
-
end
|
5
|
+
def check_weixin_legality
|
6
|
+
return render_authorize_result if !is_weixin_secret_key_valid?
|
7
|
+
super
|
8
|
+
end
|
14
9
|
|
15
|
-
|
16
|
-
|
17
|
-
|
10
|
+
def is_weixin_secret_key_valid?
|
11
|
+
weixin_secret_key == self.class.weixin_secret_string
|
12
|
+
end
|
18
13
|
|
19
|
-
|
20
|
-
|
21
|
-
|
14
|
+
def current_weixin_token
|
15
|
+
self.class.weixin_token_string
|
16
|
+
end
|
22
17
|
|
23
|
-
|
24
|
-
|
25
|
-
|
18
|
+
def current_weixin_public_account
|
19
|
+
nil
|
20
|
+
end
|
26
21
|
|
22
|
+
def error_msg
|
23
|
+
"#{__FILE__}:#{__LINE__}: Weixin secret string NotMatch."
|
27
24
|
end
|
25
|
+
|
28
26
|
end
|
29
27
|
end
|
@@ -1,74 +1,72 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
module WeixinRailsMiddleware
|
3
|
-
|
4
|
-
|
5
|
-
extend ConfigurationHelpers
|
3
|
+
class WexinAdapter
|
4
|
+
extend ConfigurationHelpers
|
6
5
|
|
7
|
-
|
8
|
-
|
6
|
+
attr_accessor :signature, :timestamp, :nonce, :echostr
|
7
|
+
attr_accessor :weixin_secret_key
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
9
|
+
def initialize(weixin_params)
|
10
|
+
@weixin_secret_key = weixin_params[:weixin_secret_key]
|
11
|
+
# 以下参数为什么加空字符串默认值的原因:
|
12
|
+
# SB微信偶尔会再重新发一次get请求,但是不会带上signature,timestamp,nonce的参数
|
13
|
+
# 一个预防措施吧。
|
14
|
+
@signature = weixin_params[:signature] || ''
|
15
|
+
@timestamp = weixin_params[:timestamp] || ''
|
16
|
+
@nonce = weixin_params[:nonce] || ''
|
17
|
+
@echostr = weixin_params[:echostr] || ''
|
18
|
+
end
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
end
|
20
|
+
def self.init_with(weixin_params)
|
21
|
+
if weixin_token_string.present?
|
22
|
+
SinglePublicAccount.new(weixin_params)
|
23
|
+
else
|
24
|
+
MultiplePublicAccount.new(weixin_params)
|
27
25
|
end
|
26
|
+
end
|
28
27
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
28
|
+
def check_weixin_legality
|
29
|
+
return render_authorize_result(403, self.class.error_msg) if !is_signature_valid?
|
30
|
+
render_authorize_result(200, echostr, true)
|
31
|
+
end
|
33
32
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
33
|
+
def is_signature_valid?
|
34
|
+
sort_params = [current_weixin_token, timestamp, nonce].sort.join
|
35
|
+
current_signature = Digest::SHA1.hexdigest(sort_params)
|
36
|
+
return true if current_signature == signature
|
37
|
+
false
|
38
|
+
end
|
40
39
|
|
41
|
-
|
42
|
-
|
43
|
-
|
40
|
+
def current_weixin_public_account
|
41
|
+
raise NotImplementedError, "Subclasses must implement current_weixin_public_account method"
|
42
|
+
end
|
44
43
|
|
45
|
-
|
46
|
-
|
47
|
-
|
44
|
+
def current_weixin_token
|
45
|
+
raise NotImplementedError, "Subclasses must implement current_weixin_token method"
|
46
|
+
end
|
48
47
|
|
49
|
-
|
50
|
-
|
51
|
-
|
48
|
+
def is_weixin_secret_key_valid?
|
49
|
+
raise NotImplementedError, "Subclasses must implement is_weixin_secret_key_valid? method"
|
50
|
+
end
|
52
51
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
end
|
52
|
+
class << self
|
53
|
+
def error_msg
|
54
|
+
"#{__FILE__}:#{__LINE__}: Weixin signature NotMatch"
|
57
55
|
end
|
56
|
+
end
|
58
57
|
|
59
|
-
|
58
|
+
private
|
60
59
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
60
|
+
# render weixin server authorize results
|
61
|
+
def render_authorize_result(status=403, text=nil, valid=false)
|
62
|
+
text = text || error_msg
|
63
|
+
Rails.logger.error(text) if status != 200
|
64
|
+
{text: text, status: status, valid: valid}
|
65
|
+
end
|
67
66
|
|
68
|
-
|
69
|
-
|
70
|
-
|
67
|
+
def error_msg
|
68
|
+
self.class.error_msg
|
69
|
+
end
|
71
70
|
|
72
|
-
end
|
73
71
|
end
|
74
72
|
end
|
@@ -51,13 +51,25 @@ module WeixinRailsMiddleware
|
|
51
51
|
|
52
52
|
def generate_video(media_id, desc, title)
|
53
53
|
video = Video.new
|
54
|
-
video.MediaId
|
54
|
+
video.MediaId = media_id
|
55
|
+
video.Title = title
|
55
56
|
video.Description = desc
|
56
|
-
video
|
57
|
-
vodeo
|
57
|
+
video
|
58
58
|
end
|
59
59
|
|
60
|
-
|
60
|
+
# <xml>
|
61
|
+
# <ToUserName><![CDATA[toUser]]></ToUserName>
|
62
|
+
# <FromUserName><![CDATA[fromUser]]></FromUserName>
|
63
|
+
# <CreateTime>12345678</CreateTime>
|
64
|
+
# <MsgType><![CDATA[video]]></MsgType>
|
65
|
+
# <Video>
|
66
|
+
# <MediaId><![CDATA[media_id]]></MediaId>
|
67
|
+
# <Title><![CDATA[title]]></Title>
|
68
|
+
# <Description><![CDATA[description]]></Description>
|
69
|
+
# </Video>
|
70
|
+
# </xml>
|
71
|
+
|
72
|
+
def reply_video_message(from=nil, to=nil, video)
|
61
73
|
message = VideoReplyMessage.new
|
62
74
|
message.FromUserName = from || @weixin_message.ToUserName
|
63
75
|
message.ToUserName = to || @weixin_message.FromUserName
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: weixin_rails_middleware
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- lanrion
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-04-
|
11
|
+
date: 2014-04-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: railties
|
@@ -52,20 +52,6 @@ dependencies:
|
|
52
52
|
- - '>='
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.1'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: multi_json
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - '>='
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: 1.9.0
|
62
|
-
type: :runtime
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - '>='
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: 1.9.0
|
69
55
|
- !ruby/object:Gem::Dependency
|
70
56
|
name: multi_xml
|
71
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -111,7 +97,6 @@ files:
|
|
111
97
|
- lib/generators/weixin_rails_middleware/install_generator.rb
|
112
98
|
- lib/generators/weixin_rails_middleware/migration_generator.rb
|
113
99
|
- lib/weixin_rails_middleware.rb
|
114
|
-
- lib/weixin_rails_middleware/adapter.rb
|
115
100
|
- lib/weixin_rails_middleware/adapter/multiple_public_account.rb
|
116
101
|
- lib/weixin_rails_middleware/adapter/single_public_account.rb
|
117
102
|
- lib/weixin_rails_middleware/adapter/weixin_adapter.rb
|