diff --git a/services/matrix-todo-bot/src/bot/matrix.service.ts b/services/matrix-todo-bot/src/bot/matrix.service.ts index 0ac117ef8..45de59234 100644 --- a/services/matrix-todo-bot/src/bot/matrix.service.ts +++ b/services/matrix-todo-bot/src/bot/matrix.service.ts @@ -28,15 +28,31 @@ export class MatrixService extends BaseMatrixService { private readonly keywordDetector = new KeywordCommandDetector( [ ...COMMON_KEYWORDS, - { keywords: ['was kannst du'], command: 'help' }, + { keywords: ['was kannst du', 'hilfe', 'help'], command: 'help' }, { - keywords: ['zeige aufgaben', 'meine aufgaben', 'was muss ich', 'show tasks', 'list'], + keywords: [ + 'zeige aufgaben', + 'meine aufgaben', + 'was muss ich', + 'show tasks', + 'list', + 'liste', + 'alle', + ], command: 'list', }, { keywords: ['heute', 'today', 'was steht an'], command: 'today' }, { keywords: ['inbox', 'eingang', 'ohne datum'], command: 'inbox' }, { keywords: ['projekte', 'projects'], command: 'projects' }, - { keywords: ['verbindung', 'connection'], command: 'status' }, + { keywords: ['verbindung', 'connection', 'status'], command: 'status' }, + { keywords: ['neu', 'neue', 'add'], command: 'add' }, + { keywords: ['erledigt', 'fertig', 'done'], command: 'done' }, + { keywords: ['löschen', 'entfernen', 'delete'], command: 'delete' }, + { keywords: ['projekt', 'project'], command: 'project' }, + { keywords: ['pin'], command: 'pin' }, + { keywords: ['login', 'anmelden'], command: 'login' }, + { keywords: ['logout', 'abmelden'], command: 'logout' }, + { keywords: ['sprache', 'language', 'lang'], command: 'language' }, ], { partialMatch: true } ); @@ -93,6 +109,39 @@ export class MatrixService extends BaseMatrixService { return BOT_INTRODUCTION; } + // Commands that can be used without ! prefix + private readonly directCommands = [ + 'help', + 'hilfe', + 'add', + 'neu', + 'neue', + 'list', + 'liste', + 'alle', + 'heute', + 'today', + 'inbox', + 'eingang', + 'done', + 'erledigt', + 'fertig', + 'delete', + 'löschen', + 'entfernen', + 'projects', + 'projekte', + 'project', + 'projekt', + 'status', + 'pin', + 'login', + 'logout', + 'language', + 'sprache', + 'lang', + ]; + protected async handleTextMessage( roomId: string, event: MatrixRoomEvent, @@ -101,20 +150,33 @@ export class MatrixService extends BaseMatrixService { const userId = event.sender; try { - // Check for natural language keywords first - const keywordCommand = this.keywordDetector.detect(body); - if (keywordCommand) { - await this.executeCommand(roomId, event, userId, keywordCommand, ''); - return; - } - - // Check for ! commands + // Check for ! commands first if (body.startsWith('!')) { const [command, ...args] = body.slice(1).split(' '); await this.executeCommand(roomId, event, userId, command.toLowerCase(), args.join(' ')); return; } + // Check for direct commands (without ! prefix) + const trimmedBody = body.trim(); + const words = trimmedBody.split(/\s+/); + const firstWord = words[0].toLowerCase(); + + if (this.directCommands.includes(firstWord)) { + const args = words.slice(1).join(' '); + await this.executeCommand(roomId, event, userId, firstWord, args); + return; + } + + // Check for natural language keywords + const keywordCommand = this.keywordDetector.detect(body); + if (keywordCommand) { + // For commands that need args, try to extract from the message + const args = this.extractArgsAfterKeyword(body, keywordCommand); + await this.executeCommand(roomId, event, userId, keywordCommand, args); + return; + } + // Fallback: treat any message as a task await this.handleAddTask(roomId, event, userId, body); } catch (error) { @@ -123,6 +185,33 @@ export class MatrixService extends BaseMatrixService { } } + /** + * Extract arguments after a keyword match + */ + private extractArgsAfterKeyword(body: string, command: string): string { + // Map commands to their trigger keywords + const commandKeywords: Record = { + add: ['neu', 'neue', 'add'], + done: ['erledigt', 'fertig', 'done'], + delete: ['löschen', 'entfernen', 'delete'], + project: ['projekt', 'project'], + login: ['login', 'anmelden'], + language: ['sprache', 'language', 'lang'], + }; + + const keywords = commandKeywords[command]; + if (!keywords) return ''; + + const lowerBody = body.toLowerCase(); + for (const keyword of keywords) { + const index = lowerBody.indexOf(keyword); + if (index !== -1) { + return body.substring(index + keyword.length).trim(); + } + } + return ''; + } + protected async handleAudioMessage( roomId: string, event: MatrixRoomEvent, @@ -338,7 +427,7 @@ export class MatrixService extends BaseMatrixService { await this.sendReply( roomId, event, - `**Sprache / Language:** ${langName}\n\n**Verfügbar / Available:** ${available}\n\nÄndern / Change: \`!language de\` oder / or \`!language en\`` + `**Sprache / Language:** ${langName}\n\n**Verfügbar / Available:** ${available}\n\nÄndern / Change: \`sprache de\` oder / or \`sprache en\`` ); return; } @@ -375,7 +464,7 @@ export class MatrixService extends BaseMatrixService { await this.sendReply( roomId, event, - 'Bitte gib eine Aufgabe an.\n\nBeispiel: `!add Einkaufen gehen`' + 'Bitte gib eine Aufgabe an.\n\nBeispiel: `neu Einkaufen gehen`' ); return; } @@ -461,7 +550,7 @@ export class MatrixService extends BaseMatrixService { await this.sendReply( roomId, event, - 'Keine offenen Aufgaben.\n\nErstelle eine mit `!add [Aufgabe]`' + 'Keine offenen Aufgaben.\n\nErstelle eine mit `neu [Aufgabe]`' ); return; } @@ -475,27 +564,46 @@ export class MatrixService extends BaseMatrixService { private async handleTodayTasks(roomId: string, event: MatrixRoomEvent, userId: string) { const token = await this.getToken(userId); - let tasks: Task[]; + let todayTasks: Task[]; + let inboxTasks: Task[]; if (token) { // Use API service - const apiTasks = await this.todoApiService.getTodayTasks(token); - tasks = apiTasks.map((t) => this.normalizeTask(t)); + const apiTodayTasks = await this.todoApiService.getTodayTasks(token); + const apiInboxTasks = await this.todoApiService.getInboxTasks(token); + todayTasks = apiTodayTasks.map((t) => this.normalizeTask(t)); + inboxTasks = apiInboxTasks.map((t) => this.normalizeTask(t)); } else { // Use local storage - tasks = await this.todoService.getTodayTasks(userId); + todayTasks = await this.todoService.getTodayTasks(userId); + inboxTasks = await this.todoService.getInboxTasks(userId); } - if (tasks.length === 0) { + const hasTodayTasks = todayTasks.length > 0; + const hasInboxTasks = inboxTasks.length > 0; + + if (!hasTodayTasks && !hasInboxTasks) { await this.sendReply( roomId, event, - 'Keine Aufgaben fuer heute.\n\nErstelle eine mit `!add Aufgabe @heute`' + 'Keine Aufgaben.\n\nErstelle eine mit `neu Aufgabe` oder `neu Aufgabe @heute`' ); return; } - let response = this.formatTaskList('**Aufgaben fuer heute:**', tasks); + let response = ''; + + if (hasTodayTasks) { + response += this.formatTaskList('**Aufgaben fuer heute:**', todayTasks); + } + + if (hasInboxTasks) { + if (hasTodayTasks) { + response += '\n\n'; + } + response += this.formatTaskList('**Inbox (ohne Datum):**', inboxTasks); + } + if (token) { response += '\n\n🔄 Synchronisiert'; } @@ -539,7 +647,7 @@ export class MatrixService extends BaseMatrixService { await this.sendReply( roomId, event, - 'Bitte gib eine gueltige Aufgabennummer an.\n\nBeispiel: `!done 1`' + 'Bitte gib eine gueltige Aufgabennummer an.\n\nBeispiel: `erledigt 1`' ); return; } @@ -586,7 +694,7 @@ export class MatrixService extends BaseMatrixService { await this.sendReply( roomId, event, - 'Bitte gib eine gueltige Aufgabennummer an.\n\nBeispiel: `!delete 1`' + 'Bitte gib eine gueltige Aufgabennummer an.\n\nBeispiel: `löschen 1`' ); return; } @@ -638,7 +746,7 @@ export class MatrixService extends BaseMatrixService { await this.sendReply( roomId, event, - 'Keine Projekte.\n\nErstelle eine Aufgabe mit Projekt: `!add Aufgabe #projektname`' + 'Keine Projekte.\n\nErstelle eine Aufgabe mit Projekt: `neu Aufgabe #projektname`' ); return; } @@ -647,7 +755,7 @@ export class MatrixService extends BaseMatrixService { for (const project of projects) { response += `- ${project.name}\n`; } - response += '\nZeige Projektaufgaben mit `!project [Name]`'; + response += '\nZeige Projektaufgaben mit `projekt [Name]`'; if (token) { response += '\n\n🔄 Synchronisiert'; } @@ -667,7 +775,7 @@ export class MatrixService extends BaseMatrixService { await this.sendReply( roomId, event, - 'Bitte gib einen Projektnamen an.\n\nBeispiel: `!project Arbeit`' + 'Bitte gib einen Projektnamen an.\n\nBeispiel: `projekt Arbeit`' ); return; } @@ -743,7 +851,7 @@ export class MatrixService extends BaseMatrixService { - Gesamt: ${stats.total} ${syncStatus} -Bot: Online${!isLoggedIn ? '\n\nTipp: Mit `!login email passwort` anmelden fuer Synchronisation mit todo-web' : ''}`; +Bot: Online${!isLoggedIn ? '\n\nTipp: Mit `login email passwort` anmelden fuer Synchronisation mit todo-web' : ''}`; await this.sendReply(roomId, event, response); } @@ -751,7 +859,7 @@ Bot: Online${!isLoggedIn ? '\n\nTipp: Mit `!login email passwort` anmelden fuer private async handleLogin(roomId: string, event: MatrixRoomEvent, userId: string, args: string) { const parts = args.trim().split(/\s+/); if (parts.length < 2) { - await this.sendReply(roomId, event, 'Verwendung: `!login email passwort`'); + await this.sendReply(roomId, event, 'Verwendung: `login email passwort`'); return; } @@ -799,7 +907,7 @@ Bot: Online${!isLoggedIn ? '\n\nTipp: Mit `!login email passwort` anmelden fuer response += `**${num}.** ${task.title}${priority}${date}${project}\n`; }); - response += `\nErledigen: \`!done [Nr]\` | Loeschen: \`!delete [Nr]\``; + response += `\nErledigen: \`erledigt [Nr]\` | Loeschen: \`löschen [Nr]\``; return response; } diff --git a/services/matrix-todo-bot/src/config/configuration.ts b/services/matrix-todo-bot/src/config/configuration.ts index 29eb6fc9d..f1aecb973 100644 --- a/services/matrix-todo-bot/src/config/configuration.ts +++ b/services/matrix-todo-bot/src/config/configuration.ts @@ -18,46 +18,48 @@ export default () => ({ export const HELP_TEXT = `🎯 **Todo Bot - Hilfe** **Aufgaben verwalten:** -• \`!add [Aufgabe]\` - Neue Aufgabe hinzufügen +• \`neu [Aufgabe]\` - Neue Aufgabe hinzufügen • Sprachnotiz senden - Aufgabe per Sprache erstellen -• \`!list\` oder \`!heute\` - Heutige Aufgaben anzeigen -• \`!inbox\` - Aufgaben ohne Datum anzeigen -• \`!done [Nr]\` - Aufgabe als erledigt markieren -• \`!delete [Nr]\` - Aufgabe löschen +• \`heute\` - Heutige Aufgaben + Inbox anzeigen +• \`liste\` - Alle offenen Aufgaben +• \`inbox\` - Nur Aufgaben ohne Datum +• \`erledigt [Nr]\` - Aufgabe als erledigt markieren +• \`löschen [Nr]\` - Aufgabe löschen **Projekte:** -• \`!projects\` - Alle Projekte anzeigen -• \`!project [Name]\` - Aufgaben eines Projekts anzeigen +• \`projekte\` - Alle Projekte anzeigen +• \`projekt [Name]\` - Aufgaben eines Projekts anzeigen -**Prioritäten:** -• \`!add Wichtige Aufgabe !p1\` - Höchste Priorität (1-4) -• \`!add Morgen machen @morgen\` - Datum setzen +**Prioritäten & Datum:** +• \`neu Wichtige Aufgabe !p1\` - Höchste Priorität (1-4) +• \`neu Morgen machen @morgen\` - Datum setzen +• \`neu Heute erledigen @heute\` - Heute fällig **Sonstiges:** -• \`!status\` - Verbindungsstatus prüfen -• \`!help\` oder \`hilfe\` - Diese Hilfe anzeigen +• \`status\` - Verbindungsstatus prüfen +• \`hilfe\` - Diese Hilfe anzeigen +• \`login email passwort\` - Anmelden für Synchronisation +• \`logout\` - Abmelden -**Natürliche Sprache:** -Du kannst auch einfach "hilfe", "zeige aufgaben", "was muss ich heute machen?" schreiben. -Oder sende eine Sprachnotiz mit deiner Aufgabe!`; +**Tipp:** Alle Befehle funktionieren auch mit \`!\` davor (z.B. \`!neu\`)`; export const WELCOME_TEXT = `👋 **Willkommen beim Todo Bot!** Ich helfe dir, deine Aufgaben zu verwalten. Hier sind die wichtigsten Befehle: -• \`!add [Aufgabe]\` - Neue Aufgabe erstellen -• \`!list\` - Heutige Aufgaben anzeigen -• \`!done [Nr]\` - Aufgabe abhaken +• \`neu [Aufgabe]\` - Neue Aufgabe erstellen +• \`heute\` - Heutige Aufgaben + Inbox anzeigen +• \`erledigt [Nr]\` - Aufgabe abhaken -Schreibe \`!help\` oder einfach "hilfe" für alle Befehle.`; +Schreibe einfach "hilfe" für alle Befehle.`; export const BOT_INTRODUCTION = `🎯 **Hallo! Ich bin der Todo Bot.** Ich bin jetzt diesem Raum beigetreten und kann dir bei der Aufgabenverwaltung helfen. **Schnellstart:** -• \`!add Einkaufen gehen\` - Aufgabe erstellen -• \`!list\` - Deine Aufgaben sehen -• \`!done 1\` - Erste Aufgabe abhaken +• \`neu Einkaufen gehen\` - Aufgabe erstellen +• \`heute\` - Deine Aufgaben sehen +• \`erledigt 1\` - Erste Aufgabe abhaken -Schreibe \`!help\` für alle Befehle!`; +Schreibe "hilfe" für alle Befehle!`;