ZReviewTender 0.0.5 → 1.0.0

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: 4905d8141a111bf59431bb91ae97e09b7a1b1d55c311c0ccda08b3ef0c9b6fc8
4
- data.tar.gz: 346a8c5be0271f0be78bcfa0947333f7e2283988785d3ade883799fae38ed205
3
+ metadata.gz: 1d3c0a75805d2e7892786cf8a8fdc2269368f1156cdaa56c1ae514d5186725f8
4
+ data.tar.gz: 41ddf4524efab2749f8abf247eb6e919bf45861b01f36c0c3c8cab82fff86762
5
5
  SHA512:
6
- metadata.gz: b4a3eba5f67873c14d40bf96ec20820a2ba4e985722f42132b1983bfa60b40eb475088be78c958d13e2c1be43bd03b8965509efcafb5adc01757333d0d44720a
7
- data.tar.gz: db5d1acb0233c84fe06dbcb654dbda40ee97b25eb85f52d74dfd77ea7f34779f072ca25c4d2367b5500bf780a0ffbc5e49d7b8b592012e9f92387b96d032acc8
6
+ metadata.gz: c39778deb56d17742a16a6af0e00fb33505b15fb93df6dd8b57179c7fec459da55e73c5bbcddd732d10d556a729f654302abb93ff595cc8518405e807d729902
7
+ data.tar.gz: be8acddbf10119a6f35b4dc2adde623be8f176b623bd4a391fe5eec8ab616a55055429feb2a0105c73b1e76e3d70f5e4a22c47d7e448d720e54968b3aaab5d96
@@ -26,7 +26,7 @@ class AndroidFetcher < ReviewFetcher
26
26
  # init first time, send welcome message
27
27
  if latestCheckTimestamp == 0
28
28
  sendWelcomMessage()
29
- setPlatformLatestCheckTimestamp()
29
+ setPlatformLatestCheckTimestamp(Time.now().to_i)
30
30
  return
31
31
  end
32
32
 
@@ -52,7 +52,7 @@ class AndroidFetcher < ReviewFetcher
52
52
  end
53
53
  customerReviewPlatform = "Android #{customerReview.comments[0].user_comment.android_os_version}"
54
54
 
55
- if latestCheckTimestamp > customerReviewCreatedDateTimestamp
55
+ if latestCheckTimestamp >= customerReviewCreatedDateTimestamp
56
56
  break
57
57
  else
58
58
  url = "https://play.google.com/store/apps/details?id=#{config.packageName}&reviewId=#{customerReviewID}"
@@ -66,9 +66,9 @@ class AndroidFetcher < ReviewFetcher
66
66
 
67
67
  if reviews.length > 0
68
68
  reviews.sort! { |a, b| a.createdDateTimestamp <=> b.createdDateTimestamp }
69
+ setPlatformLatestCheckTimestamp(reviews.last.createdDateTimestamp)
70
+
69
71
  processReviews(reviews, platform)
70
72
  end
71
-
72
- setPlatformLatestCheckTimestamp()
73
73
  end
74
74
  end
data/lib/AppleFetcher.rb CHANGED
@@ -2,6 +2,7 @@ $lib = File.expand_path('../lib', File.dirname(__FILE__))
2
2
 
3
3
  require "Models/Review"
4
4
  require "Helper"
5
+ require "ZLogger"
5
6
  require "Models/ReviewFetcher"
6
7
  require "jwt"
7
8
  require "time"
@@ -14,6 +15,7 @@ class AppleFetcher < ReviewFetcher
14
15
  def initialize(config)
15
16
  @processors = []
16
17
  @config = config
18
+ @logger = ZLogger.new(config.baseExecutePath)
17
19
  @platform = 'Apple'
18
20
  @token = generateJWT()
19
21
  end
@@ -26,7 +28,7 @@ class AppleFetcher < ReviewFetcher
26
28
  # init first time, send welcome message
27
29
  if latestCheckTimestamp == 0
28
30
  sendWelcomMessage()
29
- setPlatformLatestCheckTimestamp()
31
+ setPlatformLatestCheckTimestamp(Time.now().to_i)
30
32
  return;
31
33
  end
32
34
 
@@ -34,12 +36,11 @@ class AppleFetcher < ReviewFetcher
34
36
 
35
37
  if reviews.length > 0
36
38
  reviews.sort! { |a, b| a.createdDateTimestamp <=> b.createdDateTimestamp }
37
- reviews = fullfillAppInfo(reviews)
39
+ setPlatformLatestCheckTimestamp(reviews.last.createdDateTimestamp)
38
40
 
41
+ reviews = fullfillAppInfo(reviews)
39
42
  processReviews(reviews, platform)
40
43
  end
41
-
42
- setPlatformLatestCheckTimestamp()
43
44
  end
44
45
 
45
46
  private
@@ -66,7 +67,7 @@ class AppleFetcher < ReviewFetcher
66
67
  customerReviewCreatedDateTimestamp = Time.parse(customerReviewCreatedDate).to_i
67
68
  end
68
69
 
69
- if latestCheckTimestamp > customerReviewCreatedDateTimestamp
70
+ if latestCheckTimestamp >= customerReviewCreatedDateTimestamp
70
71
  customerReviewsLink = nil
71
72
  break
72
73
  else
@@ -167,7 +168,7 @@ class AppleFetcher < ReviewFetcher
167
168
  raise "Could not connect to api.appstoreconnect.apple.com, error message: #{response}"
168
169
  else
169
170
  @token = generateJWT()
170
- Helper.logWarn("JWT Expired, refresh a new one. (#{retryCount + 1})")
171
+ logger.logWarn("JWT Expired, refresh a new one. (#{retryCount + 1})")
171
172
  return request(url, retryCount + 1)
172
173
  end
173
174
  else
data/lib/Helper.rb CHANGED
@@ -1,7 +1,5 @@
1
1
  $lib = File.expand_path('../', File.dirname(__FILE__))
2
2
 
3
- require "logger"
4
-
5
3
  class Helper
6
4
  def self.unwrapRequiredParameter(obj, key)
7
5
  if obj[key].nil?
@@ -24,19 +22,4 @@ class Helper
24
22
  Dir.mkdir(currentDir) unless File.exists?(currentDir)
25
23
  end while dirs.length > 0
26
24
  end
27
-
28
- def self.logError(message)
29
- logger = Logger.new(STDOUT)
30
- logger.error("#{caller[0]}: #{message}")
31
- end
32
-
33
- def self.logWarn(message)
34
- logger = Logger.new(STDOUT)
35
- logger.warning("#{caller[0]}: #{message}")
36
- end
37
-
38
- def self.logInfo(message)
39
- logger = Logger.new(STDOUT)
40
- logger.info("#{caller[0]}: #{message}")
41
- end
42
25
  end
@@ -8,7 +8,7 @@ require "time"
8
8
 
9
9
  class ReviewFetcher
10
10
 
11
- attr_accessor :config, :platform, :processors
11
+ attr_accessor :config, :platform, :processors, :logger
12
12
 
13
13
  def execute()
14
14
 
@@ -31,14 +31,14 @@ class ReviewFetcher
31
31
  end
32
32
  end
33
33
 
34
- def setPlatformLatestCheckTimestamp()
35
- basePath = "#{config.baseExecutePath}/.cache"
34
+ def setPlatformLatestCheckTimestamp(timestamp)
35
+ basePath = "#{config.baseExecutePath}/latestCheckTimestamp/"
36
36
  Helper.createDirIfNotExist(basePath)
37
- File.open("#{basePath}/#{platform}-latestCheckTimestamp", 'w') { |file| file.write(Time.now().to_i) }
37
+ File.open("#{basePath}/#{platform}", 'w') { |file| file.write(timestamp) }
38
38
  end
39
39
 
40
40
  def getPlatformLatestCheckTimestamp()
41
- filePath = "#{config.baseExecutePath}/.cache/#{platform}-latestCheckTimestamp"
41
+ filePath = "#{config.baseExecutePath}/latestCheckTimestamp/#{platform}"
42
42
  if File.exists?(filePath)
43
43
  return File.read(filePath).to_i
44
44
  else
@@ -0,0 +1,54 @@
1
+ $lib = File.expand_path('../lib', File.dirname(__FILE__))
2
+
3
+ require "Models/Review"
4
+ require "Models/Processor"
5
+ require "Helper"
6
+
7
+ class FilterProcessor < Processor
8
+
9
+ attr_accessor :keywordsInclude, :ratingsInclude, :territoriesInclude
10
+
11
+ def initialize(config, configFilePath, baseExecutePath)
12
+ @config = config
13
+ @configFilePath = configFilePath
14
+ @baseExecutePath = baseExecutePath
15
+
16
+ @keywordsInclude = []
17
+ if !config["keywordsInclude"].nil?
18
+ @keywordsInclude = config["keywordsInclude"]
19
+ end
20
+
21
+ @ratingsInclude = []
22
+ if !config["ratingsInclude"].nil?
23
+ @ratingsInclude = config["ratingsInclude"]
24
+ end
25
+
26
+ @territoriesInclude = []
27
+ if !config["territoriesInclude"].nil?
28
+ @territoriesInclude = config["territoriesInclude"]
29
+ end
30
+ end
31
+
32
+ def processReviews(reviews, platform)
33
+ if reviews.length < 1
34
+ return reviews
35
+ end
36
+
37
+ if ratingsInclude.length > 0
38
+ reviews = reviews.select{ |review| ratingsInclude.map{ |rating| rating.to_i }.include? review.rating }
39
+ end
40
+
41
+ if territoriesInclude.length > 0
42
+ reviews = reviews.select{ |review| territoriesInclude.map{ |territory| territory.upcase }.include? review.territory.upcase }
43
+ end
44
+
45
+ if keywordsInclude.length > 0
46
+ keywordsInclude.select{ |keywordsInclude| keywordsInclude != "" }.each do |keywordInclude|
47
+ reviews = reviews.select{ |review| review.body.include? keywordInclude }
48
+ end
49
+ end
50
+
51
+ return reviews
52
+ end
53
+
54
+ end
@@ -9,7 +9,7 @@ require "google/cloud/translate/v2"
9
9
 
10
10
  class GoogleTranslateProcessor < Processor
11
11
 
12
- attr_accessor :client, :targetLang, :whiteListTerritories
12
+ attr_accessor :client, :targetLang, :territoriesExclude
13
13
 
14
14
  def initialize(config, configFilePath, baseExecutePath)
15
15
  @config = config
@@ -26,15 +26,19 @@ class GoogleTranslateProcessor < Processor
26
26
  ENV["TRANSLATE_CREDENTIALS"] = keyFilePath
27
27
  @client = Google::Cloud::Translate::V2.new
28
28
  @targetLang = Helper.unwrapRequiredParameter(config, "googleTranslateTargetLang")
29
- @whiteListTerritories = []
30
- if !config['googleTranslateWhiteListTerritories'].nil? && config['googleTranslateWhiteListTerritories'].length > 0
31
- @whiteListTerritories = config['googleTranslateWhiteListTerritories']
29
+ @territoriesExclude = []
30
+ if !config['googleTranslateTerritoriesExclude'].nil? && config['googleTranslateTerritoriesExclude'].length > 0
31
+ @territoriesExclude = config['googleTranslateTerritoriesExclude']
32
32
  end
33
33
  end
34
34
 
35
35
  def processReviews(reviews, platform)
36
+ if reviews.length < 1
37
+ return reviews
38
+ end
39
+
36
40
  reviews.each_index do |index|
37
- if whiteListTerritories.include? reviews[index].territory
41
+ if territoriesExclude.include? reviews[index].territory
38
42
  next
39
43
  end
40
44
 
@@ -0,0 +1,38 @@
1
+ $lib = File.expand_path('../lib', File.dirname(__FILE__))
2
+
3
+ require "Models/Review"
4
+ require "Models/Processor"
5
+ require "Helper"
6
+ require "ZLogger"
7
+
8
+ # Add to config.yml:
9
+ #
10
+ # processors:
11
+ # - ProcessorTemplate:
12
+ # class: "ProcessorTemplate"
13
+ # parameter1: "value"
14
+ # parameter2: "value"
15
+ # parameter3: "value"
16
+ # ...
17
+ #
18
+
19
+ class ProcessorTemplate < Processor
20
+
21
+ def initialize(config, configFilePath, baseExecutePath)
22
+ # init Processor
23
+ # get paraemter from config e.g. config["parameter1"]
24
+ # configFilePath: file path of config file (apple.yml/android.yml)
25
+ # baseExecutePath: user excute path
26
+ end
27
+
28
+ def processReviews(reviews, platform)
29
+ if reviews.length < 1
30
+ return reviews
31
+ end
32
+
33
+ ## do what your want to do with reviews...
34
+
35
+ ## return result reviews
36
+ return reviews
37
+ end
38
+ end
@@ -3,23 +3,30 @@ $lib = File.expand_path('../lib', File.dirname(__FILE__))
3
3
  require "Models/Review"
4
4
  require "Models/Processor"
5
5
  require "Helper"
6
+ require "ZLogger"
6
7
  require "net/http"
7
8
  require "json"
8
9
  require "time"
9
10
 
10
11
  class SlackProcessor < Processor
11
12
 
12
- attr_accessor :botToken, :inCommingWebHookURL, :targetChannel, :timeZoneOffset
13
+ attr_accessor :botToken, :inCommingWebHookURL, :targetChannel, :timeZoneOffset, :attachmentGroupByNumber, :logger
13
14
 
14
15
  def initialize(config, configFilePath, baseExecutePath)
15
16
  @config = config
16
17
  @configFilePath = configFilePath
17
18
  @baseExecutePath = baseExecutePath
19
+ @logger = ZLogger.new(baseExecutePath)
18
20
 
19
21
  @botToken = config["slackBotToken"]
20
22
  @inCommingWebHookURL = config["slackInCommingWebHookURL"]
21
23
  @targetChannel = config["slackBotTargetChannel"]
22
24
  @timeZoneOffset = Helper.unwrapRequiredParameter(config, "slackTimeZoneOffset")
25
+ @attachmentGroupByNumber = 1
26
+
27
+ if !config['slackAttachmentGroupByNumber'].nil? && config['slackAttachmentGroupByNumber'] != "" && config['slackAttachmentGroupByNumber'].to_i > 0 && config['slackAttachmentGroupByNumber'].to_i < 100
28
+ @attachmentGroupByNumber = config['slackAttachmentGroupByNumber'].to_i
29
+ end
23
30
 
24
31
  if (botToken.nil? && inCommingWebHookURL.nil?) || (botToken == "" && inCommingWebHookURL == "")
25
32
  raise "must specify slackBotToken or slackInCommingWebHookURL in SlackProcessor."
@@ -29,36 +36,55 @@ class SlackProcessor < Processor
29
36
  end
30
37
 
31
38
  def processReviews(reviews, platform)
39
+ if reviews.length < 1
40
+ return reviews
41
+ end
42
+
43
+ pendingPayloads = []
32
44
 
33
- # 1 review 1 message
34
- reviews.each do |review|
45
+ # Slack Message Limit: posting one message per second per channel
46
+ reviews.each_slice(attachmentGroupByNumber) do |reviewGroup|
35
47
  payload = Payload.new()
36
48
  payload.attachments = []
37
-
38
- attachment = Payload::Attachment.new()
39
49
 
40
- stars = "★" * review.rating + "☆" * (5 - review.rating)
41
- color = review.rating >= 4 ? "good" : (review.rating > 2 ? "warning" : "danger")
42
- date = Time.at(review.createdDateTimestamp).getlocal(timeZoneOffset)
43
-
44
- title = "#{stars}"
45
- if !review.title.nil?
46
- title = "#{review.title} - #{stars}"
47
- end
50
+ reviewGroup.each do |review|
51
+ attachment = Payload::Attachment.new()
52
+
53
+ stars = "★" * review.rating + "☆" * (5 - review.rating)
54
+ color = review.rating >= 4 ? "good" : (review.rating > 2 ? "warning" : "danger")
55
+ date = Time.at(review.createdDateTimestamp).getlocal(timeZoneOffset)
48
56
 
49
- attachment.color = color
50
- attachment.author_name = review.userName
51
- attachment.fallback = title
52
- attachment.title = title
53
- attachment.text = review.body
54
- attachment.footer = "#{platform} - #{review.platform} - #{review.appVersion} - #{review.territory} - <#{review.url}|#{date}>"
55
-
56
- payload.attachments.append(attachment)
57
+ title = "#{stars}"
58
+ if !review.title.nil?
59
+ title = "#{review.title} - #{stars}"
60
+ end
61
+
62
+ attachment.color = color
63
+ attachment.author_name = review.userName
64
+ attachment.fallback = title
65
+ attachment.title = title
66
+ attachment.text = review.body
67
+ attachment.footer = "#{platform} - #{review.platform} - #{review.appVersion} - #{review.territory} - <#{review.url}|#{date}>"
68
+
69
+ payload.attachments.append(attachment)
70
+ end
71
+
72
+ pendingPayloads.append(payload)
73
+ end
74
+
75
+ loop do
76
+ payload = pendingPayloads.shift
57
77
 
58
78
  result = request(payload)
59
- if !result
60
- Helper.logError(result)
79
+ if !result[:ok]
80
+ logger.logError(result)
81
+ if result[:message] == "ratelimited"
82
+ sleep(1)
83
+ pendingPayloads.insert(0, payload)
84
+ end
61
85
  end
86
+
87
+ break if pendingPayloads.length < 1
62
88
  end
63
89
 
64
90
  return reviews
@@ -108,10 +134,10 @@ class SlackProcessor < Processor
108
134
  res = http.request(req)
109
135
 
110
136
  if isInCommingWebHook
111
- return res.body == "ok"
137
+ return {"ok":res.body == "ok", "message":nil}
112
138
  else
113
139
  result = JSON.parse(res.body)
114
- return result["ok"] == true
140
+ return {"ok":result["ok"] == true, "message":result['error']}
115
141
  end
116
142
 
117
143
  end
data/lib/ZLogger.rb ADDED
@@ -0,0 +1,33 @@
1
+ $lib = File.expand_path('../', File.dirname(__FILE__))
2
+
3
+ require "logger"
4
+
5
+ class ZLogger
6
+
7
+ attr_accessor :logger
8
+
9
+ def initialize(baseExecutePath)
10
+ @logger = Logger.new("#{baseExecutePath}/execute.log")
11
+ end
12
+
13
+ def logError(message)
14
+ result = "#{caller[0]}: #{message}"
15
+
16
+ puts "Error: #{result}"
17
+ logger.error(result)
18
+ end
19
+
20
+ def logWarn(message)
21
+ result = "#{caller[0]}: #{message}"
22
+
23
+ puts "Warning: #{result}"
24
+ logger.warn(result)
25
+ end
26
+
27
+ def logInfo(message)
28
+ result = "#{caller[0]}: #{message}"
29
+
30
+ puts "Info: #{result}"
31
+ logger.info(result)
32
+ end
33
+ 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: 0.0.5
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ZhgChgLi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-08-07 00:00:00.000000000 Z
11
+ date: 2022-08-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: net-http
@@ -82,8 +82,11 @@ files:
82
82
  - lib/Models/Processor.rb
83
83
  - lib/Models/Review.rb
84
84
  - lib/Models/ReviewFetcher.rb
85
+ - lib/Processors/FilterProcessor.rb
85
86
  - lib/Processors/GoogleTranslateProcessor.rb
87
+ - lib/Processors/ProcessorTemplate.rb
86
88
  - lib/Processors/SlackProcessor.rb
89
+ - lib/ZLogger.rb
87
90
  homepage: https://github.com/ZhgChgLi/ZReviewTender
88
91
  licenses:
89
92
  - MIT