commands
This commit is contained in:
+53
-7
@@ -1,18 +1,64 @@
|
|||||||
#ifndef HAMMY_COMMAND_H
|
#ifndef HAMMY_COMMAND_H
|
||||||
#define HAMMY_COMMAND_H
|
#define HAMMY_COMMAND_H
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <concord/discord.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include <hammy/types.h>
|
#include <hammy/types.h>
|
||||||
|
|
||||||
|
// A command handler. Runs either on the gateway thread (instant commands) or on
|
||||||
|
// a worker thread (deferred ones), so it must not assume either. client is
|
||||||
|
// whichever client is correct for the calling thread; use it and nothing else.
|
||||||
|
//
|
||||||
|
// Instant handlers send a fresh interaction response.
|
||||||
|
// Deferred handlers EDIT the already-deferred response, via hammy_job_reply()
|
||||||
|
// or discord_edit_original_interaction_response().
|
||||||
|
//
|
||||||
|
// The handler does NOT own the job and must not destroy it.
|
||||||
|
typedef void (*hammy_command_fn)(const hammy_job_t* job, struct discord* client);
|
||||||
|
|
||||||
|
// Plain old data. Every field points at static storage, so this struct owns
|
||||||
|
// nothing, copies freely, and needs no destructor - the vector can be created
|
||||||
|
// with a NULL destructor hook.
|
||||||
struct hammy_command_t {
|
struct hammy_command_t {
|
||||||
// TODO: add shit here
|
const char* name; // Discord command name. Not owning; expected to be a literal.
|
||||||
char empty;
|
const char* description; // Not owning.
|
||||||
|
|
||||||
|
// Registration options, or NULL for none. Not owning; point at a static
|
||||||
|
// struct discord_application_command_options, e.g.
|
||||||
|
//
|
||||||
|
// static struct discord_application_command_option grid_opt_array[] = {
|
||||||
|
// { .type = DISCORD_APPLICATION_OPTION_STRING, .name = "locator",
|
||||||
|
// .description = "Maidenhead grid", .required = true },
|
||||||
|
// };
|
||||||
|
// static struct discord_application_command_options grid_opts = {
|
||||||
|
// .size = 1, .array = grid_opt_array
|
||||||
|
// };
|
||||||
|
struct discord_application_command_options* options;
|
||||||
|
|
||||||
|
hammy_command_fn handler;
|
||||||
|
|
||||||
|
// true -> pure computation; answered inline on the gateway thread, no
|
||||||
|
// defer, no queue. Must complete in microseconds.
|
||||||
|
// false -> may block (HTTP, DB, file IO); deferred and queued to a worker.
|
||||||
|
//
|
||||||
|
// Marking a blocking command instant stalls the gateway and risks missed
|
||||||
|
// heartbeats. When unsure, leave it false.
|
||||||
|
bool instant;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Creates the command on the heap, returns NULL if failed.
|
// The built-in command table. Returns a pointer to static storage; outCount
|
||||||
hammy_command_t* hammy_command_create();
|
// receives the number of entries. Never NULL.
|
||||||
|
const hammy_command_t* hammy_command_builtins(size_t* outCount);
|
||||||
|
|
||||||
// Destroys a command.
|
// Linear lookup by name over a plain array. The table is small enough that
|
||||||
void hammy_command_destroy(void* cmd);
|
// anything cleverer is not worth the code.
|
||||||
|
// Returns NULL if not found.
|
||||||
|
const hammy_command_t* hammy_command_find(const hammy_command_t* commands, size_t count,
|
||||||
|
const char* name);
|
||||||
|
|
||||||
|
// True if the command is safe to run inline on the gateway thread.
|
||||||
|
// Tolerates NULL so callers can skip a null check.
|
||||||
|
bool hammy_command_is_instant(const hammy_command_t* command);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+45
-18
@@ -1,24 +1,51 @@
|
|||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include <hammy/command.h>
|
#include <hammy/command.h>
|
||||||
|
|
||||||
hammy_command_t* hammy_command_create() {
|
// Handlers live in src/hammy/commands/. Declared here rather than in a header
|
||||||
hammy_command_t* cmd = (hammy_command_t*)malloc(sizeof(hammy_command_t));
|
// so adding a command touches exactly two places: its own .c file and this
|
||||||
if (!cmd) {
|
// table.
|
||||||
|
void hammy_cmd_ping(const hammy_job_t* job, struct discord* client);
|
||||||
|
|
||||||
|
// The command table. Pure data - no allocation, no lifetime, no destructor.
|
||||||
|
// Copying an entry into the bot's vector is a plain struct copy, and the vector
|
||||||
|
// may be created with a NULL destructor hook.
|
||||||
|
//
|
||||||
|
// Field order: name, description, options, handler, instant.
|
||||||
|
static const hammy_command_t hammy_builtin_commands[] = {
|
||||||
|
{ "ping", "Check whether Hammy is alive and how fast it is responding",
|
||||||
|
NULL, &hammy_cmd_ping, true },
|
||||||
|
|
||||||
|
// Tier 0 commands go here as they land. All pure computation, so instant:
|
||||||
|
// { "grid", "Maidenhead locator conversions", &grid_opts, &hammy_cmd_grid, true },
|
||||||
|
// { "morse", "Text to and from Morse code", &morse_opts, &hammy_cmd_morse, true },
|
||||||
|
// { "band", "What band is a frequency in", &band_opts, &hammy_cmd_band, true },
|
||||||
|
//
|
||||||
|
// Anything touching the backend API, a database or the network is NOT
|
||||||
|
// instant:
|
||||||
|
// { "call", "Look up a callsign", &call_opts, &hammy_cmd_call, false },
|
||||||
|
};
|
||||||
|
|
||||||
|
const hammy_command_t* hammy_command_builtins(size_t* outCount) {
|
||||||
|
if (outCount)
|
||||||
|
*outCount = sizeof(hammy_builtin_commands) / sizeof(hammy_builtin_commands[0]);
|
||||||
|
|
||||||
|
return hammy_builtin_commands;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hammy_command_t* hammy_command_find(const hammy_command_t* commands, size_t count,
|
||||||
|
const char* name) {
|
||||||
|
if (!commands || !name) return NULL;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
if (commands[i].name && strcmp(commands[i].name, name) == 0)
|
||||||
|
return &commands[i];
|
||||||
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: set defaults
|
bool hammy_command_is_instant(const hammy_command_t* command) {
|
||||||
|
return command && command->instant;
|
||||||
return cmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
void hammy_command_destroy(void* cmd) {
|
|
||||||
if (!cmd) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd = (hammy_command_t*)cmd;
|
|
||||||
|
|
||||||
// TODO: Other things here eventually
|
|
||||||
|
|
||||||
free(cmd);
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user