ServiceChannel webhook processor, proposal approval cards, attachment auto-post, CollabSupport commands, and Docker deployment configuration. Co-authored-by: Cursor <cursoragent@cursor.com>
111 lines
No EOL
4.7 KiB
JavaScript
111 lines
No EOL
4.7 KiB
JavaScript
//const { GraphQLClient, gql } = require('graphql-request');
|
|
import { GraphQLClient, gql } from 'graphql-request';
|
|
|
|
async function getStoreDevicesStatus(storeNumber, apiKey) {
|
|
const endpoint = 'https://graphql-gateway.optisigns.com/graphql';
|
|
|
|
const client = new GraphQLClient(endpoint, {
|
|
headers: { Authorization: `Bearer ${apiKey}` },
|
|
});
|
|
|
|
const paddedStore = storeNumber.toString().padStart(6, '0');
|
|
|
|
const query = gql`
|
|
query GetDevices($first: Int, $after: String) {
|
|
devices(query: {}, first: $first, after: $after) {
|
|
page {
|
|
edges {
|
|
node {
|
|
_id
|
|
deviceName
|
|
UUID
|
|
pairingCode
|
|
currentType
|
|
currentAssetId
|
|
currentPlaylistId
|
|
localAppVersion
|
|
lastHeartBeat # Confirmed field!
|
|
# status # still null in sample, but can keep if it populates sometimes
|
|
# updatedAt # if you see it later
|
|
}
|
|
}
|
|
pageInfo { hasNextPage endCursor }
|
|
}
|
|
}
|
|
}
|
|
`;
|
|
|
|
let allDevices = [];
|
|
let after = null;
|
|
const pageSize = 50;
|
|
|
|
try {
|
|
do {
|
|
const data = await client.request(query, { first: pageSize, after });
|
|
allDevices = allDevices.concat(data.devices.page.edges.map(e => e.node));
|
|
after = data.devices.page.pageInfo.hasNextPage ? data.devices.page.pageInfo.endCursor : null;
|
|
} while (after);
|
|
|
|
const storeDevices = allDevices.filter(d => d.deviceName?.includes(paddedStore));
|
|
|
|
if (storeDevices.length === 0) {
|
|
|
|
return [];
|
|
}
|
|
|
|
|
|
return storeDevices;
|
|
/* storeDevices.forEach(device => {
|
|
let statusLine = '• **Playback Status:** Idle / Not playing';
|
|
let details = '';
|
|
|
|
if (device.currentPlaylistId || device.currentAssetId) {
|
|
const isPlaylist = device.currentType === 'PLAYLIST' || !!device.currentPlaylistId;
|
|
statusLine = `• **Playback Status:** Active - Playing content (${isPlaylist ? 'Playlist' : 'Asset/Single'})`;
|
|
details = `\n - Assigned ID: ${device.currentPlaylistId || device.currentAssetId || 'N/A'}\n - Type: ${device.currentType || 'unknown'}\n - App Version: ${device.localAppVersion || 'N/A'}`;
|
|
}
|
|
|
|
// Heartbeat / Connectivity Status
|
|
let heartbeatInfo = '';
|
|
if (device.lastHeartBeat) {
|
|
const hbDate = new Date(device.lastHeartBeat);
|
|
const now = new Date(); // Current time in UTC, then we'll adjust display
|
|
const minutesAgo = Math.round((now - hbDate) / 60000);
|
|
|
|
const hbLocal = hbDate.toLocaleString('en-US', { timeZone: 'America/New_York' });
|
|
heartbeatInfo = `\n - Last Heartbeat: ${hbLocal} (${minutesAgo} min ago)`;
|
|
|
|
if (minutesAgo <= 10) {
|
|
heartbeatInfo += ' 🟢 **Online & checking in** (likely synced)';
|
|
} else if (minutesAgo <= 30) {
|
|
heartbeatInfo += ' 🟡 **Possibly delayed**';
|
|
} else {
|
|
heartbeatInfo += ' 🔴 **Offline / not checking in**';
|
|
}
|
|
} else {
|
|
heartbeatInfo = '\n - No heartbeat data available';
|
|
}
|
|
|
|
md += `**${device.deviceName}** (${device.UUID.substring(0, 8)}...)\n`;
|
|
md += `${statusLine}\n`;
|
|
md += `${details}${heartbeatInfo}\n\n`;
|
|
});
|
|
|
|
md += `**Last checked:** ${new Date().toLocaleString('en-US', { timeZone: 'America/New_York' })} EST\n`;
|
|
md += `(via OptiSigns API)`;
|
|
|
|
console.log(md);
|
|
return md;
|
|
*/
|
|
|
|
} catch (error) {
|
|
const errMd = `**Error:** ${error.message}`;
|
|
console.error(errMd);
|
|
return errMd;
|
|
}
|
|
}
|
|
// Example usage
|
|
const apiKey = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiJnaEF3SEhKdTdkS3RmcEdkTSIsImNpZCI6Ilk2cXlaZXAzSE1NeHBuYXRMIiwiYWlkIjoidHh1NGhUcnZEMkdaM25tOU0iLCJpYXQiOjE3NDc0MTAxNjgsImV4cCI6MTc0NzQxMzc2OCwiaXNzIjoidHh1NGhUcnZEMkdaM25tOU0ifQ.TZRzzV6-QejdW4H3vppeypzExv4ppHdIKHSHpZXbKmpDhktlWrOQLQf4sNeX-UPa0bKKQcp9b8qHGpeu163bhmQAnnkWROqMD2z8tZ2kKgW34iXH5B_Ur1TbylvUS_ZsfFDQiUnT1Lf9waMAhZJeYKP4j70ak7BclMFf7XMGNGy75vSknzi1x7nvB1AOS5kEcPIV_oZNXlBVLLnEM1t3Edk6K-jXDmR_XHqN0lI3t0NX0yr2VTgt2KSqDRPaGcNBmuXHUgeB8xsEW4Dlk8rIin7qW4WkrvycnjyL2ZpT9EFZWYjaQNyJoTmJjxeywl9JrzrO1sIoYJ0QdHAOmgsnIDiYnw_eR73Mi0l3QUC9fZfgD91gidwKMWt3LIm4KCkynRNPOlBz7UGTp_wb4qCSFCNiYB6fGhWT1u7rcoR1g7sCHwCX6pkSMhKs1ZSOprTZSUFeFloqE3ufCNACQMa5I8QYi4Lz7KdEOn9yK2UM13Te1Shs3fD2HS23pyiLjznBMQg9z5TWTzbPWteR7FyKIRZjtdF1Foe5BJp-EmN_FR74Ndz1lo-0b10stszQE18ob9HSWsmWu_Tqg_KG4wBxOzKM-V8Eksx4eFyHeh8lzGXESzH2pGYfs7M3Y5Dvy1a3Hy9Xh7O0n82EW9MUHTXiny3yycD9vuUyG4FNQ7K2NuY'; // Replace with your actual key
|
|
const storeNumber = "002248";
|
|
const deviceName = 'US000629VW02'; // Replace with your device name (e.g., incorporating the 6-digit store number)
|
|
getStoreDevicesStatus(storeNumber, apiKey); |