wechat-rails 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 49c382d50e750b4bb75413972a7f8ddc74a372a1
4
- data.tar.gz: ce7ac829af66603e79aee9ae127eec3e077f8e0f
3
+ metadata.gz: 7bf771322985241eecf82f51c367161c1bbfa08f
4
+ data.tar.gz: 8d43d3189eb4bf5ec5d2eb65b4cbe300b434c923
5
5
  SHA512:
6
- metadata.gz: 4a67f307848a3bb0fa57737fd50c136a987dbc9234f84b8a389163c1662e6fdb49b179b7e2bab4ef57f92e94e068404e7b166cb9a58a433cacc7b36990f4989a
7
- data.tar.gz: b32b63a48c6cd730c0ebc590945dc38ef886b502459eb3aabf723ca844343740de9b8df80e5907d21533964a4db49436cd35b42e7a88bc556839b812c16512ba
6
+ metadata.gz: 11dca38be272009ca91625340230b6336ce1ce1e4c3eeab2fd3396198404ec75fe9a5e3688f4eaab6998260d7ba4554c63249d5e95dae9e8e04ccf4e5efe10ec
7
+ data.tar.gz: 4ab5e207388f1e1253e1b85354d5405a92f5660a29639af3b8f3a78cb28fdf284ddf3307b3a50255fea145d9024d6e7afa5d4a154e5f99f7356207fc99b7cb15
data/README.md CHANGED
@@ -1,5 +1,273 @@
1
1
  Wechat Rails
2
2
  ======================
3
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)
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) [![Gem Version](https://badge.fury.io/rb/wechat-rails.png)](http://badge.fury.io/rb/wechat-rails)
5
+
6
+
7
+ Wechat-rails 可以帮助开发者方便地在Rails环境中集成微信公众平台提供的所有服务,目前微信公众平台提供了以下几种类型的服务。
8
+
9
+ - ##### 微信公众平台基本API, 无需Web环境。
10
+ - ##### 消息处理机制, 需运行在Web环境中。
11
+ - ##### OAuth 2.0认证机制
12
+
13
+ Wechat-rails gem 包含了一个命令行程序可以调用各种无需web环境的API。同时它也提供了Rails Controller的responder DSL, 可以帮助开发者方便地在Rails应用中集成微信的消息处理机制。如果你的App还需要集成微信OAuth2.0, 你可以考虑[omniauth-wechat-oauth2](https://github.com/skinnyworm/omniauth-wechat-oauth2), 这个gem可以方便地和devise集成提供完整的用户认证.
14
+
15
+ 在使用这个Gem前,你需要获得微信API的appid, secret, token。具体情况可以参见http://mp.weixin.qq.com
16
+
17
+ ## 安装
18
+
19
+ Using `gem install` or add to your app's `Gemfile`:
20
+
21
+ ```
22
+ gem install "wechat-rails"
23
+ ```
24
+
25
+ ```
26
+ gem "wechat-rails", git:"https://github.com/skinnyworm/wechat-rails"
27
+ ```
28
+
29
+
30
+ ## 配置
31
+
32
+ #### 命令行程序的配置
33
+
34
+ 要使用命令行程序,你需要在你的home目录中创建一个`~/.wechat.yml`,包含以下内容。其中`access_token`是存放access_token的文件位置。
35
+
36
+ ```
37
+ appid: "my_appid"
38
+ secret: "my_secret"
39
+ access_token: "/var/tmp/wechat_access_token"
40
+ ```
41
+
42
+ #### Rails 全局配置
43
+ Rails环境中, 你可以在config中创建wechat.yml, 为每个rails environment创建不同的配置。
44
+
45
+ ```
46
+ default: &default
47
+ appid: "app_id"
48
+ secret: "app_secret"
49
+ token: "app_token"
50
+ access_token: "/var/tmp/wechat_access_token"
51
+
52
+ production:
53
+ appid: <%= ENV['WECHAT_APPID'] %>
54
+ secret: <%= ENV['WECHAT_APP_SECRET'] %>
55
+ token: <%= ENV['WECHAT_TOKEN'] %>
56
+ access_token: <%= ENV['WECHAT_ACCESS_TOKEN'] %>
57
+
58
+ staging:
59
+ <<: *default
60
+
61
+ development:
62
+ <<: *default
63
+
64
+ test:
65
+ <<: *default
66
+ ```
67
+
68
+ #### Rails 为每个Responder配置不同的appid和secret
69
+ 在个别情况下,你的app可能需要处理来自多个公众账号的消息,这时你可以配置多个responder controller。
70
+
71
+ ```ruby
72
+ class WechatFirstController < ApplicationController
73
+ wechat_responder appid: "app1", secret: "secret1", token: "token1", access_token: Rails.root.join("tmp/access_token1")
74
+
75
+ on :text, with:"help", respond: "help content"
76
+ end
77
+ ```
78
+
79
+ ## 使用命令行
80
+
81
+ ```
82
+ $ wechat
83
+ Wechat commands:
84
+ wechat custom_image [OPENID, IMAGE_PATH] # 发送图片客服消息
85
+ wechat custom_music [OPENID, THUMBNAIL_PATH, MUSIC_URL] # 发送音乐客服消息
86
+ wechat custom_news [OPENID, NEWS_YAML_FILE] # 发送图文客服消息
87
+ wechat custom_text [OPENID, TEXT_MESSAGE] # 发送文字客服消息
88
+ wechat custom_video [OPENID, VIDEO_PATH] # 发送视频客服消息
89
+ wechat custom_voice [OPENID, VOICE_PATH] # 发送语音客服消息
90
+ wechat help [COMMAND] # Describe available commands or one specific command
91
+ wechat media [MEDIA_ID, PATH] # 媒体下载
92
+ wechat media_create [MEDIA_ID, PATH] # 媒体上传
93
+ wechat menu # 当前菜单
94
+ wechat menu_create [MENU_YAML] # 创建菜单
95
+ wechat menu_delete # 删除菜单
96
+ wechat user [OPEN_ID] # 查找关注者
97
+ wechat users # 关注者列表
98
+
99
+ ```
100
+
101
+ ### 使用场景
102
+ 以下是几种典型场景的使用方法
103
+
104
+ #####获取所有用户的OPENID
105
+
106
+ ```
107
+ $ wechat users
108
+
109
+ {"total"=>4, "count"=>4, "data"=>{"openid"=>["oCfEht9***********", "oCfEhtwqa***********", "oCfEht9oMCqGo***********", "oCfEht_81H5o2***********"]}, "next_openid"=>"oCfEht_81H5o2***********"}
110
+
111
+ ```
112
+
113
+ #####获取用户的信息
114
+
115
+ ```
116
+ $ wechat user "oCfEht9***********"
117
+
118
+ {"subscribe"=>1, "openid"=>"oCfEht9***********", "nickname"=>"Nickname", "sex"=>1, "language"=>"zh_CN", "city"=>"徐汇", "province"=>"上海", "country"=>"中国", "headimgurl"=>"http://wx.qlogo.cn/mmopen/ajNVdqHZLLBd0SG8NjV3UpXZuiaGGPDcaKHebTKiaTyof*********/0", "subscribe_time"=>1395715239}
119
+
120
+ ```
121
+
122
+ #####获取用户的信息
123
+
124
+ ```
125
+ $ wechat user "oCfEht9***********"
126
+
127
+ {"subscribe"=>1, "openid"=>"oCfEht9***********", "nickname"=>"Nickname", "sex"=>1, "language"=>"zh_CN", "city"=>"徐汇", "province"=>"上海", "country"=>"中国", "headimgurl"=>"http://wx.qlogo.cn/mmopen/ajNVdqHZLLBd0SG8NjV3UpXZuiaGGPDcaKHebTKiaTyof*********/0", "subscribe_time"=>1395715239}
128
+ ```
129
+
130
+ ##### 获取当前菜单
131
+ ```
132
+ $ wechat menu
133
+
134
+ {"menu"=>{"button"=>[{"type"=>"view", "name"=>"保护的", "url"=>"http://***/protected", "sub_button"=>[]}, {"type"=>"view", "name"=>"公开的", "url"=>"http://***", "sub_button"=>[]}]}}
135
+
136
+ ```
137
+
138
+ ##### 创建菜单
139
+ 创建菜单需要一个定义菜单内容的yaml文件,比如
140
+ menu.yaml
141
+
142
+ ```
143
+ button:
144
+ -
145
+ type: "view"
146
+ name: "保护的"
147
+ url: "http://***/protected"
148
+ -
149
+ type: "view"
150
+ name: "公开的"
151
+ url: "http://***"
152
+
153
+ ```
154
+
155
+ 然后执行命令行
156
+
157
+ ```
158
+ $ wechat menu_create menu.yaml
159
+
160
+ ```
161
+
162
+ ##### 发送客服图文消息
163
+ 需定义一个图文消息内容的yaml文件,比如
164
+ articles.yaml
165
+
166
+ ```
167
+ articles:
168
+ -
169
+ title: "习近平在布鲁日欧洲学院演讲"
170
+ description: "新华网比利时布鲁日4月1日电 国家主席习近平1日在比利时布鲁日欧洲学院发表重要演讲"
171
+ url: "http://news.sina.com.cn/c/2014-04-01/232629843387.shtml"
172
+ pic_url: "http://i3.sinaimg.cn/dy/c/2014-04-01/1396366518_bYays1.jpg"
173
+ ```
174
+
175
+ 然后执行命令行
176
+
177
+ ```
178
+ $ wechat custom_news oCfEht9oM*********** articles.yml
179
+
180
+ ```
181
+
182
+
183
+
184
+ ## Rails Responder Controller DSL
185
+
186
+ 为了在Rails app中响应用户的消息,开发者需要创建一个wechat responder controller. 首先在router中定义
187
+
188
+ ```ruby
189
+ resource :wechat, only:[:show, :create]
190
+
191
+ ```
192
+
193
+ 然后创建Controller class, 例如
194
+
195
+ ```ruby
196
+
197
+ class WechatsController < ApplicationController
198
+ wechat_responder
199
+
200
+ # 默认的文字信息responder
201
+ on :text do |request, content|
202
+ request.reply.text "echo: #{content}" #Just echo
203
+ end
204
+
205
+ # 当请求的文字信息内容为'help'时, 使用这个responder处理
206
+ on :text, with:"help" do |request, help|
207
+ request.reply.text "help content" #回复帮助信息
208
+ end
209
+
210
+ # 当请求的文字信息内容为'<n>条新闻'时, 使用这个responder处理, 并将n作为第二个参数
211
+ on :text, with: /^(\d+)条新闻$/ do |request, count|
212
+ articles_range = (0... [count.to_i, 10].min)
213
+ request.reply.news(articles_range) do |article, i| #回复"articles"
214
+ article.item title: "标题#{i}", description:"内容描述#{i}", pic_url: "http://www.baidu.com/img/bdlogo.gif", url:"http://www.baidu.com/"
215
+ end
216
+ end
217
+
218
+ # 处理图片信息
219
+ on :image do |request|
220
+ request.reply.image(request[:MediaId]) #直接将图片返回给用户
221
+ end
222
+
223
+ # 处理语音信息
224
+ on :voice do |request|
225
+ request.reply.voice(request[:MediaId]) #直接语音音返回给用户
226
+ end
227
+
228
+ # 处理视频信息
229
+ on :video do |request|
230
+ nickname = wechat.user(request[:FromUserName])["nickname"] #调用 api 获得发送者的nickname
231
+ request.reply.video(request[:MediaId], title: "回声", description: "#{nickname}发来的视频请求") #直接视频返回给用户
232
+ end
233
+
234
+ # 处理地理位置信息
235
+ on :location do |request|
236
+ request.reply.text("#{request[:Location_X]}, #{request[:Location_Y]}") #回复地理位置
237
+ end
238
+
239
+ # 当无任何responder处理用户信息时,使用这个responder处理
240
+ on :fallback, respond: "fallback message"
241
+ end
242
+
243
+ ```
244
+
245
+ 在controller中使用`wechat_responder`引入Responder DSL, 之后可以用
246
+
247
+ ```
248
+ on <message_type> do |message|
249
+ message.reply.text "some text"
250
+ end
251
+
252
+ ```
253
+ 来响应用户信息。
254
+
255
+ 目前支持的message_type有如下几种
256
+
257
+ - :text 响应文字消息,可以用`:with`参数来匹配文本内容 `on(:text, with:'help'){|message, content| ...}`
258
+ - :image 响应图片消息
259
+ - :voice 响应语音消息
260
+ - :video 响应视频消息
261
+ - :location 响应地理位置消息
262
+ - :link 响应链接消息
263
+ - :event 响应事件消息, 可以用`:with`参数来匹配事件类型
264
+ - :fallback 默认响应,当收到的消息无法被其他responder响应时,会使用这个responder.
265
+
266
+ ## Message DSL
267
+
268
+ Wechat-rails 的核心是一个Message DSL,帮助开发者构建各种类型的消息,包括主动推送的和被动响应的。
269
+ ....
270
+
271
+
272
+
5
273
 
data/bin/wechat CHANGED
@@ -14,17 +14,21 @@ require 'fileutils'
14
14
  class App < Thor
15
15
  class Helper
16
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"]
17
+ config_file = File.join(Dir.home, ".wechat.yml")
18
+ config = YAML.load(File.new(config_file).read) if File.exist?(config_file)
19
+
20
+ config ||= {}
21
+ appid = config["appid"]
22
+ secret = config["secret"]
23
+ token_file = options[:toke_file] || config["access_token"] || "/var/tmp/wechat_access_token"
20
24
 
21
25
  if (appid.nil? || secret.nil? || token_file.nil?)
22
26
  puts <<-HELP
23
- You need set wechat appid and secret in environment variables.
27
+ You need create ~/.wechat.yml with wechat appid and secret. For example:
24
28
 
25
- export WECHAT_APPID=<appid>
26
- export WECHAT_SECRET=<secret>
27
- export WECHAT_ACCESS_TOKEN=<file location for storing access token>
29
+ appid: <wechat appid>
30
+ secret: <wechat secret>
31
+ access_toke: "/var/tmp/wechat_access_token"
28
32
 
29
33
  HELP
30
34
  exit 1
@@ -56,7 +60,7 @@ HELP
56
60
  puts "Menu deleted" if Helper.with(options).menu_delete
57
61
  end
58
62
 
59
- desc "menu_create [MENU_YAML]", "删除菜单"
63
+ desc "menu_create [MENU_YAML]", "创建菜单"
60
64
  def menu_create(menu_yaml)
61
65
  menu = YAML.load(File.new(menu_yaml).read)
62
66
  puts "Menu created" if Helper.with(options).menu_create(menu)
@@ -17,23 +17,35 @@ module Wechat
17
17
  attr_reader :config
18
18
 
19
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
- )
20
+ @config ||= begin
21
+ if defined? Rails
22
+ config_file = Rails.root.join("config/wechat.yml")
23
+ config = YAML.load(ERB.new(File.new(config_file).read).result)[Rails.env] if (File.exist?(config_file))
24
+ end
25
+
26
+ config ||= {appid: ENV["WECHAT_APPID"], secret: ENV["WECHAT_SECRET"], token: ENV["WECHAT_TOKEN"], access_token: ENV["WECHAT_ACCESS_TOKEN"]}
27
+ config.symbolize_keys!
28
+ config[:access_token] ||= Rails.root.join("tmp/access_token").to_s
29
+ OpenStruct.new(config)
30
+ end
26
31
  end
27
32
 
28
33
  def self.api
29
- @api ||= Wechat::Api.new(self.config.app_id, self.config.secret, self.config.access_token)
34
+ @api ||= Wechat::Api.new(self.config.appid, self.config.secret, self.config.access_token)
30
35
  end
31
36
  end
32
37
 
33
38
  if defined? ActionController::Base
34
39
  class ActionController::Base
35
- def self.wechat_rails
40
+ def self.wechat_responder opts={}
36
41
  self.send(:include, Wechat::Responder)
42
+ if (opts.empty?)
43
+ self.wechat = Wechat.api
44
+ self.token = Wechat.config.token
45
+ else
46
+ self.wechat = Wechat::Api.new(opts[:appid], opts[:secret], opts[:access_token])
47
+ self.token = opts[:token]
48
+ end
37
49
  end
38
50
  end
39
51
  end
@@ -2,14 +2,14 @@ require 'wechat/client'
2
2
  require 'wechat/access_token'
3
3
 
4
4
  class Wechat::Api
5
- attr_reader :app_id, :secret, :access_token, :client
5
+ attr_reader :access_token, :client
6
6
 
7
7
  API_BASE = "https://api.weixin.qq.com/cgi-bin/"
8
8
  FILE_BASE = "http://file.api.weixin.qq.com/cgi-bin/"
9
9
 
10
- def initialize app_id, secret, token_file
10
+ def initialize appid, secret, token_file
11
11
  @client = Wechat::Client.new(API_BASE)
12
- @access_token = Wechat::AccessToken.new(@client, app_id, secret, token_file)
12
+ @access_token = Wechat::AccessToken.new(@client, appid, secret, token_file)
13
13
  end
14
14
 
15
15
  def users
@@ -80,7 +80,7 @@ module Wechat
80
80
  end
81
81
 
82
82
  def video media_id, opts={}
83
- video_fields = camelize_hash_keys(opts.slice(:title, :description).merge(media_id: media_id))
83
+ video_fields = camelize_hash_keys({media_id: media_id}.merge(opts.slice(:title, :description)))
84
84
  update(:MsgType=>"video", :Video=>video_fields)
85
85
  end
86
86
 
@@ -5,10 +5,13 @@ module Wechat
5
5
  included do
6
6
  self.skip_before_filter :verify_authenticity_token
7
7
  self.before_filter :verify_signature, only: [:show, :create]
8
+ #delegate :wehcat, to: :class
8
9
  end
9
10
 
10
11
  module ClassMethods
11
12
 
13
+ attr_accessor :wechat, :token
14
+
12
15
  def on message_type, with: nil, respond: nil, &block
13
16
  raise "Unknow message type" unless message_type.in? [:text, :image, :voice, :video, :location, :link, :event, :fallback]
14
17
  config=respond.nil? ? {} : {:respond=>respond}
@@ -91,7 +94,7 @@ module Wechat
91
94
 
92
95
  private
93
96
  def verify_signature
94
- array = [Wechat.config.token, params[:timestamp], params[:nonce]].compact.sort
97
+ array = [self.class.token, params[:timestamp], params[:nonce]].compact.sort
95
98
  render :text => "Forbidden", :status => 403 if params[:signature] != Digest::SHA1.hexdigest(array.join)
96
99
  end
97
100
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wechat-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Skinnyworm
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-01 00:00:00.000000000 Z
11
+ date: 2014-04-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails