【iOS】App Store Connect API アプリのダウンロード数をLINEへ通知する

## 自己紹介
個人開発でいくつかアプリをリリースしている者です。

## LINE通知までの経緯
ダウンロード数のチェックでApp Store Connectに頻繁にアクセスするのですが、都度チェックしに行くのは面倒！

よし、LINEに通知してくれるようなアプリを作ってみようということで、作りました。

## 使用技術、言語
- Google Cloud Functions
- Python
- LINE Notify

(2024/10/25追加)
LINE Notifyはサービス提供が2025年3月31日に終了します。
https://notify-bot.line.me/closing-announce
よく確認してから、お使いください。
私はこの記事投稿後にSlackへ通知するように修正しました。

### 構成図
![スクリーンショット 2024-09-20 11.21.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469133/9a983d79-11e7-9e15-0657-eff855eca8aa.png)

## CloudFunctions
ここで情報の取得〜LINE通知まで全て行います。
大まかな流れです。
- JWTトークンの作成
- App Store Connect APIへリクエスト
- レスポンス情報の加工
- LINE Notify経由でLINEへ通知

基本的なApp Store Connect APIの使い方は調べたら出てくるので注意点のみ記載します。

### 利用ライブラリ
```requirements.txt
functions-framework==3.*
pyjwt==2.8.0
requests==2.31.0
cryptography==41.0.5
pandas==2.2.2
```

### App Store Connect APIへリクエスト
リクエスト先
`https://api.appstoreconnect.apple.com/v1/salesReports`
[リファレンス](https://developer.apple.com/documentation/appstoreconnectapi/download_sales_and_trends_reports)

毎日実行して、昨日のデータを取得する方針だったのですが、ここで問題が。

昨日のデータを取得しようとすると、404エラーになる！

> Daily reports are available the following day.
> Reports are generally available by 8 a.m. Pacific Time (PT).
>
> (日本語訳)
> 日次レポートは翌日からご利用いただけます。
> レポートは通常、太平洋標準時 (PT) の午前 8 時までに利用可能になります。

https://developer.apple.com/help/app-store-connect/reference/sales-and-trends-reports-availability

太平洋標準時と日本時間で時差が17時間あるため、レポートは2日後の午前1時以降に取得する必要があります。
9月18日のレポート情報が欲しければ、9月21日の午前1時以降ということです。

前日のレポートが取得できないのは、辛いですが、しゃーなしということで、2日前のデータを取得するようにしています。

App Store Connect APIへリクエストするときのヘッダーはこんな感じです。
```py
  two_days_ago = (datetime.now() - timedelta(2)).strftime('%Y-%m-%d')
  params = {
  'filter[frequency]': 'DAILY',
  'filter[reportSubType]': 'SUMMARY',
  'filter[reportType]': 'SALES',
  'filter[reportDate]': two_days_ago,
  'filter[vendorNumber]': vender_no # App Store Connectから取得
  }
```

### レスポンス情報の加工
コンテンツの形式が `application/a-gzip` と `GZIP形式` になっています。

なので、解凍して、LINEで通知して欲しい文字列に加工します。
参考にコードを載せてますが、コードは全てChatGPTさんにお願して作りました。
参考程度に。

```py
# AppStoreConnectAPIからのレスポンス
response = requests.get(url, headers=headers, params=params)

# レスポンスの解凍
compressed_data = BytesIO(response.content)
with gzip.GzipFile(fileobj=compressed_data) as decompressed_file:
  # 解凍されたデータを読み込む
  decompressed_data = decompressed_file.read()

# 解凍されたデータを加工
tsv_data = decompressed_data.decode('utf-8')
df = pd.read_csv(StringIO(tsv_data), sep='\t')

# データの集計用の辞書
results = {}
# DataFrameをループしてデータを集計
for _, row in df.iterrows():
  title = row['Title']
  product_type = row['Product Type Identifier']
  units = row['Units']
  
  # タイプを取得
  product_type_desc = product_type_map.get(product_type, '未知のタイプ')

  # タイトルのキーが存在しない場合は追加
  if title not in results:
      results[title] = {'ダウンロード': 0, 'アップデート': 0, 'App 内課金': 0, '再ダウンロード': 0}

  # 集計
  if product_type_desc == '再ダウンロード':
      results[title]['再ダウンロード'] += units
  elif product_type_desc == 'アップデート':
      results[title]['アップデート'] += units
  elif product_type_desc == 'App 内課金':
      results[title]['App 内課金'] += units
  else:  # デフォルトはダウンロード
      results[title]['ダウンロード'] += units

# 結果を文字列に整形
result_str = f"{two_days_ago}"
for title, metrics in results.items():
  result_str += f"\n・{title}"
  for metric, value in metrics.items():
      if value > 0:
          result_str += f"\n　{metric}：{value}"


```

データの中にあるプロダクトIDは下記のタイプに紐づいてます。
`product_type_map`はここの情報を元に変換してます。
https://developer.apple.com/jp/help/app-store-connect/reference/product-type-identifiers/


### LINE Notify経由でLINEへ通知
ここは単純にLINE NotifyのAPIを呼び出すだけです。

LINEのメッセージのイメージはこんな感じです。

https://x.com/takatikidesu/status/1832001663730930101?s=61

現状は、毎朝9時30分にCloud SchedulerとCloud Pub/Subで定期実行するようにしています。


## 最後に
実際に通知が来ると、モチベアップになりますね！
引き続き個人開発とか頑張っていきます〜。

## 参考
https://zenn.dev/yamazaking/articles/eedf5e9179d46a
https://dev.classmethod.jp/articles/try-cloud-functions-scheduler-pubsub/