initial commit

pull/1/head
Drake 2 years ago
commit 67b3902bff

2
.gitignore vendored

@ -0,0 +1,2 @@
dist/
config.json

@ -0,0 +1,7 @@
{
"deno.enable": true,
"deno.unstable": true,
"deno.importMap": "import_map.json",
"editor.tabSize": 4,
"editor.detectIndentation": false
}

@ -0,0 +1,2 @@
# ai_bot
this code should not be used by any sane person, ever.

@ -0,0 +1,26 @@
{
"general": {
"name": "GPT-4",
"desc": "OpenAI's newest GPT text-generation AI",
"ais": ["huggingface"],
"db": {
"url": "SURREAL_DB_URL_HERE",
"user": "USER_HERE",
"pass": "PASS_HERE",
"namespace": "NS_HERE",
"database": "DB_HERE"
}
},
"discord": {
"channels": [
"CHANNEL_ID_HERE"
]
},
"huggingface": {
"tokens": [
"TOKEN_HERE"
],
"memoryLen": 5,
"model": "EleutherAI/gpt-neo-2.7B"
}
}

@ -0,0 +1,12 @@
{
"fmt": {
"options": {
"indentWidth": 4,
"lineWidth": 120
}
},
"importMap": "./import_map.json",
"tasks": {
"run:db": "surreal start --user root --pass root rocksdb://dist/database"
}
}

@ -0,0 +1,5 @@
{
"imports": {
"@wackford/": "https://deno.land/x/wackford@v0.0.1/"
}
}

@ -0,0 +1 @@
import "./src/bot.ts";

20
src/ai/BaseAI.d.ts vendored

@ -0,0 +1,20 @@
import { configType } from "../config.ts";
import { Channel } from "../database.ts"
export default class BaseAI {
name: string;
description: string;
history?: string[];
memory?: string[];
constructor(name: string, description: string, config: configType, db: Channel);
changeShit(opts: {
name?: string;
description?: string;
}): void;
reset(): void;
complete(username: string, message: string): Promise<string>;
}

@ -0,0 +1,28 @@
class EchoAI {
name: string;
description: string;
constructor(name: string, description: string, _config: unknown) {
this.name = name;
this.description = description;
}
reset() {
//NOP
}
changeShit(opts: {
name?: string;
description?: string;
}) {
this.name = opts.name ?? this.name;
this.description = opts.description ?? this.description;
this.reset();
}
complete(_username: string, message: string) {
return message;
}
}
export default EchoAI;

@ -0,0 +1,80 @@
import { configType } from "../config.ts";
import { Channel } from "../database.ts"
class HuggingFaceAI {
name: string;
description: string;
prefix: string;
memory: string[];
tokens: string[];
tokenNum: number;
memoryLen: number;
parameters: {
max_new_tokens: number;
temperature: number;
repetition_penalty: number;
top_k: number;
return_full_text: boolean;
};
model: string;
constructor(name: string, description: string, config: configType, db: Channel) {
this.name = name;
this.description = description;
this.prefix = `The following is a chat with ${this.name}, ${this.description}.\n`;
this.tokens = config.huggingface.tokens;
this.tokenNum = 0;
this.memoryLen = config.huggingface.memoryLen;
this.parameters = {
"max_new_tokens": 50,
"temperature": 0.8,
"repetition_penalty": 1.8,
"top_k": 40,
"return_full_text": false,
};
this.model = config.huggingface.model;
this.memory = db.history ? db.history.map((m) => `${m.name}: "${m.content}"`) : [];
}
async #query(prompt: string) {
const res = await fetch(`https://api-inference.huggingface.co/models/${this.model}`, {
body: JSON.stringify({
inputs: prompt,
parameters: this.parameters,
}),
headers: {
Authorization: `Bearer ${this.tokens[this.tokenNum]}`,
},
method: "POST",
});
return await res.json();
}
reset() {
this.prefix = `The following is a chat with ${this.name}, ${this.description}.\n`;
this.memory = [];
}
changeShit(opts: {
name?: string;
description?: string;
}) {
this.name = opts.name ?? this.name;
this.description = opts.description ?? this.description;
this.reset();
}
async complete(username: string, message: string) {
console.log(`${username}: "${message}"`);
const ctx = this.memory.slice(this.memoryLen * -2);
const prompt = `${this.prefix}\n${ctx.join("\n")}\n${username}: "${message}"\n${this.name}: "`;
const res = await this.#query(prompt);
const botMsg = res[0].generated_text.split('"\n')[0];
this.memory.push(`${username}: "${message}"`);
this.memory.push(`${this.name}: "${botMsg}"`);
console.log(`${this.name}: "${botMsg}"`);
return botMsg;
}
}
export default HuggingFaceAI;

@ -0,0 +1,63 @@
import { Bot, Interaction } from "@wackford/discordeno.ts";
import { sendInteractionResponse } from "@wackford/mod.ts";
import { GoofyAhhException } from "../bot.ts";
import config from "../config.ts";
import { db, Channel } from "../database.ts";
import BaseAI from "./BaseAI.d.ts";
import HuggingFaceAI from "./huggingface.ts";
const dirname = new URL(".", import.meta.url).pathname;
const AIs: Record<string, BaseAI> = new Proxy({} as Record<string, BaseAI>, {
get: (target, prop: string) => {
if (!Object.keys(AIs).includes(prop)) {
return false;
}
return target[prop];
},
});
export default function initAI() {
config.discord.channels.forEach(async (channelId) => {
let channel = (await db.select(channelId))[0] as Channel;
if (!channel) {
db.create(channelId, {
moduleName: "huggingface",
name: config.general.name,
description: config.general.desc,
history: []
})
channel = (await db.select(channelId))[0] as Channel;
}
console.log(channel)
changeAI(channel.moduleName, channelId, channel.name, channel.description)
});
}
export async function changeAI(moduleName: string, channelId: string, newName?: string, newDesc?: string) {
const channel = (await db.select(channelId))[0] as Channel;
const name = newName ?? AIs[channelId].name;
const desc = newDesc ?? AIs[channelId].description;
const mod = (await import(dirname + moduleName + ".ts")).default;
AIs[channelId] = new mod(name, desc, config, channel);
await db.change(channelId, { moduleName: moduleName });
}
export function checkAI(bot: Bot, interaction: Interaction, AI: BaseAI | boolean) {
if (!AI) {
sendInteractionResponse(bot, interaction, {
content: "AI unavailable in this channel; please check your server for the correct channel!",
private: true,
});
return false;
} else {
return true;
}
}
export { AIs };

@ -0,0 +1,26 @@
import { BotEmitter, initCommands } from "@wackford/mod.ts";
import { Intents } from "@wackford/discordeno.ts";
import initLocalCommands from "./commands/index.ts";
import initOnMessage from "./events/onMessage.ts";
import initAIs from "./ai/index.ts";
import initDB from "./database.ts";
export class GoofyAhhException extends Error {
constructor(message: string) {
super(message);
this.name = "GoofyAhhException";
}
}
await initDB();
initOnMessage();
initLocalCommands();
initCommands();
initAIs();
await BotEmitter.emit("start", {
token: Deno.env.get("TOKEN") ?? (() => {
throw new GoofyAhhException("No token?");
})(),
intents: Intents.Guilds | Intents.GuildMessages | Intents.MessageContent,
});

@ -0,0 +1,44 @@
import { sendInteractionResponse, SlashCommandOptions } from "@wackford/mod.ts";
import { ApplicationCommandOptionTypes } from "@wackford/discordeno.ts";
import { AIs, checkAI } from "../ai/index.ts";
import { db } from "../database.ts";
export default {
name: "change",
description: "shows the current name and description of the chatbot",
options: [
{
name: "name",
description: "sets the name of the bot to this value",
type: ApplicationCommandOptionTypes.String,
},
{
name: "description",
description: "sets the description of the bot to this value",
type: ApplicationCommandOptionTypes.String,
},
],
execute: async (bot, interaction, args) => {
const channelId = interaction?.channelId?.toString() as string;
const AI = AIs[channelId];
const newName = args?.name?.value?.toString() ?? undefined;
const newDesc = args?.description?.value?.toString() ?? undefined;
if (!checkAI(bot, interaction, AI)) return;
AI.changeShit({
name: newName,
description: newDesc,
});
db.change(channelId, {
name: newName,
description: newDesc,
history: []
})
await sendInteractionResponse(bot, interaction, {
content: `Name: ${newName ?? "[unchanged]"}\nDescription: ${newDesc ?? "[unchanged]"}`,
});
},
} as SlashCommandOptions;

@ -0,0 +1,40 @@
import { ApplicationCommandOptionChoice, ApplicationCommandOptionTypes } from "@wackford/discordeno.ts";
import { sendInteractionResponse, SlashCommandOptions } from "@wackford/mod.ts";
import { AIs, changeAI, checkAI } from "../ai/index.ts";
import config from "../config.ts";
import { db } from "../database.ts";
export default {
name: "changeai",
description: "changes the AI",
options: [
{
name: "name",
description: "name of the",
type: ApplicationCommandOptionTypes.String,
required: true,
choices: <ApplicationCommandOptionChoice[]> config.general.ais.map((m) =>
<ApplicationCommandOptionChoice> {
name: m,
value: m,
}
),
},
],
execute: async (bot, interaction, args) => {
const AI = AIs[interaction?.channelId?.toString() as string];
const name = args["name"].value?.toString();
if (!checkAI(bot, interaction, AI)) return;
if (name) {
await changeAI(name, interaction.channelId?.toString() as string);
db.change(interaction?.channelId?.toString() as string, {
history: []
})
}
await sendInteractionResponse(bot, interaction, {
content: `Changed AI to ${name}!`,
});
},
} as SlashCommandOptions;

@ -0,0 +1,17 @@
import { sendInteractionResponse, SlashCommandOptions } from "@wackford/mod.ts";
import { AIs, checkAI } from "../ai/index.ts";
export default {
name: "debug",
description: "shows debug information",
execute: async (bot, interaction) => {
const AI = AIs[interaction?.channelId?.toString() as string];
if (!checkAI(bot, interaction, AI)) return;
await sendInteractionResponse(bot, interaction, {
content: `Name: ${AI.name}\nDescription: ${AI.description}\nAI: ${AI.constructor.name}\nHistory: ${JSON.stringify(AI.memory ?? AI.history ?? "[unused]")}`,
private: true
});
},
} as SlashCommandOptions;

@ -0,0 +1,27 @@
import { createSlashCommand, sendInteractionResponse } from "@wackford/mod.ts";
const dirname = new URL(".", import.meta.url).pathname;
const helpMessage: string[] = [
"/help - helps your idiot ass", // we do this manually because fuck you too
];
export default async function initLocalCommands() {
for await (const file of Deno.readDir(dirname)) {
if (file.name !== "index.ts") {
const mod = (await import(dirname + file.name)).default;
helpMessage.push(`/${mod.name} - ${mod.description}`);
createSlashCommand(mod);
}
}
createSlashCommand({
name: "help",
description: "helps your idiot ass",
execute: async (bot, interaction) => {
await sendInteractionResponse(bot, interaction, {
content: helpMessage.join("\n"),
private: true,
});
},
});
}

@ -0,0 +1,24 @@
import { sendInteractionResponse, SlashCommandOptions } from "@wackford/mod.ts";
import { AIs, checkAI } from "../ai/index.ts";
import { db } from "../database.ts";
export default {
name: "reset",
description: "resets the conversation history of the chatbot",
execute: async (bot, interaction) => {
const channelId = interaction?.channelId?.toString() as string;
const AI = AIs[interaction?.channelId?.toString() as string];
if (!checkAI(bot, interaction, AI)) return;
AI.reset();
db.change(channelId, {
history: []
})
await sendInteractionResponse(bot, interaction, {
content: `Done!`,
});
},
} as SlashCommandOptions;

@ -0,0 +1,16 @@
import { sendInteractionResponse, SlashCommandOptions } from "@wackford/mod.ts";
import { AIs, checkAI } from "../ai/index.ts";
export default {
name: "status",
description: "shows the current name and description of the chatbot",
execute: async (bot, interaction) => {
const AI = AIs[interaction?.channelId?.toString() as string];
if (!checkAI(bot, interaction, AI)) return;
await sendInteractionResponse(bot, interaction, {
content: `Name: ${AI.name}\nDescription: ${AI.description}\nAI: ${AI.constructor.name}`,
});
},
} as SlashCommandOptions;

@ -0,0 +1,7 @@
import config from "../config.json" assert { type: "json" };
export type configType = typeof config;
export { config };
export default config;

@ -0,0 +1,28 @@
import Surreal from "https://deno.land/x/surrealdb@v0.5.0/mod.ts";
import config from "./config.ts";
import { GoofyAhhException } from "./bot.ts";
export const db = new Surreal(`${config.general.db.url}/rpc`);
export interface Channel {
moduleName: string;
name: string;
description: string;
history: {
name: string,
content: string
}[];
}
export default async function init() {
try {
await db.signin({
user: config.general.db.user,
pass: config.general.db.pass
});
await db.use(config.general.db.namespace, config.general.db.database);
} catch (e) {
throw new GoofyAhhException(e);
}
}

@ -0,0 +1,49 @@
import { db, Channel } from "../database.ts";
import { BotEmitter } from "@wackford/mod.ts";
import { AIs } from "../ai/index.ts";
import config from "../../config.json" assert { type: "json" };
//const dirname = new URL(".", import.meta.url).pathname;
export default function init() {
BotEmitter.on("message", async (bot, message) => {
if (
message.content &&
!message.content.startsWith("!") &&
config.discord.channels.includes(message.channelId.toString()) &&
!message.isFromBot &&
message.member
) {
const channel = (await db.select(message.channelId.toString()))[0] as Channel
const user = await bot.helpers.getUser(message.member?.id);
await bot.helpers.startTyping(message.channelId);
const res = await AIs[message.channelId.toString()].complete(user.username, message.content)
const history = channel.history ?? []
history.push({
name: user.username,
content: message.content
}, {
name: AIs[message.channelId.toString()].name,
content: res
})
db.change(message.channelId.toString(), {
history
})
await bot.helpers.sendMessage(message.channelId, {
content: `[${AIs[message.channelId.toString()].name}] ${res}`,
messageReference: {
messageId: message.id,
channelId: message.channelId,
guildId: message.guildId,
failIfNotExists: false,
},
});
}
});
}
Loading…
Cancel
Save