Skip to main content
Glama
quest.repo.ts9.08 kB
import Database from 'better-sqlite3'; import { Quest, QuestSchema, QuestLog, QuestLogSchema } from '../../schema/quest.js'; // Extended types for full quest log export interface QuestWithStatus extends Quest { logStatus: 'active' | 'completed' | 'failed'; } export interface FullQuestLog { characterId: string; quests: QuestWithStatus[]; summary: { active: number; completed: number; failed: number; }; } export class QuestRepository { constructor(private db: Database.Database) { } create(quest: Quest): void { const validQuest = QuestSchema.parse(quest); const stmt = this.db.prepare(` INSERT INTO quests (id, world_id, name, description, status, objectives, rewards, prerequisites, giver, created_at, updated_at) VALUES (@id, @worldId, @name, @description, @status, @objectives, @rewards, @prerequisites, @giver, @createdAt, @updatedAt) `); stmt.run({ id: validQuest.id, worldId: validQuest.worldId, name: validQuest.name, description: validQuest.description, status: validQuest.status, objectives: JSON.stringify(validQuest.objectives), rewards: JSON.stringify(validQuest.rewards), prerequisites: JSON.stringify(validQuest.prerequisites), giver: validQuest.giver || null, createdAt: validQuest.createdAt, updatedAt: validQuest.updatedAt }); } findById(id: string): Quest | null { const stmt = this.db.prepare('SELECT * FROM quests WHERE id = ?'); const row = stmt.get(id) as QuestRow | undefined; if (!row) return null; return this.rowToQuest(row); } update(id: string, updates: Partial<Quest>): Quest | null { const existing = this.findById(id); if (!existing) return null; const updated = { ...existing, ...updates, updatedAt: new Date().toISOString() }; const validQuest = QuestSchema.parse(updated); const stmt = this.db.prepare(` UPDATE quests SET name = ?, description = ?, status = ?, objectives = ?, rewards = ?, prerequisites = ?, giver = ?, updated_at = ? WHERE id = ? `); stmt.run( validQuest.name, validQuest.description, validQuest.status, JSON.stringify(validQuest.objectives), JSON.stringify(validQuest.rewards), JSON.stringify(validQuest.prerequisites), validQuest.giver || null, validQuest.updatedAt, id ); return validQuest; } getLog(characterId: string): QuestLog | null { const stmt = this.db.prepare('SELECT * FROM quest_logs WHERE character_id = ?'); const row = stmt.get(characterId) as QuestLogRow | undefined; if (!row) return null; return this.rowToQuestLog(row); } /** * Get full quest log with complete quest objects (not just IDs) * Returns quests organized by status with full details */ getFullQuestLog(characterId: string): FullQuestLog { const log = this.getLog(characterId); if (!log) { return { characterId, quests: [], summary: { active: 0, completed: 0, failed: 0 } }; } const quests: QuestWithStatus[] = []; // Fetch active quests with full data for (const questId of log.activeQuests) { const quest = this.findById(questId); if (quest) { quests.push({ ...quest, logStatus: 'active' }); } } // Fetch completed quests with full data for (const questId of log.completedQuests) { const quest = this.findById(questId); if (quest) { quests.push({ ...quest, logStatus: 'completed' }); } } // Fetch failed quests with full data for (const questId of log.failedQuests) { const quest = this.findById(questId); if (quest) { quests.push({ ...quest, logStatus: 'failed' }); } } return { characterId, quests, summary: { active: log.activeQuests.length, completed: log.completedQuests.length, failed: log.failedQuests.length } }; } /** * Find all quests, optionally filtered by world */ findAll(worldId?: string): Quest[] { let stmt; if (worldId) { stmt = this.db.prepare('SELECT * FROM quests WHERE world_id = ?'); const rows = stmt.all(worldId) as QuestRow[]; return rows.map(row => this.rowToQuest(row)); } else { stmt = this.db.prepare('SELECT * FROM quests'); const rows = stmt.all() as QuestRow[]; return rows.map(row => this.rowToQuest(row)); } } /** * Update a specific objective's progress */ updateObjectiveProgress(questId: string, objectiveId: string, progress: number): Quest | null { const quest = this.findById(questId); if (!quest) return null; const objectiveIndex = quest.objectives.findIndex(o => o.id === objectiveId); if (objectiveIndex === -1) return null; const objective = quest.objectives[objectiveIndex]; objective.current = Math.min(objective.required, objective.current + progress); if (objective.current >= objective.required) { objective.completed = true; } quest.objectives[objectiveIndex] = objective; return this.update(quest.id, { objectives: quest.objectives }); } /** * Check if all objectives for a quest are completed */ areAllObjectivesComplete(questId: string): boolean { const quest = this.findById(questId); if (!quest) return false; return quest.objectives.every(o => o.completed); } /** * Complete a specific objective (set current = required) */ completeObjective(questId: string, objectiveId: string): Quest | null { const quest = this.findById(questId); if (!quest) return null; const objectiveIndex = quest.objectives.findIndex(o => o.id === objectiveId); if (objectiveIndex === -1) return null; const objective = quest.objectives[objectiveIndex]; objective.current = objective.required; objective.completed = true; quest.objectives[objectiveIndex] = objective; return this.update(quest.id, { objectives: quest.objectives }); } updateLog(log: QuestLog): void { const validLog = QuestLogSchema.parse(log); const stmt = this.db.prepare(` INSERT INTO quest_logs (character_id, active_quests, completed_quests, failed_quests) VALUES (@characterId, @activeQuests, @completedQuests, @failedQuests) ON CONFLICT(character_id) DO UPDATE SET active_quests = excluded.active_quests, completed_quests = excluded.completed_quests, failed_quests = excluded.failed_quests `); stmt.run({ characterId: validLog.characterId, activeQuests: JSON.stringify(validLog.activeQuests), completedQuests: JSON.stringify(validLog.completedQuests), failedQuests: JSON.stringify(validLog.failedQuests) }); } private rowToQuest(row: QuestRow): Quest { return QuestSchema.parse({ id: row.id, worldId: row.world_id, name: row.name, description: row.description, status: row.status, objectives: JSON.parse(row.objectives), rewards: JSON.parse(row.rewards), prerequisites: JSON.parse(row.prerequisites), giver: row.giver || undefined, createdAt: row.created_at, updatedAt: row.updated_at }); } private rowToQuestLog(row: QuestLogRow): QuestLog { return QuestLogSchema.parse({ characterId: row.character_id, activeQuests: JSON.parse(row.active_quests), completedQuests: JSON.parse(row.completed_quests), failedQuests: JSON.parse(row.failed_quests) }); } } interface QuestRow { id: string; world_id: string; name: string; description: string; status: string; objectives: string; rewards: string; prerequisites: string; giver: string | null; created_at: string; updated_at: string; } interface QuestLogRow { character_id: string; active_quests: string; completed_quests: string; failed_quests: string; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Mnehmos/rpg-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server