🗂

Saasの契約更新月が近づいてきたら通知する仕組みをつくりました

2024/02/09に公開

導入と概要

こんにちは、delyの後藤です。
弊社では、Saas管理ツールとしてdxeco を導入しています。
ツール内のインサイトとして契約更新前のnヶ月前になったらUI上にお知らせを出してくれる機能があり、それをSlackに通知する仕組みを作ったところ、意外にもウケが良かったのでzennに記事を作ることにしました。

(ちなみに導入時の私のインタビュー記事は こちら ですw)

設計

APIを叩いて、流し込むだけの難しいことは何もしていない形で作成しました。

データ登録と設定

データの登録と、いつお知らせを受け取りたいか、という設定はdxecoのUI上で手登録を行います。
これにより、時期が来たらdxecoの画面上にインサイトとしてお知らせが表示されるようになります。

データ取得と通知処理

dxecoには標準でインサイトデータを取得するAPIが準備されているため、今回はこちらを利用しました。
また、通知はUIをカスタマイズしたかったので、webhookではなく、Slackの chat.postMessage APIを利用しました。

動作イメージ

動作環境

言語/ライブラリ

実行環境

  • Github Actions
    • runner image: ubuntu-latest
    • trigger: schedule

事前準備

APIキーなどは環境変数に持ち、スクリプトから参照する形にしました。

dxecoから必要な値を取得

  1. dxecoのAPIキー
    2. 画面案内に従って生成する
  2. dxecoの組織ID
    4. dxecoのURLに含まれているIDを控える

SlackのAPIキーの生成

  1. https://api.slack.com/ へアクセス
  2. OAuth & Permissions から Scopeに chat:write を追加
  3. Install Workspaceをクリックしてインストール
  4. Bot User OAuth Tokenの値を控える

通知したいSlackチャンネルのIDを取得

  1. 通知したいチャンネルを右クリック > チャンネル詳細を開く をクリック
  2. 開いたウインドウの最下部に表示されたチャンネルIDをコピーする

.envの設定

.env ファイルを作成し、以下のように設定する

### 共通
export DXECO_API_TOKEN={{dxecoのAPIキー}}
export DXECO_ORG_ID={{dxecoの組織ID}}
export SLACK_API_TOKEN={{Bot User OAuth Tokenの値}}

### Saas契約更新通知
export SLACK_JOIN_CH_ID={{SlackチャンネルのID}}

実装

ファイル構成

  • root/
    • tempalets/
      • テンプレートファイル
    • .env
    • スクリプトファイル

コード

テンプレートファイル

{
    "blocks": [
        {
            "type": "header",
            "text": {
                "type": "plain_text",
                "text": ":toolbox:  契約更新が近いSaasのお知らせ  :toolbox:"
            }
        },
        {
            "type": "context",
            "elements": [
                {
                    "text": "",
                    "type": "mrkdwn"
                }
            ]
        },
        {
            "type": "divider"
        },
        {
            "type": "section",
            "text": {
                "type": "mrkdwn",
                "text": ":calendar: |  *対象*  | :calendar: "
            }
        }
    ]
}

スクリプトファイル

"""
Saasの契約更新通知
"""
from os import environ as env
import requests
from datetime import datetime
import slack_sdk
import json

api_base_url = "https://api.dxeco.io/api"
insights_url = f"{api_base_url}/insights"
applications_url = f"{api_base_url}/applications"
headers = {
    "X-API-Key": env['DXECO_API_TOKEN']
}

params = {    
    "organizationId": env['DXECO_ORG_ID'],
    "type": "RenewalContract",
    "status": "Active"
}

res = requests.get(url=insights_url, headers=headers, params=params)
res.raise_for_status()

renewal_info_arr = []
for contract in res.json()["data"]:

    # Application名取得
    application_detail_url = f"{applications_url}/{contract['applicationId']}"
    a_res = requests.get(url=application_detail_url, headers=headers)
    app_name = a_res.json()["name"]

    print(app_name, contract["reason"])
    renewal_info_arr.append({
        "app": app_name, "reason": contract["reason"]
    })

with open("templates/tpl-contract-renewal.json", "r") as fh:
    template = json.load(fh)

if len(renewal_info_arr) > 0:
    for info in renewal_info_arr:
        template["blocks"].append(
            {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*{info['app']} ({info['reason']})* "
                }
            }
        )
else:
    """ 対象が存在しない場合 """
    template["blocks"].append(
        {
            "type": "section",
            "text": {
                "type": "mrkdwn",
                "text": "対象なしです"
            }
        }
    )

# 共通部分のテンプレート当て込み
template["blocks"].append({"type": "divider"})
template["blocks"][1]["elements"][0]["text"] = datetime.today().strftime("%Y/%m/%d")

client = slack_sdk.WebClient(token=env.get("SLACK_API_TOKEN"))
client.chat_postMessage(channel=env.get("SLACK_SAAS_INFO_CH_ID"), text="お知らせ!", blocks=template["blocks"])

実行環境の構築

Github Actionsの設定

ライブラリインストール用のファイル作成

pip freezeの出力無いようをrequirements.txtに保存する

pip freeze > requirements.txt

実行設定用のyaml作成

  • root/
    • workflows/
      • 実行用のyamlファイル
name: {{いい感じの名前}}

on:
  workflow_dispatch:
  # 設定はUTCで記述、ここでは毎週月曜/AM9:00に実行としています。
  schedule:
    - cron: '0 0 * * 1'

env:
  DXECO_API_TOKEN: ${{ secrets.DXECO_API_TOKEN }}
  DXECO_ORG_ID: ${{ secrets.DXECO_ORG_ID }}
  SLACK_API_TOKEN: ${{ secrets.SLACK_API_TOKEN }}
  SLACK_SAAS_INFO_CH_ID: ${{ secrets.SLACK_SAAS_INFO_CH_ID }}


jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Set up Python 3.10
      uses: actions/setup-python@v2
      with:
        python-version: "3.10"
    - name: check Version
      run: python -V
    - name: Install dependencies
      run: pip install -r requirements.txt
    - name: ログに残したい適当なメッセージ
      run: python {{Pythonスクリプトファイル名}}

環境変数の設定

  1. Repository > Settings と遷移
  2. 左カラムの Security > Secrets and variables > actionsを選択
  3. Repository secrets欄の「New repository secret」をクリックし、環境変数を設定する
  4. .envの設定 で設定した値と同じ変数名と値にする

実行

テスト

Repository > Actionsと遷移し、左カラムにyamlファイルに記述したワークフロー名が表示されているのでクリック
「Run workflow」というボタンがあるのでクリックしてmainブランチを実行する
問題なく実行され、目的のチャンネルに投稿されたテスト完了

本実行

yamlで指定したスケジュールで自動的に実行されます。

おわりに

最近、情シス関連でバッチ処理したいものはGithub Actionsで実行するのがお金をかけず、メンテナンスコストも低くいけるので気に入っています。
dxecoでは色々なAPIが提供されており、UIは見せたくないが、一定有益な情報は流したいときに柔軟に対応できる点で非常にいいSaas管理ツールと思います。

Discussion