Send cronjob result and notify-agent reaction to Telegram

The cronjob notification ran the notify-agent on the result summary and
sent only its reaction to Telegram. When the agent merely acknowledged the
summary (e.g. "👍"), the actual cronjob result never reached Telegram —
the user got a header plus an emoji and no information.

Send the real result as the guaranteed first Telegram message, then run
the notify-agent best-effort and send its reaction as a separate second
message. The agent run is wrapped in try/catch with a canRunNotifyAgent
guard instead of the old return-aborts, so a missing key / OpenAI without
key / unavailable CLI only skips the reaction — the result is already
delivered. The notify-agent still persists its reaction to the web history
automatically (ChatNonStreaming / ChatNonStreamingViaCli).

TelegramService.SendNotification now uses SendLongMessage so long results
are chunked into 4096-char messages instead of being rejected whole.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MelekiandClaude Opus 4.8 committed 2026-06-19 20:45:00 +02:00
1 parent 6e301b7498
commit e395927a67
2 files changed
+87 -57

No files matched your search

+83 -56
View File
@@ -178,43 +178,9 @@ public class CronjobService : BackgroundService
return;
}
// API-Key des Cronjob-Owners für Notify-Agent auflösen
var notifyApiKey = await _db.GetAgentApiKeyRecord(job.UserId, notifyCtx.AgentId);
bool notifyUseCliFallback = false;
if (notifyApiKey == null)
{
// Kein User-Key – Verhalten wie ExecuteCronjob/ChatEndpoint:
// - Anthropic (Provider leer) → Claude-CLI-Fallback (sofern berechtigt/verfügbar)
// - "openai" → abbrechen (kein Server-Key, kein CLI-Äquivalent)
// - nvidia/gemini/minimax/pollinations → durchlassen: Server-Key via ResolveProviderConfig
if (string.IsNullOrEmpty(notifyCtx.Provider))
{
var notifyOwner = await _db.GetUser(job.UserId);
if ((notifyOwner != null && !notifyOwner.CanUseCli && !notifyOwner.IsAdmin) || !_anthropicService.IsCliAvailable)
{
_logger.LogWarning("Kein API-Key für Agent {AgentId} (Cronjob '{Name}' Notification) und CLI nicht verfügbar/berechtigt", notifyCtx.AgentId, job.Name);
return;
}
notifyUseCliFallback = true;
}
else if (notifyCtx.Provider == "openai")
{
_logger.LogWarning("Kein API-Key für Agent {AgentId} (Cronjob '{Name}' Notification, OpenAI benötigt zwingend einen Key)", notifyCtx.AgentId, job.Name);
return;
}
// sonst: nvidia/gemini/minimax/pollinations → notifyCtx.ApiToken bleibt leer (Server-Key-Auflösung).
}
else
{
notifyCtx.ApiToken = notifyApiKey.ApiKey;
}
// Kurze Zusammenfassung als User-Nachricht in die Notify-Session schreiben
// Cronjob-Ergebnis als Nachricht in die Notify-Session schreiben (für die Web-History).
var summary = $"[Cronjob \"{job.Name}\" wurde in Session \"{job.SessionName}\" ausgeführt]\n\nAntwort:\n{Truncate(responseText, 2000)}";
// In Notify-Session als Nachricht speichern
await _db.SaveMessage(new MessageRecord
{
ChainId = notifyCtx.ChainId,
@@ -224,31 +190,92 @@ public class CronjobService : BackgroundService
Content = summary
});
// Nur die Benachrichtigung als History senden (nicht volle Session-History,
// da diese mit einer assistant-Nachricht enden könnte → prefill-Fehler)
var notifyHistory = new List<object>
{
new Dictionary<string, object>
{
["role"] = "user",
["content"] = new List<object> { new { type = "text", text = summary } }
}
};
string notifyResponse;
if (!string.IsNullOrEmpty(notifyCtx.Provider))
notifyResponse = await _openAiService.ChatNonStreaming(notifyCtx, notifyHistory);
else if (notifyUseCliFallback)
notifyResponse = await _anthropicService.ChatNonStreamingViaCli(notifyCtx, notifyHistory);
else
notifyResponse = await _anthropicService.ChatNonStreaming(notifyCtx, notifyHistory);
await _db.UpdateSessionTimestamp(job.NotifySessionId);
// Telegram auf Notify-Session senden
if (!string.IsNullOrEmpty(notifyResponse))
// ── Nachricht 1 (Pflicht): das echte Cronjob-Ergebnis ──────────────────────────
// Geht IMMER raus, unabhängig vom Notify-Agent / API-Key. Früher lief hier nur der
// Notify-Agent auf der Zusammenfassung; quittierte er bloß mit "👍", erreichte der
// eigentliche Inhalt Telegram nie. Das Ergebnis ist jetzt die garantierte Nachricht.
if (!string.IsNullOrEmpty(responseText))
{
await _telegramService.SendNotification(job.NotifySessionId,
$"📋 Cronjob: {job.Name}\n📂 Session: {job.SessionName}\n\n{notifyResponse}");
$"📋 Cronjob: {job.Name}\n📂 Session: {job.SessionName}\n\n{responseText}");
}
// ── Nachricht 2 (optional, best effort): die Reaktion des Notify-Agenten ────────
// Der Notify-Agent reagiert auf das Ergebnis; seine Antwort geht als SEPARATE
// Telegram-Nachricht raus und wird (über ChatNonStreaming*) automatisch in die
// Web-History persistiert. Schlägt der Lauf fehl (kein Key / OpenAI ohne Key /
// CLI nicht verfügbar), wird die Reaktion still übersprungen – Nachricht 1 ist
// dann längst zugestellt, es geht also nie wieder Information verloren.
try
{
// API-Key des Cronjob-Owners für den Notify-Agenten auflösen (wie ChatEndpoint):
// - Anthropic (Provider leer) → Claude-CLI-Fallback (sofern berechtigt/verfügbar)
// - "openai" → überspringen (kein Server-Key, kein CLI-Äquivalent)
// - nvidia/gemini/minimax/pollinations → durchlassen: Server-Key via ResolveProviderConfig
var notifyApiKey = await _db.GetAgentApiKeyRecord(job.UserId, notifyCtx.AgentId);
bool notifyUseCliFallback = false;
bool canRunNotifyAgent = true;
if (notifyApiKey == null)
{
if (string.IsNullOrEmpty(notifyCtx.Provider))
{
var notifyOwner = await _db.GetUser(job.UserId);
if ((notifyOwner != null && !notifyOwner.CanUseCli && !notifyOwner.IsAdmin) || !_anthropicService.IsCliAvailable)
{
_logger.LogWarning("Notify-Agent-Reaktion für Cronjob '{Name}' übersprungen: kein API-Key und CLI nicht verfügbar/berechtigt", job.Name);
canRunNotifyAgent = false;
}
else
{
notifyUseCliFallback = true;
}
}
else if (notifyCtx.Provider == "openai")
{
_logger.LogWarning("Notify-Agent-Reaktion für Cronjob '{Name}' übersprungen: OpenAI benötigt zwingend einen Key", job.Name);
canRunNotifyAgent = false;
}
// sonst: nvidia/gemini/minimax/pollinations → notifyCtx.ApiToken bleibt leer (Server-Key-Auflösung).
}
else
{
notifyCtx.ApiToken = notifyApiKey.ApiKey;
}
if (canRunNotifyAgent)
{
// Nur die Benachrichtigung als History senden (nicht volle Session-History,
// da diese mit einer assistant-Nachricht enden könnte → prefill-Fehler)
var notifyHistory = new List<object>
{
new Dictionary<string, object>
{
["role"] = "user",
["content"] = new List<object> { new { type = "text", text = summary } }
}
};
string notifyResponse;
if (!string.IsNullOrEmpty(notifyCtx.Provider))
notifyResponse = await _openAiService.ChatNonStreaming(notifyCtx, notifyHistory);
else if (notifyUseCliFallback)
notifyResponse = await _anthropicService.ChatNonStreamingViaCli(notifyCtx, notifyHistory);
else
notifyResponse = await _anthropicService.ChatNonStreaming(notifyCtx, notifyHistory);
if (!string.IsNullOrEmpty(notifyResponse))
{
await _telegramService.SendNotification(job.NotifySessionId, $"💬 {notifyResponse}");
}
}
}
catch (Exception reactEx)
{
// Reaktion ist rein optional – das Ergebnis (Nachricht 1) ist bereits zugestellt.
_logger.LogWarning(reactEx, "Notify-Agent-Reaktion für Cronjob '{Name}' fehlgeschlagen (Ergebnis wurde dennoch gesendet)", job.Name);
}
}
catch (Exception ex)
+4 -1
View File
@@ -280,7 +280,10 @@ public class TelegramService
if (bot == null || string.IsNullOrEmpty(bot.BotToken)) return;
var chatId = long.Parse(session.TelegramChatId);
await SendMessage(chatId, bot.BotToken, text);
// SendLongMessage statt SendMessage: lange Ergebnisse (z.B. Cronjob-Antworten) werden in
// 4096-Zeichen-Chunks zerlegt. Sonst lehnt die Telegram-API zu lange Texte komplett ab
// (400 → der User bekäme gar nichts).
await SendLongMessage(chatId, bot.BotToken, text);
}
// ── Telegram Bot API ──