ocean-wechat 0.1.0 → 0.1.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 +4 -4
- data/lib/ocean/wechat/version.rb +1 -1
- data/lib/ocean/wechat.rb +143 -141
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e0a59062bb573b2b23fbc4a9fd79b1ec0c6bd86da75bdadc7182fbef530e4fd0
|
4
|
+
data.tar.gz: 8d5052896d617d42b9d0309fc02d56079aa5eaad991fd7a4333b923a4b2948ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 25e273cb62ce790f09e25f0c6002e65aafd5dc9b1f012437a1fe24687fa33c08769571297deb0cf01b1bd47398621edfca16b2bd17929f28c0cada156690a84f
|
7
|
+
data.tar.gz: a53b4e5b6e01b8686c897b3d67198ce694a5e592bdae8c2da9a1c9adbd8bdf121d87c0412ebec69e4c043b6756bd207ed2625ed43179084d83326a2388e00aa0
|
data/lib/ocean/wechat/version.rb
CHANGED
data/lib/ocean/wechat.rb
CHANGED
@@ -3,185 +3,187 @@
|
|
3
3
|
# Wechat service, View more at: https://mp.weixin.qq.com/wiki
|
4
4
|
# example:
|
5
5
|
# Wechat.notify(event: 'crash', first: 'Your website is not accessible', time: Time.now, reason: 'Connect fail!', remark: 'remark', redirect_url: 'www.baidu.com')
|
6
|
-
module
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
6
|
+
module Ocean
|
7
|
+
module Wechat
|
8
|
+
extend self
|
9
|
+
|
10
|
+
BASE_URL = 'https://api.weixin.qq.com/cgi-bin'
|
11
|
+
ACCESS_TOKEN_FILE = 'tmp/access_token.txt'
|
12
|
+
NOTIFY_RECIPIENT_FILE = 'config/wechat_notify.yml'
|
13
|
+
SUCCESS_CODE = 0
|
14
|
+
ACCESS_TOKEN_EXPIRED_CODE = 420_01
|
15
|
+
ACCESS_TOKEN_INVALID_CODE = 400_01
|
16
|
+
NOTIFY_DEFAULT_COLOR = '#173177'
|
17
|
+
MAX_RETRY_NUM = 5
|
18
|
+
|
19
|
+
def access_token
|
20
|
+
read_access_token || refresh_access_token
|
21
|
+
end
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
23
|
+
# Use wechat template to send message
|
24
|
+
def notify(**args)
|
25
|
+
notify_block = lambda do |open_id|
|
26
|
+
url = "#{BASE_URL}/message/template/send"
|
27
|
+
params = {
|
28
|
+
touser: open_id,
|
29
|
+
template_id: wechat_notify(args[:event], 'template'),
|
30
|
+
url: args[:redirect_url],
|
31
|
+
data: {}
|
32
|
+
}
|
32
33
|
|
33
|
-
|
34
|
+
args.each { |k, v| params[:data][k] = { value: v, color: NOTIFY_DEFAULT_COLOR } }
|
34
35
|
|
35
|
-
|
36
|
-
|
36
|
+
request(url, params, :post)
|
37
|
+
end
|
37
38
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
[].tap do |item|
|
40
|
+
wechat_notify(args[:event], 'users').each do |open_id|
|
41
|
+
item << {
|
42
|
+
open_id: open_id,
|
43
|
+
success: notify_block.call(open_id)[:errcode] == SUCCESS_CODE
|
44
|
+
}
|
45
|
+
end
|
44
46
|
end
|
45
47
|
end
|
46
|
-
end
|
47
48
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
# Get all notify template
|
50
|
+
def notify_templates
|
51
|
+
request("#{BASE_URL}/template/get_all_private_template")
|
52
|
+
end
|
52
53
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
54
|
+
# Get all followers
|
55
|
+
def users
|
56
|
+
request("#{BASE_URL}/user/get")
|
57
|
+
end
|
57
58
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
59
|
+
def create_user_tag(name)
|
60
|
+
params = {
|
61
|
+
tag: {
|
62
|
+
name: name
|
63
|
+
}
|
62
64
|
}
|
63
|
-
}
|
64
65
|
|
65
|
-
|
66
|
-
|
66
|
+
request("#{BASE_URL}/tags/create", params, :post)
|
67
|
+
end
|
67
68
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
69
|
+
# Get all user tag
|
70
|
+
def user_tags
|
71
|
+
request("#{BASE_URL}/tags/get")
|
72
|
+
end
|
72
73
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
74
|
+
def find_users_by_tag(name)
|
75
|
+
res = request(
|
76
|
+
"#{BASE_URL}/user/tag/get",
|
77
|
+
{
|
78
|
+
tagid: find_tag_id_by_name(name)
|
79
|
+
},
|
80
|
+
:post
|
81
|
+
)
|
81
82
|
|
82
|
-
|
83
|
-
|
83
|
+
res['count'] == 0 ? [] : res['data']['openid']
|
84
|
+
end
|
84
85
|
|
85
|
-
|
86
|
-
|
87
|
-
|
86
|
+
def find_tag_id_by_name(name)
|
87
|
+
user_tags[:tags].detect { |item| item['name'] == name }['id']
|
88
|
+
end
|
88
89
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
90
|
+
def add_tag_for_users(users, tag_id)
|
91
|
+
params = {
|
92
|
+
open_list: users,
|
93
|
+
tagid: tag_id
|
94
|
+
}
|
94
95
|
|
95
|
-
|
96
|
-
|
96
|
+
request("#{BASE_URL}/tags/members/batchtagging", params, :post)
|
97
|
+
end
|
97
98
|
|
98
|
-
private
|
99
|
+
private
|
99
100
|
|
100
|
-
|
101
|
-
|
101
|
+
def request(source_url, params = {}, method = :get, retry_num = 0)
|
102
|
+
url = compose_url(source_url)
|
102
103
|
|
103
|
-
|
104
|
+
log.debug "Send #{method} request #{params} to #{url}"
|
104
105
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
106
|
+
res = if method == :get
|
107
|
+
RestClient.get(url, params)
|
108
|
+
else
|
109
|
+
RestClient.post(url, params.to_json, content_type: :json, accept: :json)
|
110
|
+
end
|
110
111
|
|
111
|
-
|
112
|
-
|
112
|
+
res_body = read_content(res)
|
113
|
+
json = JSON.parse force_encoding(res_body)
|
113
114
|
|
114
|
-
|
115
|
+
json.symbolize_keys!
|
115
116
|
|
116
|
-
|
117
|
-
|
118
|
-
|
117
|
+
if json[:errcode] == ACCESS_TOKEN_EXPIRED_CODE || json[:errcode] == ACCESS_TOKEN_INVALID_CODE
|
118
|
+
# Try again five times at most
|
119
|
+
return if retry_num == MAX_RETRY_NUM
|
119
120
|
|
120
|
-
|
121
|
+
log.info("Access token is expired or invalid, retry #{retry_num} ...")
|
121
122
|
|
122
|
-
|
123
|
+
refresh_access_token
|
123
124
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
125
|
+
# Control call frequency
|
126
|
+
sleep(retry_num * 10)
|
127
|
+
retry_num += 1
|
128
|
+
return request(source_url, params, method, retry_num)
|
129
|
+
elsif json[:errcode] && json[:errcode] != SUCCESS_CODE
|
130
|
+
raise "Request #{url}, errmsg: #{json[:errmsg]}, errcode: #{json[:errcode]}"
|
131
|
+
else
|
132
|
+
json
|
133
|
+
end
|
134
|
+
rescue StandardError => ex
|
135
|
+
log.error("Call wechat api error: #{ex}\n #{ex.backtrace.join("\n")}")
|
131
136
|
json
|
132
137
|
end
|
133
|
-
rescue StandardError => ex
|
134
|
-
log.error("Call wechat api error: #{ex}\n #{ex.backtrace.join("\n")}")
|
135
|
-
json
|
136
|
-
end
|
137
138
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
139
|
+
# Remove not UTF-8 encoding
|
140
|
+
def force_encoding(data)
|
141
|
+
data.force_encoding('UTF-8').gsub(/[\u0000-\u001f\u007f\u0080-\u009f]+/, '*')
|
142
|
+
end
|
142
143
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
144
|
+
def read_content(res)
|
145
|
+
if res['content-encoding'].present? && res['content-encoding'] == 'gzip'
|
146
|
+
Zlib::GzipReader.new(StringIO.new(res.body), encoding: 'UTF-8').read
|
147
|
+
else
|
148
|
+
res.body
|
149
|
+
end
|
148
150
|
end
|
149
|
-
end
|
150
151
|
|
151
|
-
|
152
|
-
|
153
|
-
|
152
|
+
def compose_url(source_url)
|
153
|
+
source_url.include?('?') ? source_url : "#{source_url}?access_token=#{access_token}"
|
154
|
+
end
|
154
155
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
156
|
+
def refresh_access_token
|
157
|
+
params = {
|
158
|
+
grant_type: 'client_credential',
|
159
|
+
appid: ENV.fetch('WECHAT_APP_ID'),
|
160
|
+
secret: ENV.fetch('WECHAT_APP_SECRET')
|
161
|
+
}
|
161
162
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
163
|
+
res = request("#{BASE_URL}/token?#{params.to_query}", {}, 'GET')
|
164
|
+
save_access_token(res[:access_token]) if res[:access_token]
|
165
|
+
res[:access_token]
|
166
|
+
end
|
166
167
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
168
|
+
def save_access_token(token)
|
169
|
+
file = File.new(ACCESS_TOKEN_FILE, 'w')
|
170
|
+
file.write(token)
|
171
|
+
rescue IOError => ex
|
172
|
+
log.error("Write access_token fail: #{ex}")
|
173
|
+
ensure
|
174
|
+
file.close
|
175
|
+
end
|
175
176
|
|
176
|
-
|
177
|
-
|
178
|
-
|
177
|
+
def read_access_token
|
178
|
+
File.exist?(ACCESS_TOKEN_FILE) ? File.read(ACCESS_TOKEN_FILE) : nil
|
179
|
+
end
|
179
180
|
|
180
|
-
|
181
|
-
|
182
|
-
|
181
|
+
def wechat_notify(event, key)
|
182
|
+
File.exist?(NOTIFY_RECIPIENT_FILE) ? YAML.load_file(NOTIFY_RECIPIENT_FILE)[event.to_s][key] : []
|
183
|
+
end
|
183
184
|
|
184
|
-
|
185
|
-
|
185
|
+
def log
|
186
|
+
Rails.logger
|
187
|
+
end
|
186
188
|
end
|
187
189
|
end
|