servchan/discover-note-timestamps.js
jmcqueen c2e98d105e Initial commit: ServChan Webex bot for ServiceChannel work orders.
ServiceChannel webhook processor, proposal approval cards, attachment auto-post,
CollabSupport commands, and Docker deployment configuration.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-24 15:12:08 -04:00

107 lines
3.1 KiB
JavaScript

/**
* Temporary discovery script.
* Goal: Inspect the shape of notes returned by the ServiceChannel API
* to determine the correct timestamp field name.
*
* Run with: node discover-note-timestamps.js
*
* This script now loads credentials from environment variables (via .env)
* — same as the main app — so there's no plaintext credential in this file.
* Feel free to delete once you're done using it.
*/
import 'dotenv/config';
import axios from 'axios';
import qs from 'node:querystring';
const SC = {
clientId: process.env.SC_CLIENT_ID,
clientSecret: process.env.SC_CLIENT_SECRET,
username: process.env.SC_USERNAME,
password: process.env.SC_PASSWORD,
baseUrl: process.env.SC_BASE_URL || 'https://api.servicechannel.com/v3',
oauthUrl: process.env.SC_OAUTH_URL || 'https://login.servicechannel.com/oauth/token',
};
if (!SC.clientId || !SC.clientSecret || !SC.username || !SC.password) {
console.error('Missing ServiceChannel credentials. Set SC_CLIENT_ID, SC_CLIENT_SECRET, SC_USERNAME, SC_PASSWORD in .env.');
process.exit(1);
}
async function getToken() {
const basicAuth = Buffer.from(`${SC.clientId}:${SC.clientSecret}`).toString('base64');
const response = await axios.post(
SC.oauthUrl,
qs.stringify({
grant_type: 'password',
username: SC.username,
password: SC.password,
}),
{
headers: {
'Authorization': `Basic ${basicAuth}`,
'Content-Type': 'application/x-www-form-urlencoded',
},
timeout: 15000,
}
);
return response.data.access_token;
}
async function main() {
const workOrderId = Number(process.argv[2] || 352088878);
console.log(`Discovering note structure for work order ${workOrderId}...\n`);
try {
const token = await getToken();
const notesRes = await axios.get(
`${SC.baseUrl}/workorders/${workOrderId}/notes`,
{
headers: { Authorization: `Bearer ${token}` },
timeout: 30000,
}
);
const notes = notesRes.data?.Notes || notesRes.data || [];
console.log(`Found ${notes.length} notes.\n`);
if (notes.length === 0) {
console.log("No notes found on this work order.");
return;
}
const firstNote = notes[0];
console.log("Keys on the first note object:");
console.log(Object.keys(firstNote));
console.log("\n");
console.log("=== First note (full object) ===");
console.dir(firstNote, { depth: null, colors: true });
console.log("\n=== Last 3 notes - looking for timestamp fields ===");
const lastNotes = notes.slice(-3).reverse();
lastNotes.forEach((note, i) => {
console.log(`\nNote ${i + 1}:`);
const possibleTimestampFields = Object.keys(note).filter(k =>
/time|date|stamp|created|updated/i.test(k)
);
console.log(" Possible timestamp fields:", possibleTimestampFields);
possibleTimestampFields.forEach(field => {
console.log(` ${field}: ${note[field]}`);
});
});
} catch (err) {
console.error("Error:", err.response?.data || err.message);
if (err.response) {
console.error("Status:", err.response.status);
}
}
}
main();