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

@ -0,0 +1,24 @@
{
"name": "@zitare/content",
"version": "1.0.0",
"description": "Static quote content for Zitare",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs"
}
},
"scripts": {
"build": "tsup",
"type-check": "tsc --noEmit"
},
"devDependencies": {
"tsup": "^8.0.0",
"typescript": "^5.3.0"
}
}

View file

@ -0,0 +1,47 @@
/**
* Quote categories
*/
export const CATEGORIES = [
'motivation',
'weisheit',
'liebe',
'leben',
'erfolg',
'glueck',
'freundschaft',
'mut',
'hoffnung',
'natur',
] as const;
export type Category = (typeof CATEGORIES)[number];
/**
* German labels for categories
*/
export const CATEGORY_LABELS: Record<Category, string> = {
motivation: 'Motivation',
weisheit: 'Weisheit',
liebe: 'Liebe',
leben: 'Leben',
erfolg: 'Erfolg',
glueck: 'Glueck',
freundschaft: 'Freundschaft',
mut: 'Mut',
hoffnung: 'Hoffnung',
natur: 'Natur',
};
/**
* Get label for a category
*/
export function getCategoryLabel(category: Category): string {
return CATEGORY_LABELS[category];
}
/**
* Check if a string is a valid category
*/
export function isValidCategory(value: string): value is Category {
return CATEGORIES.includes(value as Category);
}

View file

@ -0,0 +1,25 @@
// Types
export type { Quote } from './types';
export type { Category } from './categories';
// Data
export { QUOTES, QUOTE_COUNT } from './quotes';
export { CATEGORIES, CATEGORY_LABELS } from './categories';
// Utilities
export {
getRandomQuote,
getDailyQuote,
getQuotesByCategory,
getRandomQuoteByCategory,
searchQuotes,
getQuoteById,
getQuoteByIndex,
getAllCategories,
getCategoryByName,
formatQuote,
formatQuoteWithNumber,
getTotalCount,
} from './utils';
export { getCategoryLabel, isValidCategory } from './categories';

View file

@ -0,0 +1,375 @@
import type { Quote } from './types';
/**
* German inspirational quotes collection
*/
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 endgueltig, Misserfolg ist nicht fatal: Was zaehlt, ist der Mut weiterzumachen.',
author: 'Winston Churchill',
category: 'motivation',
},
{
id: 'mot-3',
text: 'Die Zukunft gehoert denen, die an die Schoenheit ihrer Traeume glauben.',
author: 'Eleanor Roosevelt',
category: 'motivation',
},
{
id: 'mot-4',
text: 'Es ist nie zu spaet, das zu werden, was man haette sein koennen.',
author: 'George Eliot',
category: 'motivation',
},
{
id: 'mot-5',
text: 'Gib jedem Tag die Chance, der schoenste deines Lebens zu werden.',
author: 'Mark Twain',
category: 'motivation',
},
{
id: 'mot-6',
text: 'Der Anfang ist die Haelfte des Ganzen.',
author: 'Aristoteles',
category: 'motivation',
},
{
id: 'mot-7',
text: 'Tue heute etwas, wofuer dein zukuenftiges Ich dir danken wird.',
author: 'Sean Patrick Flanery',
category: 'motivation',
},
{
id: 'mot-8',
text: 'Der beste Zeitpunkt einen Baum zu pflanzen war vor 20 Jahren. Der zweitbeste ist jetzt.',
author: 'Chinesisches Sprichwort',
category: 'motivation',
},
// ============================================
// WEISHEIT
// ============================================
{
id: 'weis-1',
text: 'Der Weg ist das Ziel.',
author: 'Konfuzius',
category: 'weisheit',
},
{
id: 'weis-2',
text: 'Wer kaempft, kann verlieren. Wer nicht kaempft, hat schon verloren.',
author: 'Bertolt Brecht',
category: 'weisheit',
},
{
id: 'weis-3',
text: 'Man sieht nur mit dem Herzen gut. Das Wesentliche ist fuer 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',
},
{
id: 'weis-6',
text: 'Ich weiss, dass ich nichts weiss.',
author: 'Sokrates',
category: 'weisheit',
},
{
id: 'weis-7',
text: 'Die einzige Konstante im Leben ist die Veraenderung.',
author: 'Heraklit',
category: 'weisheit',
},
{
id: 'weis-8',
text: 'Handle nur nach derjenigen Maxime, durch die du zugleich wollen kannst, dass sie ein allgemeines Gesetz werde.',
author: 'Immanuel Kant',
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 Glueck in diesem Leben: zu lieben und geliebt zu werden.',
author: 'George Sand',
category: 'liebe',
},
{
id: 'liebe-4',
text: 'Liebe ist das einzige, was waechst, wenn wir es verschwenden.',
author: 'Ricarda Huch',
category: 'liebe',
},
{
id: 'liebe-5',
text: 'Die groesste Sache der Welt ist es, zu wissen, wie man sich selbst gehoert.',
author: 'Michel de Montaigne',
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, waehrend du damit beschaeftigt bist, andere Plaene zu machen.',
author: 'John Lennon',
category: 'leben',
},
{
id: 'leben-3',
text: 'Das Leben ist zu kurz fuer spaeter.',
author: 'Alexandra Reinwarth',
category: 'leben',
},
{
id: 'leben-4',
text: 'Lebe jeden Tag, als waere es dein letzter.',
author: 'Marcus Aurelius',
category: 'leben',
},
{
id: 'leben-5',
text: 'Das Leben ist kein Problem, das geloest werden muss, sondern eine Wirklichkeit, die erfahren werden will.',
author: 'Soeren Kierkegaard',
category: 'leben',
},
{
id: 'leben-6',
text: 'Wer sein Leben so einrichtet, dass er niemals auf die Nase fallen kann, der kann nur auf dem Bauch kriechen.',
author: 'Heinz Riesenhuber',
category: 'leben',
},
// ============================================
// ERFOLG
// ============================================
{
id: 'erfolg-1',
text: 'Erfolg besteht darin, dass man genau die Faehigkeiten hat, die im Moment gefragt sind.',
author: 'Henry Ford',
category: 'erfolg',
},
{
id: 'erfolg-2',
text: 'Der Preis des Erfolges ist Hingabe, harte Arbeit und unablaessiger 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',
},
{
id: 'erfolg-4',
text: 'Der Erfolg ist nicht das Endergebnis, das Scheitern ist nicht toedlich: Was zaehlt, ist der Mut, weiterzumachen.',
author: 'Winston Churchill',
category: 'erfolg',
},
{
id: 'erfolg-5',
text: 'Erfolg hat drei Buchstaben: TUN.',
author: 'Johann Wolfgang von Goethe',
category: 'erfolg',
},
// ============================================
// GLUECK
// ============================================
{
id: 'glueck-1',
text: 'Glueck ist das Einzige, das sich verdoppelt, wenn man es teilt.',
author: 'Albert Schweitzer',
category: 'glueck',
},
{
id: 'glueck-2',
text: 'Glueck ist kein Ziel, sondern ein Weg.',
author: 'Buddha',
category: 'glueck',
},
{
id: 'glueck-3',
text: 'Nicht die Gluecklichen sind dankbar. Es sind die Dankbaren, die gluecklich sind.',
author: 'Francis Bacon',
category: 'glueck',
},
{
id: 'glueck-4',
text: 'Das Vergleichen ist das Ende des Gluecks und der Anfang der Unzufriedenheit.',
author: 'Soeren Kierkegaard',
category: 'glueck',
},
{
id: 'glueck-5',
text: 'Glueck entsteht oft durch Aufmerksamkeit in kleinen Dingen, Unglueck oft durch Vernachlaessigung kleiner Dinge.',
author: 'Wilhelm Busch',
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 Koerpern.',
author: 'Aristoteles',
category: 'freundschaft',
},
{
id: 'freund-3',
text: 'Ein Freund ist ein Mensch, vor dem man laut denken kann.',
author: 'Ralph Waldo Emerson',
category: 'freundschaft',
},
{
id: 'freund-4',
text: 'Die Freundschaft gehoert zum Notwendigsten in unserem Leben.',
author: 'Aristoteles',
category: 'freundschaft',
},
// ============================================
// MUT
// ============================================
{
id: 'mut-1',
text: 'Mut steht am Anfang des Handelns, Glueck 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',
},
{
id: 'mut-4',
text: 'Mut ist nicht die Abwesenheit von Angst, sondern die Erkenntnis, dass etwas anderes wichtiger ist als Angst.',
author: 'Ambrose Redmoon',
category: 'mut',
},
{
id: 'mut-5',
text: 'Inmitten der Schwierigkeit liegt die Moeglichkeit.',
author: 'Albert Einstein',
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',
},
{
id: 'hoff-3',
text: 'Auch aus Steinen, die einem in den Weg gelegt werden, kann man Schoenes bauen.',
author: 'Johann Wolfgang von Goethe',
category: 'hoffnung',
},
{
id: 'hoff-4',
text: 'Das Licht am Ende des Tunnels ist kein Zug.',
author: 'Deutsches Sprichwort',
category: 'hoffnung',
},
// ============================================
// NATUR
// ============================================
{
id: 'natur-1',
text: 'In der Natur ist nichts isoliert; alles haengt 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',
},
{
id: 'natur-3',
text: 'Die Natur macht keine Spruenge.',
author: 'Carl von Linne',
category: 'natur',
},
{
id: 'natur-4',
text: 'Vergiss nicht, dass die Erde sich freut, deine nackten Fuesse zu fuehlen, und die Winde sich danach sehnen, mit deinem Haar zu spielen.',
author: 'Khalil Gibran',
category: 'natur',
},
];
/**
* Total number of quotes
*/
export const QUOTE_COUNT = QUOTES.length;

View file

@ -0,0 +1,15 @@
import type { Category } from './categories';
/**
* A quote with author and category
*/
export interface Quote {
/** Unique identifier (e.g., 'mot-1', 'weis-2') */
id: string;
/** The quote text in German */
text: string;
/** Author name */
author: string;
/** Category for filtering */
category: Category;
}

View file

@ -0,0 +1,133 @@
import { QUOTES } from './quotes';
import { CATEGORIES, CATEGORY_LABELS, type Category } from './categories';
import type { Quote } from './types';
/**
* Get a random quote
*/
export function getRandomQuote(): Quote {
const index = Math.floor(Math.random() * QUOTES.length);
return QUOTES[index];
}
/**
* Get deterministic daily quote based on date
*/
export function getDailyQuote(date: Date = new Date()): Quote {
const dateStr = date.toISOString().split('T')[0];
const hash = hashString(dateStr);
const index = Math.abs(hash) % QUOTES.length;
return QUOTES[index];
}
/**
* Get quotes by category
*/
export function getQuotesByCategory(category: Category): Quote[] {
return QUOTES.filter((q) => q.category === category);
}
/**
* Get a random quote from a specific category
*/
export function getRandomQuoteByCategory(category: Category): Quote | null {
const quotes = getQuotesByCategory(category);
if (quotes.length === 0) return null;
const index = Math.floor(Math.random() * quotes.length);
return quotes[index];
}
/**
* Search quotes by text or author
*/
export function searchQuotes(searchText: string): Quote[] {
const lowerSearch = searchText.toLowerCase();
return QUOTES.filter(
(q) =>
q.text.toLowerCase().includes(lowerSearch) || q.author.toLowerCase().includes(lowerSearch)
);
}
/**
* Get a quote by ID
*/
export function getQuoteById(id: string): Quote | undefined {
return QUOTES.find((q) => q.id === id);
}
/**
* Get quote by index (1-based)
*/
export function getQuoteByIndex(index: number): Quote | null {
if (index < 1 || index > QUOTES.length) return null;
return QUOTES[index - 1];
}
/**
* Get all categories with counts
*/
export function getAllCategories(): { category: Category; label: string; count: number }[] {
return CATEGORIES.map((category) => ({
category,
label: CATEGORY_LABELS[category],
count: QUOTES.filter((q) => q.category === category).length,
}));
}
/**
* Find category by name (partial match)
*/
export function getCategoryByName(name: string): Category | null {
const lowerName = name.toLowerCase();
// Exact match first
if (CATEGORIES.includes(lowerName as Category)) {
return lowerName as Category;
}
// Partial match
for (const category of CATEGORIES) {
if (
category.startsWith(lowerName) ||
CATEGORY_LABELS[category].toLowerCase().startsWith(lowerName)
) {
return category;
}
}
return null;
}
/**
* Format a quote for display
*/
export function formatQuote(quote: Quote): string {
const categoryLabel = CATEGORY_LABELS[quote.category];
return `"${quote.text}"\n\n— *${quote.author}*\n\n[${categoryLabel}]`;
}
/**
* Format a quote with number
*/
export function formatQuoteWithNumber(quote: Quote, number: number): string {
const categoryLabel = CATEGORY_LABELS[quote.category];
return `**#${number}**\n"${quote.text}"\n\n— *${quote.author}* [${categoryLabel}]`;
}
/**
* Get total quote count
*/
export function getTotalCount(): number {
return QUOTES.length;
}
// Helper function
function hashString(str: string): number {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash = hash & hash;
}
return hash;
}

View file

@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"declaration": true,
"declarationMap": true,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

View file

@ -0,0 +1,9 @@
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm', 'cjs'],
dts: true,
clean: true,
sourcemap: true,
});