Antraege eingrenzen, Reaktivierung und Bestaetigungen
Sechs Ruecklaeufer aus dem ersten Praxistest: Das Archiv reicht ueber 25 Seiten und enthaelt vieles, was kein CC-Antrag ist. Aufgenommen werden jetzt nur Threads von Spielern, die im CC-Manager mit passendem Forum-Namen erfasst sind. Angepinnte Ankuendigungen tragen die Labels "Wichtig" oder "Information" und fallen darueber raus. Der Titel wird bewusst nirgends ausgewertet - weder zum Erkennen noch zum Aussortieren. Antraege heissen mal "CC Antrag | Name", mal "The Duke Rang Update"; daran ist nichts abzulesen. Eine Reaktivierung laeuft ebenfalls ueber einen Forums-Antrag und fehlte als Entscheidung. Sie ist jetzt waehlbar und kostet nichts, solange der CC nicht redesignt wurde - die bestehende Regel entscheidet das, nicht der Dialog. Der Titel in der Liste verlinkt jetzt auf den Thread im Forum. Die Labels stehen als Kontext daneben, ohne die Entscheidung vorwegzunehmen. Wer einen Antrag von Hand eintraegt, kann beim Anlegen oder Redesignen den Thread-Link angeben. Der Antrag gilt damit als erledigt und taucht nicht erneut als offen auf. Manuelle Guthaben-Buchungen fragen vorher nach: bisher buchte ein einzelner Klick sofort, ohne zu sagen, was genau passiert. Der Dialog nennt die Wirkung und nimmt eine Notiz entgegen.
This commit is contained in:
@@ -20,6 +20,16 @@ export interface ForumThread {
|
||||
authorForumId: number | null;
|
||||
/// WoltLab lässt beim Verschieben optional einen Verweis am alten Ort zurück.
|
||||
isMovedStub: boolean;
|
||||
/// Labels des Threads, etwa "Wichtig" oder "SCP-RP".
|
||||
labels: string[];
|
||||
}
|
||||
|
||||
/// Angepinnte Ankündigungen liegen mit in den CC-Boards - Changelogs,
|
||||
/// Regelwerke, Bewerbungsphasen. Sie tragen diese Labels und sind keine Anträge.
|
||||
export const IGNORED_LABELS = ['wichtig', 'information'];
|
||||
|
||||
export function hasIgnoredLabel(thread: ForumThread): boolean {
|
||||
return thread.labels.some((label) => IGNORED_LABELS.includes(label.toLowerCase()));
|
||||
}
|
||||
|
||||
const ROW_SPLIT = /<li class="tabularListRow">/;
|
||||
@@ -32,6 +42,9 @@ const USER_LINK =
|
||||
// Ein zurückgelassener Verweis ist kein eigener Thread.
|
||||
const MOVED_MARKER = /wbbTopicLinkMoved|class="[^"]*movedTopic|>\s*Verschoben\s*</i;
|
||||
|
||||
// Labels hängen als eigene Links über dem Titel, nicht als span.
|
||||
const LABEL = /<a[^>]*class="[^"]*\bbadge label\b[^"]*"[^>]*>([^<]{1,40})<\/a>/g;
|
||||
|
||||
function decode(value: string): string {
|
||||
return value
|
||||
.replace(/"/g, '"')
|
||||
@@ -59,6 +72,9 @@ export function parseBoard(html: string): ForumThread[] {
|
||||
|
||||
const user = USER_LINK.exec(block);
|
||||
|
||||
LABEL.lastIndex = 0;
|
||||
const labels = [...block.matchAll(LABEL)].map((m) => decode(m[1] ?? '')).filter(Boolean);
|
||||
|
||||
threads.push({
|
||||
threadId,
|
||||
title: decode(topic[3] ?? ''),
|
||||
@@ -66,6 +82,7 @@ export function parseBoard(html: string): ForumThread[] {
|
||||
authorName: user ? decode(user[2] ?? '') || null : null,
|
||||
authorForumId: user ? Number(user[1]) : null,
|
||||
isMovedStub: MOVED_MARKER.test(block),
|
||||
labels,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -77,3 +94,18 @@ export function parsePageCount(html: string): number {
|
||||
const numbers = [...html.matchAll(/pageNo=(\d+)/g)].map((m) => Number(m[1]));
|
||||
return numbers.length > 0 ? Math.max(1, ...numbers) : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zieht die Thread-ID aus einer Forums-URL. Angenommen werden der volle Link,
|
||||
* der Pfad oder die blanke Nummer - beim Eintippen von Hand landet mal das
|
||||
* eine, mal das andere im Feld.
|
||||
*/
|
||||
export function parseThreadId(input: string): number | null {
|
||||
const value = input.trim();
|
||||
if (value.length === 0) return null;
|
||||
|
||||
const fromUrl = /thread\/(\d+)/.exec(value);
|
||||
if (fromUrl) return Number(fromUrl[1]);
|
||||
|
||||
return /^\d+$/.test(value) ? Number(value) : null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user