break up library, move bots to their own repositories

This commit is contained in:
JB
2026-02-12 21:05:15 -05:00
parent 415aa3dbfe
commit cb39431a11
289 changed files with 1931 additions and 6235 deletions

View File

@@ -0,0 +1,193 @@
# Discord
Star Kitten's discord functionality is built on top of [Project Dysnomia](https://github.com/projectdysnomia/dysnomia) to provide up to date and performance interactions with the Discord API.
## Create a bot
Running a bot with Star Kitten is as simple as calling startBot()!
_src/main.ts_
```ts
import { startBot } from '@star-kitten/lib/discord';
startBot({
token: 'XXXXXX', // can be omitted if you set a DISCORD_BOT_TOKEN environment variable
});
```
You can configure the bot further through environment variables:
_I recommend using dotenvx to encrypt your .env files rather than using plaintext .env files_
```.env
# Required
DISCORD_BOT_TOKEN="XXXXXX" # https://discord.com/developers/applications
# Optional
DEBUG="true" # Enable debug mode
NODE_ENV="development"
LOG_LEVEL="debug"
RESET_COMMANDS="true" # "true" or NODE_ENV === "development" will have the bot delete and register all commands on every startup, useful for development
STAR_KITTEN_KV_DB_PATH="./kv.db" # Allows the bot to remember commands between restarts for resume functionality and recovery from crashes. If not set, :memory: is used so commands created before any restart will no longer function
# if using EVE ESI functionality
EVE_CLIENT_ID="XXXXX"
EVE_CLIENT_SECRET="XXXX"
EVE_CALLBACK_URL="http://my.callback.url"
ESI_USER_AGENT="XXXX" # provided with each ESI request so CCP knows who to contact if there is any issue
JANICE_KEY="XXXXX" # If you have one for using janice to get pricing data or appraisals
# AI Features
PERPLEXITY_API_KEY="XXXXX"
```
Although, a bot without any commands isn't very useful, so on to Commands
## Commands
Star Kitten will automatically register commands for you at startup. By default, any file in your `./src` directory that matches the pattern `**/*.command.{js,ts,jsx,tsx}`, like `./src/ping.command.ts` or `./src/commands/time.command.tsx`.
### Command File
A command file's exports must match the [CommandHandler](../src/discord/commands/command-handler.ts#14) structure.
definition: Defines the command to Discord, providing the command name, description, parameters and more.
execute: Function that is ran when this command is executed. The execute method will get called for every interaction associated with that command, so ensure you properly handle different interaction types, like Autocomplete vs. ApplicationCommand.
Example:
```tsx
import { Constants, type ChatInputApplicationCommandStructure } from '@projectdysnomia/dysnomia';
import { type ExecutableInteraction, type CommandContext, isApplicationCommand, Locale } from '@star-kitten/lib/discord';
const definition: ChatInputApplicationCommandStructure = {
type: Constants.ApplicationCommandTypes.CHAT_INPUT,
name: 'time',
nameLocalizations: {
[Locale.DE]: 'zeit',
},
description: 'Get the current EVE time',
descriptionLocalizations: {
[Locale.DE]: 'Holen Sie sich die aktuelle EVE-Zeit',
},
};
const eveTimeText = {
[Locale.EN_US]: 'EVE Time',
[Locale.DE]: 'EVE-Zeit',
};
function renderTimeDisplay(locale: string = 'en-US') {
const now = new Date();
const eveTime = now.toISOString().split('T')[1].split('.')[0];
const eveDate = now.toLocaleDateString(locale, {
timeZone: 'UTC',
year: 'numeric',
month: 'long',
day: '2-digit',
weekday: 'long',
});
return (
<container>
<textDisplay>
{`### ${eveTimeText[locale] || eveTimeText[Locale.EN_US]}
${eveTime}
${eveDate}`}
</textDisplay>
</container>
);
}
async function execute(interaction: ExecutableInteraction, ctx: CommandContext) {
if (!isApplicationCommand(interaction)) return;
interaction.createMessage({
flags: Constants.MessageFlags.IS_COMPONENTS_V2,
components: [renderTimeDisplay(interaction.locale)],
});
}
export default {
definition,
execute,
};
```
## Components V2
Star Kitten commands should utilize components v2. We also have a JSX syntax for creating commands. Look at the examples below to compare using JSX vs plain JavaScript objecst to define your components.
### JSX Components
```tsx
export function renderAppraisalModal(interaction: Interaction) {
return (
<modal customId="appraisalResult" title="Appraise Items">
<label label="Select a market">
<stringSelect customId="market" placeholder="Select a market" minValues={1} maxValues={1}>
{markets.map((m) => (
<option
label={m.name}
value={m.id.toString()}
default={m.id === 2} // Jita
/>
))}
</stringSelect>
</label>
<label label="Enter items to appraise">
<textInput
customId="input"
isParagraph={true}
placeholder={`e.g. Tritanium 22222
Pyerite 8000
Mexallon 2444`}
/>
</label>
</modal>
);
}
```
### Plain JS Object
```ts
export function renderAppraisalModal(interaction: Interaction) {
return {
type: Constants.InteractionResponseTypes.MODAL,
custom_id: 'appraisalResult',
title: 'Appraise Items',
components: [
{
type: Constants.ComponentTypes.LABEL,
label: 'Select a market',
component: {
type: Constants.ComponentTypes.STRING_SELECT,
custom_id: 'market',
placeholder: 'Select a market',
min_values: 1,
max_values: 1,
options: markets.map((m) => ({
label: m.name,
value: m.id.toString(),
default: m.id === 2, // Jita
})),
},
},
{
type: Constants.ComponentTypes.LABEL,
label: 'Enter items to appraise',
component: {
type: Constants.ComponentTypes.TEXT_INPUT,
custom_id: 'input',
style: Constants.TextInputStyles.PARAGRAPH,
placeholder: `e.g. Tritanium 22222
Pyerite 8000
Mexallon 2444`,
},
},
],
};
}
```