- Reset master to upstream/main (16,697 commits) - Overlay 2,271 local-only files (skills, tools, workspace, configs, apps) - Restore IDENTITY.md and USER.md templates - Build verified, gateway running, Discord working Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
115 lines
3.6 KiB
JavaScript
115 lines
3.6 KiB
JavaScript
#!/usr/bin/env node
|
||
|
||
const fs = require("fs");
|
||
const https = require("https");
|
||
const { homedir } = require("os");
|
||
const { join } = require("path");
|
||
|
||
function httpsRequest(url, options = {}) {
|
||
return new Promise((resolve, reject) => {
|
||
const req = https.request(url, options, (res) => {
|
||
let data = "";
|
||
res.on("data", (chunk) => (data += chunk));
|
||
res.on("end", () => {
|
||
if (res.statusCode >= 400) {
|
||
reject(new Error(`HTTP ${res.statusCode}: ${data}`));
|
||
} else {
|
||
resolve(JSON.parse(data));
|
||
}
|
||
});
|
||
});
|
||
req.on("error", reject);
|
||
if (options.body) req.write(options.body);
|
||
req.end();
|
||
});
|
||
}
|
||
|
||
async function getAccessToken(creds) {
|
||
const body = new URLSearchParams({
|
||
client_id: creds.client_id,
|
||
client_secret: creds.client_secret,
|
||
refresh_token: creds.refresh_token,
|
||
grant_type: "refresh_token",
|
||
}).toString();
|
||
|
||
const data = await httpsRequest("https://oauth2.googleapis.com/token", {
|
||
method: "POST",
|
||
headers: {
|
||
"Content-Type": "application/x-www-form-urlencoded",
|
||
"Content-Length": Buffer.byteLength(body),
|
||
},
|
||
body,
|
||
});
|
||
|
||
return data.access_token;
|
||
}
|
||
|
||
async function main() {
|
||
const spreadsheetId = process.argv[2] || "1G6KEBQER_qBI3Hau8GUBrhEOf4FrHMtmKypPjD_8pBw";
|
||
|
||
const credsPath = join(homedir(), ".openclaw/secrets/google-oauth.json");
|
||
const creds = JSON.parse(fs.readFileSync(credsPath, "utf8"));
|
||
|
||
const accessToken = await getAccessToken(creds);
|
||
|
||
console.log(`📊 Fetching Google Sheet: ${spreadsheetId}\n`);
|
||
|
||
// Get spreadsheet metadata
|
||
const metaUrl = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}`;
|
||
|
||
const metadata = await httpsRequest(metaUrl, {
|
||
headers: { Authorization: `Bearer ${accessToken}` },
|
||
});
|
||
|
||
console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
||
console.log(`📄 Title: ${metadata.properties.title}`);
|
||
console.log(
|
||
`📅 Last updated: ${new Date(metadata.properties.modifiedTime).toLocaleString("da-DK")}`,
|
||
);
|
||
console.log(`📑 Sheets: ${metadata.sheets.length}`);
|
||
console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
|
||
|
||
// List all sheets
|
||
for (const sheet of metadata.sheets) {
|
||
const title = sheet.properties.title;
|
||
const rows = sheet.properties.gridProperties.rowCount;
|
||
const cols = sheet.properties.gridProperties.columnCount;
|
||
console.log(`📊 Sheet: "${title}" (${rows} rows × ${cols} columns)`);
|
||
}
|
||
|
||
console.log("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
|
||
|
||
// Get data from first sheet
|
||
const firstSheet = metadata.sheets[0].properties.title;
|
||
const range = `${firstSheet}!A1:Z100`; // Get first 100 rows
|
||
|
||
const dataUrl = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${encodeURIComponent(range)}`;
|
||
|
||
const data = await httpsRequest(dataUrl, {
|
||
headers: { Authorization: `Bearer ${accessToken}` },
|
||
});
|
||
|
||
if (data.values && data.values.length > 0) {
|
||
console.log(`📋 First sheet data (${data.values.length} rows):\n`);
|
||
|
||
// Print first 20 rows
|
||
for (let i = 0; i < Math.min(20, data.values.length); i++) {
|
||
const row = data.values[i];
|
||
if (row && row.length > 0) {
|
||
console.log(`Row ${i + 1}: ${row.join(" | ")}`);
|
||
}
|
||
}
|
||
|
||
if (data.values.length > 20) {
|
||
console.log(`\n... and ${data.values.length - 20} more rows`);
|
||
}
|
||
} else {
|
||
console.log("⚠️ No data found in first sheet");
|
||
}
|
||
}
|
||
|
||
main().catch((err) => {
|
||
console.error("❌ Error:", err.message);
|
||
process.exit(1);
|
||
});
|