Ideen bekommen Bereiche und einen Thread zum Reden
Nach dem Vorbild von windrose.support: Bereiche zum Einsortieren, Sortierung nach Top / Bewegung / Neu, und eine Zahl daneben, wie viel darueber geredet wird. Der Unterschied zu so einem Dienst ist, wo geredet wird. Zu jedem Wunsch macht der Bot jetzt einen Thread unter dem Post auf. Der Kommentar-Zaehler auf der Roadmap zaehlt die Beitraege darin und verlinkt hinein — es gibt also einen Zaehler wie bei einem Ideen-Board, aber ohne ein zweites Kommentarsystem neben Discord zu stellen. Genau das hatte ich beim letzten Mal als das Falsche bezeichnet, und dabei bleibt es. Eingereicht wird jetzt auch ueber einen Aufruf-Post: /wunsch-setup postet ihn, und mit Bereichen wird daraus ein Auswahlmenue — erst wohin, dann das Formular. Ohne Bereiche bleibt es beim einen Knopf. Derselbe Aufbau wie bei den Ticket-Anliegen, bis hin zur Emoji-Pruefung, die ich mir dort schon geschrieben hatte. Das Anlegen selbst ist nach bot/wishes.js gewandert. /wunsch, der Knopf und die Webseite gehen jetzt denselben Weg — vorher hatte die Webseite ihr eigenes Embed zusammengebaut, das dem aus dem Befehl nur aehnlich sah und keinen Thread bekam. "Bewegung" sind die Stimmen der letzten sieben Tage. Die Rangliste allein zementiert alte Wuensche: was einmal oben steht, bleibt oben, egal ob noch jemand hinschaut. Sortiert wird auf dem Server, weil die Zeitstempel der Stimmen im Browser gar nicht ankommen. Bewusst nicht uebernommen: Gegenstimmen. Windrose hat sie, aber bei einer Community dieser Groesse laden sie zum Nachtreten ein und schrecken vom Einreichen ab — und eine Zahl, die aus zwei Richtungen kommt, sagt am Ende weniger als eine, die nur zaehlt, wer etwas will. Geprueft: Migration erneut durchgespielt (das Schema hat drei Spalten dazubekommen), Routen, SQL gegen das Schema, und im Browser alle drei Filterreihen einzeln und kombiniert, der Sortierwechsel, die Thread-Links und die Bereichs-Pflege im Panel. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -294,24 +294,80 @@ db.exec(`
|
||||
}
|
||||
}
|
||||
|
||||
// Bereiche, in die ein Wunsch faellt — „Fahrzeuge", „Wirtschaft", „Technik".
|
||||
// Ohne Eintrag bleibt alles wie bisher: eine Liste ohne Schubladen.
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS wish_categories (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
emoji TEXT NOT NULL DEFAULT '',
|
||||
sort INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
`);
|
||||
{
|
||||
const spalten = db.prepare('PRAGMA table_info(wishes)').all().map((c) => c.name);
|
||||
if (!spalten.includes('category_id')) db.exec('ALTER TABLE wishes ADD COLUMN category_id INTEGER');
|
||||
// Der Thread unter dem Wunsch-Post ist der Diskussionsort. Die Zahl der
|
||||
// Beitraege darin zaehlen wir mit, statt sie fuer jede Seitenansicht bei
|
||||
// Discord zu erfragen — 50 Threads je Aufruf waere nicht zu bezahlen.
|
||||
if (!spalten.includes('thread_id')) db.exec('ALTER TABLE wishes ADD COLUMN thread_id TEXT');
|
||||
if (!spalten.includes('kommentare')) {
|
||||
db.exec('ALTER TABLE wishes ADD COLUMN kommentare INTEGER NOT NULL DEFAULT 0');
|
||||
}
|
||||
}
|
||||
const listWishCatsStmt = db.prepare('SELECT * FROM wish_categories ORDER BY sort, id');
|
||||
const getWishCatStmt = db.prepare('SELECT * FROM wish_categories WHERE id = ?');
|
||||
const insertWishCatStmt = db.prepare(
|
||||
'INSERT INTO wish_categories (name, emoji, sort) VALUES (@name, @emoji, @sort)'
|
||||
);
|
||||
const updateWishCatStmt = db.prepare(
|
||||
'UPDATE wish_categories SET name = @name, emoji = @emoji, sort = @sort WHERE id = @id'
|
||||
);
|
||||
const deleteWishCatStmt = db.prepare('DELETE FROM wish_categories WHERE id = ?');
|
||||
// Wuensche im geloeschten Bereich bleiben, sie stehen dann nur ohne Schild da
|
||||
const orphanWishesStmt = db.prepare('UPDATE wishes SET category_id = NULL WHERE category_id = ?');
|
||||
export const listWishCategories = () => listWishCatsStmt.all();
|
||||
export const getWishCategory = (id) => getWishCatStmt.get(id) ?? null;
|
||||
export const createWishCategory = (c) => insertWishCatStmt.run(c).lastInsertRowid;
|
||||
export const updateWishCategory = (c) => updateWishCatStmt.run(c);
|
||||
export const deleteWishCategory = db.transaction((id) => {
|
||||
orphanWishesStmt.run(id);
|
||||
return deleteWishCatStmt.run(id).changes > 0;
|
||||
});
|
||||
|
||||
/** Zuspruch = eigene Stimmen + nicht mehr zuordenbarer Sockel aus der Altzeit */
|
||||
const WUNSCH_FELDER = `
|
||||
w.id, w.message_id, w.idea, w.author, w.author_id, w.status, w.status_grund,
|
||||
w.status_at, w.created_at,
|
||||
w.bonus + (SELECT COUNT(*) FROM wish_votes v WHERE v.wish_id = w.id) AS score
|
||||
w.id, w.message_id, w.thread_id, w.idea, w.author, w.author_id, w.status,
|
||||
w.status_grund, w.status_at, w.created_at, w.kommentare, w.category_id,
|
||||
(SELECT k.name FROM wish_categories k WHERE k.id = w.category_id) AS kategorie,
|
||||
(SELECT k.emoji FROM wish_categories k WHERE k.id = w.category_id) AS kategorie_emoji,
|
||||
w.bonus + (SELECT COUNT(*) FROM wish_votes v WHERE v.wish_id = w.id) AS score,
|
||||
-- Bewegung: Stimmen der letzten sieben Tage. Ein alter Wunsch mit vielen
|
||||
-- Stimmen steht sonst fuer immer oben, egal ob noch jemand hinschaut.
|
||||
(SELECT COUNT(*) FROM wish_votes v
|
||||
WHERE v.wish_id = w.id AND v.created_at >= datetime('now', '-7 days')) AS bewegung
|
||||
`;
|
||||
const insertWish = db.prepare(
|
||||
'INSERT INTO wishes (message_id, author, author_id, idea) VALUES (?, ?, ?, ?)'
|
||||
'INSERT INTO wishes (message_id, author, author_id, idea, category_id) VALUES (?, ?, ?, ?, ?)'
|
||||
);
|
||||
const setWishThreadStmt = db.prepare('UPDATE wishes SET thread_id = ? WHERE id = ?');
|
||||
const bumpKommentareStmt = db.prepare(
|
||||
'UPDATE wishes SET kommentare = kommentare + 1 WHERE thread_id = ?'
|
||||
);
|
||||
const setWishCategoryStmt = db.prepare('UPDATE wishes SET category_id = ? WHERE id = ?');
|
||||
const setWishMessageStmt = db.prepare('UPDATE wishes SET message_id = ? WHERE id = ?');
|
||||
const wishByMessageStmt = db.prepare('SELECT * FROM wishes WHERE message_id = ?');
|
||||
const wishByIdStmt = db.prepare(`SELECT ${WUNSCH_FELDER} FROM wishes w WHERE w.id = ?`);
|
||||
// Zusammengeführte Doppler tauchen nirgends mehr auf — ihre Stimmen stehen beim Original
|
||||
const topWishesStmt = db.prepare(`
|
||||
SELECT ${WUNSCH_FELDER} FROM wishes w
|
||||
WHERE w.merged_into IS NULL
|
||||
ORDER BY score DESC, w.created_at DESC LIMIT ?
|
||||
`);
|
||||
const SORTIERUNG = {
|
||||
top: 'score DESC, w.created_at DESC',
|
||||
bewegung: 'bewegung DESC, score DESC',
|
||||
neu: 'w.created_at DESC',
|
||||
};
|
||||
const topWishesStmts = Object.fromEntries(Object.entries(SORTIERUNG).map(([k, ord]) => [
|
||||
k,
|
||||
db.prepare(`SELECT ${WUNSCH_FELDER} FROM wishes w WHERE w.merged_into IS NULL ORDER BY ${ord} LIMIT ?`),
|
||||
]));
|
||||
const searchWishesStmt = db.prepare(`
|
||||
SELECT ${WUNSCH_FELDER} FROM wishes w
|
||||
WHERE w.merged_into IS NULL AND w.idea LIKE ?
|
||||
@@ -327,12 +383,17 @@ const moveVotesStmt = db.prepare(
|
||||
);
|
||||
const dropVotesStmt = db.prepare('DELETE FROM wish_votes WHERE wish_id = ?');
|
||||
|
||||
export const saveWish = (messageId, author, idea, authorId = null) =>
|
||||
insertWish.run(messageId, author, authorId, idea).lastInsertRowid;
|
||||
export const saveWish = (messageId, author, idea, authorId = null, categoryId = null) =>
|
||||
insertWish.run(messageId, author, authorId, idea, categoryId ?? null).lastInsertRowid;
|
||||
export const setWishThread = (id, threadId) => setWishThreadStmt.run(threadId, id);
|
||||
export const setWishCategory = (id, categoryId) => setWishCategoryStmt.run(categoryId ?? null, id);
|
||||
/** @returns {boolean} true, wenn der Thread zu einem Wunsch gehoerte */
|
||||
export const bumpWishKommentare = (threadId) => bumpKommentareStmt.run(threadId).changes > 0;
|
||||
export const setWishMessage = (id, messageId) => setWishMessageStmt.run(messageId, id);
|
||||
export const wishByMessage = (messageId) => wishByMessageStmt.get(messageId) ?? null;
|
||||
export const getWish = (id) => wishByIdStmt.get(id) ?? null;
|
||||
export const topWishes = (limit = 20) => topWishesStmt.all(limit);
|
||||
export const topWishes = (limit = 20, sortierung = 'top') =>
|
||||
(topWishesStmts[sortierung] ?? topWishesStmts.top).all(limit);
|
||||
export const searchWishes = (text, limit = 8) => searchWishesStmt.all(`%${text}%`, limit);
|
||||
export const deleteWish = (id) => {
|
||||
dropVotesStmt.run(id);
|
||||
|
||||
Reference in New Issue
Block a user