wechat 0.17.7 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 01d659a1b0109803a9d9a7efcfa27c60d0a741f135fb1df1ea7eda8bfc1840f1
4
- data.tar.gz: 79d712076d72b55661649ddcd3de79e39823b53dd366e82552f8f080b162910a
3
+ metadata.gz: 8862a096490e208b18609027a14e80d12b386dcd86094065e4412856012ec3db
4
+ data.tar.gz: a71e7b18973fab14ff351b04e840c534ea6229ff59c98d0cc2deee93587237ed
5
5
  SHA512:
6
- metadata.gz: 959af434e46ae8e97d22e5c9263a92add7e9b6fc8b19acf72b7a6522ae4c747f140b9c3535fb1bc979522a9b8c0953f22ac08650e6fe8b5b65910f70f95447be
7
- data.tar.gz: 1e3a8f527f15ace70e5fd49fba1bd5b298c3f5150f4b904a965363c02d631bd54b1325fb0afadaa0cc2a58796fdcfe6dc7e5c639e81b82669b806354d636bbac
6
+ metadata.gz: 0dc2fab306d761d1a887478c2eb3d355064fa0c3a18bc5a91df6fb426eed5fe4d73073738d0011ca15e0b1a8042035fcb53b0d57937b9749c31d8efd94cb704e
7
+ data.tar.gz: 83eb3e8c604000a515888c0bca6f7d059de49f07fd691c67dd6a0a5bc1262a8fd1758a672f6244b54f3771bd6f591593b0b03b3d660c3a1c030397dd8ee87a0e
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## v1.0.1 (released at 2025-07-09)
4
+
5
+ * Fix after introduce httpx, status method not available for HTTPX::ErrorResponse bug
6
+ * Some Google Jules Refactor
7
+
8
+ ## v1.0.0 (released at 2025-05-17)
9
+
10
+ * WECHAT_PROXY_URL should be writen with port, like `export WECHAT_PROXY_URL=http://127.0.0.1:6152`, since WECHAT_PROXY_PORT removed.
11
+ * Replace http with httpx, [reason](https://honeyryderchuck.gitlab.io/2023/10/15/state-of-ruby-http-clients-use-httpx.html)
12
+ * Feat: add account_type to WechatConfig. by @leepood #324
13
+
3
14
  ## v0.17.7 (released at 2025-03-02)
4
15
 
5
16
  * Adding ostruct into its gemspec. Fix #323
data/README-CN.md CHANGED
@@ -227,6 +227,7 @@ test:
227
227
  environment | 字串 | 必填。配置对应的运行环境,一般有:`production`、`development`、`test`。比如 `production` 配置仅在生产环境有效。默认为 `development`。
228
228
  account | 字串 | 必填。自定义的微信账户名称。同一 `environment` 下,账户名称不允许重复。
229
229
  enabled | 布尔 | 必填。配置是否生效。默认 `true`。
230
+ account_type | 字串 | 非必填。当前只支持`mp`,表示小程序。
230
231
  appid | 字串 | 公众号 id ,此字段和 `corpid` 两者必填其一。
231
232
  secret | 字串 | 公众号相关配置。当公众号 `appid` 存在时必填。
232
233
  corpid | 字串 | 企业号 id。此字段和 `appid` 两者必填其一。
@@ -896,6 +897,5 @@ end
896
897
 
897
898
  ## 已知问题
898
899
 
899
- * 企业微信接受菜单消息时,Wechat 腾讯服务器无法解析部分域名,请使用 iP 绑定回调 URL,用户的普通消息目前不受影响。
900
900
  * 企业微信全量覆盖成员使用的 CSV 通讯录格式,直接将下载的模板导入[是不工作的](http://qydev.weixin.qq.com/qa/index.php?qa=13978),必须使用 Excel 打开,然后另存为 CSV 格式才会变成合法格式。
901
- * 如果使用 Nginx + Unicron 部署方案,并且使用了 Https,必须设置 `trusted_domain_fullname` 为 Https,否则会导致 JS-SDK 签名失效。
901
+ * 在开发模式上使用外部反响代理 https 回调,可能需要设置 `trusted_domain_fullname` 为 https,否则会导致 JS-SDK 签名失效。
data/README.md CHANGED
@@ -254,6 +254,7 @@ Attribute | Type | Annotation
254
254
  ---- | ---- | ----
255
255
  environment | string | Required. Environment of account configuration. Typical values are: `production`, `development` and `test`. For example, a `production` config will only be available in `production`. Default to `development`.
256
256
  account | string | Required. Custom wechat account name. Account names must be unique within each environment.
257
+ account_type | string | account type, only support `mp` which is short for `mini program` currently
257
258
  enabled | boolean | Required. Whether this configuration is activated. Default to `true`.
258
259
  appid | string | Public account id. Either this attribute or `corpid` must be specified.
259
260
  secret | string | Public account configuration. Required when `appid` exists.
@@ -928,6 +929,5 @@ end
928
929
 
929
930
  ## Known Issues
930
931
 
931
- * Sometimes, enterprise account can not receive the menu message due to Tencent server unable to resolve DNS, so using IP as a callback URL is more stable, but it never happens for user sent text messages.
932
932
  * Enterprise batch "replace users" uses a CSV format file, but if you are using the downloaded 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 supports Excel "Save as CSV" file format.
933
- * If you using unicorn behind nginx and https, you need to set `trusted_domain_fullname` and point it to https, otherwise it will be http and will lead to invalid signature in the JS-SDK.
933
+ * Using an external feedback proxy HTTPS callback in development mode may require setting `trusted_domain_fullname` to HTTPS, otherwise the JS-SDK signature will be invalid.
@@ -41,7 +41,6 @@ module ActionController
41
41
  self.skip_verify_ssl = opts.key?(:skip_verify_ssl) ? opts[:skip_verify_ssl] : cfg.skip_verify_ssl
42
42
 
43
43
  proxy_url = opts.key?(:proxy_url) ? opts[:proxy_url] : cfg.proxy_url
44
- proxy_port = opts.key?(:proxy_port) ? opts[:proxy_port] : cfg.proxy_port
45
44
  proxy_username = opts.key?(:proxy_username) ? opts[:proxy_username] : cfg.proxy_username
46
45
  proxy_password = opts.key?(:proxy_password) ? opts[:proxy_password] : cfg.proxy_password
47
46
 
@@ -56,18 +55,20 @@ module ActionController
56
55
  api_type = opts[:type] || cfg.type
57
56
  secret = corpid.present? ? opts[:corpsecret] || cfg.corpsecret : opts[:secret] || cfg.secret
58
57
 
59
- network_setting = Wechat::NetworkSetting.new(timeout, skip_verify_ssl, proxy_url, proxy_port, proxy_username, proxy_password)
58
+ network_setting = Wechat::NetworkSetting.new(timeout, skip_verify_ssl, proxy_url, proxy_username, proxy_password)
59
+ # access_token is token_file, jsapi_ticket is jsapi_ticket_file
60
+ api_config = Wechat::ApiConfig.new(corpid.presence || appid, secret, access_token, jsapi_ticket, network_setting)
60
61
  qcloud_setting = Wechat::Qcloud::Setting.new(qcloud_env, qcloud_token, qcloud_token_lifespan)
61
- get_wechat_api(api_type, corpid, appid, secret, access_token, agentid, network_setting, jsapi_ticket, qcloud_setting)
62
+ get_wechat_api(api_type, api_config, agentid, qcloud_setting)
62
63
  end
63
64
 
64
- def get_wechat_api(api_type, corpid, appid, secret, access_token, agentid, network_setting, jsapi_ticket, qcloud_setting)
65
+ def get_wechat_api(api_type, api_config, agentid, qcloud_setting)
65
66
  if api_type && api_type.to_sym == :mp
66
- Wechat::MpApi.new(appid, secret, access_token, network_setting, jsapi_ticket, qcloud_setting)
67
- elsif corpid.present?
68
- Wechat::CorpApi.new(corpid, secret, access_token, agentid, network_setting, jsapi_ticket)
67
+ Wechat::MpApi.new(api_config, qcloud_setting)
68
+ elsif api_config.appid.present? && agentid.present? # Assuming corpid is now in api_config.appid for CorpApi
69
+ Wechat::CorpApi.new(api_config, agentid)
69
70
  else
70
- Wechat::Api.new(appid, secret, access_token, network_setting, jsapi_ticket)
71
+ Wechat::Api.new(api_config)
71
72
  end
72
73
  end
73
74
  end
@@ -9,10 +9,11 @@ class WechatConfig < ActiveRecord::Base
9
9
  validates :access_token, presence: true
10
10
  validates :jsapi_ticket, presence: true
11
11
  validates :encoding_aes_key, presence: { if: :encrypt_mode? }
12
+ validates :account_type, inclusion: { in: %w[mp] }, if: -> { account_type.present? }
12
13
 
13
14
  validate :app_config_is_valid
14
15
 
15
- ATTRIBUTES_TO_REMOVE = %w[environment account created_at updated_at enabled].freeze
16
+ ATTRIBUTES_TO_REMOVE = %w[environment account created_at updated_at enabled account_type].freeze
16
17
 
17
18
  def self.get_all_configs(environment)
18
19
  WechatConfig.where(environment: environment, enabled: true).each_with_object({}) do |config, hash|
@@ -21,7 +22,9 @@ class WechatConfig < ActiveRecord::Base
21
22
  end
22
23
 
23
24
  def build_config_hash
24
- as_json(except: ATTRIBUTES_TO_REMOVE)
25
+ config_hash = as_json(except: ATTRIBUTES_TO_REMOVE)
26
+ config_hash[:type] = account_type if account_type.present?
27
+ config_hash
25
28
  end
26
29
 
27
30
  private
@@ -5,6 +5,8 @@ class CreateWechatConfigs < ActiveRecord::Migration<%= migration_version %>
5
5
  t.string :environment, null: false, default: 'development'
6
6
  # account name
7
7
  t.string :account, null: false
8
+ # account type, "mp" is short for mini program
9
+ t.string :account_type
8
10
  # whether this config is activated
9
11
  t.boolean :enabled, default: true
10
12
 
data/lib/wechat/api.rb CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  module Wechat
4
4
  class Api < ApiBase
5
- def initialize(appid, secret, token_file, network_setting, jsapi_ticket_file, record = nil)
5
+ def initialize(api_config, record = nil)
6
6
  super()
7
- @client = HttpClient.new(Wechat::Api::API_BASE, network_setting)
8
- @access_token = Token::PublicAccessToken.new(@client, appid, secret, token_file, record)
9
- @jsapi_ticket = Ticket::PublicJsapiTicket.new(@client, @access_token, jsapi_ticket_file)
7
+ @client = HttpClient.new(Wechat::Api::API_BASE, api_config.network_setting)
8
+ @access_token = Token::PublicAccessToken.new(@client, api_config.appid, api_config.secret, api_config.token_file, record)
9
+ @jsapi_ticket = Ticket::PublicJsapiTicket.new(@client, @access_token, api_config.jsapi_ticket_file)
10
10
  @qcloud = nil
11
11
  end
12
12
 
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wechat
4
+ class ApiConfig
5
+ attr_reader :appid, :secret, :token_file, :jsapi_ticket_file, :network_setting
6
+
7
+ def initialize(appid, secret, token_file, jsapi_ticket_file, network_setting)
8
+ @appid = appid
9
+ @secret = secret
10
+ @token_file = token_file
11
+ @jsapi_ticket_file = jsapi_ticket_file
12
+ @network_setting = network_setting
13
+ end
14
+ end
15
+ end
@@ -12,21 +12,26 @@ module Wechat
12
12
  js_token_file = options[:js_token_file] || c.jsapi_ticket.presence || '/var/tmp/wechat_jsapi_ticket'
13
13
  type = options[:type] || c.type
14
14
 
15
- network_setting = Wechat::NetworkSetting.new(c.timeout, c.skip_verify_ssl, c.proxy_url, c.proxy_port, c.proxy_username, c.proxy_password)
16
- if c.appid && c.secret && token_file.present?
17
- if type == 'mp'
18
- qcloud_env = options[:qcloud_env] || c.qcloud_env
19
- qcloud_token_file = options[:qcloud_token_file] || c.qcloud_token_file.presence || '/var/tmp/qcloud_access_token'
20
- qcloud_token_lifespan = options[:qcloud_token_lifespan] || c.qcloud_token_lifespan
21
- qcloud_setting = Wechat::Qcloud::Setting.new(qcloud_env, qcloud_token_file, qcloud_token_lifespan)
22
- Wechat::MpApi.new(c.appid, c.secret, token_file, network_setting, js_token_file, qcloud_setting)
23
- else
24
- Wechat::Api.new(c.appid, c.secret, token_file, network_setting, js_token_file)
25
- end
26
- elsif c.corpid && c.corpsecret && token_file.present?
27
- Wechat::CorpApi.new(c.corpid, c.corpsecret, token_file, c.agentid, network_setting, js_token_file)
28
- else
29
- raise 'Need create ~/.wechat.yml with wechat appid and secret or running at rails root folder so wechat can read config/wechat.yml'
15
+ network_setting = Wechat::NetworkSetting.new(c.timeout, c.skip_verify_ssl, c.proxy_url, c.proxy_username, c.proxy_password)
16
+
17
+ current_appid = c.corpid.presence || c.appid
18
+ current_secret = c.corpsecret.presence || c.secret
19
+
20
+ unless current_appid && current_secret && token_file.present?
21
+ raise 'Need create ~/.wechat.yml with wechat appid and secret or corpid and corpsecret, or running at rails root folder so wechat can read config/wechat.yml'
22
+ end
23
+
24
+ api_config = Wechat::ApiConfig.new(current_appid, current_secret, token_file, js_token_file, network_setting)
25
+ if type == 'mp'
26
+ qcloud_env = options[:qcloud_env] || c.qcloud_env
27
+ qcloud_token_file = options[:qcloud_token_file] || c.qcloud_token_file.presence || '/var/tmp/qcloud_access_token'
28
+ qcloud_token_lifespan = options[:qcloud_token_lifespan] || c.qcloud_token_lifespan
29
+ qcloud_setting = Wechat::Qcloud::Setting.new(qcloud_env, qcloud_token_file, qcloud_token_lifespan)
30
+ Wechat::MpApi.new(api_config, qcloud_setting)
31
+ elsif c.corpid.present? # CorpApi needs agentid
32
+ Wechat::CorpApi.new(api_config, c.agentid)
33
+ else # Regular Public Api
34
+ Wechat::Api.new(api_config)
30
35
  end
31
36
  end
32
37
 
@@ -166,7 +171,6 @@ module Wechat
166
171
  timeout: ENV.fetch('WECHAT_TIMEOUT', nil),
167
172
  skip_verify_ssl: ENV.fetch('WECHAT_SKIP_VERIFY_SSL', nil),
168
173
  proxy_url: ENV.fetch('WECHAT_PROXY_URL', nil),
169
- proxy_port: ENV.fetch('WECHAT_PROXY_PORT', nil),
170
174
  proxy_username: ENV.fetch('WECHAT_PROXY_USERNAME', nil),
171
175
  proxy_password: ENV.fetch('WECHAT_PROXY_PASSWORD', nil),
172
176
  encoding_aes_key: ENV.fetch('WECHAT_ENCODING_AES_KEY', nil),
@@ -144,12 +144,6 @@ module Wechat
144
144
  post 'menu/delconditional', JSON.generate(menuid: menuid)
145
145
  end
146
146
 
147
- def material(media_id)
148
- ActiveSupport::Deprecation.new.warn('material is deprecated. use get_material instead.')
149
-
150
- post 'material/get_material', JSON.generate(media_id: media_id), as: :file
151
- end
152
-
153
147
  def get_material(media_id)
154
148
  post 'material/get_material', JSON.generate(media_id: media_id), as: :file
155
149
  end
@@ -102,7 +102,7 @@ module Wechat
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
- client.httprb.post(upload_url, form: form_data, ssl_context: client.ssl_context)
105
+ client.httpx.post(upload_url, form: form_data)
106
106
  end
107
107
 
108
108
  def tcb_upload_file(q_path, file)
@@ -4,12 +4,12 @@ module Wechat
4
4
  class CorpApi < ApiBase
5
5
  attr_reader :agentid
6
6
 
7
- def initialize(appid, secret, token_file, agentid, network_setting, jsapi_ticket_file)
7
+ def initialize(api_config, agentid)
8
8
  super()
9
- @client = HttpClient.new(QYAPI_BASE, network_setting)
10
- @access_token = Token::CorpAccessToken.new(@client, appid, secret, token_file)
9
+ @client = HttpClient.new(QYAPI_BASE, api_config.network_setting)
10
+ @access_token = Token::CorpAccessToken.new(@client, api_config.appid, api_config.secret, api_config.token_file)
11
11
  @agentid = agentid
12
- @jsapi_ticket = Ticket::CorpJsapiTicket.new(@client, @access_token, jsapi_ticket_file)
12
+ @jsapi_ticket = Ticket::CorpJsapiTicket.new(@client, @access_token, api_config.jsapi_ticket_file)
13
13
  @qcloud = nil
14
14
  end
15
15
 
@@ -170,12 +170,6 @@ module Wechat
170
170
  post 'material/batchget', JSON.generate(type: type, agentid: agentid, offset: offset, count: count)
171
171
  end
172
172
 
173
- def material(media_id)
174
- ActiveSupport::Deprecation.new.warn('material is deprecated. use get_material instead.')
175
-
176
- post 'material/get_material', JSON.generate(media_id: media_id), params: { agentid: agentid }, as: :file
177
- end
178
-
179
173
  def get_material(media_id)
180
174
  post 'material/get_material', JSON.generate(media_id: media_id), params: { agentid: agentid }, as: :file
181
175
  end
@@ -1,51 +1,49 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'http'
3
+ require 'httpx'
4
4
 
5
5
  module Wechat
6
6
  class HttpClient
7
- attr_reader :base, :ssl_context, :httprb
7
+ attr_reader :base, :httpx
8
8
 
9
9
  def initialize(base, network_setting)
10
10
  @base = base
11
- @httprb = if HTTP::VERSION.to_i >= 4
12
- HTTP.timeout(write: network_setting.timeout, connect: network_setting.timeout, read: network_setting.timeout)
13
- else
14
- HTTP.timeout(:global, write: network_setting.timeout, connect: network_setting.timeout, read: network_setting.timeout)
15
- end
16
-
17
- unless network_setting.proxy_url.nil?
18
- @httprb = @httprb.via(network_setting.proxy_url, network_setting.proxy_port.to_i, network_setting.proxy_username, network_setting.proxy_password)
11
+ @httpx = HTTPX.with(timeout: { connect_timeout: network_setting.timeout, request_timeout: network_setting.timeout })
12
+
13
+ if network_setting.proxy_url.present?
14
+ @httpx = @httpx.with_proxy(uri: network_setting.proxy_url,
15
+ username: network_setting.proxy_username,
16
+ password: network_setting.proxy_password)
19
17
  end
20
18
 
21
- @ssl_context = OpenSSL::SSL::SSLContext.new
22
- @ssl_context.ssl_version = 'TLSv1_2'
23
- @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE if network_setting.skip_verify_ssl
19
+ return unless network_setting.skip_verify_ssl
20
+
21
+ @httpx = @httpx.with(ssl: { verify_mode: OpenSSL::SSL::VERIFY_NONE })
24
22
  end
25
23
 
26
24
  def get(path, get_header = {})
27
- request(path, get_header) do |url, header|
28
- params = header.delete(:params)
29
- httprb.headers(header).get(url, params: params, ssl_context: ssl_context)
25
+ request(path, get_header) do |url, headers|
26
+ params = headers.delete(:params)
27
+ httpx.with(headers: headers)
28
+ .get(url, params: params)
30
29
  end
31
30
  end
32
31
 
33
32
  def post(path, payload, post_header = {})
34
- request(path, post_header) do |url, header|
35
- params = header.delete(:params)
36
- httprb.headers(header).post(url, params: params, body: payload, ssl_context: ssl_context)
33
+ request(path, post_header) do |url, headers|
34
+ params = headers.delete(:params)
35
+ httpx.with(headers: headers)
36
+ .post(url, params: params, body: payload)
37
37
  end
38
38
  end
39
39
 
40
40
  def post_file(path, file, post_header = {})
41
- request(path, post_header) do |url, header|
42
- params = header.delete(:params)
41
+ request(path, post_header) do |url, headers|
42
+ params = headers.delete(:params)
43
43
  form_file = file.is_a?(HTTP::FormData::File) ? file : HTTP::FormData::File.new(file)
44
- httprb.headers(header)
45
- .post(url, params: params,
46
- form: { media: form_file,
47
- hack: 'X' }, # Existing here for http-form_data 1.0.1 handle single param improperly
48
- ssl_context: ssl_context)
44
+ httpx.with(headers: headers)
45
+ .post(url, params: params,
46
+ form: { media: form_file })
49
47
  end
50
48
  end
51
49
 
@@ -57,7 +55,18 @@ module Wechat
57
55
  header['Accept'] ||= 'application/json'
58
56
  response = yield("#{url_base}#{path}", header)
59
57
 
60
- raise "Request not OK, response status #{response.status}" if response.status != 200
58
+ # Error responses (network failures, DNS errors, etc.) are returned as
59
+ # `HTTPX::ErrorResponse` objects which **do not** implement `#status`.
60
+ # Attempting to access that method would raise a `NoMethodError`.
61
+ # Instead, surface the underlying error immediately so that callers can
62
+ # handle it using the existing error-handling flow.
63
+ if defined?(HTTPX::ErrorResponse) && response.is_a?(HTTPX::ErrorResponse)
64
+ # `response.error` returns the wrapped exception (e.g. Errno::ECONNREFUSED)
65
+ # Fallback to raising the response itself if no wrapped error exists.
66
+ raise(response.error || StandardError.new(response.inspect))
67
+ end
68
+
69
+ raise "Request not OK, response #{response}" if response.respond_to?(:status) && response.status != 200
61
70
 
62
71
  parse_response(response, as || :json) do |parse_as, data|
63
72
  break data unless parse_as == :json && data['errcode'].present?
@@ -80,7 +89,7 @@ module Wechat
80
89
  end
81
90
 
82
91
  def parse_response(response, as_type)
83
- content_type = response.headers[:content_type]
92
+ content_type = response.headers['content-type']
84
93
  parse_as = {
85
94
  %r{^application/json} => :json,
86
95
  %r{^image/.*} => :file,
@@ -9,16 +9,16 @@ module Wechat
9
9
 
10
10
  def to(to_users = '', towxname: nil, send_ignore_reprint: 0)
11
11
  if towxname.present?
12
- new(ToWxName: towxname, CreateTime: Time.now.to_i)
12
+ new(ToWxName: towxname, CreateTime: Time.zone.now.to_i)
13
13
  elsif send_ignore_reprint == 1
14
- new(ToUserName: to_users, CreateTime: Time.now.to_i, send_ignore_reprint: send_ignore_reprint)
14
+ new(ToUserName: to_users, CreateTime: Time.zone.now.to_i, send_ignore_reprint: send_ignore_reprint)
15
15
  else
16
- new(ToUserName: to_users, CreateTime: Time.now.to_i)
16
+ new(ToUserName: to_users, CreateTime: Time.zone.now.to_i)
17
17
  end
18
18
  end
19
19
 
20
20
  def to_party(party)
21
- new(ToPartyName: party, CreateTime: Time.now.to_i)
21
+ new(ToPartyName: party, CreateTime: Time.zone.now.to_i)
22
22
  end
23
23
 
24
24
  def to_mass(tag_id: nil, send_ignore_reprint: 0)
@@ -65,7 +65,7 @@ module Wechat
65
65
  Message.new(
66
66
  ToUserName: message_hash[:FromUserName],
67
67
  FromUserName: message_hash[:ToUserName],
68
- CreateTime: Time.now.to_i,
68
+ CreateTime: Time.zone.now.to_i,
69
69
  WechatSession: session
70
70
  )
71
71
  end
@@ -238,8 +238,14 @@ module Wechat
238
238
  key = key.to_s
239
239
  [TO_JSON_KEY_MAP[key] || (keep_camel_case_key ? key : key.downcase), value]
240
240
  end
241
- json_hash = json_hash.transform_keys(&:downcase).select { |k, _v| TO_JSON_ALLOWED.include? k }
241
+ json_hash = json_hash.transform_keys(&:downcase).slice(*TO_JSON_ALLOWED)
242
+ json_hash = transform_json_hash_by_msgtype(json_hash)
243
+ JSON.generate(json_hash)
244
+ end
245
+
246
+ private
242
247
 
248
+ def transform_json_hash_by_msgtype(json_hash)
243
249
  case json_hash['msgtype']
244
250
  when 'text'
245
251
  json_hash['text'] = { 'content' => json_hash.delete('content') }
@@ -255,7 +261,7 @@ module Wechat
255
261
  when 'template'
256
262
  json_hash = { 'touser' => json_hash['touser'] }.merge!(json_hash['template'])
257
263
  end
258
- JSON.generate(json_hash)
264
+ json_hash
259
265
  end
260
266
 
261
267
  def save_to!(model_class)
@@ -264,8 +270,6 @@ module Wechat
264
270
  self
265
271
  end
266
272
 
267
- private
268
-
269
273
  def camelize_hash_keys(hash)
270
274
  deep_recursive(hash) { |key, value| [key.to_s.camelize.to_sym, value] }
271
275
  end
data/lib/wechat/mp_api.rb CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  module Wechat
4
4
  class MpApi < ApiBase
5
- def initialize(appid, secret, token_file, network_setting, jsapi_ticket_file, qcloud_setting)
5
+ def initialize(api_config, qcloud_setting)
6
6
  super()
7
- @client = HttpClient.new(Wechat::Api::API_BASE, network_setting)
8
- @access_token = Token::PublicAccessToken.new(@client, appid, secret, token_file)
9
- @jsapi_ticket = Ticket::PublicJsapiTicket.new(@client, @access_token, jsapi_ticket_file)
7
+ @client = HttpClient.new(Wechat::Api::API_BASE, api_config.network_setting)
8
+ @access_token = Token::PublicAccessToken.new(@client, api_config.appid, api_config.secret, api_config.token_file)
9
+ @jsapi_ticket = Ticket::PublicJsapiTicket.new(@client, @access_token, api_config.jsapi_ticket_file)
10
10
  @qcloud = Qcloud::Token.new(@client, @access_token, qcloud_setting)
11
11
  end
12
12
 
@@ -2,13 +2,12 @@
2
2
 
3
3
  module Wechat
4
4
  class NetworkSetting
5
- attr_reader :timeout, :skip_verify_ssl, :proxy_url, :proxy_port, :proxy_username, :proxy_password
5
+ attr_reader :timeout, :skip_verify_ssl, :proxy_url, :proxy_username, :proxy_password
6
6
 
7
- def initialize(timeout, skip_verify_ssl, proxy_url, proxy_port, proxy_username, proxy_password)
7
+ def initialize(timeout, skip_verify_ssl, proxy_url, proxy_username, proxy_password)
8
8
  @timeout = timeout
9
9
  @skip_verify_ssl = skip_verify_ssl
10
10
  @proxy_url = proxy_url
11
- @proxy_port = proxy_port
12
11
  @proxy_username = proxy_username
13
12
  @proxy_password = proxy_password
14
13
  end
@@ -59,7 +59,7 @@ module Wechat
59
59
  end
60
60
 
61
61
  def remain_life_seconds
62
- qcloud_token_expired_time - Time.now.to_i
62
+ qcloud_token_expired_time - Time.zone.now.to_i
63
63
  end
64
64
  end
65
65
  end
@@ -41,7 +41,7 @@ module Wechat
41
41
  def signature(url)
42
42
  params = {
43
43
  noncestr: SecureRandom.base64(16),
44
- timestamp: Time.now.to_i,
44
+ timestamp: Time.zone.now.to_i,
45
45
  jsapi_ticket: ticket,
46
46
  url: url
47
47
  }
@@ -65,7 +65,7 @@ module Wechat
65
65
  end
66
66
 
67
67
  def write_ticket_to_store(ticket_hash)
68
- ticket_hash['got_ticket_at'] = Time.now.to_i
68
+ ticket_hash['got_ticket_at'] = Time.zone.now.to_i
69
69
  ticket_hash['ticket_expires_in'] = ticket_hash.delete('expires_in')
70
70
  write_ticket(ticket_hash)
71
71
  end
@@ -79,7 +79,7 @@ module Wechat
79
79
  end
80
80
 
81
81
  def remain_life_seconds
82
- ticket_life_in_seconds - (Time.now.to_i - got_ticket_at)
82
+ ticket_life_in_seconds - (Time.zone.now.to_i - got_ticket_at)
83
83
  end
84
84
  end
85
85
  end
@@ -35,7 +35,7 @@ module Wechat
35
35
  def write_token_to_store(token_hash)
36
36
  raise InvalidCredentialError unless token_hash.is_a?(Hash) && token_hash['access_token']
37
37
 
38
- token_hash['got_token_at'] = Time.now.to_i
38
+ token_hash['got_token_at'] = Time.zone.now.to_i
39
39
  token_hash['token_expires_in'] = token_hash.delete('expires_in')
40
40
  write_token(token_hash)
41
41
  end
@@ -63,7 +63,7 @@ module Wechat
63
63
  end
64
64
 
65
65
  def remain_life_seconds
66
- token_life_in_seconds - (Time.now.to_i - got_token_at)
66
+ token_life_in_seconds - (Time.zone.now.to_i - got_token_at)
67
67
  end
68
68
 
69
69
  private
@@ -77,7 +77,7 @@ module Wechat
77
77
 
78
78
  record.access_token = token_hash['access_token']
79
79
  record.token_expires_in = token_hash['token_expires_in']
80
- record.got_token_at = Time.now
80
+ record.got_token_at = Time.zone.now
81
81
  record.save || record.save(validate: false)
82
82
  end
83
83
 
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.17.7
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Skinnyworm
@@ -35,7 +35,7 @@ cert_chain:
35
35
  200/Tqe769E/Y40VHi+y7u06vT756gzVfC7nH5EUd2eIFAePlp8wMvNFW5+56bKl
36
36
  LbbJnkHtUxxHWQTe2lJQuFErTSuYIaN9DhzJYNot7Ot7SPbdeChVsbPtjPg=
37
37
  -----END CERTIFICATE-----
38
- date: 2025-03-01 00:00:00.000000000 Z
38
+ date: 1980-01-02 00:00:00.000000000 Z
39
39
  dependencies:
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: activesupport
@@ -58,25 +58,19 @@ dependencies:
58
58
  - !ruby/object:Gem::Version
59
59
  version: '9'
60
60
  - !ruby/object:Gem::Dependency
61
- name: http
61
+ name: httpx
62
62
  requirement: !ruby/object:Gem::Requirement
63
63
  requirements:
64
64
  - - ">="
65
65
  - !ruby/object:Gem::Version
66
- version: 1.0.4
67
- - - "<"
68
- - !ruby/object:Gem::Version
69
- version: '6'
66
+ version: 1.3.4
70
67
  type: :runtime
71
68
  prerelease: false
72
69
  version_requirements: !ruby/object:Gem::Requirement
73
70
  requirements:
74
71
  - - ">="
75
72
  - !ruby/object:Gem::Version
76
- version: 1.0.4
77
- - - "<"
78
- - !ruby/object:Gem::Version
79
- version: '6'
73
+ version: 1.3.4
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: 1.70.0
150
+ version: 1.72.2
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: 1.70.0
157
+ version: 1.72.2
164
158
  - !ruby/object:Gem::Dependency
165
159
  name: rails
166
160
  requirement: !ruby/object:Gem::Requirement
167
161
  requirements:
168
162
  - - ">="
169
163
  - !ruby/object:Gem::Version
170
- version: 8.0.0
164
+ version: '7.2'
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: 8.0.0
171
+ version: '7.2'
178
172
  - !ruby/object:Gem::Dependency
179
173
  name: rspec-rails
180
174
  requirement: !ruby/object:Gem::Requirement
@@ -248,6 +242,7 @@ files:
248
242
  - lib/wechat.rb
249
243
  - lib/wechat/api.rb
250
244
  - lib/wechat/api_base.rb
245
+ - lib/wechat/api_config.rb
251
246
  - lib/wechat/api_loader.rb
252
247
  - lib/wechat/cipher.rb
253
248
  - lib/wechat/concern/common.rb
@@ -276,9 +271,12 @@ licenses:
276
271
  metadata:
277
272
  bug_tracker_uri: https://github.com/Eric-Guo/wechat/issues
278
273
  changelog_uri: https://github.com/Eric-Guo/wechat/releases
279
- documentation_uri: https://github.com/Eric-Guo/wechat/tree/v0.17.7#readme
280
- source_code_uri: https://github.com/Eric-Guo/wechat/tree/v0.17.7
274
+ documentation_uri: https://github.com/Eric-Guo/wechat/tree/v1.0.1#readme
275
+ source_code_uri: https://github.com/Eric-Guo/wechat/tree/v1.0.1
281
276
  rubygems_mfa_required: 'true'
277
+ post_install_message: |2
278
+
279
+ BREAKING changes: WECHAT_PROXY_URL should be written with port now, like `export WECHAT_PROXY_URL=http://127.0.0.1:6152`, since WECHAT_PROXY_PORT removed.
282
280
  rdoc_options: []
283
281
  require_paths:
284
282
  - lib
@@ -293,7 +291,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
293
291
  - !ruby/object:Gem::Version
294
292
  version: 3.1.6
295
293
  requirements: []
296
- rubygems_version: 3.6.5
294
+ rubygems_version: 3.6.9
297
295
  specification_version: 4
298
296
  summary: DSL for wechat message handling and API
299
297
  test_files: []
metadata.gz.sig CHANGED
Binary file