Git Product home page Git Product logo

wechat's Introduction

WeChat Gem Version Build Status Maintainability Test Coverage

Join the chat

中文文档 Chinese document

Wechat is a Chinese multi-purpose messaging, social media and mobile payment app developed by Tencent. It was first released in 2011, and by 2018 it was one of the world's largest standalone mobile apps by monthly active users, with over 1 billion monthly active users (902 million daily active users). (According to wiki)

WeChat gem helps Rails developers integrate WeChat Official Accounts Platform or Wechat mini program easily, including features:

  • Sending message API(can be both accessed via console or rails server)
  • Receiving message(rails server is required to be running)
  • Wechat JS-SDK config signature
  • OAuth 2.0 authentication
  • Record session when receiving message from user (Optional)

wechat command shares the same API in console, so you can interact with wechat server quickly without starting up web environment/code.

A responder DSL can be used in Rails controller, which gives an event based interface to handle messages sent by end users.

If Wechat OAuth 2.0 is required by your app, omniauth-wechat-oauth2 is recommended in order to apply devise authentication.

If tencent's weui UI style is adoped in your project, gem weui-rails is available for you.

For web page only wechat application, please use wechat_api, which only contains web features, compared with traditional message type wechat_responder.

There is a more complete wechat-starter demo available, which futher includes the payment SDK feature.

Installation

Use gem install

gem install "wechat"
# If your ruby version < 2.6
# gem install wechat -v 0.12.4

Or add it to your app's Gemfile:

gem 'wechat'
# If your rails version < 6.0
# gem 'wechat', '~> 0.12.4'

Run the following command to install it:

bundle install

Run the generator:

rails generate wechat:install

rails g wechat:install will generate the initial wechat.yml configuration file, including an sample wechat controller and corresponding routes.

Enable session record:

rails g wechat:session
rake db:migrate

Enabling session will generate two files in Rails folder, you can add more columns to wechat_session table and add declaration to link to users table, it's also possible to store data directly in hash_store. If you are using PostgreSQL, using hstore/json maybe better, but the best way is to add a dedicated column to record the data (the Rails way).

Using Redis to store wechat token and ticket:

rails g wechat:redis_store

Redis storage supports Rails application running in multiple servers. It is recommended to use default file storage if there is only one single server. Besides that, wechat command won't read token/ticket stored in Redis.

Enable database wechat configurations:

rails g wechat:config
rake db:migrate

After running the migration, a wechat_configs table will be created that allows storage of multiple wechat accounts.

Configuration

Configure wechat for the first time

Make sure to finish all the setup on rails side first, then submit those setting to Tencent wechat management website. Otherwise, wechat will raise error.

URL address for wechat created by running rails g wechat:install is http://your-server.com/wechat

How to setup appid/corpid and secret see below section.

Configure wechat by record-based mode

Make sure the record attributes include access_token, token_expires_in, got_token_at.

def client
  @client ||= Wechat::Api.new(app_id, app_secret, token_file, network_setting, jsapi_ticket_file, record)
end

Configure for command line

To use standalone wechat command, you need to create configuration file ~/.wechat.yml and include content below for public account. The access_token will be written to file /var/tmp/wechat_access_token.

appid: "my_appid"
secret: "my_secret"
access_token: "/var/tmp/wechat_access_token"

For enterprise account, you need to use corpid instead of appid as enterprise account supports multiply application (Tencent calls them agents) in one enterprise account. Obtaining the corpsecret is a little bit tricky, must be created at management mode->privilege setting and create any of management group to obtain. Due to Tencent currently only providing Chinese interface for their management console, it's highly recommended you find a colleague knowing Mandarin to help you to obtain the corpsecret.

Windows users need to store .wechat.yml at C:/Users/[user_name]/ (replace with your user name), also pay attention to the direction of folder separator.

corpid: "my_appid"
corpsecret: "my_secret"
agentid: 1 # Integer, which can be obtained from application settings
access_token: "C:/Users/[user_name]/wechat_access_token"

Configure for Rails

Rails configuration file supports different environment similar to database.yml, after running rails generate wechat:install you can find configuration file at config/wechat.yml

Public account configuration example:

default: &default
  appid: "app_id"
  secret: "app_secret"
  token:  "app_token"
  access_token: "/var/tmp/wechat_access_token"
  jsapi_ticket: "/var/tmp/wechat_jsapi_ticket"

production:
  appid: <%= ENV['WECHAT_APPID'] %>
  secret: <%= ENV['WECHAT_APP_SECRET'] %>
  token:   <%= ENV['WECHAT_TOKEN'] %>
  access_token: <%= ENV['WECHAT_ACCESS_TOKEN'] %>
  jsapi_ticket: <%= ENV['WECHAT_JSAPI_TICKET'] %>
  oauth2_cookie_duration: <%= ENV['WECHAT_OAUTH2_COOKIE_DURATION'] %> # seconds

development:
  <<: *default
  trusted_domain_fullname: "http://your_dev.proxy.qqbrowser.cc"

test:
  <<: *default

Although it's optional for public account, but highly recommended to enable encrypt mode by adding these two items to wechat.yml

default: &default
  encrypt_mode: true
  encoding_aes_key:  "my_encoding_aes_key"

Enterprise account must use encrypt mode (encrypt_mode: true is on by default, no need to configure).

The token and encoding_aes_key can be obtained from management console -> one of the agent application -> Mode selection, select callback mode and get/set.

default: &default
  corpid: "corpid"
  corpsecret: "corpsecret"
  agentid:  1
  access_token: "C:/Users/[user_name]/wechat_access_token"
  token:    ""
  encoding_aes_key:  ""
  jsapi_ticket: "C:/Users/[user_name]/wechat_jsapi_ticket"

production:
  corpid:     <%= ENV['WECHAT_CORPID'] %>
  corpsecret: <%= ENV['WECHAT_CORPSECRET'] %>
  agentid:    <%= ENV['WECHAT_AGENTID'] %>
  access_token:  <%= ENV['WECHAT_ACCESS_TOKEN'] %>
  token:      <%= ENV['WECHAT_TOKEN'] %>
  timeout:    30,
  skip_verify_ssl: true # not recommend
  encoding_aes_key:  <%= ENV['WECHAT_ENCODING_AES_KEY'] %>
  jsapi_ticket: <%= ENV['WECHAT_JSAPI_TICKET'] %>
  oauth2_cookie_duration: <%= ENV['WECHAT_OAUTH2_COOKIE_DURATION'] %>

development:
  <<: *default
  trusted_domain_fullname: "http://your_dev.proxy.qqbrowser.cc"

test:
  <<: *default

 # Multiple Accounts
 #
 # wx2_development:
 #  <<: *default
 #  appid: "my_appid"
 #  secret: "my_secret"
 #  access_token: "tmp/wechat_access_token2"
 #  jsapi_ticket: "tmp/wechat_jsapi_ticket2"
 #
 # wx2_test:
 #  <<: *default
 #  appid: "my_appid"
 #  secret: "my_secret"
 #
 # wx2_production:
 #  <<: *default
 #  appid: "my_appid"
 #  secret: "my_secret"

Notes about supporting multiple accounts of WeChat Official Accounts Platform / WeChat Enterprise (for example, adding account wx2):

  • Configuration for multiple accounts is similar to multi-database configuration in config/database.yml, where development, test, production segments are the default configuration, one needs to add wx2_development, wx2_test, wx2_production in order to add additional account named wx2.

  • Declaration of additional wechat_responder:

    wechat_responder account: :wx2
  • Use Wechat.api or Wechat.api(:default) to represent the default wechat api. Use Wechat.api(:wx2) to call for wechat api of account wx2.

  • When using Wechat command line, one can switch to another wechat account by adding optional parameters -a ACCOUNT [--account=ACCOUNT].

For details about supporting multiple accounts, please check PR 150

For wechat mini program, can specified by the item type:

# Mini Program Accounts

  mini_development:
    <<: *default
    appid: "my_appid"
    secret: "my_secret"
    # `mp` is short for **mini program**
    type: 'mp'

Database wechat account configuration

After enabling database account configuration, the following table will be created:

Attribute Type Annotation
environment string Required. Environment of account configuration. Typical values are: production, development and test. For example, a production config will only be available in production. Default to development.
account string Required. Custom wechat account name. Account names must be unique within each environment.
enabled boolean Required. Whether this configuration is activated. Default to true.
appid string Public account id. Either this attribute or corpid must be specified.
secret string Public account configuration. Required when appid exists.
corpid string Corp account id. Either this attribute or appid must be specified.
corpsecret string Corp account configuration. Required when corpid exists.
agentid integer Corp account configuration. Required when corpid exists.
encrypt_mode boolean
encoding_aes_key string Required when encrypt_mode is true.
token string Required.
access_token string Required. Path to access token storage file.
jsapi_ticket string Required. Path to jsapi ticket storage file.
skip_verify_ssl boolean
timeout integer Default to 20.
trusted_domain_fullname string

After updating database account configurations, you need to restart the server, or call Wechat.reload_config! to reload the updates.

Configure priority

Running wechat command in the root folder of Rails application will be using the Rails configuration first (default section), if can not find it, will relay on ~\.wechat.yml, such behavior enables managing more wechat public account and enterprise account without changing your home ~\.wechat.yml file.

When database account configuration is enabled, database configurations will be loaded after yml configuration file or environment parameters. When configurations with the same account name exist in both database and yml file or environment parameter, the one in the database will take precedence.

Wechat server timeout setting

Stability varies for Tencent wechat server, so setting a long timeout may be needed, default is 20 seconds if not set.

Skip the SSL verification

SSL Certification can also be corrupted for some reason in China, it's reported and if it happens to you, you can set skip_verify_ssl: true. (not recommend)

Configure individual responder with different appid

Sometimes, you may want to host more than one enterprise/public wechat account in one Rails application, so you can provide this configuration info when calling wechat_responder or wechat_api

class WechatFirstController < ActionController::Base
   wechat_responder account: :new_account, account_from_request: Proc.new{ |request| request.params[:wechat] }

   on :text, with:"help", respond: "help content"
end

Or you can provide full list of options.

class WechatFirstController < ActionController::Base
   wechat_responder appid: "app1", secret: "secret1", token: "token1", access_token: Rails.root.join("tmp/access_token1"),
                    account_from_request: Proc.new{ |request| request.params[:wechat] }

   on :text, with:"help", respond: "help content"
end

account_from_request is a Proc that takes in request as its parameter, and returns the corresponding wechat account name. In the above examples, controller will choose the account based on the wechat parameter passed in the request. If account_from_request is not specified, or this Proc evaluates to nil, configuration specified by account or the full list of options will be used.

JS-SDK helper

JS-SDK gives you control over Wechat App behavior in html, by injecting a config signature, helper wechat_config_js does that in a simple way:

To make wechat_config_js work, you need to put wechat_api or wechat_responder at controller first.

<body>
<%= wechat_config_js debug: false, api: %w(hideMenuItems closeWindow) -%>
<script type="application/javascript">
  wx.ready(function() {
      wx.hideOptionMenu();
  });
</script>
<a href="javascript:wx.closeWindow();">Close</a>
</body>

Configure the trusted_domain_fullname if you are in development mode and app is running behind a reverse proxy server, otherwise wechat gem won't be able to get the correct url to be signed later.

OAuth2.0 authentication

For public account, code below will get following user's info.

class CartController < ActionController::Base
  wechat_api
  def index
    wechat_oauth2 do |openid|
      @current_user = User.find_by(wechat_openid: openid)
      @articles = @current_user.articles
    end

    # specify account_name to use arbitrary wechat account configuration
    # wechat_oauth2('snsapi_base', nil, account_name) do |openid|
    #  ...
    # end
  end
end

For enterprise account, code below will get enterprise member's userinfo.

class WechatsController < ActionController::Base
  layout 'wechat'
  wechat_responder
  def apply_new
    wechat_oauth2 do |userid|
      @current_user = User.find_by(wechat_userid: userid)
      @apply = Apply.new
      @apply.user_id = @current_user.id
    end
  end
end

wechat_oauth2 already implements the necessary OAuth2.0 and cookie logic. userid defined as the enterprise member UserID. openid defined as the user who following the public account, also notice openid will be different for the same user for different following public accounts.

Notice:

  • If you use wechat_responder in your controller, you cannot use create and show action in your controller, otherwise it will throw errors.
  • If you get redirect_uri parameter error message, make sure you set the correct callback url value in wechat management console with path Development center / Webpage service / Webpage authorization for retrieving user basic information.

The API privilege

wechat gems won't handle any privilege exceptions. (except token timeout, but it's not important to you as it's auto retry/recovery in gems internally), but Tencent will control a lot of privilege based on your public account type and certification, for more info please reference official document.

Command line mode

The available API is different between public account and enterprise account, so wechat gems provide different set of command.

Feel safe if you can not read Chinese in the comments, it's kept there in order to copy & find in the official documentation easier.

Public account command line

$ wechat
Wechat Public Account commands:
  wechat addvoicetorecofortext [VOICE_ID]                       # AI 开放接口 - 提交语音
  wechat callbackip                                             # 获取微信服务器 IP 地址
  wechat clear_quota                                            # 接口调用次数清零
  wechat custom_image [OPENID, IMAGE_PATH]                      # 发送图片客服消息
  wechat custom_music [OPENID, THUMBNAIL_PATH, MUSIC_URL]       # 发送音乐客服消息
  wechat custom_news [OPENID, NEWS_YAML_PATH]                   # 发送图文客服消息
  wechat custom_text [OPENID, TEXT_MESSAGE]                     # 发送文字客服消息
  wechat custom_video [OPENID, VIDEO_PATH]                      # 发送视频客服消息
  wechat custom_voice [OPENID, VOICE_PATH]                      # 发送语音客服消息
  wechat customservice_getonlinekflist                          # 获取在线客服接待信息
  wechat group_create [GROUP_NAME]                              # 创建分组
  wechat group_delete [GROUP_ID]                                # 删除分组
  wechat group_update [GROUP_ID, NEW_GROUP_NAME]                # 修改分组名
  wechat groups                                                 # 查询所有分组
  wechat material_get [MEDIA_ID, PATH]                          # 永久媒体下载
  wechat material_add [MEDIA_TYPE, PATH]                        # 永久媒体上传
  wechat material_add_news [MPNEWS_YAML_PATH]                   # 永久图文素材上传
  wechat material_count                                         # 获取永久素材总数
  wechat material_delete [MEDIA_ID]                             # 删除永久素材
  wechat material_list [TYPE, OFFSET, COUNT]                    # 获取永久素材列表
  wechat media [MEDIA_ID, PATH]                                 # 媒体下载
  wechat media_hq [MEDIA_ID, PATH]                              # 高清音频下载
  wechat media_create [MEDIA_TYPE, PATH]                        # 媒体上传
  wechat media_uploadimg [IMAGE_PATH]                           # 上传图文消息内的图片
  wechat media_uploadnews [MPNEWS_YAML_PATH]                    # 上传图文消息素材
  wechat menu                                                   # 当前菜单
  wechat menu_addconditional [CONDITIONAL_MENU_YAML_PATH]       # 创建个性化菜单
  wechat menu_create [MENU_YAML_PATH]                           # 创建菜单
  wechat menu_delconditional [MENU_ID]                          # 删除个性化菜单
  wechat menu_delete                                            # 删除菜单
  wechat menu_trymatch [USER_ID]                                # 测试个性化菜单匹配结果
  wechat message_mass_delete [MSG_ID]                           # 删除群发消息
  wechat message_mass_get [MSG_ID]                              # 查询群发消息发送状态
  wechat message_mass_preview [WX_NAME, MPNEWS_MEDIA_ID]        # 预览图文消息素材
  wechat qrcode_create_limit_scene [SCENE_ID_OR_STR]            # 请求永久二维码
  wechat qrcode_create_scene [SCENE_ID_OR_STR, EXPIRE_SECONDS]  # 请求临时二维码
  wechat qrcode_download [TICKET, QR_CODE_PIC_PATH]             # 通过 ticket 下载二维码
  wechat queryrecoresultfortext [VOICE_ID]                      # AI 开放接口 - 获取语音识别结果
  wechat shorturl [LONG_URL]                                    # 长链接转短链接
  wechat tag [TAGID]                                            # 获取标签下粉丝列表
  wechat tag_add_user [TAG_ID, OPEN_IDS]                        # 批量为用户打标签
  wechat tag_create [TAGNAME, TAG_ID]                           # 创建标签
  wechat tag_del_user [TAG_ID, OPEN_IDS]                        # 批量为用户取消标签
  wechat tag_delete [TAG_ID]                                    # 删除标签
  wechat tag_update [TAG_ID, TAGNAME]                           # 更新标签名字
  wechat tags                                                   # 获取所有标签
  wechat template_message [OPENID, TEMPLATE_YAML_PATH]          # 模板消息接口
  wechat translatecontent [CONTENT]                             # AI 开放接口 - 微信翻译
  wechat user [OPEN_ID]                                         # 获取用户基本信息
  wechat user_batchget [OPEN_ID_LIST]                           # 批量获取用户基本信息
  wechat user_change_group [OPEN_ID, TO_GROUP_ID]               # 移动用户分组
  wechat user_group [OPEN_ID]                                   # 查询用户所在分组
  wechat user_update_remark [OPEN_ID, REMARK]                   # 设置备注名
  wechat users                                                  # 关注者列表
  wechat wxa_msg_sec_check [CONTENT]                            # 检查一段文本是否含有违法违规内容。
  wechat wxacode_download [WXA_CODE_PIC_PATH, PATH, WIDTH]      # 下载小程序码
  wechat clear_quota                                            # 接口调用次数清零

Enterprise account command line

$ wechat
Wechat Enterprise Account commands:
  wechat agent [AGENT_ID]                                  # 获取企业号应用详情
  wechat agent_list                                        # 获取应用概况列表
  wechat batch_job_result [JOB_ID]                         # 获取异步任务结果
  wechat batch_replaceparty [BATCH_PARTY_CSV_MEDIA_ID]     # 全量覆盖部门
  wechat batch_replaceuser [BATCH_USER_CSV_MEDIA_ID]       # 全量覆盖成员
  wechat batch_syncuser [SYNC_USER_CSV_MEDIA_ID]           # 增量更新成员
  wechat callbackip                                        # 获取微信服务器 IP 地址
  wechat clear_quota                                       # 接口调用次数清零
  wechat convert_to_openid [USER_ID]                       # userid 转换成 openid
  wechat convert_to_userid [OPENID]                        # openid 转换成 userid
  wechat custom_image [OPENID, IMAGE_PATH]                 # 发送图片客服消息
  wechat custom_music [OPENID, THUMBNAIL_PATH, MUSIC_URL]  # 发送音乐客服消息
  wechat custom_news [OPENID, NEWS_YAML_PATH]              # 发送图文客服消息
  wechat custom_text [OPENID, TEXT_MESSAGE]                # 发送文字客服消息
  wechat custom_video [OPENID, VIDEO_PATH]                 # 发送视频客服消息
  wechat custom_voice [OPENID, VOICE_PATH]                 # 发送语音客服消息
  wechat department [DEPARTMENT_ID]                        # 获取部门列表
  wechat department_create [NAME, PARENT_ID]               # 创建部门
  wechat department_delete [DEPARTMENT_ID]                 # 删除部门
  wechat department_update [DEPARTMENT_ID, NAME]           # 更新部门
  wechat getusercumulate [BEGIN_DATE, END_DATE]            # 获取累计用户数据
  wechat getusersummary [BEGIN_DATE, END_DATE]             # 获取用户增减数据
  wechat invite_user [USER_ID]                             # 邀请成员关注
  wechat material [MEDIA_ID, PATH]                         # 永久媒体下载
  wechat material_add [MEDIA_TYPE, PATH]                   # 永久媒体上传
  wechat material_count                                    # 获取永久素材总数
  wechat material_delete [MEDIA_ID]                        # 删除永久素材
  wechat material_list [TYPE, OFFSET, COUNT]               # 获取永久素材列表
  wechat media [MEDIA_ID, PATH]                            # 媒体下载
  wechat media_create [MEDIA_TYPE, PATH]                   # 媒体上传
  wechat media_hq [MEDIA_ID, PATH]                         # 高清音频媒体下载
  wechat media_uploadimg [IMAGE_PATH]                      # 上传图文消息内的图片
  wechat menu                                              # 当前菜单
  wechat menu_addconditional [CONDITIONAL_MENU_YAML_PATH]  # 创建个性化菜单
  wechat menu_create [MENU_YAML_PATH]                      # 创建菜单
  wechat menu_delconditional [MENU_ID]                     # 删除个性化菜单
  wechat menu_delete                                       # 删除菜单
  wechat menu_trymatch [USER_ID]                           # 测试个性化菜单匹配结果
  wechat message_send [OPENID, TEXT_MESSAGE]               # 发送文字消息
  wechat qrcode_download [TICKET, QR_CODE_PIC_PATH]        # 通过 ticket 下载二维码
  wechat tag [TAG_ID]                                      # 获取标签成员
  wechat tag_add_department [TAG_ID, PARTY_IDS]            # 增加标签部门
  wechat tag_add_user [TAG_ID, USER_IDS]                   # 增加标签成员
  wechat tag_create [TAGNAME, TAG_ID]                      # 创建标签
  wechat tag_del_department [TAG_ID, PARTY_IDS]            # 删除标签部门
  wechat tag_del_user [TAG_ID, USER_IDS]                   # 删除标签成员
  wechat tag_delete [TAG_ID]                               # 删除标签
  wechat tag_update [TAG_ID, TAGNAME]                      # 更新标签名字
  wechat tags                                              # 获取所有标签
  wechat template_message [OPENID, TEMPLATE_YAML_PATH]     # 模板消息接口
  wechat upload_replaceparty [BATCH_PARTY_CSV_PATH]        # 上传文件方式全量覆盖部门
  wechat upload_replaceuser [BATCH_USER_CSV_PATH]          # 上传文件方式全量覆盖成员
  wechat user [OPEN_ID]                                    # 获取用户基本信息
  wechat user_batchdelete [USER_ID_LIST]                   # 批量删除成员
  wechat user_create [USER_ID, NAME]                       # 创建成员
  wechat user_delete [USER_ID]                             # 删除成员
  wechat user_list [DEPARTMENT_ID]                         # 获取部门成员详情
  wechat user_simplelist [DEPARTMENT_ID]                   # 获取部门成员
  wechat user_update_remark [OPEN_ID, REMARK]              # 设置备注名

Note: replaceparty full departments uploads only supports a single root node as a department and does not support parallel multiple root nodes.

Command line usage demo (partially)

Fetch all users open id
$ wechat users

{"total"=>4, "count"=>4, "data"=>{"openid"=>["oCfEht9***********", "oCfEhtwqa***********", "oCfEht9oMCqGo***********", "oCfEht_81H5o2***********"]}, "next_openid"=>"oCfEht_81H5o2***********"}

Fetch user info
$ 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}

Fetch menu
$ wechat menu

{"menu"=>{"button"=>[{"type"=>"view", "name"=>"保护的", "url"=>"http://***/protected", "sub_button"=>[]}, {"type"=>"view", "name"=>"公开的", "url"=>"http://***", "sub_button"=>[]}]}}

Menu create

Running command rails g wechat:menu to generate a menu definition yaml file:

button:
 -
  name: "Want"
  sub_button:
   -
    type: "scancode_waitmsg"
    name: "绑定用餐二维码"
    key: "BINDING_QR_CODE"
   -
    type: "click"
    name: "预订午餐"
    key:  "BOOK_LUNCH"
   -
    type: "miniprogram"
    name: "小程序示例"
    url:  "http://ericguo.com/"
    appid: "wx1234567890"
    pagepath: "pages/index"
 -
  name: "Query"
  sub_button:
   -
    type: "click"
    name: "进出记录"
    key:  "BADGE_IN_OUT"
   -
    type: "click"
    name: "年假余额"
    key:  "ANNUAL_LEAVE"
 -
  type: "view"
  name: "About"
  url:  "http://blog.cloud-mes.com/"

Running command below to upload the menu:

$ wechat menu_create menu.yaml

Caution: make sure you have management privilege for this application, otherwise you will get 60011 error.

Send custom news

Sending custom_news should also be defined as a yaml file, like articles.yml

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"

After that, you can run this command:

$ wechat custom_news oCfEht9oM*********** articles.yml

Send template message

Sending template message via yaml file is similar, too, define template.yml and content is just the template content.

template:
  template_id: "o64KQ62_xxxxxxxxxxxxxxx-Qz-MlNcRKteq8"
  url: "http://weixin.qq.com/download"
  topcolor: "#FF0000"
  data:
    first:
      value: "Hello, you successfully registered"
      color: "#0A0A0A"
    keynote1:
      value: "5km Health Running"
      color: "#CCCCCC"
    keynote2:
      value: "2014-09-16"
      color: "#CCCCCC"
    keynote3:
      value: "Centry Park, Pudong, Shanghai"
      color: "#CCCCCC"
    remark:
      value: "Welcome back"
      color: "#173177"

After that, you can run this command:

$ wechat template_message oCfEht9oM*********** template.yml

In code:

template = YAML.load(File.read(template_yaml_path))
Wechat.api.template_message_send Wechat::Message.to(openid).template(template["template"])

If using wechat_api or wechat_responder in controller, can also use wechat as shortcut (supports multi account):

template = YAML.load(File.read(template_yaml_path))
wechat.template_message_send Wechat::Message.to(openid).template(template["template"])

wechat_api - Rails Controller Wechat API

Although user can always access all wechat features via Wechat.api, but it's highly recommended to use wechat directly in the controller. It's not only mandatory required if you plan to support multi-account, it also helps to separate the wechat specific logic from the model layer.

class WechatReportsController < ApplicationController
  wechat_api
  layout 'wechat'

  def index
    @lots = Lot.with_preloading.wip_lot
  end
end

Using wechat api at ActiveJob/Rake tasks

Using Wechat.api to access the wechat api function at any place.

Below is an example via rails console to call AI Voice Recognition API:

# Audio file with ID3 version 2.4.0, contains:MPEG ADTS, layer III, v2,  40 kbps, 16 kHz, Monaural
test_voice_file='test_voice.mp3'
Wechat.api.addvoicetorecofortext('test_voice_id', File.open(test_voice_file))
Wechat.api.queryrecoresultfortext 'test_voice_id'

Checking the signature

Using Wechat.decrypt(encrypted_data,session_key, iv) to decode the data. via. Signature Checking

wechat_responder - Rails Responder Controller DSL

In order to respond to the message user sent, Rails developer needs to create a wechat responder controller and define the routing in routes.rb

  resource :wechat, only: [:show, :create]

So the ActionController should be defined like below:

class WechatsController < ActionController::Base
  wechat_responder

  # default text responder when no other match
  on :text do |request, content|
    request.reply.text "echo: #{content}" # Just echo
  end

  # When receive 'help', will trigger this responder
  on :text, with: 'help' do |request|
    request.reply.text 'help content'
  end

  # When receive '<n>news', will match and will get count as <n> as parameter
  on :text, with: /^(\d+) news$/ do |request, count|
    # Wechat article can only contain max 8 items, large than 8 will be dropped.
    news = (1..count.to_i).each_with_object([]) { |n, memo| memo << { title: 'News title', content: "No. #{n} news content" } }
    request.reply.news(news) do |article, n, index| # article is return object
      article.item title: "#{index} #{n[:title]}", description: n[:content], pic_url: 'http://www.baidu.com/img/bdlogo.gif', url: 'http://www.baidu.com/'
    end
  end

  on :event, with: 'subscribe' do |request|
    request.reply.text "#{request[:FromUserName]} subscribe now"
  end

  # When unsubscribe user scan qrcode qrscene_xxxxxx to subscribe in public account
  # notice user will subscribe public account at the same time, so wechat won't trigger subscribe event anymore
  on :scan, with: 'qrscene_xxxxxx' do |request, ticket|
    request.reply.text "Unsubscribe user #{request[:FromUserName]} Ticket #{ticket}"
  end

  # When subscribe user scan scene_id in public account
  on :scan, with: 'scene_id' do |request, ticket|
    request.reply.text "Subscribe user #{request[:FromUserName]} Ticket #{ticket}"
  end

  # When no any on :scan responder can match subscribe user scanned scene_id
  on :event, with: 'scan' do |request|
    if request[:EventKey].present?
      request.reply.text "event scan got EventKey #{request[:EventKey]} Ticket #{request[:Ticket]}"
    end
  end

  # When enterprise user press menu BINDING_QR_CODE and success to scan bar code
  on :scan, with: 'BINDING_QR_CODE' do |request, scan_result, scan_type|
    request.reply.text "User #{request[:FromUserName]} ScanResult #{scan_result} ScanType #{scan_type}"
  end

  # Except QR code, wechat can also scan CODE_39 bar code in enterprise account
  on :scan, with: 'BINDING_BARCODE' do |message, scan_result|
    if scan_result.start_with? 'CODE_39,'
      message.reply.text "User: #{message[:FromUserName]} scan barcode, result is #{scan_result.split(',')[1]}"
    end
  end

  # When user clicks the menu button
  on :click, with: 'BOOK_LUNCH' do |request, key|
    request.reply.text "User: #{request[:FromUserName]} click #{key}"
  end

  # When user views URL in the menu button
  on :view, with: 'http://wechat.somewhere.com/view_url' do |request, view|
    request.reply.text "#{request[:FromUserName]} view #{view}"
  end

  # When user sends an image
  on :image do |request|
    request.reply.image(request[:MediaId]) # Echo the sent image to user
  end

  # When user sends a voice
  on :voice do |request|
    # Echo the sent voice to user
    # request.reply.voice(request[:MediaId])

    voice_id = request[:MediaId]
    # It's only avaiable for Service Account and enable it in dashboard.
    recognition = request[:Recognition]
    request.reply.text "#{voice_id} #{recognition}"
  end

  # When user sends a video
  on :video do |request|
    nickname = wechat.user(request[:FromUserName])['nickname'] # Call wechat api to get sender nickname
    request.reply.video(request[:MediaId], title: 'Echo', description: "Got #{nickname} sent video") # Echo the sent video to user
  end

  # When user sends location message with label
  on :label_location do |request|
    request.reply.text("Label: #{request[:Label]} Location_X: #{request[:Location_X]} Location_Y: #{request[:Location_Y]} Scale: #{request[:Scale]}")
  end

  # When user sends location
  on :location do |request|
    request.reply.text("Latitude: #{request[:Latitude]} Longitude: #{request[:Longitude]} Precision: #{request[:Precision]}")
  end

  on :event, with: 'unsubscribe' do |request|
    request.reply.success # user can not receive this message
  end

  # When user enters the app / agent app
  on :event, with: 'enter_agent' do |request|
    request.reply.text "#{request[:FromUserName]} enter agent app now"
  end

  # When batch job "create/update user (incremental)" is finished.
  on :batch_job, with: 'sync_user' do |request, batch_job|
    request.reply.text "sync_user job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}"
  end

  # When batch job "replace user (full sync)" is finished.
  on :batch_job, with: 'replace_user' do |request, batch_job|
    request.reply.text "replace_user job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}"
  end

  # When batch job "invite user" is finished.
  on :batch_job, with: 'invite_user' do |request, batch_job|
    request.reply.text "invite_user job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}"
  end

  # When batch job "replace department (full sync)" is finished.
  on :batch_job, with: 'replace_party' do |request, batch_job|
    request.reply.text "replace_party job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}"
  end

  # mass sent job finish result notification
  on :event, with: 'masssendjobfinish' do |request|
    # https://mp.weixin.qq.com/wiki?action=doc&id=mp1481187827_i0l21&t=0.03571905015619936#8
    request.reply.success # request is XML result hash.
  end

  # The customer agrees to call back the chat content archive event
  on :change_external_contact do |request|
    # https://open.work.weixin.qq.com/api/doc/90000/90135/92005
    request.reply.success # request is XML result hash.
  end

  # Session event callback
  on :msgaudit_notify do |request|
    # https://open.work.weixin.qq.com/api/doc/90000/90135/95039
    request.reply.success # request is XML result hash.
  end

  # If no match above will fallback to below
  on :fallback, respond: 'fallback message'
end

So the important statement is only wechat_responder, all other is just a DSL:

on <message_type> do |message|
 message.reply.text "some text"
end

The block code will be running to respond to user's message.

Below are currently supported message_types:

  • :text text message, using :with to match text content like on(:text, with:'help'){|message, content| ...}
  • :image image message
  • :voice voice message
  • :shortvideo shortvideo message
  • :video video message
  • :label_location location message with label
  • :link link message
  • :event event message, using :with to match particular event, supports regular expression match similar to text message.
  • :click virtual event message, wechat still sends event message,but gems will map to menu click event.
  • :view virtual view message, wechat still sends event message,but gems will map to menu view page event.
  • :scan virtual scan message, wechat still sends event message, but gems will map to scan event.
  • :batch_job virtual batch job message
  • :location virtual location message
  • :fallback default message, when no other responder can handle incoming message, will be used as a fallback handler

Transfer to customer service

class WechatsController < ActionController::Base
  # When no other responder can handle incoming message, will transfer to human customer service.
  on :fallback do |message|
    message.reply.transfer_customer_service
  end
end

Caution: do not set default text responder if you want to use multiply human customer service, other will lead text message can not transfer.

Notifications

  • wechat.responder.after_create data includes request Wechat::Message and response Wechat::Message.

Example:

ActiveSupport::Notifications.subscribe('wechat.responder.after_create') do |name, started, finished, unique_id, data|
  WechatLog.create request: data[:request], response: data[:response]
end

Known Issues

  • Sometimes, enterprise account can not receive the menu message due to Tencent server unable to resolve DNS, so using IP as a callback URL is more stable, but it never happens for user sent text messages.
  • Enterprise batch "replace users" uses a CSV format file, but if you are using the downloaded template directly, it's not working, must open the CSV file in Excel first, then save as CSV format again, seems Tencent only supports Excel "Save as CSV" file format.
  • If you using unicorn behind nginx and https, you need to set trusted_domain_fullname and point it to https, otherwise it will be http and will lead to invalid signature in the JS-SDK.

wechat's People

Contributors

3014zhangshuo avatar acenqiu avatar bastengao avatar eric-guo avatar fahchen avatar feitian124 avatar fogisland avatar goofansu avatar guange2015 avatar hlltc avatar hophacker avatar huangxiangdan avatar huhao98 avatar jimmy0017 avatar kikyous avatar leepood avatar mechiland avatar oiahoon avatar paicha avatar qinmingyuan avatar skinnyworm avatar snow avatar tuliren avatar xiewenwei avatar xixilive avatar xmonkey avatar yiichou avatar zfben avatar zlei1 avatar zymiboxpay avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wechat's Issues

API请求能否增加timeout option?

如题,最近微信的API好像不稳定,有时请求会花很长时间,看了一下代码好像Wechat::ClientRestClient没有处理timeout

微信提示 UnSubscribe 返回内容不合法

微信公众平台报警提示 UnSubscribe 返回内容不合法
具体报错信息:

Appid: wx********6d63a
昵称: *
社区助手
时间: 2015-11-13 17:11:00
内容: 微信服务器向公众号推送消息或事件后,得到的回应不合法
次数: 5分钟 159次
错误样例: [OpenID=oAXFPt2m_VFQfnsrNX5DCqfzfOvs][Stamp=1447405860][3rdUrl10=http://_/api/weixin_mp/][Msg=Subscribe][Event=UnSubscribe][ip=
][response_length=234][response_content=
oAXFPt2m_VF**_CqfzfOvs
gh_b66
***eb
1447405860
text
user can not receive this message
]
报警排查指引,请见: http://url.cn/ab0jnP

这个报错会在短时间大量退订的时候出现,应该是微信设置了容许少量错误返回

能不能补充接收事件推送的示例

用户点击菜单之后会发送如下的数据:

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[CLICK]]></Event>
<EventKey><![CDATA[EVENTKEY]]></EventKey>
</xml>

服务器的处理:

  on :event, with:"CLICK" do |request, key|
    case key
      when "1" then request.reply.text "1"
      when "2" then request.reply.text "2"
      else "没有这个菜单!"
    end
  end

with 中代表的是事件的类型吗?如何获取到 message 中的 EventKey ?

普通controller如何调用jsapi_ticket?

除了在controller里引入wechat_responder,还有别的方法可以在这个controller中调用PlainController.wechat.jsapi_ticket.signature(request.original_url)吗?

我想将这个项目的所有权转交给其他感兴趣的开发者,请大家报名

由于我现在的主要开发语言不再是Ruby,所以可能没有太多精力维护这个项目了。希望Ruby社区的开发者能接手这个项目。

这个项目有很多有趣的想法,特别是API设计上,至今我还觉得挺满意的,但是很多细节包括

  • 是否要使用restful-client
  • 对新API的支持
  • 如何配置本地的AccessToken存储服务等

还需要进一步的改进,有兴趣的开发者可以回复报名,谢谢。

项目内执行wechat出错,Ruby2.1.5,ActiveSupport 4.2.1

λ ~/dev/lit/ wechat2* wechat
/Users/xprimes/.rvm/gems/ruby-2.1.5/gems/activesupport-4.2.1/lib/active_support/core_ext/module/deprecation.rb:21:in `deprecate': uninitialized constant ActiveSupport::Deprecation (NameError)
    from /Users/xprimes/.rvm/gems/ruby-2.1.5/gems/activesupport-4.2.1/lib/active_support/core_ext/class/delegating_attributes.rb:26:in `<class:Class>'
    from /Users/xprimes/.rvm/gems/ruby-2.1.5/gems/activesupport-4.2.1/lib/active_support/core_ext/class/delegating_attributes.rb:6:in `<top (required)>'
    from /Users/xprimes/.rvm/gems/ruby-2.1.5/gems/activesupport-4.2.1/lib/active_support/core_ext/class.rb:2:in `require'
    from /Users/xprimes/.rvm/gems/ruby-2.1.5/gems/activesupport-4.2.1/lib/active_support/core_ext/class.rb:2:in `<top (required)>'
    from /Users/xprimes/.rvm/gems/ruby-2.1.5/gems/activesupport-4.2.1/lib/active_support/core_ext.rb:2:in `require'
    from /Users/xprimes/.rvm/gems/ruby-2.1.5/gems/activesupport-4.2.1/lib/active_support/core_ext.rb:2:in `block in <top (required)>'
    from /Users/xprimes/.rvm/gems/ruby-2.1.5/gems/activesupport-4.2.1/lib/active_support/core_ext.rb:1:in `each'
    from /Users/xprimes/.rvm/gems/ruby-2.1.5/gems/activesupport-4.2.1/lib/active_support/core_ext.rb:1:in `<top (required)>'
    from /Users/xprimes/.rvm/gems/ruby-2.1.5/gems/wechat-0.6.0/bin/wechat:10:in `require'
    from /Users/xprimes/.rvm/gems/ruby-2.1.5/gems/wechat-0.6.0/bin/wechat:10:in `<top (required)>'
    from /Users/xprimes/.rvm/gems/ruby-2.1.5/bin/wechat:23:in `load'
    from /Users/xprimes/.rvm/gems/ruby-2.1.5/bin/wechat:23:in `<main>'
    from /Users/xprimes/.rvm/gems/ruby-2.1.5/bin/ruby_executable_hooks:15:in `eval'
    from /Users/xprimes/.rvm/gems/ruby-2.1.5/bin/ruby_executable_hooks:15:in `<main>'

建议完善 README

hi ,
gem 非常好。
不过有个问题: 对于没有认证过的 订阅号怎么办? 没有appid, appsecret.. 我知道的很多朋友都是只有 “未认证的订阅号”.
如果这两样没有的话,可以使用这个GEM吗?

谢谢
思维

添加错误码40001处理

当使用多台机器时,除了42001: access_token超时, 40014:不合法的access_token,还有可能出现{"errcode":40001,"errmsg":"invalid credential, access_token is invalid or not latest hint"} 错误。感觉也需要更新access_token.

:verify_signature rendered or redirected

config/wechat.yml
default: &default
  appid: "wxdddddddddddd"
  secret: "36cddddddddd"
  token:  "neonn"
  access_token: "/var/tmp/wechat_access_token"

production:
  <<: *default

在公众号里也设置了 token等,

出现错误,

Filter chain halted as :verify_signature rendered or redirected

哪里没有配置吗?

"application did not send a complete response

App 30814 stderr: [ 2015-10-21 11:32:42.9762 30835/0xb8107b48(Worker 1) utils.rb:86 ]: ***         Exception RuntimeError in Rack application object (Missing `secret_token` and                         `secret_key_base` for 'production' environment, set these values in                         `config/secrets.yml`) (process 30835, thread 0xb8107b48(Worker 1)):
App 30814 stderr:       from /home/cong/projects/pingtanfeiyu/vendor/bundle/ruby/2.2.0                    /gems/railties-4.2.4/lib/rails/application.rb:534:in `validate_secret_key_config!'
App 30814 stderr:       from /home/cong/projects/pingtanfeiyu/vendor/bundle/ruby/2.2.0                    /gems/railties-4.2.4/lib/rails/application.rb:246:in `env_config'
App 30814 stderr:       from /home/cong/projects/pingtanfeiyu/vendor/bundle/ruby/2.2.0                    /gems/railties-4.2.4/lib/rails/engine.rb:514:in `call'
App 30814 stderr:       from /home/cong/projects/pingtanfeiyu/vendor/bundle/ruby/2.2.0                    /gems/railties-4.2.4/lib/rails/application.rb:165:in `call'
App 30814 stderr:       from /usr/lib/ruby/vendor_ruby/phusion_passenger                                        /rack/thread_handler_extension.rb:94:in                         `process_request'
App 30814 stderr:       from /usr/lib/ruby/vendor_ruby/phusion_passenger/request_handler                /thread_handler.rb:151:in `accept_and_process_next_request'
App 30814 stderr:       from /usr/lib/ruby/vendor_ruby/phusion_passenger/request_handler                /thread_handler.rb:112:in `main_loop'
App 30814 stderr:       from /usr/lib/ruby/vendor_ruby/phusion_passenger                                        /request_handler.rb:415:in `block (3 levels) in                 start_threads'
App 30814 stderr:       from /usr/lib/ruby/vendor_ruby/phusion_passenger/utils.rb:112:in `block in     create_thread_and_abort_on_exception'
[ 2015-10-21 11:32:42.9769 30188/b61d1b40 age/Cor/Req/Utils.cpp:95 ]: [Client 1-11] Sending     502 response: application did not send a complete respons                                                    

部署环境:passenger + nginx + digitalocean + ubuntu
微信认证:token验证失败
模拟请求:http://domain/wechat?signature=signature_value&echostr=echostr_value&timestamp=timestamp_value&nonce=nonce_value返回Incomplete response received from application

希望添加多客服消息转发功能

下面是官方原文,出处

如果公众号处于开发模式,需要在接收到用户发送的消息时,返回一个MsgType为transfer_customer_service的消息,微信服务器在收到这条消息时,会把这次发送的消息转到多客服系统。用户被客服接入以后,客服关闭会话以前,处于会话过程中,用户发送的消息均会被直接转发至客服系统。

gem报错

SyntaxError (/Users/byte/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/bundler/gems/wechat-rails-b7acbc05aa5c/lib/wechat/message.rb:27: syntax error, unexpected ':', expecting ';' or '\n'
      def item title: "title", description: nil, p...
                     ^
/Users/byte/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/bundler/gems/wechat-rails-b7acbc05aa5c/lib/wechat/message.rb:27: syntax error, unexpected ',', expecting keyword_end
      def item title: "title", description: nil, pic_url: n...
                              ^
/Users/byte/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/bundler/gems/wechat-rails-b7acbc05aa5c/lib/wechat/message.rb:27: Can't assign to nil
...tle: "title", description: nil, pic_url: nil, url: nil
...                               ^
/Users/byte/.rbenv/versions/2.0.0-p0/lib/ruby/gems/2.0.0/bundler/gems/wechat-rails-b7acbc05aa5c/lib/wechat/message.rb:27: Can't assign to nil
...description: nil, pic_url: nil, url: nil

按照README来配置的,服务器配置没有问题,但是不知道为啥微信服务器传数据过来的时候会报错。

关于access token的过期时间

感谢贡献,并恭喜接手项目呵呵。
我看代码里面判断过期时间是3*60,3个小时?微信文档里面是2个小时吧?另外以后是否可以提供被动刷新token的方法呢。

图文消息的空链接问题

如果图文消息中,存在空的URL node,在微信中会打开空白页面,微信中是允许不带链接的图文消息的,前提是消息中不包含URL节点。

如下面的被动响应图文消息会在微信中打开空页面:

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[news]]></MsgType>
<ArticleCount>1</ArticleCount>
<Articles>
<item>
<Title><![CDATA[title1]]></Title> 
<Description><![CDATA[description1]]></Description>
<PicUrl><![CDATA[picurl]]></PicUrl>
<Url/>
</item>
</Articles>
</xml> 

而下面的被动响应图文消息不会在微信中打开空页面:

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[news]]></MsgType>
<ArticleCount>1</ArticleCount>
<Articles>
<item>
<Title><![CDATA[title1]]></Title> 
<Description><![CDATA[description1]]></Description>
<PicUrl><![CDATA[picurl]]></PicUrl>
</item>
</Articles>
</xml> 

有考虑加入ticket的支持么?

我们现在用混合使用wechat的服务端API,和js-sdk
所以我们自己有加入'ticket/getticket'的支持
如果可以加入我发起Pull request

生成二维码失败

网站生成二维码时调用的API,如果token失效导致生成二维码失败,微信API返回json格式的错误信息时用的Content-Type是text/plain,程序只对application/json格式的返回做处理,导致无法自动刷新token、从而连续失败;

def parse_response(response, as)
content_type = response.headers[:content_type]
parse_as = {
%r{^application/json} => :json,
%r{^text/plain} => :json, # 我加了这一行用以处理这个特例
%r{^image/.*} => :file
}.each_with_object([]) { |match, memo| memo << match[1] if content_type =~ match[0] }.first || as || :text

Create rails generators

Rails generator is required to integrate devise oauth2 and wechat responders for existing rails project.

本地接入调试成功,部署到Heroku就403。

参数写死:signature=9beb5b249c3677956590959b584896d40dba8785&timestamp=12345&nonce=abcde&echostr=hahaha4
本地调试顺利返回echostr值,部署到Heroku就403了,头大中,验证方法提取出来写入controller还能通过:

class WechatSupportsController < ApplicationController
  skip_before_filter :verify_authenticity_token
  before_filter :check_weixin_legality

  def show
    render :text => params[:echostr]
  end

  def create
    if params[:xml][:MsgType] == "text"
      render "echo", :formats => :xml
    end
  end
  private
    def check_weixin_legality
      array = ["wechat", params[:timestamp], params[:nonce]].sort
      render :text => "Forbiden", :status => 403 if params[:signature] != Digest::SHA1.hexdigest(array.join)
    end
end

wechat.yml:

default: &default
  appid: "*******"
  secret: "**********"
  token:    "wechat"
  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'] %>

development:
  <<: *default

test:
  <<: *default

wrong number of arguments (2 for 1) for subscribe event

 ArgumentError:
       wrong number of arguments (2 for 1)
     # ./app/controllers/wechats_controller.rb:26:in `block in <class:WechatsController>'
     # /home/ming/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/bundler/gems/wechat-e75d5cd12414/lib/wechat/responder.rb:141:in `block in run_responder'
     # /home/ming/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/bundler/gems/wechat-e75d5cd12414/lib/wechat/responder.rb:53:in `responder_for'
     # /home/ming/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/bundler/gems/wechat-e75d5cd12414/lib/wechat/responder.rb:132:in `run_responder'
     # /home/ming/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/bundler/gems/wechat-e75d5cd12414/lib/wechat/responder.rb:94:in `create'
  # 用户关注
  on :event, with: "subscribe" do |request|
      request.reply.text "欢迎关注!"
  end

以上的代码不工作了, 好像和 cd0b874 有关.

define_singleton_method :process, responder[:proc]
send(:process, *args.unshift(request))

看上去似乎是 define_singleton_method 动态定义的方法只需要一个参数, 但 unshift 出来了2个. 我继续跟一下.

skip_verify_ssl: false的设置对命令行无效

命令行里执行wechat就报SSL的错误,我就把skip_verify_ssl: false加到~/.wechat.yml,但是无效

PS:感谢这个库,替换掉了我自己的逻辑,删了大片代码,很爽

Can not use helper like root_path inside on block

  # 默认的文字信息responder
  on :text do |request, content|
    request.reply.text "echo: #{content}" #Just echo
  end

I can not use root_path inside on block because it is in the Controller class scope not in the Controller instance scope. Why not inside a instance scope so we can use all the benefit from rails like rendering xml template with XmlBuilder, url helpers, etc.

关于access_token问题

微信的 access_token,不是需要动态获取吗,为什么直接写在配置文件config/wechar.yml里?

报告一个问题

我尝试反复获取access_token,发现每次获取的access token都不同,但是我看代码应该不会这样的。
我发现如果启动服务器之前,如果/var/tmp/access_token文件不存在,则这个问题总是存在。
我调试了一下发现读出文件的created_at为0,所以总是刷新
如果在启动server之前文件就存在,则貌似没有问题。

另外这里不会发生多线程写文件的冲突吗?

自定義菜單, no such file or directory

寫入menu.yaml後執行wechat menu_create menu.yaml 返回 No such file or directory,根據提示的文件地址找到gem下wechat文件 318行menu = YAML.load(File.read(menu_yaml_path)),但文件中找不到申明menu_yaml_path的地方。是我哪裏錯了,還是需要對menu.yaml進行系統權限更改嗎?

material add 41005

use material add to upload a image, always this error:
block in request': media data missing hint: HjjhHa0639ent1 (Wechat::ResponseError)

any help? plz

uninitialized constant ActionController::Responder (NameError)

升级到最新的commit 6a925af 后, 使用它的项目报如下错误:

/home/ming/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/responders-2.1.0/lib/action_controller/respond_with.rb:10:in `block in <module:RespondWith>': uninitialized constant ActionController::Responder (NameError)

调用jsapi_ticket出现reach max api daily quota limit hint: [VAB1bA0573vr20](45009)

每次进入搜索页面就调用一次api填充页面的jssdk签名,因为在搜索页面中我要使用jssdk获取用户的地理位置。

示例controller如下:

class SearchesController < ApplicationController
  before_action :authenticate_user!
  before_action :set_jssdk

  def new
    @query = Query.new
  end

  def set_jssdk
      @hash = Wechat.api.jsapi_ticket.signature(request.original_url)
  end
end

问题:出现reach max api daily quota limit hint错误,但在重启rails服务后正常。

说明:

  • appid和secret没有给别人使用
  • 只开了一个rails服务

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.