weixin_rails_middleware_uxinji 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|