weichat_rails 0.0.1

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: dbde8b2fd7ae43678424029ec5a2bde7b8ee99df
4
+ data.tar.gz: 4d380976e2671b6e8b547543efb9428d356b5f15
5
+ SHA512:
6
+ metadata.gz: ae0761e2533164dfd36d6dd1fee6ff174a7fe57febeb82ef363ab0fc690a75119d23bbba3f8e464511639924ab6ac1c29b8d02ebc80b88be585bcde25d07becb
7
+ data.tar.gz: cb58481410216c5d1b4885b10ee00ed08589b97f943d06cc9e5ef22a6f0c0c0f63688060b2cda088812bf6a7b989c0546db7152ee791a2863272701444a65510
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.swp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in weichat_rails.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 qmliu
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # WeichatRails
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'weichat_rails'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install weichat_rails
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it ( http://github.com/<my-github-username>/weichat_rails/fork )
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
30
+ touch readme
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,34 @@
1
+ module WeichatRails
2
+ class AccessToken
3
+ attr_reader :client, :appid, :secret
4
+
5
+ CacheScope = "#{Rails.application.class.parent_name}_access_token"
6
+ def initialize(client, appid, secret)
7
+ @appid = appid
8
+ @secret = secret
9
+ @client = client
10
+ end
11
+
12
+ #store token in rails.cache
13
+ def token
14
+ Rails.cache.fetch("#{CacheScope}#{appid}",expires_in: 7200) do
15
+ data = client.get("token", params:{grant_type: "client_credential", appid: appid, secret: secret})
16
+ valid_token(data)
17
+ end
18
+ end
19
+
20
+ #delete the cache
21
+ def refresh
22
+ Rails.cache.delete("#{CacheScope}#{appid}")
23
+ end
24
+
25
+ private
26
+ def valid_token token_data
27
+ access_token = token_data["access_token"]
28
+ raise "Response didn't have access_token" if access_token.blank?
29
+ return access_token
30
+ end
31
+
32
+ end
33
+ end
34
+
@@ -0,0 +1,103 @@
1
+ require 'weichat_rails/client'
2
+ require 'weichat_rails/access_token'
3
+
4
+ class WeichatRails::Api
5
+ attr_reader :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
+ KEFU_BASE = "https://api.weixin.qq.com/customservice/"
10
+ #https://api.weixin.qq.com/cgi-bin/customservice/getkflist?access_token=ACCESS_TOKEN
11
+
12
+ def initialize appid, secret
13
+ @client = WeichatRails::Client.new(API_BASE)
14
+ @access_token = WeichatRails::AccessToken.new(@client, appid, secret)
15
+ end
16
+
17
+ def users
18
+ get("user/get")
19
+ end
20
+
21
+ def user openid
22
+ get("user/info", params:{openid: openid})
23
+ end
24
+
25
+ def menu
26
+ get("menu/get")
27
+ end
28
+
29
+ def menu_delete
30
+ get("menu/delete")
31
+ end
32
+
33
+ def kefulist
34
+ get("customservice/getkflist")
35
+ end
36
+
37
+ def group_get
38
+ get("groups/get")
39
+ end
40
+
41
+ def group_user_id openid
42
+ json_str = JSON.generate({openid: openid})
43
+ post("groups/getid", json_str)
44
+ end
45
+
46
+ def group_update openid, to_groupid
47
+ json_str = JSON.generate({openid: openid,to_groupid: to_groupid})
48
+ post("groups/members/update", json_str)
49
+ end
50
+
51
+ def get_duokefu_records time,pageindex
52
+ json_str = JSON.generate({starttime: time.beginning_of_day.to_i,endtime: time.end_of_day.to_i,pagesize: 50,pageindex: pageindex})
53
+ post("msgrecord/getrecord",json_str,base: KEFU_BASE)
54
+ end
55
+
56
+ def menu_create menu
57
+ # 微信不接受7bit escaped json(eg \uxxxx), 中文必须UTF-8编码, 这可能是个安全漏洞
58
+ # 如果是rails4.0以上使用 to_json 即可,否则使用 JSON.generate(menu,:ascii_only => false)
59
+ # 但以上试过之后仍是不行,在rails c中却是可以的,所以因该是被其它旧版本的json gem给重写了,所以采用以下方法
60
+ # 原因找到了是:
61
+ # 如果传的hash为ActiveSupport::HashWithIndifferentAccess 对像,因为用的是3.2.19,所以其编码可能不对
62
+ # 直接用 Hash对像的话,用的是ruby 2.0 的方法,做JSON.generate的时候对中文不会再编码成 \uxxxx的形式
63
+ #
64
+ json_str = JSON.generate(menu)#.gsub!(/\\u([0-9a-z]{4})/){|u| [$1.to_i(16)].pack('U')}
65
+
66
+ post("menu/create", json_str)
67
+ end
68
+
69
+ #返回媒体文件
70
+ def media media_id
71
+ get "media/get", params:{media_id: media_id}, base: FILE_BASE, as: :file
72
+ end
73
+
74
+ def media_create type, file
75
+ post "media/upload", {upload:{media: file}}, params:{type: type}, base: FILE_BASE
76
+ end
77
+
78
+ def custom_message_send message
79
+ post "message/custom/send", message.to_json, content_type: :json
80
+ end
81
+
82
+
83
+ protected
84
+ def get path, headers={}
85
+ with_access_token(headers[:params]){|params| client.get path, headers.merge(params: params)}
86
+ end
87
+
88
+ def post path, payload, headers = {}
89
+ with_access_token(headers[:params]){|params| client.post path, payload, headers.merge(params: params)}
90
+ end
91
+
92
+ def with_access_token params={}, tries=2
93
+ begin
94
+ params ||= {}
95
+ yield(params.merge(access_token: access_token.token))
96
+ rescue WeichatRails::AccessTokenExpiredError
97
+ access_token.refresh
98
+ retry unless (tries -= 1).zero?
99
+ end
100
+ end
101
+
102
+ end
103
+
@@ -0,0 +1,25 @@
1
+ module WeichatRails
2
+ module AutoGenerateSecretKey
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ before_create do
7
+ self.wechat_secret_key = generate_wechat_secret_key(generator: :urlsafe_base64,size: 16).downcase
8
+ self.app_token = generate_wechat_secret_key
9
+ end
10
+ end
11
+
12
+
13
+ private
14
+
15
+ def generate_wechat_secret_key(options={})
16
+ # SecureRandom: hex, base64, random_bytes, urlsafe_base64, random_number, uuid
17
+ generator_method_type = options.delete(:generator).try(:to_sym) || :hex
18
+ generator_method = SecureRandom.method(generator_method_type)
19
+ token_size = options.delete(:size).try(:to_i) || 12
20
+ return generator_method.call if generator_method_type == :uuid
21
+ generator_method.call(token_size)
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,81 @@
1
+ require 'rest_client'
2
+
3
+ module WeichatRails
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
+ #break data if (parse_as != :json || data["errcode"].blank?)
34
+
35
+ #如果返回的是json数据并且errcode有值,则会对errcode再做开关判断
36
+
37
+ case data["errcode"]
38
+ when 0 # for request didn't expect results
39
+ [true,data]
40
+
41
+ when 42001, 40014 #42001: access_token超时, 40014:不合法的access_token
42
+ raise AccessTokenExpiredError
43
+
44
+ when 1613189120 # for wx duokefu' msg code
45
+ [true,data]
46
+
47
+ else
48
+ raise ResponseError.new(data['errcode'], data['errmsg'])
49
+ end
50
+ end
51
+ end
52
+
53
+ private
54
+ def parse_response response, as
55
+ content_type = response.headers[:content_type]
56
+ parse_as = {
57
+ /^application\/json/ => :json,
58
+ /^image\/.*/ => :file
59
+ }.inject([]){|memo, match| memo<<match[1] if content_type =~ match[0]; memo}.first || as || :text
60
+
61
+ case parse_as
62
+ when :file
63
+ file = Tempfile.new("tmp")
64
+ file.binmode
65
+ file.write(response.body)
66
+ file.close
67
+ data = file
68
+
69
+ when :json
70
+ data = JSON.parse(response.body)
71
+
72
+ else
73
+ data = response.body
74
+ end
75
+
76
+ return yield(parse_as, data)
77
+ end
78
+
79
+ end
80
+ end
81
+
@@ -0,0 +1,170 @@
1
+ module WeichatRails
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 msg_hash
12
+ self.new(msg_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(msg_hash)
35
+ @message_hash = msg_hash || {}
36
+ end
37
+
38
+ def [](key)
39
+ message_hash[key]
40
+ end
41
+
42
+ def reply
43
+ Message.new( :ToUserName=>message_hash[:FromUserName], :FromUserName=>message_hash[:ToUserName], :CreateTime=>Time.now.to_i)
44
+ end
45
+
46
+ def as type
47
+ case type
48
+ when :text
49
+ message_hash[:Content]
50
+
51
+ when :image, :voice, :video
52
+ WeichatRails.api.media(message_hash[:MediaId])
53
+
54
+ when :location
55
+ message_hash.slice(:Location_X, :Location_Y, :Scale, :Label).inject({}){|results, value|
56
+ results[value[0].to_s.underscore.to_sym] = value[1]
57
+ results
58
+ }
59
+ else
60
+ raise "Don't know how to parse message as #{type}"
61
+ end
62
+ end
63
+
64
+ #add wechat_user for load wechat_user in proc callback
65
+ #def wechat_user user
66
+ # update(:wechat_user=>user)
67
+ #end
68
+ def kefu msg_type
69
+ update(:MsgType => msg_type)
70
+ end
71
+
72
+ def to openid
73
+ update(:ToUserName=>openid)
74
+ end
75
+
76
+ def text content
77
+ update(:MsgType=>"text", :Content=>content)
78
+ end
79
+
80
+ def image media_id
81
+ update(:MsgType=>"image", :Image=>{:MediaId=>media_id})
82
+ end
83
+
84
+ def voice media_id
85
+ update(:MsgType=>"voice", :Voice=>{:MediaId=>media_id})
86
+ end
87
+
88
+ def video media_id, opts={}
89
+ video_fields = camelize_hash_keys({media_id: media_id}.merge(opts.slice(:title, :description)))
90
+ update(:MsgType=>"video", :Video=>video_fields)
91
+ end
92
+
93
+ def music thumb_media_id, music_url, opts={}
94
+ music_fields = camelize_hash_keys(opts.slice(:title, :description, :HQ_music_url).merge(music_url: music_url, thumb_media_id: thumb_media_id))
95
+ update(:MsgType=>"music", :Music=>music_fields)
96
+ end
97
+
98
+ def news collection, &block
99
+ if block_given?
100
+ article = ArticleBuilder.new
101
+ collection.each{|item| yield(article, item)}
102
+ items = article.items
103
+ else
104
+ items = collection.collect do |item|
105
+ camelize_hash_keys(item.symbolize_keys.slice(:title, :description, :pic_url, :url))
106
+ end
107
+ end
108
+
109
+ update(:MsgType=>"news", :ArticleCount=> items.count,
110
+ :Articles=> items.collect{|item| camelize_hash_keys(item)})
111
+ end
112
+
113
+ def to_xml
114
+ message_hash.to_xml(root: "xml", children: "item", skip_instruct: true, skip_types: true)
115
+ end
116
+
117
+ def to_json
118
+ json_hash = deep_recursive(message_hash) do |key, value|
119
+ key = key.to_s
120
+ [(JSON_KEY_MAP[key] || key.downcase), value]
121
+ end
122
+
123
+ json_hash.slice!("touser", "msgtype", "content", "image", "voice", "video", "music", "news", "articles").to_hash
124
+ case json_hash["msgtype"]
125
+ when "text"
126
+ json_hash["text"] = {"content" => json_hash.delete("content")}
127
+ when "news"
128
+ json_hash["news"] = {"articles" => json_hash.delete("articles")}
129
+ end
130
+ JSON.generate(json_hash)
131
+ end
132
+
133
+ def save_to! model_class
134
+ model = model_class.new(underscore_hash_keys(message_hash))
135
+ model.save!
136
+ return self
137
+ end
138
+
139
+ private
140
+ def camelize_hash_keys hash
141
+ deep_recursive(hash){|key, value| [key.to_s.camelize.to_sym, value]}
142
+ end
143
+
144
+ def underscore_hash_keys hash
145
+ deep_recursive(hash){|key, value| [key.to_s.underscore.to_sym, value]}
146
+ end
147
+
148
+ def update fields={}
149
+ message_hash.merge!(fields)
150
+ return self
151
+ end
152
+
153
+ def deep_recursive hash, &block
154
+ hash.inject({}) do |memo, val|
155
+ key,value = *val
156
+ case value.class.name
157
+ when "Hash"
158
+ value = deep_recursive(value, &block)
159
+ when "Array"
160
+ value = value.collect{|item| item.is_a?(Hash) ? deep_recursive(item, &block) : item}
161
+ end
162
+
163
+ key,value = yield(key, value)
164
+ memo.merge!(key => value)
165
+ end
166
+ end
167
+
168
+ end
169
+ end
170
+
@@ -0,0 +1,167 @@
1
+ module WeichatRails
2
+ module Responder
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ self.before_filter :init_wechat_or_token#, only: [:show, :create]
7
+ self.skip_before_filter :verify_authenticity_token
8
+ self.before_filter :verify_signature, only: [:show, :create]
9
+ #delegate :wehcat, to: :class
10
+ end
11
+
12
+ attr_accessor :wechat, :token,:wechat_user
13
+
14
+
15
+ module ClassMethods
16
+
17
+ #定义整体的处理规则,对于不同的message_type,在block块中进行返回数据处理,
18
+ #on :text do |res,content|
19
+ #
20
+ #end
21
+ def on message_type, with: nil, respond: nil, &block
22
+ raise "Unknow message type" unless message_type.in? [:text, :image, :voice, :video, :location, :link, :event, :fallback]
23
+ config=respond.nil? ? {} : {:respond=>respond}
24
+ config.merge!(:proc=>block) if block_given?
25
+
26
+ if (with.present? && !message_type.in?([:text, :event]))
27
+ raise "Only text and event message can take :with parameters"
28
+ else
29
+ config.merge!(:with=>with) if with.present?
30
+ end
31
+
32
+ responders(message_type) << config
33
+ return config
34
+ end
35
+
36
+ def responders type
37
+ @responders ||= Hash.new
38
+ @responders[type] ||= Array.new
39
+ end
40
+
41
+
42
+ #指定的过滤方法,默认不使用指定的匹配方法,如为true,
43
+ def use_matcher mat = false
44
+ @use_matcher = mat
45
+ end
46
+
47
+ def use_matcher?
48
+ @use_matcher
49
+ end
50
+
51
+ #用于处理用户请求,该方法只关注用户信息类型及用户请求的内容或事件信息
52
+ #事件类型:subscribe(订阅)、unsubscribe(取消订阅),SCAN(关注后扫描),LOCATION(上报地理位置),CLICK(自定义菜单事件),VIEW(点击菜单跳转链接时的事件推送)
53
+ def responder_for message, &block
54
+ message_type = message[:MsgType].to_sym
55
+ responders = responders(message_type)
56
+ if use_matcher?
57
+ responder = {:method => :find_matcher}
58
+ case message_type
59
+ when :text
60
+ yield(responder,message[:Content])
61
+ when :event
62
+ yield(responder,message[:Event],message[:EventKey])
63
+ when :image,:voice,:shortvideo
64
+ yield(responder,'MEDIA',picurl: message[:PicUrl],media_id: message[:MediaId])
65
+ else
66
+ yield(responder)
67
+ end
68
+ else
69
+ case message_type
70
+ when :text
71
+ yield(* match_responders(responders, message[:Content]))
72
+ when :event
73
+ yield(* match_responders(responders, message[:Event]))
74
+ else
75
+ yield(responders.first)
76
+ end
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ #该方法用于确定预先用on定义的返回信息(一个数组 [{with:,proc:,respond:},*args])做循环处理
83
+ #responds : 预先定义的某类返回信息,类型为[:text, :image, :voice, :video, :location, :link, :event, :fallback]中之一,为一个hash数组
84
+ #value : 请求内容
85
+ #优先返回具有with值的on规则
86
+ #注:使用on定义的规则只适用于直接在控制器中写死的规则,属于类级别,不适用于后台配置类型,如果responders[msg_type]为空的情况下不产生任何内容
87
+ def match_responders responders, value
88
+ mat = responders.inject({scoped:nil, general:nil}) do |matched, responder|
89
+ condition = responder[:with]
90
+
91
+ if condition.nil?
92
+ matched[:general] ||= [responder, value]
93
+ next matched
94
+ end
95
+
96
+ if condition.is_a? Regexp
97
+ matched[:scoped] ||= [responder] + $~.captures if(value =~ condition)
98
+ else
99
+ matched[:scoped] ||= [responder, value] if(value == condition)
100
+ end
101
+ matched
102
+ end
103
+ return mat[:scoped] || mat[:general]
104
+ end
105
+ end
106
+
107
+
108
+ #用于公众号开发者中心服务器配置中的URL配置,其中xxx为该公众号在开发者数据库中的唯一识别码,由auto_generate_secret_key 来产生
109
+ #如:http://m.pipgame.com/wx/xxxxx
110
+ def show
111
+ render :text => params[:echostr]
112
+ end
113
+
114
+ #创建两个message对像,原请求message及返回message信息,返回的message中传入当前公众号对像,用于回调函数中访问该公众号在数据库中的配置信息
115
+ def create
116
+ req = WeichatRails::Message.from_hash(params[:xml] || post_xml)
117
+ #add whchat_user for multiplay wechat user
118
+ #req.wechat_user(self.wechat_user)
119
+ #如果是多客服消息,直接返回
120
+ response = self.class.responder_for(req) do |responder, *args|
121
+ responder ||= self.class.responders(:fallback).first
122
+
123
+ #next method(responder[:method]).call(*args.unshift(req)) if (responder[:method])
124
+ next if responder.nil?
125
+ next find_matcher(*args.unshift(req)) if (responder[:method])
126
+ next req.reply.text responder[:respond] if (responder[:respond])
127
+ next responder[:proc].call(*args.unshift(req)) if (responder[:proc])
128
+ end
129
+
130
+ if response.respond_to? :to_xml
131
+ render xml: response.to_xml
132
+ else
133
+ render :nothing => true, :status => 200, :content_type => 'text/html'
134
+ end
135
+ end
136
+
137
+ private
138
+ def verify_signature
139
+ array = [self.token, params[:timestamp], params[:nonce]].compact.sort
140
+ render :text => "Forbidden", :status => 403 if params[:signature] != Digest::SHA1.hexdigest(array.join)
141
+ end
142
+
143
+ def post_xml
144
+ data = Hash.from_xml(reqest.raw_post)
145
+ HashWithIndifferentAccess.new_from_hash_copying_default data.fetch('xml', {})
146
+ end
147
+
148
+
149
+ #def wechat_model
150
+ #@wechat_model || WeichatRails.config.public_account_class.constantize
151
+ #end
152
+
153
+ #TODO init wechat , wechat_user,token from database
154
+ def init_wechat_or_token
155
+ raise NotImplementedError, "controller must implement init_wechat_or_token method!if you just need reply,you can init the token,otherwise you need to init whchat and token like: wechat = Wechat::Api.new(opts[:appid], opts[:secret], opts[:access_token]) token = opts[:token]
156
+ "
157
+ end
158
+
159
+ #need to inplement
160
+ #在这里处理不同值的内容匹配:subscribe(订阅)、unsubscribe(取消订阅),SCAN(关注后扫描),LOCATION(上报地理位置),CLICK(自定义菜单事件),VIEW(点击菜单跳转链接时的事件推送)
161
+ def find_matcher(req,keyword,event_key=nil)
162
+ raise NotImplementedError, "controller must implement find_matcher method,eg: get matcher content from database"
163
+ end
164
+
165
+ end
166
+ end
167
+
@@ -0,0 +1,3 @@
1
+ module WeichatRails
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,47 @@
1
+ require "weichat_rails/api"
2
+ require "weichat_rails/auto_generate_secret_key"
3
+
4
+ module WeichatRails
5
+
6
+ autoload :Message, "weichat_rails/message"
7
+ autoload :Responder, "weichat_rails/responder"
8
+
9
+ class AccessTokenExpiredError < StandardError; end
10
+
11
+ class ResponseError < StandardError
12
+ attr_reader :error_code
13
+ def initialize(errcode, errmsg)
14
+ error_code = errcode
15
+ super "#{errmsg}(#{error_code})"
16
+ end
17
+ end
18
+
19
+
20
+ class << self
21
+ def config
22
+ @config || OpenStruct.new({wechat_secret_string: nil,wechat_token_string: nil})
23
+ end
24
+
25
+ #can configure the wechat_secret_string,wechat_token_string in weichat_rails_config.rb file
26
+ def configure
27
+ yield config if block_given?
28
+ end
29
+
30
+ end
31
+
32
+ #DEFAULT_TOKEN_COLUMN_NAME = "wechat_token".freeze
33
+ #DEFAULT_WECHAT_SECRET_KEY = "wechat_secret_key".freeze
34
+
35
+
36
+ #def self.api
37
+ # # @api ||= WechatRails::Api.new(self.config.appid, self.config.secret, self.config.access_token)
38
+ #end
39
+ end
40
+
41
+ if defined? ActionController::Base
42
+ class ActionController::Base
43
+ def self.wechat_responder opts={}
44
+ self.send(:include, WeichatRails::Responder)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'weichat_rails/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "weichat_rails"
8
+ spec.version = WeichatRails::VERSION
9
+ spec.authors = ["javy_liu"]
10
+ spec.email = ["javy_liu@163.com"]
11
+ spec.summary = %q{weichat interface for rails based on weichat-rails}
12
+ spec.description = %q{weichat use for more than on public weichat account,base on the mysql,~> rails 3.2.16}
13
+ spec.homepage = "https://github.com/javyliu/weichat_rails"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.5"
22
+ spec.add_development_dependency "rake"
23
+
24
+ spec.add_dependency "rails", "~> 3.2.14"
25
+ spec.add_dependency "nokogiri", '>=1.6.0'
26
+ spec.add_dependency 'rest-client'
27
+ spec.add_dependency 'dalli'
28
+
29
+ end
metadata ADDED
@@ -0,0 +1,144 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: weichat_rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - javy_liu
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-07-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
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.2.14
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 3.2.14
55
+ - !ruby/object:Gem::Dependency
56
+ name: nokogiri
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 1.6.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 1.6.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: rest-client
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
+ - !ruby/object:Gem::Dependency
84
+ name: dalli
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: weichat use for more than on public weichat account,base on the mysql,~>
98
+ rails 3.2.16
99
+ email:
100
+ - javy_liu@163.com
101
+ executables: []
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - Gemfile
107
+ - LICENSE.txt
108
+ - README.md
109
+ - Rakefile
110
+ - lib/weichat_rails.rb
111
+ - lib/weichat_rails/access_token.rb
112
+ - lib/weichat_rails/api.rb
113
+ - lib/weichat_rails/auto_generate_secret_key.rb
114
+ - lib/weichat_rails/client.rb
115
+ - lib/weichat_rails/message.rb
116
+ - lib/weichat_rails/responder.rb
117
+ - lib/weichat_rails/version.rb
118
+ - weichat_rails.gemspec
119
+ homepage: https://github.com/javyliu/weichat_rails
120
+ licenses:
121
+ - MIT
122
+ metadata: {}
123
+ post_install_message:
124
+ rdoc_options: []
125
+ require_paths:
126
+ - lib
127
+ required_ruby_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ required_rubygems_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ requirements: []
138
+ rubyforge_project:
139
+ rubygems_version: 2.4.6
140
+ signing_key:
141
+ specification_version: 4
142
+ summary: weichat interface for rails based on weichat-rails
143
+ test_files: []
144
+ has_rdoc: