Extends the single-Novi codebase into a multi-bot mass-messenger where each bot has its own token, avatar, label, and per-user authorization. - Secrets moved out of config.json: per-bot tokens in gitignored config/botTokens.json (with enabled flag), service-account OAuth in gitignored config/token.json (rewritten by refresh cron), integration and Google keys in .env. - Single Webex integration handles OAuth for all bots via a per-app redirect URI derived from OAUTH_CALLBACK_URL_TEMPLATE. - New requireBot middleware and getBotConfig helper reject requests for unknown or disabled bots at the /CollabCentral/:app boundary. - New /info endpoint plus dynamic frontend loading (sendMessage, monitorJobs) so pages self-describe per bot, including bot avatar fetched from Webex /people/me at startup. - Job draft state keyed by cookieId + appName so each bot has its own building queue; job list/detail endpoints filter by appName so users only see jobs from bots they are authorized on. - New jobDetail page for a readable per-job view; completed jobs are retained for 30 days by the cleanup cron. - Completion adaptive cards use per-bot avatar and label. - Miscellaneous fixes: off-by-two in the send loop, removed three dead send/process variants, added defensive init for jobs.* on load, dropped the deprecated crypto npm shim, and cleaned up stray logger labels and typos. Co-authored-by: Cursor <cursoragent@cursor.com>
132 lines
4.4 KiB
JavaScript
132 lines
4.4 KiB
JavaScript
var pathArray = window.location.pathname.split('/');
|
|
var appName = pathArray[2];
|
|
|
|
if (getCookie("id")) {
|
|
console.log("Found ID Cookie: " + getCookie("id"));
|
|
|
|
// Fetch bot metadata + authorization state, then render the page.
|
|
fetch('/CollabCentral/' + appName + '/info')
|
|
.then(res => res.json())
|
|
.then(info => {
|
|
document.title = info.label;
|
|
document.getElementById("appLabel").innerHTML = info.label;
|
|
document.getElementById("name").innerHTML = getCookie("displayName") || "";
|
|
document.getElementById("appName").href = info.faviconUrl;
|
|
document.getElementById("jobLink").href = "/CollabCentral/" + appName + "/sendMessage.html";
|
|
|
|
var appIcon = document.getElementById("appIcon");
|
|
appIcon.src = info.iconUrl;
|
|
appIcon.style.maxHeight = "150px";
|
|
appIcon.style.maxWidth = "150px";
|
|
|
|
if (!info.authorized) {
|
|
document.getElementById("unauthorizedPanel").classList.remove("hidden");
|
|
return;
|
|
}
|
|
|
|
document.getElementById("jobsPanel").classList.remove("hidden");
|
|
initJobTables();
|
|
})
|
|
.catch(err => console.error("Failed to load /info:", err));
|
|
} else {
|
|
console.log("No ID Cookie found.");
|
|
|
|
var randomNumber = Math.random().toString();
|
|
randomNumber = randomNumber.substring(2, randomNumber.length);
|
|
fetch('/CollabCentral/' + appName + '/authUrl')
|
|
.then(res => res.text())
|
|
.then((res) => {
|
|
window.location.href = res + randomNumber;
|
|
});
|
|
}
|
|
|
|
function initJobTables() {
|
|
$('#runningJobs').dataTable({
|
|
ajax: {
|
|
url: '/CollabCentral/' + appName + '/jobs/list/running',
|
|
dataSrc: ''
|
|
},
|
|
columns: [
|
|
{ data: 'senderDisplayName' },
|
|
{ data: 'appName' },
|
|
{ data: 'startTime' },
|
|
{ data: 'totalRecipients' },
|
|
{
|
|
data: 'completedRecipients',
|
|
render: function (val, type, row) {
|
|
if (type !== 'display' || row.jobId === undefined || row.jobId === null) return val;
|
|
return val + ' <a href="/CollabCentral/' + appName + '/jobDetail.html?jobId=' + encodeURIComponent(row.jobId) + '" style="margin-left: 0.5em; font-size: 0.85em;">view</a>';
|
|
}
|
|
}
|
|
],
|
|
searching: false,
|
|
info: false,
|
|
ordering: false,
|
|
paging: false
|
|
});
|
|
|
|
$('#scheduledJobs').dataTable({
|
|
ajax: {
|
|
url: '/CollabCentral/' + appName + '/jobs/list/scheduled',
|
|
dataSrc: ''
|
|
},
|
|
columns: [
|
|
{ data: 'senderDisplayName' },
|
|
{ data: 'appName' },
|
|
{ data: 'message' },
|
|
{ data: 'totalRecipients' },
|
|
{ data: 'scheduledFor' }
|
|
],
|
|
searching: false,
|
|
info: false,
|
|
ordering: false,
|
|
paging: false
|
|
});
|
|
|
|
$('#completedJobs').dataTable({
|
|
ajax: {
|
|
url: '/CollabCentral/' + appName + '/jobs/list/completed',
|
|
dataSrc: ''
|
|
},
|
|
columns: [
|
|
{
|
|
data: 'jobId',
|
|
render: function (jobId) {
|
|
if (jobId === undefined || jobId === null) return '';
|
|
return '<a href="/CollabCentral/' + appName + '/jobDetail.html?jobId=' + encodeURIComponent(jobId) + '">#' + jobId + '</a>';
|
|
}
|
|
},
|
|
{ data: 'senderDisplayName' },
|
|
{ data: 'appName' },
|
|
{ data: 'message.english.html' },
|
|
{ data: 'startTime' },
|
|
{ data: 'endTime' },
|
|
{ data: 'stats.totalTime' },
|
|
{ data: 'stats.webexTime' },
|
|
{ data: 'stats.averageTime' },
|
|
{ data: 'stats.succeededMsgs' },
|
|
{ data: 'stats.totalMsgs' },
|
|
{ data: 'stats.succeededMsgPct' }
|
|
],
|
|
searching: false,
|
|
info: false,
|
|
ordering: false,
|
|
paging: false
|
|
});
|
|
}
|
|
|
|
function getCookie(cname) {
|
|
let name = cname + "=";
|
|
let decodedCookie = decodeURIComponent(document.cookie);
|
|
let ca = decodedCookie.split(';');
|
|
for (let i = 0; i < ca.length; i++) {
|
|
let c = ca[i];
|
|
while (c.charAt(0) == ' ') {
|
|
c = c.substring(1);
|
|
}
|
|
if (c.indexOf(name) == 0) {
|
|
return c.substring(name.length, c.length);
|
|
}
|
|
}
|
|
return "";
|
|
}
|