はじめに
毎朝8時30分、スマホのLINEにこれが届くようになります。
sunabar残高 2026-09-02
残高 9,000円(前日比 +0円)
これまでの3本で作った残高照会APIの呼び出しを、macOSのlaunchdで毎朝動かし、LINE Messaging APIで自分に送る。実体はshellスクリプト1本です。ただ、送り先のLINE側で思っていたより回り道がありました。LINE Notifyは2025年3月に終了していて、代わりのMessaging APIを使うにはLINE公式アカウントを作ることになります。 しかもLINE Developersコンソールからチャネルを作ることはできませんでした。この記事は2026年9月時点の画面でどう進んだかの記録です。
- 対象読者:準備編〜応用編で動かした残高照会APIを、毎朝自動で動かしてみたい方。LINE Notify終了後に、LINEへの通知を作ってみたい方
- 前提:sunabarのアクセストークンで残高照会APIを呼べること(準備編)
- かかるお金:0円。LINE公式アカウントは無料プランで月200通まで送れます。毎朝1通なら31通です
- 試した日と環境:2026年9月2日・macOS 13.7.8
先に押さえること
| # | 押さえること | 中身 |
|---|---|---|
| 1 | LINE Notifyは2025年3月31日に終了 | 今からLINEへ通知を送るならMessaging APIです。LINE公式アカウントを作ることになります(2章) |
| 2 | Developersコンソールからチャネルを直接作れない | LINE公式アカウントを先に作り、後からMessaging APIを有効化する順でした(2章) |
| 3 | launchdはシェルの設定を読まない |
.zshrc に書いた環境変数は、スクリプト側で zsh -ic を使って取り出しました(6章) |
1. 全体の形
毎朝8時30分にlaunchdがshellスクリプトを起動します。スクリプトは残高照会APIで残高を取得し、通知の文面を作って、LINE Messaging APIで自分のLINEに送ります。
作ったファイルは3つです。
| # | ファイル | 役割 |
|---|---|---|
| 1 | ~/Library/Scripts/sunabar_line_balance.sh |
残高を取得してLINEへ送る本体(5章) |
| 2 | ~/Library/LaunchAgents/com.tri-ponte.sunabar-line-balance.plist |
毎朝8時30分に1を起動(6章) |
| 3 | ~/Library/Scripts/sunabar_token_taken.txt |
sunabarトークンを取得した日を1行だけ書く(7章) |
2. LINE公式アカウントを作る(2026年9月の画面)
ここがこの記事でいちばん長い工程でした。古い記事では「LINE DevelopersコンソールでMessaging APIチャネルを作る」と書かれていますが、いま同じ場所で「Messaging API」を選ぶとこう案内されます。
LINE DevelopersコンソールからMessaging APIチャネルを直接作成することはできなくなりました
先にLINE公式アカウントを作り、後からMessaging APIを有効化するのが正しい順番でした。実際に進んだ順に書きます。
- LINE Developersコンソールを開き、「LINEアカウント」でログイン。続けてLINEヤフー Business IDの作成と開発者登録を求められるので、氏名・メールアドレスを入れて進みます
- 「新規チャネル作成」で「Messaging API」を選ぶと上の案内が出るので、「LINE公式アカウントを作成する」ボタンから外部フォーム(entry.line.biz)へ移動します
- 最初にSMS認証があります。電話番号に届いた認証コードを入れます
- アカウント名と業種を入れて申し込むと、完了画面にベーシックID(
@で始まるID)が表示されます。このとき、連携したLINEアカウントに公式アカウントが自動で友だち追加されていました - 完了画面からLINE Official Account Managerへ移動し、設定 →「Messaging API」→「Messaging APIを利用する」を押します
- プロバイダーを選びます。この連携は一度決めると変更も解除もできないと注意書きが出ます。プライバシーポリシーと利用規約のURLは任意入力なので空のまま進めました
- ステータスが「利用中」になり、Channel IDが表示されたら完了です
Webhook URLは設定していません。今回は送るだけで、受信はしないからです。
3. トークンとuserIdを取得し、.zshrc に置く
LINE Developersコンソールに戻ると、さっき作った公式アカウントがチャネルとして並んでいます。ここで控えておく値は2つです。
- チャネルアクセストークン(長期):「Messaging API設定」タブの下部で発行します。失効日はありません。⚠「再発行」を押すと古いトークンがすぐ失効するので、動き始めた後は押さないことにしました
-
自分のuserId:「チャネル基本設定」タブの下部「あなたのユーザーID」に出ています。
Uで始まる33文字で、push送信の宛先になります
2つを ~/.zshrc に書きます。
export LINE_CHANNEL_TOKEN="<チャネルアクセストークン>"
export LINE_USER_ID="<Uで始まる33文字>"
4. テスト送信1通
スクリプトを書く前に、curl 1本で通ることを確かめました。
curl -X POST https://api.line.me/v2/bot/message/push \
-H "Authorization: Bearer $LINE_CHANNEL_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"to\":\"$LINE_USER_ID\",\"messages\":[{\"type\":\"text\",\"text\":\"テスト送信です\"}]}"
HTTP 200と sentMessages が返り、スマホのLINEに「テスト送信です」が届きました。
無料通数が気になる場合は、これもAPIで見られます。上限と今月の消費数です。
curl -H "Authorization: Bearer $LINE_CHANNEL_TOKEN" https://api.line.me/v2/bot/message/quota
# → {"type":"limited","value":200}
curl -H "Authorization: Bearer $LINE_CHANNEL_TOKEN" https://api.line.me/v2/bot/message/quota/consumption
# → {"totalUsage":1}
5. スクリプト本体
決めたことは2つです。
-
通知は残高+前日比。残高照会APIの応答に
previousDayBalanceが入っているので、呼ぶAPIは1本で足ります - 残高の取得に失敗した朝も、その旨をLINEへ送る。通知が来ない朝が「取得の失敗」なのか「スクリプトが動かなかった」なのか、受け取る側から見分けられなくなるのを防ぐためです。ただしLINE送信そのものが失敗した朝はログにしか残りません。そこまでの備えは足していません
#!/bin/bash
# sunabar の残高を毎朝 LINE へ送る
LOG="$HOME/Library/Logs/sunabar_line_balance.log"
TAKEN_FILE="$HOME/Library/Scripts/sunabar_token_taken.txt"
TODAY=$(date "+%Y-%m-%d")
# launchd はシェルの設定を読まないので、環境変数は zsh -ic で取り出す
TOKEN=$(zsh -ic 'echo $SUNABAR_TOKEN' 2>/dev/null | tail -1)
LINE_TOKEN=$(zsh -ic 'echo $LINE_CHANNEL_TOKEN' 2>/dev/null | tail -1)
LINE_TO=$(zsh -ic 'echo $LINE_USER_ID' 2>/dev/null | tail -1)
RES=$(curl -s -m 15 -w "\n%{http_code}" \
-H "accept: application/json;charset=UTF-8" \
-H "x-access-token: $TOKEN" \
"https://api.sunabar.gmo-aozora.com/personal/v1/accounts/balances")
CODE=$(echo "$RES" | tail -1)
BODY=$(echo "$RES" | sed '$d')
if [ "$CODE" = "200" ]; then
MSG=$(echo "$BODY" | /usr/bin/python3 -c '
import sys, json
d = json.load(sys.stdin)["balances"][0]
bal = int(d["balance"]); prev = int(d["previousDayBalance"])
diff = bal - prev
sign = "+" if diff >= 0 else "-"
print("sunabar残高 " + d["baseDate"])
print("残高 {:,}円(前日比 {}{:,}円)".format(bal, sign, abs(diff)))
' 2>/dev/null)
if [ -z "$MSG" ]; then
MSG="sunabar残高 $TODAY
⚠ 残高は取れたが読み取りに失敗(ログ参照)"
fi
else
MSG="sunabar残高 $TODAY
⚠ 残高の取得に失敗(HTTP $CODE)。トークンの30日失効かもしれない"
fi
# トークン30日失効の予告(取得から25日を超えたら1行足す)
if [ -f "$TAKEN_FILE" ]; then
TAKEN=$(head -1 "$TAKEN_FILE" | tr -d ' \n')
TAKEN_EPOCH=$(date -j -f "%Y-%m-%d" "$TAKEN" "+%s" 2>/dev/null)
if [ -n "$TAKEN_EPOCH" ]; then
DAYS=$(( ($(date "+%s") - TAKEN_EPOCH) / 86400 ))
if [ "$DAYS" -gt 25 ]; then
MSG="$MSG
⚠ sunabarトークン取得から${DAYS}日。ポータルで取り直して sunabar_token_taken.txt の日付を書き換える"
fi
fi
fi
PAYLOAD=$(MSG="$MSG" LINE_TO="$LINE_TO" /usr/bin/python3 -c 'import os,json;print(json.dumps({"to":os.environ["LINE_TO"],"messages":[{"type":"text","text":os.environ["MSG"]}]}))')
LINE_CODE=$(curl -s -m 15 -o /dev/null -w "%{http_code}" \
-X POST "https://api.line.me/v2/bot/message/push" \
-H "Authorization: Bearer $LINE_TOKEN" \
-H "Content-Type: application/json" \
-d "$PAYLOAD")
echo "$TODAY $(date "+%H:%M:%S") sunabar HTTP $CODE / LINE HTTP $LINE_CODE" >> "$LOG"
文面をJSONにするところはPythonに任せました。文面に改行や記号が入るので、shellの文字列連結で組み立てるより事故が少ないと考えたからです。
6. launchdで毎朝8時30分に動かす
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.tri-ponte.sunabar-line-balance</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>/Users/<ユーザー名>/Library/Scripts/sunabar_line_balance.sh</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>8</integer>
<key>Minute</key>
<integer>30</integer>
</dict>
<key>StandardOutPath</key>
<string>/Users/<ユーザー名>/Library/Logs/sunabar_line_balance_launchd.log</string>
<key>StandardErrorPath</key>
<string>/Users/<ユーザー名>/Library/Logs/sunabar_line_balance_launchd.log</string>
</dict>
</plist>
登録は1回です。
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.tri-ponte.sunabar-line-balance.plist
launchd まわりで確かめたことが2つあります。
-
launchdはシェルの設定を読みません。
.zshrcのexportはそのままでは届かないので、スクリプト側でzsh -icを使って取り出しています(5章のスクリプト冒頭) -
8時30分にMacが起きているか。
pmset -g customでsleep 0(本体はスリープしない設定)を確認しました。スリープする設定のMacでは、StartCalendarIntervalの実行は復帰後に持ち越されるので、通知が遅れて届きます
7. 30日で失効するsunabarトークンを切らさない
sunabarのアクセストークンは30日で失効します。起算点は「ポータルへの最終ログイン」です。
そこで、トークンを取得した日を sunabar_token_taken.txt に1行だけ書いておき、スクリプトが25日を超えたら通知の末尾に予告を1行足すことにしました(5章のスクリプト後半)。
2026-08-28
起算点がポータルのログイン日なので、この予告は実際より早く鳴ることがあります。切れてから気づくよりは早く鳴るほうを選びました。ポータルにログインした日にこのファイルの日付を書き換えれば、ずれは消えます。
まとめ
| # | 分かったこと |
|---|---|
| 1 | LINE Notify終了後にLINEへ通知を送るなら、LINE公式アカウント+Messaging API |
| 2 | 2026年9月時点では、Developersコンソールからチャネルを直接作れない。公式アカウントが先 |
| 3 | プロバイダーと公式アカウントの連携は、一度決めると変更も解除もできない |
| 4 | 無料プランは月200通。上限も消費数もAPIで見られる |
| 5 | 残高照会APIの応答に前日残高が入っているので、前日比はAPI1本で作れる |
| 6 | launchdはシェルの設定を読まない。環境変数は zsh -ic で取り出す |
| 7 | 取得に失敗した朝も送ると決めておくと、通知が来ない朝の意味が1つに絞れる |
3本の連載で作ったものは、shellスクリプト1本とplist 1枚で毎朝の通知になりました。いちばん時間を使ったのは、APIでもlaunchdでもなくLINE公式アカウントの開設です。