4b32655e67
- BotRunner: server-side goroutine manager for built-in bot types - steamfree bot embedded in server (polls Steam API, posts free games) - bot_type + config JSONB columns on bots table - Create/Update/Delete handlers manage runner lifecycle - GET /bots/types returns registered bot types - BotManager: type selector dropdown + config fields on create - No SSH needed: create a 'Steam Free Games' bot from /bots/manage
246 lines
6.3 KiB
TypeScript
246 lines
6.3 KiB
TypeScript
import { create } from 'zustand';
|
|
import { api } from '../lib/api.ts';
|
|
|
|
export interface Bot {
|
|
id: string;
|
|
name: string;
|
|
avatar: string | null;
|
|
description: string;
|
|
bot_type: string;
|
|
config: Record<string, unknown>;
|
|
owner_id: string;
|
|
created_at: string;
|
|
}
|
|
|
|
export interface SlashCommand {
|
|
id: string;
|
|
bot_id: string;
|
|
server_id: string;
|
|
name: string;
|
|
description: string;
|
|
}
|
|
|
|
export interface StoreBot {
|
|
id: string;
|
|
name: string;
|
|
avatar: string | null;
|
|
description: string;
|
|
server_count: number;
|
|
owner_id: string;
|
|
created_at: string;
|
|
}
|
|
|
|
interface BotState {
|
|
bots: Bot[];
|
|
storeBots: StoreBot[];
|
|
botTypes: string[];
|
|
loading: boolean;
|
|
error: string | null;
|
|
|
|
fetchBots: () => Promise<void>;
|
|
fetchStoreBots: () => Promise<void>;
|
|
fetchBotTypes: () => Promise<void>;
|
|
createBot: (name: string, description: string, botType?: string, config?: Record<string, unknown>) => Promise<Bot & { token: string }>;
|
|
updateBot: (id: string, data: { name?: string; description?: string; avatar?: string; bot_type?: string; config?: Record<string, unknown> }) => Promise<Bot>;
|
|
deleteBot: (id: string) => Promise<void>;
|
|
addToServer: (botId: string, serverId: string) => Promise<void>;
|
|
removeFromServer: (botId: string, serverId: string) => Promise<void>;
|
|
regenerateToken: (id: string) => Promise<{ token: string }>;
|
|
|
|
fetchCommands: (botId: string) => Promise<SlashCommand[]>;
|
|
createCommand: (botId: string, serverId: string, name: string, description: string) => Promise<SlashCommand>;
|
|
deleteCommand: (botId: string, commandId: string) => Promise<void>;
|
|
fetchServerCommands: (serverId: string) => Promise<SlashCommand[]>;
|
|
}
|
|
|
|
export const useBotStore = create<BotState>((set) => ({
|
|
bots: [],
|
|
storeBots: [],
|
|
botTypes: [],
|
|
loading: false,
|
|
error: null,
|
|
|
|
fetchBots: async () => {
|
|
set({ loading: true, error: null });
|
|
try {
|
|
const bots = await api.get<Bot[]>('/bots');
|
|
set({ bots, loading: false });
|
|
} catch (error) {
|
|
set({
|
|
loading: false,
|
|
error: error instanceof Error ? error.message : 'Failed to fetch bots',
|
|
});
|
|
}
|
|
},
|
|
|
|
fetchStoreBots: async () => {
|
|
set({ loading: true, error: null });
|
|
try {
|
|
const storeBots = await api.get<StoreBot[]>('/bots/store');
|
|
set({ storeBots, loading: false });
|
|
} catch (error) {
|
|
set({
|
|
loading: false,
|
|
error: error instanceof Error ? error.message : 'Failed to fetch bot store',
|
|
});
|
|
}
|
|
},
|
|
|
|
fetchBotTypes: async () => {
|
|
try {
|
|
const types = await api.get<string[]>('/bots/types');
|
|
set({ botTypes: types });
|
|
} catch {
|
|
// silent
|
|
}
|
|
},
|
|
|
|
createBot: async (name, description, botType, config) => {
|
|
set({ loading: true, error: null });
|
|
try {
|
|
const result = await api.post<Bot & { token: string }>('/bots', {
|
|
name,
|
|
description,
|
|
bot_type: botType || '',
|
|
config: config || {},
|
|
});
|
|
set((state) => ({
|
|
bots: [...state.bots, result],
|
|
loading: false,
|
|
}));
|
|
return result;
|
|
} catch (error) {
|
|
set({
|
|
loading: false,
|
|
error: error instanceof Error ? error.message : 'Failed to create bot',
|
|
});
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
updateBot: async (id, data) => {
|
|
set({ loading: true, error: null });
|
|
try {
|
|
const bot = await api.patch<Bot>(`/bots/${id}`, data);
|
|
set((state) => ({
|
|
bots: state.bots.map((b) => (b.id === id ? bot : b)),
|
|
loading: false,
|
|
}));
|
|
return bot;
|
|
} catch (error) {
|
|
set({
|
|
loading: false,
|
|
error: error instanceof Error ? error.message : 'Failed to update bot',
|
|
});
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
deleteBot: async (id) => {
|
|
set({ loading: true, error: null });
|
|
try {
|
|
await api.delete(`/bots/${id}`);
|
|
set((state) => ({
|
|
bots: state.bots.filter((b) => b.id !== id),
|
|
loading: false,
|
|
}));
|
|
} catch (error) {
|
|
set({
|
|
loading: false,
|
|
error: error instanceof Error ? error.message : 'Failed to delete bot',
|
|
});
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
addToServer: async (botId, serverId) => {
|
|
set({ error: null });
|
|
try {
|
|
await api.post(`/bots/${botId}/servers`, { server_id: serverId });
|
|
} catch (error) {
|
|
set({
|
|
error: error instanceof Error ? error.message : 'Failed to add bot to server',
|
|
});
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
removeFromServer: async (botId, serverId) => {
|
|
set({ error: null });
|
|
try {
|
|
await api.delete(`/bots/${botId}/servers/${serverId}`);
|
|
} catch (error) {
|
|
set({
|
|
error: error instanceof Error ? error.message : 'Failed to remove bot from server',
|
|
});
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
regenerateToken: async (id) => {
|
|
set({ loading: true, error: null });
|
|
try {
|
|
const result = await api.post<{ token: string }>(`/bots/${id}/regenerate-token`);
|
|
set({ loading: false });
|
|
return result;
|
|
} catch (error) {
|
|
set({
|
|
loading: false,
|
|
error: error instanceof Error ? error.message : 'Failed to regenerate token',
|
|
});
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
fetchCommands: async (botId) => {
|
|
try {
|
|
return await api.get<SlashCommand[]>(`/bots/${botId}/commands`);
|
|
} catch (error) {
|
|
set({
|
|
error: error instanceof Error ? error.message : 'Failed to fetch commands',
|
|
});
|
|
return [];
|
|
}
|
|
},
|
|
|
|
createCommand: async (botId, serverId, name, description) => {
|
|
set({ error: null });
|
|
try {
|
|
const cmd = await api.post<SlashCommand>(`/bots/${botId}/commands`, {
|
|
server_id: serverId,
|
|
name,
|
|
description,
|
|
});
|
|
return cmd;
|
|
} catch (error) {
|
|
set({
|
|
error: error instanceof Error ? error.message : 'Failed to create command',
|
|
});
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
deleteCommand: async (botId, commandId) => {
|
|
set({ error: null });
|
|
try {
|
|
await api.delete(`/bots/${botId}/commands/${commandId}`);
|
|
} catch (error) {
|
|
set({
|
|
error: error instanceof Error ? error.message : 'Failed to delete command',
|
|
});
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
fetchServerCommands: async (serverId) => {
|
|
try {
|
|
return await api.get<SlashCommand[]>(`/bots/servers/${serverId}/commands`);
|
|
} catch (error) {
|
|
set({
|
|
error: error instanceof Error ? error.message : 'Failed to fetch server commands',
|
|
});
|
|
return [];
|
|
}
|
|
},
|
|
}));
|