generated from nhcarrigan/template
5beebeff44
- Centralise all Claude API calls through a shared makeAiRequest module - Apply a consistent Amari personality to all AI-generated content - Add try/catch with logger.error to all AI and external API call sites - Extract postGiteaIssue helper in createIssue for better structure
132 lines
4.2 KiB
TypeScript
132 lines
4.2 KiB
TypeScript
/**
|
|
* @copyright NHCarrigan
|
|
* @license Naomi's Public License
|
|
* @author Naomi Carrigan
|
|
*/
|
|
|
|
import { MessageFlags, type ChatInputCommandInteraction } from "discord.js";
|
|
import { ids } from "../config/ids.js";
|
|
import { logger } from "../utils/logger.js";
|
|
import { makeAiRequest } from "../utils/makeAiRequest.js";
|
|
|
|
interface LeantimeResponse {
|
|
error?: { message: string };
|
|
result?: number;
|
|
}
|
|
|
|
const taskSystemPrompt = "Create well-structured task descriptions."
|
|
+ " Be concise and actionable."
|
|
+ " Return only the description text with no extra formatting or headers.";
|
|
|
|
/**
|
|
* Generates an AI-augmented task description.
|
|
* @param description - Optional additional context for the task.
|
|
* @param title - The subject of the Leantime task.
|
|
* @returns The generated task description text, or the original description as fallback.
|
|
*/
|
|
const generateTaskDescription = async(
|
|
description: string,
|
|
title: string,
|
|
): Promise<string> => {
|
|
const result = await makeAiRequest({
|
|
maxTokens: 500,
|
|
systemPrompt: taskSystemPrompt,
|
|
userMessage: `Create a clear, concise task description for a personal productivity board.\n\nTask title: ${title}${description === ""
|
|
? ""
|
|
: `\nAdditional context: ${description}`}`,
|
|
});
|
|
return result ?? description;
|
|
};
|
|
|
|
/**
|
|
* Posts a task to the Leantime board via JSON-RPC.
|
|
* @param description - Body copy for the Leantime task.
|
|
* @param priority - The task priority level.
|
|
* @param title - The headline for the Leantime task.
|
|
* @returns The Leantime API response.
|
|
*/
|
|
const postLeantimeTask = async(
|
|
description: string,
|
|
priority: number,
|
|
title: string,
|
|
): Promise<LeantimeResponse> => {
|
|
const response = await fetch("https://board.nhcarrigan.com/api/jsonrpc", {
|
|
body: JSON.stringify({
|
|
id: `amari-task-${Date.now().toString()}`,
|
|
jsonrpc: "2.0",
|
|
method: "leantime.rpc.tickets.addTicket",
|
|
params: {
|
|
values: {
|
|
description: description,
|
|
editorId: "1",
|
|
headline: title,
|
|
priority: priority.toString(),
|
|
projectId: "1",
|
|
type: "task",
|
|
},
|
|
},
|
|
}),
|
|
headers: {
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention -- HTTP header name.
|
|
"Content-Type": "application/json",
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention -- HTTP header name.
|
|
"x-api-key": process.env.LEANTIME_KEY ?? "",
|
|
},
|
|
method: "POST",
|
|
});
|
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Required to type Response.json() output.
|
|
const data = await response.json() as LeantimeResponse;
|
|
return data;
|
|
};
|
|
|
|
/**
|
|
* Creates a Leantime task using AI-augmented description content.
|
|
* @param interaction - The Discord slash command interaction.
|
|
*/
|
|
export const createTask = async(
|
|
interaction: ChatInputCommandInteraction,
|
|
): Promise<void> => {
|
|
if (interaction.user.id !== ids.users.naomi) {
|
|
await interaction.reply({
|
|
content: "This command is restricted to Naomi.",
|
|
flags: [ MessageFlags.Ephemeral ],
|
|
});
|
|
return;
|
|
}
|
|
|
|
const title = interaction.options.getString("title", true);
|
|
const description = interaction.options.getString("description") ?? "";
|
|
const priority = interaction.options.getInteger("priority") ?? 3;
|
|
|
|
await interaction.deferReply({ ephemeral: true });
|
|
|
|
try {
|
|
const augmentedDesc = await generateTaskDescription(description, title);
|
|
const data = await postLeantimeTask(augmentedDesc, priority, title);
|
|
|
|
if (data.error !== undefined) {
|
|
await interaction.editReply({
|
|
content: `❌ Failed to create task: ${data.error.message}`,
|
|
});
|
|
return;
|
|
}
|
|
|
|
const taskId = data.result;
|
|
const taskUrl = taskId === undefined
|
|
? "https://board.nhcarrigan.com"
|
|
: `https://board.nhcarrigan.com/dashboard/home#/tickets/showTicket/${taskId.toString()}`;
|
|
|
|
await logger.metric("created_task", 1, { title });
|
|
await interaction.editReply({
|
|
content: `✅ Task created: **${title}**\n${taskUrl}`,
|
|
});
|
|
} catch (error) {
|
|
if (error instanceof Error) {
|
|
await logger.error("createTask command", error);
|
|
}
|
|
await interaction.editReply({
|
|
content: "❌ An unexpected error occurred while creating the task.",
|
|
});
|
|
}
|
|
};
|