♻️ refactor: migrate 4 more bots to use UserListMapper

Migrated to UserListMapper<T> from @manacore/matrix-bot-common:
- matrix-manadeck-bot (decksMapper, cardsMapper)
- matrix-planta-bot (plantsMapper)
- matrix-questions-bot (questionsMapper, collectionsMapper, answersMapper)
- matrix-storage-bot (filesMapper, foldersMapper, sharesMapper, trashMapper)

Benefits:
- Consistent API for user-specific list storage
- Type-safe getByNumber(), setList(), hasList(), clearList()
- Reduced boilerplate code

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Till-JS 2026-02-01 03:03:10 +01:00
parent 48dfcd180f
commit 7a2e037cd5
4 changed files with 89 additions and 105 deletions

View file

@ -4,6 +4,7 @@ import {
BaseMatrixService,
MatrixBotConfig,
MatrixRoomEvent,
UserListMapper,
} from '@manacore/matrix-bot-common';
import { ManadeckService, Deck, Card } from '../manadeck/manadeck.service';
import { SessionService } from '@manacore/bot-services';
@ -12,8 +13,9 @@ import { HELP_MESSAGE } from '../config/configuration';
@Injectable()
export class MatrixService extends BaseMatrixService {
// Store last shown decks/cards per user for reference by number
private lastDecksList: Map<string, Deck[]> = new Map();
private lastCardsList: Map<string, { deckId: string; cards: Card[] }> = new Map();
private decksMapper = new UserListMapper<Deck>();
private cardsMapper = new UserListMapper<Card>();
private currentDeckId: Map<string, string> = new Map();
constructor(
configService: ConfigService,
@ -206,7 +208,7 @@ export class MatrixService extends BaseMatrixService {
}
const decks = result.data || [];
this.lastDecksList.set(sender, decks);
this.decksMapper.setList(sender, decks);
if (decks.length === 0) {
await this.sendHtml(
@ -304,7 +306,7 @@ export class MatrixService extends BaseMatrixService {
}
// Clear cached list
this.lastDecksList.delete(sender);
this.decksMapper.clearList(sender);
await this.sendHtml(roomId, `<p>Deck <strong>${deck.title}</strong> geloescht.</p>`);
}
@ -329,7 +331,8 @@ export class MatrixService extends BaseMatrixService {
}
const cards = result.data || [];
this.lastCardsList.set(sender, { deckId: deck.id, cards });
this.cardsMapper.setList(sender, cards);
this.currentDeckId.set(sender, deck.id);
if (cards.length === 0) {
await this.sendHtml(
@ -358,10 +361,7 @@ export class MatrixService extends BaseMatrixService {
cardNumStr: string
) {
const token = this.requireAuth(sender);
// Try to get from cache first
let cachedCards = this.lastCardsList.get(sender);
const deck = this.getDeckByNumber(sender, deckNumStr);
const deck = this.decksMapper.getByNumber(sender, parseInt(deckNumStr, 10));
if (!deck) {
await this.sendHtml(
@ -372,26 +372,26 @@ export class MatrixService extends BaseMatrixService {
}
// Refresh cards if needed
if (!cachedCards || cachedCards.deckId !== deck.id) {
const cachedDeckId = this.currentDeckId.get(sender);
if (!cachedDeckId || cachedDeckId !== deck.id || !this.cardsMapper.hasList(sender)) {
const result = await this.manadeckService.getCards(token, deck.id);
if (result.error) {
await this.sendHtml(roomId, `<p>Fehler: ${result.error}</p>`);
return;
}
cachedCards = { deckId: deck.id, cards: result.data || [] };
this.lastCardsList.set(sender, cachedCards);
this.cardsMapper.setList(sender, result.data || []);
this.currentDeckId.set(sender, deck.id);
}
const cardIndex = parseInt(cardNumStr, 10) - 1;
if (isNaN(cardIndex) || cardIndex < 0 || cardIndex >= cachedCards.cards.length) {
const cardNum = parseInt(cardNumStr, 10);
const card = this.cardsMapper.getByNumber(sender, cardNum);
if (!card) {
await this.sendHtml(
roomId,
`<p>Ungueltige Kartennummer. Nutze <code>!karten ${deckNumStr}</code></p>`
);
return;
}
const card = cachedCards.cards[cardIndex];
let html = `<h3>Karte #${cardNumStr}</h3>`;
html += `<p><strong>Typ:</strong> ${card.cardType}</p>`;
@ -618,13 +618,9 @@ export class MatrixService extends BaseMatrixService {
// Helper methods
private getDeckByNumber(sender: string, numberStr: string): Deck | null {
const decks = this.lastDecksList.get(sender);
if (!decks) return null;
const index = parseInt(numberStr, 10) - 1;
if (isNaN(index) || index < 0 || index >= decks.length) return null;
return decks[index];
const num = parseInt(numberStr, 10);
if (isNaN(num)) return null;
return this.decksMapper.getByNumber(sender, num);
}
private getCardPreview(card: Card): string {

View file

@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { BaseMatrixService, MatrixBotConfig, MatrixRoomEvent } from '@manacore/matrix-bot-common';
import { BaseMatrixService, MatrixBotConfig, MatrixRoomEvent, UserListMapper } from '@manacore/matrix-bot-common';
import { PlantaService, Plant } from '../planta/planta.service';
import { SessionService } from '@manacore/bot-services';
import { HELP_MESSAGE } from '../config/configuration';
@ -8,7 +8,7 @@ import { HELP_MESSAGE } from '../config/configuration';
@Injectable()
export class MatrixService extends BaseMatrixService {
// Store last shown plants per user for reference by number
private lastPlantsList: Map<string, Plant[]> = new Map();
private plantsMapper = new UserListMapper<Plant>();
// Field mappings for edit command
private readonly fieldMappings: Record<string, string> = {
@ -196,7 +196,7 @@ export class MatrixService extends BaseMatrixService {
}
const plants = result.data || [];
this.lastPlantsList.set(sender, plants);
this.plantsMapper.setList(sender, plants);
if (plants.length === 0) {
await this.sendMessage(
@ -274,7 +274,7 @@ export class MatrixService extends BaseMatrixService {
}
// Clear cached list
this.lastPlantsList.delete(sender);
this.plantsMapper.clearList(sender);
await this.sendMessage(
roomId,
`<p>Pflanze <strong>${result.data!.name}</strong> hinzugefuegt!</p>
@ -302,7 +302,7 @@ export class MatrixService extends BaseMatrixService {
}
// Clear cached list
this.lastPlantsList.delete(sender);
this.plantsMapper.clearList(sender);
await this.sendMessage(roomId, `<p>Pflanze <strong>${plant.name}</strong> entfernt.</p>`);
}
@ -448,7 +448,7 @@ export class MatrixService extends BaseMatrixService {
html += '</ul>';
// Store plants for reference
this.lastPlantsList.set(sender, upcoming.map(u => u.plant));
this.plantsMapper.setList(sender, upcoming.map(u => u.plant));
await this.sendMessage(roomId, html);
}
@ -544,13 +544,9 @@ export class MatrixService extends BaseMatrixService {
// Helper methods
private getPlantByNumber(sender: string, numberStr: string): Plant | null {
const plants = this.lastPlantsList.get(sender);
if (!plants) return null;
const index = parseInt(numberStr, 10) - 1;
if (isNaN(index) || index < 0 || index >= plants.length) return null;
return plants[index];
const num = parseInt(numberStr, 10);
if (isNaN(num)) return null;
return this.plantsMapper.getByNumber(sender, num);
}
private getHealthEmoji(status?: string): string {

View file

@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { BaseMatrixService, MatrixBotConfig, MatrixRoomEvent } from '@manacore/matrix-bot-common';
import { BaseMatrixService, MatrixBotConfig, MatrixRoomEvent, UserListMapper } from '@manacore/matrix-bot-common';
import { QuestionsService, Question, Collection, Answer } from '../questions/questions.service';
import { SessionService } from '@manacore/bot-services';
import { HELP_MESSAGE } from '../config/configuration';
@ -8,9 +8,9 @@ import { HELP_MESSAGE } from '../config/configuration';
@Injectable()
export class MatrixService extends BaseMatrixService {
// Store last shown items per user for reference by number
private lastQuestionsList: Map<string, Question[]> = new Map();
private lastCollectionsList: Map<string, Collection[]> = new Map();
private lastAnswersList: Map<string, Answer[]> = new Map();
private questionsMapper = new UserListMapper<Question>();
private collectionsMapper = new UserListMapper<Collection>();
private answersMapper = new UserListMapper<Answer>();
constructor(
configService: ConfigService,
@ -220,7 +220,7 @@ export class MatrixService extends BaseMatrixService {
}
const questions = result.data || [];
this.lastQuestionsList.set(sender, questions);
this.questionsMapper.setList(sender, questions);
if (questions.length === 0) {
await this.sendMessage(
@ -292,7 +292,7 @@ export class MatrixService extends BaseMatrixService {
return;
}
this.lastQuestionsList.delete(sender);
this.questionsMapper.clearList(sender);
await this.sendMessage(
roomId,
`<p>Frage erstellt: <strong>${result.data!.title}</strong></p>
@ -316,7 +316,7 @@ export class MatrixService extends BaseMatrixService {
return;
}
this.lastQuestionsList.delete(sender);
this.questionsMapper.clearList(sender);
await this.sendMessage(roomId, `<p>Frage geloescht: <strong>${question.title}</strong></p>`);
}
@ -496,7 +496,7 @@ export class MatrixService extends BaseMatrixService {
}
const answers = result.data || [];
this.lastAnswersList.set(sender, answers);
this.answersMapper.setList(sender, answers);
if (answers.length === 0) {
await this.sendMessage(
@ -532,9 +532,8 @@ export class MatrixService extends BaseMatrixService {
private async handleRateAnswer(roomId: string, sender: string, numberStr: string, ratingStr: string) {
const token = this.requireAuth(sender);
const answers = this.lastAnswersList.get(sender);
if (!answers || answers.length === 0) {
if (!this.answersMapper.hasList(sender)) {
await this.sendMessage(roomId, '<p>Zeige zuerst eine Antwort mit <code>!antwort [nr]</code></p>');
return;
}
@ -545,7 +544,12 @@ export class MatrixService extends BaseMatrixService {
return;
}
const answer = answers[0];
// Get first answer (most recent)
const answer = this.answersMapper.getByNumber(sender, 1);
if (!answer) {
await this.sendMessage(roomId, '<p>Keine Antwort gefunden.</p>');
return;
}
const result = await this.questionsService.rateAnswer(token, answer.id, rating);
if (result.error) {
@ -558,14 +562,18 @@ export class MatrixService extends BaseMatrixService {
private async handleAcceptAnswer(roomId: string, sender: string, numberStr: string) {
const token = this.requireAuth(sender);
const answers = this.lastAnswersList.get(sender);
if (!answers || answers.length === 0) {
if (!this.answersMapper.hasList(sender)) {
await this.sendMessage(roomId, '<p>Zeige zuerst eine Antwort mit <code>!antwort [nr]</code></p>');
return;
}
const answer = answers[0];
// Get first answer (most recent)
const answer = this.answersMapper.getByNumber(sender, 1);
if (!answer) {
await this.sendMessage(roomId, '<p>Keine Antwort gefunden.</p>');
return;
}
const result = await this.questionsService.acceptAnswer(token, answer.id);
if (result.error) {
@ -587,7 +595,7 @@ export class MatrixService extends BaseMatrixService {
}
const collections = result.data || [];
this.lastCollectionsList.set(sender, collections);
this.collectionsMapper.setList(sender, collections);
if (collections.length === 0) {
await this.sendMessage(
@ -622,7 +630,7 @@ export class MatrixService extends BaseMatrixService {
return;
}
this.lastCollectionsList.delete(sender);
this.collectionsMapper.clearList(sender);
await this.sendMessage(roomId, `<p>Sammlung <strong>${result.data!.name}</strong> erstellt.</p>`);
}
@ -642,7 +650,7 @@ export class MatrixService extends BaseMatrixService {
}
const questions = result.data || [];
this.lastQuestionsList.set(sender, questions);
this.questionsMapper.setList(sender, questions);
if (questions.length === 0) {
await this.sendMessage(roomId, `<p>Keine Fragen gefunden fuer "${query}"</p>`);
@ -661,13 +669,9 @@ export class MatrixService extends BaseMatrixService {
// Helper methods
private getQuestionByNumber(sender: string, numberStr: string): Question | null {
const questions = this.lastQuestionsList.get(sender);
if (!questions) return null;
const index = parseInt(numberStr, 10) - 1;
if (isNaN(index) || index < 0 || index >= questions.length) return null;
return questions[index];
const num = parseInt(numberStr, 10);
if (isNaN(num)) return null;
return this.questionsMapper.getByNumber(sender, num);
}
private getStatusEmoji(status: string): string {

View file

@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { BaseMatrixService, MatrixBotConfig, MatrixRoomEvent } from '@manacore/matrix-bot-common';
import { BaseMatrixService, MatrixBotConfig, MatrixRoomEvent, UserListMapper } from '@manacore/matrix-bot-common';
import { StorageService, StorageFile, Folder, ShareLink, TrashItem } from '../storage/storage.service';
import { SessionService } from '@manacore/bot-services';
import { HELP_MESSAGE } from '../config/configuration';
@ -8,10 +8,10 @@ import { HELP_MESSAGE } from '../config/configuration';
@Injectable()
export class MatrixService extends BaseMatrixService {
// Store last shown items per user for reference by number
private lastFilesList: Map<string, StorageFile[]> = new Map();
private lastFoldersList: Map<string, Folder[]> = new Map();
private lastSharesList: Map<string, ShareLink[]> = new Map();
private lastTrashList: Map<string, TrashItem[]> = new Map();
private filesMapper = new UserListMapper<StorageFile>();
private foldersMapper = new UserListMapper<Folder>();
private sharesMapper = new UserListMapper<ShareLink>();
private trashMapper = new UserListMapper<TrashItem>();
private currentFolder: Map<string, string | null> = new Map();
constructor(
@ -245,7 +245,7 @@ export class MatrixService extends BaseMatrixService {
}
const files = result.data || [];
this.lastFilesList.set(sender, files);
this.filesMapper.setList(sender, files);
if (files.length === 0) {
await this.sendMessage(roomId, '<p>Keine Dateien vorhanden.</p>');
@ -331,7 +331,7 @@ export class MatrixService extends BaseMatrixService {
return;
}
this.lastFilesList.delete(sender);
this.filesMapper.clearList(sender);
await this.sendMessage(roomId, `<p><strong>${file.name}</strong> in Papierkorb verschoben.</p>`);
}
@ -413,7 +413,7 @@ export class MatrixService extends BaseMatrixService {
}
const folders = result.data || [];
this.lastFoldersList.set(sender, folders);
this.foldersMapper.setList(sender, folders);
if (folders.length === 0) {
await this.sendMessage(roomId, '<p>Keine Ordner vorhanden.</p>');
@ -460,7 +460,7 @@ export class MatrixService extends BaseMatrixService {
return;
}
this.lastFoldersList.delete(sender);
this.foldersMapper.clearList(sender);
await this.sendMessage(roomId, `<p>Ordner <strong>${result.data!.name}</strong> erstellt.</p>`);
}
@ -480,7 +480,7 @@ export class MatrixService extends BaseMatrixService {
return;
}
this.lastFoldersList.delete(sender);
this.foldersMapper.clearList(sender);
await this.sendMessage(roomId, `<p>Ordner <strong>${folder.name}</strong> in Papierkorb verschoben.</p>`);
}
@ -547,7 +547,7 @@ export class MatrixService extends BaseMatrixService {
}
const shares = result.data || [];
this.lastSharesList.set(sender, shares);
this.sharesMapper.setList(sender, shares);
if (shares.length === 0) {
await this.sendMessage(roomId, '<p>Keine Share-Links vorhanden.</p>');
@ -568,20 +568,18 @@ export class MatrixService extends BaseMatrixService {
private async handleDeleteShare(roomId: string, sender: string, numberStr: string) {
const token = this.requireAuth(sender);
const shares = this.lastSharesList.get(sender);
if (!shares) {
if (!this.sharesMapper.hasList(sender)) {
await this.sendMessage(roomId, '<p>Nutze zuerst <code>!links</code></p>');
return;
}
const index = parseInt(numberStr, 10) - 1;
if (isNaN(index) || index < 0 || index >= shares.length) {
const num = parseInt(numberStr, 10);
const share = isNaN(num) ? null : this.sharesMapper.getByNumber(sender, num);
if (!share) {
await this.sendMessage(roomId, '<p>Ungueltige Nummer.</p>');
return;
}
const share = shares[index];
const result = await this.storageService.deleteShare(token, share.id);
if (result.error) {
@ -589,7 +587,7 @@ export class MatrixService extends BaseMatrixService {
return;
}
this.lastSharesList.delete(sender);
this.sharesMapper.clearList(sender);
await this.sendMessage(roomId, '<p>Share-Link geloescht.</p>');
}
@ -609,8 +607,8 @@ export class MatrixService extends BaseMatrixService {
}
const { files, folders } = result.data!;
this.lastFilesList.set(sender, files);
this.lastFoldersList.set(sender, folders);
this.filesMapper.setList(sender, files);
this.foldersMapper.setList(sender, folders);
if (files.length === 0 && folders.length === 0) {
await this.sendMessage(roomId, `<p>Keine Ergebnisse fuer "${query}"</p>`);
@ -648,8 +646,8 @@ export class MatrixService extends BaseMatrixService {
}
const { files, folders } = result.data!;
this.lastFilesList.set(sender, files);
this.lastFoldersList.set(sender, folders);
this.filesMapper.setList(sender, files);
this.foldersMapper.setList(sender, folders);
if (files.length === 0 && folders.length === 0) {
await this.sendMessage(roomId, '<p>Keine Favoriten vorhanden.</p>');
@ -720,7 +718,7 @@ export class MatrixService extends BaseMatrixService {
}
const items = result.data || [];
this.lastTrashList.set(sender, items);
this.trashMapper.setList(sender, items);
if (items.length === 0) {
await this.sendMessage(roomId, '<p>Papierkorb ist leer.</p>');
@ -741,20 +739,18 @@ export class MatrixService extends BaseMatrixService {
private async handleRestore(roomId: string, sender: string, numberStr: string) {
const token = this.requireAuth(sender);
const items = this.lastTrashList.get(sender);
if (!items) {
if (!this.trashMapper.hasList(sender)) {
await this.sendMessage(roomId, '<p>Nutze zuerst <code>!papierkorb</code></p>');
return;
}
const index = parseInt(numberStr, 10) - 1;
if (isNaN(index) || index < 0 || index >= items.length) {
const num = parseInt(numberStr, 10);
const item = isNaN(num) ? null : this.trashMapper.getByNumber(sender, num);
if (!item) {
await this.sendMessage(roomId, '<p>Ungueltige Nummer.</p>');
return;
}
const item = items[index];
const result = await this.storageService.restoreFromTrash(token, item.id, item.type);
if (result.error) {
@ -762,7 +758,7 @@ export class MatrixService extends BaseMatrixService {
return;
}
this.lastTrashList.delete(sender);
this.trashMapper.clearList(sender);
await this.sendMessage(roomId, `<p><strong>${item.name}</strong> wiederhergestellt.</p>`);
}
@ -775,29 +771,21 @@ export class MatrixService extends BaseMatrixService {
return;
}
this.lastTrashList.delete(sender);
this.trashMapper.clearList(sender);
await this.sendMessage(roomId, '<p>Papierkorb geleert.</p>');
}
// Helper methods
private getFileByNumber(sender: string, numberStr: string): StorageFile | null {
const files = this.lastFilesList.get(sender);
if (!files) return null;
const index = parseInt(numberStr, 10) - 1;
if (isNaN(index) || index < 0 || index >= files.length) return null;
return files[index];
const num = parseInt(numberStr, 10);
if (isNaN(num)) return null;
return this.filesMapper.getByNumber(sender, num);
}
private getFolderByNumber(sender: string, numberStr: string): Folder | null {
const folders = this.lastFoldersList.get(sender);
if (!folders) return null;
const index = parseInt(numberStr, 10) - 1;
if (isNaN(index) || index < 0 || index >= folders.length) return null;
return folders[index];
const num = parseInt(numberStr, 10);
if (isNaN(num)) return null;
return this.foldersMapper.getByNumber(sender, num);
}
private formatSize(bytes: number): string {