E36e48bb9265
ℹ️ℹ️ℹ️ The following content is translated by OpenAI.
Click here to view the original Chinese version. | 點此查看本文中文版
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
title: "ZReviewTender — A Free and Open Source App Reviews Monitoring Bot"
author: "ZhgChgLi"
date: 2022-08-10T11:56:05.731+0000
last_modified_at: 2024-09-26T12:48:18.465+0000
categories: ["ZRealm Dev."]
tags: ["ios-app-development", "app-store", "google-play", "app-review", "automation"]
description: "Real-time monitoring of the latest app reviews with instant feedback to enhance collaboration efficiency and consumer satisfaction."
image:
path: /assets/e36e48bb9265/1*DjHhZ7Yq-rE3LkFDiYW9lg.jpeg
render_with_liquid: false
---
### ZReviewTender — A Free and Open Source App Reviews Monitoring Bot
Real-time monitoring of the latest app reviews with instant feedback to enhance collaboration efficiency and consumer satisfaction.
{:target="_blank"} / [ZReviewTender](https://github.com/ZhgChgLi/ZReviewTender){:target="_blank"}](/assets/e36e48bb9265/1*DjHhZ7Yq-rE3LkFDiYW9lg.jpeg)
[ZhgChgLi](https://github.com/ZhgChgLi){:target="_blank"} / [ZReviewTender](https://github.com/ZhgChgLi/ZReviewTender){:target="_blank"}
#### [ZhgChgLi](https://github.com/ZhgChgLi){:target="_blank"} / [ZReviewTender](https://github.com/ZhgChgLi/ZReviewTender){:target="_blank"}

App Reviews to Slack Channel
[**ZReviewTender**](https://github.com/ZhgChgLi/ZReviewTender){:target="_blank"} **—** Automatically monitors user reviews for iOS/macOS apps on the App Store and Android apps on Google Play, providing continuous integration tools that integrate into team workflows to enhance collaboration efficiency and consumer satisfaction.
[](https://github.com/ZhgChgLi/ZReviewTender){:target="_blank"}
### Key Features
- Retrieve review lists for iOS/macOS apps on the App Store and Android apps on Google Play, filtering for the latest reviews that haven't been crawled yet.
- \[Default Feature\] Forward the latest crawled reviews to Slack, with clickable message timestamps for quick access to the backend for responding to reviews.
- \[Default Feature\] Support for automatic translation of reviews in non-specified languages or regions using the Google Translate API.
- \[Default Feature\] Automatically log reviews to Google Sheets.
- Flexible expansion: In addition to the default features, you can develop and integrate custom functionalities according to your team's workflow, e.g., forwarding reviews to Discord, Line, Telegram, etc.
- Use timestamps to record crawl locations, preventing duplicate reviews from being crawled.
- Support filtering options to specify which ratings, keywords, or regions/languages to crawl.
- Apple provides a stable and reliable source of App Store app review data based on the [**new App Store Connect API**](https://developer.apple.com/documentation/appstoreconnectapi/list_all_customer_reviews_for_an_app){:target="_blank"}, eliminating the unreliability of previous XML data or the need for manual maintenance of Fastlane Spaceship sessions.
- Android uses the official AndroidpublisherV3 API to fetch review data.
- Supports deployment using GitHub Repo with GitHub Actions, allowing you to quickly and freely set up the ZReviewTender App Reviews bot.
- 100% Ruby @ [RubyGem](https://rubygems.org/gems/ZReviewTender){:target="_blank"}
### TL;DR \[2024/09/27\] Update
> [**_For a quick deployment guide, please refer to the latest article: \[Quick Start!\] GitHub Action x ZReviewTender - Free and Fast Deployment of Your App Store Review Monitoring Bot._**](../0095528cf875/)
#### Comparison with Similar Services

#### Example of App Reviews Workflow Integration (in Pinkoi)
**Problem:**

Reviews in the marketplace are crucial for products, but managing them is a repetitive and manual task.
It often requires manual checking for new reviews and forwarding any customer service issues to the support team, which is tedious and labor-intensive.

With the ZReviewTender review bot, reviews are automatically forwarded to a Slack channel, allowing everyone to quickly receive the latest review information, track discussions in real-time, and keep the entire team informed about user feedback and suggestions.
For more information, refer to: [2021 Pinkoi Tech Career Talk — Secrets of an Efficient Engineering Team](../11f6c8568154/).
### Deployment — Using Only Default Features
If you only need the default features of ZReviewTender (to Slack/Google Translate/Filter), you can use the following quick deployment method.
ZReviewTender is packaged and published on [RubyGems](https://rubygems.org/gems/ZReviewTender){:target="_blank"}, allowing you to easily install and use ZReviewTender.
#### \[Recommended\] Directly Use GitHub Repo Template for Deployment
- No need for any hosting space ✅
- No environmental requirements ✅
- No need to understand engineering principles ✅
- Complete configuration of the config file to finish deployment ✅
- Deployment can be completed in 8 steps ✅
- Completely free ✅
GitHub Actions provides each account with 2,000+ minutes/month of execution time, and running ZReviewTender to fetch reviews takes about 15-30 seconds.
By default, it runs every 6 hours, totaling about 4 times a day, **consuming only about 60 minutes of quota per month**.
You can create unlimited private GitHub repos for free.
1. Go to the ZReviewTender Template Repo: [**ZReviewTender-deploy-with-github-action**](https://github.com/ZhgChgLi/ZReviewTender-deploy-with-github-action){:target="_blank"}

Click the "Use this template" button in the upper right corner.
2. Create a Repo

- Repository name: Enter your desired repo project name
- Access: **Private**
> ⚠️⚠️ Please ensure to create a **Private Repo** ⚠️⚠️
> **Because you will upload configuration and secret keys to the project.**
Finally, click the "Create repository from template" button at the bottom.
3. Confirm that your created Repo is a Private Repo

Check that the repo name in the upper right corner has a "🔒" and a Private label.
If not, it means you created a **Public Repo, which is very risky**. Please go to the "Settings" tab at the top -> "General" -> bottom "Danger Zone" -> "Change repository visibility" -> "Make private" to revert it back to a Private Repo.
4. Wait for Project Initialization to Succeed
You can check the badge in the README on the repo homepage:

If it says "passing," it means initialization was successful.
Alternatively, click the "Actions" tab at the top -> wait for the "Init ZReviewTender" workflow to complete:

The execution status will change to "✅ Init ZReviewTender" -> Project initialization successful.
5. Confirm that the initialization files and directories were created correctly

Click the "Code" tab at the top to return to the project directory. If the project initialization was successful, you should see:
- Directory: `config/`
- File: `config/android.yml`
- File: `config/apple.yml`
- Directory: `latestCheckTimestamp/`
- File: `latestCheckTimestamp/.keep`
6. Complete Configuration of `android.yml` & `apple.yml`
Enter the `config/` directory to complete the configuration of the `android.yml` & `apple.yml` files.

Click to enter the config YML file you want to edit, then click the "✏️" button in the upper right corner to edit the file.
Refer to the " **Configuration** " section at the bottom of this article to complete the configuration of `android.yml` & `apple.yml`.

After editing, you can save the settings directly by clicking "Commit changes" below.
Upload the corresponding key files to the `config/` directory:

In the `config/` directory, select "Add file" -> "Upload files" in the upper right corner.

Upload the corresponding keys and external file paths specified in the config YML to the `config/` directory by dragging the files into the "upper block" -> wait for the files to finish uploading -> then click "Commit changes" below to save.
After uploading, return to the `/config` directory to check if the files are stored and uploaded correctly.

7. Initialize ZReviewTender (Manually Trigger Execution Once)

Click the "Actions" tab at the top -> select "ZReviewTender" on the left -> choose the "Run workflow" button on the right -> click the "Run workflow" button to execute ZReviewTender once.
**After clicking, refresh the page** to see:

Clicking "ZReviewTender" will take you to view the execution status.

Expand the " `Run ZreviewTender -r` " section to view the execution log.
Here you may see an error because I haven't configured my config YML files correctly.
Go back and adjust the android/apple config YML, then return to step 6 to trigger execution again.

Check the log in the " `ZReviewTender -r` " section to confirm successful execution!

The Slack channel designated to receive the latest review messages will also show an init success message 🎉
8. **Done!** 🎉 🎉 🎉

Configuration is complete! From now on, it will automatically crawl the latest reviews every 6 hours and forward them to your Slack channel!
You can check the latest execution status at the top of the README on the repo homepage:

If an error occurs, it indicates that there was an issue during execution. Please go to Actions -> ZReviewTender to view the logs; if there are unexpected errors, please [**create an issue**](https://github.com/ZhgChgLi/ZReviewTender/issues){:target="_blank"} **with the log information, and it will be fixed as soon as possible!**
> ❌❌❌ If an error occurs during execution, GitHub will also send an email notification, so you don't have to worry about the bot failing without anyone noticing!
#### Adjusting GitHub Actions
You can also configure the execution rules for GitHub Actions according to your needs.
Click the "Actions" tab at the top -> select "ZReviewTender" on the left -> click the " `ZReviewTender.yml` " button in the upper right corner.


Click the "✏️" button in the upper right corner to edit the file.

**There are two parameters you can adjust:**
**cron**: Set how often to check for new reviews. The default is `15 */6 * * *`, which means it will run once every 6 hours at 15 minutes past the hour.

You can refer to [crontab.guru](https://crontab.guru/#15_*/6_*_*_*){:target="_blank"} to configure it according to your needs.
> **Please note:**
> 1. GitHub Actions uses UTC time zone.
> 2. The higher the execution frequency, the more GitHub Action execution quota will be consumed.
**run**: Set the command to execute. You can refer to the " **Execution** " section at the bottom of this article. The default is `ZReviewTender -r`.
- Default execution for Android App & Apple (iOS/macOS App): `ZReviewTender -r`
- Execute only Android App: `ZReviewTender -g`
- Execute only Apple (iOS/macOS App): `ZReviewTender -a`
After editing, click the "Start commit" button in the upper right corner and select "Commit changes" to save the settings.
#### Manually Trigger Execution of ZReviewTender
Refer to the previous section "6. Initialize ZReviewTender (Manually Trigger Execution Once)."
#### Using Gem Installation
If you are familiar with Gems, you can directly use the following command to install `ZReviewTender`:
```bash
gem install ZReviewTender
Using Gem Installation (If You Are Not Familiar with Ruby/Gems)
If you are not familiar with Ruby or Gems, you can follow these step-by-step instructions to install ZReviewTender
:
- Although macOS comes with Ruby, it is recommended to use rbenv or rvm to install a new Ruby version and manage Ruby versions (I use
2.6.5
). - Use rbenv or rvm to install Ruby 2.6.5 and switch to the Ruby version managed by rbenv/rvm.
- Use
which ruby
to confirm that the current Ruby being used is not the system Ruby located at/usr/bin/ruby
. - Once the Ruby environment is set up correctly, use the following command to install
ZReviewTender
:
1
gem install ZReviewTender
Deployment — Custom Functionality Expansion
Manual
- Clone the ZReviewTender source code.
- Confirm and improve your Ruby environment.
- Enter the directory and run
bundle install
to install the necessary dependencies for ZReviewTender.
The method for creating the processor can be referenced in the later sections of this article.
Configuration
ZReviewTender — Use YAML files to configure the Apple/Google review bot.
[Recommended] Directly use the execution command at the bottom of this article — “Generate Configuration Files”:
1
ZReviewTender -i
This will directly generate blank apple.yml
& android.yml
configuration files.
Apple (iOS/macOS App)
Refer to the apple.example.yml
file:
⚠️ After downloading
apple.example.yml
, remember to rename the file toapple.yml
.
The apple.yml
file should look like this:
1
2
3
4
5
6
platform: 'apple'
appStoreConnectP8PrivateKeyFilePath: '' # APPLE STORE CONNECT API PRIVATE .p8 KEY File Path
appStoreConnectP8PrivateKeyID: '' # APPLE STORE CONNECT API PRIVATE KEY ID
appStoreConnectIssueID: '' # APPLE STORE CONNECT ISSUE ID
appID: '' # APP ID
...
appStoreConnectIssueID:
- App Store Connect -> Keys -> App Store Connect API
- Issuer ID:
appStoreConnectIssueID
appStoreConnectP8PrivateKeyID & appStoreConnectP8PrivateKeyFilePath:
- Name:
ZReviewTender
- Access:
App Manager
- appStoreConnectP8PrivateKeyID:
Key ID
- appStoreConnectP8PrivateKeyFilePath:
/AuthKey_XXXXXXXXXX.p8
— Download the API Key and place the file in the same directory as the config YML.
appID:
appID: App Store Connect -> App Store -> General -> App Information -> Apple ID
GCP Service Account
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
The Google API services used by ZReviewTender (for fetching store reviews, Google Translate, and Google Sheets) are authenticated using a Service Account.
First, follow the [**official steps to create a GCP & Service Account**](https://cloud.google.com/docs/authentication/production#create_service_account){:target="_blank"} to download and save the GCP Service Account credentials key (`*.json`).
- To use the automatic translation feature, ensure that the `Cloud Translation API` is enabled in GCP and that the Service Account has been granted access.
- To use the feature that logs to Google Sheets, make sure that the `Google Sheets API` and `Google Drive API` are enabled in GCP, and that the Service Account has been granted access.

#### Google Play Console (Android App)
Refer to the android.example.yml file:
[](https://github.com/ZhgChgLi/ZReviewTender/blob/main/config/android.example.yml){:target="_blank"}
> ⚠️ After downloading `android.example.yml`, remember to rename the file to `android.yml`.
android.yml:
```yaml
platform: 'android'
packageName: '' # Android App Package Name
keyFilePath: '' # Google Android Publisher API Credential .json File Path
playConsoleDeveloperAccountID: '' # Google Console Developer Account ID
playConsoleAppID: '' # Google Console App ID
......
packageName:
You can find the packageName: com.XXXXX
in the Google Play Console -> Dashboard -> App.
playConsoleDeveloperAccountID & playConsoleAppID:
You can obtain these from the URL of the App page in the Google Play Console -> Dashboard:
These will be used to create links to review messages, allowing the team to quickly access the backend review response page.
keyFilePath:
This is the most important information, the GCP Service Account credentials key (*.json
).
You need to follow the steps in the official documentation to create a Google Cloud Project & Service Account, and then go to Google Play Console -> Setup -> API Access to enable the Google Play Android Developer API
and link the project. Click to download the JSON key for the Service Account from GCP.
Example content of the JSON key:
gcp_key.json:
1
2
3
4
5
6
7
8
9
10
11
12
{
"type": "service_account",
"project_id": "XXXX",
"private_key_id": "XXXX",
"private_key": "-----BEGIN PRIVATE KEY-----\nXXXX\n-----END PRIVATE KEY-----\n",
"client_email": "XXXX@XXXX.iam.gserviceaccount.com",
"client_id": "XXXX",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/XXXX.iam.gserviceaccount.com"
}
- keyFilePath:
/gcp_key.json
is the path to the key file; place the file in the same directory as the config yml.
Processors
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
processors:
- FilterProcessor:
class: "FilterProcessor"
enable: true # enable
keywordsInclude: [] # keywords you want to filter out
ratingsInclude: [] # ratings you want to filter out
territoriesInclude: [] # territories you want to filter out
- GoogleTranslateProcessor: # Google Translate Processor, will translate review text to your language; you can remove this block if you don't need it.
class: "GoogleTranslateProcessor"
enable: false # enable
googleTranslateAPIKeyFilePath: '' # Google Translate API Credential .json File Path
googleTranslateTargetLang: 'zh-TW' # Translate to what Language
googleTranslateTerritoriesExclude: ["TWN","CHN"] # Review origin Territory (language) that you don't want to translate.
- SlackProcessor: # Slack Processor, resend App Review to Slack.
class: "SlackProcessor"
enable: true # enable
slackTimeZoneOffset: "+08:00" # Review Created Date TimeZone
slackAttachmentGroupByNumber: "1" # 1~100, how many review messages in 1 slack message.
slackBotToken: "" # Slack Bot Token, send slack messages through Slack Bot.
slackBotTargetChannel: "" # Slack Bot Token, send slack messages through Slack Bot. (recommended, first priority)
slackInCommingWebHookURL: "" # Slack Incoming WebHook URL, send slack messages through Incoming WebHook, not recommended, deprecated.
...More Processors...
ZReviewTender comes with four processors, and the order of execution affects the data processing flow: FilterProcessor -> GoogleTranslateProcessor -> SlackProcessor -> GoogleSheetProcessor.
FilterProcessor:
Filters the fetched reviews based on specified criteria, processing only those that meet the conditions.
- class:
FilterProcessor
does not need adjustments; it points to lib/Processors/FilterProcessor.rb
. - enable:
true
/false
to enable or disable this processor. - keywordsInclude: [“
keyword1
”,“keyword2
”…] filters reviews containing these keywords. - ratingsInclude: [
1
,2
…] filters reviews with these rating scores. - territoriesInclude: [“
zh-hant
”,”TWN
”…] filters reviews from these regions (Apple) or languages (Android).
GoogleTranslateProcessor:
Translates reviews into the specified language.
- class:
GoogleTranslateProcessor
does not need adjustments; it points to lib/Processors/GoogleTranslateProcessor.rb
. - enable:
true
/false
to enable or disable this processor. - googleTranslateAPIKeyFilePath:
/gcp_key.json
is the path to the GCP Service Account credentials key file (*.json
); place the file in the same directory as the config yml. Refer to the example JSON key above. (Please ensure that the service account associated with this JSON key has access to theCloud Translation API
.) - googleTranslateTargetLang:
zh-TW
,en
, etc. is the target translation language. - googleTranslateTerritoriesExclude: [“
zh-hant
”,”TWN
”…] are regions (Apple) or languages (Android) that do not need translation.
SlackProcessor:
Forwards reviews to Slack.
- class:
SlackProcessor
does not need adjustments; it points to lib/Processors/SlackProcessor.rb
. - enable:
true
/false
to enable or disable this processor. - slackTimeZoneOffset:
+08:00
is the timezone for displaying review times. - slackAttachmentGroupByNumber:
1
sets how many reviews to combine into a single message for faster sending; by default, 1 review corresponds to 1 Slack message. - slackBotToken:
xoxb-xxxx-xxxx-xxxx
is the Slack Bot Token; it is recommended to create a Slack Bot with thepostMessages
scope and use it to send Slack messages. - slackBotTargetChannel:
CXXXXXX
is the group ID ( not the group name ) where the Slack Bot will send messages; you need to add your Slack Bot to that group. - slackInCommingWebHookURL:
https://hooks.slack.com/services/XXXXX
uses the old Incoming WebHook URL to send messages to Slack. Note! Slack does not recommend using this method anymore.
Please note, this is a legacy custom integration — an outdated way for teams to integrate with Slack. These integrations lack newer features and will be deprecated and possibly removed in the future. We do not recommend their use. Instead, we suggest that you check out their replacement: Slack apps.
- Between slackBotToken and slackInCommingWebHookURL, the SlackProcessor will prefer to use slackBotToken.
GoogleSheetProcessor
Records reviews to Google Sheets.
- class:
GoogleSheetProcessor
does not need adjustments; it points to lib/Processors/GoogleSheetProcessor.rb
. - enable:
true
/false
to enable or disable this processor. - googleSheetAPIKeyFilePath:
/gcp_key.json
is the path to the GCP Service Account credentials key file (*.json
); place the file in the same directory as the config yml. Refer to the example JSON key above. (Please ensure that the service account associated with this JSON key has access to theGoogle Sheets API
andGoogle Drive API
.) - googleSheetTimeZoneOffset:
+08:00
is the timezone for displaying review times. - googleSheetID:
Google Sheet ID
can be obtained from the Google Sheet URL: https://docs.google.com/spreadsheets/d/googleSheetID
/ - googleSheetName: Sheet name, e.g.,
Sheet1
. - keywordsInclude: [“
keyword1
”,“keyword2
”…] filters reviews containing these keywords. - ratingsInclude: [
1
,2
…] filters reviews with these rating scores. - territoriesInclude: [“
zh-hant
”,”TWN
”…] filters reviews from these regions (Apple) or languages (Android). - values: [ ] is the combination of review information fields.
1
2
3
4
5
6
7
8
9
10
%TITLE% Review Title
%BODY% Review Content
%RATING% Review Score 1~5
%PLATFORM% Review Source Platform Apple or Android
%ID% Review ID
%USERNAME% Reviewer
%URL% Review Link
%TERRITORY% Review Region (Apple) or Review Language (Android)
%APPVERSION% Version of the App being reviewed
%CREATEDDATE% Review Creation Date
For example, if my Google Sheet columns are as follows:
1
Review Score, Review Title, Review Content, Review Information
Then values can be set as:
1
values: ["%TITLE%","%BODY%","%RATING%","%PLATFORM% - %APPVERSION%"]
Custom Processor to Integrate Your Workflow
If you need a custom processor, please switch to manual deployment, as the gem version of ZReviewTender is packaged and cannot be dynamically adjusted.
You can refer to lib/Processors/ProcessorTemplate.rb to create your extension:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
$lib = File.expand_path('../lib', File.dirname(__FILE__))
require "Models/Review"
require "Models/Processor"
require "Helper"
require "ZLogger"
# Add to config.yml:
#
# processors:
# - ProcessorTemplate:
# class: "ProcessorTemplate"
# parameter1: "value"
# parameter2: "value"
# parameter3: "value"
# ...
#
class ProcessorTemplate < Processor
def initialize(config, configFilePath, baseExecutePath)
# init Processor
# get parameter from config e.g. config["parameter1"]
# configFilePath: file path of config file (apple.yml/android.yml)
# baseExecutePath: user execute path
end
def processReviews(reviews, platform)
if reviews.length < 1
return reviews
end
## do what you want to do with reviews...
## return result reviews
return reviews
end
end
initialize will provide:
- config Object: corresponding to the values in the config yml.
- configFilePath: the path of the config yml file.
- baseExecutePath: the path where the user executes ZReviewTender.
processReviews(reviews, platform):
After fetching new reviews, this function will be called to allow the processor to handle them; after processing, please return the resulting reviews.
The review data structure is defined in lib/Models/ Review.rb.
Notes
XXXterritorXXX
parameters:
- Apple uses regions: TWM/JPN…
- Android uses languages: zh-hant/en/…
If you do not need a certain processor: You can set enable: false
or simply remove that processor’s config block.
The execution order of processors can be adjusted according to your needs: e.g., execute Filter first, then translation, then Slack, and finally log to Google Sheets…
Execution
⚠️ Use the gem directly with
ZReviewTender
; for manually deployed projects, usebundle exec ruby bin/ZReviewTender
to execute.
Generate Configuration Files:
1
ZReviewTender -i
This will generate apple.yml & android.yml from apple.example.yml & android.example.yml into the current execution directory’s config/
folder.
Execute Apple & Android Review Fetching:
1
ZReviewTender -r
- By default, it reads the
apple.yml
&android.yml
settings from the/config/
folder.
Execute Apple & Android Review Fetching & Specify Configuration File Directory:
1
ZReviewTender --run=configuration_file_directory
- By default, it reads the
apple.yml
&android.yml
settings from the/config/
folder.
Execute Only Apple Review Fetching:
1
ZReviewTender -a
- By default, it reads the
apple.yml
settings from the/config/
folder.
Execute Only Apple Review Fetching & Specify Configuration File Location:
1
ZReviewTender --apple=apple.yml_configuration_file_location
Execute Only Android Review Fetching:
1
ZReviewTender -g
- By default, it reads the
android.yml
settings from the/config/
folder.
Execute Only Android Review Fetching & Specify Configuration File Location:
1
ZReviewTender --googleAndroid=android.yml_configuration_file_location
Clear Execution Records and Return to Initial Settings:
1
ZReviewTender -d
This will delete the timestamp record files in /latestCheckTimestamp
, returning to the initial state. Running the fetch again will trigger the init success message:
Current ZReviewTender Version
1
ZReviewTender -v
This will display the current version of ZReviewTender available on RubyGem.
Update ZReviewTender to the Latest Version (rubygem only)
1
ZReviewTender -n
First Execution
The first successful execution will send an initialization success message to the specified Slack channel and create latestCheckTimestamp/Apple
and latestCheckTimestamp/Android
files to record the last fetched review timestamps.
Additionally, an execute.log
will be generated to record execution errors.
Set Up a Schedule for Continuous Execution
Set up a cron job ( crontab ) to continuously fetch new reviews. ZReviewTender will fetch new reviews from the last fetched timestamp recorded in latestCheckTimestamp
to the current time and update the timestamp record file.
For example, crontab: 15 */6 * * * ZReviewTender -r
Please note that the Android API only provides access to reviews added or edited in the last 7 days, so do not set the cron job interval to exceed 7 days to avoid missing reviews.
https://developers.google.com/android-publisher/reply-to-reviews#retrieving_a_set_of_reviews
Github Action Deployment
ZReviewTender App Reviews Automatic Bot
1
2
3
4
5
6
7
8
9
10
11
12
13
14
name: ZReviewTender
on:
workflow_dispatch:
schedule:
- cron: "15 */6 * * *" # Runs every six hours; you can change the settings as needed.
jobs:
ZReviewTender:
runs-on: ubuntu-latest
steps:
- name: ZReviewTender Automatic Bot
uses: ZhgChgLi/ZReviewTender@main
with:
command: '-r' # Executes Apple & iOS App review checks; you can change it to other execution commands as needed.
⚠️️️️️ Final Warning!
Ensure that your configuration files and keys are not publicly accessible, as sensitive information could lead to unauthorized access to your App/Slack permissions; the author is not responsible for any misuse.
If you encounter any unexpected errors, please create an issue with the log information, and we will address it as soon as possible!
Done
This concludes the usage tutorial, followed by some behind-the-scenes development secrets.
=========================
The War with App Reviews
I initially thought that last year’s summary of the AppStore APP’s Reviews Slack Bot and the related technology behind the ZReviewsBot — Slack App Review Notification Bot would wrap up the integration of the latest app reviews into our company workflow. However, I was surprised to find that Apple updated the App Store Connect API this year, allowing this project to continue evolving.
Last year’s solution for fetching reviews for Apple iOS/macOS apps included:
- Public URL API (RSS) ⚠️: This method lacks flexible filtering, provides limited information, has a quantity cap, and we occasionally encounter data inconsistency issues, making it quite unstable; the official API may be deprecated in the future.
- Using Fastlane — SpaceShip to encapsulate complex web operations and session management, allowing us to scrape review data from the App Store Connect backend (essentially simulating a web browser to crawl the backend for data).
Following last year’s approach, we could only use the second method, which wasn’t perfect; sessions would expire, requiring manual updates, and it couldn’t be placed on a CI/CD server because any change in IP would cause the session to expire immediately.
important-note-about-session-duration by Fastlane
Upon receiving news of Apple’s update to the App Store Connect API, I immediately began redesigning the review bot. In addition to switching to the official API, I also optimized the previous architecture and became more familiar with Ruby usage.
Issues Encountered with the App Store Connect API
- The List All Customer Reviews for an App endpoint does not provide app version information.
Strangely, I had to work around this by first calling this endpoint to filter for the latest reviews, then using List All App Store Versions for an App and List All Customer Reviews for an App Store Version to combine and obtain app version information.
Issues Encountered with AndroidpublisherV3
- The API does not provide a method to retrieve all review lists; it only allows fetching reviews added or modified in the last 7 days.
- I also used JWT to connect to the Google API (without relying on related libraries, e.g., google-apis-androidpublisher_v3).
- Here’s an example of generating and using a Google API JWT:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
require "jwt"
require "time"
payload = {
iss: "client_email field from GCP API service account key (*.json)",
sub: "client_email field from GCP API service account key (*.json)",
scope: ["https://www.googleapis.com/auth/androidpublisher"].join(' '),
aud: "token_uri field from GCP API service account key (*.json)",
iat: Time.now.to_i,
exp: Time.now.to_i + 60*20
}
rsa_private = OpenSSL::PKey::RSA.new("private_key field from GCP API service account key (*.json)")
token = JWT.encode payload, rsa_private, 'RS256', header_fields = {kid: "private_key_id field from GCP API service account key (*.json)", typ: "JWT"}
uri = URI("token_uri field from GCP API service account key (*.json)")
https = Net::HTTP.new(uri.host, uri.port)
https.use_ssl = true
request = Net::HTTP::Post.new(uri)
request.body = "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=#{token}"
response = https.request(request).read_body
bearer = result["access_token"]
### use bearer token
uri = URI("https://androidpublisher.googleapis.com/androidpublisher/v3/applications/APP_PACKAGE_NAME/reviews")
https = Net::HTTP.new(uri.host, uri.port)
https.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Authorization'] = "Bearer #{bearer}";
response = https.request(request).read_body
result = JSON.parse(response)
# success!
If you have any questions or feedback, feel free to contact me.
This article was first published on Medium ➡️ Click Here
Automatically converted and synchronized using ZMediumToMarkdown and Medium-to-jekyll-starter.