Bläddra i källkod

feat: discord

zhangchunrui 3 veckor sedan
förälder
incheckning
587689dedb
3 ändrade filer med 33 tillägg och 55 borttagningar
  1. 2 3
      .env.example
  2. 3 3
      src/lib/config.ts
  3. 28 49
      src/lib/notifications/index.ts

+ 2 - 3
.env.example

@@ -20,6 +20,5 @@ REBALANCE_COOLDOWN_MS=10000
 SLIPPAGE_BPS=50
 MAX_DAILY_REBALANCES=50
 
-# === Notifications (optional) ===
-NOTIFY_PROVIDER=bark
-NOTIFY_ENDPOINT=https://api.day.app/YOUR_KEY
+# === Discord Notifications ===
+DISCORD_WEBHOOK_URL=

+ 3 - 3
src/lib/config.ts

@@ -70,9 +70,9 @@ export const config: BotConfig = {
     maxDailyRebalances: Number(env('MAX_DAILY_REBALANCES', '50')),
   },
   notifications: {
-    enabled: env('NOTIFY_ENDPOINT') !== '',
-    provider: (env('NOTIFY_PROVIDER', 'bark') as BotConfig['notifications']['provider']) || 'bark',
-    endpoint: env('NOTIFY_ENDPOINT'),
+    enabled: env('DISCORD_WEBHOOK_URL') !== '',
+    provider: 'discord' as const,
+    endpoint: env('DISCORD_WEBHOOK_URL'),
     alertOnRebalance: true,
     alertOnError: true,
     alertOnLowBalance: true,

+ 28 - 49
src/lib/notifications/index.ts

@@ -1,59 +1,38 @@
 import { config } from '../config'
 import type { RebalanceResult } from '../engine/types'
 
-export async function sendNotification(
-  _title: string,
-  _body: string,
-  _level: 'info' | 'warning' | 'error' = 'info',
-): Promise<void> {
-  // Notifications disabled
-}
-
-async function sendBark(
-  endpoint: string,
-  title: string,
-  body: string,
-  level: string,
-): Promise<void> {
-  const icon = level === 'error' ? 'exclamationmark.triangle' : 'arrow.triangle.2.circlepath'
-  const url = `${endpoint}/${encodeURIComponent(title)}/${encodeURIComponent(body)}?group=lfj-rebalancer&icon=${icon}`
-  const res = await fetch(url)
-  if (!res.ok) console.error('[notify:bark] HTTP', res.status)
+const COLORS = {
+  info: 0x3498db,
+  warning: 0xf39c12,
+  error: 0xe74c3c,
 }
 
-async function sendNtfy(
-  endpoint: string,
-  title: string,
-  body: string,
-  level: string,
-): Promise<void> {
-  const priority = level === 'error' ? '5' : level === 'warning' ? '4' : '3'
-  const res = await fetch(endpoint, {
-    method: 'POST',
-    headers: {
-      Title: title,
-      Priority: priority,
-      Tags: 'chart_with_upwards_trend',
-    },
-    body,
-  })
-  if (!res.ok) console.error('[notify:ntfy] HTTP', res.status)
-}
-
-async function sendPushover(
-  endpoint: string,
+export async function sendNotification(
   title: string,
   body: string,
-  level: string,
+  level: 'info' | 'warning' | 'error' = 'info',
 ): Promise<void> {
-  const priority = level === 'error' ? 1 : 0
-  const [token, user] = endpoint.split(':')
-  const res = await fetch('https://api.pushover.net/1/messages.json', {
-    method: 'POST',
-    headers: { 'Content-Type': 'application/json' },
-    body: JSON.stringify({ token, user, title, message: body, priority }),
-  })
-  if (!res.ok) console.error('[notify:pushover] HTTP', res.status)
+  if (!config.notifications.enabled || !config.notifications.endpoint) return
+
+  try {
+    await fetch(config.notifications.endpoint, {
+      method: 'POST',
+      headers: { 'Content-Type': 'application/json' },
+      body: JSON.stringify({
+        embeds: [
+          {
+            title,
+            description: body,
+            color: COLORS[level],
+            timestamp: new Date().toISOString(),
+            footer: { text: 'LFJ Rebalancer' },
+          },
+        ],
+      }),
+    })
+  } catch (err) {
+    console.error('[notify] Discord webhook failed:', err)
+  }
 }
 
 export async function notifyRebalance(result: RebalanceResult): Promise<void> {
@@ -61,7 +40,7 @@ export async function notifyRebalance(result: RebalanceResult): Promise<void> {
 
   const title = result.success ? 'Rebalance Success' : 'Rebalance Failed'
   const body = result.success
-    ? `Bin ${result.prevActiveId} → ${result.newActiveId} | Range [${result.newMinBin}, ${result.newMaxBin}] | ${result.durationMs}ms`
+    ? `Bin ${result.prevActiveId} → ${result.newActiveId}\nRange [${result.newMinBin}, ${result.newMaxBin}]\nDuration: ${result.durationMs}ms`
     : `Failed: ${result.error}`
 
   await sendNotification(title, body, result.success ? 'info' : 'error')