actually git-ize

This commit is contained in:
osmarks 2021-04-16 20:46:46 +01:00
commit d932447591
7 changed files with 3665 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
node_modules
db.sqlite3
config.toml

3347
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

20
package.json Normal file
View File

@ -0,0 +1,20 @@
{
"name": "ircbot",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "nodemon src/index.js"
},
"license": "MIT",
"devDependencies": {
"nodemon": "^2.0.4"
},
"dependencies": {
"better-sqlite3": "^7.1.0",
"irc-upd": "^0.11.0",
"pluralize": "^8.0.0",
"ramda": "^0.27.1",
"syllable": "^4.1.0"
}
}

40
src-old/db.py Normal file
View File

@ -0,0 +1,40 @@
import aiosqlite
import logging
migrations = [
"""
CREATE TABLE deleted_items (
id INTEGER PRIMARY KEY,
timestamp INTEGER NOT NULL,
item TEXT NOT NULL
);
""",
"""
CREATE INDEX deleted_items_timestamps ON deleted_items(timestamp);
""",
"""
CREATE TABLE reminders (
id INTEGER PRIMARY KEY,
remind_timestamp INTEGER NOT NULL,
created_timestamp INTEGER NOT NULL,
reminder TEXT NOT NULL,
expired INTEGER NOT NULL,
extra TEXT NOT NULL
);
"""
]
async def init(db_path):
db = await aiosqlite.connect(db_path)
version = (await (await db.execute("PRAGMA user_version")).fetchone())[0]
for i in range(version, len(migrations)):
await db.executescript(migrations[i])
# Normally this would be a terrible idea because of SQL injection.
# However, in this case there is not an obvious alternative (the parameter-based way apparently doesn't work)
# and i + 1 will always be an integer anyway
await db.execute(f"PRAGMA user_version = {i + 1}")
await db.commit()
logging.info(f"Migrated DB to schema {i + 1}")
return db

60
src-old/main.py Normal file
View File

@ -0,0 +1,60 @@
import irc.bot
import irc.strings
import jaraco.logging
import hashlib
import toml
import ssl
import string
import random
conf = toml.load(open("config.toml"))
class Bot(irc.bot.SingleServerIRCBot):
def __init__(self, servers, nick, channel):
super().__init__(servers, nick, nick, **{"ipv6": True})
print(dir(self))
self.channel = channel
def on_nicknameinuse(self, c, e):
c.nick("".join(random.choices(string.ascii_lowercase ,k=8)))
print("nick in use, changing")
def on_welcome(self, c, e):
c.join(self.channel)
print("joined", self.channel)
def on_privmsg(self, c, e):
self.do_command(e, e.arguments[0])
def on_pubmsg(self, c, e):
a = e.arguments[0].split(":", 1)
if len(a) > 1 and irc.strings.lower(a[0]) == irc.strings.lower(self.connection.get_nickname()):
self.do_command(e, a[1].strip())
return
def do_command(self, e, cmd):
nick = e.source.nick
c = self.connection
print("exec", nick, cmd)
if cmd == "stats":
for chname, chobj in self.channels.items():
c.notice(nick, "--- Channel statistics ---")
c.notice(nick, "Channel: " + chname)
users = sorted(chobj.users())
c.notice(nick, "Users: " + ", ".join(users))
opers = sorted(chobj.opers())
c.notice(nick, "Opers: " + ", ".join(opers))
voiced = sorted(chobj.voiced())
c.notice(nick, "Voiced: " + ", ".join(voiced))
elif cmd == "starch":
c.notice(nick, "starch: it exists.")
else:
c.notice(nick, "Not understood: " + cmd)
if __name__ == "__main__":
s = conf["server"]
bot = Bot([(s["host"], s["port"])], conf["nickname"], "#a")
bot.start()

60
src/db.js Executable file
View File

@ -0,0 +1,60 @@
const Database = require("better-sqlite3")
const DB = Database(process.env.DB || "./db.sqlite3")
const migrations = [
`
CREATE TABLE thing_log (
id INTEGER PRIMARY KEY,
timestamp INTEGER NOT NULL,
type TEXT NOT NULL,
thing TEXT NOT NULL
);
`,
`
CREATE TABLE inventory (
thing TEXT NOT NULL UNIQUE,
obtained_at INTEGER NOT NULL,
quantity REAL NOT NULL
);
`
]
const executeMigration = DB.transaction((i) => {
const migration = migrations[i]
DB.exec(migration)
DB.pragma(`user_version = ${i + 1}`)
console.log(`Migrated to schema ${i + 1}`)
})
const schemaVersion = DB.pragma("user_version", { simple: true })
if (schemaVersion < migrations.length) {
console.log(`Migrating DB - schema ${schemaVersion} used, schema ${migrations.length} available`)
for (let i = schemaVersion; i < migrations.length; i++) {
executeMigration(i)
}
}
DB.pragma("foreign_keys = 1")
const preparedStatements = new Map()
const SQL = (strings, ...params) => {
const sql = strings.join("?")
let stmt
const cachedValue = preparedStatements.get(sql)
if (!cachedValue) {
stmt = DB.prepare(sql)
preparedStatements.set(sql, stmt)
} else {
stmt = cachedValue
}
return {
get: () => stmt.get.apply(stmt, params),
run: () => stmt.run.apply(stmt, params),
all: () => stmt.all.apply(stmt, params),
statement: stmt
}
}
module.exports = { DB, SQL }

135
src/index.js Normal file
View File

@ -0,0 +1,135 @@
const irc = require("irc-upd")
const childProcess = require("child_process")
const syllables = require("syllable")
const R = require("ramda")
const pluralize = require("pluralize")
const { DB, SQL } = require("./db")
var client = new irc.Client(process.argv[2] || "207:1473:146:ae77:a3be:d39f:8e59:d256", "testbot", {
channels: ["#a", "#botrobots", "#b"],
userName: "testbot",
encoding: "utf8",
port: 6667
});
let links = null
let linksResolve = null
const logEv = (ty, thing) => SQL`INSERT INTO thing_log (timestamp, type, thing) VALUES (${Date.now()}, ${ty}, ${thing})`.run()
const scanlinks = () => {
return new Promise(resolve => {
if (!linksResolve) {
client.send("LINKS")
linksResolve = [resolve]
links = []
} else {
linksResolve.push(resolve)
}
})
}
let sylhist = []
let mhist = []
// pluralize seems to drop unicode sometimes
// hackily cope with this
const safelyOperate = (s, fn) => {
let res = fn(s)
if (res.trim() === "") { return s }
return res
}
const handleOf = (thing, fn) => {
const ofsplt = /^(.*)\W+of\W+(.*)$/.exec(thing)
if (ofsplt) {
return `${fn(ofsplt[1])} of ${ofsplt[2]}`
} else {
return fn(thing)
}
}
const renderItem = x => x.quantity === 1 ? x.thing : `${x.quantity} ${handleOf(x.thing, pluralize.plural)}`
client.addListener("message", (nick, channel, message, ev) => {
const e = /^<([^>]+)> (.*)$/.exec(message)
if (e) {
nick = e[1]
message = e[2]
}
const res = /^([A-Za-z0-9_-]+)[:, ]*([A-Za-z0-9_-]+) *(.*)$/.exec(message)
if (res) {
let [_, tnick, cmd, args] = res
tnick = tnick.toLowerCase()
args = args.trim().replace(/[.!?]$/, "")
cmd = cmd.toLowerCase()
if (tnick === client.nick) {
console.log(nick, cmd, channel)
if (cmd === "starch") {
client.say(channel, `starch exists (${Math.random() * 20 + 80}% confidence).`)
} else if (cmd === "stats") {
client.say(channel, "haha no")
} else if (cmd === "help") {
client.say(channel, "nobody can help you now")
} else if (cmd === "fortune") {
childProcess.exec("fortune", (err, out) => {
if (err) { return console.warn(err) }
client.say(channel, out)
})
} else if (cmd === "cat") {
client.say(channel, "meow")
} else if (cmd === "servers") {
scanlinks().then(links => client.say(channel, links.map(x => `${x[1]}: ${/^[0-9]+ (.*)$/.exec(x[3])[1]}`).join("\n")))
} else if (cmd === "take" || cmd === "have") {
const things = args.split(/(,| and )/).map(x => x.trim().replace(",", "").replace(/^(an?|the) /, "")).filter(x => x !== "" && x !== "and")
for (let thing of things) {
let qty = 1
const res = /^([-0-9.]+|some|many)\b\s*(.*)$/iu.exec(thing)
if (res) {
const [_, rrqty, rthing] = res
thing = rthing
const rqty = rrqty.toLowerCase()
if (rqty === "some") {
qty = Math.floor(Math.random() * 17)
} else if (rqty === "many") {
qty = Math.floor(Math.random() * 2300)
} else {
qty = parseFloat(rqty)
}
}
thing = handleOf(thing, pluralize.singular)
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 })}.`)
}
} else if (cmd.startsWith("inv")) {
const inv = SQL`SELECT * FROM inventory ORDER BY obtained_at DESC LIMIT 10`.all()
client.say(channel, inv.map(renderItem).join("\n"))
} else if (cmd === "deploy") {
client.say(channel, `Deploying ${args}`)
}
}
}
sylhist = R.takeLast(3, R.append(syllables(message), sylhist))
mhist = R.takeLast(50, R.append(message, mhist))
if (R.equals(sylhist, [5, 7, 5])) {
client.say(channel, "haiku detected!")
logEv("haiku", R.takeLast(3, mhist).join("\n"))
}
console.log(sylhist)
})
client.addListener("raw", ev => {
if (ev.command === "rpl_links") {
links.push(ev.args)
} else if (ev.command === "rpl_endoflinks" && linksResolve) {
linksResolve.forEach(r => r(links))
linksResolve = null
links = []
}
})