weixin_rails_middleware_uxinji 1.0.4
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/MIT-LICENSE +20 -0
- data/README.md +90 -0
- data/Rakefile +34 -0
- data/app/controllers/weixin_rails_middleware/application_controller.rb +4 -0
- data/app/controllers/weixin_rails_middleware/weixin_controller.rb +96 -0
- data/config/routes.rb +4 -0
- data/lib/generators/templates/initializer.rb +20 -0
- data/lib/generators/templates/weixin_controller.rb +39 -0
- data/lib/generators/weixin_rails_middleware/install_generator.rb +35 -0
- data/lib/tasks/weixin_rails_middleware_tasks.rake +4 -0
- data/lib/weixin_rails_middleware.rb +27 -0
- data/lib/weixin_rails_middleware/configuration.rb +22 -0
- data/lib/weixin_rails_middleware/engine.rb +5 -0
- data/lib/weixin_rails_middleware/message.rb +163 -0
- data/lib/weixin_rails_middleware/reply_message.rb +183 -0
- data/lib/weixin_rails_middleware/version.rb +3 -0
- data/lib/weixin_rails_middleware/weixin_message_helper.rb +97 -0
- metadata +160 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a4f8e4584fbb40a0097d749f8e08b626f10db4cd
|
4
|
+
data.tar.gz: d68d4a280935b7796f600b9188b70edbe543989e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ada7e9e76c2adc0ddd1882d233caff70dd83b1c987bb652aceb365860c00d730ec9ffa0f765b861c90559a9fcf80e9ac44b049b6794feea4abb4925e28fa3943
|
7
|
+
data.tar.gz: beea4453004e0884e67b6c31d2777f60e0b56d605606da3c72e8c1379f9a9a0c7cb9316bd4fe70a4169a5cf7380c2070d1d1c0207cfdb6a6f4a7bba9b3e4173a
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2014 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# WeixinRailsMiddleware
|
2
|
+
|
3
|
+
This project rocks and uses MIT-LICENSE.
|
4
|
+
|
5
|
+
https://rubygems.org/gems/weixin_rails_middleware
|
6
|
+
|
7
|
+
Example: https://github.com/lanrion/weixin_rails_middleware_example
|
8
|
+
|
9
|
+
## Function
|
10
|
+
|
11
|
+
* 自动验证微信请求。
|
12
|
+
|
13
|
+
* 无需拼接XML格式,只需要使用 `WeixinMessageHelper` 辅助方法,即可快速回复。
|
14
|
+
使用方法: ` render xml: reply_text_message("Your Message: #{current_message.Content}") `
|
15
|
+
|
16
|
+
* 支持自定义token,适合一个用户使用。
|
17
|
+
|
18
|
+
* 支持多用户token: 适合多用户注册网站,每个用户有不同的token,通过 `weixin_rails_middleware.rb` 配置好存储token的Model与字段名,即可。
|
19
|
+
|
20
|
+
* 文本回复: `reply_text_message(content)`。
|
21
|
+
|
22
|
+
* 音乐回复: `reply_music_message(music)`, `generate_music(title, desc, music_url, hq_music_url)`。
|
23
|
+
|
24
|
+
* 图文回复: `reply_news_message(articles)`, `generate_article(title, desc, pic_url, link_url)`。
|
25
|
+
|
26
|
+
* 视频回复: `replay_video_message(video)`。
|
27
|
+
|
28
|
+
* 语音回复: `reply_voice_message(voice)`。
|
29
|
+
|
30
|
+
* 图片回复: `reply_imgage_message(image)`。
|
31
|
+
|
32
|
+
* 地理位置回复: 自定义需求。
|
33
|
+
|
34
|
+
## Install
|
35
|
+
|
36
|
+
In your `Gemfile`: `gem 'weixin_rails_middleware'`
|
37
|
+
|
38
|
+
And `bundle intall`
|
39
|
+
|
40
|
+
## Init weixin_rails_middleware
|
41
|
+
|
42
|
+
`rails generate weixin_rails_middleware:install`
|
43
|
+
|
44
|
+
It will create `config/initializers/weixin_rails_middleware.rb`
|
45
|
+
|
46
|
+
Note: You need to checkout comments in file
|
47
|
+
|
48
|
+
And
|
49
|
+
|
50
|
+
Create `app/decorators/controllers/weixin_rails_middleware/weixin_controller_decorator.rb`
|
51
|
+
|
52
|
+
Note: You need to overwrite the `reply` method. And there are two instance: `@weixin_message`,
|
53
|
+
`@weixin_public_account(token_model instance if you setup, otherwise return nil)`
|
54
|
+
|
55
|
+
Other
|
56
|
+
|
57
|
+
Add a line: `WeixinRailsMiddleware::Engine, at: WeixinRailsMiddleware.config.engine_path` in `routes.rb`
|
58
|
+
|
59
|
+
## Helpers
|
60
|
+
|
61
|
+
Please see detail in `weixin_message_helper.rb`
|
62
|
+
|
63
|
+
## How to test
|
64
|
+
Install `ngrok` and run with `ngrok 4000`, `4000` is your port that Rails Server needed
|
65
|
+
|
66
|
+
Then, it will generate like this:
|
67
|
+
|
68
|
+
```
|
69
|
+
Tunnel Status online
|
70
|
+
Version 1.6/1.5
|
71
|
+
Forwarding http://e0ede89.ngrok.com -> 127.0.0.1:4000
|
72
|
+
Forwarding https://e0ede89.ngrok.com -> 127.0.0.1:4000
|
73
|
+
Web Interface 127.0.0.1:4040
|
74
|
+
# Conn 67
|
75
|
+
Avg Conn Time 839.50ms
|
76
|
+
|
77
|
+
```
|
78
|
+
|
79
|
+
Yes, She is `http://e0ede89.ngrok.com`
|
80
|
+
|
81
|
+
神器!!
|
82
|
+
|
83
|
+
## XML parser
|
84
|
+
We use `roxml` to generate XML and use `multi_xml` to parse `XML`, but we let them all use `nokogiri` plugin default.
|
85
|
+
|
86
|
+
## TODO
|
87
|
+
|
88
|
+
* 添加微信自定义菜单
|
89
|
+
* 多媒体资料上传
|
90
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'WeixinRailsMiddleware'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
Bundler::GemHelper.install_tasks
|
23
|
+
|
24
|
+
require 'rake/testtask'
|
25
|
+
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
27
|
+
t.libs << 'lib'
|
28
|
+
t.libs << 'test'
|
29
|
+
t.pattern = 'test/**/*_test.rb'
|
30
|
+
t.verbose = false
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
task default: :test
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module WeixinRailsMiddleware
|
2
|
+
class WeixinController < ApplicationController
|
3
|
+
include WeixinMessageHelper
|
4
|
+
|
5
|
+
skip_before_filter :verify_authenticity_token
|
6
|
+
before_action :check_weixin_params, only: [:index, :reply]
|
7
|
+
before_action :set_weixin_public_account, :set_weixin_message, only: :reply
|
8
|
+
|
9
|
+
def index
|
10
|
+
render text: params[:echostr]
|
11
|
+
end
|
12
|
+
|
13
|
+
def reply
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def check_weixin_params
|
19
|
+
if check_weixin_token_valid?
|
20
|
+
unless is_hexdigest?
|
21
|
+
render text: "Forbidden", status: 403
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# check the token from Weixin Service is exist in local store.
|
27
|
+
def check_weixin_token_valid?
|
28
|
+
token_string.present? ? check_token_string : check_token_model_instance
|
29
|
+
end
|
30
|
+
|
31
|
+
def token_string
|
32
|
+
WeixinRailsMiddleware.config.token_string
|
33
|
+
end
|
34
|
+
|
35
|
+
def check_token_string
|
36
|
+
if current_weixin_token != token_string
|
37
|
+
render text: "Forbidden", status: 403
|
38
|
+
return false
|
39
|
+
end
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
def check_token_model_instance
|
44
|
+
if token_model_instance.blank?
|
45
|
+
render text: "Forbidden", status: 403
|
46
|
+
return false
|
47
|
+
end
|
48
|
+
true
|
49
|
+
end
|
50
|
+
|
51
|
+
def is_hexdigest?
|
52
|
+
temp_array = weixin_token, timestamp, nonce
|
53
|
+
current_signature = Digest::SHA1.hexdigest(temp_array.sort.join)
|
54
|
+
return true if current_signature == signature
|
55
|
+
false
|
56
|
+
end
|
57
|
+
|
58
|
+
[:signature, :timestamp, :nonce, :weixin_token].each do |e|
|
59
|
+
define_method e do
|
60
|
+
params[e]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def current_weixin_token
|
65
|
+
@current_weixin_token ||= weixin_token
|
66
|
+
end
|
67
|
+
|
68
|
+
def token_model_instance
|
69
|
+
token_model = WeixinRailsMiddleware.config.token_model_class
|
70
|
+
token_column = WeixinRailsMiddleware.config.token_column
|
71
|
+
token_model_instance = token_model.where("#{token_column}" => current_weixin_token).first
|
72
|
+
end
|
73
|
+
|
74
|
+
# e.g. will generate +@weixin_public_account+
|
75
|
+
def set_weixin_public_account
|
76
|
+
return nil if token_string.present?
|
77
|
+
@weixin_public_account ||= token_model_instance
|
78
|
+
end
|
79
|
+
|
80
|
+
def set_weixin_message
|
81
|
+
# Get the current_message
|
82
|
+
@weixin_message ||= current_weixin_message
|
83
|
+
end
|
84
|
+
|
85
|
+
# take the weixin params
|
86
|
+
def current_weixin_params
|
87
|
+
@current_weixin_params ||= request.body.read
|
88
|
+
end
|
89
|
+
|
90
|
+
# return a message class with current_weixin_params
|
91
|
+
def current_weixin_message
|
92
|
+
Message.factory(current_weixin_params)
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# Use this hook to configure WeixinRailsMiddleware bahaviors.
|
2
|
+
WeixinRailsMiddleware.configure do |config|
|
3
|
+
|
4
|
+
## NOTE:
|
5
|
+
## if you config all them, it will use `token_string` default
|
6
|
+
##
|
7
|
+
# Th FIRST configure
|
8
|
+
# if you config `token_model`, it will use it to find_by_weixin_token
|
9
|
+
# you must config a column name, `weixin_token` default
|
10
|
+
# config.token_model = "" # ActiveRecord subclass or other ORM subclass
|
11
|
+
# config.token_column = "weixin_token"
|
12
|
+
|
13
|
+
# OR the SECOND configure
|
14
|
+
# if you config `token_string`, so it will directly use it
|
15
|
+
# config.token_string = "token string"
|
16
|
+
|
17
|
+
# router
|
18
|
+
# config.engine_path = "/"
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
WeixinRailsMiddleware::WeixinController.class_eval do
|
2
|
+
|
3
|
+
# There are two instance: @weixin_message,
|
4
|
+
# @weixin_public_account(token_model instance if you setup, otherwise return nil)
|
5
|
+
def reply
|
6
|
+
render xml: send("response_#{@weixin_message.MsgType}_message", {})
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def response_text_message(options={})
|
12
|
+
reply_text_message("Your Message: #{@weixin_message.Content}")
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
def response_location_message(options={})
|
17
|
+
reply_text_message("Your Location: #{@weixin_message.Location_X}, #{@weixin_message.Location_Y}")
|
18
|
+
end
|
19
|
+
|
20
|
+
def response_image_message(options={})
|
21
|
+
# image message handler
|
22
|
+
end
|
23
|
+
|
24
|
+
def response_link_message(options={})
|
25
|
+
# link message handler
|
26
|
+
end
|
27
|
+
|
28
|
+
def response_event_message(options={})
|
29
|
+
# event messge handler
|
30
|
+
end
|
31
|
+
|
32
|
+
def response_voice_message(options={})
|
33
|
+
# voice message handler
|
34
|
+
end
|
35
|
+
|
36
|
+
def response_video_message(options={})
|
37
|
+
# video message handler
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# Rails::Generators::Base dont need a name
|
2
|
+
# Rails::Generators::NamedBase need a name
|
3
|
+
module WeixinRailsMiddleware
|
4
|
+
module Generators
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
6
|
+
source_root File.expand_path('../../templates', __FILE__)
|
7
|
+
|
8
|
+
desc 'Creates a Dashing initializer for your application.'
|
9
|
+
|
10
|
+
def install
|
11
|
+
route 'mount WeixinRailsMiddleware::Engine, at: WeixinRailsMiddleware.config.engine_path'
|
12
|
+
end
|
13
|
+
|
14
|
+
def copy_initializer
|
15
|
+
template 'initializer.rb', 'config/initializers/weixin_rails_middleware.rb'
|
16
|
+
end
|
17
|
+
|
18
|
+
def configure_application
|
19
|
+
application <<-APP
|
20
|
+
config.to_prepare do
|
21
|
+
# Load application's model / class decorators
|
22
|
+
Dir.glob(File.join(File.dirname(__FILE__), "../app/**/*_decorator*.rb")) do |c|
|
23
|
+
Rails.configuration.cache_classes ? require(c) : load(c)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
APP
|
27
|
+
end
|
28
|
+
|
29
|
+
def copy_decorators
|
30
|
+
template 'weixin_controller.rb', 'app/decorators/controllers/weixin_rails_middleware/weixin_controller_decorator.rb'
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "weixin_rails_middleware/engine"
|
2
|
+
require "weixin_rails_middleware/configuration"
|
3
|
+
require "weixin_rails_middleware/message"
|
4
|
+
require "weixin_rails_middleware/reply_message"
|
5
|
+
require "weixin_rails_middleware/weixin_message_helper"
|
6
|
+
|
7
|
+
module WeixinRailsMiddleware
|
8
|
+
|
9
|
+
DEFAULT_TOKEN_COLUMN_NAME = "weixin_token".freeze
|
10
|
+
DEFAULT_ENGINE_PATH = "/".freeze
|
11
|
+
|
12
|
+
class << self
|
13
|
+
|
14
|
+
attr_accessor :configuration
|
15
|
+
|
16
|
+
def config
|
17
|
+
self.configuration ||= Configuration.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def configure
|
21
|
+
yield config if block_given?
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module WeixinRailsMiddleware
|
2
|
+
class Configuration
|
3
|
+
# use 'token_model': if the token is saved in SomeModel, then find token by it
|
4
|
+
# use 'token_string': if the token is a String, just use it,
|
5
|
+
attr_accessor :token_model, :token_column, :token_string, :engine_path
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@engine_path = DEFAULT_ENGINE_PATH
|
9
|
+
@token_column = DEFAULT_TOKEN_COLUMN_NAME
|
10
|
+
end
|
11
|
+
|
12
|
+
def token_model_class
|
13
|
+
raise "You need to config `token_model` in config/initializers/weixin_rails_middleware.rb" if token_model.blank?
|
14
|
+
token_model_c = token_model.constantize
|
15
|
+
unless token_model_c.table_exists?
|
16
|
+
raise "You don't have #{token_model_c.table_name} table"
|
17
|
+
end
|
18
|
+
token_model_c
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
# ref: https://github.com/wolfg1969/rack-weixin/lib/weixin/model.rb
|
3
|
+
require 'roxml'
|
4
|
+
require 'multi_xml'
|
5
|
+
require 'ostruct'
|
6
|
+
|
7
|
+
# multi_xml will use Nokogiri if it is available
|
8
|
+
MultiXml.parser = :nokogiri
|
9
|
+
|
10
|
+
module WeixinRailsMiddleware
|
11
|
+
|
12
|
+
class Message
|
13
|
+
|
14
|
+
def initialize(hash)
|
15
|
+
@source = OpenStruct.new(hash)
|
16
|
+
end
|
17
|
+
|
18
|
+
def method_missing(method, *args, &block)
|
19
|
+
@source.send(method, *args, &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
def CreateTime
|
23
|
+
@source.CreateTime.to_i
|
24
|
+
end
|
25
|
+
|
26
|
+
def MsgId
|
27
|
+
@source.MsgId.to_i
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.factory(xml)
|
31
|
+
hash = MultiXml.parse(xml)['xml']
|
32
|
+
case hash['MsgType']
|
33
|
+
when 'text'
|
34
|
+
TextMessage.new(hash)
|
35
|
+
when 'image'
|
36
|
+
ImageMessage.new(hash)
|
37
|
+
when 'location'
|
38
|
+
LocationMessage.new(hash)
|
39
|
+
when 'link'
|
40
|
+
LinkMessage.new(hash)
|
41
|
+
when 'event'
|
42
|
+
EventMessage.new(hash)
|
43
|
+
when 'voice'
|
44
|
+
VoiceMessage.new(hash)
|
45
|
+
when 'video'
|
46
|
+
VideoMessage.new(hash)
|
47
|
+
else
|
48
|
+
raise ArgumentError, 'Unknown Message'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
# <xml>
|
55
|
+
# <ToUserName><![CDATA[toUser]]></ToUserName>
|
56
|
+
# <FromUserName><![CDATA[fromUser]]></FromUserName>
|
57
|
+
# <CreateTime>1348831860</CreateTime>
|
58
|
+
# <MsgType><![CDATA[text]]></MsgType>
|
59
|
+
# <Content><![CDATA[this is a test]]></Content>
|
60
|
+
# <MsgId>1234567890123456</MsgId>
|
61
|
+
# </xml>
|
62
|
+
TextMessage = Class.new(Message)
|
63
|
+
|
64
|
+
# <xml>
|
65
|
+
# <ToUserName><![CDATA[toUser]]></ToUserName>
|
66
|
+
# <FromUserName><![CDATA[fromUser]]></FromUserName>
|
67
|
+
# <CreateTime>1348831860</CreateTime>
|
68
|
+
# <MsgType><![CDATA[image]]></MsgType>
|
69
|
+
# <PicUrl><![CDATA[this is a url]]></PicUrl>
|
70
|
+
# <MsgId>1234567890123456</MsgId>
|
71
|
+
# </xml>
|
72
|
+
ImageMessage = Class.new(Message)
|
73
|
+
|
74
|
+
# <xml>
|
75
|
+
# <ToUserName><![CDATA[toUser]]></ToUserName>
|
76
|
+
# <FromUserName><![CDATA[fromUser]]></FromUserName>
|
77
|
+
# <CreateTime>1351776360</CreateTime>
|
78
|
+
# <MsgType><![CDATA[link]]></MsgType>
|
79
|
+
# <Title><![CDATA[公众平台官网链接]]></Title>
|
80
|
+
# <Description><![CDATA[公众平台官网链接]]></Description>
|
81
|
+
# <Url><![CDATA[url]]></Url>
|
82
|
+
# <MsgId>1234567890123456</MsgId>
|
83
|
+
# </xml>
|
84
|
+
LinkMessage = Class.new(Message)
|
85
|
+
|
86
|
+
# <xml>
|
87
|
+
# <ToUserName><![CDATA[toUser]]></ToUserName>
|
88
|
+
# <FromUserName><![CDATA[FromUser]]></FromUserName>
|
89
|
+
# <CreateTime>123456789</CreateTime>
|
90
|
+
# <MsgType><![CDATA[event]]></MsgType>
|
91
|
+
# <Event><![CDATA[EVENT]]></Event>
|
92
|
+
# <EventKey><![CDATA[EVENTKEY]]></EventKey>
|
93
|
+
# </xml>
|
94
|
+
EventMessage = Class.new(Message)
|
95
|
+
|
96
|
+
# <xml>
|
97
|
+
# <ToUserName><![CDATA[toUser]]></ToUserName>
|
98
|
+
# <FromUserName><![CDATA[fromUser]]></FromUserName>
|
99
|
+
# <CreateTime>1351776360</CreateTime>
|
100
|
+
# <MsgType><![CDATA[location]]></MsgType>
|
101
|
+
# <Location_X>23.134521</Location_X>
|
102
|
+
# <Location_Y>113.358803</Location_Y>
|
103
|
+
# <Scale>20</Scale>
|
104
|
+
# <Label><![CDATA[位置信息]]></Label>
|
105
|
+
# <MsgId>1234567890123456</MsgId>
|
106
|
+
# </xml>
|
107
|
+
class LocationMessage < Message
|
108
|
+
|
109
|
+
def Location_X
|
110
|
+
@source.Location_X.to_f
|
111
|
+
end
|
112
|
+
|
113
|
+
def Location_Y
|
114
|
+
@source.Location_Y.to_f
|
115
|
+
end
|
116
|
+
|
117
|
+
def Scale
|
118
|
+
@source.Scale.to_i
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# <xml>
|
123
|
+
# <ToUserName><![CDATA[toUser]]></ToUserName>
|
124
|
+
# <FromUserName><![CDATA[fromUser]]></FromUserName>
|
125
|
+
# <CreateTime>1376632760</CreateTime>
|
126
|
+
# <MsgType><![CDATA[voice]]></MsgType>
|
127
|
+
# <MediaId><![CDATA[Qyb0tgux6QLjhL6ipvFZJ-kUt2tcQtkn0BU365Vt3wUAtqfGam4QpZU35RXVhv6G]]></MediaId>
|
128
|
+
# <Format><![CDATA[amr]]></Format>
|
129
|
+
# <MsgId>5912592682802219078</MsgId>
|
130
|
+
# <Recognition><![CDATA[]]></Recognition>
|
131
|
+
# </xml>
|
132
|
+
class VoiceMessage < Message
|
133
|
+
|
134
|
+
def MediaId
|
135
|
+
@source.MediaId
|
136
|
+
end
|
137
|
+
|
138
|
+
def Format
|
139
|
+
@source.Format
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# <xml>
|
144
|
+
# <ToUserName><![CDATA[toUser]]></ToUserName>
|
145
|
+
# <FromUserName><![CDATA[fromUser]]></FromUserName>
|
146
|
+
# <CreateTime>1376632994</CreateTime>
|
147
|
+
# <MsgType><![CDATA[video]]></MsgType>
|
148
|
+
# <MediaId><![CDATA[TAAGb6iS5LcZR1d5ICiZTWGWi6-Upic9tlWDpAKcNJA]]></MediaId>
|
149
|
+
# <ThumbMediaId><![CDATA[U-xulPW4kq6KKMWFNaBSPc65Bcgr7Qopwex0DfCeyQs]]></ThumbMediaId>
|
150
|
+
# <MsgId>5912593687824566343</MsgId>
|
151
|
+
# </xml>
|
152
|
+
class VideoMessage < Message
|
153
|
+
|
154
|
+
def MediaId
|
155
|
+
@source.MediaId
|
156
|
+
end
|
157
|
+
|
158
|
+
def ThumbMediaId
|
159
|
+
@source.ThumbMediaId
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
# ref: https://github.com/wolfg1969/rack-weixin/lib/weixin/model.rb
|
3
|
+
require 'roxml'
|
4
|
+
|
5
|
+
module WeixinRailsMiddleware
|
6
|
+
|
7
|
+
class ReplyMessage
|
8
|
+
include ROXML
|
9
|
+
xml_name :xml
|
10
|
+
#xml_convention :camelcase
|
11
|
+
|
12
|
+
xml_accessor :ToUserName, :cdata => true
|
13
|
+
xml_accessor :FromUserName, :cdata => true
|
14
|
+
xml_reader :CreateTime, :as => Integer
|
15
|
+
xml_reader :MsgType, :cdata => true
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@CreateTime = Time.now.to_i
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_xml
|
22
|
+
super.to_xml(:encoding => 'UTF-8', :indent => 0, :save_with => 0)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# <xml>
|
27
|
+
# <ToUserName><![CDATA[toUser]]></ToUserName>
|
28
|
+
# <FromUserName><![CDATA[fromUser]]></FromUserName>
|
29
|
+
# <CreateTime>12345678</CreateTime>
|
30
|
+
# <MsgType><![CDATA[text]]></MsgType>
|
31
|
+
# <Content><![CDATA[Hello]]></Content>
|
32
|
+
# </xml>
|
33
|
+
|
34
|
+
class TextReplyMessage < ReplyMessage
|
35
|
+
xml_accessor :Content, :cdata => true
|
36
|
+
def initialize
|
37
|
+
super
|
38
|
+
@MsgType = 'text'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class Music
|
43
|
+
include ROXML
|
44
|
+
xml_accessor :Title, :cdata => true
|
45
|
+
xml_accessor :Description, :cdata => true
|
46
|
+
xml_accessor :MusicUrl, :cdata => true
|
47
|
+
xml_accessor :HQMusicUrl, :cdata => true
|
48
|
+
end
|
49
|
+
|
50
|
+
# <xml>
|
51
|
+
# <ToUserName><![CDATA[toUser]]></ToUserName>
|
52
|
+
# <FromUserName><![CDATA[fromUser]]></FromUserName>
|
53
|
+
# <CreateTime>12345678</CreateTime>
|
54
|
+
# <MsgType><![CDATA[music]]></MsgType>
|
55
|
+
# <Music>
|
56
|
+
# <Title><![CDATA[TITLE]]></Title>
|
57
|
+
# <Description><![CDATA[DESCRIPTION]]></Description>
|
58
|
+
# <MusicUrl><![CDATA[MUSIC_Url]]></MusicUrl>
|
59
|
+
# <HQMusicUrl><![CDATA[HQ_MUSIC_Url]]></HQMusicUrl>
|
60
|
+
# <ThumbMediaId><![CDATA[media_id]]></ThumbMediaId>
|
61
|
+
# </Music>
|
62
|
+
# </xml>
|
63
|
+
|
64
|
+
class MusicReplyMessage < ReplyMessage
|
65
|
+
xml_accessor :Music, :as => Music
|
66
|
+
def initialize
|
67
|
+
super
|
68
|
+
@MsgType = 'music'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class Article
|
73
|
+
include ROXML
|
74
|
+
xml_accessor :Title, :cdata => true
|
75
|
+
xml_accessor :Description, :cdata => true
|
76
|
+
xml_accessor :PicUrl, :cdata => true
|
77
|
+
xml_accessor :Url, :cdata => true
|
78
|
+
end
|
79
|
+
|
80
|
+
# <xml>
|
81
|
+
# <ToUserName><![CDATA[toUser]]></ToUserName>
|
82
|
+
# <FromUserName><![CDATA[fromUser]]></FromUserName>
|
83
|
+
# <CreateTime>12345678</CreateTime>
|
84
|
+
# <MsgType><![CDATA[news]]></MsgType>
|
85
|
+
# <ArticleCount>2</ArticleCount>
|
86
|
+
# <Articles>
|
87
|
+
# <item>
|
88
|
+
# <Title><![CDATA[title1]]></Title>
|
89
|
+
# <Description><![CDATA[description1]]></Description>
|
90
|
+
# <PicUrl><![CDATA[picurl]]></PicUrl>
|
91
|
+
# <Url><![CDATA[url]]></Url>
|
92
|
+
# </item>
|
93
|
+
# <item>
|
94
|
+
# <Title><![CDATA[title]]></Title>
|
95
|
+
# <Description><![CDATA[description]]></Description>
|
96
|
+
# <PicUrl><![CDATA[picurl]]></PicUrl>
|
97
|
+
# <Url><![CDATA[url]]></Url>
|
98
|
+
# </item>
|
99
|
+
# </Articles>
|
100
|
+
# </xml>
|
101
|
+
|
102
|
+
class NewsReplyMessage < ReplyMessage
|
103
|
+
xml_accessor :ArticleCount, :as => Integer
|
104
|
+
xml_accessor :Articles, :as => [Article], :in => 'Articles', :from => 'item'
|
105
|
+
def initialize
|
106
|
+
super
|
107
|
+
@MsgType = 'news'
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# <xml>
|
112
|
+
# <ToUserName><![CDATA[toUser]]></ToUserName>
|
113
|
+
# <FromUserName><![CDATA[fromUser]]></FromUserName>
|
114
|
+
# <CreateTime>12345678</CreateTime>
|
115
|
+
# <MsgType><![CDATA[video]]></MsgType>
|
116
|
+
# <Video>
|
117
|
+
# <MediaId><![CDATA[media_id]]></MediaId>
|
118
|
+
# <Title><![CDATA[title]]></Title>
|
119
|
+
# <Description><![CDATA[description]]></Description>
|
120
|
+
# </Video>
|
121
|
+
# </xml>
|
122
|
+
|
123
|
+
class Video
|
124
|
+
include ROXML
|
125
|
+
xml_accessor :MediaId, :cdata => true
|
126
|
+
xml_accessor :Description, :cdata => true
|
127
|
+
xml_accessor :Title, :cdata => true
|
128
|
+
end
|
129
|
+
|
130
|
+
class VideoReplyMessage < ReplyMessage
|
131
|
+
xml_accessor :Video, :as => Video
|
132
|
+
def initialize
|
133
|
+
super
|
134
|
+
@MsgType = 'video'
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# <xml>
|
139
|
+
# <ToUserName><![CDATA[toUser]]></ToUserName>
|
140
|
+
# <FromUserName><![CDATA[fromUser]]></FromUserName>
|
141
|
+
# <CreateTime>12345678</CreateTime>
|
142
|
+
# <MsgType><![CDATA[voice]]></MsgType>
|
143
|
+
# <Voice>
|
144
|
+
# <MediaId><![CDATA[media_id]]></MediaId>
|
145
|
+
# </Voice>
|
146
|
+
# </xml>
|
147
|
+
class Voice
|
148
|
+
include ROXML
|
149
|
+
xml_accessor :MediaId, :cdata => true
|
150
|
+
end
|
151
|
+
|
152
|
+
class VoiceReplyMessage < ReplyMessage
|
153
|
+
xml_accessor :Voice, :as => Voice
|
154
|
+
def initialize
|
155
|
+
super
|
156
|
+
@MsgType = 'voice'
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# <xml>
|
161
|
+
# <ToUserName><![CDATA[toUser]]></ToUserName>
|
162
|
+
# <FromUserName><![CDATA[fromUser]]></FromUserName>
|
163
|
+
# <CreateTime>12345678</CreateTime>
|
164
|
+
# <MsgType><![CDATA[image]]></MsgType>
|
165
|
+
# <Image>
|
166
|
+
# <MediaId><![CDATA[media_id]]></MediaId>
|
167
|
+
# </Image>
|
168
|
+
# </xml>
|
169
|
+
|
170
|
+
class Image
|
171
|
+
include ROXML
|
172
|
+
xml_accessor :MediaId, :cdata => true
|
173
|
+
end
|
174
|
+
|
175
|
+
class ImageReplyMessage < ReplyMessage
|
176
|
+
xml_accessor :Image, :as => Image
|
177
|
+
def initialize
|
178
|
+
super
|
179
|
+
@MsgType = 'image'
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module WeixinRailsMiddleware
|
2
|
+
module WeixinMessageHelper
|
3
|
+
|
4
|
+
# e.g.
|
5
|
+
# reply_text_message(@weixin_message.ToUserName, @weixin_message.FromUserName, "Your Message: #{@weixin_message.Content}")
|
6
|
+
# Or reply_text_message("Your Message: #{@weixin_message.Content}")
|
7
|
+
def reply_text_message(from=nil, to=nil, content)
|
8
|
+
message = TextReplyMessage.new
|
9
|
+
message.FromUserName = from || @weixin_message.ToUserName
|
10
|
+
message.ToUserName = to || @weixin_message.FromUserName
|
11
|
+
message.Content = content
|
12
|
+
message.to_xml
|
13
|
+
end
|
14
|
+
|
15
|
+
def generate_music(title, desc, music_url, hq_music_url)
|
16
|
+
music = Music.new
|
17
|
+
music.Title = title
|
18
|
+
music.Description = desc
|
19
|
+
music.MusicUrl = music_url
|
20
|
+
music.HQMusicUrl = hq_music_url
|
21
|
+
music
|
22
|
+
end
|
23
|
+
|
24
|
+
# music = generate_music
|
25
|
+
def reply_music_message(from=nil, to=nil, music)
|
26
|
+
message = MusicReplyMessage.new
|
27
|
+
message.FromUserName = from || @weixin_message.ToUserName
|
28
|
+
message.ToUserName = to || @weixin_message.FromUserName
|
29
|
+
message.Music = music
|
30
|
+
message.to_xml
|
31
|
+
end
|
32
|
+
|
33
|
+
def generate_article(title, desc, pic_url, link_url)
|
34
|
+
item = Article.new
|
35
|
+
item.Title = title
|
36
|
+
item.Description = desc
|
37
|
+
item.PicUrl = pic_url
|
38
|
+
item.Url = link_url
|
39
|
+
item
|
40
|
+
end
|
41
|
+
|
42
|
+
# articles = [generate_article]
|
43
|
+
def reply_news_message(from=nil, to=nil, articles)
|
44
|
+
message = NewsReplyMessage.new
|
45
|
+
message.FromUserName = from || @weixin_message.ToUserName
|
46
|
+
message.ToUserName = to || @weixin_message.FromUserName
|
47
|
+
message.Articles = articles
|
48
|
+
message.ArticleCount = articles.count
|
49
|
+
message.to_xml
|
50
|
+
end
|
51
|
+
|
52
|
+
def generate_video(media_id, desc, title)
|
53
|
+
video = Video.new
|
54
|
+
video.MediaId = media_id
|
55
|
+
video.Description = desc
|
56
|
+
video.Title = title
|
57
|
+
vodeo
|
58
|
+
end
|
59
|
+
|
60
|
+
def replay_video_message(from=nil, to=nil, video)
|
61
|
+
message = VideoReplyMessage.new
|
62
|
+
message.FromUserName = from || @weixin_message.ToUserName
|
63
|
+
message.ToUserName = to || @weixin_message.FromUserName
|
64
|
+
message.Video = video
|
65
|
+
message.to_xml
|
66
|
+
end
|
67
|
+
|
68
|
+
def generate_voice(media_id)
|
69
|
+
voice = Voice.new
|
70
|
+
voice.MediaId = media_id
|
71
|
+
voice
|
72
|
+
end
|
73
|
+
|
74
|
+
def reply_voice_message(from=nil, to=nil, voice)
|
75
|
+
message = VoiceReplyMessage.new
|
76
|
+
message.FromUserName = from || @weixin_message.ToUserName
|
77
|
+
message.ToUserName = to || @weixin_message.FromUserName
|
78
|
+
message.Voice = voice
|
79
|
+
message.to_xml
|
80
|
+
end
|
81
|
+
|
82
|
+
def generate_image(media_id)
|
83
|
+
image = Image.new
|
84
|
+
image.MediaId = media_id
|
85
|
+
image
|
86
|
+
end
|
87
|
+
|
88
|
+
def reply_imgage_message(from=nil, to=nil, image)
|
89
|
+
message = ImageReplyMessage.new
|
90
|
+
message.FromUserName = from || @weixin_message.ToUserName
|
91
|
+
message.ToUserName = to || @weixin_message.FromUserName
|
92
|
+
message.Image = image
|
93
|
+
message.to_xml
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
metadata
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: weixin_rails_middleware_uxinji
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.4
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- lanrion
|
8
|
+
- turristan
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-02-21 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: railties
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - '>='
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '3.1'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - '>='
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '3.1'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: nokogiri
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - '>='
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 1.6.1
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - '>='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 1.6.1
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rails
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '3.1'
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '3.1'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: multi_json
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 1.7.9
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 1.7.9
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: multi_xml
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: 0.5.2
|
77
|
+
type: :runtime
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: 0.5.2
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: roxml
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: 3.3.1
|
91
|
+
type: :runtime
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - '>='
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: 3.3.1
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: rest_client
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - '>='
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: 1.6.7
|
105
|
+
type: :runtime
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - '>='
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: 1.6.7
|
112
|
+
description: weixin_rails_middleware for uxinji weixin public service
|
113
|
+
email:
|
114
|
+
- huaitao-deng@foxmail.com
|
115
|
+
- turristan@gmail.com
|
116
|
+
executables: []
|
117
|
+
extensions: []
|
118
|
+
extra_rdoc_files: []
|
119
|
+
files:
|
120
|
+
- MIT-LICENSE
|
121
|
+
- README.md
|
122
|
+
- Rakefile
|
123
|
+
- app/controllers/weixin_rails_middleware/application_controller.rb
|
124
|
+
- app/controllers/weixin_rails_middleware/weixin_controller.rb
|
125
|
+
- config/routes.rb
|
126
|
+
- lib/generators/templates/initializer.rb
|
127
|
+
- lib/generators/templates/weixin_controller.rb
|
128
|
+
- lib/generators/weixin_rails_middleware/install_generator.rb
|
129
|
+
- lib/tasks/weixin_rails_middleware_tasks.rake
|
130
|
+
- lib/weixin_rails_middleware.rb
|
131
|
+
- lib/weixin_rails_middleware/configuration.rb
|
132
|
+
- lib/weixin_rails_middleware/engine.rb
|
133
|
+
- lib/weixin_rails_middleware/message.rb
|
134
|
+
- lib/weixin_rails_middleware/reply_message.rb
|
135
|
+
- lib/weixin_rails_middleware/version.rb
|
136
|
+
- lib/weixin_rails_middleware/weixin_message_helper.rb
|
137
|
+
homepage: https://github.com/turristan/weixin_rails_middleware/tree/uxinji
|
138
|
+
licenses: []
|
139
|
+
metadata: {}
|
140
|
+
post_install_message:
|
141
|
+
rdoc_options: []
|
142
|
+
require_paths:
|
143
|
+
- lib
|
144
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
145
|
+
requirements:
|
146
|
+
- - '>='
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: '0'
|
149
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - '>='
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '0'
|
154
|
+
requirements: []
|
155
|
+
rubyforge_project:
|
156
|
+
rubygems_version: 2.2.2
|
157
|
+
signing_key:
|
158
|
+
specification_version: 4
|
159
|
+
summary: weixin_rails_middleware specified uxinji
|
160
|
+
test_files: []
|