2026-05-14 20:06:15 +02:00
|
|
|
import { join } from "node:path";
|
|
|
|
|
import levenshtein from "fast-levenshtein";
|
|
|
|
|
import { respond } from "../audio/AudioService.js";
|
|
|
|
|
|
|
|
|
|
const isStringInt = (s: string): boolean => /^-?\d+$/.test(s);
|
|
|
|
|
|
|
|
|
|
import type { BotStateRow, WBWStoryRow } from "../db/schema.js";
|
|
|
|
|
import type { Module } from "./types.js";
|
|
|
|
|
|
|
|
|
|
export const wordbyword: Module = ({ audio, commands, db, t, rootDir }) => {
|
|
|
|
|
const sysmsg = join(rootDir, "sysmsg.wav");
|
|
|
|
|
let currentWBW = "";
|
|
|
|
|
|
|
|
|
|
commands.register("wbw", async (args, message) => {
|
|
|
|
|
if (args.length === 1) {
|
|
|
|
|
return respond(
|
|
|
|
|
audio,
|
|
|
|
|
sysmsg,
|
|
|
|
|
message,
|
|
|
|
|
currentWBW ? t("CURRENT_STORY", currentWBW) : t("NO_STORY"),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (args.length > 2) {
|
|
|
|
|
return respond(audio, sysmsg, message, t("TOO_MANY_ARGUMENTS"));
|
|
|
|
|
}
|
|
|
|
|
const arg1 = args[1];
|
|
|
|
|
if (arg1 === undefined) return;
|
|
|
|
|
|
|
|
|
|
if (isStringInt(arg1)) {
|
|
|
|
|
const story = await db.get<WBWStoryRow>(
|
|
|
|
|
"select * from WBWStories where story_id=?",
|
|
|
|
|
parseInt(arg1, 10),
|
|
|
|
|
);
|
|
|
|
|
if (!story) return respond(audio, sysmsg, message, t("WBW_INVALID_ID"));
|
|
|
|
|
return respond(audio, sysmsg, message, story.story_text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const lastUser = await db.get<BotStateRow>(
|
|
|
|
|
"select value from BotState where key='last_wbw'",
|
|
|
|
|
);
|
|
|
|
|
if (lastUser && message.author.id === lastUser.value && currentWBW !== "") {
|
|
|
|
|
const lastWord =
|
|
|
|
|
currentWBW.indexOf(" ") === currentWBW.lastIndexOf(" ")
|
|
|
|
|
? currentWBW.trim()
|
|
|
|
|
: currentWBW.slice(currentWBW.slice(0, -1).lastIndexOf(" ") + 1).trim();
|
|
|
|
|
if (levenshtein.get(arg1, lastWord) <= 3) {
|
|
|
|
|
currentWBW = currentWBW.replace(
|
|
|
|
|
new RegExp(`${escapeRegExp(lastWord)}([^${escapeRegExp(lastWord)}]*)$`),
|
|
|
|
|
`${arg1}$1 `,
|
|
|
|
|
);
|
|
|
|
|
respond(audio, sysmsg, message, t("WBW_REPLACED", lastWord, arg1));
|
|
|
|
|
} else {
|
|
|
|
|
return respond(audio, sysmsg, message, t("WBW_TOO_DIFFERENT"));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
currentWBW += `${arg1} `;
|
|
|
|
|
respond(audio, sysmsg, message, t("WBW_NEW_WORD"));
|
|
|
|
|
const toSay =
|
|
|
|
|
currentWBW.indexOf(".") === -1 ? currentWBW : currentWBW.slice(currentWBW.lastIndexOf(".") + 2);
|
|
|
|
|
const voiceChannel = message.member?.voice.channel;
|
|
|
|
|
if (voiceChannel) await audio.speak(voiceChannel, toSay);
|
2026-05-14 21:33:18 +02:00
|
|
|
await db.run("insert or replace into BotState (key, value) values (?, ?)", [
|
|
|
|
|
"last_wbw",
|
|
|
|
|
message.author.id,
|
|
|
|
|
]);
|
2026-05-14 20:06:15 +02:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
commands.register("newwbw", async (_args, message) => {
|
|
|
|
|
await db.run("insert into WBWStories (story_text) values(?)", [currentWBW]);
|
|
|
|
|
currentWBW = "";
|
|
|
|
|
respond(audio, sysmsg, message, t("WBW_RESET"));
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function escapeRegExp(s: string): string {
|
|
|
|
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
|
|
|
}
|