tasks/noc-integration/setup-skills-and-notifications/20260330-1610
This commit is contained in:
@@ -1,49 +1,52 @@
|
|||||||
#!/usr/bin/env bun
|
#!/usr/bin/env bun
|
||||||
import { readFileSync } from "node:fs";
|
import { readFileSync, existsSync } from "node:fs";
|
||||||
|
import { join } from "node:path";
|
||||||
|
|
||||||
// Fungsi untuk mencari string terpanjang dalam objek (biasanya balasan AI)
|
// Function to manually load .env from project root if process.env is missing keys
|
||||||
function findLongestString(obj: unknown): string {
|
function loadEnv() {
|
||||||
let longest = "";
|
const envPath = join(process.cwd(), ".env");
|
||||||
const search = (item: unknown) => {
|
if (existsSync(envPath)) {
|
||||||
if (typeof item === "string") {
|
const envContent = readFileSync(envPath, "utf-8");
|
||||||
if (item.length > longest.length) {
|
const lines = envContent.split("\n");
|
||||||
longest = item;
|
for (const line of lines) {
|
||||||
}
|
if (line && !line.startsWith("#")) {
|
||||||
} else if (Array.isArray(item)) {
|
const [key, ...valueParts] = line.split("=");
|
||||||
for (const child of item) {
|
if (key && valueParts.length > 0) {
|
||||||
search(child);
|
const value = valueParts.join("=").trim().replace(/^["']|["']$/g, "");
|
||||||
}
|
process.env[key.trim()] = value;
|
||||||
} else if (item !== null && typeof item === "object") {
|
}
|
||||||
for (const value of Object.values(item)) {
|
|
||||||
search(value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
search(obj);
|
|
||||||
return longest;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function run() {
|
async function run() {
|
||||||
try {
|
try {
|
||||||
|
// Ensure environment variables are loaded
|
||||||
|
loadEnv();
|
||||||
|
|
||||||
const inputRaw = readFileSync(0, "utf-8");
|
const inputRaw = readFileSync(0, "utf-8");
|
||||||
if (!inputRaw) return;
|
if (!inputRaw) return;
|
||||||
const input = JSON.parse(inputRaw);
|
|
||||||
|
|
||||||
// DEBUG: Lihat struktur asli di console terminal (stderr)
|
let finalText = "";
|
||||||
console.error("DEBUG KEYS:", Object.keys(input));
|
let sessionId = "dashboard-desa-plus";
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Try parsing as JSON first
|
||||||
|
const input = JSON.parse(inputRaw);
|
||||||
|
sessionId = input.session_id || "dashboard-desa-plus";
|
||||||
|
finalText = typeof input === "string" ? input : (input.response || input.text || JSON.stringify(input));
|
||||||
|
} catch {
|
||||||
|
// If not JSON, use raw text
|
||||||
|
finalText = inputRaw;
|
||||||
|
}
|
||||||
|
|
||||||
const BOT_TOKEN = process.env.BOT_TOKEN;
|
const BOT_TOKEN = process.env.BOT_TOKEN;
|
||||||
const CHAT_ID = process.env.CHAT_ID;
|
const CHAT_ID = process.env.CHAT_ID;
|
||||||
|
|
||||||
const sessionId = input.session_id || "unknown";
|
if (!BOT_TOKEN || !CHAT_ID) {
|
||||||
|
console.error("Missing BOT_TOKEN or CHAT_ID in environment variables");
|
||||||
// Cari teks secara otomatis di seluruh objek JSON
|
return;
|
||||||
let finalText = findLongestString(input.response || input);
|
|
||||||
|
|
||||||
if (!finalText || finalText.length < 5) {
|
|
||||||
finalText =
|
|
||||||
"Teks masih gagal diekstraksi. Struktur: " +
|
|
||||||
Object.keys(input).join(", ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const message =
|
const message =
|
||||||
@@ -51,7 +54,7 @@ async function run() {
|
|||||||
`🆔 Session: \`${sessionId}\` \n\n` +
|
`🆔 Session: \`${sessionId}\` \n\n` +
|
||||||
`🧠 Output:\n${finalText.substring(0, 3500)}`;
|
`🧠 Output:\n${finalText.substring(0, 3500)}`;
|
||||||
|
|
||||||
await fetch(`https://api.telegram.org/bot${BOT_TOKEN}/sendMessage`, {
|
const res = await fetch(`https://api.telegram.org/bot${BOT_TOKEN}/sendMessage`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
@@ -61,6 +64,13 @@ async function run() {
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
const errorData = await res.json();
|
||||||
|
console.error("Telegram API Error:", errorData);
|
||||||
|
} else {
|
||||||
|
console.log("Notification sent successfully!");
|
||||||
|
}
|
||||||
|
|
||||||
process.stdout.write(JSON.stringify({ status: "continue" }));
|
process.stdout.write(JSON.stringify({ status: "continue" }));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Hook Error:", err);
|
console.error("Hook Error:", err);
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "activity" ALTER COLUMN "villageId" SET DEFAULT 'desa1';
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "discussion" ALTER COLUMN "villageId" SET DEFAULT 'desa1';
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "division" ADD COLUMN "externalActivityCount" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
ALTER COLUMN "villageId" SET DEFAULT 'desa1';
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "document" ALTER COLUMN "villageId" SET DEFAULT 'desa1';
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "event" ALTER COLUMN "villageId" SET DEFAULT 'desa1';
|
||||||
@@ -46,9 +46,10 @@ model Division {
|
|||||||
villageId String? @default("desa1") // ID Desa dari sistem NOC
|
villageId String? @default("desa1") // ID Desa dari sistem NOC
|
||||||
name String @unique
|
name String @unique
|
||||||
description String?
|
description String?
|
||||||
color String @default("#1E3A5F")
|
color String @default("#1E3A5F")
|
||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
lastSyncedAt DateTime? // Terakhir kali sinkronisasi dilakukan
|
externalActivityCount Int @default(0) // Total kegiatan dari sistem NOC (misal: 47)
|
||||||
|
lastSyncedAt DateTime? // Terakhir kali sinkronisasi dilakukan
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
|||||||
22
scripts/check-sync-data.ts
Normal file
22
scripts/check-sync-data.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { prisma } from "../src/utils/db";
|
||||||
|
|
||||||
|
async function check() {
|
||||||
|
console.log("--- Checking Division Data in DB ---");
|
||||||
|
const divisions = await prisma.division.findMany({
|
||||||
|
select: {
|
||||||
|
name: true,
|
||||||
|
externalActivityCount: true,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.table(divisions);
|
||||||
|
|
||||||
|
console.log("\n--- Checking API Response for /api/division/ ---");
|
||||||
|
// Mocking the mapping logic from src/api/division.ts
|
||||||
|
const formatted = divisions.map(d => ({
|
||||||
|
name: d.name,
|
||||||
|
activityCount: d.externalActivityCount
|
||||||
|
}));
|
||||||
|
console.table(formatted);
|
||||||
|
}
|
||||||
|
|
||||||
|
check().catch(console.error).finally(() => prisma.$disconnect());
|
||||||
@@ -58,12 +58,14 @@ async function syncActiveDivisions() {
|
|||||||
externalId: extId,
|
externalId: extId,
|
||||||
color: div.color || "#1E3A5F",
|
color: div.color || "#1E3A5F",
|
||||||
villageId: ID_DESA,
|
villageId: ID_DESA,
|
||||||
|
externalActivityCount: div.totalKegiatan || 0,
|
||||||
},
|
},
|
||||||
create: {
|
create: {
|
||||||
externalId: extId,
|
externalId: extId,
|
||||||
name: name,
|
name: name,
|
||||||
color: div.color || "#1E3A5F",
|
color: div.color || "#1E3A5F",
|
||||||
villageId: ID_DESA,
|
villageId: ID_DESA,
|
||||||
|
externalActivityCount: div.totalKegiatan || 0,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,12 @@ export const division = new Elysia({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return { data: divisions };
|
return {
|
||||||
|
data: divisions.map(d => ({
|
||||||
|
...d,
|
||||||
|
activityCount: d.externalActivityCount || d._count.activities
|
||||||
|
}))
|
||||||
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error({ error }, "Failed to fetch divisions");
|
logger.error({ error }, "Failed to fetch divisions");
|
||||||
set.status = 500;
|
set.status = 500;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ interface DivisionData {
|
|||||||
interface DivisionApiResponse {
|
interface DivisionApiResponse {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
activityCount: number;
|
||||||
_count?: {
|
_count?: {
|
||||||
activities: number;
|
activities: number;
|
||||||
};
|
};
|
||||||
@@ -40,7 +41,7 @@ export function DivisionProgress() {
|
|||||||
setData(
|
setData(
|
||||||
(res.data.data as DivisionApiResponse[]).map((d) => ({
|
(res.data.data as DivisionApiResponse[]).map((d) => ({
|
||||||
name: d.name,
|
name: d.name,
|
||||||
value: d._count?.activities || 0,
|
value: d.activityCount || 0,
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user