diff --git a/src/index.js b/src/index.js index 9c295f1..ce4b658 100644 --- a/src/index.js +++ b/src/index.js @@ -50,9 +50,95 @@ const handleOf = (thing, fn) => { } } -const renderItem = x => x.quantity === 1 ? x.thing : `${x.quantity} ${handleOf(x.thing, pluralize.plural)}` +const PLURALS = [ + ["spoonful", "spoonfuls"], + ["index", "indices"], + ["lackey", "lackeys"], + ["abiogenesis", "abiogeneses"], + ["sheaf", "sheaves"], + ["goose", "geese"], + ["$DEITY", "$DEITIES"], + ["ox", "oxen"], + ["Category:English irregular plurals", "Categories:English irregular plurals"], + ["enigma", "enigmas"], + ["radix", "radixes"], + ["water", "water"], + ["funny", "funnies"], + ["octopus", "octopoda"], + ["the place in the world which is northernmost", "the places in the world which are northernmost"], + ["datum", "data"], + ["testbot", "testbots"], + ["oil of vitriol", "oil of vitriol"], + ["noun phrase", "noun phrases"], + ["information", "information"], + ["This phrase pluralizes as 'yellows'", "These phrases pluralize as 'yellows'"], + ["fireman", "firemen"], + ["potions of cryoapiocity", "potions of cryoapiocity"], + ["sulfuric acid", "sulfuric acid"], + ["dollar", "dollars"], +] +const PLURALS_KEEP_SAME = [ + "mice", + "computers", + "lackeys", + "dodecahedra", + "those things which must not be seen", + "announcements", + "rotations" +] +const SINGULARS_KEEP_SAME = [ + "mouse", + "computer", + "lackey", + "dodecahedron", + "that thing which must not be seen", + "announcement", + "rotation" +] -client.addListener("message", (nick, channel, message, ev) => { +const spliceIn = (xs, ys) => { + const locations = new Map(ys.map((y, i) => [Math.floor(xs.length * (i / ys.length)), y])) + const out = xs.map((x, i) => (locations.get(i) ? [x, locations.get(i)] : [x])) + const realOut = [] + for (const x of out) { + for (const y of x) { + realOut.push(y) + } + } + return realOut +} + +const queryLLM = async prompt => { + const res = await fetch("https://gpt.osmarks.net/v1/completions", { + body: JSON.stringify({ + "max_tokens": 40, + "stop": ["\n"], + prompt + }), + method: "POST", + headers: {"Content-type": "application/json"} + }) + const data = await res.json() + return data["choices"][0]["text"] +} + +const deriveLLMMapping = (things, splice, fallback) => async input => { + const prompt = spliceIn(things, splice).map(([a, b]) => `${a} :: ${b}`).join("\n") + "\n" + input + " ::" + try { + const res = await queryLLM(prompt) + return res.trim() + } catch (e) { + console.warn("switched to fallback", e) + return fallback(input) + } +} + +const pluralizer = deriveLLMMapping(PLURALS, PLURALS_KEEP_SAME.map(x => ([x, x])), pluralize.plural) +const singularizer = deriveLLMMapping(PLURALS.map(([a, b]) => ([b, a])), SINGULARS_KEEP_SAME.map(x => ([x, x])), pluralize.singular) + +const renderItem = async x => x.quantity === 1 ? x.thing : `${x.quantity} ${await pluralizer(x.thing)}` + +client.addListener("message", async (nick, channel, message, ev) => { const e = /^<([^>]+)> (.*)$/.exec(message) if (e) { nick = e[1] @@ -100,13 +186,14 @@ client.addListener("message", (nick, channel, message, ev) => { qty = parseFloat(rqty) } } - thing = handleOf(thing, pluralize.singular) + thing = await singularizer(thing) const currQty = SQL`SELECT * FROM inventory WHERE thing = ${thing}`.get()?.quantity if (currQty) { qty += currQty } qty = qty || 0 SQL`INSERT OR REPLACE INTO inventory VALUES (${thing}, ${Date.now()}, ${qty})`.run() console.log("attained", qty, thing) - client.say(channel, `I have ${renderItem({ thing, quantity: qty })}.`) + const hadThing = await renderItem({ thing, quantity: qty }) + client.say(channel, `I have ${hadThing}.`) } } else if (cmd.startsWith("inv")) { const inv = SQL`SELECT * FROM inventory ORDER BY obtained_at DESC LIMIT 10`.all() @@ -117,7 +204,7 @@ client.addListener("message", (nick, channel, message, ev) => { } } - const messageContent = message.replace(/^(\s*[<\[][A-Za-z0-9_-]+[>\]]\s*)+/, "") + const messageContent = message.replace(/^(\s*[<\[][^\]>]+[>\]]\s*)+/, "") sylhist = R.takeLast(3, R.append(syllables(messageContent), sylhist)) mhist = R.takeLast(50, R.append(messageContent, mhist)) if (R.equals(sylhist, [5, 7, 5])) {