代码拉取完成,页面将自动刷新
import { Random, escapeRegExp } from "koishi"
import { PassThrough } from "stream"
import * as what from "whatlang-interpreter"
for (const f of "cmd".split(" ")) if (!what.need_svo.includes(f)) what.need_svo.push(f)
export const name = "-lnnbot-whatapi"
export const inject = ["database", "server"]
export function apply(ctx) {
/*ctx.server.get("/whatnoter/:id", async r => {
r.set("Access-Control-Allow-Origin", "*")
r.set("Content-Type", "text/plain; charset=utf-8")
const match = r.params.id.match(/^(0|[1-9]\d*)([cd])$/)
if (!match) {
r.status = 404
r.body = "Invalid note specifier"
return
}
const uid = +match[1]
const type = { c: "public", d: "protected" }[match[2]]
const [d] = await ctx.database.get("whatnoter", uid, [type])
if (!d) {
r.status = 404
r.body = "Uid not present in noter"
return
}
r.status = 200
r.body = d[type]
})*/
/*ctx.server.get("/whatcommands", async r => {
r.set("Access-Control-Allow-Origin", "*")
r.set("Content-Type", "application/json; charset=utf-8")
r.status = 200
r.body = JSON.stringify((await ctx.database.get("whatcommands", {}, ["name"])).map(e => e.name))
})
ctx.server.get("/whatcommands/:name", async r => {
r.set("Access-Control-Allow-Origin", "*")
r.set("Content-Type", "application/json; charset=utf-8")
const [d] = await ctx.database.get("whatcommands", r.params.name)
if (!d) {
r.status = 404
r.body = JSON.stringify({ error: "Specified command does not exist" })
return
}
r.status = 200
r.body = JSON.stringify(d)
})*/
const whatServerLogger = ctx.logger("-lnnbot-whatserver")
//let whatServerRequestId = 1
ctx.server.all("/what([\\s\\S]*)", async r => {
r.set("Access-Control-Allow-Origin", "*")
let aid = undefined
const token = r.request.get("x-lnnbot-whatserver-login-token")
login: if (token) {
const [tokenInfo] = await ctx.database.get("token", { token }, ["id", "expiredAt"])
if (!tokenInfo || tokenInfo.expiredAt <= Date.now()) break login
if (tokenInfo.id === 0) {
aid = 0
break login
}
const [binding] = await ctx.database.get("binding", { bid: tokenInfo.id })
//if (!binding) break login
aid = binding?.aid ?? tokenInfo.id
}
const p = decodeURIComponent(r.path.slice(5))
const command = p.split("/")[0]
const arg = command === p ? undefined : p.slice(command.length + 1)
const methodSuffix = r.method === "GET" ? "" : r.method.toLowerCase()
const d = await ctx.database.get(
"whatcommands",
[
`server ${command}`,
`serverpost ${command}`,
`serverput ${command}`,
`serverdelete ${command}`,
`serverpatch ${command}`,
`serverall ${command}`,
],
["name", "code"],
)
if (!d.length) {
r.status = 404
r.body = `WhatServer route ${JSON.stringify(command)} does not exist`
return
}
const c = d.find(d => d.name === `server${methodSuffix} ${command}`) ??
d.find(d => d.name === `serverall ${command}`)
if (!c) {
r.status = 405
r.body = `WhatServer route ${JSON.stringify(command)} does not support ${r.method} requests`
return
}
const { name, code } = c
const escapeCharMap = { b: "\b", f: "\f", n: "\n", r: "\r", t: "\t" }
function formatting(value, options = {}) {
if (value === Infinity) return "Inf"
if (value === -Infinity) return "-Inf"
if (value === undefined) return "undef"
if (typeof value === "string") {
const { maxStringLength = 4000 } = options
let maxLen = maxStringLength
if (maxLen < value.length && value.codePointAt(maxLen - 1) > 0xffff) maxLen--
const truncated = value.slice(0, maxLen)
const lines =
truncated.length > 50
? truncated.match(/[^\n\r\f]*(?:\r?\n|\r|\f)?/g).filter(Boolean)
: [truncated]
const escapedLines = lines.map(line => {
line = line.replaceAll("\\", "\\\\").replaceAll('"', '\\"')
for (const [key, val] of Object.entries(escapeCharMap))
line = line.replaceAll(val, "\\" + key)
return line
})
let quoted = '"' + escapedLines.join('"\n "') + '"'
if (value.length > maxLen) {
const restCount = value.length - maxLen
quoted += `... ${restCount} more char${restCount > 1 ? "s" : ""}`
}
return quoted
}
if (Array.isArray(value)) {
const { depth = 4, maxArrayLength = 100, _seen = [] } = options
if (_seen.includes(value)) return "[...circular]"
if (depth < 0) return "[...]"
const contents = value.slice(0, maxArrayLength).map(item =>
formatting(item, {
...options,
depth: depth - 1,
_seen: [..._seen, value],
})
)
if (value.length > maxArrayLength) {
const restCount = value.length - maxArrayLength
contents.push(`... ${restCount} more item${restCount > 1 ? "s" : ""}`)
}
if (contents.some(c => c.includes("\n")))
return (
"[\n " +
contents.map(c => c.replaceAll("\n", "\n ")).join(",\n ") +
"\n]"
)
return "[" + contents.join(", ") + "]"
}
return String(value)
}
function toString(value) {
if (typeof value === "string") return value
return formatting(value)
}
function headersArrToObj(pairs) {
const headers = {}
for (let [key, value] of pairs) {
key = toString(key)
value = toString(value)
if (Object.hasOwn(headers, key)) headers[key] += ", " + value
else headers[key] = value
}
return headers
}
const userId = r.request.headers["x-real-ip"] || r.ip
const fauxCode = `${arg === undefined ? "" : `"${arg.replace(/(["\\])/g, "\\$1")}" `}"${name.replace(/(["\\])/g, "\\$1")}" cmd@`
const messageId = /*String(whatServerRequestId++)*/Random.id()
ctx.emit("whatlang/run", fauxCode, {
sid: `-lnnbot-whatapi-:${r.request.headers["x-forwarded-host"] || r.request.headers.host}`,
isDirect: true,
userId,
messageId,
})
whatServerLogger.debug("request", messageId, fauxCode)
const stream = r.body = new PassThrough()
r.type = "text/plain"
const { promise, resolve } = Promise.withResolvers()
let resolved = false
const output = []
let time = Date.now()
const initTime = time
const stop = ctx.setInterval(() => time = Date.now(), 100)
const dead_loop_check = () => {
const now = Date.now()
if (now - time > 1000 || now - initTime > 60000) return true
}
what.eval_what(
code,
[[arg]],
{
...what.default_var_dict,
you: () => "WhatLang/2024 Environment/WhatServer Brand/LNNBot Id/47.122.30.193",
reqh: Array.from(r.req.rawHeaders.filter((_, i) => i % 2 === 0), (x, i) => [x, r.req.rawHeaders[i * 2 + 1]]),
reqm: r.method.toLowerCase(),
//reqbget: async () => await (require("raw-body"))(require("inflation")(r.req), "utf-8"),
//reqbgetb: async () => await (require("raw-body"))(require("inflation")(r.req)),
reqb: r.request.body ?
typeof r.request.body === "object" ?
Symbol.for("unparsedBody") in r.request.body ?
r.request.body[Symbol.for("unparsedBody")]
: Object.entries(r.request.body)
: r.request.body
: r.method !== "GET" ?
[...await (require("raw-body"))(require("inflation")(r.req))]
: undefined,
hset: (x, y) => r.set(String(x), String(y)),
me: () => [r.method + " " + decodeURI(r.url.replaceAll("%25", "%2525")), messageId, userId, userId, aid, "__WHATSERVER__", undefined],
ou: x => { output.push(Buffer.from(x)) },
nout: () => { output.pop() },
nouts: x => { output.splice(-x) },
send: () => {
whatServerLogger.debug("send", messageId, { resolved })
stream.write(output.pop())
resolved || resolve()
resolved = true
},
sends: x => {
whatServerLogger.debug("sends", messageId, { resolved })
for (const chunk of output.splice(-x)) stream.write(chunk)
resolved || resolve()
resolved = true
},
cat: async x => await ctx.http.get(String(x), { responseType: "text" }),
ca: async x => [...new Uint8Array(await ctx.http.get(String(x), { responseType: "arraybuffer" }))],
fetch: async (method, url, headers, data) => {
const resp = await ctx.http(url, {
method,
headers: headersArrToObj(headers),
data: typeof data === "number" ? String(data) : Array.isArray(data) ? Buffer.from(data) : data,
responseType: "text",
validateStatus: () => true,
redirect: "manual",
})
return [resp.status, resp.statusText, [...resp.headers], resp.data]
},
fech: async (method, url, headers, data) => {
const resp = await ctx.http(url, {
method,
headers: headersArrToObj(headers),
data: typeof data === "number" ? String(data) : Array.isArray(data) ? Buffer.from(data) : data,
responseType: "arraybuffer",
validateStatus: () => true,
redirect: "manual",
})
return [resp.status, resp.statusText, [...resp.headers], [...new Uint8Array(resp.data)]]
},
reesc: x => escapeRegExp(x),
sleep: async x => await new Promise((res) => ctx.setTimeout(res, x * 1000)),
notewc: async (x, y) => {
if (aid === undefined) throw new Error("notewc@: you must authenticate to edit data in WhatNoter")
await ctx.database.upsert("whatnoter", [{uid: x, public: y}], "uid")
},
notewd: async x => {
if (aid === undefined) throw new Error("notewd@: you must authenticate to edit data in WhatNoter")
await ctx.database.upsert("whatnoter", [{uid: aid, protected: x}], "uid")
},
notewe: async x => {
if (aid === undefined) throw new Error("notewe@: you must authenticate to edit data in WhatNoter")
await ctx.database.upsert("whatnoter", [{uid: aid, private: x}], "uid")
},
noterc: async x => (await ctx.database.get("whatnoter", {uid: x}, ["public"]))[0]?.public ?? null,
noterd: async x => (await ctx.database.get("whatnoter", {uid: x}, ["protected"]))[0]?.protected ?? null,
notere: async () => {
if (aid === undefined) throw new Error("notere@: you must authenticate to access private data in WhatNoter")
return (await ctx.database.get("whatnoter", {uid: aid}, ["private"]))[0]?.private ?? null
},
cmdset: async (x, y) => {
if (aid === undefined) throw new Error("cmdset@: you must authenticate to edit WhatCommands")
await ctx.database.upsert("whatcommands", [{name: y, code: x}], "name")
},
cmdall: async () => (await ctx.database.get("whatcommands", {}, ["name"])).map(i => i.name),
cmdsethelp: async (x, y) => {
if (aid === undefined) throw new Error("cmdsethelp@: you must authenticate to edit WhatCommands")
await ctx.database.upsert("whatcommands", [{name: y, help: x}], "name")
},
cmdseth: async (x, y) => {
if (aid === undefined) throw new Error("cmdseth@: you must authenticate to edit WhatCommands")
await ctx.database.upsert("whatcommands", [{name: y, h: x}], "name")
},
cmddel: async x => { await ctx.database.remove("whatcommands", {name: x}) },
cmdget: async x => (await ctx.database.get("whatcommands", {name: x}, ["code"]))[0]?.code ?? null,
cmdgethelp: async x => (await ctx.database.get("whatcommands", {name: x}, ["help"]))[0]?.help ?? null,
cmdgeth: async x => (await ctx.database.get("whatcommands", {name: x}, ["h"]))[0]?.h ?? null,
cmd: async (x, y, s, v, o) => {
let temp = (await ctx.database.get("whatcommands", {name: y}, ["code"]))[0]?.code
if (temp == undefined) throw new Error("command not found")
return await what.exec_what([...s.slice(0, -1), s.at(-1).concat([x, temp])], v, o) ?? null
},
[Symbol.for("whatlang.dead_loop_check")]: dead_loop_check,
},
x => output.push(x),
)
.then(
res => {
whatServerLogger.debug("end", messageId, { resolved })
for (const chunk of output) stream.write(chunk)
stream.end()
if (!resolved) {
let m
if (
Array.isArray(res) &&
/^[2-5]\d\d$/.test(res[0]) &&
res.length === 2 &&
typeof res[1] === "string" && /^.+$/.test(res[1])
) {
r.status = +res[0]
r.message = res[1]
} else if (m = /^([2-5]\d\d)(?: (.+))?/.exec(res)) {
r.status = +m[1]
if (m?.[2]) r.message = m[2]
} else {
r.status = 200
}
resolve()
}
},
err => {
whatServerLogger.debug("error-end", messageId, { resolved }, err)
for (const chunk of output) stream.write(chunk)
if (resolved || output.length) stream.write("\n")
stream.end("\fUNCAUGHT " + err.toString())
if (!resolved) {
r.status = 500
resolve()
}
}
)
.finally(stop)
return promise
})
ctx.server.get("/microcommands", async r => {
r.set("Access-Control-Allow-Origin", "*")
r.set("Content-Type", "application/json; charset=utf-8")
r.status = 200
r.body = JSON.stringify((await ctx.database.get("dgck81lnn-microcommands", {}, ["name"])).map(e => e.name))
})
ctx.server.get("/microcommands/:name", async r => {
r.set("Access-Control-Allow-Origin", "*")
r.set("Content-Type", "text/plain; charset=utf-8")
const [d] = await ctx.database.get("dgck81lnn-microcommands", r.params.name, ["code"])
if (!d) {
r.status = 404
r.body = "Specified microcommand does not exist"
return
}
r.status = 200
r.body = d.code
})
ctx.server.get("/patrons", async r => {
r.set("Access-Control-Allow-Origin", "*")
r.set("Content-Type", "text/plain; charset=utf-8")
r.status = 200
r.body = JSON.stringify(Object.fromEntries((await ctx.database.get("lnnbot.patron", {})).map(e => [e.uid, { name: e.name, ctime: e.ctime }])))
})
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。