generated from nhcarrigan/template
feat: add ability to clear or "reset" conversation (#2)
### Explanation _No response_ ### Issue _No response_ ### Attestations - [ ] I have read and agree to the [Code of Conduct](https://docs.nhcarrigan.com/community/coc/) - [ ] I have read and agree to the [Community Guidelines](https://docs.nhcarrigan.com/community/guide/). - [ ] My contribution complies with the [Contributor Covenant](https://docs.nhcarrigan.com/dev/covenant/). ### Dependencies - [ ] I have pinned the dependencies to a specific patch version. ### Style - [ ] I have run the linter and resolved any errors. - [ ] My pull request uses an appropriate title, matching the conventional commit standards. - [ ] My scope of feat/fix/chore/etc. correctly matches the nature of changes in my pull request. ### Tests - [ ] My contribution adds new code, and I have added tests to cover it. - [ ] My contribution modifies existing code, and I have updated the tests to reflect these changes. - [ ] All new and existing tests pass locally with my changes. - [ ] Code coverage remains at or above the configured threshold. ### Documentation _No response_ ### Versioning _No response_ Reviewed-on: #2 Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com> Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
This commit is contained in:
parent
1eed3afa99
commit
add485970b
24
src/commands/clear.ts
Normal file
24
src/commands/clear.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
ApplicationIntegrationType,
|
||||||
|
SlashCommandBuilder,
|
||||||
|
InteractionContextType,
|
||||||
|
} from "discord.js";
|
||||||
|
|
||||||
|
const command = new SlashCommandBuilder().
|
||||||
|
setContexts(
|
||||||
|
InteractionContextType.BotDM,
|
||||||
|
InteractionContextType.Guild,
|
||||||
|
InteractionContextType.PrivateChannel,
|
||||||
|
).
|
||||||
|
setIntegrationTypes(ApplicationIntegrationType.UserInstall).
|
||||||
|
setName("clear").
|
||||||
|
setDescription("Clear your current conversation so you can start a new one!");
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-console -- We don't need our logger here as this never runs in production.
|
||||||
|
console.log(JSON.stringify(command.toJSON()));
|
@ -14,13 +14,14 @@ import { personality } from "../config/personality.js";
|
|||||||
import { ai } from "../utils/ai.js";
|
import { ai } from "../utils/ai.js";
|
||||||
import { calculateCost } from "../utils/calculateCost.js";
|
import { calculateCost } from "../utils/calculateCost.js";
|
||||||
import { isSubscribedMessage } from "../utils/isSubscribed.js";
|
import { isSubscribedMessage } from "../utils/isSubscribed.js";
|
||||||
|
import { logger } from "../utils/logger.js";
|
||||||
import type { MessageParam } from "@anthropic-ai/sdk/resources/index.js";
|
import type { MessageParam } from "@anthropic-ai/sdk/resources/index.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the Discord message event.
|
* Handles the Discord message event.
|
||||||
* @param message - The message payload from Discord.
|
* @param message - The message payload from Discord.
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line max-lines-per-function -- We're off by one bloody line.
|
// eslint-disable-next-line max-lines-per-function, max-statements -- We're off by one bloody line.
|
||||||
export const onMessage = async(message: Message): Promise<void> => {
|
export const onMessage = async(message: Message): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
if (message.channel.type !== ChannelType.DM) {
|
if (message.channel.type !== ChannelType.DM) {
|
||||||
@ -33,9 +34,21 @@ export const onMessage = async(message: Message): Promise<void> => {
|
|||||||
if (!subbed) {
|
if (!subbed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const history = await message.channel.messages.fetch({ limit: 6 });
|
const historyRequest = await message.channel.messages.fetch({ limit: 20 });
|
||||||
const context: Array<MessageParam>
|
const history = [ ...historyRequest.values() ];
|
||||||
= history.reverse().map((messageInner) => {
|
const clearMessageIndex = history.findIndex((messageInner) => {
|
||||||
|
return (
|
||||||
|
messageInner.content === "<Clear History>"
|
||||||
|
&& messageInner.author.id === message.client.user.id
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (clearMessageIndex !== -1) {
|
||||||
|
// Remove the clear message and everything sent before it, which means everything after in the array because the array is backwards
|
||||||
|
history.splice(clearMessageIndex, history.length - clearMessageIndex);
|
||||||
|
}
|
||||||
|
const context: Array<MessageParam> = history.
|
||||||
|
reverse().
|
||||||
|
map((messageInner) => {
|
||||||
return {
|
return {
|
||||||
content: messageInner.content,
|
content: messageInner.content,
|
||||||
role:
|
role:
|
||||||
@ -61,8 +74,13 @@ export const onMessage = async(message: Message): Promise<void> => {
|
|||||||
response?.text ?? "There was an error. Please try again later.",
|
response?.text ?? "There was an error. Please try again later.",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!response) {
|
||||||
|
await logger.log("info", `No response from AI, here's the payload: ${JSON.stringify(messages)}`);
|
||||||
|
}
|
||||||
|
|
||||||
await calculateCost(messages.usage, message.author.username);
|
await calculateCost(messages.usage, message.author.username);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
await logger.error("message event", error as Error);
|
||||||
const button = new ButtonBuilder().
|
const button = new ButtonBuilder().
|
||||||
setLabel("Need help?").
|
setLabel("Need help?").
|
||||||
setStyle(ButtonStyle.Link).
|
setStyle(ButtonStyle.Link).
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
import { Client, Events, GatewayIntentBits, Partials } from "discord.js";
|
import { Client, Events, GatewayIntentBits, Partials } from "discord.js";
|
||||||
import { onMessage } from "./events/message.js";
|
import { onMessage } from "./events/message.js";
|
||||||
import { about } from "./modules/about.js";
|
import { about } from "./modules/about.js";
|
||||||
|
import { clear } from "./modules/clear.js";
|
||||||
import { instantiateServer } from "./server/serve.js";
|
import { instantiateServer } from "./server/serve.js";
|
||||||
import { logger } from "./utils/logger.js";
|
import { logger } from "./utils/logger.js";
|
||||||
|
|
||||||
@ -45,6 +46,9 @@ client.on(Events.InteractionCreate, (interaction) => {
|
|||||||
ephemeral: true,
|
ephemeral: true,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case "clear":
|
||||||
|
void clear(interaction);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
void interaction.reply({
|
void interaction.reply({
|
||||||
content: `I'm sorry, I don't know the ${interaction.commandName} command.`,
|
content: `I'm sorry, I don't know the ${interaction.commandName} command.`,
|
||||||
|
47
src/modules/clear.ts
Normal file
47
src/modules/clear.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
MessageFlags,
|
||||||
|
type ChatInputCommandInteraction,
|
||||||
|
} from "discord.js";
|
||||||
|
import { isSubscribedInteraction } from "../utils/isSubscribed.js";
|
||||||
|
import { logger } from "../utils/logger.js";
|
||||||
|
import { replyToError } from "../utils/replyToError.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a clear message in the DMs.
|
||||||
|
* @param interaction -- The interaction payload from Discord.
|
||||||
|
*/
|
||||||
|
export const clear = async(
|
||||||
|
interaction: ChatInputCommandInteraction,
|
||||||
|
): Promise<void> => {
|
||||||
|
try {
|
||||||
|
await interaction.deferReply({ flags: [ MessageFlags.Ephemeral ] });
|
||||||
|
|
||||||
|
const subbed = await isSubscribedInteraction(interaction);
|
||||||
|
if (!subbed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const sent = await interaction.user.send({
|
||||||
|
content: "<Clear History>",
|
||||||
|
}).catch(() => {
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
await interaction.editReply({
|
||||||
|
content: sent
|
||||||
|
? "I have added a clear history marker to your DMs."
|
||||||
|
// eslint-disable-next-line stylistic/max-len -- This is a long string.
|
||||||
|
: "I was unable to send you a DM. Please ensure your privacy settings allow direct messages.",
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
await replyToError(interaction);
|
||||||
|
if (error instanceof Error) {
|
||||||
|
await logger.error("about command", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user