Skip to content

Commit 6e81c90

Browse files
committed
Added config support for per responder controller
1 parent fad21d1 commit 6e81c90

File tree

7 files changed

+263
-49
lines changed

7 files changed

+263
-49
lines changed

README.md

+202-27
Original file line numberDiff line numberDiff line change
@@ -4,47 +4,198 @@ Wechat Rails
44
[![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)
55

66

7-
Wechat MP platform provides following services for developers to use
7+
Wechat-rails 可以帮助开发者方便地在Rails环境中集成微信公众平台提供的所有服务,目前微信公众平台提供了以下几种类型的服务。
88

9-
- API service (user query, media retrieval and sending custom messages)
10-
- Messaging service (repspond to messages sent by user)
11-
- OAuth 2.0
9+
- ##### 微信公众平台基本API, 无需Web环境。
10+
- ##### 消息处理机制, 需运行在Web环境中。
11+
- ##### OAuth 2.0认证机制
1212

13-
Wechat-rails gem helps developer to easily use the API service and Messaging service in rails app. For oauth2 integeration, you can consider [omniauth-wechat-oauth2](https://github.com/skinnyworm/omniauth-wechat-oauth2).
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集成提供完整的用户认证.
1414

15-
You need to get a wechat API key at: http://mp.weixin.qq.com
15+
在使用这个Gem前,你需要获得微信API的appid, secret, token。具体情况可以参见http://mp.weixin.qq.com
1616

17-
## Installation
17+
## 安装
1818

19-
Add to your `Gemfile`:
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。
2070

2171
```ruby
22-
gem "wechat-rails"
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+
2399
```
24100

25-
Then `bundle install`.
101+
### 使用场景
102+
以下是几种典型场景的使用方法
26103

27-
### Setup Environment
28-
For development environment, you need to export some environment variables before you can use the commandline utilities.
104+
#####获取所有用户的OPENID
29105

30106
```
31-
export WECHAT_APPID=<app id>
32-
export WECHAT_SECRET=<app secret>
33-
export WECHAT_ACCESS_TOKEN=<储存access_token文件的位置, eg. /var/tmp/access_token>
34-
export TOKEN=<Token可由开发者任意填写,用作生成签名,在配置接口时使用>
107+
$ wechat users
108+
109+
{"total"=>4, "count"=>4, "data"=>{"openid"=>["oCfEht9***********", "oCfEhtwqa***********", "oCfEht9oMCqGo***********", "oCfEht_81H5o2***********"]}, "next_openid"=>"oCfEht_81H5o2***********"}
110+
35111
```
36-
For production environment, consult your service configuration for how to expose your environment variables.
37112

38-
## Command line utilities
39-
Once the gem was installed, you should have a command line utility for invoking all the api methods.
113+
#####获取用户的信息
40114

41-
## Controller DSL
42-
To set up a rails controlelr for reponding user message is easy. Following is an example
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, 例如
43194

44195
```ruby
45196

46197
class WechatsController < ApplicationController
47-
wechat_rails
198+
wechat_responder
48199

49200
# 默认的文字信息responder
50201
on :text do |request, content|
@@ -76,7 +227,7 @@ class WechatsController < ApplicationController
76227

77228
# 处理视频信息
78229
on :video do |request|
79-
nickname = Wechat.api.user(request[:FromUserName])["nickname"] #呼叫 api 获得发送者的nickname
230+
nickname = wechat.user(request[:FromUserName])["nickname"] #调用 api 获得发送者的nickname
80231
request.reply.video(request[:MediaId], title: "回声", description: "#{nickname}发来的视频请求") #直接视频返回给用户
81232
end
82233

@@ -86,13 +237,37 @@ class WechatsController < ApplicationController
86237
end
87238

88239
# 当无任何responder处理用户信息时,使用这个responder处理
89-
on :fallback do |request|
90-
request.reply.text "fallback"
91-
end
92-
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"
93250
end
94251
95252
```
253+
来响应用户信息。
96254

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+
97272

98273

VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.1.0
1+
0.1.1

bin/wechat

+12-8
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,21 @@ require 'fileutils'
1414
class App < Thor
1515
class Helper
1616
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"
2024

2125
if (appid.nil? || secret.nil? || token_file.nil?)
2226
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:
2428
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"
2832
2933
HELP
3034
exit 1
@@ -56,7 +60,7 @@ HELP
5660
puts "Menu deleted" if Helper.with(options).menu_delete
5761
end
5862

59-
desc "menu_create [MENU_YAML]", "删除菜单"
63+
desc "menu_create [MENU_YAML]", "创建菜单"
6064
def menu_create(menu_yaml)
6165
menu = YAML.load(File.new(menu_yaml).read)
6266
puts "Menu created" if Helper.with(options).menu_create(menu)

lib/wechat-rails.rb

+19-8
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,34 @@ def initialize(errcode, errmsg)
1717
attr_reader :config
1818

1919
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[:access_token] ||= Rails.root.join("tmp/access_token").to_s
28+
OpenStruct.new(config)
29+
end
2630
end
2731

2832
def self.api
29-
@api ||= Wechat::Api.new(self.config.app_id, self.config.secret, self.config.access_token)
33+
@api ||= Wechat::Api.new(self.config.appid, self.config.secret, self.config.access_token)
3034
end
3135
end
3236

3337
if defined? ActionController::Base
3438
class ActionController::Base
35-
def self.wechat_rails
39+
def self.wechat_responder opts={}
3640
self.send(:include, Wechat::Responder)
41+
if (opts.empty?)
42+
self.wechat = Wechat.api
43+
self.token = Wechat.config.token
44+
else
45+
self.wechat = Wechat::Api.new(opts[:appid], opts[:secret], opts[:access_token])
46+
self.token = opts[:token]
47+
end
3748
end
3849
end
3950
end

lib/wechat/api.rb

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
require 'wechat/access_token'
33

44
class Wechat::Api
5-
attr_reader :app_id, :secret, :access_token, :client
5+
attr_reader :access_token, :client
66

77
API_BASE = "https://api.weixin.qq.com/cgi-bin/"
88
FILE_BASE = "http://file.api.weixin.qq.com/cgi-bin/"
99

10-
def initialize app_id, secret, token_file
10+
def initialize appid, secret, token_file
1111
@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)
1313
end
1414

1515
def users

lib/wechat/responder.rb

+4-1
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@ module Responder
55
included do
66
self.skip_before_filter :verify_authenticity_token
77
self.before_filter :verify_signature, only: [:show, :create]
8+
#delegate :wehcat, to: :class
89
end
910

1011
module ClassMethods
1112

13+
attr_accessor :wechat, :token
14+
1215
def on message_type, with: nil, respond: nil, &block
1316
raise "Unknow message type" unless message_type.in? [:text, :image, :voice, :video, :location, :link, :event, :fallback]
1417
config=respond.nil? ? {} : {:respond=>respond}
@@ -91,7 +94,7 @@ def create
9194

9295
private
9396
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
9598
render :text => "Forbidden", :status => 403 if params[:signature] != Digest::SHA1.hexdigest(array.join)
9699
end
97100
end

0 commit comments

Comments
 (0)