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,23 @@
# tsdown-starter
A starter for creating a TypeScript package.
## Development
- Install dependencies:
```bash
npm install
```
- Run the unit tests:
```bash
npm run test
```
- Build the library:
```bash
npm run build
```

View File

@@ -0,0 +1,59 @@
{
"name": "@star-kitten/eve-discord",
"version": "0.0.1",
"description": "Star Kitten EVE Discord Library.",
"type": "module",
"license": "MIT",
"homepage": "https://git.f302.me/jb/star-kitten#readme",
"bugs": {
"url": "https://git.f302.me/jb/star-kitten/issues"
},
"repository": {
"type": "git",
"url": "git+https://git.f302.me/jb/star-kitten.git"
},
"author": "JB <j-b-3.deviate267@passmail.net>",
"files": [
"dist"
],
"main": "./dist/index.js",
"module": "./dist/index.js",
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.js",
"types": "./dist/index*.d.ts"
},
"./commands/*.js": {
"require": "./dist/commands/*.js",
"import": "./dist/commands/*.js",
"types": "./dist/types/commands/*.d.ts"
},
"./package.json": "./package.json"
},
"publishConfig": {
"access": "public"
},
"peerDependencies": {
"@projectdysnomia/dysnomia": "github:projectdysnomia/dysnomia#dev"
},
"devDependencies": {
"@types/bun": "^1.3.5",
"prettier-plugin-multiline-arrays": "^4.0.3",
"tsdown": "^0.14.2",
"typescript": "beta"
},
"dependencies": {
"@star-kitten/util": "link:@star-kitten/util",
"@star-kitten/eve": "link:@star-kitten/eve",
"@star-kitten/discord": "link:@star-kitten/discord"
},
"scripts": {
"build": "tsdown",
"dev": "tsdown --watch",
"link": "bun link",
"test": "bun test",
"typecheck": "tsc --noEmit",
"release": "bumpp && npm publish"
}
}

View File

@@ -0,0 +1,136 @@
import { appraiseItems, type Appraisal } from '@star-kitten/eve/third-party/janice.js';
import {
componentHasIdPrefix,
isModalLabel,
isModalSelect,
isModalTextInput,
} from '@star-kitten/discord/components';
import { createChatCommand, type CommandContext, type ExecutableInteraction, components as c, type PageContext, ButtonStyle } from '@star-kitten/discord';
import { PageType, usePages } from '@star-kitten/discord/pages';
import { Constants } from '@projectdysnomia/dysnomia';
import { markets } from '@star-kitten/eve/third-party/janice.js';
import { formatNumberToShortForm } from '@star-kitten/util';
export interface AppraisalState {
appraisal?: Appraisal;
}
function renderAppraisalModal() {
return c.modal({ custom_id: 'appraisalResult', title: 'Appraise Items' },
c.label({ label: 'Select a market' },
c.stringSelect('market', { placeholder: 'Select a market', min_values: 1, max_values: 1 },
...markets.map((m) => c.option({ label: m.name, value: m.id.toString(), default: m.id === 2 }))
),
),
c.label({ label: 'Enter items to appraise' },
c.input('input',{
isParagraph: true,
placeholder: `e.g. Tritanium 22222\nPyerite 8000\nMexallon 2444`,
}),
),
);
}
function renderAppraisalFollowUp(appraisal: Appraisal,
pageCtx: PageContext<AppraisalState>,
interaction: ExecutableInteraction
) {
const content = `
# [Appraisal ${appraisal.id} @ ${appraisal.market.name}](https://janice.e-351.com/a/${appraisal.id})
### Buy: \`${formatNumberToShortForm(appraisal.effectivePrices.totalBuyPrice)}\` ISK
### Split: \`${formatNumberToShortForm(appraisal.effectivePrices.totalSplitPrice)}\` ISK
### Sell: \`${formatNumberToShortForm(appraisal.effectivePrices.totalSellPrice)}\` ISK
-# Volume: ${formatNumberToShortForm(appraisal.totalPackagedVolume)}
\`\`\`
Buy: Sell: Qty: Item:
${appraisal.items.map((i) => `${formatNumberToShortForm(i.effectivePrices.buyPrice).padEnd(10)}${formatNumberToShortForm(i.effectivePrices.sellPrice).padEnd(10)}${formatNumberToShortForm(i.amount).padEnd(10)}${i.itemType.name}`).join('\n')}
\`\`\`
-# https://janice.e-351.com/a/${appraisal.id}
`;
return c.container({ accent: 0x1da57a },
c.text(content),
pageCtx.state.currentPage !== 'share' && c.actionRow(
c.button({ custom_id: 'share', label: 'Share in Channel', disabled: !interaction.channel?.id, style: ButtonStyle.PRIMARY }),
)
);
}
async function execute(interaction: ExecutableInteraction, ctx: CommandContext) {
return await usePages<AppraisalState>(
{
pages: {
appraiseModal: {
key: 'appraiseModal',
type: PageType.MODAL,
render: () => renderAppraisalModal(),
},
appraisalResult: {
key: 'appraisalResult',
render: async (pageCtx) => {
if (!interaction.isModalSubmit()) {
throw new Error('Expected a modal submit interaction for appraisalResult page');
}
let marketId = 2; // Default to Jita
let items = '';
interaction.data.components.forEach((comp) => {
if (isModalLabel(comp)) {
if (isModalSelect(comp.component) && componentHasIdPrefix(comp.component, `market`)) {
marketId = Number.parseInt(comp.component.values[0]) || marketId;
} else if (isModalTextInput(comp.component) && componentHasIdPrefix(comp.component, `input`)) {
items = comp.component.value || items;
}
}
});
const appraisal = await appraiseItems(items, marketId);
pageCtx.state.data.appraisal = appraisal;
return renderAppraisalFollowUp(appraisal, pageCtx, interaction);
},
},
share: {
key: 'share',
type: PageType.FOLLOWUP,
followUpFlags: Constants.MessageFlags.IS_COMPONENTS_V2,
render: (pageCtx) => {
const rendered = renderAppraisalFollowUp(pageCtx.state.data.appraisal!, pageCtx, interaction);
return rendered;
},
},
},
initialPage: 'appraiseModal',
timeout: 300, // 5 minutes
ephemeral: true,
},
interaction,
ctx,
);
}
export default createChatCommand({
name: 'appraise',
nameLocalizations: {
de: 'bewerten',
'es-ES': 'tasar',
fr: 'estimer',
ja: '査定',
ko: '감정',
ru: 'оценить',
'zh-CN': '评估',
},
description: 'Evaluate the worth of your space junk',
descriptionLocalizations: {
de: 'Bewerten Sie den Wert Ihres Weltraumschrotts',
'es-ES': 'Evalúa el valor de tu chatarra espacial',
fr: 'Évaluez la valeur de vos déchets spatiaux',
ja: 'あなたの宇宙のガラクタの価値を評価します',
ko: '우주 쓰레기의 가치를 평가하십시오',
ru: 'Оцените стоимость вашего космического мусора',
'zh-CN': '评估您宇宙垃圾的价值',
},
}, execute);

View File

@@ -0,0 +1,78 @@
import {
type ExecutableInteraction,
type CommandContext,
createChatCommand,
components,
options,
} from '@star-kitten/discord';
async function execute(interaction: ExecutableInteraction, context: CommandContext) {
if (!interaction.isApplicationCommand()) return;
const days = (interaction.data.options.find((option) => option.name === 'days')?.value || 0) as number;
const hours = (interaction.data.options.find((option) => option.name === 'hours')?.value || 0) as number;
const minutes = (interaction.data.options.find((option) => option.name === 'minutes')?.value || 0) as number;
const ephemeral = !(interaction.data.options.find((option) => option.name === 'public')?.value || false) as boolean;
const totalSeconds = days * 24 * 60 * 60 + hours * 60 * 60 + minutes * 60;
const now = new Date();
const futureTime = new Date(now.getTime() + totalSeconds * 1000);
const eveTime = futureTime.toISOString().split('T')[1].split('.')[0];
const eveDate = futureTime.toLocaleDateString(interaction.locale, {
timeZone: 'UTC',
year: 'numeric',
month: 'long',
day: '2-digit',
weekday: 'long',
});
const discordTimestamp = Math.floor(futureTime.getTime() / 1000);
const timestampCodes = [
`<t:${discordTimestamp}:f>`, // Full date and time
`<t:${discordTimestamp}:F>`, // Long date and time
`<t:${discordTimestamp}:T>`, // 24-hour time
`<t:${discordTimestamp}:R>`, // Relative time
];
const textContent = `In ${days ? `**${days}** day${days !== 1 ? 's' : ''}, ` : ''}${hours ? `**${hours}** hour${hours !== 1 ? 's' : ''}, ` : ''}${minutes ? `**${minutes}** minute${minutes !== 1 ? 's' : ''} ` : ''}from now, the EVE time will be:
${eveDate} at ${eveTime}
### Discord timestamp codes:
${timestampCodes.map((code) => `${code}\n\`\`\`${code}\`\`\``).join('\n')}
`;
interaction.createMessage(
components.componentsV2({ ephemeral }, components.container({}, components.text(textContent))),
);
}
export default createChatCommand(
{
name: 'time_from_now',
description: 'Calculate time from now in EVE format with Discord timestamp codes',
options: [
options.numberOption({
name: 'days',
description: 'Number of days to add',
required: false,
}),
options.numberOption({
name: 'hours',
description: 'Number of hours to add',
required: false,
}),
options.numberOption({
name: 'minutes',
description: 'Number of minutes to add',
required: false,
}),
options.booleanOption({
name: 'public',
description: 'Whether the response should be public (visible to everyone)',
required: false,
}),
],
},
execute,
);

View File

@@ -0,0 +1,62 @@
import {
type ExecutableInteraction,
type CommandContext,
Locale,
componentsV2,
container,
text,
createChatCommand,
} from '@star-kitten/discord';
const eveTimeText = {
[Locale.EN_US]: 'EVE Time',
[Locale.EN_GB]: 'EVE Time',
[Locale.DE]: 'EVE-Zeit',
[Locale.ES_ES]: 'Hora EVE',
[Locale.FR]: "Heure d'EVE",
[Locale.JA]: 'EVE時間',
[Locale.KO]: 'EVE 시간',
[Locale.RU]: 'Время EVE',
[Locale.ZH_CN]: 'EVE时间',
};
async function execute(interaction: ExecutableInteraction, ctx: CommandContext) {
if (!interaction.isApplicationCommand()) return;
const now = new Date();
const eveTime = now.toISOString().split('T')[1].split('.')[0];
const eveDate = now.toLocaleDateString(interaction.locale, {
timeZone: 'UTC',
year: 'numeric',
month: 'long',
day: '2-digit',
weekday: 'long',
});
interaction.createMessage(componentsV2({}, container({}, text(`### ${eveTimeText[interaction.locale] || eveTimeText[Locale.EN_US]}
${eveTime}
${eveDate}`))));
}
export default createChatCommand({
name: 'time',
nameLocalizations: {
[Locale.DE]: 'zeit',
[Locale.ES_ES]: 'hora',
[Locale.FR]: 'heure',
[Locale.JA]: '時間',
[Locale.KO]: '시간',
[Locale.RU]: 'время',
[Locale.ZH_CN]: '时间',
},
description: 'Get the current EVE time',
descriptionLocalizations: {
[Locale.DE]: 'Holen Sie sich die aktuelle EVE-Zeit',
[Locale.ES_ES]: 'Obtén la hora actual de EVE',
[Locale.FR]: "Obtenez l'heure actuelle d'EVE",
[Locale.JA]: '現在のEVE時間を取得します',
[Locale.KO]: '현재 EVE 시간을 가져옵니다',
[Locale.RU]: 'Получите текущее время EVE',
[Locale.ZH_CN]: '获取当前的EVE时间',
},
}, execute);

View File

@@ -0,0 +1,3 @@
export default function() {
console.log('Hello from eve-discord!');
}

View File

@@ -0,0 +1,21 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"composite": true,
"strict": false,
"noImplicitAny": false,
"skipLibCheck": true,
"paths": {
"@/*": ["./src/*"],
"@data/*": ["./data/*"],
},
"emitDeclarationOnly": true,
"noEmit": false,
"noEmitOnError": false,
"declaration": true,
"rootDir": ".",
"allowImportingTsExtensions": true
},
"include": ["src"],
"exclude": ["node_modules", "build", "**/*.test.ts"]
}

View File

@@ -0,0 +1,16 @@
import { defineConfig } from 'tsdown';
export default defineConfig([
{
entry: [
'./src/**/*.ts',
'!./src/**/*.test.ts',
],
platform: 'node',
dts: true,
minify: false,
sourcemap: true,
unbundle: true,
external: ['bun:sqlite', 'bun'],
},
]);