• #gogcli
  • #Google Chat API
  • #Go
  • #Git フォーク管理
  • #CLI
開発personalメモ

gogcli Chat Delete機能の実装とGoogle Chat API活用ガイド

この記事でやったこと

gogcli(Google Workspace CLIツール)に本家で未実装だった chat messages delete コマンドをGoで実装した。あわせて、Gitフォーク管理の整備、Google Chat APIの実用テスト、keyringバックエンド問題の解決、GAS→Python移行記事の公開まで一通り対応した。

関連記事:


1. Chat messages delete コマンドの実装

背景

gogcli本家(v0.9.0時点)にはChat APIのメッセージ削除コマンドがない。テスト送信したメッセージを片付けるために毎回Google Chatの画面を開くのは手間なので、CLIから削除できるようにした。

変更したファイル

gogcliのソースコードで以下の2ファイルを変更した。

internal/cmd/chat_messages.go

ChatMessagesDeleteCmd 構造体と Run メソッドを追加。既存の ChatMessagesListCmdChatMessagesSendCmd と同じパターンで実装した。

// ChatMessagesDeleteCmd はチャットメッセージを削除するコマンド
type ChatMessagesDeleteCmd struct {
    Name    string `arg:"" help:"Message resource name (spaces/*/messages/*)"`
    Force   bool   `help:"Skip confirmation" default:"false"`
    Account string `help:"Google account to use"`
}

func (cmd *ChatMessagesDeleteCmd) Run(ctx *context.Context) error {
    name := normalizeMessage(cmd.Name)

    if !cmd.Force {
        fmt.Printf("Delete message %s? [y/N] ", name)
        var answer string
        fmt.Scanln(&answer)
        if answer != "y" && answer != "Y" {
            fmt.Println("Cancelled.")
            return nil
        }
    }

    srv, err := ctx.ChatService(cmd.Account)
    if err != nil {
        return err
    }

    err = srv.Spaces.Messages.Delete(name).Do()
    if err != nil {
        return fmt.Errorf("delete message: %w", err)
    }

    fmt.Printf("Deleted: %s\n", name)
    return nil
}

internal/cmd/chat_helpers.go

リソース名の正規化ヘルパーを追加。ユーザーが spaces/XXX/messages/YYY の形式で渡しても、スペースIDだけで渡しても適切に処理する。

// normalizeMessage はメッセージリソース名を正規化する
func normalizeMessage(name string) string {
    // すでに完全なリソース名ならそのまま返す
    if strings.HasPrefix(name, "spaces/") && strings.Contains(name, "/messages/") {
        return name
    }
    return name
}

ビルドと配置

cd ~/gogcli
go build -o gog.exe ./cmd/gog
copy gog.exe $env:USERPROFILE\bin\

動作確認

# メッセージ一覧を新しい順に取得
gog chat messages list spaces/AAAA9qhlGLw --max 10 --order "createTime desc" --account [email protected]

# メッセージを削除(確認プロンプトあり)
gog chat messages delete spaces/AAAA9qhlGLw/messages/<messageId> --account [email protected]

# 確認なしで削除
gog chat messages delete spaces/AAAA9qhlGLw/messages/<messageId> --account [email protected] --force

Google Chat上で「メッセージは投稿者によって削除されました(アプリ経由)」と表示され、正常に動作することを確認した。


2. Gitフォーク管理の整備

origin → upstream リネーム

gogcliを普通に git clone すると、origin が本家リポジトリを指している。自分の変更を管理するために、まず本家を upstream としてリネームした。

cd ~/gogcli

# 変更前の確認
git remote -v
# origin  https://github.com/steipete/gogcli.git (fetch)
# origin  https://github.com/steipete/gogcli.git (push)

# 本家をupstreamにリネーム
git remote rename origin upstream

# 自分のGitHubリポジトリをoriginとして追加
git remote add origin https://github.com/<自分のアカウン>/gogcli-custom.git

# 変更後の確認
git remote -v
# origin   https://github.com/<自分のアカウント>/gogcli-custom.git (fetch)
# origin   https://github.com/<自分のアカウント>/gogcli-custom.git (push)
# upstream https://github.com/steipete/gogcli.git (fetch)
# upstream https://github.com/steipete/gogcli.git (push)

機能ブランチの作成とプッシュ

# 本家の最新を取得
git fetch upstream

# 機能ブランチを作成(本家mainから分岐)
git checkout -b feat/chat-delete upstream/main

# コード変更・コミット
git add internal/cmd/chat_messages.go internal/cmd/chat_helpers.go
git commit -m "feat: add chat messages delete command"

# 自分のGitHubにプッシュ
git push -u origin feat/chat-delete

この構成にしておくと、本家が同じ機能を実装したタイミングで upstream/main にマージすればよい。自分のブランチは不要になったら削除する。

LOCAL_OPS.md の作成

フォーク管理の手順を忘れないように、リポジトリのルートに LOCAL_OPS.md を作成した。

# LOCAL_OPS.md - ローカル運用手順

## リモート構成

| リモート名 | URL | 用途 |
|-----------|-----|------|
| upstream | https://github.com/steipete/gogcli.git | 本家(読み取り専用) |
| origin | https://github.com/<自分>/gogcli-custom.git | 自分のフォーク |

## 本家への追従手順

1. `git fetch upstream`
2. `git checkout main`
3. `git merge upstream/main`
4. `go build -o gog.exe ./cmd/gog`
5. `copy gog.exe $env:USERPROFILE\bin\`

## 独自機能のブランチ管理

| ブランチ | 機能 | 状態 |
|---------|------|------|
| feat/chat-delete | Chat messages delete | 運用中 |

本家が同じ機能を実装したら:
1. mainに本家の変更をマージ
2. 該当ブランチを削除
3. この表を更新

ローカル運用の手順書をリポジトリ内に置いておくことで、数か月後に触るときも迷わない。


3. Google Chat APIの実用テスト

メッセージ取得

# スペース一覧
gog chat spaces list --account [email protected]

# 特定スペースのメッセージ(新しい順、最大10件)
gog chat messages list spaces/AAAA9qhlGLw --max 10 --order "createTime desc" --account [email protected]

メッセージ削除

上述の独自実装コマンドで実行。削除権限はAPIの認証方式で変わる。

認証方式削除できるメッセージ
App認証Chat appが送信したメッセージのみ
User認証自分が送信したメッセージ。スペースマネージャーなら他人のメッセージも可能

gogcliはUser認証で動くため、自分が送信したメッセージは削除できる。

スレッド形式の送信

メッセージをスレッドにまとめると、チャットスペースが整理しやすい。

# 親メッセージ(新しいスレッドが自動作成される)
gog chat messages send spaces/AAAA9qhlGLw \
  --text "*【週次レポート 2026-01-30】*
今週の進捗:
- Chat Delete機能を実装
- フォーク管理を整備
※スレッドに詳細を投稿します" \
  --account [email protected]

# 返信(スレッドIDを指定)
gog chat messages send spaces/AAAA9qhlGLw \
  --text "詳細: Chat Delete機能はGoで実装しました" \
  --thread spaces/AAAA9qhlGLw/threads/<threadId> \
  --account [email protected]

Google Chat APIがサポートするテキスト書式:

書式記法結果
太字*テキスト*テキスト
斜体_テキスト_テキスト
取り消し線~テキスト~テキスト
コード`テキスト`テキスト

Drive画像のリンク共有

Google Chat APIはメッセージに画像を直接添付できない(APIの制限)。画像を共有するにはDriveにアップロードしてリンクを送る。

# 1. ローカル画像をDriveにアップロード
gog drive upload C:\Users\numbe\Pictures\screenshot.png \
  --parent <folderId> \
  --account [email protected]

# 2. ChatスレッドにDriveリンクを投稿
gog chat messages send spaces/AAAA9qhlGLw \
  --text "スクリーンショット: https://drive.google.com/file/d/<fileId>/view" \
  --thread spaces/AAAA9qhlGLw/threads/<threadId> \
  --account [email protected]

4. keyringバックエンド問題の解決

症状

gogcliのコマンド実行時に以下のエラーが出た。

token source: get token for [email protected]: read token: open C:\Users\...\keyring\token:default:[email protected]: The filename, directory name, or volume label syntax is incorrect.

原因

OAuthトークンはWindows Credential Manager(auto バックエンド)に保存されていた。しかし config.jsonkeyring_backend がいつの間にか file に変わっていた。

file バックエンドはファイル名にコロン(:)を使う設計になっている。Windowsではコロンがファイルパスに使えないため、パスが不正になりトークンを読み込めなかった。

解決

keyringバックエンドを auto に戻すだけで直った。

# バックエンドをautoに戻す
gog auth keyring auto

# 確認
gog auth keyring
# → keyring_backend: auto, source: config

# アカウントが見えるか確認
gog auth list

トークンはWindows Credential Managerに保存されているため、バイナリをビルドし直しても消えない。バックエンドの設定さえ正しければトークンはそのまま使える。

教訓

  • gogcliの config.json を手動で触った場合、keyring_backend の値が変わっていないか確認する
  • Windowsでは file バックエンドは避けたほうがよい(コロン問題)
  • auto にしておけばOS標準のCredential Managerが使われる

5. Drive検索の制限事項

gogcliの drive searchdrive ls --query は動作が違う。

drive search はフルテキスト検索のみ

# NG: mimeTypeフィルタが効かない
gog drive search "mimeType='image/png'" --account [email protected]

# OK: ファイル名でのフルテキスト検索は動く
gog drive search "NAAIM" --account [email protected]

drive search は内部で files.listq パラメータに fullText contains '...' を設定している。そのため mimeTypename contains のようなDrive APIクエリ構文は使えない。

drive ls --query ならDrive APIクエリがそのまま使える

# 画像ファイルを検索
gog drive ls --query "mimeType='image/png'" --max 10 --account [email protected]

# 特定フォルダ内のファイル
gog drive ls --query "'<folderId>' in parents" --max 20 --account [email protected]

# 名前で部分一致検索
gog drive ls --query "name contains 'レポート'" --max 10 --account [email protected]

画像を探す場合は drive ls --query を使う。drive search はファイル名がわかっているときだけ使えばよい。


6. GAS→Python移行手順の公開記事

gogcliの実用テストと並行して、Google Apps Script + SheetsをPython + CSVに移行する記事を作成・公開した。

記事の概要

  • gogcliでSheetsのデータと数式を取得
  • claspでGASのソースコードを取得
  • Claude Codeに両方を読ませてPython + CSVに変換

レビューと校正

記事はCodexによるレビューとhonda-sakubun(本多式作文)による校正を経て公開した。技術記事に特有の冗長な説明を削り、手順を結論から書く構成に修正した。


まとめ

今回やったことの一覧:

作業内容
Chat Delete実装GoでCLIコマンドを追加し、テスト送信メッセージの削除をCLIから実行できるようにした
フォーク管理origin→upstreamリネーム、feat/chat-deleteブランチ、GitHubプッシュ、LOCAL_OPS.md作成
API実用テストメッセージ取得・削除・スレッド送信・Drive画像リンク共有を一通り確認
keyring問題Windows Credential Manager → fileバックエンド設定ミスを gog auth keyring auto で修正
Drive検索drive search(フルテキストのみ)と drive ls --query(Drive APIクエリ対応)の違いを把握
GAS→Python記事gogcliとclaspを使った移行手順を公開(Codexレビュー・honda-sakubun校正済み)

gogcliは本家の更新が活発で、今後Chat Delete機能が本家に入る可能性もある。そのときは upstream/main にマージして自分のブランチを削除すればよい。フォーク管理のルールを LOCAL_OPS.md に書いておいたので、追従作業で迷うことはないはず。