google-sheets-social
Đọc/ghi 1 Google Sheet dùng cho việc theo dõi chia sẻ bài (content-distribution tracking sheet) qua Google Sheets API v4. Skill hạ tầng thuần túy — KHÔNG viết lại nội dung, KHÔNG đăng bài lên bất kỳ nền tảng nào; agent share-bai-social dùng skill này để đọc cấu trúc cột/dòng của sheet rồi tự quyết định gọi skill share-bai-* nào, sau đó dùng lại skill này để ghi link kết quả về sheet.
Khi nào dùng
- Agent
share-bai-socialgọi qua Skill tool để đọc sheet trước khi xử lý, và ghi lại link sau khi publish xong 1 platform. - User trực tiếp yêu cầu "đọc sheet chia sẻ bài", "kiểm tra cột nào đã tích", "ghi link X vào ô Y trong sheet".
KHÔNG dùng skill này khi:
- User muốn viết lại nội dung bài hoặc đăng bài lên 1 nền tảng cụ thể — đó là các skill
share-bai-*. - User muốn thao tác Google Sheets không liên quan sheet chia sẻ bài (vd tạo sheet mới, format cell, chart...) — ngoài phạm vi skill này.
Tiền điều kiện
- Đã có 1 OAuth client RIÊNG cho skill này (KHÔNG dùng chung client_id với
share-bai-blogger/share-bai-ggrhay skill khác — xem lý do trong "Known limits") đã bật Google Sheets API, và đã lấy đượcrefresh_tokencho đúng tài khoản Google có quyền sửa sheet đích — Step 2 hướng dẫn nếu chưa có. - File
sheets-accounts.local.jsonnằm ngay trong thư mục skill này (.claude/skills/google-sheets-social/sheets-accounts.local.json) — lưuclient_id/client_secret/access_token/refresh_token/spreadsheet_id/sheet_nametừng sheet. Nếu chưa có, Step 2 sẽ tạo. - Mọi lệnh HTTP/JSON BẮT BUỘC gọi qua
scripts/sheets-http.shvàscripts/sheets-json.js— KHÔNG gọicurl/node -etrực tiếp. 2 script này cần allowlist trong.claude/settings.jsonđể agentshare-bai-social(chạy nền) không bị treo vì permission prompt.
Default settings
| Setting | Default | Override khi |
|---|---|---|
| Sheet đích khi không chỉ định | Sheet duy nhất có trong sheets-accounts.local.json |
Nhiều sheet trong file → hỏi user chọn |
| Nguồn xác định nền tảng của 1 cột | Hyperlink thật gắn trên header cell | Header không có hyperlink → fallback text, cảnh báo caller có thể nhầm |
| Access token | Tự refresh mỗi lần chạy bằng refresh_token đã lưu (silent, không hỏi user) |
— |
| Ghi cell | Luôn ghi đúng 1 ô/lần gọi (valueInputOption=USER_ENTERED) |
— (API không hỗ trợ batch trong skill này) |
Pipeline — 4 bước
Step 1 — Xác định sheet đích
Parse spreadsheet_id từ URL sheet user cung cấp (đoạn giữa /d/ và /edit), và tên tab (sheet_name, thấy trên URL param gid hoặc user cho biết trực tiếp tên tab). Không có sheet-key nào khớp trong sheets-accounts.local.json → coi là sheet mới, sang Step 2.
Exit condition: có spreadsheet_id + sheet_name + xác định được sheet-key.
Step 2 — Đảm bảo có OAuth client + token hợp lệ
Đọc references/sheets-api-steps.md toàn bộ.
- Chưa có entry → hướng dẫn user tạo 1 OAuth Client ID RIÊNG cho skill này (mục "Setup OAuth" — mặc định KHÔNG tái sử dụng client_id của skill khác, trừ khi user chủ động yêu cầu và chấp nhận rủi ro 50-token-cap), lưu bằng
set-client. - Có
client_id/client_secretnhưng chưa córefresh_token→ chạysheets-oauth-server.js, lưu bằngsave-tokens. - Đã có
refresh_token→ gọirefresh-tokenlấy access_token mới, lưu bằngsave-refreshed-access-token.
Decision point: mặc định luôn đề xuất Client ID riêng, không tự ý dùng chung client với skill khác. API trả lỗi permission/token hết hạn → coi như mất token, quay lại bước tương ứng.
Step 3 — Đọc sheet
bash scripts/sheets-http.sh get-sheet-data <access_token> <spreadsheet_id> <sheet_name> <out-file>
node scripts/sheets-json.js parse-sheet-data <out-file> <parsed-file>
Exit condition: parsed-file có headers và rows non-empty. checkboxHeaderText là null (không nhận diện được cột checkbox) → báo caller rõ ràng, không đoán cột nào là checkbox.
Step 4 — Ghi 1 cell
node scripts/sheets-json.js build-a1-range "<sheet_name>" <column-index-hoặc-chữ> <row-number>
node scripts/sheets-json.js build-update-cell-body "<value>" <payload-file>
bash scripts/sheets-http.sh update-cell <access_token> <spreadsheet_id> "<a1-range>" <payload-file> <out-file>
node scripts/sheets-json.js show-update-result <out-file>
Exit condition: response có updatedCells: 1, không có error.
Decision points
| Step | Hỏi user khi | Auto-proceed khi |
|---|---|---|
| 1 | Không xác định được spreadsheet_id/sheet_name | URL sheet hợp lệ, tab rõ ràng |
| 2 | Chưa có OAuth client, hoặc chưa có refresh_token — hướng dẫn tạo Client ID riêng, không tự dùng chung | Đã có đủ access_token cho sheet-key đích |
| 3-4 | — (không hỏi, dừng nếu không đọc/ghi được) | Đọc/ghi thành công |
Recovery
parse-sheet-databáocheckboxHeaderText: null→ dừng, báo caller tên các header thực tế để user xác nhận cột nào là checkbox, không tự đoán.- API trả 401/403 → access_token hết hạn hoặc thiếu quyền — refresh lại theo Step 2; nếu vẫn lỗi,
refresh_tokencó thể đã bị revoke, quay lại lấy refresh_token mới. update-celltrả lỗi range không hợp lệ (sai tên tab, cell ngoài phạm vi) → kiểm tra lạisheet_namevàrowIndex/cột dùng từ đúng dữ liệu Step 3 trả về, không tự bịa toạ độ.
Output format
Khi đọc xong, trả nguyên văn nội dung file JSON từ parse-sheet-data cho caller (agent/user) tự xử lý tiếp. Khi ghi xong, trả kết quả từ show-update-result:
Đã ghi <value> vào <a1-range>.
Ghi lỗi → báo rõ mã lỗi + message, không báo "đã ghi thành công".
Anti-patterns
- KHÔNG dùng text hiển thị của header để xác định nền tảng — luôn ưu tiên
hyperlink, vì các tên hiển thị có thể giống nhau nhưng trỏ domain khác (2 instance Mastodon khác nhau là ví dụ thật đã gặp). - KHÔNG tự đoán cột nào là checkbox khi
checkboxHeaderTexttrả vềnull— dừng và hỏi thay vì đoán. - KHÔNG lưu client_secret/access_token/refresh_token dạng plaintext trong SKILL.md, chat log, hay file được commit — chỉ lưu trong
sheets-accounts.local.json, thêm vào.gitignorenếu có. - KHÔNG ghi đè 1 cell đã có giá trị mà không được caller xác nhận — skill này chỉ thực thi lệnh ghi được yêu cầu, việc kiểm tra "cell đã có link chưa" là trách nhiệm của caller (vd agent
share-bai-social) trước khi gọiupdate-cell. - KHÔNG tự ý dùng chung 1 OAuth client_id với
share-bai-blogger/share-bai-ggrhay skill khác — mỗi mục đích cần Client ID riêng để tránh chạm giới hạn 50 refresh_token/cặp (client, tài khoản) của Google (xem "Known limits").
Skill files
| File | Purpose | Load when |
|---|---|---|
references/sheets-api-steps.md |
Chi tiết OAuth, đọc sheet, ghi cell, A1 notation, xử lý sheet name có dấu cách | Step 2, 3, 4 |
scripts/sheets-http.sh |
Thực thi mọi lệnh HTTP (refresh-token, get-sheet-data, update-cell) — allowlisted trong .claude/settings.json |
Step 2, 3, 4 |
scripts/sheets-json.js |
Thực thi mọi xử lý JSON (đọc/lưu credential, parse sheet, build A1 range, build payload, đọc kết quả) — allowlisted trong .claude/settings.json |
Step 1, 2, 3, 4 |
scripts/sheets-oauth-server.js |
Chạy OAuth flow lần đầu: mở local server tạm bắt redirect code, đổi lấy token | Step 2 (chỉ lần đầu/sheet) |
sheets-accounts.local.json |
Credential từng sheet (client_id/client_secret/access_token/refresh_token/spreadsheet_id/sheet_name) — KHÔNG commit, chỉ đọc/ghi qua scripts/sheets-json.js |
Step 2, 3, 4 |