wechat-rails 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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