/** * Liest Thread-Listen eines WoltLab-Forums (Burning Board). * * Gelesen wird ausschließlich die Übersichtsseite: sie enthält neben Titel und * Thread-ID auch den Ersteller, sodass die Threads nicht einzeln geöffnet * werden müssen. Das hält einen Lauf bei drei Anfragen statt einer je Thread. * * Wichtig: Eine Boardseite enthält neben der Thread-Liste auch Unterforen und * globale Kästen ("letzte Beiträge"). Beides trägt ebenfalls Thread-Links, ist * aber kein Thread dieses Boards - deshalb wird strikt entlang der * tabularListRow-Einträge gelesen und nicht über alle Links im Dokument. */ export interface ForumThread { threadId: number; title: string; url: string; authorName: string | null; /// Stabiler als der Name: Forumsnamen ändern sich, die ID nicht. authorForumId: number | null; /// Sein Profil, so wie das Forum selbst darauf verweist. Übernommen statt /// zusammengebaut: die Profile liegen nicht unter dem Forums-Pfad, sondern /// eine Ebene darüber, und das lässt sich nicht verlässlich erraten. authorProfileUrl: string | 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[]; /// Wann der Antrag gestellt wurde, laut Forum. postedAt: Date | null; } /// 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 = /