wechat-rails 0.1.0

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: 49c382d50e750b4bb75413972a7f8ddc74a372a1
4
+ data.tar.gz: ce7ac829af66603e79aee9ae127eec3e077f8e0f
5
+ SHA512:
6
+ metadata.gz: 4a67f307848a3bb0fa57737fd50c136a987dbc9234f84b8a389163c1662e6fdb49b179b7e2bab4ef57f92e94e068404e7b166cb9a58a433cacc7b36990f4989a
7
+ data.tar.gz: b32b63a48c6cd730c0ebc590945dc38ef886b502459eb3aabf723ca844343740de9b8df80e5907d21533964a4db49436cd35b42e7a88bc556839b812c16512ba
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 skinnyworm
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,5 @@
1
+ Wechat Rails
2
+ ======================
3
+
4
+ [![Build Status](https://travis-ci.org/skinnyworm/omniauth-wechat-oauth2.svg)](https://travis-ci.org/skinnyworm/wechat-rails) [![Code Climate](https://codeclimate.com/github/skinnyworm/wechat-rails.png)](https://codeclimate.com/github/skinnyworm/wechat-rails) [![Code Coverage](https://codeclimate.com/github/skinnyworm/wechat-rails/coverage.png)](https://codeclimate.com/github/skinnyworm/wechat-rails)
5
+
data/Rakefile ADDED
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'WechatRails'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+
24
+ require File.join('bundler', 'gem_tasks')
25
+ require File.join('rspec', 'core', 'rake_task')
26
+ RSpec::Core::RakeTask.new(:spec)
27
+
28
+
29
+ Bundler::GemHelper.install_tasks
30
+
31
+ task :default => :spec
data/bin/wechat ADDED
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+ $LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
5
+
6
+ require 'thor'
7
+ require "wechat-rails"
8
+ require 'json'
9
+ require "active_support/core_ext"
10
+ require 'fileutils'
11
+
12
+
13
+
14
+ class App < Thor
15
+ class Helper
16
+ def self.with(options)
17
+ appid = ENV["WECHAT_APPID"]
18
+ secret = ENV["WECHAT_SECRET"]
19
+ token_file = options[:toke_file] || ENV["WECHAT_ACCESS_TOKEN"]
20
+
21
+ if (appid.nil? || secret.nil? || token_file.nil?)
22
+ puts <<-HELP
23
+ You need set wechat appid and secret in environment variables.
24
+
25
+ export WECHAT_APPID=<appid>
26
+ export WECHAT_SECRET=<secret>
27
+ export WECHAT_ACCESS_TOKEN=<file location for storing access token>
28
+
29
+ HELP
30
+ exit 1
31
+ end
32
+ Wechat::Api.new(appid, secret, token_file)
33
+ end
34
+ end
35
+
36
+ package_name "Wechat"
37
+ option :toke_file, :aliases=>"-t", :desc => "File to store access token"
38
+
39
+ desc "users", "关注者列表"
40
+ def users
41
+ puts Helper.with(options).users
42
+ end
43
+
44
+ desc "user [OPEN_ID]", "查找关注者"
45
+ def user(open_id)
46
+ puts Helper.with(options).user(open_id)
47
+ end
48
+
49
+ desc "menu", "当前菜单"
50
+ def menu
51
+ puts Helper.with(options).menu
52
+ end
53
+
54
+ desc "menu_delete", "删除菜单"
55
+ def menu_delete
56
+ puts "Menu deleted" if Helper.with(options).menu_delete
57
+ end
58
+
59
+ desc "menu_create [MENU_YAML]", "删除菜单"
60
+ def menu_create(menu_yaml)
61
+ menu = YAML.load(File.new(menu_yaml).read)
62
+ puts "Menu created" if Helper.with(options).menu_create(menu)
63
+ end
64
+
65
+ desc "media [MEDIA_ID, PATH]", "媒体下载"
66
+ def media(media_id, path)
67
+ tmp_file = Helper.with(options).media(media_id)
68
+ FileUtils.mv(tmp_file.path, path)
69
+ puts "File downloaded"
70
+ end
71
+
72
+ desc "media_create [MEDIA_ID, PATH]", "媒体上传"
73
+ def media_create(type, path)
74
+ file = File.new(path)
75
+ puts Helper.with(options).media_create(type, file)
76
+ end
77
+
78
+ desc "custom_text [OPENID, TEXT_MESSAGE]", "发送文字客服消息"
79
+ def custom_text openid, text_message
80
+ puts Helper.with(options).custom_message_send Wechat::Message.to(openid).text(text_message)
81
+ end
82
+
83
+ desc "custom_image [OPENID, IMAGE_PATH]", "发送图片客服消息"
84
+ def custom_image openid, image_path
85
+ file = File.new(image_path)
86
+ api = Helper.with(options)
87
+
88
+ media_id = api.media_create("image", file)["media_id"]
89
+ puts api.custom_message_send Wechat::Message.to(openid).image(media_id)
90
+ end
91
+
92
+ desc "custom_voice [OPENID, VOICE_PATH]", "发送语音客服消息"
93
+ def custom_voice openid, voice_path
94
+ file = File.new(voice_path)
95
+ api = Helper.with(options)
96
+
97
+ media_id = api.media_create("voice", file)["media_id"]
98
+ puts api.custom_message_send Wechat::Message.to(openid).voice(media_id)
99
+ end
100
+
101
+ desc "custom_video [OPENID, VIDEO_PATH]", "发送视频客服消息"
102
+ method_option :title, :aliases => "-h", :desc => "视频标题"
103
+ method_option :description, :aliases => "-d", :desc => "视频描述"
104
+ def custom_video openid, video_path
105
+ file = File.new(video_path)
106
+ api = Helper.with(options)
107
+
108
+ api_opts = options.slice(:title, :description)
109
+ media_id = api.media_create("video", file)["media_id"]
110
+ puts api.custom_message_send Wechat::Message.to(openid).video(media_id, api_opts)
111
+ end
112
+
113
+ desc "custom_music [OPENID, THUMBNAIL_PATH, MUSIC_URL]", "发送音乐客服消息"
114
+ method_option :title, :aliases => "-h", :desc => "音乐标题"
115
+ method_option :description, :aliases => "-d", :desc => "音乐描述"
116
+ method_option :HQ_music_url, :aliases => "-u", :desc => "高质量音乐URL链接"
117
+ def custom_music openid, thumbnail_path, music_url
118
+ file = File.new(thumbnail_path)
119
+ api = Helper.with(options)
120
+
121
+ api_opts = options.slice(:title, :description, :HQ_music_url)
122
+ thumb_media_id = api.media_create("thumb", file)["thumb_media_id"]
123
+ puts api.custom_message_send Wechat::Message.to(openid).music(thumb_media_id, music_url, api_opts)
124
+ end
125
+
126
+ desc "custom_news [OPENID, NEWS_YAML_FILE]", "发送图文客服消息"
127
+ def custom_news openid, news_yaml
128
+ articles = YAML.load(File.new(news_yaml).read)
129
+ puts Helper.with(options).custom_message_send Wechat::Message.to(openid).news(articles["articles"])
130
+ end
131
+
132
+ end
133
+
134
+ App.start
@@ -0,0 +1,39 @@
1
+ require "wechat/api"
2
+
3
+ module Wechat
4
+ autoload :Message, "wechat/message"
5
+ autoload :Responder, "wechat/responder"
6
+ autoload :Response, "wechat/response"
7
+
8
+ class AccessTokenExpiredError < StandardError; end
9
+ class ResponseError < StandardError
10
+ attr_reader :error_code
11
+ def initialize(errcode, errmsg)
12
+ error_code = errcode
13
+ super "#{errmsg}(#{error_code})"
14
+ end
15
+ end
16
+
17
+ attr_reader :config
18
+
19
+ def self.config
20
+ @config ||= OpenStruct.new(
21
+ app_id: ENV["WECHAT_APPID"],
22
+ secret: ENV["WECHAT_SECRET"],
23
+ token: ENV["WECHAT_TOKEN"],
24
+ access_token: ENV["WECHAT_ACCESS_TOKEN"]
25
+ )
26
+ end
27
+
28
+ def self.api
29
+ @api ||= Wechat::Api.new(self.config.app_id, self.config.secret, self.config.access_token)
30
+ end
31
+ end
32
+
33
+ if defined? ActionController::Base
34
+ class ActionController::Base
35
+ def self.wechat_rails
36
+ self.send(:include, Wechat::Responder)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,35 @@
1
+ module Wechat
2
+ class AccessToken
3
+ attr_reader :client, :appid, :secret, :token_file, :token_data
4
+
5
+ def initialize(client, appid, secret, token_file)
6
+ @appid = appid
7
+ @secret = secret
8
+ @client = client
9
+ @token_file = token_file
10
+ end
11
+
12
+ def token
13
+ begin
14
+ @token_data ||= JSON.parse(File.read(token_file))
15
+ rescue
16
+ self.refresh
17
+ end
18
+ return valid_token(@token_data)
19
+ end
20
+
21
+ def refresh
22
+ data = client.get("token", params:{grant_type: "client_credential", appid: appid, secret: secret})
23
+ File.open(token_file, 'w'){|f| f.write(data.to_s)} if valid_token(data)
24
+ return @token_data = data
25
+ end
26
+
27
+ private
28
+ def valid_token token_data
29
+ access_token = token_data["access_token"]
30
+ raise "Response didn't have access_token" if access_token.blank?
31
+ return access_token
32
+ end
33
+
34
+ end
35
+ end
data/lib/wechat/api.rb ADDED
@@ -0,0 +1,68 @@
1
+ require 'wechat/client'
2
+ require 'wechat/access_token'
3
+
4
+ class Wechat::Api
5
+ attr_reader :app_id, :secret, :access_token, :client
6
+
7
+ API_BASE = "https://api.weixin.qq.com/cgi-bin/"
8
+ FILE_BASE = "http://file.api.weixin.qq.com/cgi-bin/"
9
+
10
+ def initialize app_id, secret, token_file
11
+ @client = Wechat::Client.new(API_BASE)
12
+ @access_token = Wechat::AccessToken.new(@client, app_id, secret, token_file)
13
+ end
14
+
15
+ def users
16
+ get("user/get")
17
+ end
18
+
19
+ def user openid
20
+ get("user/info", params:{openid: openid})
21
+ end
22
+
23
+ def menu
24
+ get("menu/get")
25
+ end
26
+
27
+ def menu_delete
28
+ get("menu/delete")
29
+ end
30
+
31
+ def menu_create menu
32
+ # 微信不接受7bit escaped json(eg \uxxxx), 中文必须UTF-8编码, 这可能是个安全漏洞
33
+ post("menu/create", JSON.generate(menu))
34
+ end
35
+
36
+ def media media_id
37
+ response = get "media/get", params:{media_id: media_id}, base: FILE_BASE, as: :file
38
+ end
39
+
40
+ def media_create type, file
41
+ post "media/upload", {upload:{media: file}}, params:{type: type}, base: FILE_BASE
42
+ end
43
+
44
+ def custom_message_send message
45
+ post "message/custom/send", message.to_json, content_type: :json
46
+ end
47
+
48
+
49
+ protected
50
+ def get path, headers={}
51
+ with_access_token(headers[:params]){|params| client.get path, headers.merge(params: params)}
52
+ end
53
+
54
+ def post path, payload, headers = {}
55
+ with_access_token(headers[:params]){|params| client.post path, payload, headers.merge(params: params)}
56
+ end
57
+
58
+ def with_access_token params={}, tries=2
59
+ begin
60
+ params ||= {}
61
+ yield(params.merge(access_token: access_token.token))
62
+ rescue Wechat::AccessTokenExpiredError => ex
63
+ access_token.refresh
64
+ retry unless (tries -= 1).zero?
65
+ end
66
+ end
67
+
68
+ end
@@ -0,0 +1,74 @@
1
+ require 'rest_client'
2
+
3
+ module Wechat
4
+ class Client
5
+
6
+ attr_reader :base
7
+
8
+ def initialize(base)
9
+ @base = base
10
+ end
11
+
12
+ def get path, header={}
13
+ request(path, header) do |url, header|
14
+ RestClient.get(url, header)
15
+ end
16
+ end
17
+
18
+ def post path, payload, header = {}
19
+ request(path, header) do |url, header|
20
+ RestClient.post(url, payload, header)
21
+ end
22
+ end
23
+
24
+ def request path, header={}, &block
25
+ url = "#{header.delete(:base) || self.base}#{path}"
26
+ as = header.delete(:as)
27
+ header.merge!(:accept => :json)
28
+ response = yield(url, header)
29
+
30
+ raise "Request not OK, response code #{response.code}" if response.code != 200
31
+ parse_response(response, as || :json) do |parse_as, data|
32
+ break data unless (parse_as == :json && data["errcode"].present?)
33
+
34
+ case data["errcode"]
35
+ when 0 # for request didn't expect results
36
+ true
37
+
38
+ when 42001, 40014 #42001: access_token超时, 40014:不合法的access_token
39
+ raise AccessTokenExpiredError
40
+
41
+ else
42
+ raise ResponseError.new(data['errcode'], data['errmsg'])
43
+ end
44
+ end
45
+ end
46
+
47
+ private
48
+ def parse_response response, as
49
+ content_type = response.headers[:content_type]
50
+ parse_as = {
51
+ /^application\/json/ => :json,
52
+ /^image\/.*/ => :file
53
+ }.inject([]){|memo, match| memo<<match[1] if content_type =~ match[0]; memo}.first || as || :text
54
+
55
+ case parse_as
56
+ when :file
57
+ file = Tempfile.new("tmp")
58
+ file.binmode
59
+ file.write(response.body)
60
+ file.close
61
+ data = file
62
+
63
+ when :json
64
+ data = JSON.parse(response.body)
65
+
66
+ else
67
+ data = response.body
68
+ end
69
+
70
+ return yield(parse_as, data)
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,163 @@
1
+ module Wechat
2
+ class Message
3
+
4
+ JSON_KEY_MAP = {
5
+ "ToUserName" => "touser",
6
+ "MediaId" => "media_id",
7
+ "ThumbMediaId" => "thumb_media_id"
8
+ }
9
+
10
+ class << self
11
+ def from_hash message_hash
12
+ self.new(message_hash)
13
+ end
14
+
15
+ def to to_user
16
+ self.new(:ToUserName=>to_user, :CreateTime=>Time.now.to_i)
17
+ end
18
+ end
19
+
20
+ class ArticleBuilder
21
+ attr_reader :items
22
+ delegate :count, to: :items
23
+ def initialize
24
+ @items=Array.new
25
+ end
26
+
27
+ def item title: "title", description: nil, pic_url: nil, url: nil
28
+ items << {:Title=> title, :Description=> description, :PicUrl=> pic_url, :Url=> url}
29
+ end
30
+ end
31
+
32
+ attr_reader :message_hash
33
+
34
+ def initialize(message_hash)
35
+ @message_hash = message_hash || {}
36
+ end
37
+
38
+ def [](key)
39
+ message_hash[key]
40
+ end
41
+
42
+ def reply
43
+ Message.new(
44
+ :ToUserName=>message_hash[:FromUserName],
45
+ :FromUserName=>message_hash[:ToUserName],
46
+ :CreateTime=>Time.now.to_i
47
+ )
48
+ end
49
+
50
+ def as type
51
+ case type
52
+ when :text
53
+ message_hash[:Content]
54
+
55
+ when :image, :voice, :video
56
+ Wechat.api.media(message_hash[:MediaId])
57
+
58
+ when :location
59
+ message_hash.slice(:Location_X, :Location_Y, :Scale, :Label).inject({}){|results, value|
60
+ results[value[0].to_s.underscore.to_sym] = value[1]; results}
61
+ else
62
+ raise "Don't know how to parse message as #{type}"
63
+ end
64
+ end
65
+
66
+ def to openid
67
+ update(:ToUserName=>openid)
68
+ end
69
+
70
+ def text content
71
+ update(:MsgType=>"text", :Content=>content)
72
+ end
73
+
74
+ def image media_id
75
+ update(:MsgType=>"image", :Image=>{:MediaId=>media_id})
76
+ end
77
+
78
+ def voice media_id
79
+ update(:MsgType=>"voice", :Voice=>{:MediaId=>media_id})
80
+ end
81
+
82
+ def video media_id, opts={}
83
+ video_fields = camelize_hash_keys(opts.slice(:title, :description).merge(media_id: media_id))
84
+ update(:MsgType=>"video", :Video=>video_fields)
85
+ end
86
+
87
+ def music thumb_media_id, music_url, opts={}
88
+ music_fields = camelize_hash_keys(opts.slice(:title, :description, :HQ_music_url).merge(music_url: music_url, thumb_media_id: thumb_media_id))
89
+ update(:MsgType=>"music", :Music=>music_fields)
90
+ end
91
+
92
+ def news collection, &block
93
+ if block_given?
94
+ article = ArticleBuilder.new
95
+ collection.each{|item| yield(article, item)}
96
+ items = article.items
97
+ else
98
+ items = collection.collect do |item|
99
+ camelize_hash_keys(item.symbolize_keys.slice(:title, :description, :pic_url, :url))
100
+ end
101
+ end
102
+
103
+ update(:MsgType=>"news", :ArticleCount=> items.count,
104
+ :Articles=> items.collect{|item| camelize_hash_keys(item)})
105
+ end
106
+
107
+ def to_xml
108
+ message_hash.to_xml(root: "xml", children: "item", skip_instruct: true, skip_types: true)
109
+ end
110
+
111
+ def to_json
112
+ json_hash = deep_recursive(message_hash) do |key, value|
113
+ key = key.to_s
114
+ [(JSON_KEY_MAP[key] || key.downcase), value]
115
+ end
116
+
117
+ json_hash.slice!("touser", "msgtype", "content", "image", "voice", "video", "music", "news", "articles").to_hash
118
+ case json_hash["msgtype"]
119
+ when "text"
120
+ json_hash["text"] = {"content" => json_hash.delete("content")}
121
+ when "news"
122
+ json_hash["news"] = {"articles" => json_hash.delete("articles")}
123
+ end
124
+ JSON.generate(json_hash)
125
+ end
126
+
127
+ def save_to! model_class
128
+ model = model_class.new(underscore_hash_keys(message_hash))
129
+ model.save!
130
+ return self
131
+ end
132
+
133
+ private
134
+ def camelize_hash_keys hash
135
+ deep_recursive(hash){|key, value| [key.to_s.camelize.to_sym, value]}
136
+ end
137
+
138
+ def underscore_hash_keys hash
139
+ deep_recursive(hash){|key, value| [key.to_s.underscore.to_sym, value]}
140
+ end
141
+
142
+ def update fields={}
143
+ message_hash.merge!(fields)
144
+ return self
145
+ end
146
+
147
+ def deep_recursive hash, &block
148
+ hash.inject({}) do |memo, val|
149
+ key,value = *val
150
+ case value.class.name
151
+ when "Hash"
152
+ value = deep_recursive(value, &block)
153
+ when "Array"
154
+ value = value.collect{|item| item.is_a?(Hash) ? deep_recursive(item, &block) : item}
155
+ end
156
+
157
+ key,value = yield(key, value)
158
+ memo.merge!(key => value)
159
+ end
160
+ end
161
+
162
+ end
163
+ end
@@ -0,0 +1,98 @@
1
+ module Wechat
2
+ module Responder
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ self.skip_before_filter :verify_authenticity_token
7
+ self.before_filter :verify_signature, only: [:show, :create]
8
+ end
9
+
10
+ module ClassMethods
11
+
12
+ def on message_type, with: nil, respond: nil, &block
13
+ raise "Unknow message type" unless message_type.in? [:text, :image, :voice, :video, :location, :link, :event, :fallback]
14
+ config=respond.nil? ? {} : {:respond=>respond}
15
+ config.merge!(:proc=>block) if block_given?
16
+
17
+ if (with.present? && !message_type.in?([:text, :event]))
18
+ raise "Only text and event message can take :with parameters"
19
+ else
20
+ config.merge!(:with=>with) if with.present?
21
+ end
22
+
23
+ responders(message_type) << config
24
+ return config
25
+ end
26
+
27
+ def responders type
28
+ @responders ||= Hash.new
29
+ @responders[type] ||= Array.new
30
+ end
31
+
32
+ def responder_for message, &block
33
+ message_type = message[:MsgType].to_sym
34
+ responders = responders(message_type)
35
+
36
+ case message_type
37
+ when :text
38
+ yield(* match_responders(responders, message[:Content]))
39
+
40
+ when :event
41
+ yield(* match_responders(responders, message[:Event]))
42
+
43
+ else
44
+ yield(responders.first)
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def match_responders responders, value
51
+ matched = responders.inject({scoped:nil, general:nil}) do |matched, responder|
52
+ condition = responder[:with]
53
+
54
+ if condition.nil?
55
+ matched[:general] ||= [responder, value]
56
+ next matched
57
+ end
58
+
59
+ if condition.is_a? Regexp
60
+ matched[:scoped] ||= [responder] + $~.captures if(value =~ condition)
61
+ else
62
+ matched[:scoped] ||= [responder, value] if(value == condition)
63
+ end
64
+ matched
65
+ end
66
+ return matched[:scoped] || matched[:general]
67
+ end
68
+ end
69
+
70
+
71
+ def show
72
+ render :text => params[:echostr]
73
+ end
74
+
75
+ def create
76
+ request = Wechat::Message.from_hash(params[:xml])
77
+ response = self.class.responder_for(request) do |responder, *args|
78
+ responder ||= self.class.responders(:fallback).first
79
+
80
+ next if responder.nil?
81
+ next request.reply.text responder[:respond] if (responder[:respond])
82
+ next responder[:proc].call(*args.unshift(request)) if (responder[:proc])
83
+ end
84
+
85
+ if response.respond_to? :to_xml
86
+ render xml: response.to_xml
87
+ else
88
+ render :nothing => true, :status => 200, :content_type => 'text/html'
89
+ end
90
+ end
91
+
92
+ private
93
+ def verify_signature
94
+ array = [Wechat.config.token, params[:timestamp], params[:nonce]].compact.sort
95
+ render :text => "Forbidden", :status => 403 if params[:signature] != Digest::SHA1.hexdigest(array.join)
96
+ end
97
+ end
98
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wechat-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Skinnyworm
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 3.2.14
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 3.2.14
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.0
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.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: rest-client
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 2.14.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 2.14.0
69
+ description: API and message handling for wechat in rails environment
70
+ email: askinnyworm@gmail.com
71
+ executables:
72
+ - wechat
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - LICENSE
77
+ - README.md
78
+ - Rakefile
79
+ - bin/wechat
80
+ - lib/wechat-rails.rb
81
+ - lib/wechat/access_token.rb
82
+ - lib/wechat/api.rb
83
+ - lib/wechat/client.rb
84
+ - lib/wechat/message.rb
85
+ - lib/wechat/responder.rb
86
+ homepage: https://github.com/skinnyworm/wechat-rails
87
+ licenses: []
88
+ metadata: {}
89
+ post_install_message:
90
+ rdoc_options: []
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ requirements: []
104
+ rubyforge_project:
105
+ rubygems_version: 2.2.1
106
+ signing_key:
107
+ specification_version: 4
108
+ summary: DSL for wechat message handling and api
109
+ test_files: []