Move bots into Fanout & Forwarding

This commit is contained in:
Jack Kingsman
2026-03-05 18:14:03 -08:00
parent 7cd54d14d8
commit 5ecb63fde9
27 changed files with 671 additions and 908 deletions
+44 -8
View File
@@ -183,13 +183,6 @@ export function markAllRead(): Promise<{ status: string; timestamp: number }> {
export type Favorite = { type: string; id: string };
export interface BotConfig {
id: string;
name: string;
enabled: boolean;
code: string;
}
export interface AppSettings {
max_radio_contacts: number;
favorites: Favorite[];
@@ -197,7 +190,6 @@ export interface AppSettings {
sidebar_sort_order: string;
last_message_times: Record<string, number>;
preferences_migrated: boolean;
bots: BotConfig[];
advert_interval: number;
}
@@ -212,6 +204,50 @@ export function updateSettings(patch: Partial<AppSettings>): Promise<AppSettings
});
}
// --- Fanout ---
export interface FanoutConfig {
id: string;
type: string;
name: string;
enabled: boolean;
config: Record<string, unknown>;
scope: Record<string, unknown>;
sort_order: number;
created_at: number;
}
export function getFanoutConfigs(): Promise<FanoutConfig[]> {
return fetchJson('/fanout');
}
export function createFanoutConfig(body: {
type: string;
name: string;
config: Record<string, unknown>;
scope?: Record<string, unknown>;
enabled?: boolean;
}): Promise<FanoutConfig> {
return fetchJson('/fanout', {
method: 'POST',
body: JSON.stringify(body),
});
}
export function updateFanoutConfig(
id: string,
patch: Partial<{ name: string; config: Record<string, unknown>; scope: Record<string, unknown>; enabled: boolean }>
): Promise<FanoutConfig> {
return fetchJson(`/fanout/${id}`, {
method: 'PATCH',
body: JSON.stringify(patch),
});
}
export function deleteFanoutConfig(id: string): Promise<{ deleted: boolean }> {
return fetchJson(`/fanout/${id}`, { method: 'DELETE' });
}
// --- Helpers ---
/**
+24 -20
View File
@@ -1,6 +1,12 @@
import { test, expect } from '@playwright/test';
import { ensureFlightlessChannel, getSettings, updateSettings } from '../helpers/api';
import type { BotConfig } from '../helpers/api';
import {
ensureFlightlessChannel,
getFanoutConfigs,
createFanoutConfig,
deleteFanoutConfig,
updateFanoutConfig,
} from '../helpers/api';
import type { FanoutConfig } from '../helpers/api';
const BOT_CODE = `def bot(sender_name, sender_key, message_text, is_dm, channel_key, channel_name, sender_timestamp, path):
if channel_name == "#flightless" and "!e2etest" in message_text.lower():
@@ -8,45 +14,43 @@ const BOT_CODE = `def bot(sender_name, sender_key, message_text, is_dm, channel_
return None`;
test.describe('Bot functionality', () => {
let originalBots: BotConfig[];
let createdBotId: string | null = null;
test.beforeAll(async () => {
await ensureFlightlessChannel();
const settings = await getSettings();
originalBots = settings.bots ?? [];
});
test.afterAll(async () => {
// Restore original bot config
try {
await updateSettings({ bots: originalBots });
} catch {
console.warn('Failed to restore bot config');
// Clean up the bot we created
if (createdBotId) {
try {
await deleteFanoutConfig(createdBotId);
} catch {
console.warn('Failed to delete test bot');
}
}
});
test('create a bot via API, verify it in UI, trigger it, and verify response', async ({
page,
}) => {
// --- Step 1: Create and enable bot via API ---
// CodeMirror is difficult to drive via Playwright (contenteditable, lazy-loaded),
// so we set the bot code via the REST API and verify it through the UI.
const testBot: BotConfig = {
id: crypto.randomUUID(),
// --- Step 1: Create and enable bot via fanout API ---
const bot = await createFanoutConfig({
type: 'bot',
name: 'E2E Test Bot',
config: { code: BOT_CODE },
enabled: true,
code: BOT_CODE,
};
await updateSettings({ bots: [...originalBots, testBot] });
});
createdBotId = bot.id;
// --- Step 2: Verify bot appears in settings UI ---
await page.goto('/');
await expect(page.getByText('Connected')).toBeVisible();
await page.getByText('Settings').click();
await page.getByRole('button', { name: /🤖 Bots/ }).click();
await page.getByRole('button', { name: /Fanout/ }).click();
// The bot name should be visible in the bot list
// The bot name should be visible in the integration list
await expect(page.getByText('E2E Test Bot')).toBeVisible();
// Exit settings page mode