ZReviewTender 1.3.7 → 1.3.8
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/.version +1 -1
- data/lib/AndroidFetcher.rb +1 -1
- data/lib/AppleFetcher.rb +1 -1
- data/lib/Models/Review.rb +3 -2
- data/lib/Processors/AsanaProcessor.rb +2 -0
- data/lib/Processors/SlackAndAsanaConnector.rb +159 -0
- data/lib/Processors/SlackProcessor.rb +18 -9
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '016962d0d7c4215f6c60a77d20fb83290d5cd5b65e6883de27d7fb5116cfccdb'
|
4
|
+
data.tar.gz: 84810201df2669c00b955a8d7da1382112dd4041e963b8cbd3730dc05e988f8c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a2cbe8d510432fb5a353faa7357ea5b9b194d58d8ef4f3e8dfec69792ab3f8b4512c1b43efb26d525be4ccb62c42ad61ebe3f6c9c37ebecd43da4fc6b71d4d4d
|
7
|
+
data.tar.gz: 520a4d667e91b28dba82c1b5f7829fd6566c7b0976ca7c90f7620b9d857e6efa463dd854d2eb3f5b9629e106557349b7f2dab031dfd8a20e457912c87dbd9d03
|
data/.version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.3.
|
1
|
+
1.3.8
|
data/lib/AndroidFetcher.rb
CHANGED
@@ -112,7 +112,7 @@ class AndroidFetcher < ReviewFetcher
|
|
112
112
|
if !config.accountID.nil? && !config.appID.nil?
|
113
113
|
url = "https://play.google.com/console/developers/#{config.accountID}/app/#{config.appID}/user-feedback/review-details?reviewId=#{customerReviewID}"
|
114
114
|
end
|
115
|
-
reviews.append(Review.new(customerReviewPlatform, customerReviewID, customerReviewReviewerNickname, customerReviewRating, customerReviewTitle, customerReviewBody, customerReviewCreatedDateTimestamp, url, customerReviewVersionString, customerReviewTerritory))
|
115
|
+
reviews.append(Review.new(customerReviewPlatform, customerReviewID, customerReviewReviewerNickname, customerReviewRating, customerReviewTitle, customerReviewBody, customerReviewCreatedDateTimestamp, url, customerReviewVersionString, customerReviewTerritory, {}))
|
116
116
|
|
117
117
|
# init first time, need first review to set as latestCheckTimestamp
|
118
118
|
if latestCheckTimestamp == 0
|
data/lib/AppleFetcher.rb
CHANGED
@@ -80,7 +80,7 @@ class AppleFetcher < ReviewFetcher
|
|
80
80
|
break
|
81
81
|
else
|
82
82
|
url = "https://appstoreconnect.apple.com/WebObjects/iTunesConnect.woa/ra/ng/app/#{config.appID}/ios/ratingsResponses"
|
83
|
-
reviews.append(Review.new(nil, customerReviewID, customerReviewReviewerNickname, customerReviewRating, customerReviewTitle, customerReviewBody, customerReviewCreatedDateTimestamp, url, nil, customerReviewTerritory))
|
83
|
+
reviews.append(Review.new(nil, customerReviewID, customerReviewReviewerNickname, customerReviewRating, customerReviewTitle, customerReviewBody, customerReviewCreatedDateTimestamp, url, nil, customerReviewTerritory, {}))
|
84
84
|
|
85
85
|
# init first time, need first review to set as latestCheckTimestamp
|
86
86
|
if latestCheckTimestamp == 0
|
data/lib/Models/Review.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
$lib = File.expand_path('../', File.dirname(__FILE__))
|
2
2
|
|
3
3
|
class Review
|
4
|
-
attr_accessor :platform, :id, :userName, :rating, :title, :body, :createdDateTimestamp, :url, :appVersion, :territory
|
5
|
-
def initialize(platform, id, userName, rating, title, body, createdDateTimestamp, url, appVersion, territory)
|
4
|
+
attr_accessor :platform, :id, :userName, :rating, :title, :body, :createdDateTimestamp, :url, :appVersion, :territory, :tempData
|
5
|
+
def initialize(platform, id, userName, rating, title, body, createdDateTimestamp, url, appVersion, territory, tempData)
|
6
6
|
@platform = platform # 來源平台 android or apple
|
7
7
|
@id = id # review id
|
8
8
|
@userName = userName # reviewer
|
@@ -13,5 +13,6 @@ class Review
|
|
13
13
|
@url = url # 前往 review 的連結 (apple 不提供後台指定到某個 review 的連結)
|
14
14
|
@appVersion = appVersion # app 版本號
|
15
15
|
@territory = territory # apple: 地區(TWN/JPN...), android: 語系(zh-tw/en...)
|
16
|
+
@tempData = tempData # 程式內部傳遞使用
|
16
17
|
end
|
17
18
|
end
|
@@ -84,6 +84,8 @@ class AsanaProcessor < Processor
|
|
84
84
|
|
85
85
|
taskData = asanaAPI("/tasks", "POST", requestTaskData)
|
86
86
|
taskData = taskData["data"]
|
87
|
+
|
88
|
+
review.tempData["asanaTaskGID"] = taskData["gid"]
|
87
89
|
|
88
90
|
if !sectionID.nil? && !taskData.nil?
|
89
91
|
asanaAPI("/sections/#{sectionID}/addTask", "POST", {"task": taskData["gid"]})
|
@@ -0,0 +1,159 @@
|
|
1
|
+
$lib = File.expand_path('../lib', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
require "Models/Review"
|
4
|
+
require "Models/Processor"
|
5
|
+
require "Helper"
|
6
|
+
require "pathname"
|
7
|
+
require "GoogleAPI"
|
8
|
+
|
9
|
+
class SlackAndAsanaConnector < Processor
|
10
|
+
|
11
|
+
attr_accessor :logger, :slackBotToken, :asanaToken, :projectID, :asanaAPIURL
|
12
|
+
|
13
|
+
def initialize(config, configFilePath, baseExecutePath)
|
14
|
+
@config = config
|
15
|
+
@configFilePath = configFilePath
|
16
|
+
@baseExecutePath = baseExecutePath
|
17
|
+
@logger = ZLogger.new(baseExecutePath)
|
18
|
+
|
19
|
+
@asanaAPIURL = "https://app.asana.com/api/1.0"
|
20
|
+
@asanaToken = Helper.unwrapRequiredParameter(config, "asanaToken")
|
21
|
+
@slackBotToken = Helper.unwrapRequiredParameter(config, "slackBotToken")
|
22
|
+
|
23
|
+
puts "[SlackAndAsanaConnector] Init Success."
|
24
|
+
end
|
25
|
+
|
26
|
+
def processReviews(reviews, platform)
|
27
|
+
if reviews.length < 1
|
28
|
+
return reviews
|
29
|
+
end
|
30
|
+
|
31
|
+
pendingReviews = reviews.dup
|
32
|
+
|
33
|
+
loop do
|
34
|
+
review = pendingReviews.shift
|
35
|
+
|
36
|
+
result = retrieveSlackMessage(review)
|
37
|
+
if !result["ok"]
|
38
|
+
if result["error"] == "ratelimited"
|
39
|
+
puts "[SlackAndAsanaConnector] Reached Rate Limited, sleep 1 sec..."
|
40
|
+
sleep(1)
|
41
|
+
pendingReviews.insert(0, review)
|
42
|
+
end
|
43
|
+
else
|
44
|
+
sendAsanaCommentMessage(review, "ref: #{result["permalink"]}")
|
45
|
+
end
|
46
|
+
|
47
|
+
puts "[SlackAndAsanaConnector] Connect Slack Message to Asana Task, rest: #{pendingReviews.length}"
|
48
|
+
break if pendingReviews.length < 1
|
49
|
+
end
|
50
|
+
|
51
|
+
pendingReviews = reviews.dup
|
52
|
+
|
53
|
+
loop do
|
54
|
+
review = pendingReviews.shift
|
55
|
+
asanaTaskURL = retrieveAsanaTaskURL(review)
|
56
|
+
if !asanaTaskURL.nil?
|
57
|
+
result = sendSlackCommnetMessage(review, "<#{asanaTaskURL}| Go To *Asana Task*>")
|
58
|
+
if !result["ok"]
|
59
|
+
if result["error"] == "ratelimited"
|
60
|
+
puts "[SlackAndAsanaConnector] Reached Rate Limited, sleep 1 sec..."
|
61
|
+
sleep(1)
|
62
|
+
pendingReviews.insert(0, review)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
puts "[SlackAndAsanaConnector] Connect Asana Task to Slack Message, rest: #{pendingReviews.length}"
|
68
|
+
break if pendingReviews.length < 1
|
69
|
+
end
|
70
|
+
|
71
|
+
return reviews
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
def retrieveSlackMessage(review)
|
76
|
+
uri = URI("https://slack.com/api/chat.getPermalink?channel=#{review.tempData["slackChannelID"]}&message_ts=#{review.tempData["slackTS"]}")
|
77
|
+
headers = {'Content-Type': 'application/json; charset=utf-8', 'Authorization': "Bearer #{slackBotToken}"}
|
78
|
+
|
79
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
80
|
+
http.use_ssl = true
|
81
|
+
req = Net::HTTP::Get.new(uri.request_uri, headers)
|
82
|
+
res = http.request(req)
|
83
|
+
|
84
|
+
result = JSON.parse(res.body)
|
85
|
+
return result
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
def sendSlackCommnetMessage(review, message)
|
90
|
+
payload = Payload.new()
|
91
|
+
payload.text = message
|
92
|
+
payload.channel = review.tempData["slackChannelID"]
|
93
|
+
payload.thread_ts = review.tempData["slackTS"]
|
94
|
+
payload.unfurl_links = true
|
95
|
+
payload.unfurl_media = true
|
96
|
+
uri = URI("https://slack.com/api/chat.postMessage")
|
97
|
+
headers = {'Content-Type': 'application/json; charset=utf-8', 'Authorization': "Bearer #{slackBotToken}"}
|
98
|
+
|
99
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
100
|
+
http.use_ssl = true
|
101
|
+
req = Net::HTTP::Post.new(uri.request_uri, headers)
|
102
|
+
req.body = payload.to_json
|
103
|
+
res = http.request(req)
|
104
|
+
|
105
|
+
result = JSON.parse(res.body)
|
106
|
+
return result
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
private
|
111
|
+
def retrieveAsanaTaskURL(review)
|
112
|
+
if review.tempData["asanaTaskGID"].nil? || review.tempData["asanaTaskGID"] == ""
|
113
|
+
return nil
|
114
|
+
end
|
115
|
+
|
116
|
+
return "https://app.asana.com/0/0/#{review.tempData["asanaTaskGID"]}/f"
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
def sendAsanaCommentMessage(review, message)
|
121
|
+
if review.tempData["asanaTaskGID"].nil? || review.tempData["asanaTaskGID"] == ""
|
122
|
+
return nil
|
123
|
+
end
|
124
|
+
|
125
|
+
path = "/tasks/#{review.tempData["asanaTaskGID"]}/stories"
|
126
|
+
uri = URI(asanaAPIURL+path)
|
127
|
+
https = Net::HTTP.new(uri.host, uri.port)
|
128
|
+
https.use_ssl = true
|
129
|
+
|
130
|
+
request = Net::HTTP::Post.new(uri)
|
131
|
+
request['Content-Type'] = 'application/json'
|
132
|
+
request.body = JSON.dump({"data": {"text": message}})
|
133
|
+
|
134
|
+
request['Authorization'] = "Bearer #{asanaToken}"
|
135
|
+
|
136
|
+
response = https.request(request).read_body
|
137
|
+
|
138
|
+
return result = JSON.parse(response)
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
class Payload
|
143
|
+
attr_accessor :channel, :thread_ts, :text, :unfurl_links, :unfurl_media
|
144
|
+
|
145
|
+
def as_json(options={})
|
146
|
+
{
|
147
|
+
channel: @channel,
|
148
|
+
thread_ts: @thread_ts,
|
149
|
+
text: @text,
|
150
|
+
unfurl_links: @unfurl_links,
|
151
|
+
unfurl_media: @unfurl_media
|
152
|
+
}
|
153
|
+
end
|
154
|
+
|
155
|
+
def to_json(*options)
|
156
|
+
as_json(*options).to_json(*options)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -42,7 +42,7 @@ class SlackProcessor < Processor
|
|
42
42
|
return reviews
|
43
43
|
end
|
44
44
|
|
45
|
-
|
45
|
+
pendingRequests = []
|
46
46
|
|
47
47
|
# Slack Message Limit: posting one message per second per channel
|
48
48
|
reviews.each_slice(attachmentGroupByNumber) do |reviewGroup|
|
@@ -71,12 +71,16 @@ class SlackProcessor < Processor
|
|
71
71
|
payload.attachments.append(attachment)
|
72
72
|
end
|
73
73
|
|
74
|
-
|
74
|
+
pendingRequests.append({"payload" => payload, "reviewGroup" => reviewGroup})
|
75
75
|
end
|
76
|
+
|
77
|
+
resultReviews = []
|
76
78
|
|
77
79
|
loop do
|
78
|
-
|
79
|
-
|
80
|
+
pendingRequest = pendingRequests.shift
|
81
|
+
payload = pendingRequest["payload"]
|
82
|
+
reviewGroup = pendingRequest["reviewGroup"]
|
83
|
+
|
80
84
|
result = request(payload)
|
81
85
|
if !result[:ok]
|
82
86
|
logger.logError(payload)
|
@@ -84,12 +88,17 @@ class SlackProcessor < Processor
|
|
84
88
|
if result[:message] == "ratelimited"
|
85
89
|
puts "[SlackProcessor] Reached Rate Limited, sleep 1 sec..."
|
86
90
|
sleep(1)
|
87
|
-
|
91
|
+
pendingRequests.insert(0, pendingRequest)
|
92
|
+
end
|
93
|
+
elsif !result[:ts].nil?
|
94
|
+
reviewGroup.each do |review|
|
95
|
+
review.tempData["slackTS"] = result[:ts]
|
96
|
+
review.tempData["slackChannelID"] = targetChannel
|
88
97
|
end
|
89
98
|
end
|
90
99
|
|
91
|
-
puts "[SlackProcessor] Send new Review messages, rest: #{
|
92
|
-
break if
|
100
|
+
puts "[SlackProcessor] Send new Review messages, rest: #{pendingRequests.length}"
|
101
|
+
break if pendingRequests.length < 1
|
93
102
|
end
|
94
103
|
|
95
104
|
return reviews
|
@@ -140,10 +149,10 @@ class SlackProcessor < Processor
|
|
140
149
|
res = http.request(req)
|
141
150
|
|
142
151
|
if isInCommingWebHook
|
143
|
-
return {"ok":res.body == "ok", "message":nil}
|
152
|
+
return {"ok":res.body == "ok", "message":nil, "ts": result['ts']}
|
144
153
|
else
|
145
154
|
result = JSON.parse(res.body)
|
146
|
-
return {"ok":result["ok"] == true, "message":result['error']}
|
155
|
+
return {"ok":result["ok"] == true, "message":result['error'], "ts": result['ts']}
|
147
156
|
end
|
148
157
|
|
149
158
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ZReviewTender
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.3.
|
4
|
+
version: 1.3.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ZhgChgLi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-08-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: net-http
|
@@ -64,6 +64,7 @@ files:
|
|
64
64
|
- lib/Processors/GoogleSheetProcessor.rb
|
65
65
|
- lib/Processors/GoogleTranslateProcessor.rb
|
66
66
|
- lib/Processors/ProcessorTemplate.rb
|
67
|
+
- lib/Processors/SlackAndAsanaConnector.rb
|
67
68
|
- lib/Processors/SlackProcessor.rb
|
68
69
|
- lib/ZLogger.rb
|
69
70
|
homepage: https://github.com/ZhgChgLi/ZReviewTender
|