updates to concierge bot to support adding services and routes

This commit is contained in:
JB
2026-02-12 19:25:01 -05:00
parent e9865d3ee8
commit abc8070835
86 changed files with 2396 additions and 723 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "@star-kitten/lib",
"version": "0.0.0",
"version": "0.0.1",
"description": "Star Kitten Library.",
"type": "module",
"license": "MIT",
@@ -24,87 +24,81 @@
"require": "./dist/index.js",
"types": "./dist/index*.d.ts"
},
"./package.json": "./package.json",
"./util": {
"types": "./src/util/index.d.ts",
"require": "./src/util/index.js",
"types": "./dist/util/index.d.ts",
"require": "./dist/util/index.js",
"import": "./dist/util/index.js"
},
"./util/*": {
"types": "./src/util/**/*.d.ts",
"require": "./src/util/*",
"types": "./dist/util/*",
"require": "./dist/util/*",
"import": "./dist/util/*"
},
"./eve": {
"types": "./src/eve/index.d.ts",
"require": "./src/eve/index.js",
"types": "./dist/eve/index*.d.ts",
"require": "./dist/eve/index.js",
"import": "./dist/eve/index.js"
},
"./eve/*": {
"types": "./src/eve/**/*.d.ts",
"require": "./src/eve/*",
"import": "./dist/eve/*"
},
"./eve/esi": {
"import": "./dist/eve/esi/index.js",
"types": "./src/eve/esi/index*.d.ts",
"require": "./src/eve/esi/index.js"
"types": "./dist/eve/esi/index*.d.ts",
"require": "./dist/eve/esi/index.js"
},
"./eve/db": {
"import": "./dist/eve/db/index.js",
"types": "./src/eve/db/index*.d.ts",
"require": "./src/eve/db/index.js"
"types": "./dist/eve/db/index*.d.ts",
"require": "./dist/eve/db/index.js"
},
"./eve/ref": {
"import": "./dist/eve/ref/index.js",
"types": "./src/eve/ref/index*.d.ts",
"require": "./src/eve/ref/index.js"
"types": "./dist/eve/ref/index*.d.ts",
"require": "./dist/eve/ref/index.js"
},
"./eve/third-party/janice.js": {
"import": "./dist/eve/third-party/janice.js",
"types": "./dist/types/eve/third-party/janice.d.ts",
"require": "./src/eve/third-party/janice.js"
"require": "./dist/eve/third-party/janice.js"
},
"./eve/models": {
"import": "./dist/eve/models/index.js",
"types": "./src/eve/models/index*.d.ts",
"require": "./src/eve/models/index.js"
"types": "./dist/eve/models/index*.d.ts",
"require": "./dist/eve/models/index.js"
},
"./eve/data/*": "./data/*",
"./discord": {
"import": "./dist/discord/index.js",
"require": "./src/discord/index.js",
"require": "./dist/discord/index.js",
"types": "./dist/types/discord/index.d.ts"
},
"./discord/commands": {
"require": "./src/discord/commands/index.js",
"require": "./dist/discord/commands/index.js",
"import": "./dist/discord/commands/index.js",
"types": "./dist/types/discord/commands/index.d.ts"
},
"./discord/components": {
"types": "./dist/types/discord/components/index.d.ts",
"require": "./src/discord/components/index.js",
"require": "./dist/discord/components/index.js",
"import": "./dist/discord/components/index.js"
},
"./discord/pages": {
"require": "./src/discord/pages/index.js",
"require": "./dist/discord/pages/index.js",
"import": "./dist/discord/pages/index.js",
"types": "./dist/types/discord/pages/index.d.ts"
},
"./discord/common": {
"require": "./src/discord/common/index.js",
"require": "./dist/discord/common/index.js",
"import": "./dist/discord/common/index.js"
},
"./discord/jsx": {
"types": "./src/discord/jsx/types.d.ts",
"types": "./dist/discord/jsx/types.d.ts",
"import": "./dist/discord/jsx/index.js"
},
"./discord/jsx-runtime": {
"types": "./src/discord/jsx/types.d.ts",
"types": "./dist/discord/jsx/types.d.ts",
"default": "./dist/discord/jsx/jsx-runtime.js"
},
"./discord/jsx-dev-runtime": {
"types": "./src/discord/jsx/types.d.ts",
"types": "./dist/discord/jsx/types.d.ts",
"default": "./dist/discord/jsx/jsx-dev-runtime.js"
}
},
@@ -124,6 +118,8 @@
"bumpp": "^10.1.0",
"drizzle-kit": "^0.31.4",
"ghooks": "^2.0.4",
"openapi-fetch": "^0.15.0",
"openapi-typescript": "^7.10.1",
"prettier-plugin-multiline-arrays": "^4.0.3",
"tsdown": "^0.14.2",
"typescript": "^5.9.2"
@@ -158,10 +154,11 @@
"release": "bumpp && npm publish",
"generate-migrations": "bunx drizzle-kit generate --dialect sqlite --schema ./src/db/schema.ts",
"migrate": "bun run ./src/db/migrate.ts",
"postinstall": "bun get-data",
"get-data": "bun refresh:reference-data && bun refresh:hoboleaks",
"refresh:reference-data": "bun scripts/download-and-extract.ts https://data.everef.net/reference-data/reference-data-latest.tar.xz ./data/reference-data",
"refresh:hoboleaks": "bun scripts/download-and-extract.ts https://data.everef.net/hoboleaks-sde/hoboleaks-sde-latest.tar.xz ./data/hoboleaks",
"static-export": "bun scripts/export-solar-systems.ts"
"postinstall": "bun run get-data",
"everef-api": "bunx openapi-typescript https://raw.githubusercontent.com/autonomouslogic/eve-ref/refs/heads/main/spec/eve-ref-api.yaml -o src/eve/everef/schema.d.ts",
"get-data": "bun run refresh:reference-data && bun run refresh:hoboleaks",
"refresh:reference-data": "bun run scripts/download-and-extract.ts https://data.everef.net/reference-data/reference-data-latest.tar.xz ./data/reference-data",
"refresh:hoboleaks": "bun run scripts/download-and-extract.ts https://data.everef.net/hoboleaks-sde/hoboleaks-sde-latest.tar.xz ./data/hoboleaks",
"static-export": "bun run scripts/export-solar-systems.ts"
}
}

View File

@@ -1,15 +0,0 @@
import { type ChatInputApplicationCommandStructure } from '@projectdysnomia/dysnomia';
import type { ExecutableInteraction } from '../types/interaction.type';
import type { ChatCommandDefinition, CommandContext, CommandHandler } from '../types';
export function createChatCommand(
definition: ChatCommandDefinition,
execute: (interaction: ExecutableInteraction, ctx: CommandContext) => Promise<void>,
): CommandHandler<ChatInputApplicationCommandStructure> {
const def = definition as ChatInputApplicationCommandStructure;
def.type = 1; // CHAT_INPUT
return {
definition: def,
execute,
};
}

View File

@@ -7,6 +7,8 @@ import type {
ComponentInteraction,
ModalSubmitInteraction,
PingInteraction,
SelectMenuInteraction,
ButtonInteraction,
} from '../types';
export function isApplicationCommand(interaction: Interaction): interaction is CommandInteraction {
@@ -39,6 +41,7 @@ export function commandHasIdPrefix(interaction: Interaction, prefix: string): bo
export function getCommandName(interaction: ExecutableInteraction): string | undefined {
if (isApplicationCommand(interaction) || isAutocomplete(interaction)) {
console.log(`command name: ${interaction.data.name}`);
return interaction.data.name;
}
return undefined;
@@ -54,6 +57,12 @@ export function augmentInteraction(interaction: Interaction): Interaction {
interaction.isMessageComponent = function (): this is ComponentInteraction {
return interaction.type === Constants.InteractionTypes.MESSAGE_COMPONENT;
};
interaction.isSelectMenu = function (): this is SelectMenuInteraction {
return interaction.isMessageComponent() && interaction.data.component_type in [3, 5, 6, 7, 8];
};
interaction.isButton = function (): this is ButtonInteraction {
return interaction.isMessageComponent() && interaction.data.component_type === Constants.ComponentTypes.BUTTON;
};
interaction.isAutocomplete = function (): this is AutocompleteInteraction {
return interaction.type === Constants.InteractionTypes.APPLICATION_COMMAND_AUTOCOMPLETE;
};

View File

@@ -1,6 +1,5 @@
import { type InteractionModalContent, type Component, Constants } from '@projectdysnomia/dysnomia';
import type { CommandContext, PartialContext, ExecutableInteraction } from '../types';
import { int } from 'drizzle-orm/mysql-core';
import _ from 'lodash';
export function injectInteraction(interaction: ExecutableInteraction, ctx: PartialContext): [ExecutableInteraction, CommandContext] {

View File

@@ -0,0 +1,134 @@
import {
Constants,
type AutocompleteInteractionData,
type ChatInputApplicationCommandStructure,
type CommandInteractionData,
type InteractionDataOption,
type InteractionDataOptions,
type MessageApplicationCommandStructure,
type PrimaryEntryPointApplicationCommandStructure,
type UserApplicationCommandStructure,
} from '@projectdysnomia/dysnomia';
import type { ExecutableInteraction } from '../types/interaction.type';
import type { AuthorizedFn, ChatCommandDefinition, CommandContext, CommandOptions, ExecuteFn } from '../types';
import { addCommand } from './handle-commands';
export function createChatCommand(def: ChatCommandDefinition, execute: ExecuteFn, options: Partial<CommandOptions> = {}) {
const definition = def as ChatInputApplicationCommandStructure;
definition.type = Constants.ApplicationCommandTypes.CHAT_INPUT;
const command = {
definition,
execute,
options,
};
addCommand(command);
return command;
}
export function createUserAppCommand(
def: Omit<UserApplicationCommandStructure, 'type'>,
execute: ExecuteFn,
options: Partial<CommandOptions> = {},
) {
const definition = def as UserApplicationCommandStructure;
definition.type = Constants.ApplicationCommandTypes.USER;
const command = {
definition,
execute,
options,
};
addCommand(command);
return command;
}
export function createPrimaryEntryCommand(
def: Omit<PrimaryEntryPointApplicationCommandStructure, 'type'>,
execute: ExecuteFn,
options: Partial<CommandOptions> = {},
) {
const definition = def as PrimaryEntryPointApplicationCommandStructure;
definition.type = Constants.ApplicationCommandTypes.PRIMARY_ENTRY_POINT;
const command = {
definition,
execute,
options,
};
addCommand(command);
return command;
}
export function createMessageCommand(
def: Omit<MessageApplicationCommandStructure, 'type'>,
execute: ExecuteFn,
options: Partial<CommandOptions> = {},
) {
const definition = def as MessageApplicationCommandStructure;
definition.type = Constants.ApplicationCommandTypes.MESSAGE;
const command = {
definition,
execute,
options,
};
addCommand(command);
return command;
}
export function hasAnyRole(roleIds: string[]): AuthorizedFn {
return (interaction: ExecutableInteraction, ctx: CommandContext) => {
const member = interaction.member;
if (!member) return false;
for (let i = 0; i < roleIds.length; ++i) {
if (member.roles.indexOf(roleIds[i]) !== -1) {
return true;
}
}
return false;
};
}
export const Permissions = Constants.Permissions;
export function hasPermissions(permissions: bigint): AuthorizedFn {
return (interaction: ExecutableInteraction, ctx: CommandContext) => {
const member = interaction.member;
if (!member) return false;
return (member.permissions.allow & permissions) !== 0n;
};
}
export function hasUserId(userIds: string[]): AuthorizedFn {
return (interaction: ExecutableInteraction, ctx: CommandContext) => {
return userIds.indexOf(interaction.user?.id) !== -1;
};
}
export function isAdministrator(): AuthorizedFn {
return (interaction: ExecutableInteraction, ctx: CommandContext) => {
const member = interaction.member;
if (!member) return false;
return (member.permissions.allow & Permissions.administrator) !== 0n;
};
}
type SubCommandRouter = Record<string, SubCommandExecuteFn>;
type SubCommandExecuteFn = (
interaction: ExecutableInteraction,
ctx: CommandContext,
data: CommandInteractionData | AutocompleteInteractionData,
) => any | Promise<any>;
export function subCommandRouter(handlers: Record<string, SubCommandExecuteFn | SubCommandRouter>) {
return (interaction: ExecutableInteraction, ctx: CommandContext) => {
// console.log(JSON.stringify(interaction));
if (interaction.isMessageComponent() || interaction.isModalSubmit()) return;
const name = interaction.data.options[0].name;
if (handlers[name]) {
if (typeof handlers[name] === 'function') {
return handlers[name](interaction, ctx, interaction.data.options[0] as any);
} else {
const subName = (interaction.data.options[0] as any).options[0].name;
return handlers[name][subName](interaction, ctx, interaction.data.options[0].options[0]);
}
}
};
}

View File

@@ -3,14 +3,14 @@ import { handleCommands } from './handle-commands';
import { CommandInteraction, Constants, ModalSubmitInteraction, type ApplicationCommandStructure } from '@projectdysnomia/dysnomia';
import { CommandHandler } from '../types';
let commands: Record<string, CommandHandler<ApplicationCommandStructure>>;
let commands: Map<string, CommandHandler<ApplicationCommandStructure>>;
beforeEach(() => {
commands = {};
commands = new Map();
});
afterEach(() => {
commands = {};
commands = new Map();
});
mock.module('./command-helpers', () => ({
@@ -20,7 +20,7 @@ mock.module('./command-helpers', () => ({
test('handleCommands executes command when interaction is CommandInteraction and command exists', async () => {
const mockExecute = mock(() => Promise.resolve());
const mockCommand = { definition: { name: 'testCommand' } as any, execute: mockExecute };
commands['testCommand'] = mockCommand;
commands.set('testCommand', mockCommand);
const mockInteraction = {
type: Constants.InteractionTypes.APPLICATION_COMMAND,
@@ -36,7 +36,7 @@ test('handleCommands executes command when interaction is CommandInteraction and
test('handleCommands executes command when interaction is CommandInteraction and command exists', async () => {
const mockExecute = mock(() => Promise.resolve());
const mockCommand = { definition: { name: 'testCommand' } as any, execute: mockExecute };
commands['testCommand'] = mockCommand;
commands.set('testCommand', mockCommand);
const mockInteraction = {
type: Constants.InteractionTypes.MODAL_SUBMIT,

View File

@@ -1,47 +1,54 @@
import { type ApplicationCommandStructure } from '@projectdysnomia/dysnomia';
import { augmentInteraction, getCommandName } from './command-helpers';
import { Constants, type ApplicationCommandStructure } from '@projectdysnomia/dysnomia';
import { injectInteraction } from './command-injection';
import { getCommandState } from './command-state';
import { type ExecutableInteraction } from '../types/interaction.type';
import type { CommandHandler, PartialContext } from '../types';
import type { CommandContext, CommandHandler, CommandOptions, PartialContext } from '../types';
import { augmentInteraction, getCommandName } from './command-helpers';
import { awaitMaybePromise } from '@/util/promise';
export async function handleCommands(
interaction: ExecutableInteraction,
commands: Record<string, CommandHandler<ApplicationCommandStructure>>,
commands: Map<string, CommandHandler<ApplicationCommandStructure>>,
ctx: PartialContext,
) {
ctx.state = await getCommandState(interaction, ctx);
if (!ctx.state.name) {
ctx.state.name = getCommandName(interaction);
let commandContext = ctx as CommandContext;
commandContext.state = await getCommandState(interaction, commandContext);
if (!commandContext.state.name) {
commandContext.state.name = getCommandName(interaction);
}
if (interaction.isAutocomplete() && ctx.state.name) {
const acCommand = commands[ctx.state.name];
return acCommand.execute(interaction, ctx as any);
if (interaction.isAutocomplete() && commandContext.state.name) {
const acCommand = commands.get(commandContext.state.name);
if (acCommand.options?.authorize && !(await awaitMaybePromise(acCommand.options.authorize(interaction, commandContext)))) {
return;
}
return acCommand.execute(interaction, commandContext as any);
}
if (!ctx.state.id) {
if (!commandContext.state.id) {
console.error(`No command ID found for interaction ${interaction.id}`);
return;
}
const command = commands[ctx.state.name || ''];
const command = commands.get(commandContext.state.name || '');
if (!command) {
console.warn(`No command found for interaction: ${JSON.stringify(interaction, undefined, 2)}`);
return;
}
cleanInteractionCustomIds(interaction, ctx.state.id);
const [injectedInteraction, fullContext] = await injectInteraction(interaction, ctx);
return command.execute(injectedInteraction, fullContext);
}
export function initializeCommandHandling(commands: Record<string, CommandHandler<ApplicationCommandStructure>>, ctx: PartialContext) {
ctx.client.on('interactionCreate', async (_interaction) => {
const interaction = augmentInteraction(_interaction as any);
if (interaction.isExecutable()) {
handleCommands(interaction, commands, ctx);
if (command.options?.authorize && !(await awaitMaybePromise(command.options.authorize(interaction, commandContext)))) {
if (interaction.isApplicationCommand()) {
interaction.createMessage({
flags: Constants.MessageFlags.EPHEMERAL,
content: `You are not authorized to execute the \`${command.definition.name}\` command.`,
});
}
});
return;
}
cleanInteractionCustomIds(interaction, commandContext.state.id);
const [injectedInteraction, fullContext] = await injectInteraction(interaction, commandContext);
return command.execute(injectedInteraction, fullContext);
}
function cleanInteractionCustomIds(interaction: ExecutableInteraction, id: string) {
@@ -72,3 +79,68 @@ function removeCommandIdFromComponentCustomIds(components: { custom_id?: string;
}
});
}
const commandRegistry = new Map<string, Map<string, CommandHandler>>();
export async function registerCommands(ctx: PartialContext) {
if (!ctx.client) throw new Error('Client not initialized');
if (!(await ctx.client.getCommands()).length || process.env.RESET_COMMANDS === 'true' || process.env.NODE_ENV === 'development') {
console.debug('Registering commands...');
const registry = commandRegistry.get(ctx.client.commandKey);
const defs: Map<string, ApplicationCommandStructure[]> = new Map();
for (const cmd of registry.values().toArray()) {
if (cmd.options?.guilds) {
for (const guildId of cmd.options.guilds) {
if (defs.get(guildId)) {
defs.get(guildId).push(cmd.definition);
} else {
defs.set(guildId, [cmd.definition]);
}
}
} else {
if (defs.get('global')) {
defs.get('global').push(cmd.definition);
} else {
defs.set('global', [cmd.definition]);
}
}
}
for (const guildId of defs.keys().toArray()) {
if (guildId === 'global') {
const response = await ctx.client.bulkEditCommands(defs.get(guildId));
console.debug(`Registered ${response.length} global commands`);
} else {
const response = await ctx.client.bulkEditGuildCommands(guildId, defs.get(guildId));
console.debug(`Registered ${response.length} guild commands on ${guildId}`);
for (const cmdResponse of response) {
const cmd = registry.get(cmdResponse.name);
if (cmd.options.guildPermissions && cmd.options.guildPermissions[guildId]) {
console.debug(`Editing permissions for command on ${guildId}`);
const response = await ctx.client.editCommandPermissions(guildId, cmdResponse.id, cmd.options.guildPermissions[guildId]);
console.debug(`Edited permissions for command on ${guildId}`);
}
}
}
}
}
ctx.client.on('interactionCreate', async (_interaction) => {
const interaction = augmentInteraction(_interaction as any);
if (interaction.isExecutable()) {
const registry = commandRegistry.get(ctx.client.commandKey);
handleCommands(interaction, registry, ctx);
}
});
return commandRegistry.values().toArray();
}
export function addCommand(command: CommandHandler) {
const commandKey = command.options.commandKey || 'default';
if (!commandRegistry.get(commandKey)) {
commandRegistry.set(commandKey, new Map());
}
const registry = commandRegistry.get(commandKey);
registry.set(command.definition.name, command);
}

View File

@@ -1,7 +1,3 @@
export * from './command-handler';
export * from './import-commands';
export * from './handle-commands';
export * from './command-helpers';
export * from './register-commands';
export * from './command-state';
// Exports that will be made publicly available outside this library
export * from './create';
export * from './option-builders';

View File

@@ -1,11 +0,0 @@
import type { ApplicationCommandStructure, Client } from '@projectdysnomia/dysnomia';
export async function registerCommands(client: Client, commands: ApplicationCommandStructure[]) {
if (!client) throw new Error('Client not initialized');
if (!(await client.getCommands()).length || process.env.RESET_COMMANDS === 'true' || process.env.NODE_ENV === 'development') {
console.debug('Registering commands...');
const response = await client.bulkEditCommands(commands);
console.debug(`Registered ${response.length} commands.`);
}
return commands;
}

View File

@@ -4,6 +4,7 @@ import {
type ModalSubmitInteractionDataLabelComponent,
type ModalSubmitInteractionDataSelectComponent,
type ModalSubmitInteractionDataTextInputComponent,
type StringSelectMenu,
} from '@projectdysnomia/dysnomia';
export function isModalLabel(component: ComponentBase): component is ModalSubmitInteractionDataLabelComponent {
@@ -21,3 +22,7 @@ export function isModalSelect(component: ComponentBase): component is ModalSubmi
export function componentHasIdPrefix(component: ComponentBase, prefix: string): boolean {
return (isModalTextInput(component) || isModalSelect(component)) && component.custom_id.startsWith(prefix);
}
export function isStringSelectMenu(component: ComponentBase): component is StringSelectMenu {
return component.type === Constants.ComponentTypes.STRING_SELECT;
}

View File

@@ -1,14 +1,14 @@
import { importCommands, initializeCommandHandling, registerCommands } from '../commands';
import { Client } from '@projectdysnomia/dysnomia';
import { Client as DJSClient } from '@projectdysnomia/dysnomia';
import kv, { asyncKV } from '@/util/kv.js';
import type { KVStore } from './kv-store.type.ts.ts';
import type { Cache } from './cache.type.ts';
import { registerCommands } from '../commands/handle-commands.ts';
import type { Client } from './client.ts';
export interface DiscordBotOptions {
token?: string;
intents?: number[];
commandPattern?: string;
commandBaseDir?: string;
commandKey?: string;
keyStore?: KVStore;
cache?: Cache;
onError?: (error: Error) => void;
@@ -18,28 +18,23 @@ export interface DiscordBotOptions {
export function startBot({
token = process.env.DISCORD_BOT_TOKEN || '',
intents = [],
commandPattern = '**/*.command.{js,ts,jsx,tsx}',
commandBaseDir = 'src',
commandKey = 'default',
keyStore = asyncKV,
cache = kv,
onError,
onReady,
}: DiscordBotOptions = {}): Client {
const client = new Client(`Bot ${token}`, {
const client = new DJSClient(`Bot ${token}`, {
gateway: {
intents,
},
});
}) as Client;
client.commandKey = commandKey;
client.on('ready', async () => {
console.debug(`Logged in as ${client.user?.username}#${client.user?.discriminator}`);
onReady?.();
const commands = await importCommands(commandPattern, commandBaseDir);
await registerCommands(
client,
Object.values(commands).map((cmd) => cmd.definition),
);
initializeCommandHandling(commands, { client, cache, kv: keyStore });
await registerCommands({ client, cache, kv: keyStore });
console.debug('Bot is ready and command handling is initialized.');
});

View File

@@ -0,0 +1,5 @@
import { Client as DJSClient } from '@projectdysnomia/dysnomia';
export interface Client extends DJSClient {
commandKey: string;
}

View File

@@ -1,4 +1,4 @@
import type { ActionRowItem, ContainerItems, MediaItem, Padding } from '@/discord/components';
import type { ActionRowItem, ButtonStyle, ContainerItems, MediaItem, Padding } from '@/discord/components';
import type {
ActionRow,
Button,
@@ -68,7 +68,7 @@ export interface ButtonElement {
props: {
label: string;
customId: string;
style: number;
style: ButtonStyle;
emoji?: PartialEmoji;
disabled?: boolean;
};

View File

@@ -86,7 +86,7 @@ export interface StarKittenIntrinsicElements {
premiumButton: PremiumButtonElement['props'];
modal: ModalElement['props'] & { children: StarKittenElement | StarKittenElement[] };
label: LabelElement['props'] & { children: StarKittenElement | StarKittenElement[] };
stringSelect: StringSelectElement['props'] & { children: StringSelectElement['children'] };
stringSelect: StringSelectElement['props'] & { children: StarKittenElement | StarKittenElement[] };
option: OptionElement['props'];
textInput: TextInputElement['props'];
text: TextElement['props'];

View File

@@ -1,5 +1,6 @@
import { Constants, type InteractionContentEdit, type InteractionModalContent } from '@projectdysnomia/dysnomia';
import type { CommandContext, ExecutableInteraction } from '../types';
import type { StarKittenElement } from '../jsx';
export enum PageType {
MODAL = 'modal',
@@ -13,7 +14,9 @@ export interface Page<T> {
followUpFlags?: number;
render: (
ctx: PageContext<T>,
) => (InteractionModalContent | InteractionContentEdit) | Promise<InteractionModalContent | InteractionContentEdit>;
) =>
| (InteractionModalContent | InteractionContentEdit | StarKittenElement)
| Promise<InteractionModalContent | InteractionContentEdit | StarKittenElement>;
}
export interface PagesOptions<T> {

View File

@@ -2,7 +2,7 @@ import type { ChatInputApplicationCommandStructure, ApplicationCommandStructure
import type { ExecutableInteraction } from './interaction.type';
import type { Cache } from '@/discord/core/cache.type';
import type { KVStore } from '@/discord/core/kv-store.type.ts';
import type { Client } from '@projectdysnomia/dysnomia';
import type { Client } from '@/discord/core/client';
export interface CommandState<T = any> {
id: string; // unique id for this command instance
@@ -18,11 +18,33 @@ export interface PartialContext<T = any> {
state?: CommandState<T>; // state associated with this command instance
}
export enum PermissionType {
ROLE = 1,
USER = 2,
CHANNEL = 3,
}
export interface CommandPermission {
id: string;
permission: boolean;
type: PermissionType;
}
export interface CommandOptions {
commandKey: string;
authorize: AuthorizedFn;
guilds: string[]; // make this a guild command
guildPermissions: Record<string, CommandPermission[]>; // only works if a guild command
}
export type CommandContext<T = any> = Required<PartialContext<T>>;
export type ChatCommandDefinition = Omit<ChatInputApplicationCommandStructure, 'type'>;
export type ExecuteFn = (interaction: ExecutableInteraction, ctx: CommandContext) => any | Promise<any>;
export type AuthorizedFn = (interaction: ExecutableInteraction, ctx: CommandContext) => boolean | Promise<boolean>;
export interface CommandHandler<T extends ApplicationCommandStructure> {
export interface CommandHandler<T extends ApplicationCommandStructure = ApplicationCommandStructure> {
definition: T;
execute: (interaction: ExecutableInteraction, ctx: CommandContext) => Promise<void>;
execute: ExecuteFn;
options?: Partial<CommandOptions>;
}

View File

@@ -9,6 +9,8 @@ export interface InteractionAugments {
isApplicationCommand: () => this is Dysnomia.CommandInteraction;
isModalSubmit: () => this is Dysnomia.ModalSubmitInteraction;
isMessageComponent: () => this is Dysnomia.ComponentInteraction;
isSelectMenu: () => this is SelectMenuInteraction;
isButton: () => this is ButtonInteraction;
isAutocomplete: () => this is Dysnomia.AutocompleteInteraction;
isPing: () => this is Dysnomia.PingInteraction;
isExecutable: () => this is ExecutableInteraction;
@@ -23,3 +25,11 @@ export type ModalSubmitInteraction = Dysnomia.ModalSubmitInteraction & Interacti
export type ComponentInteraction = Dysnomia.ComponentInteraction & InteractionAugments;
export type AutocompleteInteraction = Dysnomia.AutocompleteInteraction & InteractionAugments;
export type PingInteraction = Dysnomia.PingInteraction & InteractionAugments;
export interface SelectMenuInteraction extends ComponentInteraction {
data: Dysnomia.ComponentInteractionSelectMenuData;
}
export interface ButtonInteraction extends ComponentInteraction {
data: Dysnomia.ComponentInteractionButtonData;
}

View File

@@ -9,7 +9,7 @@ import { esiFetch, type PublicEsiOptions } from './util/fetch';
* - This route is cached for an hour
* @returns {number[]} - An array of all active player alliance ids
*/
export async function listAlliances(options?: PublicEsiOptions) {
export async function listAlliances(options?: PublicEsiOptions): Promise<number[]> {
return await esiFetch<number[]>('/alliances/', options);
}
@@ -29,7 +29,7 @@ interface AllianceInfo {
* @param alliance_id Alliance id
* @returns {AllianceInfo}
*/
export async function getAllianceInformation(alliance_id: number, options?: PublicEsiOptions) {
export async function getAllianceInformation(alliance_id: number, options?: PublicEsiOptions): Promise<Partial<AllianceInfo>> {
return await esiFetch<Partial<AllianceInfo>>(`/alliances/${alliance_id}/`, options);
}
@@ -39,7 +39,7 @@ export async function getAllianceInformation(alliance_id: number, options?: Publ
* @param alliance_id Alliance id
* @returns {number[]} - Array of corporation ids
*/
export async function listAllianceCorporations(alliance_id: number, options?: PublicEsiOptions) {
export async function listAllianceCorporations(alliance_id: number, options?: PublicEsiOptions): Promise<number[]> {
return await esiFetch<number[]>(`/alliances/${alliance_id}/corporations/`, options);
}
@@ -54,6 +54,6 @@ interface AllianceIcon {
* @param alliance_id Alliance id
* @returns {AllianceIcon}
*/
export async function getAllianceIcon(alliance_id: number, options?: PublicEsiOptions) {
export async function getAllianceIcon(alliance_id: number, options?: PublicEsiOptions): Promise<Partial<AllianceIcon>> {
return await esiFetch<Partial<AllianceIcon>>(`/alliances/${alliance_id}/icons/`, options);
}

View File

@@ -25,7 +25,7 @@ export interface Asset {
type_id: number;
}
export function getCharacterAssets(options: EsiOptions, page: number = 1) {
export function getCharacterAssets(options: EsiOptions, page: number = 1): Promise<Partial<Asset>[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-assets.read_assets.v1']);
return esiFetch<Partial<Asset>[]>(`/characters/${character_id}/assets/?page=${page}`, {
...options,
@@ -42,7 +42,7 @@ export interface AssetLocation {
};
}
export function getCharacterAssetLocations(options: EsiOptions, ids: number[]) {
export function getCharacterAssetLocations(options: EsiOptions, ids: number[]): any[] | Promise<Partial<AssetLocation>[]> {
if (ids.length === 0) return [];
if (ids.length > 1000) throw 'Maximum of 1000 IDs can be requested at once';
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-assets.read_assets.v1']);
@@ -59,7 +59,7 @@ export interface AssetNames {
name: string;
}
export function getCharacterAssetNames(options: EsiOptions, ids: number[]) {
export function getCharacterAssetNames(options: EsiOptions, ids: number[]): any[] | Promise<Partial<AssetNames>[]> {
if (ids.length === 0) return [];
if (ids.length > 1000) throw 'Maximum of 1000 IDs can be requested at once';
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-assets.read_assets.v1']);
@@ -82,7 +82,7 @@ export interface CorpAsset {
type_id: number;
}
export async function getCorporationAssets(options: EsiOptions, corporation_id: number, page: number = 1) {
export async function getCorporationAssets(options: EsiOptions, corporation_id: number, page: number = 1): Promise<Partial<CorpAsset>[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-assets.read_corporation_assets.v1']);
return await esiFetch<Partial<CorpAsset>[]>(`/corporations/${corporation_id}/assets/?page=${page}`, {
...options,
@@ -99,7 +99,11 @@ export interface AssetLocation {
};
}
export async function getCorporationAssetLocations(options: EsiOptions, corporation_id: number, ids: number[]) {
export async function getCorporationAssetLocations(
options: EsiOptions,
corporation_id: number,
ids: number[],
): Promise<Partial<AssetLocation>[]> {
if (ids.length === 0) return [];
if (ids.length > 1000) throw 'Maximum of 1000 IDs can be requested at once';
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-assets.read_corporation_assets.v1']);
@@ -115,7 +119,7 @@ export interface AssetNames {
name: string;
}
export async function getCorporationAssetNames(options: EsiOptions, id: number, ids: number[]) {
export async function getCorporationAssetNames(options: EsiOptions, id: number, ids: number[]): Promise<Partial<AssetNames>[]> {
if (ids.length === 0) return [];
if (ids.length > 1000) throw 'Maximum of 1000 IDs can be requested at once';
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-assets.read_corporation_assets.v1']);

View File

@@ -31,7 +31,7 @@ export interface CalendarEvent {
* @param from_event Event from which to get the next 50 chronological event summaries
* @returns {Partial<CalendarEvent>[]}
*/
export async function listCalendarEventSummaries(options: EsiOptions, from_event?: number) {
export async function listCalendarEventSummaries(options: EsiOptions, from_event?: number): Promise<Partial<CalendarEvent>[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-calendar.read_calendar_events.v1']);
return await esiFetch<Partial<CalendarEvent>[]>(`/characters/${character_id}/calendar/${from_event ?? '?from_event=' + from_event}`, {
...options,
@@ -62,7 +62,7 @@ export interface CalendarEventDetails {
* @param event_id Event Id
* @returns {Partial<CalendarEventDetails>}
*/
export async function getEventDetails(options: EsiOptions, event_id: number) {
export async function getEventDetails(options: EsiOptions, event_id: number): Promise<Partial<CalendarEventDetails>> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-calendar.read_calendar_events.v1']);
return await esiFetch<Partial<CalendarEventDetails>>(`/characters/${character_id}/calendar/${event_id}/`, {
...options,
@@ -79,7 +79,11 @@ export async function getEventDetails(options: EsiOptions, event_id: number) {
* @param event_id Event Id
* @param response Response: 'accepted' | 'declined' | 'tentative'
*/
export async function respondToEvent(options: EsiOptions, event_id: number, response: 'accepted' | 'declined' | 'tentative') {
export async function respondToEvent(
options: EsiOptions,
event_id: number,
response: 'accepted' | 'declined' | 'tentative',
): Promise<void> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-calendar.respond_calendar_events.v1']);
return await esiFetch<void>(`/characters/${character_id}/calendar/${event_id}/`, {
...options,
@@ -104,7 +108,7 @@ export interface CalendarEventAttendee {
* @param event_id Event Id
* @returns {Partial<CalendarEventAttendee>[]}
*/
export async function getEventAttendees(options: EsiOptions, event_id: number) {
export async function getEventAttendees(options: EsiOptions, event_id: number): Promise<Partial<CalendarEventAttendee>[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-calendar.read_calendar_events.v1']);
return await esiFetch<Partial<CalendarEventAttendee>[]>(`/characters/${character_id}/calendar/${event_id}/attendees/`, {
...options,

View File

@@ -9,7 +9,10 @@ export interface CharacterAffiliations {
alliance_id?: number;
faction_id?: number;
}
export function getCharacterAffiliations(character_ids: number[], options?: PublicEsiOptions) {
export function getCharacterAffiliations(
character_ids: number[],
options?: PublicEsiOptions,
): any[] | Promise<Partial<CharacterAffiliations>[]> {
if (character_ids.length === 0) return [];
if (character_ids.length > 1000) throw 'Maximum of 1000 character IDs can be requested at once';
return esiFetch<Partial<CharacterAffiliations>[]>(`/characters/affiliation/`, {
@@ -33,7 +36,7 @@ export interface CharacterData {
title: string;
}
export function getCharacterPublicData(id: number, options?: PublicEsiOptions) {
export function getCharacterPublicData(id: number, options?: PublicEsiOptions): Promise<Partial<CharacterData>> {
return esiFetch<Partial<CharacterData>>(`/characters/${id}/`, options);
}
@@ -46,7 +49,7 @@ export interface AgentResearch {
}
// required scope: esi-characters.read_agents_research.v1
export function getCharacterAgentResearch(options: EsiOptions) {
export function getCharacterAgentResearch(options: EsiOptions): Promise<Partial<AgentResearch>[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-characters.read_agents_research.v1']);
return esiFetch<Partial<AgentResearch>[]>(`/characters/${character_id}/agents_research/`, {
...options,
@@ -55,7 +58,7 @@ export function getCharacterAgentResearch(options: EsiOptions) {
}
// required scope: esi-characters.read_blueprints.v1
export function getCharacterBlueprints(options: EsiOptions, page: number = 1) {
export function getCharacterBlueprints(options: EsiOptions, page: number = 1): Promise<Partial<Blueprint>[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-characters.read_blueprints.v1']);
return esiFetch<Partial<Blueprint>[]>(`/characters/${character_id}/blueprints/?page=${page}`, {
...options,
@@ -70,12 +73,12 @@ export interface CharacterCorporationHistory {
start_date: string;
}
export function getCharacterCorporationHistory(options: EsiOptions) {
export function getCharacterCorporationHistory(options: EsiOptions): Promise<Partial<CharacterCorporationHistory>[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-characters.read_corporation_roles.v1']);
return esiFetch<Partial<CharacterCorporationHistory>[]>(`/characters/${character_id}/corporationhistory/`, options);
}
export function calculateCSPAChargeCost(options: EsiOptions, target_character_ids: number[]) {
export function calculateCSPAChargeCost(options: EsiOptions, target_character_ids: number[]): Promise<number[]> {
if (target_character_ids.length === 0) return null;
if (target_character_ids.length > 100) throw 'Maximum of 100 target character IDs can be requested at once';
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-characters.read_cspa.v1']);
@@ -93,7 +96,7 @@ export interface JumpFatigue {
last_update_date: string;
}
export function getCharacterJumpFatigue(options: EsiOptions) {
export function getCharacterJumpFatigue(options: EsiOptions): Promise<Partial<JumpFatigue>> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-characters.read_fatigue.v1']);
return esiFetch<Partial<JumpFatigue>>(`/characters/${character_id}/fatigue/`, {
...options,
@@ -118,7 +121,7 @@ export interface Medals {
title: string;
}
export function getCharacterMedals(options: EsiOptions) {
export function getCharacterMedals(options: EsiOptions): Promise<Partial<Medals>[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-characters.read_medals.v1']);
return esiFetch<Partial<Medals>[]>(`/characters/${character_id}/medals/`, {
...options,
@@ -136,7 +139,7 @@ export interface Notification {
type: NotificationType;
}
export function getCharacterNotifications(options: EsiOptions) {
export function getCharacterNotifications(options: EsiOptions): Promise<Partial<Notification>[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-characters.read_notifications.v1']);
return esiFetch<Partial<Notification>[]>(`/characters/${character_id}/notifications/`, {
...options,
@@ -152,7 +155,7 @@ export interface ContactNotification {
standing_level: -10 | -5 | 0 | 5 | 10;
}
export function getCharacterContactNotifications(options: EsiOptions) {
export function getCharacterContactNotifications(options: EsiOptions): Promise<Partial<ContactNotification>[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-characters.read_notifications.v1']);
return esiFetch<Partial<ContactNotification>[]>(`/characters/${character_id}/notifications/contacts`, {
...options,
@@ -167,7 +170,7 @@ export interface CharacterPortraits {
px64x64: string;
}
export function getCharacterPortraits(character_id: number, options?: PublicEsiOptions) {
export function getCharacterPortraits(character_id: number, options?: PublicEsiOptions): Promise<Partial<CharacterPortraits>> {
return esiFetch<Partial<CharacterPortraits>>(`/characters/${character_id}/portrait/`, options);
}
@@ -182,7 +185,7 @@ export interface CharacterCorporationRoles {
roles_at_other: string[];
}
export function getCharacterCorporationRoles(options: EsiOptions) {
export function getCharacterCorporationRoles(options: EsiOptions): Promise<Partial<CharacterCorporationRoles>> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-characters.read_corporation_roles.v1']);
return esiFetch<Partial<CharacterCorporationRoles>>(`/characters/${character_id}/roles`, {
...options,
@@ -196,7 +199,7 @@ export interface CharacterStandings {
standing: number;
}
export function getCharacterStandings(options: EsiOptions) {
export function getCharacterStandings(options: EsiOptions): Promise<Partial<CharacterStandings>[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-characters.read_standings.v1']);
return esiFetch<Partial<CharacterStandings>[]>(`/characters/${character_id}/standings`, {
...options,
@@ -211,7 +214,7 @@ export interface CharacterTitles {
}[];
}
export function getCharacterTitles(options: EsiOptions) {
export function getCharacterTitles(options: EsiOptions): Promise<Partial<CharacterTitles>> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-characters.read_titles.v1']);
return esiFetch<Partial<CharacterTitles>>(`/characters/${character_id}/titles`, {
...options,

View File

@@ -18,7 +18,7 @@ export interface CharacterClones {
last_station_change_date: string;
}
export function getCharacterClones(options: EsiOptions) {
export function getCharacterClones(options: EsiOptions): Promise<Partial<CharacterClones>> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-clones.read_clones.v1']);
return esiFetch<Partial<CharacterClones>>(`/characters/${character_id}/clones`, {
...options,
@@ -26,7 +26,7 @@ export function getCharacterClones(options: EsiOptions) {
});
}
export function getCharacterActiveImplants(options: EsiOptions) {
export function getCharacterActiveImplants(options: EsiOptions): Promise<number[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-clones.read_implants.v1']);
return esiFetch<number[]>(`/characters/${character_id}/implants`, {
...options,

View File

@@ -10,7 +10,7 @@ export interface Contact {
standing: STANDING;
}
export function getAllianceContacts(options: EsiOptions, alliance_id: number, page: number = 1) {
export function getAllianceContacts(options: EsiOptions, alliance_id: number, page: number = 1): Promise<Contact[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-alliances.read_contacts.v1']);
return esiFetch<Contact[]>(`/alliances/${alliance_id}/contacts/?page=${page}`, {
...options,
@@ -23,7 +23,7 @@ export interface ContactLabel {
name: string;
}
export function getAllianceContactLabels(options: EsiOptions, alliance_id: number) {
export function getAllianceContactLabels(options: EsiOptions, alliance_id: number): Promise<ContactLabel[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-alliances.read_contacts.v1']);
return esiFetch<ContactLabel[]>(`/alliances/${alliance_id}/contacts/labels/`, {
...options,
@@ -31,7 +31,7 @@ export function getAllianceContactLabels(options: EsiOptions, alliance_id: numbe
});
}
export function deleteCharacterContacts(options: EsiOptions, character_ids: number[]) {
export function deleteCharacterContacts(options: EsiOptions, character_ids: number[]): Promise<void> {
if (character_ids.length === 0) return;
if (character_ids.length > 20) throw 'Maximum of 20 IDs can be deleted at once';
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-characters.write_contacts.v1']);
@@ -48,7 +48,7 @@ export interface CharacterContact extends Contact {
is_watched?: boolean;
}
export function getCharacterContacts(options: EsiOptions, page: number = 1) {
export function getCharacterContacts(options: EsiOptions, page: number = 1): Promise<CharacterContact[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-characters.read_contacts.v1']);
return esiFetch<CharacterContact[]>(`/characters/${character_id}/contacts/?page=${page}`, {
...options,
@@ -62,7 +62,7 @@ export function addCharacterContacts(
standing: STANDING,
label_ids?: number[],
watched?: boolean,
) {
): Promise<void> {
if (character_ids.length === 0) return;
if (character_ids.length > 100) throw 'Maximum of 100 IDs can be added at once';
if (label_ids && label_ids.length > 63) throw 'Maximum of 63 label IDs can be assigned at once';
@@ -86,7 +86,7 @@ export function editCharacterContacts(
standing: STANDING,
label_ids?: number[],
watched?: boolean,
) {
): Promise<void> {
if (character_ids.length === 0) return;
if (character_ids.length > 100) throw 'Maximum of 100 IDs can be edited at once';
if (label_ids && label_ids.length > 63) throw 'Maximum of 63 label IDs can be assigned at once';
@@ -104,7 +104,7 @@ export function editCharacterContacts(
);
}
export function getCharacterContactLabels(options: EsiOptions) {
export function getCharacterContactLabels(options: EsiOptions): Promise<Partial<ContactLabel>[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-characters.read_contacts.v1']);
return esiFetch<Partial<ContactLabel>[]>(`/characters/${character_id}/contacts/labels/`, {
...options,
@@ -116,7 +116,7 @@ export interface CorporationContact extends Contact {
is_watched?: boolean;
}
export function getCorporationContacts(options: EsiOptions, corporation_id: number, page: number = 1) {
export function getCorporationContacts(options: EsiOptions, corporation_id: number, page: number = 1): Promise<CorporationContact[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-corporations.read_contacts.v1']);
return esiFetch<CorporationContact[]>(`/corporations/${corporation_id}/contacts/?page=${page}`, {
...options,
@@ -124,7 +124,7 @@ export function getCorporationContacts(options: EsiOptions, corporation_id: numb
});
}
export function getCorporationContactLabels(options: EsiOptions, corporation_id: number) {
export function getCorporationContactLabels(options: EsiOptions, corporation_id: number): Promise<ContactLabel[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-corporations.read_contacts.v1']);
return esiFetch<ContactLabel[]>(`/corporations/${corporation_id}/contacts/labels/`, {
...options,

View File

@@ -2,6 +2,13 @@ import { ESI_SCOPE } from '../oauth/auth.types';
import { ESI_RATE_LIMIT_GROUP } from './util/rate-limits';
import { checkScopesAndGetCharacterId, esiFetch, type EsiOptions, type PublicEsiOptions } from './util/fetch';
export enum ContractType {
ITEM_EXCHANGE = 'item_exchange',
AUCTION = 'auction',
COURIER = 'courier',
LOAN = 'loan',
}
export interface PublicContract {
buyout?: number;
collateral?: number;
@@ -21,10 +28,29 @@ export interface PublicContract {
volume?: number;
}
export enum ContractAvailability {
PUBLIC = 'public',
PERSONAL = 'personal',
CORPORATION = 'corporation',
ALLIANCE = 'alliance',
}
export enum ContractStatus {
OUTSTANDING = 'outstanding',
IN_PROGRESS = 'in_progress',
FINISHED_ISSUER = 'finished_issuer',
FINISHED_CONTRACTOR = 'finished_contractor',
CANCELLED = 'cancelled',
REJECTED = 'rejected',
FAILED = 'failed',
DELETED = 'deleted',
REVERSED = 'reversed',
}
export interface Contract extends PublicContract {
acceptor_id: number;
assignee_id: number;
availability: 'public' | 'personal' | 'corporation' | 'alliance';
availability: ContractAvailability;
date_accepted?: string;
date_completed?: string;
status:
@@ -43,7 +69,7 @@ export interface Contract extends PublicContract {
* Returns contracts available to a character, only if the character is issuer, acceptor or assignee.
* Only returns contracts no older than 30 days, or if the status is "in_progress".
*/
export function getCharacterContracts(options: EsiOptions, page: number = 1) {
export function getCharacterContracts(options: EsiOptions, page: number = 1): Promise<Contract[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-contracts.read_character_contracts.v1']);
return esiFetch<Contract[]>(`/characters/${character_id}/contracts/?page=${page}`, {
...options,
@@ -61,7 +87,7 @@ export interface ContractBid extends PublicContractBid {
bidder_id: number;
}
export function getContractBids(options: EsiOptions, contract_id: number) {
export function getContractBids(options: EsiOptions, contract_id: number): Promise<ContractBid[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-contracts.read_character_contracts.v1']);
return esiFetch<ContractBid[]>(`/characters/${character_id}/contracts/${contract_id}/bids/`, {
...options,
@@ -78,7 +104,7 @@ export interface ContractItem {
type_id: number; // type ID of the item
}
export function getContractItems(options: EsiOptions, contract_id: number) {
export function getContractItems(options: EsiOptions, contract_id: number): Promise<ContractItem[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-contracts.read_character_contracts.v1']);
return esiFetch<ContractItem[]>(`/characters/${character_id}/contracts/${contract_id}/items/`, {
...options,
@@ -86,7 +112,7 @@ export function getContractItems(options: EsiOptions, contract_id: number) {
});
}
export function getPublicContractBids(contract_id: number, page: number = 1, options?: PublicEsiOptions) {
export function getPublicContractBids(contract_id: number, page: number = 1, options?: PublicEsiOptions): Promise<PublicContractBid[]> {
return esiFetch<PublicContractBid[]>(`/contracts/public/bids/${contract_id}?page=${page}`, options);
}
@@ -102,15 +128,15 @@ export interface PublicContractItem {
type_id: number; // type ID of the item
}
export function getPublicContractItems(contract_id: number, page: number = 1, options?: PublicEsiOptions) {
export function getPublicContractItems(contract_id: number, page: number = 1, options?: PublicEsiOptions): Promise<PublicContractItem[]> {
return esiFetch<PublicContractItem[]>(`/contracts/public/items/${contract_id}?page=${page}`, options);
}
export function getPublicContracts(region_id: number, page: number = 1, options?: PublicEsiOptions) {
export function getPublicContracts(region_id: number, page: number = 1, options?: PublicEsiOptions): Promise<PublicContract[]> {
return esiFetch<PublicContract[]>(`/contracts/public/${region_id}?page=${page}`, options);
}
export function getCorporationContracts(options: EsiOptions, corporation_id: number, page: number = 1) {
export function getCorporationContracts(options: EsiOptions, corporation_id: number, page: number = 1): Promise<Contract[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-contracts.read_corporation_contracts.v1']);
return esiFetch<Contract[]>(`/corporations/${corporation_id}/contracts/?page=${page}`, {
...options,
@@ -118,7 +144,7 @@ export function getCorporationContracts(options: EsiOptions, corporation_id: num
});
}
export function getCorporationContractBids(options: EsiOptions, corporation_id: number, contract_id: number) {
export function getCorporationContractBids(options: EsiOptions, corporation_id: number, contract_id: number): Promise<ContractBid[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-contracts.read_corporation_contracts.v1']);
return esiFetch<ContractBid[]>(`/corporations/${corporation_id}/contracts/${contract_id}/bids/`, {
...options,
@@ -126,7 +152,7 @@ export function getCorporationContractBids(options: EsiOptions, corporation_id:
});
}
export function getCorporationContractItems(options: EsiOptions, corporation_id: number, contract_id: number) {
export function getCorporationContractItems(options: EsiOptions, corporation_id: number, contract_id: number): Promise<ContractItem[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-contracts.read_corporation_contracts.v1']);
return esiFetch<ContractItem[]>(`/corporations/${corporation_id}/contracts/${contract_id}/items/`, {
...options,

View File

@@ -50,7 +50,7 @@ export function listCorporationProjects(
limit?: number;
state?: 'Unspecified' | 'Active' | 'Closed' | 'Completed' | 'Expired' | 'Deleted';
} = {},
) {
): Promise<CorporationProjectResponse> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-corporations.read_projects.v1']);
const queryParams = new URLSearchParams();
if (filters.after) queryParams.append('after', filters.after);

View File

@@ -8,7 +8,7 @@ import { checkScopesAndGetCharacterId, esiFetch, type EsiOptions, type PublicEsi
import type { Blueprint, Icons, STANDING } from './types/shared';
import type { CorporationRoles } from './types/corporation';
export async function getNpcCorporations(options?: PublicEsiOptions) {
export async function getNpcCorporations(options?: PublicEsiOptions): Promise<number[]> {
return await esiFetch<number[]>('/corporations/npccorps', options);
}
@@ -29,7 +29,7 @@ interface CorporationInfo {
war_eligible?: boolean;
}
export async function getCorporationData(corporation_id: number, options?: PublicEsiOptions) {
export async function getCorporationData(corporation_id: number, options?: PublicEsiOptions): Promise<CorporationInfo> {
return await esiFetch<CorporationInfo>(`/corporations/${corporation_id}/`, options);
}
@@ -40,11 +40,15 @@ interface AllianceHistory {
start_date: string;
}
export async function getCorporationAllianceHistory(corporation_id: number, options?: PublicEsiOptions) {
export async function getCorporationAllianceHistory(corporation_id: number, options?: PublicEsiOptions): Promise<AllianceHistory[]> {
return await esiFetch<AllianceHistory[]>(`/corporations/${corporation_id}/alliancehistory/`, options);
}
export async function getCorporationBlueprints(options: EsiOptions, corporation_id: number, page: number = 1) {
export async function getCorporationBlueprints(
options: EsiOptions,
corporation_id: number,
page: number = 1,
): Promise<Partial<Blueprint>[]> {
checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-corporations.read_blueprints.v1']);
return await esiFetch<Partial<Blueprint>[]>(`/corporations/${corporation_id}/blueprints/?page=${page}`, {
...options,
@@ -52,7 +56,7 @@ export async function getCorporationBlueprints(options: EsiOptions, corporation_
});
}
export async function getAllCorporationALSCLogs(options: EsiOptions, corporation_id: number, page: number = 1) {
export async function getAllCorporationALSCLogs(options: EsiOptions, corporation_id: number, page: number = 1): Promise<any[]> {
checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-corporations.read_container_logs.v1']);
return await esiFetch<any[]>(`/corporations/${corporation_id}/containers/logs/?page=${page}`, {
...options,
@@ -70,7 +74,7 @@ export interface CorporationDivisions {
}[];
}
export async function getCorporationDivisions(options: EsiOptions, corporation_id: number) {
export async function getCorporationDivisions(options: EsiOptions, corporation_id: number): Promise<CorporationDivisions> {
checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-corporations.read_divisions.v1']);
return await esiFetch<CorporationDivisions>(`/corporations/${corporation_id}/divisions/`, {
...options,
@@ -84,14 +88,14 @@ export interface CorporationFacility {
type_id: number;
}
export async function getCorporationFacilities(options: EsiOptions, corporation_id: number) {
export async function getCorporationFacilities(options: EsiOptions, corporation_id: number): Promise<CorporationFacility[]> {
checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-corporations.read_facilities.v1']);
return await esiFetch<CorporationFacility[]>(`/corporations/${corporation_id}/facilities/`, {
...options,
});
}
export async function getCorporationIcons(corporation_id: number, options?: PublicEsiOptions) {
export async function getCorporationIcons(corporation_id: number, options?: PublicEsiOptions): Promise<Icons> {
return await esiFetch<Icons>(`/corporations/${corporation_id}/icons/`, options);
}
@@ -103,7 +107,7 @@ export interface CorporationMedal {
title: string;
}
export async function getCorporationMedals(options: EsiOptions, corporation_id: number, page: number = 1) {
export async function getCorporationMedals(options: EsiOptions, corporation_id: number, page: number = 1): Promise<CorporationMedal[]> {
checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-corporations.read_medals.v1']);
return await esiFetch<CorporationMedal[]>(`/corporations/${corporation_id}/medals/?page=${page}`, {
...options,
@@ -120,7 +124,11 @@ export interface CorporationIssuedMedal {
status: string;
}
export async function getCorporationIssuedMedals(options: EsiOptions, corporation_id: number, page: number = 1) {
export async function getCorporationIssuedMedals(
options: EsiOptions,
corporation_id: number,
page: number = 1,
): Promise<CorporationIssuedMedal[]> {
checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-corporations.read_medals.v1']);
return await esiFetch<CorporationIssuedMedal[]>(`/corporations/${corporation_id}/medals/issued/?page=${page}`, {
...options,
@@ -128,7 +136,7 @@ export async function getCorporationIssuedMedals(options: EsiOptions, corporatio
});
}
export async function getCorporationMembers(options: EsiOptions, corporation_id: number) {
export async function getCorporationMembers(options: EsiOptions, corporation_id: number): Promise<number[]> {
checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-corporations.read_corporation_membership.v1']);
return await esiFetch<number[]>(`/corporations/${corporation_id}/members/`, {
...options,
@@ -136,7 +144,7 @@ export async function getCorporationMembers(options: EsiOptions, corporation_id:
});
}
export async function getCorporationMemberLimit(options: EsiOptions, corporation_id: number) {
export async function getCorporationMemberLimit(options: EsiOptions, corporation_id: number): Promise<number> {
checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-corporations.track_members.v1']);
return await esiFetch<number>(`/corporations/${corporation_id}/members/limit/`, {
...options,
@@ -149,7 +157,7 @@ export interface CorporationMemberTitles {
titles: number[];
}
export async function getCorporationMemberTitles(options: EsiOptions, corporation_id: number) {
export async function getCorporationMemberTitles(options: EsiOptions, corporation_id: number): Promise<CorporationMemberTitles[]> {
checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-corporations.read_titles.v1']);
return await esiFetch<CorporationMemberTitles[]>(`/corporations/${corporation_id}/members/titles/`, {
...options,
@@ -167,7 +175,7 @@ export interface CorporationMemberTracking {
start_date?: string;
}
export async function getCorporationMemberTracking(options: EsiOptions, corporation_id: number) {
export async function getCorporationMemberTracking(options: EsiOptions, corporation_id: number): Promise<CorporationMemberTracking[]> {
checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-corporations.track_members.v1']);
return await esiFetch<CorporationMemberTracking[]>(`/corporations/${corporation_id}/membertracking/`, {
...options,
@@ -187,7 +195,7 @@ export interface CorporationMemberRole {
roles_at_other: CorporationRoles[];
}
export async function getCorporationMemberRoles(options: EsiOptions, corporation_id: number) {
export async function getCorporationMemberRoles(options: EsiOptions, corporation_id: number): Promise<CorporationMemberRole[]> {
checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-corporations.read_corporation_membership.v1']);
return await esiFetch<CorporationMemberRole[]>(`/corporations/${corporation_id}/members/roles/`, {
...options,
@@ -212,7 +220,11 @@ export interface CorporationMemberRoleHistory {
| 'roles_at_other';
}
export async function getCorporationMemberRoleHistory(options: EsiOptions, corporation_id: number, page: number = 1) {
export async function getCorporationMemberRoleHistory(
options: EsiOptions,
corporation_id: number,
page: number = 1,
): Promise<CorporationMemberRoleHistory[]> {
checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-corporations.read_corporation_membership.v1']);
return await esiFetch<CorporationMemberRoleHistory[]>(`/corporations/${corporation_id}/roles/history/?page=${page}`, {
...options,
@@ -226,7 +238,11 @@ export interface CorporationShareholder {
shareholder_type: 'character' | 'corporation';
}
export async function getCorporationShareholders(options: EsiOptions, corporation_id: number, page: number = 1) {
export async function getCorporationShareholders(
options: EsiOptions,
corporation_id: number,
page: number = 1,
): Promise<CorporationShareholder[]> {
checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-wallet.read_corporation_wallets.v1']);
return await esiFetch<CorporationShareholder[]>(`/corporations/${corporation_id}/shareholders/?page=${page}`, {
...options,
@@ -240,7 +256,11 @@ export interface CorporationStanding {
standing: STANDING;
}
export async function getCorporationStandings(options: EsiOptions, corporation_id: number, page: number = 1) {
export async function getCorporationStandings(
options: EsiOptions,
corporation_id: number,
page: number = 1,
): Promise<CorporationStanding[]> {
checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-corporations.read_standings.v1']);
return await esiFetch<CorporationStanding[]>(`/corporations/${corporation_id}/standings/?page=${page}`, {
...options,
@@ -259,7 +279,11 @@ export interface CorporationStarbase {
unanchor_at?: string;
}
export async function getCorporationStarbases(options: EsiOptions, corporation_id: number, page: number = 1) {
export async function getCorporationStarbases(
options: EsiOptions,
corporation_id: number,
page: number = 1,
): Promise<CorporationStarbase[]> {
checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-corporations.read_starbases.v1']);
return await esiFetch<CorporationStarbase[]>(`/corporations/${corporation_id}/starbases/?page=${page}`, {
...options,
@@ -288,7 +312,12 @@ export interface StarbaseDetail {
use_alliance_standings: boolean;
}
export async function getCorporationStarbaseDetail(options: EsiOptions, corporation_id: number, starbase_id: number, system_id: number) {
export async function getCorporationStarbaseDetail(
options: EsiOptions,
corporation_id: number,
starbase_id: number,
system_id: number,
): Promise<StarbaseDetail> {
checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-corporations.read_starbases.v1']);
return await esiFetch<StarbaseDetail>(`/corporations/${corporation_id}/starbases/${starbase_id}/?system_id=${system_id}`, {
...options,
@@ -331,7 +360,11 @@ export interface CorporationStructure {
unanchors_at: string;
}
export async function getCorporationStructures(options: EsiOptions, corporation_id: number, page: number = 1) {
export async function getCorporationStructures(
options: EsiOptions,
corporation_id: number,
page: number = 1,
): Promise<CorporationStructure[]> {
checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-corporations.read_structures.v1']);
return await esiFetch<CorporationStructure[]>(`/corporations/${corporation_id}/structures/?page=${page}`, {
...options,
@@ -351,7 +384,7 @@ export interface CorporationTitle {
title_id: number;
}
export async function getCorporationTitles(options: EsiOptions, corporation_id: number) {
export async function getCorporationTitles(options: EsiOptions, corporation_id: number): Promise<CorporationTitle[]> {
checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-corporations.read_titles.v1']);
return await esiFetch<CorporationTitle[]>(`/corporations/${corporation_id}/titles/`, {
...options,

View File

@@ -7,10 +7,12 @@ export * from './util/options';
export * from './mail';
export * from './character';
export * from './alliance';
export * from './contracts';
import * as alliance from './alliance';
import * as assets from './assets';
import * as character from './character';
import * as corporation from './corporation';
import * as contracts from './contracts';
export { alliance, assets, character, corporation };
export { alliance, assets, character, corporation, contracts };

View File

@@ -8,7 +8,7 @@ export interface Location {
}
// required scope: esi-location.read_location.v1
export function getCharacterLocation(options: EsiOptions) {
export function getCharacterLocation(options: EsiOptions): Promise<Partial<Location>> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-location.read_location.v1']);
return esiFetch<Partial<Location>>(`/characters/${character_id}/location/`, options);
}
@@ -21,7 +21,7 @@ export interface Online {
}
// required scope: esi-location.read_online.v1
export function getCharacterOnline(options: EsiOptions) {
export function getCharacterOnline(options: EsiOptions): Promise<Partial<Online>> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-location.read_online.v1']);
return esiFetch<Partial<Online>>(`/characters/${character_id}/online/`, options);
}
@@ -33,7 +33,7 @@ export interface CurrentShip {
}
// required scope: esi-location.read_ship_type.v1
export function getCharacterCurrentShip(options: EsiOptions) {
export function getCharacterCurrentShip(options: EsiOptions): Promise<Partial<CurrentShip>> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-location.read_ship_type.v1']);
return esiFetch<Partial<CurrentShip>>(`/characters/${character_id}/ship/`, options);
}

View File

@@ -16,7 +16,7 @@ export interface MailHeader {
}
// requires scope: esi-mail.read_mail.v1
export function getMailHeaders(options: EsiOptions) {
export function getMailHeaders(options: EsiOptions): Promise<MailHeader[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-mail.read_mail.v1']);
return esiFetch<MailHeader[]>(`/characters/${character_id}/mail/`, {
...options,
@@ -34,7 +34,7 @@ export interface SendMail {
}
// requires scope: esi-mail.send_mail.v1
export function sendMail(options: EsiOptions, mail: SendMail) {
export function sendMail(options: EsiOptions, mail: SendMail): Promise<any> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-mail.send_mail.v1']);
return esiFetch(`/characters/${character_id}/mail/`, {
...options,
@@ -44,7 +44,7 @@ export function sendMail(options: EsiOptions, mail: SendMail) {
}
// requires scope: esi-mail.read_mail.v1
export function deleteMail(options: EsiOptions, mailID: number) {
export function deleteMail(options: EsiOptions, mailID: number): Promise<any> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-mail.organize_mail.v1']);
return esiFetch(`/characters/${character_id}/mail/${mailID}/`, {
...options,
@@ -66,7 +66,7 @@ export interface Mail {
}
// requires scope: esi-mail.read_mail.v1
export function getMail(options: EsiOptions, mailID: number) {
export function getMail(options: EsiOptions, mailID: number): Promise<Mail> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-mail.read_mail.v1']);
return esiFetch<Mail>(`/characters/${character_id}/mail/${mailID}/`, {
...options,
@@ -79,7 +79,7 @@ export interface MailMetadata {
}
// requires scope: esi-mail.organize_mail.v1
export function updateMailMetadata(options: EsiOptions, mailID: number, metadata: MailMetadata) {
export function updateMailMetadata(options: EsiOptions, mailID: number, metadata: MailMetadata): Promise<any> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-mail.organize_mail.v1']);
return esiFetch(`/characters/${character_id}/mail/${mailID}/`, {
...options,
@@ -99,7 +99,7 @@ export interface MailLabels {
}
// requires scope: esi-mail.read_mail.v1
export function getMailLabels(options: EsiOptions) {
export function getMailLabels(options: EsiOptions): Promise<MailLabels> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-mail.read_mail.v1']);
return esiFetch<MailLabels>(`/characters/${character_id}/mail/labels/`, {
...options,
@@ -112,7 +112,7 @@ export interface CreateMailLabel {
}
// requires scope: esi-mail.organize_mail.v1
export function createMailLabel(options: EsiOptions, label: CreateMailLabel) {
export function createMailLabel(options: EsiOptions, label: CreateMailLabel): Promise<any> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-mail.organize_mail.v1']);
return esiFetch(`/characters/${character_id}/mail/labels/`, {
...options,
@@ -122,7 +122,7 @@ export function createMailLabel(options: EsiOptions, label: CreateMailLabel) {
}
// requires scope: esi-mail.organize_mail.v1
export function deleteMailLabel(options: EsiOptions, labelID: number) {
export function deleteMailLabel(options: EsiOptions, labelID: number): Promise<any> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-mail.organize_mail.v1']);
return esiFetch(`/characters/${character_id}/mail/labels/${labelID}/`, {
...options,
@@ -136,7 +136,7 @@ export interface MailingList {
}
// requires scope: esi-mail.read_mail.v1
export function getMailingLists(options: EsiOptions) {
export function getMailingLists(options: EsiOptions): Promise<MailingList[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-mail.read_mail.v1']);
return esiFetch<MailingList[]>(`/characters/${character_id}/mail/lists/`, {
...options,

View File

@@ -13,7 +13,7 @@ export interface CharacterAttributes {
}
// required scope: esi-skills.read_skills.v1
export function getCharacterAttributes(options: EsiOptions) {
export function getCharacterAttributes(options: EsiOptions): Promise<CharacterAttributes> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-skills.read_skills.v1']);
return esiFetch<CharacterAttributes>(`/characters/${character_id}/attributes`, {
...options,
@@ -32,7 +32,7 @@ export interface SkillQueueItem {
}
// required scope: esi-skills.read_skillqueue.v1
export function getCharacterSkillQueue(options: EsiOptions) {
export function getCharacterSkillQueue(options: EsiOptions): Promise<SkillQueueItem[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-skills.read_skillqueue.v1']);
return esiFetch<SkillQueueItem[]>(`/characters/${character_id}/skillqueue`, {
...options,
@@ -53,14 +53,14 @@ export interface CharacterSkills {
}
// required scope: esi-skills.read_skills.v1
export function getCharacterSkills(options: EsiOptions) {
export function getCharacterSkills(options: EsiOptions): Promise<CharacterSkills> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-skills.read_skills.v1']);
return esiFetch<CharacterSkills>(`/characters/${character_id}/skills`, {
...options,
});
}
export function calculateTrainingPercentage(queuedSkill: SkillQueueItem) {
export function calculateTrainingPercentage(queuedSkill: SkillQueueItem): number {
// percentage in when training started
const trainingStartPosition = (queuedSkill.training_start_sp! - queuedSkill.level_start_sp!) / queuedSkill.level_end_sp!;
// percentage completed between start and now

View File

@@ -2,7 +2,7 @@ import { ESI_SCOPE } from '../oauth';
import { checkScopesAndGetCharacterId, esiFetch, type EsiOptions } from './util/fetch';
// required scope: esi-wallet.read_character_wallet.v1
export function getCharacterWallet(options: EsiOptions) {
export function getCharacterWallet(options: EsiOptions): Promise<number> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-wallet.read_character_wallet.v1']);
return esiFetch<number>(`/characters/${character_id}/wallet/`, {
...options,
@@ -23,7 +23,7 @@ export interface WalletTransaction {
}
// required scope: esi-wallet.read_character_wallet.v1
export function getCharacterWalletTransactions(options: EsiOptions, fromId: number) {
export function getCharacterWalletTransactions(options: EsiOptions, fromId: number): Promise<Partial<WalletTransaction>[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-wallet.read_character_wallet.v1']);
return esiFetch<Partial<WalletTransaction>[]>(`/characters/${character_id}/wallet/transactions/`, {
...options,
@@ -49,7 +49,7 @@ export interface WalletJournalEntry {
}
// required scope: esi-wallet.read_character_wallet.v1
export function getCharacterWalletJournal(options: EsiOptions, page: number = 1) {
export function getCharacterWalletJournal(options: EsiOptions, page: number = 1): Promise<Partial<WalletJournalEntry>[]> {
const character_id = checkScopesAndGetCharacterId(options, ESI_SCOPE['esi-wallet.read_character_wallet.v1']);
return esiFetch<Partial<WalletJournalEntry>[]>(`/characters/${character_id}/wallet/journal/?page=${page}`, {
...options,

View File

@@ -0,0 +1,10 @@
// Get info for a blueprint
GET https://api.everef.net/v1/industry/cost
?blueprint_id=21028
&runs=40
&structure_type_id=35825
&security=NULL_SEC
&rig_id=43891
&rig_id=43892
&copying_cost=0.0168

View File

@@ -0,0 +1,94 @@
import createClient, { type FetchOptions, type FetchResponse } from 'openapi-fetch';
import type { components, paths } from './schema.d';
export * as schema from './schema.d';
const client = createClient<paths>({
baseUrl: 'https://api.everef.net',
});
export function getBlueprintCosts(blueprintTypeId: number): Promise<
FetchResponse<
{
parameters: {
query?: {
input?: components['schemas']['IndustryCostInput'];
};
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
200: {
headers: {
[name: string]: unknown;
};
content: {
'application/json': components['schemas']['IndustryCost'];
};
};
400: {
headers: {
[name: string]: unknown;
};
content: {
'application/json': components['schemas']['ApiError'];
};
};
500: {
headers: {
[name: string]: unknown;
};
content: {
'application/json': components['schemas']['ApiError'];
};
};
};
},
FetchOptions<{
parameters: {
query?: {
input?: components['schemas']['IndustryCostInput'];
};
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
200: {
headers: {
[name: string]: unknown;
};
content: {
'application/json': components['schemas']['IndustryCost'];
};
};
400: {
headers: {
[name: string]: unknown;
};
content: {
'application/json': components['schemas']['ApiError'];
};
};
500: {
headers: {
[name: string]: unknown;
};
content: {
'application/json': components['schemas']['ApiError'];
};
};
};
}>,
`${string}/${string}`
>
> {
return client.GET('/v1/industry/cost', {
query: {
blueprint_type_id: blueprintTypeId,
},
});
}

702
packages/lib/src/eve/everef/schema.d.ts vendored Normal file
View File

@@ -0,0 +1,702 @@
/**
* This file was auto-generated by openapi-typescript.
* Do not make direct changes to the file.
*/
export interface paths {
"/v1/industry/cost": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get: operations["industryCost"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/v1/search": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/**
* Search for inventory types
* @description Search for EVE Online inventory types by name
*/
get: operations["search"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
}
export type webhooks = Record<string, never>;
export interface components {
schemas: {
ApiError: {
message?: string;
};
CopyingCost: {
/** @description The alpha clone tax amount */
alpha_clone_tax?: number;
/** @description The estimated item value (EIV). This may not be completely accurate. */
estimated_item_value?: number;
/** @description The facility amount */
facility_tax?: number;
job_cost_base?: number;
materials?: {
[key: string]: components["schemas"]["MaterialCost"];
};
materials_volume?: number;
/** Format: int64 */
product_id?: number;
product_volume?: number;
/**
* Format: double
* @description The number of runs
*/
runs?: number;
/** @description The SCC surcharge amount */
scc_surcharge?: number;
/** @description Bonuses to system cost from structures, rigs, etc. */
system_cost_bonuses?: number;
/**
* @description The system cost index amount.
* Note that this will always be a slightly off, as the ESI does not report the full precision of the system cost index rates.
* See https://github.com/esi/esi-issues/issues/1411
*/
system_cost_index?: number;
time?: string;
total_cost?: number;
total_cost_per_run?: number;
/** @description The total amount of ISK required to start the job */
total_job_cost?: number;
total_material_cost?: number;
};
IndustryCost: {
copying?: {
[key: string]: components["schemas"]["CopyingCost"];
};
input?: components["schemas"]["IndustryCostInput"];
invention?: {
[key: string]: components["schemas"]["InventionCost"];
};
manufacturing?: {
[key: string]: components["schemas"]["ProductionCost"];
};
reaction?: {
[key: string]: components["schemas"]["ProductionCost"];
};
};
IndustryCostInput: {
/**
* Format: int32
* @description The Advanced Capital Ship Construction skill level the installing character
* @default 5
*/
advanced_capital_ship_construction: number;
/**
* Format: int32
* @description The Advanced Industrial Ship Construction skill level the installing character
* @default 5
*/
advanced_industrial_ship_construction: number;
/**
* Format: int32
* @description The Advanced Industry skill level the installing character
* @default 5
*/
advanced_industry: number;
/**
* Format: int32
* @description The Advanced Large Ship Construction skill level the installing character
* @default 5
*/
advanced_large_ship_construction: number;
/**
* Format: int32
* @description The Advanced Medium Ship Construction skill level the installing character
* @default 5
*/
advanced_medium_ship_construction: number;
/**
* Format: int32
* @description The Advanced Small Ship Construction skill level the installing character
* @default 5
*/
advanced_small_ship_construction: number;
/**
* @description Whether installing character is an alpha clone or not
* @default false
*/
alpha: boolean;
/**
* Format: int32
* @description The Amarr Encryption Methods skill level the installing character
* @default 5
*/
amarr_encryption_methods: number;
/**
* Format: int32
* @description The Amarr Starship Engineering skill level the installing character
* @default 5
*/
amarr_starship_engineering: number;
/**
* Format: int64
* @description The blueprint ID to calculate
*/
blueprint_id?: number;
/**
* Format: int32
* @description The Caldari Encryption Methods skill level the installing character
* @default 5
*/
caldari_encryption_methods: number;
/**
* Format: int32
* @description The Caldari Starship Engineering skill level the installing character
* @default 5
*/
caldari_starship_engineering: number;
/** @description The copying cost index of the system where the job is installed */
copying_cost?: number;
/**
* Format: int32
* @description The Core Subsystem Technology skill level the installing character
* @default 5
*/
core_subsystem_technology: number;
/**
* Format: int64
* @description The decryptor type ID to use
*/
decryptor_id?: number;
/**
* Format: int32
* @description The Defensive Subsystem Technology skill level the installing character
* @default 5
*/
defensive_subsystem_technology: number;
/**
* Format: int32
* @description The Electromagnetic Physics skill level the installing character
* @default 5
*/
electromagnetic_physics: number;
/**
* Format: int32
* @description The Electronic Engineering skill level the installing character
* @default 5
*/
electronic_engineering: number;
/**
* @description The facility tax rate of the station or structure where the job is installed
* @default 0
*/
facility_tax: number;
/**
* Format: int32
* @description The Gallente Encryption Methods skill level the installing character
* @default 5
*/
gallente_encryption_methods: number;
/**
* Format: int32
* @description The Gallente Starship Engineering skill level the installing character
* @default 5
*/
gallente_starship_engineering: number;
/**
* Format: int32
* @description The Graviton Physics skill level the installing character
* @default 5
*/
graviton_physics: number;
/**
* Format: int32
* @description The High Energy Physics skill level the installing character
* @default 5
*/
high_energy_physics: number;
/**
* Format: int32
* @description The Hydromagnetic Physics skill level the installing character
* @default 5
*/
hydromagnetic_physics: number;
/**
* Format: int32
* @description The Industry skill level the installing character
* @default 5
*/
industry: number;
/** @description The invention cost index of the system where the job is installed */
invention_cost?: number;
/**
* Format: int32
* @description The Laser Physics skill level the installing character
* @default 5
*/
laser_physics: number;
/** @description The manufacturing cost index of the system where the job is installed */
manufacturing_cost?: number;
/**
* @description Where to get material prices from
* @default ESI_AVG
* @enum {string}
*/
material_prices: "ESI_AVG" | "FUZZWORK_JITA_SELL_MIN" | "FUZZWORK_JITA_SELL_AVG" | "FUZZWORK_JITA_BUY_MAX" | "FUZZWORK_JITA_BUY_AVG";
/**
* Format: int32
* @description The material efficiency of the blueprint. Defaults to 10 for T1 products or invention output ME to T2 products
*/
me?: number;
/**
* Format: int32
* @description The Mechanical Engineering skill level the installing character
* @default 5
*/
mechanical_engineering: number;
/**
* Format: int32
* @description The Metallurgy skill level the installing character
* @default 5
*/
metallurgy: number;
/**
* Format: int32
* @description The Minmatar Encryption Methods skill level the installing character
* @default 5
*/
minmatar_encryption_methods: number;
/**
* Format: int32
* @description The Minmatar Starship Engineering skill level the installing character
* @default 5
*/
minmatar_starship_engineering: number;
/**
* Format: int32
* @description The Molecular Engineering skill level the installing character
* @default 5
*/
molecular_engineering: number;
/**
* Format: int32
* @description The Mutagenic Stabilization skill level the installing character
* @default 5
*/
mutagenic_stabilization: number;
/**
* Format: int32
* @description The Nanite Engineering skill level the installing character
* @default 5
*/
nanite_engineering: number;
/**
* Format: int32
* @description The Nuclear Physics skill level the installing character
* @default 5
*/
nuclear_physics: number;
/**
* Format: int32
* @description The Offensive Subsystem Technology skill level the installing character
* @default 5
*/
offensive_subsystem_technology: number;
/**
* Format: int32
* @description The Outpost Construction skill level the installing character
* @default 5
*/
outpost_construction: number;
/**
* Format: int32
* @description The Plasma Physics skill level the installing character
* @default 5
*/
plasma_physics: number;
/**
* Format: int64
* @description The desired product type ID
*/
product_id?: number;
/**
* Format: int32
* @description The Propulsion Subsystem Technology skill level the installing character
* @default 5
*/
propulsion_subsystem_technology: number;
/**
* Format: int32
* @description The Quantum Physics skill level the installing character
* @default 5
*/
quantum_physics: number;
/** @description The reaction cost index of the system where the job is installed */
reaction_cost?: number;
/**
* Format: int32
* @description The Reactions skill level the installing character
* @default 5
*/
reactions: number;
/**
* Format: int32
* @description The Research skill level the installing character
* @default 5
*/
research: number;
/** @description The researching material efficiency cost index of the system where the job is installed */
researching_me_cost?: number;
/** @description The researching time efficiency cost index of the system where the job is installed */
researching_te_cost?: number;
/** @description The type IDs of the rigs installed on the sture structure where the job is installed */
rig_id?: number[];
/**
* Format: int32
* @description The Rocket Science skill level the installing character
* @default 5
*/
rocket_science: number;
/**
* Format: int32
* @description The number of runs
* @default 1
*/
runs: number;
/**
* Format: int32
* @description The Science skill level the installing character
* @default 5
*/
science: number;
/**
* @description The security class of the system where the job is installed. If neither security nor system is supplied, high sec is assumed
* @enum {string}
*/
security?: "HIGH_SEC" | "LOW_SEC" | "NULL_SEC";
/**
* Format: int32
* @description The Sleeper Encryption Methods skill level the installing character
* @default 5
*/
sleeper_encryption_methods: number;
/**
* Format: int64
* @description The type ID of the structure where the job is installed. If not set, an NPC station is assumed.
*/
structure_type_id?: number;
/**
* @description Bonus to apply to system cost, such as the faction warfare bonus
* @default 0
* @example -0.5
*/
system_cost_bonus: number;
/**
* Format: int32
* @description The ID of the system where the job is installed. This will resolve security class and cost indices. If neither security nor system is supplied, high sec is assumed
*/
system_id?: number;
/**
* Format: int32
* @description The time efficiency of the blueprint. Defaults to 20 for T1 products or invention output TE to T2 products
*/
te?: number;
/**
* Format: int32
* @description The Triglavian Encryption Methods skill level the installing character
* @default 5
*/
triglavian_encryption_methods: number;
/**
* Format: int32
* @description The Triglavian Quantum Engineering skill level the installing character
* @default 5
*/
triglavian_quantum_engineering: number;
/**
* Format: int32
* @description The Upwell Encryption Methods skill level the installing character
* @default 5
*/
upwell_encryption_methods: number;
/**
* Format: int32
* @description The Upwell Starship Engineering skill level the installing character
* @default 5
*/
upwell_starship_engineering: number;
};
InventionCost: {
/** @description The alpha clone tax amount */
alpha_clone_tax?: number;
avg_cost_per_copy?: number;
avg_cost_per_run?: number;
avg_cost_per_unit?: number;
avg_time_per_copy?: string;
avg_time_per_run?: string;
avg_time_per_unit?: string;
/**
* Format: int64
* @description The source blueprint of the invention
*/
blueprint_id?: number;
/** @description The estimated item value (EIV). This may not be completely accurate. */
estimated_item_value?: number;
/** Format: double */
expected_copies?: number;
/** Format: double */
expected_runs?: number;
/** Format: double */
expected_units?: number;
/** @description The facility amount */
facility_tax?: number;
job_cost_base?: number;
materials?: {
[key: string]: components["schemas"]["MaterialCost"];
};
materials_volume?: number;
/**
* Format: int32
* @description The material efficiency of the invented blueprint
*/
me?: number;
/** Format: double */
probability?: number;
/** Format: int64 */
product_id?: number;
product_volume?: number;
/**
* Format: double
* @description The number of runs
*/
runs?: number;
/**
* Format: int32
* @description The number of runs on each successfully invented copy
*/
runs_per_copy?: number;
/** @description The SCC surcharge amount */
scc_surcharge?: number;
/** @description Bonuses to system cost from structures, rigs, etc. */
system_cost_bonuses?: number;
/**
* @description The system cost index amount.
* Note that this will always be a slightly off, as the ESI does not report the full precision of the system cost index rates.
* See https://github.com/esi/esi-issues/issues/1411
*/
system_cost_index?: number;
/**
* Format: int32
* @description The time efficiency of the invented blueprint
*/
te?: number;
time?: string;
total_cost?: number;
/** @description The total amount of ISK required to start the job */
total_job_cost?: number;
total_material_cost?: number;
/** Format: int32 */
units_per_run?: number;
};
MaterialCost: {
cost?: number;
cost_per_unit?: number;
/** Format: double */
quantity?: number;
/** Format: int64 */
type_id?: number;
volume?: number;
volume_per_unit?: number;
};
ProductionCost: {
/** @description The alpha clone tax amount */
alpha_clone_tax?: number;
/**
* Format: int64
* @description The source blueprint of the manufacture
*/
blueprint_id?: number;
/** @description The estimated item value (EIV). This may not be completely accurate. */
estimated_item_value?: number;
/** @description The facility amount */
facility_tax?: number;
materials?: {
[key: string]: components["schemas"]["MaterialCost"];
};
materials_volume?: number;
/**
* Format: int32
* @description The material efficiency used
*/
me?: number;
/** Format: int64 */
product_id?: number;
product_volume?: number;
/**
* Format: double
* @description The number of runs
*/
runs?: number;
/** @description The SCC surcharge amount */
scc_surcharge?: number;
/** @description Bonuses to system cost from structures, rigs, etc. */
system_cost_bonuses?: number;
/**
* @description The system cost index amount.
* Note that this will always be a slightly off, as the ESI does not report the full precision of the system cost index rates.
* See https://github.com/esi/esi-issues/issues/1411
*/
system_cost_index?: number;
/**
* Format: int32
* @description The time efficiency used
*/
te?: number;
time?: string;
time_per_run?: string;
time_per_unit?: string;
total_cost?: number;
total_cost_per_run?: number;
total_cost_per_unit?: number;
/** @description The total amount of ISK required to start the job */
total_job_cost?: number;
total_material_cost?: number;
/**
* Format: int64
* @description Total number of item produced
*/
units?: number;
/**
* Format: int64
* @description Total number of item produced
*/
units_per_run?: number;
};
SearchEntry: {
/** Format: int64 */
id?: number;
language?: string;
/**
* Format: int64
* @description Relevance score of the search result. Lower is better.
*/
relevance?: number;
title?: string;
/** @enum {string} */
type?: "inventory_type" | "market_group" | "category" | "group";
type_name?: string;
urls?: components["schemas"]["SearchEntryUrls"];
};
SearchEntryUrls: {
everef?: string;
reference_data?: string;
};
SearchResult: {
entries?: components["schemas"]["SearchEntry"][];
};
};
responses: never;
parameters: never;
requestBodies: never;
headers: never;
pathItems: never;
}
export type $defs = Record<string, never>;
export interface operations {
industryCost: {
parameters: {
query?: {
input?: components["schemas"]["IndustryCostInput"];
};
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description Success */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["IndustryCost"];
};
};
/** @description Client error */
400: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["ApiError"];
};
};
/** @description Server error */
500: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["ApiError"];
};
};
};
};
search: {
parameters: {
query: {
/** @description Search query (minimum 3 characters) */
q: string;
};
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description Success */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["SearchResult"];
};
};
/** @description Client error */
400: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["ApiError"];
};
};
/** @description Server error */
500: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["ApiError"];
};
};
};
};
}

View File

@@ -1,4 +1,4 @@
export * from './esi/index';
export * from './esi';
export * from './db';
export * from './ref';
export * from './third-party';

View File

View File

View File

@@ -0,0 +1,19 @@
import { schema } from '@/eve/everef';
export type IndustryCostInput = schema.components['schemas']['IndustryCostInput'];
export type GeneralOptions = Pick<IndustryCostInput, 'alpha' | 'runs' | 'product_id' | 'material_prices'>;
export type LocationOptions = Pick<
IndustryCostInput,
'system_id' | 'structure_type_id' | 'rig_id' | 'system_cost_bonus' | 'security' | 'facility_tax'
>;
export type IndexOptions = Pick<
IndustryCostInput,
'researching_me_cost' | 'researching_te_cost' | 'manufacturing_cost' | 'copying_cost' | 'invention_cost' | 'reaction_cost'
>;
export type BlueprintOptions = Pick<IndustryCostInput, 'blueprint_id' | 'copying_cost' | 'me' | 'te' | 'decryptor_id'>;
export type Skills = Omit<IndustryCostInput, keyof BlueprintOptions | keyof IndexOptions | keyof LocationOptions | keyof GeneralOptions>;

View File

@@ -18,7 +18,7 @@ export interface Attribute {
readonly tooltip_description?: LocalizedString;
}
export const getAttribute = (id: number) => {
export const getAttribute = (id: number): Attribute => {
if (!dataSets.loaded) loadModels();
const data = dataSets.dogma_attributes[String(id)];
if (!data) throw new Error(`Attribute ID ${id} not found in reference data`);

View File

@@ -37,14 +37,21 @@ export interface Blueprint {
};
}
export function getBlueprint(blueprint_type_id: number) {
export function getBlueprint(blueprint_type_id: number): Blueprint {
if (!dataSets.loaded) loadModels();
const data = dataSets.blueprints[String(blueprint_type_id)];
if (!data) throw new Error(`Blueprint Type ID ${blueprint_type_id} not found in reference data`);
return data;
}
export function getManufacturingMaterials(blueprint: Blueprint) {
export function getManufacturingMaterials(blueprint: Blueprint):
| any[]
| Promise<
{
type: Type;
quantity: number;
}[]
> {
const manufacturing = blueprint.activities[ActivityType.MANUFACTURING];
if (!manufacturing) return [];
@@ -56,7 +63,14 @@ export function getManufacturingMaterials(blueprint: Blueprint) {
);
}
export function getManufacturingProducts(blueprint: Blueprint) {
export function getManufacturingProducts(blueprint: Blueprint):
| any[]
| Promise<
{
type: Type;
quantity: number;
}[]
> {
const manufacturing = blueprint.activities[ActivityType.MANUFACTURING];
if (!manufacturing) return [];
@@ -68,7 +82,14 @@ export function getManufacturingProducts(blueprint: Blueprint) {
);
}
export function getInventionMaterials(blueprint: Blueprint) {
export function getInventionMaterials(blueprint: Blueprint):
| any[]
| Promise<
{
type: Type;
quantity: number;
}[]
> {
const invention = blueprint.activities[ActivityType.INVENTION];
if (!invention) return [];
@@ -80,7 +101,14 @@ export function getInventionMaterials(blueprint: Blueprint) {
);
}
export function getInventionProducts(blueprint: Blueprint) {
export function getInventionProducts(blueprint: Blueprint):
| any[]
| Promise<
{
type: Type;
quantity: number;
}[]
> {
const invention = blueprint.activities[ActivityType.INVENTION];
if (!invention) return [];
@@ -92,7 +120,14 @@ export function getInventionProducts(blueprint: Blueprint) {
);
}
export function getInventionSkills(blueprint: Blueprint) {
export function getInventionSkills(blueprint: Blueprint):
| any[]
| Promise<
{
type: Type;
level: number;
}[]
> {
const invention = blueprint.activities[ActivityType.INVENTION];
if (!invention) return [];

View File

@@ -27,7 +27,7 @@ export interface Category {
readonly icon_id?: number;
}
export function getCategory(category_id: number) {
export function getCategory(category_id: number): Category {
if (!dataSets.loaded) loadModels();
const data = dataSets.categories[String(category_id)];
if (!data) throw new Error(`Category ID ${category_id} not found in reference data`);

View File

@@ -1,4 +1,4 @@
import { getAttribute } from './attribute';
import { getAttribute, type Attribute } from './attribute';
import type { LocalizedString } from './shared-types';
import { dataSets, loadModels } from './loadModels';
@@ -37,29 +37,29 @@ export interface Effect {
readonly name: string;
}
export function getEffect(effect_id: number) {
export function getEffect(effect_id: number): Effect {
if (!dataSets.loaded) loadModels();
const data = dataSets.dogma_effects[String(effect_id)];
if (!data) throw new Error(`Effect ID ${effect_id} not found in reference data`);
return data;
}
export function getDischargeAttribute(effect: Effect) {
export function getDischargeAttribute(effect: Effect): Attribute {
return effect.discharge_attribute_id && getAttribute(effect.discharge_attribute_id);
}
export function getFalloffAttribute(effect: Effect) {
export function getFalloffAttribute(effect: Effect): Attribute {
return effect.falloff_attribute_id && getAttribute(effect.falloff_attribute_id);
}
export function getDurationAttribute(effect: Effect) {
export function getDurationAttribute(effect: Effect): Attribute {
return effect.duration_attribute_id && getAttribute(effect.duration_attribute_id);
}
export function getRangeAttribute(effect: Effect) {
export function getRangeAttribute(effect: Effect): Attribute {
return effect.range_attribute_id && getAttribute(effect.range_attribute_id);
}
export function getTrackingSpeedAttribute(effect: Effect) {
export function getTrackingSpeedAttribute(effect: Effect): Attribute {
return effect.tracking_speed_attribute_id && getAttribute(effect.tracking_speed_attribute_id);
}

View File

@@ -13,7 +13,7 @@ export interface Group {
readonly use_base_price: boolean;
readonly type_ids?: number[];
}
export function getGroup(group_id: number) {
export function getGroup(group_id: number): Group {
if (!dataSets.loaded) loadModels();
const data = dataSets.groups[String(group_id)];
if (!data) throw new Error(`Group ID ${group_id} not found in reference data`);

View File

@@ -14,7 +14,7 @@ export interface Icon {
readonly file: string;
}
export function getIcon(icon_id: number) {
export function getIcon(icon_id: number): Icon {
if (!dataSets.loaded) loadModels();
const data = dataSets.icons[String(icon_id)];
if (!data) throw new Error(`Icon ID ${icon_id} not found in reference data`);
@@ -33,9 +33,5 @@ export function getIconUrl(
isBpc?: boolean;
} = {},
): string {
return `https://images.evetech.net/types/${icon_id}/icon${
isBp ? '/bp'
: isBpc ? '/bpc'
: ''
}?size=${size}`;
return `https://images.evetech.net/types/${icon_id}/icon${isBp ? '/bp' : isBpc ? '/bpc' : ''}?size=${size}`;
}

View File

@@ -32,7 +32,7 @@ const dataSets = {
types: {} as Record<string, Type>,
units: {} as Record<string, Unit>,
};
export async function loadModels() {
export async function loadModels(): Promise<void> {
dataSets.dogma_attributes = JSON.parse(fs.readFileSync(join(__dirname, '../data/reference-data/dogma_attributes.json')).toString());
dataSets.blueprints = JSON.parse(fs.readFileSync(join(__dirname, '../data/reference-data/blueprints.json')).toString());
dataSets.categories = JSON.parse(fs.readFileSync(join(__dirname, '../data/reference-data/categories.json')).toString());

View File

@@ -11,7 +11,7 @@ export interface MarketGroup {
readonly has_types: boolean;
}
export function getMarketGroup(market_group_id: number) {
export function getMarketGroup(market_group_id: number): MarketGroup {
if (!dataSets.loaded) loadModels();
const data = dataSets.market_groups[String(market_group_id)];
if (!data) throw new Error(`Market group ID ${market_group_id} not found in reference data`);

View File

@@ -9,7 +9,7 @@ export interface MetaGroup {
readonly icon_suffix?: string;
}
export function getMetaGroup(meta_group_id: number) {
export function getMetaGroup(meta_group_id: number): MetaGroup {
if (!dataSets.loaded) loadModels();
const data = dataSets.meta_groups[String(meta_group_id)];
if (!data) throw new Error(`Meta group ID ${meta_group_id} not found in reference data`);

View File

@@ -16,7 +16,7 @@ export interface Region {
readonly name: LocalizedString;
}
export function getRegion(region_id: number) {
export function getRegion(region_id: number): Region {
if (!dataSets.loaded) loadModels();
const data = dataSets.regions[String(region_id)];
if (!data) throw new Error(`Region ID ${region_id} not found in reference data`);

View File

@@ -1,5 +1,5 @@
import type { LocalizedString, TypeIDQuantity } from './shared-types';
import { getType } from './type';
import { getType, type Type } from './type';
import { dataSets, loadModels } from './loadModels';
export interface Schematic {
@@ -11,27 +11,33 @@ export interface Schematic {
readonly pin_type_ids: number[];
}
export function getSchematic(schematic_id: number) {
export function getSchematic(schematic_id: number): Schematic {
if (!dataSets.loaded) loadModels();
const data = dataSets.schematics[String(schematic_id)];
if (!data) throw new Error(`Schematic ID ${schematic_id} not found in reference data`);
return data;
}
export function getMaterialQuantities(schematic: Schematic) {
export function getMaterialQuantities(schematic: Schematic): {
type: Type;
quantity: number;
}[] {
return Object.entries(schematic.materials).map(([type_id, { quantity }]) => ({
type: getType(Number(type_id)),
quantity,
}));
}
export function getProductQuantities(schematic: Schematic) {
export function getProductQuantities(schematic: Schematic): {
type: Type;
quantity: number;
}[] {
return Object.entries(schematic.products).map(([type_id, { quantity }]) => ({
type: getType(Number(type_id)),
quantity,
}));
}
export function getPinTypes(schematic: Schematic) {
export function getPinTypes(schematic: Schematic): Type[] {
return schematic.pin_type_ids.map(getType);
}

View File

@@ -11,26 +11,26 @@ export interface Skill {
readonly required_skills?: { [skill_type_id: string]: number }; // skill_type_id : level
}
export function getSkill(type_id: number) {
export function getSkill(type_id: number): Skill {
if (!dataSets.loaded) loadModels();
const data = dataSets.skills[String(type_id)];
if (!data) throw new Error(`Skill ID ${type_id} not found in reference data`);
return data;
}
export function getPrimaryDogmaAttribute(skill: Skill) {
export function getPrimaryDogmaAttribute(skill: Skill): Attribute {
return getAttribute(skill.primary_dogma_attribute_id);
}
export function getSecondaryDogmaAttribute(skill: Skill) {
export function getSecondaryDogmaAttribute(skill: Skill): Attribute {
return getAttribute(skill.secondary_dogma_attribute_id);
}
export function getPrimaryCharacterAttribute(skill: Skill) {
export function getPrimaryCharacterAttribute(skill: Skill): Attribute {
return getAttribute(skill.primary_character_attribute_id);
}
export function getSecondaryCharacterAttribute(skill: Skill) {
export function getSecondaryCharacterAttribute(skill: Skill): Attribute {
return getAttribute(skill.secondary_character_attribute_id);
}

View File

@@ -1,9 +1,16 @@
import type { AttributeIDValue, BlueprintTypeIDActivity, EffectIDDefault, LocalizedString, MaterialIDQuantity } from './shared-types';
import type {
ActivityType,
AttributeIDValue,
BlueprintTypeIDActivity,
EffectIDDefault,
LocalizedString,
MaterialIDQuantity,
} from './shared-types';
import { IconSize } from './icon';
import { getUnit, type Unit } from './unit';
import { CommonAttribute, getAttribute } from './attribute';
import { getGroup } from './group';
import { getMetaGroup } from './meta-group';
import { CommonAttribute, getAttribute, type Attribute } from './attribute';
import { getGroup, type Group } from './group';
import { getMetaGroup, type MetaGroup } from './meta-group';
import { dataSets, loadModels } from './loadModels';
interface Masteries {
@@ -67,7 +74,7 @@ export interface Type {
readonly is_blueprint?: boolean;
}
export function getType(type_id: number) {
export function getType(type_id: number): Type {
if (!dataSets.loaded) loadModels();
const data = dataSets.types[String(type_id)];
if (!data) throw new Error(`Type ID ${type_id} not found in reference data`);
@@ -114,7 +121,12 @@ export function getSkillBonuses(type: Type): {
return skillBonuses;
}
export function getRoleBonuses(type: Type) {
export function getRoleBonuses(type: Type): {
bonus: number;
bonus_text: LocalizedString;
importance: number;
unit: Unit;
}[] {
if (!type.traits || !type.traits.role_bonuses) return [];
return Object.values(type.traits.role_bonuses).map((bonus) => ({
bonus: bonus.bonus,
@@ -136,7 +148,7 @@ export function eveTycoonLink(type_id: number) {
return `https://evetycoon.com/market/${type_id}`;
}
export function getTypeAttributes(type: Type) {
export function getTypeAttributes(type: Type): any[] {
if (!type.dogma_attributes) return [];
Object.keys(type.dogma_attributes).map((attribute_id) => ({
attribute: getAttribute(Number(attribute_id)),
@@ -144,7 +156,7 @@ export function getTypeAttributes(type: Type) {
}));
}
export function typeHasAnyAttribute(type: Type, attribute_ids: CommonAttribute[]) {
export function typeHasAnyAttribute(type: Type, attribute_ids: CommonAttribute[]): boolean {
if (!type.dogma_attributes) return false;
for (const attribute_id of attribute_ids) {
if (type.dogma_attributes[attribute_id]) return true;
@@ -152,7 +164,10 @@ export function typeHasAnyAttribute(type: Type, attribute_ids: CommonAttribute[]
return false;
}
export function getTypeSkills(type: Type) {
export function getTypeSkills(type: Type): {
skill: Type;
level: number;
}[] {
if (!type.required_skills) return [];
return Object.keys(type.required_skills).map((skill_type_id) => ({
skill: getType(Number(skill_type_id)),
@@ -160,7 +175,13 @@ export function getTypeSkills(type: Type) {
}));
}
export function typeGetAttribute(type: Type, attribute_id: number) {
export function typeGetAttribute(
type: Type,
attribute_id: number,
): {
attribute: Attribute;
value: number;
} {
if (!type.dogma_attributes || !type.dogma_attributes[attribute_id]) return null;
return {
attribute: getAttribute(attribute_id),
@@ -168,7 +189,10 @@ export function typeGetAttribute(type: Type, attribute_id: number) {
};
}
export function getTypeBlueprints(type: Type) {
export function getTypeBlueprints(type: Type): {
blueprint: Type;
activity: ActivityType;
}[] {
if (!type.produced_by_blueprints) return [];
return Object.values(type.produced_by_blueprints).map((blueprint) => ({
blueprint: getType(blueprint.blueprint_type_id),
@@ -176,22 +200,25 @@ export function getTypeBlueprints(type: Type) {
}));
}
export function getTypeSchematics(type: Type) {
export function getTypeSchematics(type: Type): Type[] {
return type.produced_by_schematic_ids?.map((schematic_id) => getType(schematic_id)) ?? [];
}
export function getTypeGroup(type: Type) {
export function getTypeGroup(type: Type): Group {
if (!type.group_id) return null;
return getGroup(type.group_id);
}
export function getTypeVariants(type: Type) {
export function getTypeVariants(type: Type): {
metaGroup: MetaGroup;
types: Type[];
}[] {
return Object.entries(type.type_variations || {}).map(([meta_group_id, variant_ids]) => ({
metaGroup: getMetaGroup(Number(meta_group_id)),
types: variant_ids.map((type_id) => getType(type_id)),
}));
}
export function typeHasAttributes(type: Type) {
export function typeHasAttributes(type: Type): boolean {
return type.dogma_attributes && Object.keys(type.dogma_attributes).length > 0;
}

View File

@@ -18,7 +18,7 @@ export interface Unit {
readonly name: LocalizedString;
}
export function getUnit(unit_id: number) {
export function getUnit(unit_id: number): Unit {
if (!dataSets.loaded) loadModels();
const unit = dataSets.units[String(unit_id)];
if (!unit) throw new Error(`Unit ID ${unit_id} not found in reference data`);
@@ -61,6 +61,6 @@ export function renderUnit(unit: Unit, value: number, locale: string = 'en'): st
}
}
export function isUnitInversePercentage(unit: Unit) {
export function isUnitInversePercentage(unit: Unit): boolean {
return unit.unit_id == 108 || unit.unit_id == 111;
}

View File

@@ -1,4 +1,4 @@
import { createLogger, format, transports } from 'winston';
import { createLogger, format, Logger, transports } from 'winston';
const development = 'development';
const production = 'production';
@@ -6,7 +6,7 @@ const NODE_ENV = process.env.NODE_ENV || development;
const LOG_LEVEL = process.env.LOG_LEVEL || NODE_ENV === development ? 'debug' : 'info';
const DEBUG = LOG_LEVEL === 'debug';
export function init(name: string = 'App') {
export function init(name: string = 'App'): Logger {
const jsonFormat = format.combine(format.timestamp(), format.json());
const logger = createLogger({
level: LOG_LEVEL,

View File

@@ -0,0 +1,10 @@
export function isPromise<T>(value: T | Promise<T>): value is Promise<T> {
return typeof (value as Promise<T>)?.then === 'function';
}
export async function awaitMaybePromise<T>(value: T | Promise<T>) {
if (isPromise(value)) {
return await value;
}
return Promise.resolve(value);
}

View File

@@ -0,0 +1,99 @@
import { Database } from 'bun:sqlite';
export function dynamicInsert<T extends {} = any>(db: Database, table: string, item: T) {
const keys = Object.keys(item);
const values = keys.map((k) => item[k]);
const stmt = db.prepare(`INSERT INTO ${table} (${keys.join(', ')}) VALUES (${keys.map((k) => '?').join(', ')})`);
return stmt.run(...values).lastInsertRowid;
}
export function dynamicUpdate<T extends {} = any>(db: Database, table: string, item: T, key: string, keyValue: any) {
const keys = Object.keys(item);
const values = keys.map((k) => item[k]);
const stmt = db.prepare(`UPDATE ${table} SET ${keys.map((k) => `${k} = ?`).join(', ')} WHERE ${key} = ?`);
return stmt.run(...values, keyValue).changes;
}
export interface QueryOptions<T extends {}> {
skip?: number;
limit?: number;
orderBy?: Partial<Record<keyof T, 'ASC' | 'DESC'>>;
}
type Where<T> = Partial<Record<keyof T, any>> & { or?: Partial<Record<keyof T, any>> };
export function select<T extends {}>(db: Database, table: string, options?: QueryOptions<T>, select: string = '*', where?: Where<T>): T[] {
let query = `SELECT ${select} FROM ${table}\n`;
const values = [];
if (where) {
query += `WHERE `;
if (where.or) {
query += `${Object.keys(where.or)
.map((k) => {
values.push(where.or[k]);
return `${k} = ?\n`;
})
.join('OR ')}`;
delete where.or;
}
if (Object.keys(where).length > 0) {
query += `${Object.keys(where)
.map((k) => {
values.push(where[k]);
return `${k} = ?\n`;
})
.join('AND ')}`;
}
}
if (options?.orderBy) {
query += `ORDER BY\n${Object.keys(options.orderBy)
.map((k) => {
values.push(options.orderBy[k]);
return `${k} ?`;
})
.join(',\n')}\n`;
}
if (options?.skip) {
values.push(options.limit || -1);
values.push(options.skip);
query += `LIMIT ? OFFSET ?`;
}
return db.prepare(query).all(...values) as T[];
}
export function remove<T>(db: Database, table: string, where?: Where<T>) {
let query = `DELETE FROM ${table}`;
const values = [];
if (where) {
query += `WHERE `;
if (where.or) {
query += `${Object.keys(where.or)
.map((k) => {
values.push(where.or[k]);
return `${k} = ?\n`;
})
.join('OR ')}`;
delete where.or;
}
if (Object.keys(where).length > 0) {
query += `${Object.keys(where)
.map((k) => {
values.push(where[k]);
return `${k} = ?\n`;
})
.join('AND ')}`;
}
}
return db.prepare(query).run(...values).changes;
}

View File

@@ -19,7 +19,7 @@
"noEmitOnError": false,
"declaration": true,
"outDir": "dist/types",
"rootDir": "src",
"rootDir": ".",
"allowImportingTsExtensions": true
},
"include": ["src", "types", "src/jsx/types.d.ts", "scripts"],

View File

@@ -19,7 +19,8 @@ export default defineConfig([
platform: 'node',
dts: true,
minify: false,
mangle: false,
sourcemap: true,
unbundle: true,
external: ['bun:sqlite', 'bun'],
},
]);