|  |  | WeChat [![Gem Version][version-badge]][rubygems] [![Build Status][travis-badge]][travis] [![Code Climate][codeclimate-badge]][codeclimate] [![Code Coverage][codecoverage-badge]][codecoverage]
 | 
						
						
						
							|  |  | ======
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | [![Join the chat][gitter-badge]][gitter] [![Issue Stats][issue-badge]][issuestats] [![PR Stats][pr-badge]][issuestats]
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | [中文文档 Chinese document](/README-CN.md)
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | [Wechat](http://www.wechat.com/) is a free messaging and calling app developed by [Tencent](http://tencent.com/en-us/index.shtml), after linked billion people, Wechat become a platform of application
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | WeChat gem trying to helping Rails developer to integrated [enterprise account](https://qy.weixin.qq.com) / [public account](https://mp.weixin.qq.com/) easily. Below feature is ready and no need writing adapter code talking to wechat server directly.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | - [Sending message](http://qydev.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E6%B6%88%E6%81%AF) API(Can access via console or in rails)
 | 
						
						
						
							|  |  | - [Receiving message](http://qydev.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E6%94%B6%E6%B6%88%E6%81%AF%E4%B8%8E%E4%BA%8B%E4%BB%B6)(You must running on rails server to receiving message)
 | 
						
						
						
							|  |  | - [Wechat JS-SDK](http://qydev.weixin.qq.com/wiki/index.php?title=%E5%BE%AE%E4%BF%A1JS%E6%8E%A5%E5%8F%A3) config signature
 | 
						
						
						
							|  |  | - OAuth 2.0 authentication
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | `wechat` command share the same API in console, so you can interactive with wechat server quickly, without starting up web environment/code.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | A responder DSL can used in Rails controller, so giving a event based interface to handler message sent by end user from wechat server.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | Wechat provide OAuth2.0 as authentication service and possible to intergrated with devise/other authorization gems, [omniauth-wechat-oauth2](https://github.com/skinnyworm/omniauth-wechat-oauth2) is a good start
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | There is official [weui](https://github.com/weui/weui), which corresponding Rails gems called [weui-rails](https://github.com/Eric-Guo/weui-rails) available, if you prefer following the same UI design as wechat.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ## Installation
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | Using `gem install`
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | gem install "wechat"
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | Or add to your app's `Gemfile`:
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | gem 'wechat'
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | Run the following command to install it:
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ```console
 | 
						
						
						
							|  |  | bundle install
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | Run the generator:
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ```console
 | 
						
						
						
							|  |  | rails generate wechat:install
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | `rails g wechat:install` will generated the initial `wechat.yml` configuration, example wechat controller and corresponding routes.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ## Configuration
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | #### Configure for command line
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | You can using `wechat` command solely, you need created configure file `~/.wechat.yml` and including below content for public account. the access_token will be write as a file.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | appid: "my_appid"
 | 
						
						
						
							|  |  | secret: "my_secret"
 | 
						
						
						
							|  |  | access_token: "/var/tmp/wechat_access_token"
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | For enterprise account, need using `corpid` instead of `appid` as enterprise account support multiply application (Tencent called agent) in one enterprise account. Obtain the `corpsecret` is a little tricky, must created at management mode->privilege setting and create any of management group to obtain. Due to Tencent currently only provide Chinese interface for they management console, it's highly recommend you find a college knowing Mandarin to help you to obtain.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | Windows user need store `.wechat.yml` at `C:/Users/[user_name]/` (replace your user name), also be caution the direction of folder separator.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | corpid: "my_appid"
 | 
						
						
						
							|  |  | corpsecret: "my_secret"
 | 
						
						
						
							|  |  | agentid: 1 # Integer, which can be obtain from application, settings
 | 
						
						
						
							|  |  | access_token: "C:/Users/[user_name]/wechat_access_token"
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | #### Configure for Rails
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | Rails configuration files support different environment similar to database.yml, after running `rails generate wechat:install` you can find configuration files at `config/wechat.yml`
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | Public account congfigure example:
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | 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'] %>
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | development:
 | 
						
						
						
							|  |  |   <<: *default
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | test:
 | 
						
						
						
							|  |  |   <<: *default
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | Although it's optional for public account, but highly recommend to enable encrypt mode by add below two items to `wechat.yml`
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | default: &default
 | 
						
						
						
							|  |  |   encrypt_mode: true
 | 
						
						
						
							|  |  |   encoding_aes_key:  "my_encoding_aes_key"
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | Enterprise account must using encrypt mode (`encrypt_mode: true` is default on, no need configure).
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | The `token` and `encoding_aes_key` can be obtain 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'] %>
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | development:
 | 
						
						
						
							|  |  |   <<: *default
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | test:
 | 
						
						
						
							|  |  |   <<: *default
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ##### Configure priority
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | Running `wechat` command in the root folder of Rails application will using the Rails configuration first (`default` section), if can not find it, will relay on `~\.wechat.yml`, such behavior enable manage more wechat public account and enterprise account without changing your home `~\.wechat.yml` file.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ##### Wechat server timeout setting
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | Stability various even for Tencent wechat server, so setting a long timeout may needed, default is 20 seconds if not set.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ##### Skip the SSL verification
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | SSL Certification can also be corrupted by some reason in China, [it's reported](http://qydev.weixin.qq.com/qa/index.php?qa=11037) and if it's happen to you, can setting `skip_verify_ssl: true`. (not recommend)  
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | #### Configure individual responder with different appid
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | Rare case, you may want to hosting more than one wechat enterprise/public account in one Rails application, so you can given those configuration info when calling `wechat_responder`
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ```ruby
 | 
						
						
						
							|  |  | class WechatFirstController < ActionController::Base
 | 
						
						
						
							|  |  |    wechat_responder appid: "app1", secret: "secret1", token: "token1", access_token: Rails.root.join("tmp/access_token1")
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |    on :text, with:"help", respond: "help content"
 | 
						
						
						
							|  |  | end
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | #### JS-SDK helper
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | JS-SDK enable you control wechat behavior in your web page, but need inject a config with signature methods first, you can obtain those signature hash via below
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ```ruby
 | 
						
						
						
							|  |  | WechatsController.wechat.jsapi_ticket.signature(request.original_url)
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ## The API privilege
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | wechat gems won't handle any privilege exception. (except token time out, 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, more info, please reference [official document](http://mp.weixin.qq.com/wiki/7/2d301d4b757dedc333b9a9854b457b47.html).
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ## 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 keep there in order to copy & find in official document more easier.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | #### Public account command line
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | $ wechat
 | 
						
						
						
							|  |  | Wechat commands:
 | 
						
						
						
							|  |  |   wechat callbackip                                        # 获取微信服务器IP地址
 | 
						
						
						
							|  |  |   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 group_create [GROUP_NAME]                         # 创建分组
 | 
						
						
						
							|  |  |   wechat group_delete [GROUP_ID]                           # 删除分组
 | 
						
						
						
							|  |  |   wechat group_update [GROUP_ID, NEW_GROUP_NAME]           # 修改分组名
 | 
						
						
						
							|  |  |   wechat groups                                            # 查询所有分组
 | 
						
						
						
							|  |  |   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 menu                                              # 当前菜单
 | 
						
						
						
							|  |  |   wechat menu_create [MENU_YAML_PATH]                      # 创建菜单
 | 
						
						
						
							|  |  |   wechat menu_delete                                       # 删除菜单
 | 
						
						
						
							|  |  |   wechat oauth2_url [REDIRECT_URI]                         # 生成OAuth2.0验证URL
 | 
						
						
						
							|  |  |   wechat qrcode_create_limit_scene [SCENE_ID_OR_STR]       # 请求永久二维码
 | 
						
						
						
							|  |  |   wechat qrcode_create_scene [SCENE_ID, EXPIRE_SECONDS]    # 请求临时二维码
 | 
						
						
						
							|  |  |   wechat qrcode_download [TICKET, QR_CODE_PIC_PATH]        # 通过ticket下载二维码
 | 
						
						
						
							|  |  |   wechat template_message [OPENID, TEMPLATE_YAML_PATH]     # 模板消息接口
 | 
						
						
						
							|  |  |   wechat user [OPEN_ID]                                    # 获取用户基本信息
 | 
						
						
						
							|  |  |   wechat user_change_group [OPEN_ID, TO_GROUP_ID]          # 移动用户分组
 | 
						
						
						
							|  |  |   wechat user_group [OPEN_ID]                              # 查询用户所在分组
 | 
						
						
						
							|  |  |   wechat user_update_remark [OPEN_ID, REMARK]              # 设置备注名
 | 
						
						
						
							|  |  |   wechat users                                             # 关注者列表
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | #### Enterprise account command line
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | $ wechat
 | 
						
						
						
							|  |  | Wechat 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 convert_to_openid [USER_ID]                       # userid转换成openid
 | 
						
						
						
							|  |  |   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 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 menu                                              # 当前菜单
 | 
						
						
						
							|  |  |   wechat menu_create [MENU_YAML_PATH]                      # 创建菜单
 | 
						
						
						
							|  |  |   wechat menu_delete                                       # 删除菜单
 | 
						
						
						
							|  |  |   wechat message_send [OPENID, TEXT_MESSAGE]               # 发送文字消息
 | 
						
						
						
							|  |  |   wechat oauth2_url [REDIRECT_URI]                         # 生成OAuth2.0验证URL
 | 
						
						
						
							|  |  |   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_delete [USER_ID]                             # 删除成员
 | 
						
						
						
							|  |  |   wechat user_list [DEPARTMENT_ID]                         # 获取部门成员详情
 | 
						
						
						
							|  |  |   wechat user_simplelist [DEPARTMENT_ID]                   # 获取部门成员
 | 
						
						
						
							|  |  |   wechat user_update_remark [OPEN_ID, REMARK]              # 设置备注名
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ### 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
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | menu content for a wechat application can be defined as a yaml files, like `menu.yaml`
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | button:
 | 
						
						
						
							|  |  |  -
 | 
						
						
						
							|  |  |   name: "Want"
 | 
						
						
						
							|  |  |   sub_button:
 | 
						
						
						
							|  |  |    -
 | 
						
						
						
							|  |  |     type: "scancode_waitmsg"
 | 
						
						
						
							|  |  |     name: "绑定用餐二维码"
 | 
						
						
						
							|  |  |     key: "BINDING_QR_CODE"
 | 
						
						
						
							|  |  |    -
 | 
						
						
						
							|  |  |     type: "click"
 | 
						
						
						
							|  |  |     name: "预订午餐"
 | 
						
						
						
							|  |  |     key:  "BOOK_LUNCH"
 | 
						
						
						
							|  |  |    -
 | 
						
						
						
							|  |  |     type: "click"
 | 
						
						
						
							|  |  |     name: "预订晚餐"
 | 
						
						
						
							|  |  |     key:  "BOOK_DINNER"
 | 
						
						
						
							|  |  |  -
 | 
						
						
						
							|  |  |   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/"
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | Caution: make sure you having management privilege for those application below running below command, other will got [60011](http://qydev.weixin.qq.com/wiki/index.php?title=%E5%85%A8%E5%B1%80%E8%BF%94%E5%9B%9E%E7%A0%81%E8%AF%B4%E6%98%8E) error.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | $ wechat menu_create menu.yaml
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ##### Sent custom news
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | Sending custom_news should also defined as a yaml file, like `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"
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | After that, can running command:
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | $ wechat custom_news oCfEht9oM*********** articles.yml
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ##### Sent template message
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | Sending template message via yaml file similar too, defined `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 registed"
 | 
						
						
						
							|  |  |       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, can running command:
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | $ wechat template_message oCfEht9oM*********** template.yml
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ## Rails Responder Controller DSL
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | In order to responding the message user sent, Rails developer need create a wechat responder controller and defined the routing in `routes.rb`
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ```ruby
 | 
						
						
						
							|  |  |   resource :wechat, only:[:show, :create]
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | So the ActionController should defined like below:
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ```ruby
 | 
						
						
						
							|  |  | 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 got count as <n> as parameter
 | 
						
						
						
							|  |  |   on :text, with: /^(\d+) news$/ do |request, count|
 | 
						
						
						
							|  |  |     # Wechat article can only contain max 10 items, large than 10 will 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 same time, so wechat won't trigger subscribe event any more
 | 
						
						
						
							|  |  |   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 scaned 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 click the menu button
 | 
						
						
						
							|  |  |   on :click, with: 'BOOK_LUNCH' do |request, key|
 | 
						
						
						
							|  |  |     request.reply.text "User: #{request[:FromUserName]} click #{key}"
 | 
						
						
						
							|  |  |   end
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   # When user view 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 sent the imsage
 | 
						
						
						
							|  |  |   on :image do |request|
 | 
						
						
						
							|  |  |     request.reply.image(request[:MediaId]) # Echo the sent image to user
 | 
						
						
						
							|  |  |   end
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   # When user sent the voice
 | 
						
						
						
							|  |  |   on :voice do |request|
 | 
						
						
						
							|  |  |     request.reply.voice(request[:MediaId]) # Echo the sent voice to user
 | 
						
						
						
							|  |  |   end
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   # When user sent the 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 sent location
 | 
						
						
						
							|  |  |   on :location do |request|
 | 
						
						
						
							|  |  |     request.reply.text("Latitude: #{message[:Latitude]} Longitude: #{message[:Longitude]} Precision: #{message[:Precision]}")
 | 
						
						
						
							|  |  |   end
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   on :event, with: 'unsubscribe' do |request|
 | 
						
						
						
							|  |  |     request.reply.success # user can not receive this message
 | 
						
						
						
							|  |  |   end
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   # When user enter 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) 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) 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 invent user 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) 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
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   # Any not match above will fail to below
 | 
						
						
						
							|  |  |   on :fallback, respond: 'fallback message'
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   # If you need do something after response, you should add after_wechat_response(req, res)
 | 
						
						
						
							|  |  |   # private
 | 
						
						
						
							|  |  |   #
 | 
						
						
						
							|  |  |   # def after_wechat_response(req, res)
 | 
						
						
						
							|  |  |   #   WechatLog.create req: req, res: res
 | 
						
						
						
							|  |  |   # end
 | 
						
						
						
							|  |  | end
 | 
						
						
						
							|  |  | ```
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | So the importent 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 responding user's message.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | Below is current supported message_type:
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | - :text  text message, using `:with` to match text content like `on(:text, with:'help'){|message, content| ...}`
 | 
						
						
						
							|  |  | - :image image message
 | 
						
						
						
							|  |  | - :voice voice message
 | 
						
						
						
							|  |  | - :video video message
 | 
						
						
						
							|  |  | - :link  link message
 | 
						
						
						
							|  |  | - :event event message, using `:with` to match particular event
 | 
						
						
						
							|  |  | - :click virtual event message, wechat still sent event message,but gems will mapping to menu click event.
 | 
						
						
						
							|  |  | - :view  virtual view message, wechat still sent event message,but gems will mapping to menu view page event.
 | 
						
						
						
							|  |  | - :scan  virtual scan message, wechat still sent event message, but gems will mapping on scan event
 | 
						
						
						
							|  |  | - :batch_job  virtual batch job message
 | 
						
						
						
							|  |  | - :location virtual location message
 | 
						
						
						
							|  |  | - :fallback default message, when no other responder can handle incoming messsage, will using fallback to handle
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ### Transfer to customer service
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ```ruby
 | 
						
						
						
							|  |  | 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 setting default text responder if you want to using [multiply human customer service](http://dkf.qq.com/), other will lead text message can not transfer.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ## Known Issue
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | * Sometime, enterprise account can not receive the menu message due to Tencent server can not resolved the DNS, so using IP as a callback URL more stable, but it's never happen for user sent text message.
 | 
						
						
						
							|  |  | * Enterprise batch replace users using a CSV format file, but if you using the download template directly, it's [not working](http://qydev.weixin.qq.com/qa/index.php?qa=13978), must open the CSV file in excel first, then save as CSV format again, seems Tencent only support Excel save as CSV file format.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | [version-badge]: https://badge.fury.io/rb/wechat.svg
 | 
						
						
						
							|  |  | [rubygems]: https://rubygems.org/gems/wechat
 | 
						
						
						
							|  |  | [travis-badge]: https://travis-ci.org/Eric-Guo/wechat.svg
 | 
						
						
						
							|  |  | [travis]: https://travis-ci.org/Eric-Guo/wechat
 | 
						
						
						
							|  |  | [codeclimate-badge]: https://codeclimate.com/github/Eric-Guo/wechat.png
 | 
						
						
						
							|  |  | [codeclimate]: https://codeclimate.com/github/Eric-Guo/wechat
 | 
						
						
						
							|  |  | [codecoverage-badge]: https://codeclimate.com/github/Eric-Guo/wechat/coverage.png
 | 
						
						
						
							|  |  | [codecoverage]: https://codeclimate.com/github/Eric-Guo/wechat/coverage
 | 
						
						
						
							|  |  | [gitter-badge]: https://badges.gitter.im/Join%20Chat.svg
 | 
						
						
						
							|  |  | [gitter]: https://gitter.im/Eric-Guo/wechat?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
 | 
						
						
						
							|  |  | [issue-badge]: http://issuestats.com/github/Eric-Guo/wechat/badge/issue
 | 
						
						
						
							|  |  | [pr-badge]: http://issuestats.com/github/Eric-Guo/wechat/badge/pr
 | 
						
						
						
							|  |  | [issuestats]: http://issuestats.com/github/Eric-Guo/wechat
 |