ZReviewTender 1.3.7 → 1.3.8
Sign up to get free protection for your applications and to get access to all the features.
- 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
|