qy_wechat 1.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e0406f31fb56e074f7cf19633c0430a2a13dbedd
4
+ data.tar.gz: 44e9e8ae54ef833ab1ba3ba9c72ddb1f07d56d1b
5
+ SHA512:
6
+ metadata.gz: 1e1f81653a492b0b5d755a7e3b1e548e95313a38232568e4725127ac4c3ea395fae18d9c8f1b18cecc6c8580562376dabb77b70c4a4e007a6d8345f9a1f7fb3d
7
+ data.tar.gz: e95dddb6e3f94d51cd24cb086fdc6f7c1db1a8e8a199ad58291805752729e44b69a44bfd312be5de43e8b8bb6a5be55e5bee0368ee0c1852510bb2479ac4d5e8
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2014 lanrion
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/Rakefile ADDED
@@ -0,0 +1,37 @@
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 = 'QyWechat'
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
+ load 'rails/tasks/statistics.rake'
22
+
23
+
24
+
25
+ Bundler::GemHelper.install_tasks
26
+
27
+ require 'rake/testtask'
28
+
29
+ Rake::TestTask.new(:test) do |t|
30
+ t.libs << 'lib'
31
+ t.libs << 'test'
32
+ t.pattern = 'test/**/*_test.rb'
33
+ t.verbose = false
34
+ end
35
+
36
+
37
+ task default: :test
@@ -0,0 +1,64 @@
1
+ # encoding: utf-8
2
+ module QyWechat
3
+ class QyWechatController < ActionController::Base
4
+
5
+ include ReplyMessageHelper
6
+
7
+ skip_before_filter :verify_authenticity_token, only: :reply
8
+ before_action :setup_qy_account, only: [:verify_url, :reply]
9
+ before_action :setup_wechat_message, only: :reply
10
+
11
+ # 验证URL有效性
12
+ def verify_url
13
+ raise "Not Match" if not valid_msg_signature(params)
14
+ params.delete(:qy_secret_key)
15
+ content = Prpcrypt.decrypt(aes_key, params[:echostr], corp_id)
16
+ render text: content
17
+ end
18
+
19
+ def reply;end
20
+
21
+ private
22
+
23
+ def setup_wechat_message
24
+ param_xml = request.body.read
25
+ hash = MultiXml.parse(param_xml)['xml']
26
+ @body_xml = OpenStruct.new(hash)
27
+ hash = MultiXml.parse(Prpcrypt.decrypt(aes_key, @body_xml.Encrypt, corp_id))["xml"]
28
+ @weixin_message = Message.factory(hash)
29
+ @keyword = @weixin_message.Content
30
+ end
31
+
32
+ def encoding_aes_key
33
+ @qy_account.encoding_aes_key
34
+ end
35
+
36
+ def qy_token
37
+ @qy_account.qy_token
38
+ end
39
+
40
+ def aes_key
41
+ Base64.decode64(@qy_account.encoding_aes_key + "=")
42
+ end
43
+
44
+ def corp_id
45
+ @qy_account.corp_id
46
+ end
47
+
48
+ # String signature = SHA1.getSHA1(token, timeStamp, nonce, echoStr);
49
+ def valid_msg_signature(params)
50
+ timestamp = params[:timestamp]
51
+ nonce = params[:nonce]
52
+ echo_str = params[:echostr]
53
+ msg_signature = params[:msg_signature]
54
+ sort_params = [qy_token, timestamp, nonce, echo_str].sort.join
55
+ current_signature = Digest::SHA1.hexdigest(sort_params)
56
+ Rails.logger.info("current_signature: #{current_signature} " )
57
+ current_signature == msg_signature
58
+ end
59
+
60
+ def setup_qy_account
61
+ @qy_account ||= QyWechat.qy_model.find_by(qy_secret_key: params[:qy_secret_key])
62
+ end
63
+ end
64
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,4 @@
1
+ QyWechat::Engine.routes.draw do
2
+ get "qy_wechat/:qy_secret_key", to: "qy_wechat#verify_url", as: :qy_verify
3
+ post "qy_wechat/:qy_secret_key", to: "qy_wechat#reply", as: :qy_reply_reply
4
+ end
@@ -0,0 +1,35 @@
1
+ # Rails::Generators::Base dont need a name
2
+ # Rails::Generators::NamedBase need a name
3
+ module QyWechat
4
+ module Generators
5
+ class InstallGenerator < Rails::Generators::Base
6
+ source_root File.expand_path('../../templates', __FILE__)
7
+
8
+ desc 'Creates a QyWechat initializer for your application.'
9
+
10
+ def install
11
+ route 'mount QyWechat::Engine, at: "/"'
12
+ end
13
+
14
+ def copy_initializer
15
+ template 'qy_wechat_config.rb', 'config/initializers/qy_wechat_config.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 'qy_wechat_controller.rb', 'app/decorators/controllers/qy_wechat/qy_wechat_controller_decorator.rb'
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,35 @@
1
+ require 'rails/generators/active_record'
2
+
3
+ module QyWechat
4
+ module Generators
5
+ class MigrationGenerator < ActiveRecord::Generators::Base
6
+ source_root File.expand_path('../../templates', __FILE__)
7
+
8
+ desc 'Adds a qy qy_token, encoding_aes_key, corp_id for your application.'
9
+ def create_migration_file
10
+ if !migration_exists?(table_name)
11
+ migration_template "add_qy_wechat_columns.rb", "db/migrate/add_qy_wechat_columns_to_#{plural_name}.rb"
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def model_exists?
18
+ File.exists?(File.join(destination_root, model_path))
19
+ end
20
+
21
+ def model_path
22
+ @model_path ||= File.join("app", "models", "#{file_path}.rb")
23
+ end
24
+
25
+ def migration_exists?(table_name)
26
+ Dir.glob("#{File.join(destination_root, migration_path)}/[0-9]*_*.rb").grep(/\d+_add_qy_wechat_columns_to_#{table_name}.rb/).first
27
+ end
28
+
29
+ def migration_path
30
+ @migration_path ||= File.join("db", "migrate")
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,20 @@
1
+ class AddQyWechatColumnsTo<%= table_name.camelize %> < ActiveRecord::Migration
2
+ def self.up
3
+ create_table(:<%= table_name %>) do |t|
4
+ t.string :qy_token
5
+ t.string :encoding_aes_key
6
+ t.string :corp_id
7
+ t.string :qy_secret_key
8
+ end
9
+ add_index :<%= table_name %>, :qy_token
10
+ add_index :<%= table_name %>, :encoding_aes_key
11
+ add_index :<%= table_name %>, :corp_id
12
+ add_index :<%= table_name %>, :qy_secret_key
13
+ end
14
+
15
+ def self.down
16
+ # By default, we don't want to make any assumption about how to roll back a migration when your
17
+ # model already existed. Please edit below which fields you would like to remove in this migration.
18
+ raise ActiveRecord::IrreversibleMigration
19
+ end
20
+ end
@@ -0,0 +1,4 @@
1
+ QyWechat.configure do |config|
2
+ # 配置保存你企业信息的Model
3
+ # config.qy_account = "QyAccount"
4
+ end
@@ -0,0 +1,30 @@
1
+ # encoding: utf-8
2
+ QyWechat::QyWechatController.class_eval do
3
+
4
+ def reply
5
+ render xml: send("response_#{@weixin_message.MsgType}_message", {})
6
+ end
7
+
8
+ private
9
+
10
+ # text消息
11
+ def response_text_message(options={})
12
+ generate_text_message("Your Message: #{@keyword}")
13
+ end
14
+
15
+ # image消息
16
+ def response_image_message(options={})
17
+ generate_image_message(new_image(@weixin_message.MediaId))
18
+ end
19
+
20
+ # voice消息
21
+ def response_voice_message(options={})
22
+ generate_voice_message(new_voice(@weixin_message.MediaId))
23
+ end
24
+
25
+ # video消息
26
+ def response_video_message(options={})
27
+ generate_video_message(new_video(@weixin_message.MediaId, "desc", "title"))
28
+ end
29
+
30
+ end
@@ -0,0 +1,31 @@
1
+ module QyWechat
2
+
3
+ class << self
4
+
5
+ attr_accessor :configuration
6
+
7
+ def config
8
+ self.configuration ||= Configuration.new
9
+ end
10
+
11
+ def configure
12
+ yield config if block_given?
13
+ end
14
+
15
+ def qy_model_name
16
+ @qy_model_name ||= QyWechat.config.qy_account
17
+ end
18
+
19
+ def qy_model
20
+ if qy_model_name.blank?
21
+ raise "You need to config `qy_account` in 'config/initializers/qy_wechat_config.rb'"
22
+ end
23
+ @qy_model ||= qy_model_name.to_s.constantize
24
+ end
25
+
26
+ end
27
+
28
+ class Configuration
29
+ attr_accessor :qy_account
30
+ end
31
+ end
@@ -0,0 +1,6 @@
1
+ module QyWechat
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace QyWechat
4
+ engine_name :qy_wechat_engine
5
+ end
6
+ end
@@ -0,0 +1,26 @@
1
+ module QyWechat
2
+ module PKCS7Encoder
3
+ extend self
4
+
5
+ BLOCK_SIZE = 32
6
+
7
+ def decode(text)
8
+ pad = text[-1].ord
9
+ pad = 0 if (pad < 1 || pad > BLOCK_SIZE)
10
+ size = text.size - pad
11
+ text[0...size]
12
+ end
13
+
14
+ # 对需要加密的明文进行填充补位
15
+ # 返回补齐明文字符串
16
+ def encode(text)
17
+ # 计算需要填充的位数
18
+ amount_to_pad = BLOCK_SIZE - (text.length % BLOCK_SIZE)
19
+ amount_to_pad = BLOCK_SIZE if amount_to_pad == 0
20
+ # 获得补位所用的字符
21
+ pad_chr = amount_to_pad.chr
22
+ "#{text}#{pad_chr * amount_to_pad}"
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+ module QyWechat
3
+ module Prpcrypt
4
+ extend self
5
+
6
+ # 对密文进行解密.
7
+ # text 需要解密的密文
8
+ def decrypt(aes_key, text, corpid)
9
+ text = Base64.decode64(text)
10
+ text = handle_cipher(:decrypt, aes_key, text)
11
+ result = PKCS7Encoder.decode(text)
12
+ content = result[16...result.length]
13
+ len_list = content[0...4].unpack("N")
14
+ xml_len = len_list[0]
15
+ xml_content = content[4...4 + xml_len]
16
+ from_corpid = content[xml_len+4...content.size]
17
+ raise "UnMatch corpid" if corpid != from_corpid
18
+ xml_content
19
+ end
20
+
21
+ # 加密
22
+ def encrypt(aes_key, text, corpid)
23
+ text = text.force_encoding("ASCII-8BIT")
24
+ random = SecureRandom.hex(8)
25
+ msg_len = [text.length].pack("N")
26
+ text = "#{random}#{msg_len}#{text}#{corpid}"
27
+ text = PKCS7Encoder.encode(text)
28
+ text = handle_cipher(:encrypt, aes_key, text)
29
+ Base64.encode64(text)
30
+ end
31
+
32
+ private
33
+ def handle_cipher(action, aes_key, text)
34
+ cipher = OpenSSL::Cipher.new('AES-256-CBC')
35
+ cipher.send(action)
36
+ cipher.padding = 0
37
+ cipher.key = aes_key
38
+ cipher.iv = aes_key[0...16]
39
+ cipher.update(text) + cipher.final
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,117 @@
1
+ module QyWechat
2
+ module ReplyMessageHelper
3
+
4
+ # e.g.
5
+ # generate_text_message(@weixin_message.ToUserName, @weixin_message.FromUserName, "Your Message: #{@weixin_message.Content}")
6
+ # Or generate_text_message("Your Message: #{@weixin_message.Content}")
7
+ def generate_text_message(from=nil, to=nil, content)
8
+ message = TextResponseMessage.new
9
+ message.FromUserName = from || @weixin_message.ToUserName
10
+ message.ToUserName = to || @weixin_message.FromUserName
11
+ message.Content = content
12
+ encrypt_message(message.to_xml)
13
+ end
14
+
15
+ def new_article(title, desc, pic_url, link_url)
16
+ item = Article.new
17
+ item.Title = title
18
+ item.Description = desc
19
+ item.PicUrl = pic_url
20
+ item.Url = link_url
21
+ item
22
+ end
23
+
24
+ # articles = [new_article]
25
+ def generate_news_message(from=nil, to=nil, articles)
26
+ message = NewsResponseMessage.new
27
+ message.FromUserName = from || @weixin_message.ToUserName
28
+ message.ToUserName = to || @weixin_message.FromUserName
29
+ message.Articles = articles
30
+ message.ArticleCount = articles.count
31
+ encrypt_message message.to_xml
32
+ end
33
+
34
+ def new_video(media_id, desc, title)
35
+ video = Video.new
36
+ video.MediaId = media_id
37
+ video.Title = title
38
+ video.Description = desc
39
+ video
40
+ end
41
+
42
+ # <xml>
43
+ # <ToUserName><![CDATA[toUser]]></ToUserName>
44
+ # <FromUserName><![CDATA[fromUser]]></FromUserName>
45
+ # <CreateTime>12345678</CreateTime>
46
+ # <MsgType><![CDATA[video]]></MsgType>
47
+ # <Video>
48
+ # <MediaId><![CDATA[media_id]]></MediaId>
49
+ # <Title><![CDATA[title]]></Title>
50
+ # <Description><![CDATA[description]]></Description>
51
+ # </Video>
52
+ # </xml>
53
+
54
+ def generate_video_message(from=nil, to=nil, video)
55
+ message = VideoResponseMessage.new
56
+ message.FromUserName = from || @weixin_message.ToUserName
57
+ message.ToUserName = to || @weixin_message.FromUserName
58
+ message.Video = video
59
+ encrypt_message message.to_xml
60
+ end
61
+
62
+ def new_voice(media_id)
63
+ voice = Voice.new
64
+ voice.MediaId = media_id
65
+ voice
66
+ end
67
+
68
+ def generate_voice_message(from=nil, to=nil, voice)
69
+ message = VoiceResponseMessage.new
70
+ message.FromUserName = from || @weixin_message.ToUserName
71
+ message.ToUserName = to || @weixin_message.FromUserName
72
+ message.Voice = voice
73
+ encrypt_message message.to_xml
74
+ end
75
+
76
+ def new_image(media_id)
77
+ image = Image.new
78
+ image.MediaId = media_id
79
+ image
80
+ end
81
+
82
+ def generate_image_message(from=nil, to=nil, image)
83
+ message = ImageResponseMessage.new
84
+ message.FromUserName = from || @weixin_message.ToUserName
85
+ message.ToUserName = to || @weixin_message.FromUserName
86
+ message.Image = image
87
+ encrypt_message message.to_xml
88
+ end
89
+
90
+ private
91
+
92
+ def encrypt_message(msg_xml)
93
+ # 加密回复的XML
94
+ encrypt_xml = Prpcrypt.encrypt(aes_key, msg_xml, corp_id).gsub("\n","")
95
+ # 标准的回包
96
+ generate_encrypt_message(encrypt_xml)
97
+ end
98
+
99
+ def generate_encrypt_message(encrypt_xml)
100
+ msg = EncryptMessage.new
101
+ msg.Encrypt = encrypt_xml
102
+ msg.TimeStamp = Time.now.to_i.to_s
103
+ msg.Nonce = "123"
104
+ msg.MsgSignature = generate_msg_signature(encrypt_xml, msg)
105
+ msg.to_xml
106
+ end
107
+
108
+ # dev_msg_signature=sha1(sort(token、timestamp、nonce、msg_encrypt))
109
+ # 生成企业签名
110
+ # $array = array($encrypt_msg, $token, $timestamp, $nonce);
111
+ def generate_msg_signature(encrypt_msg, msg)
112
+ sort_params = [encrypt_msg, qy_token, msg.TimeStamp, msg.Nonce].sort.join
113
+ Digest::SHA1.hexdigest(sort_params)
114
+ end
115
+
116
+ end
117
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+ # 标准的回包
3
+ # <xml>
4
+ # <Encrypt><![CDATA[msg_encrypt]]></Encrypt>
5
+ # <MsgSignature><![CDATA[msg_signature]]></MsgSignature>
6
+ # <TimeStamp>timestamp</TimeStamp>
7
+ # <Nonce><![CDATA[nonce]]></Nonce>
8
+ # </xml>
9
+
10
+ module QyWechat
11
+ class EncryptMessage
12
+ include ROXML
13
+ xml_name :xml
14
+
15
+ xml_accessor :Encrypt, :cdata => true
16
+ xml_accessor :Nonce, :cdata => true
17
+ xml_accessor :TimeStamp, :as => Integer
18
+ xml_accessor :MsgSignature, :cdata => true
19
+
20
+ def to_xml
21
+ super.to_xml(:encoding => 'UTF-8', :indent => 0, :save_with => 0)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,171 @@
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 QyWechat
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 corp_id
31
+ @source.FromUserName
32
+ end
33
+
34
+ # 应用ID
35
+ def agent_id
36
+ @source.AgentID
37
+ end
38
+
39
+ def self.factory(hash)
40
+ case hash['MsgType']
41
+ when 'text'
42
+ TextMessage.new(hash)
43
+ when 'image'
44
+ ImageMessage.new(hash)
45
+ when 'location'
46
+ LocationMessage.new(hash)
47
+ when 'link'
48
+ LinkMessage.new(hash)
49
+ when 'event'
50
+ EventMessage.new(hash)
51
+ when 'voice'
52
+ VoiceMessage.new(hash)
53
+ when 'video'
54
+ VideoMessage.new(hash)
55
+ else
56
+ raise ArgumentError, 'Unknown Message'
57
+ end
58
+ end
59
+
60
+ end
61
+
62
+ # <xml>
63
+ # <ToUserName><![CDATA[toUser]]></ToUserName>
64
+ # <FromUserName><![CDATA[fromUser]]></FromUserName>
65
+ # <CreateTime>1348831860</CreateTime>
66
+ # <MsgType><![CDATA[text]]></MsgType>
67
+ # <Content><![CDATA[this is a test]]></Content>
68
+ # <MsgId>1234567890123456</MsgId>
69
+ # </xml>
70
+ TextMessage = Class.new(Message)
71
+
72
+ # <xml>
73
+ # <ToUserName><![CDATA[toUser]]></ToUserName>
74
+ # <FromUserName><![CDATA[fromUser]]></FromUserName>
75
+ # <CreateTime>1348831860</CreateTime>
76
+ # <MsgType><![CDATA[image]]></MsgType>
77
+ # <PicUrl><![CDATA[this is a url]]></PicUrl>
78
+ # <MsgId>1234567890123456</MsgId>
79
+ # </xml>
80
+ ImageMessage = Class.new(Message)
81
+
82
+ # <xml>
83
+ # <ToUserName><![CDATA[toUser]]></ToUserName>
84
+ # <FromUserName><![CDATA[fromUser]]></FromUserName>
85
+ # <CreateTime>1351776360</CreateTime>
86
+ # <MsgType><![CDATA[link]]></MsgType>
87
+ # <Title><![CDATA[公众平台官网链接]]></Title>
88
+ # <Description><![CDATA[公众平台官网链接]]></Description>
89
+ # <Url><![CDATA[url]]></Url>
90
+ # <MsgId>1234567890123456</MsgId>
91
+ # </xml>
92
+ LinkMessage = Class.new(Message)
93
+
94
+ # <xml>
95
+ # <ToUserName><![CDATA[toUser]]></ToUserName>
96
+ # <FromUserName><![CDATA[FromUser]]></FromUserName>
97
+ # <CreateTime>123456789</CreateTime>
98
+ # <MsgType><![CDATA[event]]></MsgType>
99
+ # <Event><![CDATA[EVENT]]></Event>
100
+ # <EventKey><![CDATA[EVENTKEY]]></EventKey>
101
+ # </xml>
102
+ EventMessage = Class.new(Message)
103
+
104
+ # <xml>
105
+ # <ToUserName><![CDATA[toUser]]></ToUserName>
106
+ # <FromUserName><![CDATA[fromUser]]></FromUserName>
107
+ # <CreateTime>1351776360</CreateTime>
108
+ # <MsgType><![CDATA[location]]></MsgType>
109
+ # <Location_X>23.134521</Location_X>
110
+ # <Location_Y>113.358803</Location_Y>
111
+ # <Scale>20</Scale>
112
+ # <Label><![CDATA[位置信息]]></Label>
113
+ # <MsgId>1234567890123456</MsgId>
114
+ # </xml>
115
+ class LocationMessage < Message
116
+
117
+ def Location_X
118
+ @source.Location_X.to_f
119
+ end
120
+
121
+ def Location_Y
122
+ @source.Location_Y.to_f
123
+ end
124
+
125
+ def Scale
126
+ @source.Scale.to_i
127
+ end
128
+ end
129
+
130
+ # <xml>
131
+ # <ToUserName><![CDATA[toUser]]></ToUserName>
132
+ # <FromUserName><![CDATA[fromUser]]></FromUserName>
133
+ # <CreateTime>1376632760</CreateTime>
134
+ # <MsgType><![CDATA[voice]]></MsgType>
135
+ # <MediaId><![CDATA[Qyb0tgux6QLjhL6ipvFZJ-kUt2tcQtkn0BU365Vt3wUAtqfGam4QpZU35RXVhv6G]]></MediaId>
136
+ # <Format><![CDATA[amr]]></Format>
137
+ # <MsgId>5912592682802219078</MsgId>
138
+ # <Recognition><![CDATA[]]></Recognition>
139
+ # </xml>
140
+ class VoiceMessage < Message
141
+
142
+ def MediaId
143
+ @source.MediaId
144
+ end
145
+
146
+ def Format
147
+ @source.Format
148
+ end
149
+ end
150
+
151
+ # <xml>
152
+ # <ToUserName><![CDATA[toUser]]></ToUserName>
153
+ # <FromUserName><![CDATA[fromUser]]></FromUserName>
154
+ # <CreateTime>1376632994</CreateTime>
155
+ # <MsgType><![CDATA[video]]></MsgType>
156
+ # <MediaId><![CDATA[TAAGb6iS5LcZR1d5ICiZTWGWi6-Upic9tlWDpAKcNJA]]></MediaId>
157
+ # <ThumbMediaId><![CDATA[U-xulPW4kq6KKMWFNaBSPc65Bcgr7Qopwex0DfCeyQs]]></ThumbMediaId>
158
+ # <MsgId>5912593687824566343</MsgId>
159
+ # </xml>
160
+ class VideoMessage < Message
161
+
162
+ def MediaId
163
+ @source.MediaId
164
+ end
165
+
166
+ def ThumbMediaId
167
+ @source.ThumbMediaId
168
+ end
169
+ end
170
+
171
+ end
@@ -0,0 +1,152 @@
1
+ # encoding: utf-8
2
+ # ref: https://github.com/wolfg1969/rack-weixin/lib/weixin/model.rb
3
+ require 'roxml'
4
+
5
+ module QyWechat
6
+
7
+ class ResponseMessage
8
+ include ROXML
9
+ xml_name :xml
10
+
11
+ xml_accessor :ToUserName, :cdata => true
12
+ xml_accessor :FromUserName, :cdata => true
13
+ xml_reader :CreateTime, :as => Integer
14
+ xml_reader :MsgType, :cdata => true
15
+
16
+ def initialize
17
+ @CreateTime = Time.now.to_i
18
+ end
19
+
20
+ def to_xml
21
+ super.to_xml(:encoding => 'UTF-8', :indent => 0, :save_with => 0)
22
+ end
23
+ end
24
+
25
+ # <xml>
26
+ # <ToUserName><![CDATA[toUser]]></ToUserName>
27
+ # <FromUserName><![CDATA[fromUser]]></FromUserName>
28
+ # <CreateTime>12345678</CreateTime>
29
+ # <MsgType><![CDATA[text]]></MsgType>
30
+ # <Content><![CDATA[Hello]]></Content>
31
+ # </xml>
32
+
33
+ class TextResponseMessage < ResponseMessage
34
+ xml_accessor :Content, :cdata => true
35
+ def initialize
36
+ super
37
+ @MsgType = 'text'
38
+ end
39
+ end
40
+
41
+ class Article
42
+ include ROXML
43
+ xml_accessor :Title, :cdata => true
44
+ xml_accessor :Description, :cdata => true
45
+ xml_accessor :PicUrl, :cdata => true
46
+ xml_accessor :Url, :cdata => true
47
+ end
48
+
49
+ # <xml>
50
+ # <ToUserName><![CDATA[toUser]]></ToUserName>
51
+ # <FromUserName><![CDATA[fromUser]]></FromUserName>
52
+ # <CreateTime>12345678</CreateTime>
53
+ # <MsgType><![CDATA[news]]></MsgType>
54
+ # <ArticleCount>2</ArticleCount>
55
+ # <Articles>
56
+ # <item>
57
+ # <Title><![CDATA[title1]]></Title>
58
+ # <Description><![CDATA[description1]]></Description>
59
+ # <PicUrl><![CDATA[picurl]]></PicUrl>
60
+ # <Url><![CDATA[url]]></Url>
61
+ # </item>
62
+ # <item>
63
+ # <Title><![CDATA[title]]></Title>
64
+ # <Description><![CDATA[description]]></Description>
65
+ # <PicUrl><![CDATA[picurl]]></PicUrl>
66
+ # <Url><![CDATA[url]]></Url>
67
+ # </item>
68
+ # </Articles>
69
+ # </xml>
70
+
71
+ class NewsResponseMessage < ResponseMessage
72
+ xml_accessor :ArticleCount, :as => Integer
73
+ xml_accessor :Articles, :as => [Article], :in => 'Articles', :from => 'item'
74
+ def initialize
75
+ super
76
+ @MsgType = 'news'
77
+ end
78
+ end
79
+
80
+ # <xml>
81
+ # <ToUserName><![CDATA[toUser]]></ToUserName>
82
+ # <FromUserName><![CDATA[fromUser]]></FromUserName>
83
+ # <CreateTime>12345678</CreateTime>
84
+ # <MsgType><![CDATA[video]]></MsgType>
85
+ # <Video>
86
+ # <MediaId><![CDATA[media_id]]></MediaId>
87
+ # <Title><![CDATA[title]]></Title>
88
+ # <Description><![CDATA[description]]></Description>
89
+ # </Video>
90
+ # </xml>
91
+
92
+ class Video
93
+ include ROXML
94
+ xml_accessor :MediaId, :cdata => true
95
+ xml_accessor :Description, :cdata => true
96
+ xml_accessor :Title, :cdata => true
97
+ end
98
+
99
+ class VideoResponseMessage < ResponseMessage
100
+ xml_accessor :Video, :as => Video
101
+ def initialize
102
+ super
103
+ @MsgType = 'video'
104
+ end
105
+ end
106
+
107
+ # <xml>
108
+ # <ToUserName><![CDATA[toUser]]></ToUserName>
109
+ # <FromUserName><![CDATA[fromUser]]></FromUserName>
110
+ # <CreateTime>12345678</CreateTime>
111
+ # <MsgType><![CDATA[voice]]></MsgType>
112
+ # <Voice>
113
+ # <MediaId><![CDATA[media_id]]></MediaId>
114
+ # </Voice>
115
+ # </xml>
116
+ class Voice
117
+ include ROXML
118
+ xml_accessor :MediaId, :cdata => true
119
+ end
120
+
121
+ class VoiceResponseMessage < ResponseMessage
122
+ xml_accessor :Voice, :as => Voice
123
+ def initialize
124
+ super
125
+ @MsgType = 'voice'
126
+ end
127
+ end
128
+
129
+ # <xml>
130
+ # <ToUserName><![CDATA[toUser]]></ToUserName>
131
+ # <FromUserName><![CDATA[fromUser]]></FromUserName>
132
+ # <CreateTime>12345678</CreateTime>
133
+ # <MsgType><![CDATA[image]]></MsgType>
134
+ # <Image>
135
+ # <MediaId><![CDATA[media_id]]></MediaId>
136
+ # </Image>
137
+ # </xml>
138
+
139
+ class Image
140
+ include ROXML
141
+ xml_accessor :MediaId, :cdata => true
142
+ end
143
+
144
+ class ImageResponseMessage < ResponseMessage
145
+ xml_accessor :Image, :as => Image
146
+ def initialize
147
+ super
148
+ @MsgType = 'image'
149
+ end
150
+ end
151
+
152
+ end
@@ -0,0 +1,3 @@
1
+ module QyWechat
2
+ VERSION = "1.0.0.beta1"
3
+ end
data/lib/qy_wechat.rb ADDED
@@ -0,0 +1,16 @@
1
+ require 'roxml'
2
+ require 'multi_xml'
3
+ require 'ostruct'
4
+
5
+ require "qy_wechat/configuration"
6
+ require "qy_wechat/engine"
7
+ require "qy_wechat/message/encrypt_message"
8
+ require "qy_wechat/message/message"
9
+ require "qy_wechat/message/response_message"
10
+
11
+ require "qy_wechat/helpers/pkcs7_encoder"
12
+ require "qy_wechat/helpers/prpcrypt"
13
+ require "qy_wechat/helpers/reply_message_helper"
14
+
15
+ module QyWechat
16
+ end
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: qy_wechat
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0.beta1
5
+ platform: ruby
6
+ authors:
7
+ - lanrion
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-11-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: railties
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '3.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '3.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: nokogiri
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.6.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 1.6.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '3.1'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '3.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: multi_xml
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 0.5.2
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 0.5.2
69
+ - !ruby/object:Gem::Dependency
70
+ name: roxml
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: "微信企业版本,应答API集成"
84
+ email:
85
+ - huaitao-deng@foxmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - MIT-LICENSE
91
+ - Rakefile
92
+ - app/controllers/qy_wechat/qy_wechat_controller.rb
93
+ - config/routes.rb
94
+ - lib/generators/qy_wechat/install_generator.rb
95
+ - lib/generators/qy_wechat/migration_generator.rb
96
+ - lib/generators/templates/add_qy_wechat_columns.rb
97
+ - lib/generators/templates/qy_wechat_config.rb
98
+ - lib/generators/templates/qy_wechat_controller.rb
99
+ - lib/qy_wechat.rb
100
+ - lib/qy_wechat/configuration.rb
101
+ - lib/qy_wechat/engine.rb
102
+ - lib/qy_wechat/helpers/pkcs7_encoder.rb
103
+ - lib/qy_wechat/helpers/prpcrypt.rb
104
+ - lib/qy_wechat/helpers/reply_message_helper.rb
105
+ - lib/qy_wechat/message/encrypt_message.rb
106
+ - lib/qy_wechat/message/message.rb
107
+ - lib/qy_wechat/message/response_message.rb
108
+ - lib/qy_wechat/version.rb
109
+ homepage: https://github.com/lanrion/qy_wechat
110
+ licenses:
111
+ - MIT
112
+ metadata: {}
113
+ post_install_message:
114
+ rdoc_options: []
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ required_rubygems_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">"
125
+ - !ruby/object:Gem::Version
126
+ version: 1.3.1
127
+ requirements: []
128
+ rubyforge_project:
129
+ rubygems_version: 2.2.2
130
+ signing_key:
131
+ specification_version: 4
132
+ summary: "微信企业版本"
133
+ test_files: []
134
+ has_rdoc: