actually git-ize
This commit is contained in:
commit
d932447591
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
db.sqlite3
|
||||
config.toml
|
3347
package-lock.json
generated
Normal file
3347
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
20
package.json
Normal file
20
package.json
Normal 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
40
src-old/db.py
Normal 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
60
src-old/main.py
Normal 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
60
src/db.js
Executable 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
135
src/index.js
Normal 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 = []
|
||||
}
|
||||
})
|
Loading…
x
Reference in New Issue
Block a user