feat(zitare): add @zitare/content package for shared quotes

- Create new @zitare/content package with 50 German quotes
- Include 10 categories: motivation, weisheit, liebe, leben, erfolg,
  glueck, freundschaft, mut, hoffnung, natur
- Add utility functions: getRandomQuote, getDailyQuote, searchQuotes,
  getQuotesByCategory, formatQuote, etc.
- Migrate matrix-zitare-bot to use the shared package
- Remove hardcoded quotes from bot configuration
This commit is contained in:
Till-JS 2026-02-13 12:30:41 +01:00
parent 9924eb545a
commit 74c1cfed4f
13 changed files with 1069 additions and 660 deletions

View file

@ -25,6 +25,7 @@
"dependencies": {
"@manacore/bot-services": "workspace:*",
"@manacore/matrix-bot-common": "workspace:*",
"@zitare/content": "workspace:*",
"@nestjs/common": "^10.4.15",
"@nestjs/config": "^3.3.0",
"@nestjs/core": "^10.4.15",

View file

@ -10,7 +10,8 @@ import {
import { QuotesService } from '../quotes/quotes.service';
import { ZitareService } from '../quotes/zitare.service';
import { SessionService, TranscriptionService, CreditService } from '@manacore/bot-services';
import { HELP_MESSAGE, Category } from '../config/configuration';
import { HELP_MESSAGE } from '../config/configuration';
import type { Category } from '@zitare/content';
@Injectable()
export class MatrixService extends BaseMatrixService {

View file

@ -18,10 +18,10 @@ export default () => ({
},
});
export const HELP_MESSAGE = `**Zitare Bot - Tagliche Inspiration**
export const HELP_MESSAGE = `**Zitare Bot - Taegliche Inspiration**
**Zitate:**
- \`!zitat\` - Zufalliges Zitat
- \`!zitat\` - Zufaelliges Zitat
- \`!heute\` - Zitat des Tages
- \`!suche [text]\` - Zitate suchen
- \`!kategorie [name]\` - Zitate nach Kategorie
@ -36,7 +36,7 @@ export const HELP_MESSAGE = `**Zitare Bot - Tagliche Inspiration**
**Listen:** (Login erforderlich)
- \`!listen\` - Alle Listen anzeigen
- \`!liste [name]\` - Neue Liste erstellen
- \`!addliste [nr] [zitat-nr]\` - Zitat zur Liste hinzufugen
- \`!addliste [nr] [zitat-nr]\` - Zitat zur Liste hinzufuegen
**Sonstiges:**
- \`!status\` - Bot-Status
@ -45,250 +45,11 @@ export const HELP_MESSAGE = `**Zitare Bot - Tagliche Inspiration**
**Sprachnotizen:**
Sende eine Sprachnotiz mit Befehlen wie "Zitat", "Motivation" oder einem Suchbegriff.
**Naturliche Sprache:**
- "zitat", "inspiration" -> Zufalliges Zitat
**Natuerliche Sprache:**
- "zitat", "inspiration" -> Zufaelliges Zitat
- "motiviere mich" -> Motivation-Zitat
- "guten morgen" -> Morgenzitat`;
// Quote categories
export const CATEGORIES = [
'motivation',
'weisheit',
'liebe',
'leben',
'erfolg',
'glueck',
'freundschaft',
'mut',
'hoffnung',
'natur',
] as const;
export type Category = (typeof CATEGORIES)[number];
// German inspirational quotes collection
export interface Quote {
id: string;
text: string;
author: string;
category: Category;
}
export const QUOTES: Quote[] = [
// Motivation
{
id: 'mot-1',
text: 'Der einzige Weg, grossartige Arbeit zu leisten, ist zu lieben, was man tut.',
author: 'Steve Jobs',
category: 'motivation',
},
{
id: 'mot-2',
text: 'Erfolg ist nicht endgultig, Misserfolg ist nicht fatal: Was zahlt, ist der Mut weiterzumachen.',
author: 'Winston Churchill',
category: 'motivation',
},
{
id: 'mot-3',
text: 'Die Zukunft gehort denen, die an die Schonheit ihrer Traume glauben.',
author: 'Eleanor Roosevelt',
category: 'motivation',
},
{
id: 'mot-4',
text: 'Es ist nie zu spat, das zu werden, was man hatte sein konnen.',
author: 'George Eliot',
category: 'motivation',
},
{
id: 'mot-5',
text: 'Gib jedem Tag die Chance, der schonste deines Lebens zu werden.',
author: 'Mark Twain',
category: 'motivation',
},
// Weisheit
{
id: 'weis-1',
text: 'Der Weg ist das Ziel.',
author: 'Konfuzius',
category: 'weisheit',
},
{
id: 'weis-2',
text: 'Wer kampft, kann verlieren. Wer nicht kampft, hat schon verloren.',
author: 'Bertolt Brecht',
category: 'weisheit',
},
{
id: 'weis-3',
text: 'Man sieht nur mit dem Herzen gut. Das Wesentliche ist fur die Augen unsichtbar.',
author: 'Antoine de Saint-Exupery',
category: 'weisheit',
},
{
id: 'weis-4',
text: 'Nicht weil es schwer ist, wagen wir es nicht, sondern weil wir es nicht wagen, ist es schwer.',
author: 'Seneca',
category: 'weisheit',
},
{
id: 'weis-5',
text: 'Wissen ist Macht.',
author: 'Francis Bacon',
category: 'weisheit',
},
// Liebe
{
id: 'liebe-1',
text: 'Wo Liebe ist, da ist auch Leben.',
author: 'Mahatma Gandhi',
category: 'liebe',
},
{
id: 'liebe-2',
text: 'Die Liebe allein versteht das Geheimnis, andere zu beschenken und dabei selbst reich zu werden.',
author: 'Clemens Brentano',
category: 'liebe',
},
{
id: 'liebe-3',
text: 'Es gibt nur ein Gluck in diesem Leben: zu lieben und geliebt zu werden.',
author: 'George Sand',
category: 'liebe',
},
// Leben
{
id: 'leben-1',
text: 'Das Leben ist wie Fahrrad fahren. Um die Balance zu halten, musst du in Bewegung bleiben.',
author: 'Albert Einstein',
category: 'leben',
},
{
id: 'leben-2',
text: 'Leben ist das, was passiert, wahrend du damit beschaftigt bist, andere Plane zu machen.',
author: 'John Lennon',
category: 'leben',
},
{
id: 'leben-3',
text: 'Das Leben ist zu kurz fur spater.',
author: 'Alexandra Reinwarth',
category: 'leben',
},
{
id: 'leben-4',
text: 'Lebe jeden Tag, als ware es dein letzter.',
author: 'Marcus Aurelius',
category: 'leben',
},
// Erfolg
{
id: 'erfolg-1',
text: 'Erfolg besteht darin, dass man genau die Fahigkeiten hat, die im Moment gefragt sind.',
author: 'Henry Ford',
category: 'erfolg',
},
{
id: 'erfolg-2',
text: 'Der Preis des Erfolges ist Hingabe, harte Arbeit und unablassiger Einsatz.',
author: 'Frank Lloyd Wright',
category: 'erfolg',
},
{
id: 'erfolg-3',
text: 'Ich habe nicht versagt. Ich habe nur 10.000 Wege gefunden, die nicht funktionieren.',
author: 'Thomas Edison',
category: 'erfolg',
},
// Glueck
{
id: 'glueck-1',
text: 'Gluck ist das Einzige, das sich verdoppelt, wenn man es teilt.',
author: 'Albert Schweitzer',
category: 'glueck',
},
{
id: 'glueck-2',
text: 'Gluck ist kein Ziel, sondern ein Weg.',
author: 'Buddha',
category: 'glueck',
},
{
id: 'glueck-3',
text: 'Nicht die Glucklichen sind dankbar. Es sind die Dankbaren, die glucklich sind.',
author: 'Francis Bacon',
category: 'glueck',
},
// Freundschaft
{
id: 'freund-1',
text: 'Ein wahrer Freund ist jemand, der die Melodie deines Herzens kennt und sie dir vorsingt, wenn du sie vergessen hast.',
author: 'Albert Einstein',
category: 'freundschaft',
},
{
id: 'freund-2',
text: 'Freundschaft ist eine Seele in zwei Korpern.',
author: 'Aristoteles',
category: 'freundschaft',
},
// Mut
{
id: 'mut-1',
text: 'Mut steht am Anfang des Handelns, Gluck am Ende.',
author: 'Demokrit',
category: 'mut',
},
{
id: 'mut-2',
text: 'Wer wagt, gewinnt.',
author: 'Deutsches Sprichwort',
category: 'mut',
},
{
id: 'mut-3',
text: 'Der Mutige hat nicht weniger Angst, er handelt trotzdem.',
author: 'Mark Twain',
category: 'mut',
},
// Hoffnung
{
id: 'hoff-1',
text: 'Hoffnung ist ein Vogel, der singt, wenn die Nacht noch dunkel ist.',
author: 'Rabindranath Tagore',
category: 'hoffnung',
},
{
id: 'hoff-2',
text: 'Nach jedem Sturm scheint auch wieder die Sonne.',
author: 'Deutsches Sprichwort',
category: 'hoffnung',
},
// Natur
{
id: 'natur-1',
text: 'In der Natur ist nichts isoliert; alles hangt mit allem zusammen.',
author: 'Johann Wolfgang von Goethe',
category: 'natur',
},
{
id: 'natur-2',
text: 'Schau tief in die Natur, und dann wirst du alles besser verstehen.',
author: 'Albert Einstein',
category: 'natur',
},
];
// Category labels in German
export const CATEGORY_LABELS: Record<Category, string> = {
motivation: 'Motivation',
weisheit: 'Weisheit',
liebe: 'Liebe',
leben: 'Leben',
erfolg: 'Erfolg',
glueck: 'Gluck',
freundschaft: 'Freundschaft',
mut: 'Mut',
hoffnung: 'Hoffnung',
natur: 'Natur',
};
// Re-export types and utilities from @zitare/content
export type { Quote, Category } from '@zitare/content';
export { CATEGORIES, CATEGORY_LABELS } from '@zitare/content';

View file

@ -1,5 +1,23 @@
import { Injectable, Logger } from '@nestjs/common';
import { QUOTES, Quote, Category, CATEGORIES, CATEGORY_LABELS } from '../config/configuration';
import {
type Quote,
type Category,
QUOTES,
CATEGORIES,
CATEGORY_LABELS,
getRandomQuote,
getDailyQuote,
getQuotesByCategory,
getRandomQuoteByCategory,
searchQuotes,
getQuoteById,
getQuoteByIndex,
getAllCategories,
getCategoryByName,
formatQuote,
formatQuoteWithNumber,
getTotalCount,
} from '@zitare/content';
@Injectable()
export class QuotesService {
@ -7,8 +25,7 @@ export class QuotesService {
private dailyQuoteCache: { date: string; quote: Quote } | null = null;
getRandomQuote(): Quote {
const index = Math.floor(Math.random() * QUOTES.length);
return QUOTES[index];
return getRandomQuote();
}
getDailyQuote(): Quote {
@ -19,95 +36,50 @@ export class QuotesService {
return this.dailyQuoteCache.quote;
}
// Generate deterministic quote based on date
const dateHash = this.hashDate(today);
const index = dateHash % QUOTES.length;
const quote = QUOTES[index];
const quote = getDailyQuote();
this.dailyQuoteCache = { date: today, quote };
this.logger.log(`Daily quote for ${today}: "${quote.text.substring(0, 30)}..."`);
return quote;
}
private hashDate(dateStr: string): number {
let hash = 0;
for (let i = 0; i < dateStr.length; i++) {
const char = dateStr.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash = hash & hash; // Convert to 32-bit integer
}
return Math.abs(hash);
}
getQuotesByCategory(category: Category): Quote[] {
return QUOTES.filter((q) => q.category === category);
return getQuotesByCategory(category);
}
getRandomQuoteByCategory(category: Category): Quote | null {
const quotes = this.getQuotesByCategory(category);
if (quotes.length === 0) return null;
const index = Math.floor(Math.random() * quotes.length);
return quotes[index];
return getRandomQuoteByCategory(category);
}
searchQuotes(searchText: string): Quote[] {
const lowerSearch = searchText.toLowerCase();
return QUOTES.filter(
(q) =>
q.text.toLowerCase().includes(lowerSearch) || q.author.toLowerCase().includes(lowerSearch)
);
return searchQuotes(searchText);
}
getQuoteById(id: string): Quote | undefined {
return QUOTES.find((q) => q.id === id);
return getQuoteById(id);
}
getQuoteByIndex(index: number): Quote | null {
if (index < 1 || index > QUOTES.length) return null;
return QUOTES[index - 1];
return getQuoteByIndex(index);
}
getAllCategories(): { category: Category; label: string; count: number }[] {
return CATEGORIES.map((category) => ({
category,
label: CATEGORY_LABELS[category],
count: QUOTES.filter((q) => q.category === category).length,
}));
return getAllCategories();
}
getCategoryByName(name: string): Category | null {
const lowerName = name.toLowerCase();
// Try exact match first
if (CATEGORIES.includes(lowerName as Category)) {
return lowerName as Category;
}
// Try partial match
for (const category of CATEGORIES) {
if (
category.startsWith(lowerName) ||
CATEGORY_LABELS[category].toLowerCase().startsWith(lowerName)
) {
return category;
}
}
return null;
return getCategoryByName(name);
}
getTotalCount(): number {
return QUOTES.length;
return getTotalCount();
}
formatQuote(quote: Quote): string {
const categoryLabel = CATEGORY_LABELS[quote.category];
return `"${quote.text}"\n\n— *${quote.author}*\n\n[${categoryLabel}]`;
return formatQuote(quote);
}
formatQuoteWithNumber(quote: Quote, number: number): string {
const categoryLabel = CATEGORY_LABELS[quote.category];
return `**#${number}**\n"${quote.text}"\n\n— *${quote.author}* [${categoryLabel}]`;
return formatQuoteWithNumber(quote, number);
}
}