/** * Yonexus Server - Discord Notification Service * * Sends pairing notifications to the configured admin user via Discord DM. */ import type { PairingRequest } from "../services/pairing.js"; import { redactPairingCode } from "../core/logging.js"; export interface DiscordNotificationService { /** * Send a pairing code notification to the admin user. * @returns Whether the notification was sent successfully */ sendPairingNotification(request: PairingRequest): Promise; } export interface DiscordNotificationConfig { botToken: string; adminUserId: string; } /** * Create a Discord notification service. * * Note: This is a framework stub. Full implementation requires: * - Discord bot client integration (e.g., using discord.js) * - DM channel creation/fetching * - Error handling for blocked DMs, invalid tokens, etc. * * For v1, this provides the interface and a mock implementation * that logs to console. Production deployments should replace * this with actual Discord bot integration. */ export function createDiscordNotificationService( config: DiscordNotificationConfig ): DiscordNotificationService { return { async sendPairingNotification(request: PairingRequest): Promise { const redactedCode = redactPairingCode(request.pairingCode); // Log to console (visible in OpenClaw logs) console.log("[Yonexus.Server] Pairing notification (Discord DM stub):", { identifier: request.identifier, pairingCode: redactedCode, expiresAt: request.expiresAt, ttlSeconds: request.ttlSeconds, adminUserId: config.adminUserId }); // TODO: Replace with actual Discord bot integration // Example with discord.js: // const client = new Client({ intents: [GatewayIntentBits.DirectMessages] }); // await client.login(config.botToken); // const user = await client.users.fetch(config.adminUserId); // await user.send(message); // For now, return true to allow pairing flow to continue // In production, this should return false if DM fails return true; } }; } /** * Format a pairing request as a Discord DM message. */ export function formatPairingMessage(request: PairingRequest): string { const expiresDate = new Date(request.expiresAt * 1000); const expiresStr = expiresDate.toISOString(); return [ "🔐 **Yonexus Pairing Request**", "", `**Identifier:** \`${request.identifier}\``, `**Pairing Code:** \`${request.pairingCode}\``, `**Expires At:** ${expiresStr}`, `**TTL:** ${request.ttlSeconds} seconds`, "", "Please relay this pairing code to the client operator via a trusted out-of-band channel.", "Do not share this code over the Yonexus WebSocket connection." ].join("\n"); } /** * Create a mock notification service for testing. * Returns success/failure based on configuration. */ export function createMockNotificationService( options: { shouldSucceed?: boolean } = {} ): DiscordNotificationService { const shouldSucceed = options.shouldSucceed ?? true; return { async sendPairingNotification(request: PairingRequest): Promise { console.log("[Yonexus.Server] Mock pairing notification:"); console.log(` Identifier: ${request.identifier}`); console.log(` Pairing Code: ${redactPairingCode(request.pairingCode)}`); console.log(` Success: ${shouldSucceed}`); return shouldSucceed; } }; }