看了 Github 上几个 ruby 的微信的 Gems, 感觉大多有以下几个问题
由于找不到好的方案,决定自己动手写一个。我的目的比较明确,这个 Gem 是帮助开发者方便地在已有的 Rails app 中集成微信的消息机制,同时提供命令行程序帮助日常的维护,比如更新菜单等。目前还没有在正式的生产环境中使用过,欢迎大家吐槽,参与,让这个项目可以成为每个希望集成微信公众平台的开发者的好工具。
Wechat-rails 可以帮助开发者方便地在 Rails 环境中集成微信公众平台提供的所有服务,目前微信公众平台提供了以下几种类型的服务。
Wechat-rails gem 包含了一个命令行程序可以调用各种无需 web 环境的 API。同时它也提供了 Rails Controller 的 responder DSL, 可以帮助开发者方便地在 Rails 应用中集成微信的消息处理机制。如果你的 App 还需要集成微信 OAuth2.0, 你可以考虑omniauth-wechat-oauth2, 这个 gem 可以方便地和 devise 集成提供完整的用户认证。
在使用这个 Gem 前,你需要获得微信 API 的 appid, secret, token。具体情况可以参见http://mp.weixin.qq.com
Using gem install
or add to your app's Gemfile
:
gem install "wechat-rails"
gem "wechat-rails", git:"https://github.com/skinnyworm/wechat-rails"
要使用命令行程序,你需要在你的 home 目录中创建一个~/.wechat.yml
,包含以下内容。其中access_token
是存放 access_token 的文件位置。
appid: "my_appid"
secret: "my_secret"
access_token: "/var/tmp/wechat_access_token"
Rails 环境中,你可以在 config 中创建 wechat.yml, 为每个 rails environment 创建不同的配置。
default: &default
appid: "app_id"
secret: "app_secret"
token: "app_token"
access_token: "/var/tmp/wechat_access_token"
production:
appid: <%= ENV['WECHAT_APPID'] %>
secret: <%= ENV['WECHAT_APP_SECRET'] %>
token: <%= ENV['WECHAT_TOKEN'] %>
access_token: <%= ENV['WECHAT_ACCESS_TOKEN'] %>
staging:
<<: *default
development:
<<: *default
test:
<<: *default
在个别情况下,你的 app 可能需要处理来自多个公众账号的消息,这时你可以配置多个 responder controller。
class WechatFirstController < ApplicationController
wechat_responder appid: "app1", secret: "secret1", token: "token1", access_token: Rails.root.join("tmp/access_token1")
on :text, with:"help", respond: "help content"
end
$ wechat
Wechat commands:
wechat custom_image [OPENID, IMAGE_PATH] # 发送图片客服消息
wechat custom_music [OPENID, THUMBNAIL_PATH, MUSIC_URL] # 发送音乐客服消息
wechat custom_news [OPENID, NEWS_YAML_FILE] # 发送图文客服消息
wechat custom_text [OPENID, TEXT_MESSAGE] # 发送文字客服消息
wechat custom_video [OPENID, VIDEO_PATH] # 发送视频客服消息
wechat custom_voice [OPENID, VOICE_PATH] # 发送语音客服消息
wechat help [COMMAND] # Describe available commands or one specific command
wechat media [MEDIA_ID, PATH] # 媒体下载
wechat media_create [MEDIA_TYPE, PATH] # 媒体上传
wechat menu # 当前菜单
wechat menu_create [MENU_YAML] # 创建菜单
wechat menu_delete # 删除菜单
wechat user [OPEN_ID] # 查找关注者
wechat users # 关注者列表
以下是几种典型场景的使用方法
#####获取所有用户的 OPENID
$ wechat users
{"total"=>4, "count"=>4, "data"=>{"openid"=>["oCfEht9***********", "oCfEhtwqa***********", "oCfEht9oMCqGo***********", "oCfEht_81H5o2***********"]}, "next_openid"=>"oCfEht_81H5o2***********"}
#####获取用户的信息
$ wechat user "oCfEht9***********"
{"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}
$ wechat menu
{"menu"=>{"button"=>[{"type"=>"view", "name"=>"保护的", "url"=>"http://***/protected", "sub_button"=>[]}, {"type"=>"view", "name"=>"公开的", "url"=>"http://***", "sub_button"=>[]}]}}
创建菜单需要一个定义菜单内容的 yaml 文件,比如 menu.yaml
button:
-
type: "view"
name: "保护的"
url: "http://***/protected"
-
type: "view"
name: "公开的"
url: "http://***"
然后执行命令行
$ wechat menu_create menu.yaml
需定义一个图文消息内容的 yaml 文件,比如 articles.yaml
articles:
-
title: "习近平在布鲁日欧洲学院演讲"
description: "新华网比利时布鲁日4月1日电 国家主席习近平1日在比利时布鲁日欧洲学院发表重要演讲"
url: "http://news.sina.com.cn/c/2014-04-01/232629843387.shtml"
pic_url: "http://i3.sinaimg.cn/dy/c/2014-04-01/1396366518_bYays1.jpg"
然后执行命令行
$ wechat custom_news oCfEht9oM*********** articles.yml
为了在 Rails app 中响应用户的消息,开发者需要创建一个 wechat responder controller. 首先在 router 中定义
resource :wechat, only:[:show, :create]
然后创建 Controller class, 例如
class WechatsController < ApplicationController
wechat_responder
# 默认的文字信息responder
on :text do |request, content|
request.reply.text "echo: #{content}" #Just echo
end
# 当请求的文字信息内容为'help'时, 使用这个responder处理
on :text, with:"help" do |request, help|
request.reply.text "help content" #回复帮助信息
end
# 当请求的文字信息内容为'<n>条新闻'时, 使用这个responder处理, 并将n作为第二个参数
on :text, with: /^(\d+)条新闻$/ do |request, count|
articles_range = (0... [count.to_i, 10].min)
request.reply.news(articles_range) do |article, i| #回复"articles"
article.item title: "标题#{i}", description:"内容描述#{i}", pic_url: "http://www.baidu.com/img/bdlogo.gif", url:"http://www.baidu.com/"
end
end
# 处理图片信息
on :image do |request|
request.reply.image(request[:MediaId]) #直接将图片返回给用户
end
# 处理语音信息
on :voice do |request|
request.reply.voice(request[:MediaId]) #直接语音音返回给用户
end
# 处理视频信息
on :video do |request|
request.reply.video(request[:MediaId], title: "回声", description: "发来的视频请求") #直接视频返回给用户
end
# 处理地理位置信息
on :location do |request|
nickname = wechat.user(request[:FromUserName])["nickname"] #呼叫 api 获得发送者的nickname
request.reply.text("#{nickname}在 #{request[:Location_X]}, #{request[:Location_Y]}") #回复地理位置
end
# 当无任何responder处理用户信息时,使用这个responder处理
on :fallback, respond: "fallback message"
end
在 controller 中使用wechat_responder
引入 Responder DSL, 之后可以用
on <message_type> do |message|
message.reply.text "some text"
end
来响应用户信息。
目前支持的 message_type 有如下几种
:with
参数来匹配文本内容 on(:text, with:'help'){|message, content| ...}
:with
参数来匹配事件类型Wechat-rails 的核心是一个 Message DSL,帮助开发者构建各种类型的消息,包括主动推送的和被动响应的。 ....