generated from nhcarrigan/template
wip: commands
This commit is contained in:
@@ -0,0 +1,86 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "onboard-mentee",
|
||||||
|
"description": "Onboard a new mentee to the nhcarrigan-mentorship GitHub org",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"name": "mentee",
|
||||||
|
"description": "The mentee's Discord user",
|
||||||
|
"type": 6,
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "mentee_name",
|
||||||
|
"description": "The mentee's full name",
|
||||||
|
"type": 3,
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "github_username",
|
||||||
|
"description": "The mentee's GitHub username",
|
||||||
|
"type": 3,
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "create-task",
|
||||||
|
"description": "Create an AI-augmented task on Leantime",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"name": "title",
|
||||||
|
"description": "The task title",
|
||||||
|
"type": 3,
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "description",
|
||||||
|
"description": "Additional context for the task (AI will expand this)",
|
||||||
|
"type": 3,
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "priority",
|
||||||
|
"description": "Task priority level",
|
||||||
|
"type": 4,
|
||||||
|
"required": false,
|
||||||
|
"choices": [
|
||||||
|
{ "name": "Urgent", "value": 1 },
|
||||||
|
{ "name": "High", "value": 2 },
|
||||||
|
{ "name": "Medium", "value": 3 },
|
||||||
|
{ "name": "Low", "value": 4 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "create-issue",
|
||||||
|
"description": "Create an AI-augmented issue on a Gitea repository",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"name": "owner",
|
||||||
|
"description": "The repository owner",
|
||||||
|
"type": 3,
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "repo",
|
||||||
|
"description": "The repository name",
|
||||||
|
"type": 3,
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "title",
|
||||||
|
"description": "The issue title",
|
||||||
|
"type": 3,
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "description",
|
||||||
|
"description": "Additional context for the issue (AI will expand this)",
|
||||||
|
"type": 3,
|
||||||
|
"required": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
"typescript": "5.9.3"
|
"typescript": "5.9.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@anthropic-ai/sdk": "0.78.0",
|
||||||
"@nhcarrigan/discord-analytics": "0.0.6",
|
"@nhcarrigan/discord-analytics": "0.0.6",
|
||||||
"@nhcarrigan/logger": "1.1.1",
|
"@nhcarrigan/logger": "1.1.1",
|
||||||
"@retroachievements/api": "2.10.0",
|
"@retroachievements/api": "2.10.0",
|
||||||
|
|||||||
Generated
+36
@@ -8,6 +8,9 @@ importers:
|
|||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@anthropic-ai/sdk':
|
||||||
|
specifier: 0.78.0
|
||||||
|
version: 0.78.0
|
||||||
'@nhcarrigan/discord-analytics':
|
'@nhcarrigan/discord-analytics':
|
||||||
specifier: 0.0.6
|
specifier: 0.0.6
|
||||||
version: 0.0.6(@nhcarrigan/logger@1.1.1)(discord.js@14.22.0)
|
version: 0.0.6(@nhcarrigan/logger@1.1.1)(discord.js@14.22.0)
|
||||||
@@ -54,6 +57,15 @@ importers:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
|
'@anthropic-ai/sdk@0.78.0':
|
||||||
|
resolution: {integrity: sha512-PzQhR715td/m1UaaN5hHXjYB8Gl2lF9UVhrrGrZeysiF6Rb74Wc9GCB8hzLdzmQtBd1qe89F9OptgB9Za1Ib5w==}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
zod: ^3.25.0 || ^4.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
zod:
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@babel/code-frame@7.27.1':
|
'@babel/code-frame@7.27.1':
|
||||||
resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
|
resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
@@ -62,6 +74,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
|
resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
'@babel/runtime@7.28.6':
|
||||||
|
resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
'@discordjs/builders@1.11.3':
|
'@discordjs/builders@1.11.3':
|
||||||
resolution: {integrity: sha512-p3kf5eV49CJiRTfhtutUCeivSyQ/l2JlKodW1ZquRwwvlOWmG9+6jFShX6x8rUiYhnP6wKI96rgN/SXMy5e5aw==}
|
resolution: {integrity: sha512-p3kf5eV49CJiRTfhtutUCeivSyQ/l2JlKodW1ZquRwwvlOWmG9+6jFShX6x8rUiYhnP6wKI96rgN/SXMy5e5aw==}
|
||||||
engines: {node: '>=16.11.0'}
|
engines: {node: '>=16.11.0'}
|
||||||
@@ -1610,6 +1626,10 @@ packages:
|
|||||||
json-schema-ref-resolver@2.0.1:
|
json-schema-ref-resolver@2.0.1:
|
||||||
resolution: {integrity: sha512-HG0SIB9X4J8bwbxCbnd5FfPEbcXAJYTi1pBJeP/QPON+w8ovSME8iRG+ElHNxZNX2Qh6eYn1GdzJFS4cDFfx0Q==}
|
resolution: {integrity: sha512-HG0SIB9X4J8bwbxCbnd5FfPEbcXAJYTi1pBJeP/QPON+w8ovSME8iRG+ElHNxZNX2Qh6eYn1GdzJFS4cDFfx0Q==}
|
||||||
|
|
||||||
|
json-schema-to-ts@3.1.1:
|
||||||
|
resolution: {integrity: sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==}
|
||||||
|
engines: {node: '>=16'}
|
||||||
|
|
||||||
json-schema-traverse@0.4.1:
|
json-schema-traverse@0.4.1:
|
||||||
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
|
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
|
||||||
|
|
||||||
@@ -2176,6 +2196,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==}
|
resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
ts-algebra@2.0.0:
|
||||||
|
resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==}
|
||||||
|
|
||||||
ts-api-utils@1.4.3:
|
ts-api-utils@1.4.3:
|
||||||
resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==}
|
resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==}
|
||||||
engines: {node: '>=16'}
|
engines: {node: '>=16'}
|
||||||
@@ -2392,6 +2415,10 @@ packages:
|
|||||||
|
|
||||||
snapshots:
|
snapshots:
|
||||||
|
|
||||||
|
'@anthropic-ai/sdk@0.78.0':
|
||||||
|
dependencies:
|
||||||
|
json-schema-to-ts: 3.1.1
|
||||||
|
|
||||||
'@babel/code-frame@7.27.1':
|
'@babel/code-frame@7.27.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/helper-validator-identifier': 7.27.1
|
'@babel/helper-validator-identifier': 7.27.1
|
||||||
@@ -2400,6 +2427,8 @@ snapshots:
|
|||||||
|
|
||||||
'@babel/helper-validator-identifier@7.27.1': {}
|
'@babel/helper-validator-identifier@7.27.1': {}
|
||||||
|
|
||||||
|
'@babel/runtime@7.28.6': {}
|
||||||
|
|
||||||
'@discordjs/builders@1.11.3':
|
'@discordjs/builders@1.11.3':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@discordjs/formatters': 0.6.1
|
'@discordjs/formatters': 0.6.1
|
||||||
@@ -4193,6 +4222,11 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
dequal: 2.0.3
|
dequal: 2.0.3
|
||||||
|
|
||||||
|
json-schema-to-ts@3.1.1:
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 7.28.6
|
||||||
|
ts-algebra: 2.0.0
|
||||||
|
|
||||||
json-schema-traverse@0.4.1: {}
|
json-schema-traverse@0.4.1: {}
|
||||||
|
|
||||||
json-schema-traverse@1.0.0: {}
|
json-schema-traverse@1.0.0: {}
|
||||||
@@ -4813,6 +4847,8 @@ snapshots:
|
|||||||
|
|
||||||
toad-cache@3.7.0: {}
|
toad-cache@3.7.0: {}
|
||||||
|
|
||||||
|
ts-algebra@2.0.0: {}
|
||||||
|
|
||||||
ts-api-utils@1.4.3(typescript@5.9.3):
|
ts-api-utils@1.4.3(typescript@5.9.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
|
|||||||
@@ -6,4 +6,7 @@ GH_PRIVATE_KEY="op://Environment Variables - Naomi/Amari/gh private key"
|
|||||||
GH_WEBHOOK_SECRET="op://Environment Variables - Naomi/Amari/gh webhook secret"
|
GH_WEBHOOK_SECRET="op://Environment Variables - Naomi/Amari/gh webhook secret"
|
||||||
BASEROW_SECRET="op://Environment Variables - Naomi/Amari/baserow hook auth"
|
BASEROW_SECRET="op://Environment Variables - Naomi/Amari/baserow hook auth"
|
||||||
BASEROW_TOKEN="op://Environment Variables - Naomi/Amari/baserow token"
|
BASEROW_TOKEN="op://Environment Variables - Naomi/Amari/baserow token"
|
||||||
RA_KEY="op://Environment Variables - Naomi/Amari/retroachievements key"
|
RA_KEY="op://Environment Variables - Naomi/Amari/retroachievements key"
|
||||||
|
LEANTIME_KEY="op://Environment Variables - Naomi/Amari/leantime key"
|
||||||
|
GITEA_KEY="op://Environment Variables - Naomi/Amari/gitea key"
|
||||||
|
ANTHROPIC_KEY="op://Environment Variables - Naomi/Hikari/anthropic_key"
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/**
|
||||||
|
* @copyright NHCarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { MessageFlags } from "discord.js";
|
||||||
|
import { ids } from "../config/ids.js";
|
||||||
|
import { anthropic } from "../utils/anthropic.js";
|
||||||
|
import { logger } from "../utils/logger.js";
|
||||||
|
import type { ChatInputCommandInteraction } from "discord.js";
|
||||||
|
|
||||||
|
interface GiteaIssueResponse {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention -- Gitea API field.
|
||||||
|
html_url: string;
|
||||||
|
number: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param interaction
|
||||||
|
*/
|
||||||
|
export const createIssue = 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 owner = interaction.options.getString("owner", true);
|
||||||
|
const repo = interaction.options.getString("repo", true);
|
||||||
|
const title = interaction.options.getString("title", true);
|
||||||
|
const description = interaction.options.getString("description") ?? "";
|
||||||
|
|
||||||
|
await interaction.deferReply({ ephemeral: true });
|
||||||
|
|
||||||
|
const aiResponse = await anthropic.messages.create({
|
||||||
|
max_tokens: 1000,
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
content: `Create a clear, detailed issue body for a software project.\n\nIssue title: ${title}${description
|
||||||
|
? `\nAdditional context: ${description}`
|
||||||
|
: ""}`,
|
||||||
|
role: "user",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
model: "claude-haiku-4-5-20251001",
|
||||||
|
system: "You are a helpful assistant that creates well-structured Gitea issue bodies in markdown. Include relevant sections like 'Description' and 'Acceptance Criteria'. Be clear and actionable. Return only the body text.",
|
||||||
|
});
|
||||||
|
|
||||||
|
const firstContent = aiResponse.content[0];
|
||||||
|
const augmentedBody = firstContent.type === "text"
|
||||||
|
? firstContent.text
|
||||||
|
: description;
|
||||||
|
|
||||||
|
const response = await fetch(
|
||||||
|
`https://git.nhcarrigan.com/api/v1/repos/${owner}/${repo}/issues`,
|
||||||
|
{
|
||||||
|
body: JSON.stringify({
|
||||||
|
body: augmentedBody,
|
||||||
|
title,
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
"Authorization": `Bearer ${process.env.GITEA_KEY ?? ""}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
method: "POST",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorText = await response.text();
|
||||||
|
await interaction.editReply({
|
||||||
|
content: `❌ Failed to create issue: ${errorText}`,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data: GiteaIssueResponse = await response.json();
|
||||||
|
|
||||||
|
await logger.metric("created_issue", 1, {
|
||||||
|
repository: `${owner}/${repo}`,
|
||||||
|
title,
|
||||||
|
});
|
||||||
|
await interaction.editReply({
|
||||||
|
content: `✅ Issue #${data.number.toString()} created: **${title}**\n${data.html_url}`,
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
/**
|
||||||
|
* @copyright NHCarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { MessageFlags } from "discord.js";
|
||||||
|
import { ids } from "../config/ids.js";
|
||||||
|
import { anthropic } from "../utils/anthropic.js";
|
||||||
|
import { logger } from "../utils/logger.js";
|
||||||
|
import type { ChatInputCommandInteraction } from "discord.js";
|
||||||
|
|
||||||
|
interface LeantimeResponse {
|
||||||
|
error?: { message: string };
|
||||||
|
result?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param 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 });
|
||||||
|
|
||||||
|
const aiResponse = await anthropic.messages.create({
|
||||||
|
max_tokens: 500,
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
content: `Create a clear, concise task description for a personal productivity board.\n\nTask title: ${title}${description
|
||||||
|
? `\nAdditional context: ${description}`
|
||||||
|
: ""}`,
|
||||||
|
role: "user",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
model: "claude-haiku-4-5-20251001",
|
||||||
|
system: "You are a helpful assistant that creates well-structured task descriptions. Be concise and actionable. Return only the description text with no extra formatting or headers.",
|
||||||
|
});
|
||||||
|
|
||||||
|
const firstContent = aiResponse.content[0];
|
||||||
|
const augmentedDesc = firstContent.type === "text"
|
||||||
|
? firstContent.text
|
||||||
|
: description;
|
||||||
|
|
||||||
|
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: augmentedDesc,
|
||||||
|
|
||||||
|
editorId: "1",
|
||||||
|
headline: title,
|
||||||
|
priority: priority.toString(),
|
||||||
|
|
||||||
|
projectId: "1",
|
||||||
|
type: "task",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"x-api-key": process.env.LEANTIME_KEY ?? "",
|
||||||
|
},
|
||||||
|
method: "POST",
|
||||||
|
});
|
||||||
|
|
||||||
|
const data: LeantimeResponse = await response.json();
|
||||||
|
|
||||||
|
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/dashboard/home#/tickets/showTicket/${taskId.toString()}`
|
||||||
|
: "https://board.nhcarrigan.com";
|
||||||
|
|
||||||
|
await logger.metric("created_task", 1, { title });
|
||||||
|
await interaction.editReply({
|
||||||
|
content: `✅ Task created: **${title}**\n${taskUrl}`,
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
/**
|
||||||
|
* @copyright NHCarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { MessageFlags } from "discord.js";
|
||||||
|
import { ids } from "../config/ids.js";
|
||||||
|
import { logger } from "../utils/logger.js";
|
||||||
|
import type { Amari } from "../interfaces/amari.js";
|
||||||
|
import type { ChatInputCommandInteraction } from "discord.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param amari
|
||||||
|
* @param interaction
|
||||||
|
*/
|
||||||
|
export const onboardMentee = async(
|
||||||
|
amari: Amari,
|
||||||
|
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 menteeName = interaction.options.getString("mentee_name", true);
|
||||||
|
const githubUsername = interaction.options.getString("github_username", true);
|
||||||
|
const menteeUser = interaction.options.getUser("mentee", true);
|
||||||
|
const discordId = menteeUser.id;
|
||||||
|
|
||||||
|
await interaction.deferReply({ ephemeral: true });
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { data: installation } = await amari.githubApp.octokit.rest.apps.getOrgInstallation({
|
||||||
|
org: "nhcarrigan-mentorship",
|
||||||
|
});
|
||||||
|
const mentorshipOctokit = await amari.githubApp.getInstallationOctokit(installation.id);
|
||||||
|
|
||||||
|
let repoUrl: string;
|
||||||
|
try {
|
||||||
|
const { data: repoData } = await mentorshipOctokit.rest.repos.createInOrg({
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention -- Octokit API field.
|
||||||
|
auto_init: true,
|
||||||
|
name: githubUsername,
|
||||||
|
org: "nhcarrigan-mentorship",
|
||||||
|
});
|
||||||
|
repoUrl = repoData.html_url;
|
||||||
|
} catch {
|
||||||
|
repoUrl = `https://github.com/nhcarrigan-mentorship/${githubUsername}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
await mentorshipOctokit.rest.repos.addCollaborator({
|
||||||
|
owner: "nhcarrigan-mentorship",
|
||||||
|
permission: "maintain",
|
||||||
|
repo: githubUsername,
|
||||||
|
username: githubUsername,
|
||||||
|
});
|
||||||
|
|
||||||
|
const channel
|
||||||
|
= amari.discord.channels.cache.get(ids.channels.menteeChat)
|
||||||
|
?? await amari.discord.channels.fetch(ids.channels.menteeChat);
|
||||||
|
|
||||||
|
if (channel?.isSendable() !== true) {
|
||||||
|
await interaction.editReply({
|
||||||
|
content: "Repo created but could not send Discord notification.",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await channel.send({
|
||||||
|
content: `Hey <@${discordId}>! I've created your mentorship repository: ${repoUrl}\n\nYou should have received an invitation to collaborate - please accept it to get started!`,
|
||||||
|
});
|
||||||
|
|
||||||
|
await logger.metric("onboarded_mentee", 1, { mentee: menteeName });
|
||||||
|
await interaction.editReply({
|
||||||
|
content: `✅ Successfully onboarded **${menteeName}**!\nRepository: ${repoUrl}`,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
await logger.log("error", `Failed to onboard mentee: ${String(error)}`);
|
||||||
|
await interaction.editReply({
|
||||||
|
content: `❌ Failed to onboard mentee: ${String(error)}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -14,6 +14,9 @@ import {
|
|||||||
} from "discord.js";
|
} from "discord.js";
|
||||||
import { scheduleJob } from "node-schedule";
|
import { scheduleJob } from "node-schedule";
|
||||||
import { App } from "octokit";
|
import { App } from "octokit";
|
||||||
|
import { createIssue } from "./commands/createIssue.js";
|
||||||
|
import { createTask } from "./commands/createTask.js";
|
||||||
|
import { onboardMentee } from "./commands/onboardMentee.js";
|
||||||
import { ids } from "./config/ids.js";
|
import { ids } from "./config/ids.js";
|
||||||
import { handleMessageCreate } from "./events/handleMessageCreate.js";
|
import { handleMessageCreate } from "./events/handleMessageCreate.js";
|
||||||
import { cacheData } from "./modules/cacheData.js";
|
import { cacheData } from "./modules/cacheData.js";
|
||||||
@@ -60,6 +63,7 @@ const amari: Amari = {
|
|||||||
partials: [ Partials.Channel ],
|
partials: [ Partials.Channel ],
|
||||||
}),
|
}),
|
||||||
github: octokit,
|
github: octokit,
|
||||||
|
githubApp,
|
||||||
lastRssItems: {
|
lastRssItems: {
|
||||||
freeCodeCamp: null,
|
freeCodeCamp: null,
|
||||||
hackerNews: null,
|
hackerNews: null,
|
||||||
@@ -114,6 +118,18 @@ amari.discord.on(Events.InteractionCreate, (interaction) => {
|
|||||||
}
|
}
|
||||||
return void interaction.message.delete();
|
return void interaction.message.delete();
|
||||||
}
|
}
|
||||||
|
if (interaction.isChatInputCommand()) {
|
||||||
|
const { commandName } = interaction;
|
||||||
|
if (commandName === "onboard-mentee") {
|
||||||
|
return void onboardMentee(amari, interaction);
|
||||||
|
}
|
||||||
|
if (commandName === "create-task") {
|
||||||
|
return void createTask(interaction);
|
||||||
|
}
|
||||||
|
if (commandName === "create-issue") {
|
||||||
|
return void createIssue(interaction);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (interaction.isAutocomplete()) {
|
if (interaction.isAutocomplete()) {
|
||||||
return void interaction;
|
return void interaction;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import type { App } from "octokit";
|
|||||||
export interface Amari {
|
export interface Amari {
|
||||||
discord: Client;
|
discord: Client;
|
||||||
github: App["octokit"];
|
github: App["octokit"];
|
||||||
|
githubApp: App;
|
||||||
lastRssItems: {
|
lastRssItems: {
|
||||||
freeCodeCamp: string | null;
|
freeCodeCamp: string | null;
|
||||||
hackerNews: string | null;
|
hackerNews: string | null;
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* @copyright NHCarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Anthropic from "@anthropic-ai/sdk";
|
||||||
|
|
||||||
|
export const anthropic = new Anthropic({
|
||||||
|
apiKey: process.env.ANTHROPIC_KEY ?? "",
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user