mirror of
https://github.com/GoodStartLabs/AI_Diplomacy.git
synced 2026-04-19 12:58:09 +00:00
webhooks
This commit is contained in:
parent
fa17592e75
commit
9b24abef53
6 changed files with 162 additions and 2 deletions
|
|
@ -1,2 +1,10 @@
|
|||
# rename me to .env and change below keys
|
||||
STREAM_KEY="live_XXXXXXXXXX"
|
||||
DEEPSEEK_API_KEY=
|
||||
OPENAI_API_KEY=
|
||||
ANTHROPIC_API_KEY=
|
||||
GEMINI_API_KEY=
|
||||
VITE_ELEVENLABS_API_KEY =
|
||||
ELEVENLABS_API_KEY =
|
||||
OPENROUTER_API_KEY=
|
||||
VITE_WEBHOOK_URL=
|
||||
|
|
@ -3,3 +3,4 @@
|
|||
| Problem | Attempted Solution | Real Outcome | Current $ Balance |
|
||||
| :----------------------------------------------------------- | :----------------- | :----------- | :---------------- |
|
||||
| 1. Relationships chart is blank. <br> 2. Game stops after narrator summary in phase 2. | 1. Updated `PhaseSchema` in `types/gameState.ts` to include `agent_relationships` definition. <br> 2. Uncommented `updateChatWindows(currentPhase, true);` in `phase.ts`. | | $0 |
|
||||
| Add webhook notifications for phase changes | 1. Added `webhookUrl` config to `src/config.ts` <br> 2. Created `src/webhooks/phaseNotifier.ts` with fire-and-forget webhook notification <br> 3. Added `notifyPhaseChange()` call in `_setPhase()` function <br> 4. Updated `.env.example` with `VITE_WEBHOOK_URL` | | $0 |
|
||||
|
|
|
|||
|
|
@ -15,5 +15,8 @@ export const config = {
|
|||
soundEffectFrequency: 3,
|
||||
|
||||
// Whether speech/TTS is enabled (can be toggled via debug menu)
|
||||
speechEnabled: import.meta.env.VITE_DEBUG_MODE ? false : true
|
||||
speechEnabled: import.meta.env.VITE_DEBUG_MODE ? false : true,
|
||||
|
||||
// Webhook URL for phase change notifications (optional)
|
||||
webhookUrl: import.meta.env.VITE_WEBHOOK_URL || ''
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { config } from "./config";
|
|||
import { debugMenuInstance } from "./debug/debugMenu";
|
||||
import { showTwoPowerConversation, closeTwoPowerConversation } from "./components/twoPowerConversation";
|
||||
import { PowerENUM } from "./types/map";
|
||||
import { notifyPhaseChange } from "./webhooks/phaseNotifier";
|
||||
|
||||
const MOMENT_THRESHOLD = 8.0
|
||||
// If we're in debug mode, show it quick, otherwise show it for 30 seconds
|
||||
|
|
@ -17,6 +18,11 @@ const MOMENT_DISPLAY_TIMEOUT_MS = config.isDebugMode ? 5000 : 30000
|
|||
|
||||
// FIXME: Going to previous phases is borked. Units do not animate properly, map doesn't update.
|
||||
export function _setPhase(phaseIndex: number) {
|
||||
console.log(`[Phase] _setPhase called with index: ${phaseIndex}`);
|
||||
|
||||
// Store the old phase index at the very beginning
|
||||
const oldPhaseIndex = gameState.phaseIndex;
|
||||
|
||||
if (config.isDebugMode) {
|
||||
debugMenuInstance.updateTools()
|
||||
}
|
||||
|
|
@ -58,6 +64,9 @@ export function _setPhase(phaseIndex: number) {
|
|||
|
||||
// Finally, update the gameState with the current phaseIndex
|
||||
gameState.phaseIndex = phaseIndex
|
||||
|
||||
// Send webhook notification for phase change
|
||||
notifyPhaseChange(oldPhaseIndex, phaseIndex);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
140
ai_animation/src/webhooks/phaseNotifier.ts
Normal file
140
ai_animation/src/webhooks/phaseNotifier.ts
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
import { config } from '../config';
|
||||
import { gameState } from '../gameState';
|
||||
|
||||
// Test webhook URL validity on startup
|
||||
testWebhookUrl().catch(err => console.error("Webhook test failed:", err));
|
||||
|
||||
async function testWebhookUrl() {
|
||||
const webhookUrl = config.webhookUrl || import.meta.env.VITE_WEBHOOK_URL || '';
|
||||
|
||||
if (!webhookUrl) {
|
||||
console.log("⚠️ No webhook URL configured (optional feature)");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// For Discord webhooks, we can test with a GET request
|
||||
if (webhookUrl.includes('discord.com/api/webhooks')) {
|
||||
const response = await fetch(webhookUrl, {
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const webhookInfo = await response.json();
|
||||
console.log(`✅ Discord webhook is valid and ready (${webhookInfo.name || 'Unnamed webhook'})`);
|
||||
} else if (response.status === 401) {
|
||||
console.error(`❌ Discord webhook invalid: Unauthorized (check webhook URL)`);
|
||||
} else {
|
||||
console.error(`❌ Discord webhook error: ${response.status}`);
|
||||
}
|
||||
} else {
|
||||
// For non-Discord webhooks, just validate the URL format
|
||||
try {
|
||||
new URL(webhookUrl);
|
||||
console.log(`✅ Webhook URL is valid: ${webhookUrl.substring(0, 50)}...`);
|
||||
} catch {
|
||||
console.error(`❌ Invalid webhook URL format`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ Webhook connection error:", error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a webhook notification when a phase changes
|
||||
* This is a fire-and-forget operation that won't block the UI
|
||||
*/
|
||||
export async function notifyPhaseChange(oldPhaseIndex: number, newPhaseIndex: number): Promise<void> {
|
||||
console.log(`[Webhook] Phase change detected: ${oldPhaseIndex} -> ${newPhaseIndex}`);
|
||||
|
||||
// Skip if no webhook URL is configured
|
||||
if (!config.webhookUrl) {
|
||||
console.log('[Webhook] No webhook URL configured, skipping notification');
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip if game data is not loaded
|
||||
if (!gameState.gameData || !gameState.gameData.phases) {
|
||||
console.warn('[Webhook] Game data not loaded, cannot send notification');
|
||||
return;
|
||||
}
|
||||
|
||||
const currentPhase = gameState.gameData.phases[newPhaseIndex];
|
||||
if (!currentPhase) {
|
||||
console.warn(`[Webhook] Phase at index ${newPhaseIndex} not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine direction of phase change
|
||||
let direction: 'forward' | 'backward' | 'jump';
|
||||
if (newPhaseIndex === oldPhaseIndex + 1) {
|
||||
direction = 'forward';
|
||||
} else if (newPhaseIndex === oldPhaseIndex - 1) {
|
||||
direction = 'backward';
|
||||
} else {
|
||||
direction = 'jump';
|
||||
}
|
||||
|
||||
const payload = {
|
||||
event: 'phase_change',
|
||||
timestamp: new Date().toISOString(),
|
||||
game_id: gameState.gameId || 0,
|
||||
phase_index: newPhaseIndex,
|
||||
phase_name: currentPhase.name,
|
||||
phase_year: currentPhase.year || parseInt(currentPhase.name.substring(1, 5)) || null,
|
||||
is_playing: gameState.isPlaying,
|
||||
direction: direction,
|
||||
total_phases: gameState.gameData.phases.length
|
||||
};
|
||||
|
||||
// Discord webhooks need a different format
|
||||
const isDiscordWebhook = config.webhookUrl.includes('discord.com/api/webhooks');
|
||||
const webhookPayload = isDiscordWebhook ? {
|
||||
content: `Phase Change: **${currentPhase.name}** (${direction})`,
|
||||
embeds: [{
|
||||
title: "AI Diplomacy Phase Update",
|
||||
color: direction === 'forward' ? 0x00ff00 : direction === 'backward' ? 0xff0000 : 0x0000ff,
|
||||
fields: [
|
||||
{ name: "Phase", value: currentPhase.name, inline: true },
|
||||
{ name: "Year", value: String(payload.phase_year || "Unknown"), inline: true },
|
||||
{ name: "Direction", value: direction, inline: true },
|
||||
{ name: "Game ID", value: String(payload.game_id), inline: true },
|
||||
{ name: "Phase Index", value: `${newPhaseIndex}/${payload.total_phases}`, inline: true },
|
||||
{ name: "Auto-playing", value: payload.is_playing ? "Yes" : "No", inline: true }
|
||||
],
|
||||
timestamp: payload.timestamp
|
||||
}]
|
||||
} : payload;
|
||||
|
||||
console.log(`[Webhook] Sending notification for phase ${currentPhase.name} to ${config.webhookUrl}`);
|
||||
|
||||
try {
|
||||
// Fire and forget - we don't await this
|
||||
fetch(config.webhookUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(webhookPayload)
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
console.log(`[Webhook] ✅ Successfully sent notification for phase ${currentPhase.name}`);
|
||||
} else {
|
||||
console.warn(`[Webhook] ❌ Failed with status ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// Log errors but don't let them break the animation
|
||||
console.error('[Webhook] ❌ Network error:', error);
|
||||
});
|
||||
|
||||
if (config.isDebugMode) {
|
||||
console.log('[Webhook] Debug - Full payload:', payload);
|
||||
}
|
||||
} catch (error) {
|
||||
// Catch any synchronous errors (shouldn't happen with fetch)
|
||||
console.error('[Webhook] ❌ Unexpected error:', error);
|
||||
}
|
||||
}
|
||||
|
|
@ -623,7 +623,6 @@ async def main():
|
|||
try:
|
||||
current_year = int(current_year_str)
|
||||
consolidation_year = current_year - 2 # Two years ago
|
||||
|
||||
logger.info(f"[DIARY CONSOLIDATION] Current year: {current_year}, Consolidation year: {consolidation_year}")
|
||||
logger.info(f"[DIARY CONSOLIDATION] Phase check - ends with 'M': {current_short_phase.endswith('M')}, starts with 'S': {current_short_phase.startswith('S')}")
|
||||
logger.info(f"[DIARY CONSOLIDATION] Consolidation year check: {consolidation_year} >= 1901: {consolidation_year >= 1901}")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue