const API_KEY = PropertiesService.getScriptProperties().getProperty('GEMINI_API_KEY');
function runCampusAgent() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const inputSheet = ss.getSheetByName('complaints_log');
const resolverSheet = ss.getSheetByName('resolution_tracker');
const vendorSheet = ss.getSheetByName('vendor_directory');
const inputData = inputSheet.getDataRange().getValues();
const resolverData = resolverSheet.getDataRange().getValues();
const vendorRaw = vendorSheet.getDataRange().getValues();
const vendorDataText = vendorRaw.join("\\n");
for (let i = 1; i < inputData.length; i++) {
let [id, complaint, location, timestamp, status, flag, userKey] = inputData[i];
if (status && status.toString().toLowerCase() === "new") {
// --- 1. DEDUPLICATION ---
let duplicateId = null;
let resolverRowIndex = -1;
for (let j = 1; j < resolverData.length; j++) {
if (location === resolverData[j][2] && resolverData[j][5] !== "Solved") {
duplicateId = resolverData[j][0];
resolverRowIndex = j + 1;
break;
}
}
if (duplicateId) {
inputSheet.getRange(i + 1, 1).setValue(duplicateId);
inputSheet.getRange(i + 1, 5).setValue("Linked to Existing Issue");
let currentCount = resolverSheet.getRange(resolverRowIndex, 11).getValue() || 1;
resolverSheet.getRange(resolverRowIndex, 11).setValue(currentCount + 1);
continue;
}
// --- 2. AI TRIAGE & SUGGESTION ---
const prompt = `You are a Campus Admin AI.
ISSUE: "${complaint}" at "${location}".
VENDORS: ${vendorDataText}
TASK:
1. Categorize: [Infrastructure, Management, Other].
2. If Infrastructure: Pick a Vendor and draft a Work Order email.
3. If Management: Draft a polite response to the student explaining the policy/next steps.
4. If Other: Draft a general admin response.
5. Urgency: [Low, High, Emergency].
Return ONLY JSON:
{"cat":"","urg":"","who":"","summary":"","draft":""}`;
try {
const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-flash-latest:generateContent?key=${API_KEY}`;
const response = UrlFetchApp.fetch(url, {
method: "post",
contentType: "application/json",
payload: JSON.stringify({
contents: [{ parts: [{ text: prompt }] }],
generationConfig: { response_mime_type: "application/json" }
}),
muteHttpExceptions: true
});
const res = JSON.parse(JSON.parse(response.getContentText()).candidates[0].content.parts[0].text);
// Find Vendor Email ONLY if it's Infrastructure
let assignedEmail = "";
if (res.cat === "Infrastructure") {
for (let v = 0; v < vendorRaw.length; v++) {
if (vendorRaw[v][0] === res.who) { assignedEmail = vendorRaw[v][1]; break; }
}
}
// --- 3. RECORDING FOR AUTHORITY ---
resolverSheet.appendRow([
id, // ID
res.cat, // Category
location, // Location
res.who, // Assigned To (Vendor or Dept)
res.summary, // AI Suggestion
"Awaiting Authority", // Status
"", // Decision (Authority will fill: "Approve AI" or "Custom")
assignedEmail, // Vendor Email
res.draft, // Draft Response (Email or Message)
res.urg, // Priority
1 // Report Count
]);
inputSheet.getRange(i + 1, 5).setValue("Processing");
Utilities.sleep(2000);
} catch (err) {
Logger.log("Error: " + err.message);
}
}
}
}