♻️ refactor: consolidate SessionService & TranscriptionService in @manacore/bot-services

Created shared services to eliminate code duplication across Matrix bots:

**New Services in @manacore/bot-services:**
- SessionService: User authentication via mana-core-auth (was duplicated in 11 bots)
- TranscriptionService: Speech-to-text via mana-stt (was duplicated in 6 bots)

**Migrated Bots:**
- matrix-todo-bot: uses TranscriptionService
- matrix-picture-bot: uses SessionService
- matrix-clock-bot: uses TranscriptionService
- matrix-zitare-bot: uses both SessionService & TranscriptionService

**Code Reduction:**
- Removed ~300 lines of duplicate code from migrated bots
- Centralized service configuration via NestJS modules
- Added comprehensive documentation in CLAUDE.md

Remaining bots can be migrated following the same pattern documented
in packages/bot-services/CLAUDE.md.

Note: @storage/backend type-check fails due to pre-existing drizzle-orm issue

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Till-JS 2026-02-01 00:37:54 +01:00
parent 508ae124a9
commit 9b61831cb5
35 changed files with 1014 additions and 903 deletions

View file

@ -1,10 +1,10 @@
import { Module } from '@nestjs/common';
import { MatrixService } from './matrix.service';
import { ClockModule } from '../clock/clock.module';
import { TranscriptionModule } from '../transcription/transcription.module';
import { TranscriptionModule } from '@manacore/bot-services';
@Module({
imports: [ClockModule, TranscriptionModule],
imports: [ClockModule, TranscriptionModule.forRoot()],
providers: [MatrixService],
exports: [MatrixService],
})

View file

@ -9,7 +9,7 @@ import {
import * as path from 'path';
import * as fs from 'fs';
import { ClockService, Timer, Alarm } from '../clock/clock.service';
import { TranscriptionService } from '../transcription/transcription.service';
import { TranscriptionService } from '@manacore/bot-services';
import { HELP_TEXT, WELCOME_TEXT } from '../config/configuration';
// Natural language keywords

View file

@ -1,8 +0,0 @@
import { Module } from '@nestjs/common';
import { TranscriptionService } from './transcription.service';
@Module({
providers: [TranscriptionService],
exports: [TranscriptionService],
})
export class TranscriptionModule {}

View file

@ -1,54 +0,0 @@
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
interface SttResponse {
text: string;
language?: string;
model?: string;
}
@Injectable()
export class TranscriptionService {
private readonly logger = new Logger(TranscriptionService.name);
private readonly sttUrl: string;
constructor(private configService: ConfigService) {
this.sttUrl = this.configService.get<string>('stt.url') || 'http://localhost:3020';
this.logger.log(`STT Service URL: ${this.sttUrl}`);
}
async transcribe(audioBuffer: Buffer, language: string = 'de'): Promise<string> {
const formData = new FormData();
const blob = new Blob([new Uint8Array(audioBuffer)], { type: 'audio/ogg' });
formData.append('file', blob, 'audio.ogg');
formData.append('language', language);
try {
const response = await fetch(`${this.sttUrl}/transcribe`, {
method: 'POST',
body: formData,
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`STT service error: ${response.status} - ${errorText}`);
}
const result: SttResponse = await response.json();
this.logger.log(`Transcription completed: ${result.text.substring(0, 50)}...`);
return result.text;
} catch (error) {
this.logger.error('Transcription failed:', error);
throw error;
}
}
async checkHealth(): Promise<boolean> {
try {
const response = await fetch(`${this.sttUrl}/health`);
return response.ok;
} catch {
return false;
}
}
}