From 1733580d05de6bfc7ceaafd84fb66019eb99eebb Mon Sep 17 00:00:00 2001 From: Till-JS <101404291+Till-JS@users.noreply.github.com> Date: Thu, 29 Jan 2026 13:33:01 +0100 Subject: [PATCH] fix(types): resolve TypeScript errors across multiple packages - bot-services: Add registerAsync to AI, Calendar, Clock, Todo modules - bot-services: Add convenience methods to ClockService for bot handlers - bot-services: Make CreateEventInput.endTime optional with sensible defaults - bot-services: Fix empty interface ESLint errors (use type aliases) - questions-backend: Add missing schema columns (isDefault, sortOrder, deletedAt) - questions-backend: Fix or() return type handling in question service - questions-web: Add guard for undefined question ID in route params - skilltree-web: Fix DBSchema type by not extending idb interface directly - calendar-web: Fix Check icon prop (use weight instead of strokeWidth) - matrix-mana-bot: Update clock handler to use new service methods Co-Authored-By: Claude Opus 4.5 --- .../lib/components/todo/TodoCheckbox.svelte | 2 +- .../src/db/schema/collections.schema.ts | 6 +- .../backend/src/db/schema/questions.schema.ts | 1 + .../backend/src/question/question.service.ts | 19 +-- .../routes/(app)/question/[id]/+page.svelte | 3 + .../apps/web/src/lib/services/storage.ts | 4 +- packages/bot-services/src/ai/ai.module.ts | 34 +++++- .../src/calendar/calendar.module.ts | 36 +++++- .../src/calendar/calendar.service.ts | 32 ++++- packages/bot-services/src/calendar/types.ts | 4 +- .../bot-services/src/clock/clock.module.ts | 34 +++++- .../bot-services/src/clock/clock.service.ts | 112 ++++++++++++++++++ packages/bot-services/src/todo/todo.module.ts | 36 +++++- .../src/handlers/clock.handler.ts | 28 +++-- 14 files changed, 314 insertions(+), 37 deletions(-) diff --git a/apps/calendar/apps/web/src/lib/components/todo/TodoCheckbox.svelte b/apps/calendar/apps/web/src/lib/components/todo/TodoCheckbox.svelte index 0c29e4f1e..f82cdba07 100644 --- a/apps/calendar/apps/web/src/lib/components/todo/TodoCheckbox.svelte +++ b/apps/calendar/apps/web/src/lib/components/todo/TodoCheckbox.svelte @@ -53,7 +53,7 @@ {#if loading} {:else if checked} - + {/if} diff --git a/apps/questions/apps/backend/src/db/schema/collections.schema.ts b/apps/questions/apps/backend/src/db/schema/collections.schema.ts index 600265ce6..d71ef913f 100644 --- a/apps/questions/apps/backend/src/db/schema/collections.schema.ts +++ b/apps/questions/apps/backend/src/db/schema/collections.schema.ts @@ -1,4 +1,4 @@ -import { pgTable, uuid, text, boolean, timestamp } from 'drizzle-orm/pg-core'; +import { pgTable, uuid, text, boolean, timestamp, integer } from 'drizzle-orm/pg-core'; export const collections = pgTable('collections', { id: uuid('id').primaryKey().defaultRandom(), @@ -9,11 +9,15 @@ export const collections = pgTable('collections', { color: text('color').default('#6366f1'), icon: text('icon').default('folder'), + isDefault: boolean('is_default').default(false), + sortOrder: integer('sort_order').default(0), + isShared: boolean('is_shared').default(false), shareToken: text('share_token').unique(), createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(), updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow(), + deletedAt: timestamp('deleted_at', { withTimezone: true }), }); export type Collection = typeof collections.$inferSelect; diff --git a/apps/questions/apps/backend/src/db/schema/questions.schema.ts b/apps/questions/apps/backend/src/db/schema/questions.schema.ts index d59b846af..1b83892bd 100644 --- a/apps/questions/apps/backend/src/db/schema/questions.schema.ts +++ b/apps/questions/apps/backend/src/db/schema/questions.schema.ts @@ -32,6 +32,7 @@ export const questions = pgTable('questions', { // Soft delete isArchived: boolean('is_archived').default(false), archivedAt: timestamp('archived_at', { withTimezone: true }), + deletedAt: timestamp('deleted_at', { withTimezone: true }), }); export type Question = typeof questions.$inferSelect; diff --git a/apps/questions/apps/backend/src/question/question.service.ts b/apps/questions/apps/backend/src/question/question.service.ts index e425cecf5..e598181fc 100644 --- a/apps/questions/apps/backend/src/question/question.service.ts +++ b/apps/questions/apps/backend/src/question/question.service.ts @@ -8,7 +8,7 @@ import { CreateQuestionDto, UpdateQuestionDto } from './dto'; export class QuestionService { constructor( @Inject('DATABASE_CONNECTION') - private readonly db: NodePgDatabase, + private readonly db: NodePgDatabase ) {} async create(userId: string, dto: CreateQuestionDto): Promise { @@ -35,7 +35,7 @@ export class QuestionService { tags?: string[]; limit?: number; offset?: number; - }, + } ): Promise<{ data: Question[]; total: number }> { const conditions = [eq(questions.userId, userId), isNull(questions.deletedAt)]; @@ -48,12 +48,13 @@ export class QuestionService { } if (options?.search) { - conditions.push( - or( - ilike(questions.title, `%${options.search}%`), - ilike(questions.description, `%${options.search}%`), - ), + const searchCondition = or( + ilike(questions.title, `%${options.search}%`), + ilike(questions.description, `%${options.search}%`) ); + if (searchCondition) { + conditions.push(searchCondition); + } } const limit = options?.limit || 20; @@ -137,8 +138,8 @@ export class QuestionService { and( eq(questions.userId, userId), eq(questions.collectionId, collectionId), - isNull(questions.deletedAt), - ), + isNull(questions.deletedAt) + ) ) .orderBy(desc(questions.createdAt)); } diff --git a/apps/questions/apps/web/src/routes/(app)/question/[id]/+page.svelte b/apps/questions/apps/web/src/routes/(app)/question/[id]/+page.svelte index d4ed11125..d80012c37 100644 --- a/apps/questions/apps/web/src/routes/(app)/question/[id]/+page.svelte +++ b/apps/questions/apps/web/src/routes/(app)/question/[id]/+page.svelte @@ -43,6 +43,9 @@ try { const id = page.params.id; + if (!id) { + throw new Error('Question ID is required'); + } question = await questionsApi.getById(id); researchResults = await researchApi.getByQuestion(id); sources = await sourcesApi.getByQuestion(id); diff --git a/apps/skilltree/apps/web/src/lib/services/storage.ts b/apps/skilltree/apps/web/src/lib/services/storage.ts index 115a0e337..f9051dd89 100644 --- a/apps/skilltree/apps/web/src/lib/services/storage.ts +++ b/apps/skilltree/apps/web/src/lib/services/storage.ts @@ -1,7 +1,7 @@ -import { openDB, type DBSchema, type IDBPDatabase } from 'idb'; +import { openDB, type IDBPDatabase } from 'idb'; import type { Skill, Activity, UserStats } from '$lib/types'; -interface SkillTreeDB extends DBSchema { +interface SkillTreeDB { skills: { key: string; value: Skill; diff --git a/packages/bot-services/src/ai/ai.module.ts b/packages/bot-services/src/ai/ai.module.ts index b67d24d4f..bd393d140 100644 --- a/packages/bot-services/src/ai/ai.module.ts +++ b/packages/bot-services/src/ai/ai.module.ts @@ -1,8 +1,13 @@ -import { Module, DynamicModule } from '@nestjs/common'; +import { Module, DynamicModule, Provider, Type, ModuleMetadata } from '@nestjs/common'; import { AiService } from './ai.service'; import { AiServiceConfig } from './types'; -export interface AiModuleOptions extends Partial {} +export type AiModuleOptions = Partial; + +export interface AiModuleAsyncOptions extends Pick { + useFactory: (...args: unknown[]) => Promise | AiModuleOptions; + inject?: (Type | string | symbol)[]; +} @Module({}) export class AiModule { @@ -42,4 +47,29 @@ export class AiModule { exports: [AiService], }; } + + /** + * Register asynchronously with factory function + */ + static registerAsync(options: AiModuleAsyncOptions): DynamicModule { + const configProvider: Provider = { + provide: 'AI_SERVICE_CONFIG', + useFactory: options.useFactory, + inject: options.inject || [], + }; + + return { + module: AiModule, + imports: options.imports || [], + providers: [ + configProvider, + { + provide: AiService, + useFactory: (config: Partial) => new AiService(config), + inject: ['AI_SERVICE_CONFIG'], + }, + ], + exports: [AiService], + }; + } } diff --git a/packages/bot-services/src/calendar/calendar.module.ts b/packages/bot-services/src/calendar/calendar.module.ts index a1cf28cdd..bac494f9f 100644 --- a/packages/bot-services/src/calendar/calendar.module.ts +++ b/packages/bot-services/src/calendar/calendar.module.ts @@ -1,4 +1,4 @@ -import { Module, DynamicModule } from '@nestjs/common'; +import { Module, DynamicModule, Provider, Type, ModuleMetadata } from '@nestjs/common'; import { CalendarService, CALENDAR_STORAGE_PROVIDER } from './calendar.service'; import { StorageProvider } from '../shared/types'; import { FileStorageProvider } from '../shared/storage'; @@ -9,6 +9,11 @@ export interface CalendarModuleOptions { storageProvider?: StorageProvider; } +export interface CalendarModuleAsyncOptions extends Pick { + useFactory: (...args: unknown[]) => Promise | CalendarModuleOptions; + inject?: (Type | string | symbol)[]; +} + @Module({}) export class CalendarModule { /** @@ -24,7 +29,8 @@ export class CalendarModule { { provide: CALENDAR_STORAGE_PROVIDER, useValue: - options?.storageProvider ?? new FileStorageProvider(storagePath, defaultData), + options?.storageProvider ?? + new FileStorageProvider(storagePath, defaultData), }, CalendarService, ], @@ -48,4 +54,30 @@ export class CalendarModule { exports: [CalendarService], }; } + + /** + * Register asynchronously with factory function + */ + static registerAsync(options: CalendarModuleAsyncOptions): DynamicModule { + const storageProvider: Provider = { + provide: CALENDAR_STORAGE_PROVIDER, + useFactory: async (...args: unknown[]) => { + const moduleOptions = await options.useFactory(...args); + const storagePath = moduleOptions?.storagePath ?? './data/calendar-data.json'; + const defaultData: CalendarData = { events: [], calendars: [] }; + return ( + moduleOptions?.storageProvider ?? + new FileStorageProvider(storagePath, defaultData) + ); + }, + inject: options.inject || [], + }; + + return { + module: CalendarModule, + imports: options.imports || [], + providers: [storageProvider, CalendarService], + exports: [CalendarService], + }; + } } diff --git a/packages/bot-services/src/calendar/calendar.service.ts b/packages/bot-services/src/calendar/calendar.service.ts index 318cd7555..7b9299def 100644 --- a/packages/bot-services/src/calendar/calendar.service.ts +++ b/packages/bot-services/src/calendar/calendar.service.ts @@ -36,7 +36,10 @@ export class CalendarService implements OnModuleInit { ) { this.storage = storage || - new FileStorageProvider('./data/calendar-data.json', { events: [], calendars: [] }); + new FileStorageProvider('./data/calendar-data.json', { + events: [], + calendars: [], + }); } async onModuleInit() { @@ -46,7 +49,9 @@ export class CalendarService implements OnModuleInit { private async loadData(): Promise { try { this.data = await this.storage.load(); - this.logger.log(`Loaded ${this.data.events.length} events, ${this.data.calendars.length} calendars`); + this.logger.log( + `Loaded ${this.data.events.length} events, ${this.data.calendars.length} calendars` + ); } catch (error) { this.logger.error('Failed to load calendar data:', error); this.data = { events: [], calendars: [] }; @@ -81,6 +86,19 @@ export class CalendarService implements OnModuleInit { async createEvent(userId: string, input: CreateEventInput): Promise { const calendar = this.ensureDefaultCalendar(userId); + // Calculate endTime if not provided + let endTime: Date; + if (input.endTime) { + endTime = input.endTime; + } else if (input.isAllDay) { + // For all-day events, end at end of the same day + endTime = new Date(input.startTime); + endTime.setHours(23, 59, 59, 999); + } else { + // Default to 1 hour after start + endTime = new Date(input.startTime.getTime() + 60 * 60 * 1000); + } + const event: CalendarEvent = { id: generateId(), userId, @@ -88,7 +106,7 @@ export class CalendarService implements OnModuleInit { description: input.description ?? null, location: input.location ?? null, startTime: input.startTime.toISOString(), - endTime: input.endTime.toISOString(), + endTime: endTime.toISOString(), isAllDay: input.isAllDay ?? false, calendarId: input.calendarId ?? calendar.id, calendarName: calendar.name, @@ -101,7 +119,11 @@ export class CalendarService implements OnModuleInit { return event; } - async updateEvent(userId: string, eventId: string, input: UpdateEventInput): Promise { + async updateEvent( + userId: string, + eventId: string, + input: UpdateEventInput + ): Promise { const event = this.data.events.find((e) => e.id === eventId && e.userId === userId); if (!event) return null; @@ -197,7 +219,7 @@ export class CalendarService implements OnModuleInit { return this.getEventsInRange(userId, today, weekEnd); } - async getUpcomingEvents(userId: string, days: number = 7): Promise { + async getUpcomingEvents(userId: string, days = 7): Promise { const now = new Date(); const endDate = addDays(now, days); return this.getEventsInRange(userId, now, endDate); diff --git a/packages/bot-services/src/calendar/types.ts b/packages/bot-services/src/calendar/types.ts index 48877c41c..a469b56a4 100644 --- a/packages/bot-services/src/calendar/types.ts +++ b/packages/bot-services/src/calendar/types.ts @@ -1,4 +1,4 @@ -import { UserEntity } from '../shared/types'; +import { type UserEntity } from '../shared/types'; /** * Calendar event entity @@ -38,7 +38,7 @@ export interface CalendarData { export interface CreateEventInput { title: string; startTime: Date; - endTime: Date; + endTime?: Date; // Optional - defaults to startTime + 1 hour, or end of day for all-day events description?: string | null; location?: string | null; isAllDay?: boolean; diff --git a/packages/bot-services/src/clock/clock.module.ts b/packages/bot-services/src/clock/clock.module.ts index cd10c275d..a0c2591e8 100644 --- a/packages/bot-services/src/clock/clock.module.ts +++ b/packages/bot-services/src/clock/clock.module.ts @@ -1,8 +1,13 @@ -import { Module, DynamicModule } from '@nestjs/common'; +import { Module, DynamicModule, Provider, Type, ModuleMetadata } from '@nestjs/common'; import { ClockService } from './clock.service'; import { ClockServiceConfig } from './types'; -export interface ClockModuleOptions extends Partial {} +export type ClockModuleOptions = Partial; + +export interface ClockModuleAsyncOptions extends Pick { + useFactory: (...args: unknown[]) => Promise | ClockModuleOptions; + inject?: (Type | string | symbol)[]; +} @Module({}) export class ClockModule { @@ -42,4 +47,29 @@ export class ClockModule { exports: [ClockService], }; } + + /** + * Register asynchronously with factory function + */ + static registerAsync(options: ClockModuleAsyncOptions): DynamicModule { + const configProvider: Provider = { + provide: 'CLOCK_SERVICE_CONFIG', + useFactory: options.useFactory, + inject: options.inject || [], + }; + + return { + module: ClockModule, + imports: options.imports || [], + providers: [ + configProvider, + { + provide: ClockService, + useFactory: (config: Partial) => new ClockService(config), + inject: ['CLOCK_SERVICE_CONFIG'], + }, + ], + exports: [ClockService], + }; + } } diff --git a/packages/bot-services/src/clock/clock.service.ts b/packages/bot-services/src/clock/clock.service.ts index f92d163ab..1237208fb 100644 --- a/packages/bot-services/src/clock/clock.service.ts +++ b/packages/bot-services/src/clock/clock.service.ts @@ -267,4 +267,116 @@ export class ClockService { return parts.join(' '); } + + // ===== Convenience Methods for Bot Handlers ===== + + /** + * Start a timer from natural language input + * Parses duration and optional label from input like "25m Pomodoro" + */ + async startTimerForUser(userId: string, input: string): Promise { + const token = this.getUserToken(userId); + if (!token) { + throw new Error('Nicht authentifiziert. Bitte zuerst anmelden.'); + } + + // Parse duration from input + const durationSeconds = this.parseDuration(input); + if (!durationSeconds) { + throw new Error('Ungültiges Dauer-Format. Beispiele: 25m, 1h30m, 90s'); + } + + // Extract label (everything after duration pattern) + const label = input.replace(/\d+\s*[hms]?(?:in)?/gi, '').trim() || null; + + const timer = await this.createTimer({ durationSeconds, label }, token); + // Start the timer immediately + const started = await this.startTimer(timer.id, token); + return { ...started, name: started.label ?? undefined }; + } + + /** + * Stop the running timer for a user + */ + async stopTimerForUser(userId: string, timerName?: string): Promise { + const token = this.getUserToken(userId); + if (!token) { + throw new Error('Nicht authentifiziert. Bitte zuerst anmelden.'); + } + + const timers = await this.getTimers(token); + let timer: Timer | undefined; + + if (timerName) { + timer = timers.find( + (t) => + (t.status === 'running' || t.status === 'paused') && + t.label?.toLowerCase().includes(timerName.toLowerCase()) + ); + } else { + timer = timers.find((t) => t.status === 'running' || t.status === 'paused'); + } + + if (!timer) { + throw new Error('Kein aktiver Timer gefunden.'); + } + + await this.deleteTimer(timer.id, token); + return { ...timer, name: timer.label ?? undefined }; + } + + /** + * Set an alarm from natural language input + * Parses time and optional label from input like "14:30 Meeting" + */ + async setAlarmForUser(userId: string, input: string): Promise { + const token = this.getUserToken(userId); + if (!token) { + throw new Error('Nicht authentifiziert. Bitte zuerst anmelden.'); + } + + const time = this.parseAlarmTime(input); + if (!time) { + throw new Error('Ungültiges Zeit-Format. Beispiele: 14:30, 9:00, 14 Uhr 30'); + } + + // Extract label (everything after time pattern) + const label = + input + .replace(/\d{1,2}:\d{2}(:\d{2})?/g, '') + .replace(/\d{1,2}\s*uhr(\s*\d{1,2})?/gi, '') + .trim() || null; + + const alarm = await this.createAlarm({ time, label }, token); + return { ...alarm, name: alarm.label ?? undefined }; + } + + /** + * Get time for a specific city/timezone + */ + async getWorldClockTime(city: string): Promise<{ city: string; time: string; date: string }> { + // Search for timezone + const results = await this.searchTimezones(city); + if (results.length === 0) { + throw new Error(`Stadt "${city}" nicht gefunden.`); + } + + const tz = results[0]; + const now = new Date(); + + const time = now.toLocaleTimeString('de-DE', { + timeZone: tz.timezone, + hour: '2-digit', + minute: '2-digit', + }); + + const date = now.toLocaleDateString('de-DE', { + timeZone: tz.timezone, + weekday: 'long', + day: 'numeric', + month: 'long', + }); + + return { city: tz.city, time, date }; + } } diff --git a/packages/bot-services/src/todo/todo.module.ts b/packages/bot-services/src/todo/todo.module.ts index b65837bd3..774671fa7 100644 --- a/packages/bot-services/src/todo/todo.module.ts +++ b/packages/bot-services/src/todo/todo.module.ts @@ -1,4 +1,4 @@ -import { Module, DynamicModule } from '@nestjs/common'; +import { Module, DynamicModule, Provider, Type, ModuleMetadata } from '@nestjs/common'; import { TodoService, TODO_STORAGE_PROVIDER } from './todo.service'; import { StorageProvider } from '../shared/types'; import { FileStorageProvider } from '../shared/storage'; @@ -9,6 +9,11 @@ export interface TodoModuleOptions { storageProvider?: StorageProvider; } +export interface TodoModuleAsyncOptions extends Pick { + useFactory: (...args: unknown[]) => Promise | TodoModuleOptions; + inject?: (Type | string | symbol)[]; +} + @Module({}) export class TodoModule { /** @@ -23,7 +28,8 @@ export class TodoModule { providers: [ { provide: TODO_STORAGE_PROVIDER, - useValue: options?.storageProvider ?? new FileStorageProvider(storagePath, defaultData), + useValue: + options?.storageProvider ?? new FileStorageProvider(storagePath, defaultData), }, TodoService, ], @@ -47,4 +53,30 @@ export class TodoModule { exports: [TodoService], }; } + + /** + * Register asynchronously with factory function + */ + static registerAsync(options: TodoModuleAsyncOptions): DynamicModule { + const storageProvider: Provider = { + provide: TODO_STORAGE_PROVIDER, + useFactory: async (...args: unknown[]) => { + const moduleOptions = await options.useFactory(...args); + const storagePath = moduleOptions?.storagePath ?? './data/todo-data.json'; + const defaultData: TodoData = { tasks: [], projects: [] }; + return ( + moduleOptions?.storageProvider ?? + new FileStorageProvider(storagePath, defaultData) + ); + }, + inject: options.inject || [], + }; + + return { + module: TodoModule, + imports: options.imports || [], + providers: [storageProvider, TodoService], + exports: [TodoService], + }; + } } diff --git a/services/matrix-mana-bot/src/handlers/clock.handler.ts b/services/matrix-mana-bot/src/handlers/clock.handler.ts index a687400e0..6a55cc982 100644 --- a/services/matrix-mana-bot/src/handlers/clock.handler.ts +++ b/services/matrix-mana-bot/src/handlers/clock.handler.ts @@ -21,7 +21,7 @@ export class ClockHandler { } try { - const result = await this.clockService.startTimer(ctx.userId, input); + const result = await this.clockService.startTimerForUser(ctx.userId, input); this.logger.log(`Started timer for ${ctx.userId}: ${result.name}`); const durationStr = this.formatDuration(result.durationSeconds); @@ -33,7 +33,12 @@ export class ClockHandler { async listTimers(ctx: CommandContext): Promise { try { - const timers = await this.clockService.getTimers(ctx.userId); + const token = this.clockService.getUserToken(ctx.userId); + if (!token) { + return '❌ Nicht authentifiziert. Bitte zuerst anmelden.'; + } + + const timers = await this.clockService.getTimers(token); if (timers.length === 0) { return '⏱️ Keine aktiven Timer.\n\nStarte einen mit `!timer [Dauer]`'; @@ -42,8 +47,8 @@ export class ClockHandler { let response = '⏱️ **Aktive Timer:**\n\n'; for (const timer of timers) { const remaining = this.formatDuration(timer.remainingSeconds); - const status = timer.isPaused ? '⏸️' : '▶️'; - response += `${status} **${timer.name || 'Timer'}** - ${remaining} verbleibend\n`; + const status = timer.status === 'paused' ? '⏸️' : '▶️'; + response += `${status} **${timer.label || 'Timer'}** - ${remaining} verbleibend\n`; } response += '\n`!stop` zum Beenden'; @@ -55,7 +60,7 @@ export class ClockHandler { async stopTimer(ctx: CommandContext, args: string): Promise { try { - const result = await this.clockService.stopTimer(ctx.userId, args.trim() || undefined); + const result = await this.clockService.stopTimerForUser(ctx.userId, args.trim() || undefined); return `⏹️ Timer gestoppt: **${result.name || 'Timer'}**`; } catch (error) { return `❌ ${error instanceof Error ? error.message : 'Kein aktiver Timer gefunden'}`; @@ -73,7 +78,7 @@ export class ClockHandler { } try { - const result = await this.clockService.setAlarm(ctx.userId, input); + const result = await this.clockService.setAlarmForUser(ctx.userId, input); this.logger.log(`Set alarm for ${ctx.userId}: ${result.name} at ${result.time}`); return `⏰ Alarm gesetzt: **${result.name || 'Alarm'}**\nZeit: ${result.time}`; @@ -84,7 +89,12 @@ export class ClockHandler { async listAlarms(ctx: CommandContext): Promise { try { - const alarms = await this.clockService.getAlarms(ctx.userId); + const token = this.clockService.getUserToken(ctx.userId); + if (!token) { + return '❌ Nicht authentifiziert. Bitte zuerst anmelden.'; + } + + const alarms = await this.clockService.getAlarms(token); if (alarms.length === 0) { return '⏰ Keine aktiven Alarme.\n\nSetze einen mit `!alarm [Zeit]`'; @@ -93,7 +103,7 @@ export class ClockHandler { let response = '⏰ **Aktive Alarme:**\n\n'; for (const alarm of alarms) { const status = alarm.enabled ? '🔔' : '🔕'; - response += `${status} **${alarm.name || 'Alarm'}** - ${alarm.time}\n`; + response += `${status} **${alarm.label || 'Alarm'}** - ${alarm.time}\n`; } return response; @@ -130,7 +140,7 @@ export class ClockHandler { } try { - const result = await this.clockService.getWorldClock(city); + const result = await this.clockService.getWorldClockTime(city); return `🕐 **${result.city}:** ${result.time}\n📅 ${result.date}`; } catch (error) { return `❌ Stadt "${city}" nicht gefunden.\n\nVersuche: Berlin, London, New York, Tokyo, Sydney`;