Compare commits
2 Commits
88f774ae76
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b27a6740a | ||
|
|
65f1a984cf |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"tasks": {
|
"tasks": {
|
||||||
"dev": "deno run --watch main.ts"
|
"dev": "deno run --watch src/main.ts"
|
||||||
},
|
},
|
||||||
"imports": {
|
"imports": {
|
||||||
"@std/assert": "jsr:@std/assert@1",
|
"@std/assert": "jsr:@std/assert@1",
|
||||||
|
|||||||
2
run.sh
2
run.sh
@@ -1 +1 @@
|
|||||||
deno run --env-file=.env --allow-net=dancing.thasky.one:443 -E main.ts
|
deno run --env-file=.env --allow-net=dancing.thasky.one:443 -E src/main.ts
|
||||||
|
|||||||
30
src/event.ts
Normal file
30
src/event.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { VideoDescription } from "./main.ts";
|
||||||
|
|
||||||
|
export function eventTitle(token: VideoDescription) {
|
||||||
|
const event_name = token.event === token.location ? token.event : token.event + " " + token.location;
|
||||||
|
|
||||||
|
// FIXME: This will break with videos in 75 years
|
||||||
|
const event_contains_year = event_name.match(/\d{2}|20\d{2}/) !== null;
|
||||||
|
const event_date = event_contains_year ? "" : new Date(token.date).getFullYear().toString();
|
||||||
|
const event_title = `${event_name} ${event_date}`.trim();
|
||||||
|
return { event_name, event_date, event_title };
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param videos
|
||||||
|
* @returns Bucket of videos for of each event, grouped by `name`, `location` and `year`
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function bucketEvents(videos: VideoDescription[]): VideoDescription[][] {
|
||||||
|
const buckets = Object.groupBy(videos, (video) => {
|
||||||
|
return `${video.event}${video.location}${new Date(video.date).getFullYear()}`;
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
const sortedBuckets = Object.values(buckets)
|
||||||
|
.filter(v => v !== undefined)
|
||||||
|
.map(b => b.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()));
|
||||||
|
|
||||||
|
return sortedBuckets
|
||||||
|
.sort((a, b) => new Date(a[0].date).getTime() - new Date(b[0].date).getTime());
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
import { Mwn, RecentChange } from 'npm:mwn'
|
import { Mwn, RecentChange } from 'npm:mwn'
|
||||||
import process from "node:process";
|
import process from "node:process";
|
||||||
import { parseSummary } from "./parse.ts";
|
import { parseSummary } from "./parse.ts";
|
||||||
import { bucketEvents, writeSections } from "./write.ts";
|
import { writeSections } from "./write_descriptions.ts";
|
||||||
|
import { bucketEvents } from "./event.ts";
|
||||||
|
import { writeGallery } from "./write_gallery.ts";
|
||||||
|
|
||||||
async function getWorkshopVideos(bot: Mwn): Promise<string[]> {
|
async function getWorkshopVideos(bot: Mwn): Promise<string[]> {
|
||||||
const response = await bot.request({
|
const response = await bot.request({
|
||||||
@@ -104,6 +106,30 @@ async function queryChanges(bot: Mwn, last_change: string | undefined): Promise<
|
|||||||
return { last_change: changes[0].timestamp, new_file_changes };
|
return { last_change: changes[0].timestamp, new_file_changes };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function changeDescriptionPage(bucketedEvents: VideoDescription[][], paths: string[], parseErrors: string[], bot: Mwn, title: string) {
|
||||||
|
const t = writeSections(bucketedEvents);
|
||||||
|
|
||||||
|
const trigger_summary = 'Triggered by changes to ' + paths.map(v => `[[${v}]]`).join(", ");
|
||||||
|
const error_summary = parseErrors.join("\n");
|
||||||
|
const summary = [trigger_summary, error_summary].filter(v => v !== undefined && v.length > 0).join("\n");
|
||||||
|
|
||||||
|
const response = await bot.save(title, "{{TOC|limit=3}}\n\n" + t, summary);
|
||||||
|
console.log(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function changeGalleryPage(bucketedEvents: VideoDescription[][], paths: string[], parseErrors: string[], bot: Mwn, title: string) {
|
||||||
|
const t = writeGallery(bucketedEvents);
|
||||||
|
|
||||||
|
const trigger_summary = 'Triggered by changes to ' + paths.map(v => `[[${v}]]`).join(", ");
|
||||||
|
const error_summary = parseErrors.join("\n");
|
||||||
|
const summary = [trigger_summary, error_summary].filter(v => v !== undefined && v.length > 0).join("\n");
|
||||||
|
|
||||||
|
const response = await bot.save(title, "\n" + t, summary);
|
||||||
|
console.log(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const bot = new Mwn({
|
const bot = new Mwn({
|
||||||
apiUrl: process.env.URL || 'https://dancing.thasky.one/api.php',
|
apiUrl: process.env.URL || 'https://dancing.thasky.one/api.php',
|
||||||
@@ -112,21 +138,17 @@ async function main() {
|
|||||||
userAgent: 'mwn bot',
|
userAgent: 'mwn bot',
|
||||||
});
|
});
|
||||||
|
|
||||||
const title = 'Video Descriptions (Automated)'
|
const description_title = 'Video Descriptions (Automated)'
|
||||||
|
const gallery_title = 'West Coast Swing/Video Gallery (Automated)'
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await bot.login();
|
await bot.login();
|
||||||
await watchdog(bot, async (paths) => {
|
await watchdog(bot, async (paths) => {
|
||||||
const relevantFiles = await getWorkshopVideos(bot)
|
const relevantFiles = await getWorkshopVideos(bot)
|
||||||
const { pages: d, errors: parseErrors } = await fetchPages(relevantFiles, bot)
|
const { pages: d, errors: parseErrors } = await fetchPages(relevantFiles, bot)
|
||||||
const t = writeSections(bucketEvents(d))
|
const bucketedEvents = bucketEvents(d)
|
||||||
|
await changeDescriptionPage(bucketedEvents, paths, parseErrors, bot, description_title);
|
||||||
const trigger_summary = 'Triggered by changes to ' + paths.map(v => `[[${v}]]`).join(", ")
|
await changeGalleryPage(bucketedEvents, paths, parseErrors, bot, gallery_title);
|
||||||
const error_summary = parseErrors.join("\n")
|
|
||||||
const summary = [trigger_summary, error_summary].filter(v => v !== undefined && v.length > 0).join("\n")
|
|
||||||
|
|
||||||
const response = await bot.save(title, "{{TOC|limit=3}}\n\n" + t, summary);
|
|
||||||
console.log(response)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
41
src/write_descriptions.ts
Normal file
41
src/write_descriptions.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { eventTitle } from "./event.ts";
|
||||||
|
import { VideoDescription } from "./main.ts";
|
||||||
|
import { camelToTitleCase } from "./util_string.ts";
|
||||||
|
|
||||||
|
|
||||||
|
export function singleVideoDescription(video: VideoDescription): string {
|
||||||
|
const teachersList = video.teachers.map(v => "[[" + v + "]]").join(" & ");
|
||||||
|
|
||||||
|
const nagElement = video.nags.length > 0 ? `<span title="${video.nags.join("
")}">🔴</span>` : "";
|
||||||
|
return `=== ${camelToTitleCase(video.title)} ===
|
||||||
|
Date: {{#time: Y-m-d (D) | ${video.date}}} ${nagElement}<br>
|
||||||
|
Teachers: ${teachersList}<br>
|
||||||
|
Level: ${video.level}
|
||||||
|
|
||||||
|
[[${video.path}|left|400px|thumb|${video.title}]]
|
||||||
|
|
||||||
|
<div style="float:left">
|
||||||
|
==== Shown Patterns ====
|
||||||
|
${video.patterns}
|
||||||
|
|
||||||
|
==== Notes ====
|
||||||
|
${video.notes}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br clear=all>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function writeSections(events: VideoDescription[][]): string {
|
||||||
|
return events.map(v => {
|
||||||
|
const token = v[0]
|
||||||
|
const { event_title } = eventTitle(token);
|
||||||
|
|
||||||
|
let r = `== ${event_title} ==\n`
|
||||||
|
r += `${v.length} Video${v.length <= 1 ? "" : "s"}\n`
|
||||||
|
r += v.map(video => singleVideoDescription(video)).join("\n\n")
|
||||||
|
return r
|
||||||
|
}).join("\n\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
19
src/write_gallery.ts
Normal file
19
src/write_gallery.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { eventTitle } from "./event.ts";
|
||||||
|
import { VideoDescription } from "./main.ts";
|
||||||
|
|
||||||
|
|
||||||
|
export function writeGallery(events: VideoDescription[][]): string {
|
||||||
|
return events.map(v => {
|
||||||
|
const token = v[0]
|
||||||
|
const { event_title } = eventTitle(token);
|
||||||
|
|
||||||
|
let r = `=== ${event_title} ===\n`
|
||||||
|
r += `<gallery>\n`
|
||||||
|
r += v.map(video => `${video.path}|[[:${video.path}|${video.title}]]`).join("\n")
|
||||||
|
r += `</gallery>`
|
||||||
|
return r
|
||||||
|
}).join("\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
63
write.ts
63
write.ts
@@ -1,63 +0,0 @@
|
|||||||
import { VideoDescription } from "./main.ts";
|
|
||||||
import { camelToTitleCase } from "./util_string.ts";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function singleVideoDescription(video: VideoDescription): string {
|
|
||||||
const teachersList = video.teachers.map(v => "[[" + v + "]]").join(" & ");
|
|
||||||
|
|
||||||
const nagElement = video.nags.length > 0 ? `<span title="${video.nags.join("
")}">🔴</span>` : "";
|
|
||||||
return `=== ${camelToTitleCase(video.title)} ===
|
|
||||||
Date: {{#time: Y-m-d (D) | ${video.date}}} ${nagElement}<br>
|
|
||||||
Teachers: ${teachersList}<br>
|
|
||||||
Level: ${video.level}
|
|
||||||
|
|
||||||
[[${video.path}|left|400px|thumb|${video.title}]]
|
|
||||||
|
|
||||||
<div style="float:left">
|
|
||||||
==== Shown Patterns ====
|
|
||||||
${video.patterns}
|
|
||||||
|
|
||||||
==== Notes ====
|
|
||||||
${video.notes}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br clear=all>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function writeSections(events: VideoDescription[][]): string {
|
|
||||||
return events.map(v => {
|
|
||||||
const token = v[0]
|
|
||||||
const event_name = token.event === token.location ? token.event : token.event + " " + token.location
|
|
||||||
|
|
||||||
// FIXME: This will break with videos in 75 years
|
|
||||||
const event_contains_year = event_name.match(/\d{2}|20\d{2}/) !== null
|
|
||||||
const event_date = event_contains_year ? "" : new Date(token.date).getFullYear().toString()
|
|
||||||
|
|
||||||
let r = `== ${event_name} ${event_date} ==\n`
|
|
||||||
r += `${v.length} Video${v.length <= 1 ? "" : "s"}\n`
|
|
||||||
r += v.map(video => singleVideoDescription(video)).join("\n\n")
|
|
||||||
return r
|
|
||||||
}).join("\n\n\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param videos
|
|
||||||
* @returns Bucket of videos for of each event, grouped by `name`, `location` and `year`
|
|
||||||
*/
|
|
||||||
export function bucketEvents(videos: VideoDescription[]): VideoDescription[][] {
|
|
||||||
const buckets = Object.groupBy(videos, (video) => {
|
|
||||||
return `${video.event}${video.location}${new Date(video.date).getFullYear()}`;
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
const sortedBuckets = Object.values(buckets)
|
|
||||||
.filter(v => v !== undefined)
|
|
||||||
.map(b =>
|
|
||||||
b.sort((a, b) =>
|
|
||||||
new Date(a.date).getTime() - new Date(b.date).getTime()))
|
|
||||||
|
|
||||||
return sortedBuckets
|
|
||||||
.sort((a, b) => new Date(a[0].date).getTime() - new Date(b[0].date).getTime())
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user