import { Mwn, RecentChange } from 'npm:mwn' import process, { title } from "node:process"; import { parseSummary } from "./parse.ts"; import { bucketEvents, writeSections } from "./write.ts"; async function getWorkshopVideos(bot: Mwn): Promise { const response = await bot.request({ action: 'query', list: 'allimages', aiprefix: 'WCS ', // Search prefix ailimit: 'max', // Maximum number of results }); if (response.query === undefined) { throw new Error("Did not receive response to file query") } // Extract and print the file titles return response.query.allimages.map(function (image: { title: string }) { return image.title }); } export interface VideoDescription { teachers: string[] location: string event: string level: string date: string patterns: string notes: string path: string title: string nags: string[] } async function fetchPages(pages: string[], bot: Mwn): Promise { const d = await Promise.allSettled(pages.map(async (v) => parseSummary(await new bot.Page(v).text(), v) )) d.map((v, index) => ({ r: v, source: pages[index] })).filter((v): v is { source: string, r: PromiseRejectedResult } => v.r.status === "rejected").forEach( v => console.warn(`Error parsing ${v.source}: ${v.r.reason}` )) return d.filter(v => v.status === "fulfilled").map(v => v.value) } function sleep(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } async function watchdog(bot: Mwn, onChange: (paths: string[]) => Promise) { let last_change: string | undefined = undefined; let heartbeat_count = 0; const heartbeat_count_max = 60; const heartbeat = () => { heartbeat_count += 1 if (heartbeat_count >= heartbeat_count_max) { heartbeat_count = 0; console.log(new Date(), "Heartbeat") } } while (true) { const { last_change: l, new_file_changes } = await queryChanges(bot, last_change); last_change = l if (new_file_changes.length > 0) { console.log(new_file_changes.length, "changes") await onChange(new_file_changes.map(v => v.title)) } heartbeat() await sleep(30000); } } async function queryChanges(bot: Mwn, last_change: string | undefined): Promise<{ last_change: string | undefined; new_file_changes: RecentChange[]; }> { const d = await bot.request({ action: "query", list: "recentchanges", format: "json", formatversion: 2, rcprop: "title|timestamp|ids|user", rctoponly: 1, rcdir: 'older', rcend: last_change || "2000-01-01T12:00:00Z", }); const changes: RecentChange[] = d.query?.recentchanges; const new_file_changes = changes .filter(v => v.title.startsWith("File:WCS ")) .filter(v => last_change === undefined || new Date(v.timestamp) > new Date(last_change)); return { last_change: changes[0].timestamp, new_file_changes }; } async function main() { const bot = new Mwn({ apiUrl: process.env.URL || 'https://dancing.thasky.one/api.php', username: process.env.BOTNAME, password: process.env.BOTPW, userAgent: 'mwn bot', }); const title = 'Video Descriptions (Automated)' try { await bot.login(); await watchdog(bot, async (paths) => { const relevantFiles = await getWorkshopVideos(bot) const d = await fetchPages(relevantFiles, bot) const t = writeSections(bucketEvents(d)) const response = await bot.save(title, "{{TOC|limit=3}}\n\n" + t, 'Triggered by changes to ' + paths.map(v => "[[" + v + "]]").join(", ")); console.log(response) }) } catch (error) { console.error('Error:', error); } finally { await bot.logout(); } } main();