mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 20:01:09 +02:00
✨ feat(matrix-todo-bot): improve UX with German commands without ! prefix
- heute command now shows both today's tasks AND inbox tasks - All commands work without ! prefix (e.g., 'heute' instead of '!heute') - Updated all help text and messages to show German commands - Added direct command recognition for: neu, heute, erledigt, löschen, etc. - Commands still work with ! prefix for backwards compatibility Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
d4663b5643
commit
cee30ff7ed
2 changed files with 162 additions and 52 deletions
|
|
@ -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<string, string[]> = {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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!`;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue