mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 22:21:10 +02:00
✨ feat(clock-bot): add timer status to room topic (Phase 3)
- Store original room topic when timer starts - Update room topic with timer progress every 10 seconds - Show status icon, remaining time, percentage, and label - Restore original topic when timer finishes or is deleted Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
db7f2dfe08
commit
992181cc87
1 changed files with 125 additions and 4 deletions
|
|
@ -33,6 +33,14 @@ interface TimerMessageTracker {
|
|||
startedAt: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Room topic tracking for timer display
|
||||
*/
|
||||
interface RoomTopicTracker {
|
||||
originalTopic: string;
|
||||
timerId: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class MatrixService extends BaseMatrixService implements OnModuleDestroy {
|
||||
// Demo token for development (TODO: implement proper auth)
|
||||
|
|
@ -43,6 +51,9 @@ export class MatrixService extends BaseMatrixService implements OnModuleDestroy
|
|||
private timerUpdateInterval: NodeJS.Timeout | null = null;
|
||||
private readonly UPDATE_INTERVAL_MS = 10000; // Update every 10 seconds
|
||||
|
||||
// Track original room topics for restoration when timer ends
|
||||
private readonly originalRoomTopics = new Map<string, RoomTopicTracker>();
|
||||
|
||||
// Note: We override COMMON_KEYWORDS' cancel->cancel with stop->stop for this bot
|
||||
private readonly keywordDetector = new KeywordCommandDetector([
|
||||
{ keywords: ['hilfe', 'help', 'befehle', 'commands'], command: 'help' },
|
||||
|
|
@ -120,8 +131,9 @@ export class MatrixService extends BaseMatrixService implements OnModuleDestroy
|
|||
const timer = await this.clockService.getTimer(tracker.timerId, tracker.token);
|
||||
|
||||
if (!timer) {
|
||||
// Timer deleted, remove tracking
|
||||
// Timer deleted, remove tracking and restore topic
|
||||
this.activeTimerMessages.delete(tracker.timerId);
|
||||
await this.restoreRoomTopic(tracker.roomId);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -129,15 +141,16 @@ export class MatrixService extends BaseMatrixService implements OnModuleDestroy
|
|||
const remainingSeconds = this.calculateRemainingSeconds(timer);
|
||||
|
||||
if (timer.status === 'finished' || remainingSeconds <= 0) {
|
||||
// Timer finished - send final message and stop tracking
|
||||
// Timer finished - send final message, restore topic, and stop tracking
|
||||
const finalMessage = this.formatTimerFinishedMessage(tracker);
|
||||
await this.editMessage(tracker.roomId, tracker.eventId, finalMessage);
|
||||
await this.restoreRoomTopic(tracker.roomId);
|
||||
this.activeTimerMessages.delete(tracker.timerId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (timer.status === 'paused') {
|
||||
// Timer paused - update message but keep tracking
|
||||
// Timer paused - update message and topic but keep tracking
|
||||
const pausedMessage = this.formatTimerMessage(
|
||||
remainingSeconds,
|
||||
tracker.durationSeconds,
|
||||
|
|
@ -145,10 +158,18 @@ export class MatrixService extends BaseMatrixService implements OnModuleDestroy
|
|||
'paused'
|
||||
);
|
||||
await this.editMessage(tracker.roomId, tracker.eventId, pausedMessage);
|
||||
await this.updateRoomTopicWithTimer(
|
||||
tracker.roomId,
|
||||
tracker.timerId,
|
||||
remainingSeconds,
|
||||
tracker.durationSeconds,
|
||||
tracker.label,
|
||||
'paused'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Timer running - update with current progress
|
||||
// Timer running - update message and topic with current progress
|
||||
const runningMessage = this.formatTimerMessage(
|
||||
remainingSeconds,
|
||||
tracker.durationSeconds,
|
||||
|
|
@ -156,6 +177,14 @@ export class MatrixService extends BaseMatrixService implements OnModuleDestroy
|
|||
'running'
|
||||
);
|
||||
await this.editMessage(tracker.roomId, tracker.eventId, runningMessage);
|
||||
await this.updateRoomTopicWithTimer(
|
||||
tracker.roomId,
|
||||
tracker.timerId,
|
||||
remainingSeconds,
|
||||
tracker.durationSeconds,
|
||||
tracker.label,
|
||||
'running'
|
||||
);
|
||||
} catch (error) {
|
||||
// Token might have expired or API error
|
||||
this.logger.warn(`Timer update failed for ${tracker.timerId}: ${error}`);
|
||||
|
|
@ -247,6 +276,88 @@ export class MatrixService extends BaseMatrixService implements OnModuleDestroy
|
|||
return `${filled}${empty}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current room topic
|
||||
*/
|
||||
private async getRoomTopic(roomId: string): Promise<string> {
|
||||
try {
|
||||
const client = this.getClient();
|
||||
const state = await client.getRoomStateEvent(roomId, 'm.room.topic', '');
|
||||
return state?.topic || '';
|
||||
} catch {
|
||||
// Room might not have a topic set
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set room topic
|
||||
*/
|
||||
private async setRoomTopic(roomId: string, topic: string): Promise<void> {
|
||||
try {
|
||||
const client = this.getClient();
|
||||
await client.sendStateEvent(roomId, 'm.room.topic', '', { topic });
|
||||
} catch (error) {
|
||||
this.logger.warn(`Failed to set room topic for ${roomId}: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format timer status for room topic
|
||||
*/
|
||||
private formatTimerTopic(
|
||||
remainingSeconds: number,
|
||||
totalSeconds: number,
|
||||
label: string | null,
|
||||
status: 'running' | 'paused'
|
||||
): string {
|
||||
const remaining = this.clockService.formatDuration(remainingSeconds);
|
||||
const percentage = Math.round((1 - remainingSeconds / totalSeconds) * 100);
|
||||
|
||||
const statusIcon = status === 'running' ? '▶️' : '⏸️';
|
||||
let topic = `${statusIcon} Timer: ${remaining} (${percentage}%)`;
|
||||
|
||||
if (label) {
|
||||
topic += ` - ${label}`;
|
||||
}
|
||||
|
||||
return topic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update room topic with timer status
|
||||
*/
|
||||
private async updateRoomTopicWithTimer(
|
||||
roomId: string,
|
||||
timerId: string,
|
||||
remainingSeconds: number,
|
||||
totalSeconds: number,
|
||||
label: string | null,
|
||||
status: 'running' | 'paused'
|
||||
): Promise<void> {
|
||||
// Save original topic if not already saved for this room
|
||||
if (!this.originalRoomTopics.has(roomId)) {
|
||||
const originalTopic = await this.getRoomTopic(roomId);
|
||||
this.originalRoomTopics.set(roomId, { originalTopic, timerId });
|
||||
}
|
||||
|
||||
// Update topic with timer status
|
||||
const timerTopic = this.formatTimerTopic(remainingSeconds, totalSeconds, label, status);
|
||||
await this.setRoomTopic(roomId, timerTopic);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore original room topic when timer ends
|
||||
*/
|
||||
private async restoreRoomTopic(roomId: string): Promise<void> {
|
||||
const tracker = this.originalRoomTopics.get(roomId);
|
||||
if (tracker) {
|
||||
await this.setRoomTopic(roomId, tracker.originalTopic);
|
||||
this.originalRoomTopics.delete(roomId);
|
||||
this.logger.log(`Restored original topic for room ${roomId}`);
|
||||
}
|
||||
}
|
||||
|
||||
protected getConfig(): MatrixBotConfig {
|
||||
return {
|
||||
homeserverUrl:
|
||||
|
|
@ -493,6 +604,16 @@ export class MatrixService extends BaseMatrixService implements OnModuleDestroy
|
|||
startedAt: new Date(),
|
||||
});
|
||||
|
||||
// Set initial room topic with timer status
|
||||
await this.updateRoomTopicWithTimer(
|
||||
roomId,
|
||||
timer.id,
|
||||
durationSeconds,
|
||||
durationSeconds,
|
||||
label,
|
||||
'running'
|
||||
);
|
||||
|
||||
this.logger.log(`Started tracking timer ${timer.id} for live updates`);
|
||||
} catch (error) {
|
||||
this.logger.error('Timer creation failed:', error);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue