wechat 0.13.1 → 0.15.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +23 -2
- data/README-CN.md +109 -96
- data/README.md +15 -2
- data/bin/wechat +6 -2
- data/lib/wechat/api_loader.rb +7 -4
- data/lib/wechat/cipher.rb +1 -1
- data/lib/wechat/concern/common.rb +9 -2
- data/lib/wechat/concern/qcloud.rb +1 -1
- data/lib/wechat/controller_api.rb +5 -5
- data/lib/wechat/corp_api.rb +16 -0
- data/lib/wechat/helpers.rb +2 -1
- data/lib/wechat/qcloud/token.rb +1 -1
- data/lib/wechat/responder.rb +35 -1
- data/lib/wechat/ticket/jsapi_base.rb +1 -1
- data/lib/wechat/token/access_token_base.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +39 -40
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fce405bc512324a4f862cab6f746833ec387b6a5eed1fc9f720a9bda16e309da
|
4
|
+
data.tar.gz: 932fba467df6c8f357e713bd63e6a5537436fe8e34fc1a9c50f0c9d5010a6707
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6a2f9892ae32dd7c86f9e4f80d026cef254f738dbb93cab3d8ae3a411307144eb767077e0b781652d2fcc768d2a51cb504e3134bc0c4e0efc4b30066b78152ce
|
7
|
+
data.tar.gz: 2fba8e299e8cb2614852d587bd84287463a57ae91248dcc8d729ee0539038160ff2ec3f469c4ffad9e3906ae87c2b12020bc6c47501b12127593202da2483322
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/CHANGELOG.md
CHANGED
@@ -1,10 +1,31 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
## v0.
|
3
|
+
## v0.15.0 (released at 2021-12-21)
|
4
|
+
|
5
|
+
* Add wechat message json format support, by @younthu #306
|
6
|
+
* Support Rails 7 in this version.
|
7
|
+
* Fix wechat command-line 1st attempt bug #307
|
8
|
+
|
9
|
+
## v0.14.0 (released at 2021-09-15)
|
10
|
+
|
11
|
+
* Add beta support for Conversation archive in WeCom, discuss at #303
|
12
|
+
* Avoid using 1.hour in early loading to improve Rails 6+ compatibility.
|
13
|
+
|
14
|
+
## v0.13.3 (released at 2021-06-18)
|
15
|
+
|
16
|
+
* material add video description by @zlei1 #301
|
17
|
+
* Allow using http v5
|
18
|
+
|
19
|
+
## v0.13.2 (released at 2021-04-21)
|
20
|
+
|
21
|
+
* New material_add_news API, by @zlei1 #300
|
22
|
+
* Support open_tag, by @xiajian2019 #299
|
23
|
+
|
24
|
+
## v0.13.1 (released at 2021-03-15)
|
4
25
|
|
5
26
|
* Fix MpApi initialize bug, by @hardywu #296
|
6
27
|
|
7
|
-
## v0.13.0 (released at
|
28
|
+
## v0.13.0 (released at 2021-03-03)
|
8
29
|
|
9
30
|
* Support zeitwerk only and Rails 6+ only.
|
10
31
|
* Support Ruby 2.6+ only.
|
data/README-CN.md
CHANGED
@@ -1,25 +1,25 @@
|
|
1
|
-
WeChat [![Gem Version](https://badge.fury.io/rb/wechat.svg)](https://rubygems.org/gems/wechat) [![Build Status](https://
|
1
|
+
WeChat [![Gem Version](https://badge.fury.io/rb/wechat.svg)](https://rubygems.org/gems/wechat) [![Build Status](https://mixtint.semaphoreci.com/badges/wechat/branches/main.svg?style=shields)](https://mixtint.semaphoreci.com/projects/wechat) [![Maintainability](https://api.codeclimate.com/v1/badges/12885358487c13e91e00/maintainability)](https://codeclimate.com/github/Eric-Guo/wechat/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/12885358487c13e91e00/test_coverage)](https://codeclimate.com/github/Eric-Guo/wechat/test_coverage)
|
2
2
|
======
|
3
3
|
|
4
4
|
[![Join the chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Eric-Guo/wechat?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
5
5
|
|
6
|
-
WeChat
|
6
|
+
WeChat Gem 帮助开发者方便地在 Rails 环境中集成[微信公众平台](https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.html)、[企业微信](https://work.weixin.qq.com/api/doc)和[小程序](https://developers.weixin.qq.com/miniprogram/dev/framework/),包括功能:
|
7
7
|
|
8
|
-
- 微信公众平台/企业微信[发送消息](http://qydev.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E6%B6%88%E6%81%AF)API(命令行和Web环境都可以使用)
|
9
|
-
- [接收消息](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)(必须运行Web服务器)
|
10
|
-
- [微信JS-SDK](http://qydev.weixin.qq.com/wiki/index.php?title=%E5%BE%AE%E4%BF%A1JS%E6%8E%A5%E5%8F%A3) config接口注入权限验证
|
11
|
-
- OAuth 2.0认证机制
|
12
|
-
- 接收消息会话(
|
8
|
+
- 微信公众平台/企业微信[发送消息](http://qydev.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E6%B6%88%E6%81%AF) API(命令行和 Web 环境都可以使用)
|
9
|
+
- [接收消息](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)(必须运行 Web 服务器)
|
10
|
+
- [微信JS-SDK](http://qydev.weixin.qq.com/wiki/index.php?title=%E5%BE%AE%E4%BF%A1JS%E6%8E%A5%E5%8F%A3) config 接口注入权限验证
|
11
|
+
- OAuth 2.0 认证机制
|
12
|
+
- 接收消息会话 ( Session ) 记录机制(可选)
|
13
13
|
|
14
|
-
|
14
|
+
命令行工具 `wechat` 可以调用各种无需 Web 环境的 API,同时也提供了 Rails Controller 的 Responder DSL 。可以帮助开发者方便地在Rails 应用中集成微信的消息处理,包括主动推送的和被动响应的消息。
|
15
15
|
|
16
|
-
如果您的App还需要集成微信OAuth2.0
|
16
|
+
如果您的 App 还需要集成微信 OAuth2.0 除了简便的 `wechat_oauth2` 指令,也可以考虑 [omniauth-wechat-oauth2](https://github.com/skinnyworm/omniauth-wechat-oauth2) 以便和 [devise](https://github.com/heartcombo/devise) 集成提供完整的用户认证。
|
17
17
|
|
18
|
-
如果您对如何制作微信网页UI没有灵感,可以参考官方的[weui](https://github.com/weui/weui)
|
18
|
+
如果您对如何制作微信网页 UI 没有灵感,可以参考官方的 [weui](https://github.com/weui/weui) 。针对 Rails 的 Gem 是 [weui-rails](https://github.com/Eric-Guo/weui-rails)。
|
19
19
|
|
20
|
-
主页型应用请使用[`wechat_api`](#wechat_api---rails-controller-wechat-api),传统消息型应用请使用[`wechat_responder`](#wechat_responder---rails-responder-controller-dsl)。
|
20
|
+
主页型应用请使用 [`wechat_api`](#wechat_api---rails-controller-wechat-api),传统消息型应用请使用 [`wechat_responder`](#wechat_responder---rails-responder-controller-dsl)。
|
21
21
|
|
22
|
-
如果您想从一个稍微完整一些的示例开始微信开发,可以参考[wechat-starter](https://github.com/goofansu/wechat-starter),这个示例甚至包括了微信支付的内容。
|
22
|
+
如果您想从一个稍微完整一些的示例开始微信开发,可以参考 [wechat-starter](https://github.com/goofansu/wechat-starter),这个示例甚至包括了微信支付的内容。
|
23
23
|
|
24
24
|
## 安装
|
25
25
|
|
@@ -51,24 +51,24 @@ bundle install
|
|
51
51
|
rails generate wechat:install
|
52
52
|
```
|
53
53
|
|
54
|
-
|
54
|
+
运行 `rails g wechat:install` 后会自动生成 wechat.yml 配置,还有 wechats_controller 及相关路由配置到当前 Rails 项目。
|
55
55
|
|
56
|
-
启用
|
56
|
+
启用 Session 会话记录:
|
57
57
|
|
58
58
|
```console
|
59
59
|
rails g wechat:session
|
60
60
|
rake db:migrate
|
61
61
|
```
|
62
62
|
|
63
|
-
运行后会自动启用回调消息会话(
|
63
|
+
运行后会自动启用回调消息会话 (Session) 记录,`wechat` Gem 会在 Rails 项目中生成两个文件,用户可以在 *wechat_session* 表中添加更多字段或者声明一些关联关系。使用已有的 **hash_store** 直接保存也是可以的,但对于 PostgreSQL 用户,使用 [hstore](http://guides.rubyonrails.org/active_record_postgresql.html#hstore) 或者 json 格式可能更佳,当然最佳方案仍然是添加新字段记录数据。
|
64
64
|
|
65
|
-
启用Redis存贮
|
65
|
+
启用 Redis 存贮 Token 和 Ticket:
|
66
66
|
|
67
67
|
```console
|
68
68
|
rails g wechat:redis_store
|
69
69
|
```
|
70
70
|
|
71
|
-
Redis存贮相比默认的文件存贮,可以允许Rails
|
71
|
+
Redis 存贮相比默认的文件存贮,可以允许 Rails 应用运行在多台服务器中。如果只有一台服务器,仍然推荐使用默认的文件存贮,另外命令行不会读取 Redis 存贮的 Token 或者 Ticket。
|
72
72
|
|
73
73
|
启用数据库配置微信账户:
|
74
74
|
|
@@ -77,7 +77,7 @@ rails g wechat:config
|
|
77
77
|
rake db:migrate
|
78
78
|
```
|
79
79
|
|
80
|
-
运行后会在数据库中创建
|
80
|
+
运行后会在数据库中创建 `wechat_configs` 表用来记录不同微信账户的配置。
|
81
81
|
|
82
82
|
## 配置
|
83
83
|
|
@@ -85,13 +85,13 @@ rake db:migrate
|
|
85
85
|
|
86
86
|
请先确保在服务器上配置成功,再到微信官网提交链接。否则微信会提示错误。
|
87
87
|
|
88
|
-
|
88
|
+
默认通过 `rails g wechat:install` 生成的 URL 是: `http://your-server.com/wechat`
|
89
89
|
|
90
|
-
appid/corpid
|
90
|
+
appid/corpid 以及 secret 的配置请阅读下一节
|
91
91
|
|
92
92
|
#### 命令行程序的配置
|
93
93
|
|
94
|
-
要使用命令行程序,需要在home
|
94
|
+
要使用命令行程序,需要在 home 目录中创建一个 `~/.wechat.yml`,包含以下内容。其中 `access_token` 是存放 access_token 的文件位置。
|
95
95
|
|
96
96
|
```
|
97
97
|
appid: "my_appid"
|
@@ -99,17 +99,17 @@ secret: "my_secret"
|
|
99
99
|
access_token: "/var/tmp/wechat_access_token"
|
100
100
|
```
|
101
101
|
|
102
|
-
Windows
|
102
|
+
Windows 或者使用企业号需要存放在 `C:/Users/[user_name]/` 下,其中 corpid 和 corpsecret 可以从企业号管理界面的设置 -> 权限管理,通过新建任意一个管理组后获取。
|
103
103
|
|
104
104
|
```
|
105
105
|
corpid: "my_appid"
|
106
106
|
corpsecret: "my_secret"
|
107
|
-
agentid: 1 # 企业应用的id
|
107
|
+
agentid: 1 # 企业应用的 id 整型可在应用的设置页面查看
|
108
108
|
access_token: "C:/Users/[user_name]/wechat_access_token"
|
109
109
|
```
|
110
110
|
|
111
111
|
#### Rails 全局配置
|
112
|
-
Rails
|
112
|
+
Rails 应用程序中,需要将配置文件放在 `config/wechat.yml`,可以为不同 environment 创建不同的配置。
|
113
113
|
|
114
114
|
微信公众平台配置示例:
|
115
115
|
|
@@ -145,7 +145,7 @@ default: &default
|
|
145
145
|
encoding_aes_key: "my_encoding_aes_key"
|
146
146
|
```
|
147
147
|
|
148
|
-
企业微信配置下必须使用加密模式,其中token和encoding_aes_key
|
148
|
+
企业微信配置下必须使用加密模式,其中 token 和 encoding_aes_key 可以从企业号管理界面的应用中心 -> 某个应用 -> 模式选择,选择回调模式后获得。
|
149
149
|
|
150
150
|
```
|
151
151
|
default: &default
|
@@ -196,31 +196,31 @@ test:
|
|
196
196
|
# secret: "my_secret"
|
197
197
|
```
|
198
198
|
|
199
|
-
|
199
|
+
支持微信公众平台 / 企业微信多账号的注意点 ( 例如: 增加账号 `wx2` ):
|
200
200
|
|
201
|
-
* 配置文件可增加多个微信公众平台(企业微信)配置,用法类似Rails
|
201
|
+
* 配置文件可增加多个微信公众平台 ( 企业微信 ) 配置,用法类似 Rails 中 `config/database.yml` 多数据库配置的处理。 `development`, `test`, `production` 是默认账号的配置段,要想增加账号 `wx2`,你需要增加配置段 `wx2_development`, `wx2_test`, `wx2_production`。
|
202
202
|
|
203
|
-
*
|
203
|
+
* 声明账号 `wx2 `的 `wechat_responder`:
|
204
204
|
```ruby
|
205
205
|
wechat_responder account: :wx2
|
206
206
|
```
|
207
207
|
|
208
|
-
* `Wechat.api(:wx2)`
|
208
|
+
* `Wechat.api(:wx2)` 表示使用账号 `wx2` 的 Wechat api,而 `Wechat.api` 或 `Wechat.api(:default)` 则表示默认账号的 wechat api。
|
209
209
|
|
210
|
-
* 在wechat
|
210
|
+
* 在 wechat 命令行中,通过增加可选参数 `-a, [--account=ACCOUNT]` 来表示使用其他账号,例如 `wechat users -a wx2` 表示列举 `wx2` 这个账号的粉丝列表
|
211
211
|
|
212
|
-
进一步的多账号支持参见[PR 150](https://github.com/Eric-Guo/wechat/pull/150)。
|
212
|
+
进一步的多账号支持参见 [PR 150](https://github.com/Eric-Guo/wechat/pull/150)。
|
213
213
|
|
214
214
|
#### 数据库微信账户配置
|
215
215
|
启用数据库微信配置之后,会生成如下数据表:
|
216
216
|
|
217
217
|
属性 | 类型 | 备注
|
218
218
|
---- | ---- | ----
|
219
|
-
environment | 字串 | 必填。配置对应的运行环境,一般有:`production`、`development`、`test`。比如
|
220
|
-
account | 字串 | 必填。自定义的微信账户名称。同一
|
221
|
-
enabled | 布尔 | 必填。配置是否生效。默认
|
222
|
-
appid | 字串 | 公众号 id
|
223
|
-
secret | 字串 | 公众号相关配置。当公众号
|
219
|
+
environment | 字串 | 必填。配置对应的运行环境,一般有:`production`、`development`、`test`。比如 `production` 配置仅在生产环境有效。默认为 `development`。
|
220
|
+
account | 字串 | 必填。自定义的微信账户名称。同一 `environment` 下,账户名称不允许重复。
|
221
|
+
enabled | 布尔 | 必填。配置是否生效。默认 `true`。
|
222
|
+
appid | 字串 | 公众号 id , 此字段和 `corpid` 两者必填其一。
|
223
|
+
secret | 字串 | 公众号相关配置。当公众号 `appid` 存在时必填。
|
224
224
|
corpid | 字串 | 企业号 id。此字段和 `appid` 两者必填其一。
|
225
225
|
corpsecret | 字串 | 企业号相关配置。当企业号 `corpid` 存在时必填。
|
226
226
|
agentid | 整数 | 企业号相关配置。当企业号 `corpid` 存在时必填。
|
@@ -229,7 +229,7 @@ encoding_aes_key | 字串 | 当 `encrypt_mode` 为 `true` 时必填。
|
|
229
229
|
token | 字串 | 必填。
|
230
230
|
access_token | 字串 | 必填。存储 `access token` 文件的路径。
|
231
231
|
jsapi_ticket | 字串 | 必填。存储 `jsapi ticket` 文件的路径。
|
232
|
-
skip_verify_ssl |
|
232
|
+
skip_verify_ssl | 布尔|
|
233
233
|
timeout | 整数 | 默认值是 20。
|
234
234
|
trusted_domain_fullname | 字串 |
|
235
235
|
|
@@ -237,21 +237,21 @@ trusted_domain_fullname | 字串 |
|
|
237
237
|
|
238
238
|
##### 配置优先级
|
239
239
|
|
240
|
-
注意在Rails
|
240
|
+
注意在Rails项目根目录下运行 `wechat` 命令行工具会优先使用 `config/wechat.yml `中的 `default`配置,如果失败则使用 `~\.wechat.yml` 中的配置,以便于在生产环境下管理多个微信账号应用。
|
241
241
|
|
242
|
-
如果启用数据库账户配置,数据库中的账户信息在读入
|
242
|
+
如果启用数据库账户配置,数据库中的账户信息在读入 `wechat.yml` 或环境变量之后被载入。当存在同名账户时,数据库中的配置会覆盖前两者。
|
243
243
|
|
244
244
|
##### 配置微信服务器超时
|
245
245
|
|
246
|
-
|
246
|
+
微信服务器有时请求会花很长时间,如果不配置默认为 20 秒,可视情况配置。
|
247
247
|
|
248
248
|
##### 配置跳过SSL认证
|
249
249
|
|
250
|
-
Wechat服务器有报道曾出现[RestClient::SSLCertificateNotVerified](http://qydev.weixin.qq.com/qa/index.php?qa=11037)错误,此时可以选择关闭SSL验证。`skip_verify_ssl: true`
|
250
|
+
Wechat 服务器有报道曾出现 [RestClient::SSLCertificateNotVerified](http://qydev.weixin.qq.com/qa/index.php?qa=11037) 错误,此时可以选择关闭SSL验证。`skip_verify_ssl: true`
|
251
251
|
|
252
|
-
#### 为每个Responder配置不同的appid和secret
|
252
|
+
#### 为每个 Responder 配置不同的 appid 和 secret
|
253
253
|
|
254
|
-
有些情况下,单个Rails
|
254
|
+
有些情况下,单个 Rails 应用可能需要处理来自多个微信公众号的消息,您可以通过在 `wechat_responder` 和 `wechat_api` 后配置多个相关参数来支持多账号。
|
255
255
|
|
256
256
|
```ruby
|
257
257
|
class WechatFirstController < ActionController::Base
|
@@ -276,9 +276,9 @@ end
|
|
276
276
|
|
277
277
|
#### JS-SDK 支持
|
278
278
|
|
279
|
-
通过JS-SDK可以在HTML
|
279
|
+
通过 JS-SDK 可以在 HTML 网页中控制微信客户端的行为,但必须先注入配置信息. Wechat gem 提供了帮助方法 `wechat_config_js` 使这个过程更简单:
|
280
280
|
|
281
|
-
注意wechat_config_js指令依赖于[`wechat_api`](#wechat_api---rails-controller-wechat-api)
|
281
|
+
注意 wechat_config_js 指令依赖于 [`wechat_api`](#wechat_api---rails-controller-wechat-api) 或 [`wechat_responder`](#wechat_responder---rails-responder-controller-dsl) ,需要先在 controller 里面添加。
|
282
282
|
|
283
283
|
```erb
|
284
284
|
<body>
|
@@ -292,9 +292,9 @@ end
|
|
292
292
|
</body>
|
293
293
|
```
|
294
294
|
|
295
|
-
|
295
|
+
在开发模式下,由于程序往往通过微信调试工具的服务器端调试工具反向代理被访问,此时需要配置 `trusted_domain_fullname` 以便wechat gem 可以使用正确的域名做 JS-SDK 的权限签名。
|
296
296
|
|
297
|
-
#### OAuth2.0验证接口支持
|
297
|
+
#### OAuth2.0 验证接口支持
|
298
298
|
|
299
299
|
公众号可使用如下代码取得关注用户的相关信息。
|
300
300
|
|
@@ -307,7 +307,7 @@ class CartController < ActionController::Base
|
|
307
307
|
@articles = @current_user.articles
|
308
308
|
end
|
309
309
|
|
310
|
-
# 指定 account_name
|
310
|
+
# 指定 account_name 可以使用任意微信账户
|
311
311
|
# wechat_oauth2('snsapi_base', nil, account_name) do |openid|
|
312
312
|
# ...
|
313
313
|
# end
|
@@ -331,27 +331,27 @@ class WechatsController < ActionController::Base
|
|
331
331
|
end
|
332
332
|
```
|
333
333
|
|
334
|
-
`wechat_oauth2`封装了OAuth2.0验证接口和
|
334
|
+
`wechat_oauth2 `封装了 OAuth2.0 验证接口和 Cookies 处理逻辑,用户仅需提供业务代码块即可。userid 指的是微信企业成员 userid,openid 是关注该公众号的用户 openid。
|
335
335
|
|
336
336
|
注意:
|
337
337
|
* 如果使用 `wechat_responder`, 请不要在 Controller 里定义 `show` 和 `create` 方法, 否则会报错。
|
338
|
-
* 如果遇到“redirect_uri参数错误”的错误信息,请登录服务号管理后台,查看“开发者中心/网页服务/网页授权获取用户基本信息”的授权回调页面域名已正确配置。
|
338
|
+
* 如果遇到“ redirect_uri 参数错误”的错误信息,请登录服务号管理后台,查看“开发者中心/网页服务/网页授权获取用户基本信息”的授权回调页面域名已正确配置。
|
339
339
|
|
340
340
|
## 关于接口权限
|
341
341
|
|
342
|
-
|
342
|
+
Wechat Gem 内部不会检查权限, 但因公众号类型不同和微信服务器端通讯时,可能会被拒绝详细权限控制可参考[官方文档](https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433401084)。
|
343
343
|
|
344
344
|
## 使用命令行
|
345
345
|
|
346
|
-
|
346
|
+
根据企业微信和微信公众平台配置不同 Wechat 提供了的命令行命令。
|
347
347
|
|
348
348
|
#### 微信公众平台命令行
|
349
349
|
|
350
350
|
```
|
351
351
|
$ wechat
|
352
352
|
Wechat Public Account commands:
|
353
|
-
wechat addvoicetorecofortext [VOICE_ID] # AI
|
354
|
-
wechat callbackip # 获取微信服务器
|
353
|
+
wechat addvoicetorecofortext [VOICE_ID] # AI 开放接口 - 提交语音
|
354
|
+
wechat callbackip # 获取微信服务器 iP 地址
|
355
355
|
wechat clear_quota # 接口调用次数清零
|
356
356
|
wechat custom_image [OPENID, IMAGE_PATH] # 发送图片客服消息
|
357
357
|
wechat custom_music [OPENID, THUMBNAIL_PATH, MUSIC_URL] # 发送音乐客服消息
|
@@ -364,8 +364,9 @@ Wechat Public Account commands:
|
|
364
364
|
wechat group_delete [GROUP_ID] # 删除分组
|
365
365
|
wechat group_update [GROUP_ID, NEW_GROUP_NAME] # 修改分组名
|
366
366
|
wechat groups # 查询所有分组
|
367
|
-
wechat material_get [MEDIA_ID, PATH]
|
367
|
+
wechat material_get [MEDIA_ID, PATH] # 永久媒体下载
|
368
368
|
wechat material_add [MEDIA_TYPE, PATH] # 永久媒体上传
|
369
|
+
wechat material_add_news [MPNEWS_YAML_PATH] # 永久图文素材上传
|
369
370
|
wechat material_count # 获取永久素材总数
|
370
371
|
wechat material_delete [MEDIA_ID] # 删除永久素材
|
371
372
|
wechat material_list [TYPE, OFFSET, COUNT] # 获取永久素材列表
|
@@ -385,7 +386,7 @@ Wechat Public Account commands:
|
|
385
386
|
wechat message_mass_preview [WX_NAME, MPNEWS_MEDIA_ID] # 预览图文消息素材
|
386
387
|
wechat qrcode_create_limit_scene [SCENE_ID_OR_STR] # 请求永久二维码
|
387
388
|
wechat qrcode_create_scene [SCENE_ID_OR_STR, EXPIRE_SECONDS] # 请求临时二维码
|
388
|
-
wechat qrcode_download [TICKET, QR_CODE_PIC_PATH] # 通过ticket下载二维码
|
389
|
+
wechat qrcode_download [TICKET, QR_CODE_PIC_PATH] # 通过 ticket 下载二维码
|
389
390
|
wechat queryrecoresultfortext [VOICE_ID] # AI开放接口-获取语音识别结果
|
390
391
|
wechat shorturl [LONG_URL] # 长链接转短链接
|
391
392
|
wechat tag [TAGID] # 获取标签下粉丝列表
|
@@ -417,10 +418,10 @@ Wechat Enterprise Account commands:
|
|
417
418
|
wechat batch_replaceparty [BATCH_PARTY_CSV_MEDIA_ID] # 全量覆盖部门
|
418
419
|
wechat batch_replaceuser [BATCH_USER_CSV_MEDIA_ID] # 全量覆盖成员
|
419
420
|
wechat batch_syncuser [SYNC_USER_CSV_MEDIA_ID] # 增量更新成员
|
420
|
-
wechat callbackip # 获取微信服务器
|
421
|
+
wechat callbackip # 获取微信服务器 iP 地址
|
421
422
|
wechat clear_quota # 接口调用次数清零
|
422
|
-
wechat convert_to_openid [USER_ID] # userid转换成openid
|
423
|
-
wechat convert_to_userid [OPENID] # openid转换成userid
|
423
|
+
wechat convert_to_openid [USER_ID] # userid 转换成 openid
|
424
|
+
wechat convert_to_userid [OPENID] # openid 转换成 userid
|
424
425
|
wechat custom_image [OPENID, IMAGE_PATH] # 发送图片客服消息
|
425
426
|
wechat custom_music [OPENID, THUMBNAIL_PATH, MUSIC_URL] # 发送音乐客服消息
|
426
427
|
wechat custom_news [OPENID, NEWS_YAML_PATH] # 发送图文客服消息
|
@@ -477,7 +478,7 @@ Wechat Enterprise Account commands:
|
|
477
478
|
### 使用场景
|
478
479
|
以下是几种典型场景的使用方法
|
479
480
|
|
480
|
-
|
481
|
+
##### 获取所有用户的 openid
|
481
482
|
|
482
483
|
```
|
483
484
|
$ wechat users
|
@@ -486,7 +487,7 @@ $ wechat users
|
|
486
487
|
|
487
488
|
```
|
488
489
|
|
489
|
-
|
490
|
+
##### 获取用户的信息
|
490
491
|
|
491
492
|
```
|
492
493
|
$ wechat user "oCfEht9***********"
|
@@ -506,7 +507,7 @@ $ wechat menu
|
|
506
507
|
##### 创建菜单
|
507
508
|
|
508
509
|
|
509
|
-
通过运行`rails g wechat:menu`可以生成一个定义菜单内容的yaml
|
510
|
+
通过运行`rails g wechat:menu`可以生成一个定义菜单内容的 yaml 文件菜单可以包含下列内容:
|
510
511
|
|
511
512
|
```
|
512
513
|
button:
|
@@ -550,17 +551,17 @@ button:
|
|
550
551
|
$ wechat menu_create menu.yaml
|
551
552
|
```
|
552
553
|
|
553
|
-
需确保设置,权限管理中有对此应用的管理权限,否则会报[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)错。
|
554
|
+
需确保设置,权限管理中有对此应用的管理权限,否则会报 [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) 错。
|
554
555
|
|
555
556
|
##### 发送客服图文消息
|
556
|
-
需定义一个图文消息内容的yaml文件,比如
|
557
|
+
需定义一个图文消息内容的 yaml 文件,比如
|
557
558
|
articles.yaml
|
558
559
|
|
559
560
|
```
|
560
561
|
articles:
|
561
562
|
-
|
562
563
|
title: "习近平在布鲁日欧洲学院演讲"
|
563
|
-
description: "新华网比利时布鲁日4月1日电 国家主席习近平1日在比利时布鲁日欧洲学院发表重要演讲"
|
564
|
+
description: "新华网比利时布鲁日 4 月 1 日电 国家主席习近平 1 日在比利时布鲁日欧洲学院发表重要演讲"
|
564
565
|
url: "http://news.sina.com.cn/c/2014-04-01/232629843387.shtml"
|
565
566
|
pic_url: "http://i3.sinaimg.cn/dy/c/2014-04-01/1396366518_bYays1.jpg"
|
566
567
|
```
|
@@ -573,7 +574,7 @@ $ wechat custom_news oCfEht9oM*********** articles.yml
|
|
573
574
|
```
|
574
575
|
|
575
576
|
##### 发送模板消息
|
576
|
-
需定义一个模板消息内容的yaml文件,比如
|
577
|
+
需定义一个模板消息内容的 yaml 文件,比如
|
577
578
|
template.yml
|
578
579
|
|
579
580
|
```
|
@@ -589,10 +590,10 @@ template:
|
|
589
590
|
value: "XX活动"
|
590
591
|
color: "#CCCCCC"
|
591
592
|
keynote2:
|
592
|
-
value: "2014年9月16日"
|
593
|
+
value: "2014 年 9 月 16 日"
|
593
594
|
color: "#CCCCCC"
|
594
595
|
keynote3:
|
595
|
-
value: "上海徐家汇xxx城"
|
596
|
+
value: "上海徐家汇 xxx 城"
|
596
597
|
color: "#CCCCCC"
|
597
598
|
remark:
|
598
599
|
value: "欢迎再次使用。"
|
@@ -613,7 +614,7 @@ template = YAML.load(File.read(template_yaml_path))
|
|
613
614
|
Wechat.api.template_message_send Wechat::Message.to(openid).template(template['template'])
|
614
615
|
```
|
615
616
|
|
616
|
-
若在Controller中使用wechat_api或者wechat_responder,可以使用wechat:
|
617
|
+
若在 Controller 中使用 wechat_api 或者 wechat_responder,可以使用 wechat:
|
617
618
|
|
618
619
|
```ruby
|
619
620
|
template = YAML.load(File.read(template_yaml_path))
|
@@ -622,7 +623,7 @@ wechat.template_message_send Wechat::Message.to(openid).template(template['templ
|
|
622
623
|
|
623
624
|
## wechat_api - Rails Controller Wechat API
|
624
625
|
|
625
|
-
|
626
|
+
虽然用户可以随时通过 `Wechat.api` 在任意代码中访问 Wechat 的 API 功能,但是更推荐的做法是仅在 Controller 中,通过引入 `wechat_api`,使用 `wechat` 调用 API 功能,不仅因为这样是支持多个微信公众平台账号的必然要求,而且也避免了在模型层内过多引入微信相关代码。
|
626
627
|
|
627
628
|
```ruby
|
628
629
|
class WechatReportsController < ApplicationController
|
@@ -635,11 +636,11 @@ class WechatReportsController < ApplicationController
|
|
635
636
|
end
|
636
637
|
```
|
637
638
|
|
638
|
-
## 在ActiveJob/Rake tasks中调用有wechat api
|
639
|
+
## 在 ActiveJob/Rake tasks 中调用有 wechat api
|
639
640
|
|
640
|
-
|
641
|
+
可以通过 `Wechat.api` 在任意地方使用 Wechat Api 的功能。
|
641
642
|
|
642
|
-
|
643
|
+
下面以通过 `rails console` 调用微信 Ai 开放接口的语音识别为例:
|
643
644
|
|
644
645
|
```bash
|
645
646
|
# Audio file with ID3 version 2.4.0, contains:MPEG ADTS, layer III, v2, 40 kbps, 16 kHz, Monaural
|
@@ -650,24 +651,24 @@ Wechat.api.queryrecoresultfortext 'test_voice_id'
|
|
650
651
|
|
651
652
|
## wechat_responder - Rails Responder Controller DSL
|
652
653
|
|
653
|
-
为了在Rails
|
654
|
+
为了在 Rails App 中响应用户的消息,开发者需要创建一个 wechat responder Controller 首先在 Router 中定义
|
654
655
|
|
655
656
|
```ruby
|
656
657
|
resource :wechat, only:[:show, :create]
|
657
658
|
```
|
658
659
|
|
659
|
-
然后创建Controller
|
660
|
+
然后创建 Controller Class 例如
|
660
661
|
|
661
662
|
```ruby
|
662
663
|
class WechatsController < ActionController::Base
|
663
664
|
wechat_responder
|
664
665
|
|
665
|
-
# 默认文字信息responder
|
666
|
+
# 默认文字信息 responder
|
666
667
|
on :text do |request, content|
|
667
668
|
request.reply.text "echo: #{content}" #Just echo
|
668
669
|
end
|
669
670
|
|
670
|
-
# 当请求的文字信息内容为'help'时, 使用这个responder处理
|
671
|
+
# 当请求的文字信息内容为 'help' 时, 使用这个 responder 处理
|
671
672
|
on :text, with: 'help' do |request|
|
672
673
|
request.reply.text 'help content' #回复帮助信息
|
673
674
|
end
|
@@ -686,29 +687,29 @@ class WechatsController < ActionController::Base
|
|
686
687
|
request.reply.text "User #{request[:FromUserName]} subscribe now"
|
687
688
|
end
|
688
689
|
|
689
|
-
# 公众平台收到未关注用户扫描qrscene_xxxxxx二维码时。注意此次扫描事件将不再引发上条的用户加关注事件
|
690
|
+
# 公众平台收到未关注用户扫描 qrscene_xxxxxx 二维码时。注意此次扫描事件将不再引发上条的用户加关注事件
|
690
691
|
on :scan, with: 'qrscene_xxxxxx' do |request, ticket|
|
691
692
|
request.reply.text "Unsubscribe user #{request[:FromUserName]} Ticket #{ticket}"
|
692
693
|
end
|
693
694
|
|
694
|
-
# 公众平台收到已关注用户扫描创建二维码的scene_id事件时
|
695
|
+
# 公众平台收到已关注用户扫描创建二维码的 scene_id 事件时
|
695
696
|
on :scan, with: 'scene_id' do |request, ticket|
|
696
697
|
request.reply.text "Subscribe user #{request[:FromUserName]} Ticket #{ticket}"
|
697
698
|
end
|
698
699
|
|
699
|
-
# 当没有任何on :scan事件处理已关注用户扫描的scene_id时
|
700
|
+
# 当没有任何 on :scan 事件处理已关注用户扫描的 scene_id 时
|
700
701
|
on :event, with: 'scan' do |request|
|
701
702
|
if request[:EventKey].present?
|
702
703
|
request.reply.text "event scan got EventKey #{request[:EventKey]} Ticket #{request[:Ticket]}"
|
703
704
|
end
|
704
705
|
end
|
705
706
|
|
706
|
-
# 企业微信收到EventKey 为二维码扫描结果事件时
|
707
|
+
# 企业微信收到 EventKey 为二维码扫描结果事件时
|
707
708
|
on :scan, with: 'BINDING_QR_CODE' do |request, scan_result, scan_type|
|
708
709
|
request.reply.text "User #{request[:FromUserName]} ScanResult #{scan_result} ScanType #{scan_type}"
|
709
710
|
end
|
710
711
|
|
711
|
-
# 企业微信收到EventKey 为CODE 39码扫描结果事件时
|
712
|
+
# 企业微信收到 EventKey 为 CODE 39 码扫描结果事件时
|
712
713
|
on :scan, with: 'BINDING_BARCODE' do |message, scan_result|
|
713
714
|
if scan_result.start_with? 'CODE_39,'
|
714
715
|
message.reply.text "User: #{message[:FromUserName]} scan barcode, result is #{scan_result.split(',')[1]}"
|
@@ -736,14 +737,14 @@ class WechatsController < ActionController::Base
|
|
736
737
|
# request.reply.voice(request[:MediaId])
|
737
738
|
|
738
739
|
voice_id = request[:MediaId]
|
739
|
-
# 开通语音识别后,用户每次发送语音给服务号时,微信会在推送的语音消息XML数据包中,增加一个Recognition字段
|
740
|
+
# 开通语音识别后,用户每次发送语音给服务号时,微信会在推送的语音消息XML数据包中,增加一个 Recognition 字段
|
740
741
|
recognition = request[:Recognition]
|
741
742
|
request.reply.text "#{voice_id} #{recognition}"
|
742
743
|
end
|
743
744
|
|
744
745
|
# 处理视频信息
|
745
746
|
on :video do |request|
|
746
|
-
nickname = wechat.user(request[:FromUserName])['nickname'] #调用 api 获得发送者的nickname
|
747
|
+
nickname = wechat.user(request[:FromUserName])['nickname'] #调用 api 获得发送者的 nickname
|
747
748
|
request.reply.video(request[:MediaId], title: '回声', description: "#{nickname}发来的视频请求") #直接视频返回给用户
|
748
749
|
end
|
749
750
|
|
@@ -793,12 +794,24 @@ class WechatsController < ActionController::Base
|
|
793
794
|
request.reply.success # request is XML result hash.
|
794
795
|
end
|
795
796
|
|
796
|
-
#
|
797
|
+
# 客户同意进行聊天内容存档事件回调
|
798
|
+
on :change_external_contact do |request|
|
799
|
+
# https://open.work.weixin.qq.com/api/doc/90000/90135/92005
|
800
|
+
request.reply.success # request is XML result hash.
|
801
|
+
end
|
802
|
+
|
803
|
+
# 会话事件回调
|
804
|
+
on :msgaudit_notify do |request|
|
805
|
+
# https://open.work.weixin.qq.com/api/doc/90000/90135/95039
|
806
|
+
request.reply.success # request is XML result hash.
|
807
|
+
end
|
808
|
+
|
809
|
+
# 当无任何 responder 处理用户信息时,使用这个 responder 处理
|
797
810
|
on :fallback, respond: 'fallback message'
|
798
811
|
end
|
799
812
|
```
|
800
813
|
|
801
|
-
在controller
|
814
|
+
在 controller 中使用 `wechat_responder` 引入 Responder DSL, 之后可以用
|
802
815
|
|
803
816
|
```
|
804
817
|
on <message_type> do |message|
|
@@ -808,18 +821,18 @@ end
|
|
808
821
|
|
809
822
|
来响应用户信息。
|
810
823
|
|
811
|
-
目前支持的message_type有如下几种
|
824
|
+
目前支持的 message_type 有如下几种
|
812
825
|
|
813
|
-
- :text
|
826
|
+
- :text 响应文字消息,可以用 `:with` 参数来匹配文本内容 `on(:text, with:'help'){|message, content| ...}`
|
814
827
|
- :image 响应图片消息
|
815
828
|
- :voice 响应语音消息
|
816
829
|
- :shortvideo 响应短视频消息
|
817
830
|
- :video 响应视频消息
|
818
831
|
- :label_location 响应地理位置消息
|
819
832
|
- :link 响应链接消息
|
820
|
-
- :event 响应事件消息,
|
821
|
-
- :click 虚拟响应事件消息, 微信传入:event,但gem内部会单独处理
|
822
|
-
- :view 虚拟响应事件消息, 微信传入:event,但gem内部会单独处理
|
833
|
+
- :event 响应事件消息, 可以用 `:with` 参数来匹配事件类型,同文字消息类似,支持正则表达式匹配
|
834
|
+
- :click 虚拟响应事件消息, 微信传入:event,但 gem 内部会单独处理
|
835
|
+
- :view 虚拟响应事件消息, 微信传入:event,但 gem 内部会单独处理
|
823
836
|
- :scan 虚拟响应事件消息
|
824
837
|
- :batch_job 虚拟响应事件消息
|
825
838
|
- :location 虚拟响应上报地理位置事件消息
|
@@ -836,13 +849,13 @@ class WechatsController < ActionController::Base
|
|
836
849
|
end
|
837
850
|
```
|
838
851
|
|
839
|
-
注意设置了[多客服消息转发](http://dkf.qq.com/)
|
852
|
+
注意设置了[多客服消息转发](http://dkf.qq.com/)后,不能再添加默认文字信息 `responder`,否则文字消息将得不到转发。
|
840
853
|
|
841
854
|
### 通知
|
842
855
|
|
843
856
|
现支持以下通知:
|
844
857
|
|
845
|
-
* `wechat.responder.after_create` data 包含 request<Wechat::Message> 和 response<Wechat::Message>
|
858
|
+
* `wechat.responder.after_create` data 包含 request <Wechat::Message> 和 response<Wechat::Message>
|
846
859
|
|
847
860
|
使用示例:
|
848
861
|
|
@@ -854,6 +867,6 @@ end
|
|
854
867
|
|
855
868
|
## 已知问题
|
856
869
|
|
857
|
-
* 企业微信接受菜单消息时,Wechat腾讯服务器无法解析部分域名,请使用
|
858
|
-
* 企业微信全量覆盖成员使用的
|
859
|
-
* 如果使用
|
870
|
+
* 企业微信接受菜单消息时,Wechat 腾讯服务器无法解析部分域名,请使用 iP 绑定回调 URL,用户的普通消息目前不受影响。
|
871
|
+
* 企业微信全量覆盖成员使用的 CSV 通讯录格式,直接将下载的模板导入[是不工作的](http://qydev.weixin.qq.com/qa/index.php?qa=13978),必须使用 Excel 打开,然后另存为 CSV 格式才会变成合法格式。
|
872
|
+
* 如果使用 Nginx + Unicron 部署方案,并且使用了 Https,必须设置 `trusted_domain_fullname` 为 Https,否则会导致 JS-SDK 签名失效。
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
WeChat [![Gem Version](https://badge.fury.io/rb/wechat.svg)](https://rubygems.org/gems/wechat) [![Build Status](https://
|
1
|
+
WeChat [![Gem Version](https://badge.fury.io/rb/wechat.svg)](https://rubygems.org/gems/wechat) [![Build Status](https://mixtint.semaphoreci.com/badges/wechat/branches/main.svg?style=shields)](https://mixtint.semaphoreci.com/projects/wechat) [![Maintainability](https://api.codeclimate.com/v1/badges/12885358487c13e91e00/maintainability)](https://codeclimate.com/github/Eric-Guo/wechat/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/12885358487c13e91e00/test_coverage)](https://codeclimate.com/github/Eric-Guo/wechat/test_coverage)
|
2
2
|
======
|
3
3
|
|
4
4
|
[![Join the chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Eric-Guo/wechat?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
@@ -395,8 +395,9 @@ Wechat Public Account commands:
|
|
395
395
|
wechat group_delete [GROUP_ID] # 删除分组
|
396
396
|
wechat group_update [GROUP_ID, NEW_GROUP_NAME] # 修改分组名
|
397
397
|
wechat groups # 查询所有分组
|
398
|
-
wechat material_get [MEDIA_ID, PATH]
|
398
|
+
wechat material_get [MEDIA_ID, PATH] # 永久媒体下载
|
399
399
|
wechat material_add [MEDIA_TYPE, PATH] # 永久媒体上传
|
400
|
+
wechat material_add_news [MPNEWS_YAML_PATH] # 永久图文素材上传
|
400
401
|
wechat material_count # 获取永久素材总数
|
401
402
|
wechat material_delete [MEDIA_ID] # 删除永久素材
|
402
403
|
wechat material_list [TYPE, OFFSET, COUNT] # 获取永久素材列表
|
@@ -827,6 +828,18 @@ class WechatsController < ActionController::Base
|
|
827
828
|
request.reply.success # request is XML result hash.
|
828
829
|
end
|
829
830
|
|
831
|
+
# The customer agrees to call back the chat content archive event
|
832
|
+
on :change_external_contact do |request|
|
833
|
+
# https://open.work.weixin.qq.com/api/doc/90000/90135/92005
|
834
|
+
request.reply.success # request is XML result hash.
|
835
|
+
end
|
836
|
+
|
837
|
+
# Session event callback
|
838
|
+
on :msgaudit_notify do |request|
|
839
|
+
# https://open.work.weixin.qq.com/api/doc/90000/90135/95039
|
840
|
+
request.reply.success # request is XML result hash.
|
841
|
+
end
|
842
|
+
|
830
843
|
# If no match above will fallback to below
|
831
844
|
on :fallback, respond: 'fallback message'
|
832
845
|
end
|
data/bin/wechat
CHANGED
@@ -6,8 +6,6 @@ $LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
|
6
6
|
require 'thor'
|
7
7
|
require 'wechat'
|
8
8
|
require 'json'
|
9
|
-
require 'active_support/core_ext'
|
10
|
-
require 'active_support/json'
|
11
9
|
require 'fileutils'
|
12
10
|
require 'yaml'
|
13
11
|
require 'cgi'
|
@@ -408,6 +406,12 @@ class App < Thor
|
|
408
406
|
puts wechat_api.material_add(type, path)
|
409
407
|
end
|
410
408
|
|
409
|
+
desc 'material_add_news [MPNEWS_YAML_PATH]', '永久图文素材上传'
|
410
|
+
def material_add_news(mpnews_yaml_path)
|
411
|
+
new = YAML.load(File.read(mpnews_yaml_path))
|
412
|
+
puts wechat_api.material_add_news(Wechat::Message.new(MsgType: 'mpnews').mpnews(new['articles']))
|
413
|
+
end
|
414
|
+
|
411
415
|
desc 'material_delete [MEDIA_ID]', '删除永久素材'
|
412
416
|
def material_delete(media_id)
|
413
417
|
puts wechat_api.material_delete(media_id)
|
data/lib/wechat/api_loader.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'active_support/core_ext/object/blank'
|
4
|
+
|
3
5
|
module Wechat
|
4
6
|
module ApiLoader
|
5
7
|
def self.with(options)
|
@@ -42,11 +44,11 @@ module Wechat
|
|
42
44
|
configs = config_from_file || config_from_environment
|
43
45
|
configs.merge!(config_from_db)
|
44
46
|
|
45
|
-
configs.
|
47
|
+
configs.transform_keys! { |key| key.to_sym rescue key }
|
46
48
|
configs.each do |key, cfg|
|
47
49
|
raise "wrong wechat configuration format for #{key}" unless cfg.is_a?(Hash)
|
48
50
|
|
49
|
-
cfg.
|
51
|
+
cfg.transform_keys! { |key| key.to_sym rescue key }
|
50
52
|
end
|
51
53
|
|
52
54
|
if defined?(::Rails)
|
@@ -60,8 +62,8 @@ module Wechat
|
|
60
62
|
configs.each do |_, cfg|
|
61
63
|
cfg[:timeout] ||= 20
|
62
64
|
cfg[:qcloud_token_lifespan] ||= 7200
|
63
|
-
cfg[:have_session_class]
|
64
|
-
cfg[:oauth2_cookie_duration] ||= 1
|
65
|
+
cfg[:have_session_class] ||= class_exists?('WechatSession')
|
66
|
+
cfg[:oauth2_cookie_duration] ||= 3600 # 1 hour
|
65
67
|
end
|
66
68
|
|
67
69
|
# create config object using raw config data
|
@@ -84,6 +86,7 @@ module Wechat
|
|
84
86
|
config_file = ENV['WECHAT_CONF_FILE'] || Rails.root.join('config', 'wechat.yml')
|
85
87
|
resolve_config_file(config_file, Rails.env.to_s)
|
86
88
|
else
|
89
|
+
require 'erb'
|
87
90
|
rails_config_file = ENV['WECHAT_CONF_FILE'] || File.join(Dir.getwd, 'config', 'wechat.yml')
|
88
91
|
application_config_file = File.join(Dir.getwd, 'config', 'application.yml')
|
89
92
|
home_config_file = File.join(Dir.home, '.wechat.yml')
|
data/lib/wechat/cipher.rb
CHANGED
@@ -148,8 +148,15 @@ module Wechat
|
|
148
148
|
post 'material/batchget_material', JSON.generate(type: type, offset: offset, count: count)
|
149
149
|
end
|
150
150
|
|
151
|
-
def material_add(type, file)
|
152
|
-
|
151
|
+
def material_add(type, file, opts = {})
|
152
|
+
params = { type: type }
|
153
|
+
params.merge!(description: opts.slice(:title, :introduction).to_json) if type == 'video'
|
154
|
+
|
155
|
+
post_file 'material/add_material', file, params: params
|
156
|
+
end
|
157
|
+
|
158
|
+
def material_add_news(mpnews_message)
|
159
|
+
post 'material/add_news', mpnews_message.to_json
|
153
160
|
end
|
154
161
|
|
155
162
|
def material_delete(media_id)
|
@@ -99,7 +99,7 @@ module Wechat
|
|
99
99
|
form_file = file.is_a?(HTTP::FormData::File) ? file : HTTP::FormData::File.new(file)
|
100
100
|
form_data = HTTP::FormData.create({ key: q_path,
|
101
101
|
Signature: signature,
|
102
|
-
|
102
|
+
'x-cos-security-token': x_cos_security_token,
|
103
103
|
'x-cos-meta-fileid': x_cos_meta_fileid,
|
104
104
|
file: form_file })
|
105
105
|
client.httprb.post(upload_url, form: form_data, ssl_context: client.ssl_context)
|
@@ -53,9 +53,9 @@ module Wechat
|
|
53
53
|
yield openid, { 'openid' => openid, 'unionid' => unionid, 'access_token' => we_token }
|
54
54
|
elsif params[:code].present? && params[:state] == oauth2_params[:state]
|
55
55
|
access_info = wechat(account).web_access_token(params[:code])
|
56
|
-
cookies.signed_or_encrypted[:we_openid] = { value: access_info['openid'], expires: self.class.oauth2_cookie_duration
|
57
|
-
cookies.signed_or_encrypted[:we_unionid] = { value: access_info['unionid'], expires: self.class.oauth2_cookie_duration
|
58
|
-
cookies.signed_or_encrypted[:we_access_token] = { value: access_info['access_token'], expires: self.class.oauth2_cookie_duration
|
56
|
+
cookies.signed_or_encrypted[:we_openid] = { value: access_info['openid'], expires: (Time.now + self.class.oauth2_cookie_duration) }
|
57
|
+
cookies.signed_or_encrypted[:we_unionid] = { value: access_info['unionid'], expires: (Time.now + self.class.oauth2_cookie_duration) }
|
58
|
+
cookies.signed_or_encrypted[:we_access_token] = { value: access_info['access_token'], expires: (Time.now + self.class.oauth2_cookie_duration) }
|
59
59
|
yield access_info['openid'], access_info
|
60
60
|
else
|
61
61
|
redirect_to generate_oauth2_url(oauth2_params), allow_other_host: true
|
@@ -69,8 +69,8 @@ module Wechat
|
|
69
69
|
yield userid, { 'UserId' => userid, 'DeviceId' => deviceid }
|
70
70
|
elsif params[:code].present? && params[:state] == oauth2_params[:state]
|
71
71
|
userinfo = wechat(account).getuserinfo(params[:code])
|
72
|
-
cookies.signed_or_encrypted[:we_userid] = { value: userinfo['UserId'], expires: self.class.oauth2_cookie_duration
|
73
|
-
cookies.signed_or_encrypted[:we_deviceid] = { value: userinfo['DeviceId'], expires: self.class.oauth2_cookie_duration
|
72
|
+
cookies.signed_or_encrypted[:we_userid] = { value: userinfo['UserId'], expires: (Time.now + self.class.oauth2_cookie_duration) }
|
73
|
+
cookies.signed_or_encrypted[:we_deviceid] = { value: userinfo['DeviceId'], expires: (Time.now + self.class.oauth2_cookie_duration) }
|
74
74
|
yield userinfo['UserId'], userinfo
|
75
75
|
else
|
76
76
|
redirect_to generate_oauth2_url(oauth2_params), allow_other_host: true
|
data/lib/wechat/corp_api.rb
CHANGED
@@ -188,5 +188,21 @@ module Wechat
|
|
188
188
|
def custom_message_send(message)
|
189
189
|
post 'message/send', message.is_a?(Wechat::Message) ? message.agent_id(agentid).to_json : JSON.generate(message.merge(agent_id: agentid)), content_type: :json
|
190
190
|
end
|
191
|
+
|
192
|
+
def msgaudit_get_permit_user_list(type = nil)
|
193
|
+
post 'msgaudit/get_permit_user_list', JSON.generate(type: type)
|
194
|
+
end
|
195
|
+
|
196
|
+
def msgaudit_check_single_agree(info)
|
197
|
+
post 'msgaudit/get_permit_user_list', JSON.generate(info: info)
|
198
|
+
end
|
199
|
+
|
200
|
+
def msgaudit_check_room_agree(roomid)
|
201
|
+
post 'msgaudit/check_room_agree', JSON.generate(roomid: roomid)
|
202
|
+
end
|
203
|
+
|
204
|
+
def msgaudit_groupchat(roomid)
|
205
|
+
post 'msgaudit/groupchat/get', JSON.generate(roomid: roomid)
|
206
|
+
end
|
191
207
|
end
|
192
208
|
end
|
data/lib/wechat/helpers.rb
CHANGED
@@ -34,7 +34,8 @@ module Wechat
|
|
34
34
|
timestamp: "#{js_hash[:timestamp]}",
|
35
35
|
nonceStr: "#{js_hash[:noncestr]}",
|
36
36
|
signature: "#{js_hash[:signature]}",
|
37
|
-
jsApiList: ['#{config_options[:api]
|
37
|
+
jsApiList: ['#{config_options[:api]&.join("','")}'],
|
38
|
+
openTagList: ['#{config_options[:open_tags]&.join("','")}']
|
38
39
|
});
|
39
40
|
WECHAT_CONFIG_JS
|
40
41
|
javascript_tag config_js, type: 'application/javascript'
|
data/lib/wechat/qcloud/token.rb
CHANGED
@@ -22,7 +22,7 @@ module Wechat
|
|
22
22
|
def token(tries = 2)
|
23
23
|
# Possible two worker running, one worker refresh ticket, other unaware, so must read every time
|
24
24
|
read_qcloud_token_from_store
|
25
|
-
refresh if remain_life_seconds < @random_generator.rand(30..3 * 60)
|
25
|
+
refresh if remain_life_seconds < @random_generator.rand(30..(3 * 60))
|
26
26
|
qcloud_token
|
27
27
|
rescue AccessTokenExpiredError
|
28
28
|
access_token.refresh
|
data/lib/wechat/responder.rb
CHANGED
@@ -50,6 +50,10 @@ module Wechat
|
|
50
50
|
user_defined_location_responders << config
|
51
51
|
when :label_location
|
52
52
|
user_defined_label_location_responders << config
|
53
|
+
when :change_external_contact
|
54
|
+
user_defined_change_external_contact_responders << config
|
55
|
+
when :msgaudit_notify
|
56
|
+
user_defined_msgaudit_notify_responders << config
|
53
57
|
else
|
54
58
|
user_defined_responders(message_type) << config
|
55
59
|
end
|
@@ -84,6 +88,14 @@ module Wechat
|
|
84
88
|
@user_defined_label_location_responders ||= []
|
85
89
|
end
|
86
90
|
|
91
|
+
def user_defined_change_external_contact_responders
|
92
|
+
@user_defined_change_external_contact_responders ||= []
|
93
|
+
end
|
94
|
+
|
95
|
+
def user_defined_msgaudit_notify_responders
|
96
|
+
@user_defined_msgaudit_notify_responders ||= []
|
97
|
+
end
|
98
|
+
|
87
99
|
def user_defined_responders(type)
|
88
100
|
@responders ||= {}
|
89
101
|
@responders[type] ||= []
|
@@ -109,6 +121,10 @@ module Wechat
|
|
109
121
|
yield(* user_defined_batch_job_responders(message[:BatchJob][:JobType]), message[:BatchJob])
|
110
122
|
elsif message[:Event] == 'location'
|
111
123
|
yield(* user_defined_location_responders, message)
|
124
|
+
elsif message[:Event] == 'change_external_contact'
|
125
|
+
yield(* user_defined_change_external_contact_responders, message)
|
126
|
+
elsif message[:Event] == 'msgaudit_notify'
|
127
|
+
yield(* user_defined_msgaudit_notify_responders, message)
|
112
128
|
else
|
113
129
|
yield(* match_responders(responders, message[:Event]))
|
114
130
|
end
|
@@ -171,7 +187,7 @@ module Wechat
|
|
171
187
|
end
|
172
188
|
|
173
189
|
def create
|
174
|
-
request_msg = Wechat::Message.from_hash(
|
190
|
+
request_msg = Wechat::Message.from_hash(post_body)
|
175
191
|
response_msg = run_responder(request_msg)
|
176
192
|
|
177
193
|
if response_msg.respond_to? :to_xml
|
@@ -213,6 +229,24 @@ module Wechat
|
|
213
229
|
msg_encrypt)
|
214
230
|
end
|
215
231
|
|
232
|
+
def post_body
|
233
|
+
if request.media_type == 'application/json'
|
234
|
+
data_hash = params
|
235
|
+
|
236
|
+
if @we_encrypt_mode && data['Encrypt'].present?
|
237
|
+
content, @we_app_id = unpack(decrypt(Base64.decode64(data['Encrypt']), @we_encoding_aes_key))
|
238
|
+
data_hash = content
|
239
|
+
end
|
240
|
+
|
241
|
+
data_hash = data_hash.to_unsafe_hash if data_hash.instance_of?(ActionController::Parameters)
|
242
|
+
HashWithIndifferentAccess.new(data_hash).tap do |msg|
|
243
|
+
msg[:Event]&.downcase!
|
244
|
+
end
|
245
|
+
else
|
246
|
+
post_xml
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
216
250
|
def post_xml
|
217
251
|
data = request_content
|
218
252
|
|
@@ -18,7 +18,7 @@ module Wechat
|
|
18
18
|
def ticket(tries = 2)
|
19
19
|
# Possible two worker running, one worker refresh ticket, other unaware, so must read every time
|
20
20
|
read_ticket_from_store
|
21
|
-
refresh if remain_life_seconds < @random_generator.rand(30..3 * 60)
|
21
|
+
refresh if remain_life_seconds < @random_generator.rand(30..(3 * 60))
|
22
22
|
access_ticket
|
23
23
|
rescue AccessTokenExpiredError
|
24
24
|
access_token.refresh
|
@@ -16,7 +16,7 @@ module Wechat
|
|
16
16
|
def token
|
17
17
|
# Possible two worker running, one worker refresh token, other unaware, so must read every time
|
18
18
|
read_token_from_store
|
19
|
-
refresh if remain_life_seconds < @random_generator.rand(30..3 * 60)
|
19
|
+
refresh if remain_life_seconds < @random_generator.rand(30..(3 * 60))
|
20
20
|
access_token
|
21
21
|
end
|
22
22
|
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wechat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.15.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Skinnyworm
|
@@ -11,31 +11,31 @@ bindir: bin
|
|
11
11
|
cert_chain:
|
12
12
|
- |
|
13
13
|
-----BEGIN CERTIFICATE-----
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
14
|
+
MIIEODCCAqCgAwIBAgIBATANBgkqhkiG9w0BAQsFADAjMSEwHwYDVQQDDBhlcmlj
|
15
|
+
L0RDPWNsb3VkLW1lcy9EQz1jb20wHhcNMjEwOTE1MDIxMDMzWhcNMjIwOTE1MDIx
|
16
|
+
MDMzWjAjMSEwHwYDVQQDDBhlcmljL0RDPWNsb3VkLW1lcy9EQz1jb20wggGiMA0G
|
17
|
+
CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDfweHJdAqu5+BQ6t+F923czZvUynqe
|
18
|
+
DyacqWVUbDg53oVwYxldDNqUea5hMlSs1UWj2sJ5ZHiU02ly0QVyDCw/5pFP2CKJ
|
19
|
+
ukQbw35ZoCF0t2i0/GPYAtBMxb1qUynkxDAtCefQG33lBt2u9scgE9xOIiPrxtJg
|
20
|
+
shl3XFYmOx1ol66JR540l7NBS1OHR7UV6WiJrRW1cZTcR+py7jIbo6ud/rhZVHCO
|
21
|
+
B2RTZtpH1I7ilknT1/NXMX6aw+XoNda4w+4lsrHfqKssfwJcsGMq1IbbG8illxRT
|
22
|
+
wsYLiUXNJaAacT9HVO4B0jIFPP5Me7FIkiqZZKr7uyHNQE0S/5OOUkIM0v5kkUOF
|
23
|
+
IE+A6WwVCyi05+JNcbLuxeSZnNeqQcXiqxl1RcodIJxw0VTcSE1CO2eYCsvfzLJ2
|
24
|
+
fn9fLaSA/mrosg72UV126GeXu/y/N6gf3F6ZHM2qAMZhJ9ZUR03bUVIqApsJN28g
|
25
|
+
JOkm/lV0PutN0Y2UrD0gSKxgrN24ZSsWlBECAwEAAaN3MHUwCQYDVR0TBAIwADAL
|
26
|
+
BgNVHQ8EBAMCBLAwHQYDVR0OBBYEFHHJIHijHoPxeoZDyGAvLbBalVvoMB0GA1Ud
|
27
|
+
EQQWMBSBEmVyaWNAY2xvdWQtbWVzLmNvbTAdBgNVHRIEFjAUgRJlcmljQGNsb3Vk
|
28
|
+
LW1lcy5jb20wDQYJKoZIhvcNAQELBQADggGBACYpors2s6sGEq0ErgijhSkAQRoj
|
29
|
+
W1EO3ORYYyCRFPFX1KLtUpFuXi/1rZoMoSug2Lpr+GWqt7eZIwNoryjYMbuE/sOn
|
30
|
+
sANkOvLx8x4RMlmTFe+WkPiV9NasFqNn7EBSpjqQRWRlCuh6rMiYzzxNbbNvbRT4
|
31
|
+
WMhBf7eWRpr1TBXDr51E8RtA+LG6wZuJFnKWBisgKOmpUw79f7EvIQAGS3MEWk/g
|
32
|
+
fSvIf14zM5Dw0whGa/n60jgSc5yiW3/75GXt8608BK+bs5dViJ/3ofuIhqpOvvdp
|
33
|
+
4Oiv2zIXsfUIGAIwHN5mLwAwHty1d0s8Kt0jtJAXDUODgTuXaBj/aOqTZUUgp8Kv
|
34
|
+
6SoPdaa0LFPbkI2eiUN1xUPelsgKz0kyRBJtkMnSKFxcCxw7VHGRGFsw0ORZodQ3
|
35
|
+
ZM9IDtdMg8E/4ujwilV8HKmgU77vVN6vSMvxx8zQFSz9a6GbdpB4egPZ++peSk/Q
|
36
|
+
uaIJtOX6M4VC6u7eZfotARKyUy6EcoN2zNqEAQ==
|
37
37
|
-----END CERTIFICATE-----
|
38
|
-
date: 2021-
|
38
|
+
date: 2021-12-21 00:00:00.000000000 Z
|
39
39
|
dependencies:
|
40
40
|
- !ruby/object:Gem::Dependency
|
41
41
|
name: activesupport
|
@@ -44,9 +44,6 @@ dependencies:
|
|
44
44
|
- - ">="
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: '6.0'
|
47
|
-
- - "<"
|
48
|
-
- !ruby/object:Gem::Version
|
49
|
-
version: '7'
|
50
47
|
type: :runtime
|
51
48
|
prerelease: false
|
52
49
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -54,9 +51,6 @@ dependencies:
|
|
54
51
|
- - ">="
|
55
52
|
- !ruby/object:Gem::Version
|
56
53
|
version: '6.0'
|
57
|
-
- - "<"
|
58
|
-
- !ruby/object:Gem::Version
|
59
|
-
version: '7'
|
60
54
|
- !ruby/object:Gem::Dependency
|
61
55
|
name: http
|
62
56
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,7 +60,7 @@ dependencies:
|
|
66
60
|
version: 1.0.4
|
67
61
|
- - "<"
|
68
62
|
- !ruby/object:Gem::Version
|
69
|
-
version: '
|
63
|
+
version: '6'
|
70
64
|
type: :runtime
|
71
65
|
prerelease: false
|
72
66
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -76,7 +70,7 @@ dependencies:
|
|
76
70
|
version: 1.0.4
|
77
71
|
- - "<"
|
78
72
|
- !ruby/object:Gem::Version
|
79
|
-
version: '
|
73
|
+
version: '6'
|
80
74
|
- !ruby/object:Gem::Dependency
|
81
75
|
name: nokogiri
|
82
76
|
requirement: !ruby/object:Gem::Requirement
|
@@ -153,28 +147,28 @@ dependencies:
|
|
153
147
|
requirements:
|
154
148
|
- - ">="
|
155
149
|
- !ruby/object:Gem::Version
|
156
|
-
version:
|
150
|
+
version: 7.0.0
|
157
151
|
type: :development
|
158
152
|
prerelease: false
|
159
153
|
version_requirements: !ruby/object:Gem::Requirement
|
160
154
|
requirements:
|
161
155
|
- - ">="
|
162
156
|
- !ruby/object:Gem::Version
|
163
|
-
version:
|
157
|
+
version: 7.0.0
|
164
158
|
- !ruby/object:Gem::Dependency
|
165
159
|
name: rspec-rails
|
166
160
|
requirement: !ruby/object:Gem::Requirement
|
167
161
|
requirements:
|
168
162
|
- - "~>"
|
169
163
|
- !ruby/object:Gem::Version
|
170
|
-
version: '
|
164
|
+
version: '5.0'
|
171
165
|
type: :development
|
172
166
|
prerelease: false
|
173
167
|
version_requirements: !ruby/object:Gem::Requirement
|
174
168
|
requirements:
|
175
169
|
- - "~>"
|
176
170
|
- !ruby/object:Gem::Version
|
177
|
-
version: '
|
171
|
+
version: '5.0'
|
178
172
|
- !ruby/object:Gem::Dependency
|
179
173
|
name: sqlite3
|
180
174
|
requirement: !ruby/object:Gem::Requirement
|
@@ -242,7 +236,12 @@ files:
|
|
242
236
|
homepage: https://github.com/Eric-Guo/wechat
|
243
237
|
licenses:
|
244
238
|
- MIT
|
245
|
-
metadata:
|
239
|
+
metadata:
|
240
|
+
bug_tracker_uri: https://github.com/Eric-Guo/wechat/issues
|
241
|
+
changelog_uri: https://github.com/Eric-Guo/wechat/releases
|
242
|
+
documentation_uri: https://github.com/Eric-Guo/wechat/tree/v0.15.0#readme
|
243
|
+
source_code_uri: https://github.com/Eric-Guo/wechat/tree/v0.15.0
|
244
|
+
rubygems_mfa_required: 'true'
|
246
245
|
post_install_message:
|
247
246
|
rdoc_options: []
|
248
247
|
require_paths:
|
@@ -256,9 +255,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
256
255
|
requirements:
|
257
256
|
- - ">="
|
258
257
|
- !ruby/object:Gem::Version
|
259
|
-
version:
|
258
|
+
version: 1.8.11
|
260
259
|
requirements: []
|
261
|
-
rubygems_version: 3.2.
|
260
|
+
rubygems_version: 3.2.33
|
262
261
|
signing_key:
|
263
262
|
specification_version: 4
|
264
263
|
summary: DSL for wechat message handling and API
|
metadata.gz.sig
CHANGED
Binary file
|