diff --git a/.gitignore b/.gitignore index 2eea525..a574ae1 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.env \ No newline at end of file +.env +cai.token \ No newline at end of file diff --git a/deno.json b/deno.json index af37f33..fb2b391 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,6 @@ { "tasks": { - "dev": "deno run --watch --allow-env --allow-read=. --allow-net --env-file main.ts", - "deployCommands": "deno run --env-file --allow-env --allow-net --allow-read=./commands scripts/deployCommands.ts" + "dev": "deno run --watch --allow-env --allow-read=. --allow-net --env-file ./src/main.ts", + "deployCommands": "deno run --env-file --allow-env --allow-net --allow-read=./src/ scripts/deployCommands.ts" } } diff --git a/deno.lock b/deno.lock index 1eaaa74..b432c97 100644 --- a/deno.lock +++ b/deno.lock @@ -1,16 +1,28 @@ { - "version": "4", + "version": "5", "specifiers": { + "jsr:@std/fs@*": "1.0.18", "jsr:@std/path@*": "1.1.0", + "jsr:@std/path@^1.1.0": "1.1.0", "npm:@types/node@*": "22.12.0", + "npm:cainode@latest": "1.2.2", "npm:discord.js@*": "14.20.0" }, "jsr": { + "@std/fs@1.0.18": { + "integrity": "24bcad99eab1af4fde75e05da6e9ed0e0dce5edb71b7e34baacf86ffe3969f3a", + "dependencies": [ + "jsr:@std/path@^1.1.0" + ] + }, "@std/path@1.1.0": { "integrity": "ddc94f8e3c275627281cbc23341df6b8bcc874d70374f75fec2533521e3d6886" } }, "npm": { + "@bufbuild/protobuf@2.5.2": { + "integrity": "sha512-foZ7qr0IsUBjzWIq+SuBLfdQCpJ1j8cTuNNT4owngTHoN5KsJb8L9t65fzz7SCeSWzescoOil/0ldqiL041ABg==" + }, "@discordjs/builders@1.11.2": { "integrity": "sha512-F1WTABdd8/R9D1icJzajC4IuLyyS8f3rTOz66JsSI3pKvpCAtsMBweu8cyNYsIyvcrKAVn9EPK+Psoymq+XC0A==", "dependencies": [ @@ -66,6 +78,52 @@ "ws" ] }, + "@livekit/mutex@1.1.1": { + "integrity": "sha512-EsshAucklmpuUAfkABPxJNhzj9v2sG7JuzFDL4ML1oJQSV14sqrpTYnsaOudMAw9yOaW53NU3QQTlUQoRs4czw==" + }, + "@livekit/rtc-node-darwin-arm64@0.11.1": { + "integrity": "sha512-M+Ui87H06ae19GGI7r937dS6hI84MBBTQAkkNlL7qd+pvdCAk25u0FYa8r4SOElKJ0VR3AbzeDoXTihLgpvjMg==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@livekit/rtc-node-darwin-x64@0.11.1": { + "integrity": "sha512-7G92fyuK2p+jdTH2cUJTNeAmtknTGsXuy0xbI727V7VzQvHFDXULCExRlgwn4t9TxvNlIjUpiltiQ6RCSai6zw==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@livekit/rtc-node-linux-arm64-gnu@0.11.1": { + "integrity": "sha512-vqZN9+87Pvxit7auYVW69M+GvUPnf+EwipIJ92GgCJA3Ir1Tcceu5ud5/Ic+0FzSoV0cotVVlQNm74F0tQvyCg==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@livekit/rtc-node-linux-x64-gnu@0.11.1": { + "integrity": "sha512-smHZUMfgILQh6/eoauYNe/VlKwQCp4/4jWxiIADHY+mtDtVSvQ9zB6y4GP8FrpohRwFWesKCUpvPBypU0Icrng==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@livekit/rtc-node-win32-x64-msvc@0.11.1": { + "integrity": "sha512-bTWVtb+UiRPFjiuhrqq40gt5vs5mMPTa1e+kd2jGQPTOlKZPLArQ0WgFcep2TAy1zmcpOgfeM1XRPVFhZl7G1A==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@livekit/rtc-node@0.11.1": { + "integrity": "sha512-EFw+giPll12fcXATZpN2zKkE3umYJAdHvfjW+Yu0aBjwfxbUBXu8rz6le2CzDNvGmRwR888DSZXFZfYikwZgiw==", + "dependencies": [ + "@bufbuild/protobuf", + "@livekit/mutex", + "@livekit/typed-emitter" + ], + "optionalDependencies": [ + "@livekit/rtc-node-darwin-arm64", + "@livekit/rtc-node-darwin-x64", + "@livekit/rtc-node-linux-arm64-gnu", + "@livekit/rtc-node-linux-x64-gnu", + "@livekit/rtc-node-win32-x64-msvc" + ] + }, + "@livekit/typed-emitter@3.0.0": { + "integrity": "sha512-9bl0k4MgBPZu3Qu3R3xy12rmbW17e3bE9yf4YY85gJIQ3ezLEj/uzpKHWBsLaDoL5Mozz8QCgggwIBudYQWeQg==" + }, "@sapphire/async-queue@1.5.5": { "integrity": "sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg==" }, @@ -94,6 +152,14 @@ "@vladfrangu/async_event_emitter@2.4.6": { "integrity": "sha512-RaI5qZo6D2CVS6sTHFKg1v5Ohq/+Bo2LZ5gzUEwZ/WkHhwtGTCB/sVLw8ijOkAUxasZ+WshN/Rzj4ywsABJ5ZA==" }, + "cainode@1.2.2": { + "integrity": "sha512-x73+JgjAJOjuNJYpRXqbET86sDuEY8pcCmC55oCZWi3Vfh9wxoPWWldTxWhmuYD2A71jGNu20UB8A70nf3Q43A==", + "optionalDependencies": [ + "@livekit/rtc-node", + "ws" + ], + "scripts": true + }, "discord-api-types@0.38.12": { "integrity": "sha512-vqkRM50N5Zc6OVckAqtSslbUEoXmpN4bd9xq2jkoK9fgO3KNRIOyMMQ7ipqjwjKuAgzWvU6G8bRIcYWaUe1sCA==" }, @@ -140,7 +206,11 @@ "integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==" }, "ws@8.18.2": { - "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==" + "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", + "optionalPeers": [ + "bufferutil@^4.0.1", + "utf-8-validate@>=5.0.2" + ] } } } diff --git a/main.ts b/main.ts deleted file mode 100644 index 6ace994..0000000 --- a/main.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { - Client as _client, - ClientOptions, - Collection, - Events, - GatewayIntentBits, - MessageFlags, -} from "npm:discord.js"; -const token = Deno.env.get("token"); -import { Command } from "./types.ts"; -import { gatherCommandsPaths } from "./commands.ts"; - - -class Client extends _client { - commands: Collection; - constructor(options: ClientOptions) { - super(options); - this.commands = new Collection(); - } -} - -const client = new Client({ intents: [GatewayIntentBits.Guilds] }); - -async function register(commandPath: string) { - const { default: command }: { default: Command } = await import(commandPath); - if ("data" in command && "execute" in command) { - client.commands.set(command.data.name, command); - console.log(`Registered ${command.data.name}.`); - } else { - console.log( - `[WARNING] The command at ${commandPath} is missing a required "data" or "execute" property.` - ); - } -} - -(await gatherCommandsPaths()).forEach(register) - -client.once(Events.ClientReady, (readyClient) => { - console.log(`Ready! Logged in as ${readyClient.user.tag}`); -}); - -client.on(Events.InteractionCreate, async interaction => { - if (!interaction.isChatInputCommand()) return; - - const command = client.commands.get(interaction.commandName); - - if (!command) { - console.error(`No command matching ${interaction.commandName} was found.`); - return; - } - - try { - await command.execute(interaction); - } catch (error) { - console.error(error); - if (interaction.replied || interaction.deferred) { - await interaction.followUp({ content: 'There was an error while executing this command!', flags: MessageFlags.Ephemeral }); - } else { - await interaction.reply({ content: 'There was an error while executing this command!', flags: MessageFlags.Ephemeral }); - } - } -}); - -client.login(token); diff --git a/scripts/deployCommands.ts b/scripts/deployCommands.ts index 0df4752..f333711 100644 --- a/scripts/deployCommands.ts +++ b/scripts/deployCommands.ts @@ -1,7 +1,7 @@ import { REST, RESTPostAPIChatInputApplicationCommandsJSONBody, Routes } from "npm:discord.js"; const { clientId, guildId, token } = Deno.env.toObject(); -import { Command } from "../types.ts"; -import { gatherCommandsPaths } from "../commands.ts"; +import { Command } from "../src/types.ts"; +import { gatherCommandsPaths } from "../src/commands.ts"; const commands: RESTPostAPIChatInputApplicationCommandsJSONBody[] = []; async function register(commandPath: string) { diff --git a/src/cai.ts b/src/cai.ts new file mode 100644 index 0000000..c28a9ec --- /dev/null +++ b/src/cai.ts @@ -0,0 +1,3 @@ +import { CAINode } from "npm:cainode@latest"; + +export default new CAINode() \ No newline at end of file diff --git a/commands.ts b/src/commands.ts similarity index 100% rename from commands.ts rename to src/commands.ts diff --git a/commands/template.ts b/src/commands/template.ts similarity index 100% rename from commands/template.ts rename to src/commands/template.ts diff --git a/src/commands/utils/login.ts b/src/commands/utils/login.ts new file mode 100644 index 0000000..561545c --- /dev/null +++ b/src/commands/utils/login.ts @@ -0,0 +1,42 @@ +import { + CacheType, + ChatInputCommandInteraction, + MessageFlags, + SlashCommandBuilder, +} from "npm:discord.js"; +import cai from "../../cai.ts"; + +async function getToken() { + try { + await Deno.stat("./cai.token"); + return await Deno.readFile("./cai.token"); + } catch { + const token: string = await cai.generate_token("charcord.bot@gmail.com", 0); + await Deno.writeTextFile("./cai.token", token); + return token; + } +} + +export default { + data: new SlashCommandBuilder() + .setName("login") + .setDescription("Login to character.ai"), + async execute(interaction: ChatInputCommandInteraction) { + if(cai.logged) { + await cai.logout() + } + await interaction.reply({ + content: "Logging in...", + flags: MessageFlags.Ephemeral, + }); + const token = await getToken(); + await cai.login(token); + console.log( + `Logged in character.ai as ${cai.user.info.user.user.username}` + ); + await interaction.followUp({ + content: `Logged in character.ai as ${cai.user.info.user.user.username}`, + flags: MessageFlags.Ephemeral, + }); + }, +}; diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..c36dae3 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,76 @@ +import { + Client as _client, + ClientOptions, + Collection, + Events, + GatewayIntentBits, + MessageFlags, +} from "npm:discord.js"; +const token = Deno.env.get("token"); +import { Command } from "./types.ts"; +import { gatherCommandsPaths } from "./commands.ts"; +import { exists } from "jsr:@std/fs/exists"; +import cai from "./cai.ts"; + +class Client extends _client { + commands: Collection; + constructor(options: ClientOptions) { + super(options); + this.commands = new Collection(); + } +} + +const client = new Client({ intents: [GatewayIntentBits.Guilds] }); + +async function register(commandPath: string) { + const { default: command }: { default: Command } = await import(commandPath); + if ("data" in command && "execute" in command) { + client.commands.set(command.data.name, command); + } else { + console.log( + `[WARNING] The command at ${commandPath} is missing a required "data" or "execute" property.` + ); + } +} + +(await gatherCommandsPaths()).forEach(register); + +client.on(Events.InteractionCreate, async (interaction) => { + if (!interaction.isChatInputCommand()) return; + + const command = client.commands.get(interaction.commandName); + + if (!command) { + console.error(`No command matching ${interaction.commandName} was found.`); + return; + } + + try { + await command.execute(interaction); + } catch (error) { + console.error(error); + if (interaction.replied || interaction.deferred) { + await interaction.followUp({ + content: "There was an error while executing this command!", + flags: MessageFlags.Ephemeral, + }); + } else { + await interaction.reply({ + content: "There was an error while executing this command!", + flags: MessageFlags.Ephemeral, + }); + } + } +}); + +client.once(Events.ClientReady, (readyClient) => { + console.log(`Ready! Logged in as ${readyClient.user.tag}`); +}); + +if (await exists("./cai.token", { isFile: true })) { + await cai.login(await Deno.readTextFile("./cai.token")); + cai.logged = true; + console.log(`Logged in character.ai as ${cai.user.info.user.user.username}`); +} + +client.login(token); diff --git a/types.ts b/src/types.ts similarity index 100% rename from types.ts rename to src/types.ts diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..e69de29