Post

ZReviewTender|免费开源 App 评价监控机器人,自动整合 Slack 与 Google Sheet

针对 iOS/macOS 与 Android App 评价监控需求,自动抓取最新评价并即时转发至 Slack,结合 Google Translate 与 Google Sheet 储存功能,提升团队协作效率与消费者满意度,免主机空间,支援 Github Actions 一键部署。

ZReviewTender|免费开源 App 评价监控机器人,自动整合 Slack 与 Google Sheet

Click here to view the English version of this article.

點擊這裡查看本文章正體中文版本。

基于 SEO 考量,本文标题与描述经 AI 调整,原始版本请参考内文。


ZReviewTender — 免费开源的 App Reviews 监控机器人

实时监测 App 的最新评价内容并即时给予反馈,提升协作效率及消费者满意度

[ZhgChgLi](https://github.com/ZhgChgLi){:target="_blank"} [ZReviewTender](https://github.com/ZhgChgLi/ZReviewTender){:target="_blank"}

ZhgChgLi / ZReviewTender

ZhgChgLi / ZReviewTender

App Reviews to Slack Channel

App Reviews to Slack Channel

ZReviewTender 为您自动监控 App Store iOS/macOS App 与 Google Play Android App 的使用者最新评价讯息,并提供持续整合工具,串接进团队工作流程,提升协作效率及消费者满意度。

特色功能

  • 取得 App Store iOS/macOS App 与 Google Play Android App 评价列表并筛选出尚未爬取过的最新评价内容

  • [预设功能] 转发爬取到的最新评价到 Slack,点击讯息 Timestamp 连结能快速进入后台回复评价

  • [预设功能] 支援使用 Google Translate API 自动翻译非指定语系、地区的评价内容成您的语言

  • [预设功能] 支援自动记录评价到 Google Sheet

  • 支援弹性扩充,除包含的预设功能外您仍可依照团队工作流程,自行开发所需功能并整合进工具中 e.g. 转发评价到 Discord, Line, Telegram…

  • 使用时间戳纪录爬取位置,防止重复爬取评价

  • 支援过滤功能,可指定只爬取 多少评分、评价内容包含什么关键字、什么地区/语系 的评价

  • Apple 基于 全新的 App Store Connect API ,提供稳定可靠的 App Store App 评价资料来源,不再像 以往使用 XML 资料不可靠 or Fastlane Spaceship Session 会过期需定时人工维护

  • Android 同样使用官方 AndroidpublisherV3 API 捞取评价资料

  • 支援使用 Github Repo w/ Github Action 部署,让您免费快速的建立 ZReviewTender App Reviews 机器人

  • 100% Ruby @ RubyGem

TL;DR [2024/09/27] Update

整理快速部署教学文件,请参考最新文章:[Quick Start! ] Github Action x ZReviewTender 免费快速部署你的 App 商城评价监控机器人。

与类似服务比较

App Reviews 工作流程整合范例 (in Pinkoi)

问题:

商城的评价对产品很重要但他却是一个非常人工跟重复转介沟通的事。

因为要时不时人工上去看一下新评价,如过有客服问题再将问题转发给客服协助处理,很重复、人工。

透过 ZReviewTender 评价机器人,将评价自动转发到 Slack Channel,大家能快速收到最新评价资讯,并即时追踪、讨论;也能让整个团队了解目前使用者对产品的评价、建议。

更多资讯可参考: 2021 Pinkoi Tech Career Talk — 高效率工程团队大解密

部署 — 只使用预设功能

如果您只需要 ZReviewTender 自带的预设功能 (to Slack/Google Translate/Filter) 则可使用以下快速部署方式。

ZReviewTender 已打包发布到 RubyGems ,您可以快速方便的使用 RubyGems 安装使用 ZReviewTender。

[推荐] 直接使用 Github Repo Template 部署

  • 无需任何主机空间 ✅

  • 无需任何环境要求 ✅

  • 无需了解工程原理 ✅

  • 完成 Config 档案配置即完成部署 ✅

  • 8 个步骤即可完成部署 ✅

  • 完全免费 ✅ Github Action 提供每个帐号 2,000+分钟/月 执行用量,执行一次 ZReviewTender 评价捞取约只需要 15~30 秒。 预设每 6 小时执行一次,一天执行 4 次, 一个月约只消耗 60 分钟额度 。 Github Private Repo 免费无限制建立。

1.前往 ZReviewTender Template Repo: ZReviewTender-deploy-with-github-action

点击右上方「Use this template」按钮。

  1. 建立 Repo

  • Repository name: 输入你想要的 Repo 专案名称

  • Access: Private

⚠️⚠️ 请务必建立 Private Repo ⚠️⚠️

因为你将上传设定及私密金钥到专案中

最后点击下方「Create repository from template」按钮。

  1. 确认你建立的 Repo 是 Private Repo

确认右上方 Repo 名称有出现「🔒」和 Private 标签。

若无则代表您建立的事 Public Repo 非常危险 ,请前往上方 Tab「Settings」-> 「General」-> 底部「Danger Zone」-> 「Change repository visibility」->「Make private」 改回 Private Repo

  1. 等待 Project init 成功

可在 Repo 首页 Readme 中的

区块查看 Badge,如果 passing 即代表 init 成功。

或是点击上方 Tab「Actions」-> 等待「Init ZReviewTender」Workflow 执行完成:

执行完成状态会变 3「✅ Init ZReviewTender」-> Project init 成功。

  1. 确认 init 档案及目录是否正确建立

点击上方 Tab「Code」回到专案目录,Project init 成功的话会出现:

  • 目录: config/

  • 档案: config/android.yml

  • 档案: config/apple.yml

  • 目录: latestCheckTimestamp/

  • 档案: latestCheckTimestamp/.keep

  1. 完成 Configuration 配置好 android.yml & apple.yml

进入 config/ 目录完成 android.yml & apple.yml 档案配置。

点击进入要编辑的 confi YML 档案点击右上方「✏️」编辑档案。

参考本文下方「 设定 」区块完成配置好 android.yml & apple.yml

编辑完成后可以直接在下方「Commit changes」储存设定。

上传相应的 Key 档案到 config/ 目录下:

config/ 目录下,右上角选择「Add file」->「Upload files」

将 config yml 里配置的相应 Key、外部档案路径一并上传到 config/ 目录下,拖曳档案到「上方区块」-> 等待档案上传完成 -> 下方直接「Commit changes」储存。

上传完成后回到 /config 目录查看档案是否正确储存&上传。

  1. 初始化 ZReviewTender (手动触发执行一次)

点击上方 Tab「Actions」-> 左方选择「ZReviewTender」-> 右方按钮选择「Run workflow」-> 点击「Run workflow」按钮执行一次 ZReviewTender。

点击后,重新整理网页 会出现:

点击「ZReviewTender」可进入查看执行状况。

展开「 Run ZreviewTender -r 」区块可查看执行 Log。

这边可以看到出现 Error,因为我还没配置好我的 config yml 档案。

回头调整好 android/apple config yml 后再回到 6. 步骤一开始重新触发执行一次。

查看 「 ZReviewTender -r 」区块的 log 确认执行成功!

Slack 指定接收最新评价讯息的 Channel 也会出现 init Success 成功讯息 🎉

  1. Done! 🎉 🎉 🎉

配置完成!尔后每 6 个小时会自动爬取期间内的最新评价并转发到你的 Slack Channel 中!

可在 Repo 首页 Readme 中的顶部查看最新一次执行状况:

若出现 Error 即代表执行发生错误,请从 Acions -> ZReviewTender 进入查看纪录;如果有意外的错误,请 建立一个 Issue 附上纪录资讯,将会尽快修正!

❌❌❌执行发生错误同时 Github 也会寄信通知,不怕发生错误机器人挂掉但没人发现!

Github Action 调整

您还可以依照自己需求配置 Github Action 执行规则。

点击上方 Tab「Actions」-> 左方「ZReviewTender」-> 右上方「 ZReviewTender.yml

点击右上方「✏️」编辑档案。

有两个参数可供调整:

cron : 设定多久检查一次有无新评价,预设是 15 */6 * * * 代表每 6 小时 15 分钟会执行一次。

可参考 crontab.guru 依照自己的需求配置。

请注意:

  1. Github Action 使用的是 UTC 时区
  1. 执行频率越高会消耗越多Github Action 执行额度

run : 设定要执行的指令,可参考本文下方「 执行 」区块,预设是 ZReviewTender -r

  • 预设执行 Android App & Apple(iOS/macOS App): ZReviewTender -r

  • 只执行 Android App: ZReviewTender -g

  • 只执行 Apple(iOS/macOS App) App: ZReviewTender -a

编辑完成后点击右上方「Start commit」选择「Commit changes」储存设定。

手动触发执行 ZReviewTender

参考前文「6. 初始化 ZReviewTender (手动触发执行一次)」

使用 Gem 安装

如果熟悉 Gems 可以直接使用以下指令安装 ZReviewTender

1
gem install ZReviewTender

使用 Gem 安装 (不熟悉 Ruby/Gems)

如果不熟悉 Ruby or Gems 可以 Follow 以下步骤 Step by Step 安装 ZReviewTender

  1. macOS 虽自带 Ruby,但建议使用 rbenv or rvm 安装新的 Ruby 及管理 Ruby 版本 (我使用 2.6.5 )

  2. 使用 rbenv or rvm 安装 Ruby 2.6.5,并切换至 rbenv/rvm 的 Ruby

  3. 使用 which ruby 确认当前使用的 Ruby /usr/bin/ruby 系统 Ruby

  4. Ruby 环境 Ok 后使用以下指令安装 ZReviewTender

1
gem install ZReviewTender

部署 — 想自行扩充功能

手动

  1. git clone ZReviewTender Source Code

  2. 确认 & 完善 Ruby 环境

  3. 进入目录,执行 bundle install ZReviewTender 安装相关依赖

Processor 建立方式可参考后面文章内容。

设定

ZReviewTender — 使用 yaml 档设定 Apple/Google 评价机器人。

[推荐] 直接使用文章下方的执行指令 — 「产生设定档案」:

1
ZReviewTender -i

直接产生空白的 apple.yml & android.yml 设定档。

Apple (iOS/macOS App)

参考 apple.example.yml 档案:

⚠️ 下载下来 apple.example.yml 后记得将档名改成 apple.yml

apple.yml:

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:

appStoreConnectP8PrivateKeyID & appStoreConnectP8PrivateKeyFilePath:

  • Name: ZReviewTender

  • Access: App Manager

  • appStoreConnectP8PrivateKeyID: Key ID

  • appStoreConnectP8PrivateKeyFilePath: /AuthKey_XXXXXXXXXX.p8 ,Download API Key,并将档案放入与 config yml 同目录下。

appID:

appID: App Store Connect -> App Store -> General -> App Information -> Apple ID

GCP Service Account

ZReviewTender 所使用到的 Google API 服务 (捞取商城评价、Google 翻译、Google Sheet) 都是使用 Service Account 验证方式。

可先依照 官方步骤建立 GCP & Service Account 下载保存 GCP Service Account 身份权限金钥 ( *.json )。

  • 如要使用自动翻译功能请确认 GCP有启用 Cloud Translation API 和使用的 Service Account 也有加入

  • 如要使用记录到 Google Sheet 功能请确认 GCP 有启用 Google Sheets APIGoogle Drive API 和使用的 Service Account 也有加入

Google Play Console (Android App)

参考 android.example.yml 档案:

⚠️ 下载下来 android.example.yml 后记得将档名改成 android.yml

android.yml:

1
2
3
4
5
6
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:

packageName: com.XXXXX 可于 Google Play Console -> Dashboard -> App 中取得

playConsoleDeveloperAccountID & playConsoleAppID:

可由 Google Play Console -> Dashboard -> App 页面网址中取得:

https://play.google.com/console/developers/ playConsoleDeveloperAccountID /app/ playConsoleAppID /app-dashboard

将用于组合评价讯息连结,让团队可以点击连结快速进入后台评价回复页面。

keyFilePath:

最重要的资讯,GCP Service Account 身份权限金钥 ( *.json )

需要按照 官方文件 步骤,建立 Google Cloud Project & Service Account 并到 Google Play Console -> Setup -> API Access 中完成启用 Google Play Android Developer API &连结专案,到 GCP 点击下载服务帐户的 JSON 金钥。

JSON 金钥范例内容如下:

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 金钥档案路径,将档案放入与 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 whole block if you don't needed 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 message in 1 slack message.
        slackBotToken: "" # Slack Bot Token, send slack message throught Slack Bot.
        slackBotTargetChannel: "" # Slack Bot Token, send slack message throught Slack Bot. (recommended, first priority)
        slackInCommingWebHookURL: "" # Slack In-Comming WebHook URL, Send slack message throught In-Comming WebHook, not recommended, deprecated.
    ...More Processors...

ZReviewTender 自带四个 Processor,先后顺序会影响到资料处理流程 FilterProcessor->GoogleTranslateProcessor->SlackProcessor-> GoogleSheetProcessor。

FilterProcessor:

依照指定条件过滤捞取的评价,只处理符合条件的评价。

  • class: FilterProcessor 无需调整,指向 lib/Processors/ FilterProcessor .rb

  • enable: true / false 启用此 Processor or Not

  • keywordsInclude: [“ 关键字1 ”,“ 关键字2 ”…] 筛选出内容包含这些关键字的评价

  • ratingsInclude: [ 1 , 2 …] 1~5 筛选出包含这些评价分数的评价

  • territoriesInclude: [“ zh-hant ”,” TWN ”…] 筛选出包含这些地区(Apple)或语系(Android)的评价

GoogleTranslateProcessor:

将评价翻译成指定语言。

  • class: GoogleTranslateProcessor 无需调整,指向 lib/Processors/ GoogleTranslateProcessor .rb

  • enable: true / false 启用此 Processor or Not

  • googleTranslateAPIKeyFilePath: /gcp_key.json GCP Service Account 身份权限金钥 File Path *.json ,将档案放入与 config yml 同目录下,内容范例可参考上方 Google Play Console JSON 金钥范例。 (请确认该 JSON key 之 service account 有 Cloud Translation API 使用权限)

  • googleTranslateTargetLang: zh-TWen …目标翻译语言

  • googleTranslateTerritoriesExclude: [“ zh-hant ”,” TWN ”…] 不需翻译的地区(Apple)或语系(Android)

SlackProcessor:

转发评价到 Slack。

  • class: SlackProcessor 无需调整,指向 lib/Processors/ SlackProcessor .rb

  • enable: true / false 启用此 Processor or Not

  • slackTimeZoneOffset: +08:00 评价时间显示时区

  • slackAttachmentGroupByNumber: 1 设定几则 Reviews 合并成同一则讯息,加速发送;预设 1 则 Review 1 则 Slack 讯息。

  • slackBotToken: xoxb-xxxx-xxxx-xxxx Slack Bot Token ,Slack 建议建立一个 Slack Bot 包含 postMessages Scope 并使用其发送 Slack 讯息

  • slackBotTargetChannel: CXXXXXX 群组 ID ( 非群组名称 ),Slack Bot 要发送到哪个 Channel 群组;且 需要把你的 Slack Bot 加入到该群组

  • slackInCommingWebHookURL: https://hooks.slack.com/services/XXXXX 使用旧的 InComming WebHookURL 发送讯息到 Slack,注意!Slack 不建议再继续使用此方法发送讯息。

Please note, this is a legacy custom integration — an outdated way for teams to integrate with Slack. These integrations lack newer features and they 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 .

  • slackBotToken 与 slackInCommingWebHookURL,SlackProcessor 会优选选择使用 slackBotToken

GoogleSheetProcessor

纪录评价到 Google Sheet。

  • class: GoogleSheetProcessor 无需调整,指向 lib/Processors/ SlackProcessor .rb

  • enable: true / false 启用此 Processor or Not

  • googleSheetAPIKeyFilePath: /gcp_key.json GCP Service Account 身份权限金钥 File Path *.json ,将档案放入与 config yml 同目录下,内容范例可参考上方 Google Play Console JSON 金钥范例。 (请确认该 JSON key 之 service account 有 Google Sheets APIGoogle Drive API 使用权限)

  • googleSheetTimeZoneOffset: +08:00 评价时间显示时区

  • googleSheetID: Google Sheet ID 可由 Google Sheet 网址中取得:https://docs.google.com/spreadsheets/d/ googleSheetID /

  • googleSheetName: Sheet 名称, e.g. Sheet1

  • keywordsInclude: [“ 关键字1 ”,“ 关键字2 ”…] 筛选出内容包含这些关键字的评价

  • ratingsInclude: [ 1 , 2 …] 1~5 筛选出包含这些评价分数的评价

  • territoriesInclude: [“ zh-hant ”,” TWN ”…] 筛选出包含这些地区(Apple)或语系(Android)的评价

  • values: [ ] 评价资讯的栏位组合

1
2
3
4
5
6
7
8
9
10
%TITLE% 评价标题
%BODY% 评价内容
%RATING% 评价分数 1~5
%PLATFORM% 评价来源平台 Apple or Android
%ID% 评价ID
%USERNAME% 评价
%URL% 评价前往连结
%TERRITORY% 评价地区(Apple)或评价语系(Android)
%APPVERSION% 被评价的 App 版本
%CREATEDDATE% 评价建立日期

例如我的 Google Sheet 栏位如下:

1
评价分数,评价标题,评价内容,评价资讯

则 values 可设定成:

1
values: ["%TITLE%","%BODY%","%RATING%","%PLATFORM% - %APPVERSION%"]

自订 Processor 串接自己的工作流程

若需要自订 Processor 请改用手动部署,因 gem 上的 ZReviewTender 已打包无法动态调整。

您可参考 lib/Processors/ProcessorTemplate.rb 建立您的扩充功能:

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 paraemter from config e.g. config["parameter1"]
        # configFilePath: file path of config file (apple.yml/android.yml)
        # baseExecutePath: user excute path
    end

    def processReviews(reviews, platform)
        if reviews.length < 1
            return reviews
        end

        ## do what your want to do with reviews...
        
        ## return result reviews
        return reviews
    end
end

initialize 会给予:

  • config Object: 对应 config yml 内的设定值

  • configFilePath: 使用的 config yml 档案路径

  • baseExecutePath: 使用者在哪个路径执行 ZReviewTender

processReviews(reviews, platform):

爬取完新评价后,会进入这个 function 让 Processor 有机会处理,处理完请 return 结果的 Reviews。

Review 资料结构定义在 lib/Models/ Review.rb

附注

XXXterritorXXX 参数:

  • Apple 使用地区:TWM/JPN…

  • Android 使用语系:zh-hant/en/…

若不需要某个 Processor: 可以设定 enable: false 或是直接移除该 Processor Config Block。

Processors 执行顺序可依照您的需求自行调整: e.g. 先执行 Filter 再执行翻译再执行 Slack 再执行 Log to Google Sheet…

执行

⚠️ 使用 Gem 可直接下 ZReviewTender ,若为手动部署专案请使用 bundle exec ruby bin/ZReviewTender 执行。

产生设定档案:

1
ZReviewTender -i

从 apple.example.yml & android.example.yml 产生 apple.yml & android.yml 到当前执行目录的 config/ 目录下。

执行 Apple & Android 评价爬取:

1
ZReviewTender -r
  • 默认读取 /config/apple.yml & android.yml 设定

执行 Apple & Android 评价爬取 & 指定设定档目录:

1
ZReviewTender --run=设定档目录
  • 默认读取 /config/apple.yml & android.yml 设定

只执行 Apple 评价爬取:

1
ZReviewTender -a
  • 默认读取 /config/apple.yml 设定

只执行 Apple 评价爬取 & 指定设定档位置:

1
ZReviewTender --apple=apple.yml设定档位置

只执行 Android 评价爬取:

1
ZReviewTender -g
  • 默认读取 /config/android.yml 设定

只执行 Android 评价爬取 & 指定设定档位置:

1
ZReviewTender --googleAndroid=android.yml设定档位置

清除执行纪录回到初始设定

1
ZReviewTender -d

会删除 /latestCheckTimestamp 里的 Timestamp 纪录档案,回到初始状态,再次执行爬取会再次收到 init success 讯息:

当前 ZReviewTender 版本

1
ZReviewTender -v

显示当前 ZReviewTender 再 RubyGem 上的最新版本号。

更新 ZReviewTender 到最新版 (rubygem only)

1
ZReviewTender -n

第一次执行

第一次执行成功会发送初始化成功讯息到指定 Slack Channel,并在执行相应目录下产生 latestCheckTimestamp/Apple , latestCheckTimestamp/Android 档案纪录最后爬取的评价 Timestamp。

另外还会产生一个 execute.log 纪录执行错误。

设定排程持续执行

设定排程定时( crontab )持续执行爬取新评价,ZReviewTender 会爬取 latestCheckTimestamp 上次爬取的评价 Timestamp 到这次爬取时间内的新评价,并更新 Timestamp 纪录档案。

e.g. crontab: 15 */6 * * * ZReviewTender -r

另外要注意因为 Android API 只提供查询近 7 天新增或编修的评价,所以排成周期请勿超过 7 天,以免有评价遗漏。

<https://developers.google.com/android-publisher/reply-to-reviews#retrieving_a_set_of_reviews>{:target="_blank"}

https://developers.google.com/android-publisher/reply-to-reviews#retrieving_a_set_of_reviews

Github Action 部署

[ZReviewTender App Reviews Automatic Bot](https://github.com/marketplace/actions/zreviewtender-app-reviews-automatic-bot){:target="_blank"}

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 * * *" #每六小时跑一次,可参照上方 crontab 自行更改设定

jobs:
  ZReviewTender:
    runs-on: ubuntu-latest
    steps:
    - name: ZReviewTender Automatic Bot
      uses: ZhgChgLi/ZReviewTender@main
      with:
        command: '-r' #执行 Apple & iOS App 评价检查,可参照上方改成其他执行指令

⚠️️️️️ 再次警告!

务必确保你的设定档及金钥无法被公开存取,因其中的敏感资讯可能导致 App/Slack 权限被盗用;作者不对被盗用负任何责任。

如果有发生意外的错误,请 建立一个 Issue 附上纪录资讯,将会尽快修正!

Done

使用教学结束,再来是幕后开发秘辛分享。

=========================

与 App Reviews 的战争

本以为去年总结的 AppStore APP’s Reviews Slack Bot 那些事 及运用相关技术实现的 ZReviewsBot — Slack App Review 通知机器人 ,与整合 App 最新评价进入公司工作流程这件事就告一段落了;没想到 Apple 居然在今年 更新了 App Store Connect API ,让这件事能持续演进。

去年总结出来的 Apple iOS/macOS App 捞取评价的解决方案:

  • Public URL API (RSS) ⚠️: 无法弹性筛选、给的资讯也少、有数量上限、还有我们偶尔会遇到资料错乱问题,很不稳定;官方未来可能弃用

  • 透过 Fastlane SpaceShip 帮我们封装复杂的网页操作、Session 管理,去 App Store Connection 网站后台捞取评价资料 (等于是起一个网页模拟器爬虫去后台爬资料)。

依照去年做法就只能使用方法二来达成,但效果不太完美;Session 会过期,需要人工定期更新,且无法放在 CI/CD Server 上,因为 IP 一变 Session 会马上过期。

[important-note-about-session-duration](https://docs.fastlane.tools/best-practices/continuous-integration/#important-note-about-session-duration){:target="_blank"} by Fastlane

important-note-about-session-duration by Fastlane

今年收到 Apple 更新了 App Store Connect API 消息后立马著手重新设计新的评价机器人,除了改用官方 API 外;还优化了之前的架构设计及更熟悉 Ruby 用法。

App Store Connect API 开发上遇到的问题

很诡异,只能 workaround 先打这个 endpoint 筛出最新评价,再打 List All App Store Versions for an App & List All Customer Reviews for an App Store Version 组合出 App 版本资讯。

AndroidpublisherV3 开发上遇到的问题

  • API 不提供取得所有评价列表的方法,只能取得近 7 天新增/编修的评价。

  • 同样使用 JWT 串接 Google API (不依赖相关类别库 e.g. google-apis-androidpublisher_v3)

  • 附上个 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: "GCP API 身份权限金钥 (*.json) 档案中的 client_email 栏位",
  sub: "GCP API 身份权限金钥 (*.json) 档案中的 client_email 栏位",
  scope: ["https://www.googleapis.com/auth/androidpublisher"]].join(' '),
  aud: "GCP API 身份权限金钥 (*.json) 档案中的 token_uri 栏位",
  iat: Time.now.to_i,
  exp: Time.now.to_i + 60*20
}

rsa_private = OpenSSL::PKey::RSA.new("GCP API 身份权限金钥 (*.json) 档案中的 private_key 栏位")
token = JWT.encode payload, rsa_private, 'RS256', header_fields = {kid:"GCP API 身份权限金钥 (*.json) 档案中的 private_key_id 栏位", typ:"JWT"}

uri = URI("API 身份权限金钥 (*.json) 档案中的 token_uri 栏位")
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!

有任何问题及指教欢迎 与我联络


Buy me a beer

本文首次发表于 Medium (点击查看原始版本),由 ZMediumToMarkdown 提供自动转换与同步技术。

Improve this page on Github.

This post is licensed under CC BY 4.0 by the author.