embeds in responses
This commit is contained in:
+2
-1
@@ -198,7 +198,7 @@ else()
|
|||||||
-Wshadow
|
-Wshadow
|
||||||
-Wconversion
|
-Wconversion
|
||||||
-Wsign-conversion
|
-Wsign-conversion
|
||||||
-Wcast-qual
|
#-Wcast-qual
|
||||||
-Wcast-align
|
-Wcast-align
|
||||||
-Wstrict-prototypes
|
-Wstrict-prototypes
|
||||||
-Wmissing-prototypes
|
-Wmissing-prototypes
|
||||||
@@ -215,6 +215,7 @@ else()
|
|||||||
-Wundef
|
-Wundef
|
||||||
-Winit-self
|
-Winit-self
|
||||||
-Wmissing-include-dirs
|
-Wmissing-include-dirs
|
||||||
|
-Wno-discarded-qualifiers # Temp
|
||||||
)
|
)
|
||||||
|
|
||||||
if(CMAKE_C_COMPILER_ID MATCHES "GNU")
|
if(CMAKE_C_COMPILER_ID MATCHES "GNU")
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
#ifndef HAMMY_EMBEDS_H
|
||||||
|
#define HAMMY_EMBEDS_H
|
||||||
|
|
||||||
|
#include <concord/discord.h>
|
||||||
|
|
||||||
|
// Constructs a generic error embed and writes it into out. Returns true on success and false on failure.
|
||||||
|
// You must allocate space for exactly one embed in the out array. This function cannot check this so please make sure yourself.
|
||||||
|
static inline bool hammy_embeds_genericerror(struct discord *client, struct discord_embed *out) {
|
||||||
|
if (!out || !client) { return false; }
|
||||||
|
|
||||||
|
static struct discord_embed_footer footer = { .text = "Hammy Bot" };
|
||||||
|
|
||||||
|
out[0] = (struct discord_embed){
|
||||||
|
.title = "An Error Occurred",
|
||||||
|
.description = "An error occurred while processing your request.",
|
||||||
|
.color = 0xFF0000,
|
||||||
|
.timestamp = discord_timestamp(client),
|
||||||
|
.footer = &footer,
|
||||||
|
};
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructs a custom error embed and writes it into out. Returns true on success and false on failure.
|
||||||
|
// You must allocate space for exactly one embed in the out array. This function cannot check this so please make sure yourself.
|
||||||
|
static inline bool hammy_embeds_customerror(struct discord *client,
|
||||||
|
struct discord_embed *out,
|
||||||
|
struct discord_embed_fields *fieldsOut,
|
||||||
|
char *title,
|
||||||
|
char *description,
|
||||||
|
struct discord_embed_field *fields,
|
||||||
|
int fieldCount)
|
||||||
|
{
|
||||||
|
if (!out || !client || !title || !description) { return false; }
|
||||||
|
if (fieldCount < 0 || (fieldCount > 0 && !fields)) { return false; }
|
||||||
|
if (fieldCount > 0 && !fieldsOut) { return false; }
|
||||||
|
|
||||||
|
static struct discord_embed_footer footer = { .text = "Hammy Bot" };
|
||||||
|
|
||||||
|
if (fieldCount > 0) {
|
||||||
|
*fieldsOut = (struct discord_embed_fields){
|
||||||
|
.size = fieldCount,
|
||||||
|
.array = fields,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
out[0] = (struct discord_embed){
|
||||||
|
.title = title,
|
||||||
|
.description = description,
|
||||||
|
.color = 0xFF0000,
|
||||||
|
.timestamp = discord_timestamp(client),
|
||||||
|
.footer = &footer,
|
||||||
|
.fields = (fieldCount > 0) ? fieldsOut : NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructs a custom generic embed and writes it into out. Returns true on success and false on failure.
|
||||||
|
// You must allocate space for exactly one embed in the out array. This function cannot check this so please make sure yourself.
|
||||||
|
static inline bool hammy_embeds_customembed(struct discord *client,
|
||||||
|
struct discord_embed *out,
|
||||||
|
struct discord_embed_fields *fieldsOut,
|
||||||
|
char *title,
|
||||||
|
char *description,
|
||||||
|
struct discord_embed_field *fields,
|
||||||
|
int fieldCount,
|
||||||
|
int color)
|
||||||
|
{
|
||||||
|
if (!out || !client || !title || !description) { return false; }
|
||||||
|
if (fieldCount < 0 || (fieldCount > 0 && !fields)) { return false; }
|
||||||
|
if (fieldCount > 0 && !fieldsOut) { return false; }
|
||||||
|
|
||||||
|
static struct discord_embed_footer footer = { .text = "Hammy Bot" };
|
||||||
|
|
||||||
|
if (fieldCount > 0) {
|
||||||
|
*fieldsOut = (struct discord_embed_fields){
|
||||||
|
.size = fieldCount,
|
||||||
|
.array = fields,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
out[0] = (struct discord_embed){
|
||||||
|
.title = title,
|
||||||
|
.description = description,
|
||||||
|
.color = color,
|
||||||
|
.timestamp = discord_timestamp(client),
|
||||||
|
.footer = &footer,
|
||||||
|
.fields = (fieldCount > 0) ? fieldsOut : NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
+2
-2
@@ -56,10 +56,10 @@ void hammy_job_run(hammy_job_t* job, struct discord* client);
|
|||||||
|
|
||||||
// Edits the (already deferred) interaction response with a plain text body.
|
// Edits the (already deferred) interaction response with a plain text body.
|
||||||
// Used by hammy_job_run() and by the pool's error paths.
|
// Used by hammy_job_run() and by the pool's error paths.
|
||||||
void hammy_job_reply(const hammy_job_t* job, struct discord* client, const char* content);
|
void hammy_job_reply(const hammy_job_t* job, struct discord* client, const char* title, const char* content, bool isError);
|
||||||
|
|
||||||
// Sends a fresh interaction response with a plain text body.
|
// Sends a fresh interaction response with a plain text body.
|
||||||
// Only valid on the instant path, where nothing has been sent yet.
|
// Only valid on the instant path, where nothing has been sent yet.
|
||||||
void hammy_job_respond(const hammy_job_t* job, struct discord* client, const char* content);
|
void hammy_job_respond(const hammy_job_t* job, struct discord* client, const char* title, const char* content, bool isError);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+1
-1
@@ -26,5 +26,5 @@ void hammy_cmd_ping(const hammy_job_t* job, struct discord* client) {
|
|||||||
"Pong!\nGateway: `%d ms`\nHandled in: `%" PRId64 " ms`",
|
"Pong!\nGateway: `%d ms`\nHandled in: `%" PRId64 " ms`",
|
||||||
gatewayMs, handledMs);
|
gatewayMs, handledMs);
|
||||||
|
|
||||||
hammy_job_respond(job, client, body);
|
hammy_job_respond(job, client, "Pong!", body, false);
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-5
@@ -9,6 +9,7 @@
|
|||||||
#include <hammy/command.h>
|
#include <hammy/command.h>
|
||||||
#include <hammy/job.h>
|
#include <hammy/job.h>
|
||||||
#include <hammy/pool.h>
|
#include <hammy/pool.h>
|
||||||
|
#include <hammy/embeds.h>
|
||||||
|
|
||||||
static void hammy_bot_attach(struct discord* client, hammy_bot_t* bot) {
|
static void hammy_bot_attach(struct discord* client, hammy_bot_t* bot) {
|
||||||
discord_set_data(client, bot);
|
discord_set_data(client, bot);
|
||||||
@@ -36,12 +37,26 @@ static void hammy_bot_on_interaction(struct discord* client, const struct discor
|
|||||||
|
|
||||||
struct discord_interaction_response params = {
|
struct discord_interaction_response params = {
|
||||||
.type = DISCORD_INTERACTION_CHANNEL_MESSAGE_WITH_SOURCE,
|
.type = DISCORD_INTERACTION_CHANNEL_MESSAGE_WITH_SOURCE,
|
||||||
.data = &(struct discord_interaction_callback_data){
|
};
|
||||||
.content = "I don't know that command!"
|
|
||||||
|
struct discord_embed embed[1];
|
||||||
|
CCORDcode code;
|
||||||
|
if (hammy_embeds_customerror(client, embed, NULL, "Unknown Command", "I don't know that command!", NULL, 0)) {
|
||||||
|
params.data = &(struct discord_interaction_callback_data){
|
||||||
|
.embeds = &(struct discord_embeds){
|
||||||
|
.size = 1,
|
||||||
|
.array = embed
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
CCORDcode code = discord_create_interaction_response(client, event->id, event->token, ¶ms, NULL);
|
code = discord_create_interaction_response(client, event->id, event->token, ¶ms, NULL);
|
||||||
|
} else {
|
||||||
|
params.data = &(struct discord_interaction_callback_data){
|
||||||
|
.content = "I don't know that command!"
|
||||||
|
};
|
||||||
|
|
||||||
|
code = discord_create_interaction_response(client, event->id, event->token, ¶ms, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
if (code != CCORD_OK) {
|
if (code != CCORD_OK) {
|
||||||
log_warn("[job] Failed to send interaction error response for interaction %" PRIu64 ": %d", event->id, code);
|
log_warn("[job] Failed to send interaction error response for interaction %" PRIu64 ": %d", event->id, code);
|
||||||
@@ -76,14 +91,14 @@ static void hammy_bot_on_interaction(struct discord* client, const struct discor
|
|||||||
break;
|
break;
|
||||||
case HAMMY_PUSH_FULL:
|
case HAMMY_PUSH_FULL:
|
||||||
log_warn("[bot] Queue pool full. Cannot queue job for interaction '%s'.", name ? name : "(null)");
|
log_warn("[bot] Queue pool full. Cannot queue job for interaction '%s'.", name ? name : "(null)");
|
||||||
hammy_job_reply(job, client, "I am a bit busy right now! Please try again later.");
|
hammy_job_reply(job, client, "Bot is Busy", "I am a bit busy right now! Please try again later.", true);
|
||||||
hammy_job_destroy(&job);
|
hammy_job_destroy(&job);
|
||||||
break;
|
break;
|
||||||
case HAMMY_PUSH_SHUTDOWN:
|
case HAMMY_PUSH_SHUTDOWN:
|
||||||
// Also covers the window before on_ready starts the pool, since
|
// Also covers the window before on_ready starts the pool, since
|
||||||
// hammy_pool_push() reports a NULL pool the same way.
|
// hammy_pool_push() reports a NULL pool the same way.
|
||||||
log_error("[bot] Worker pool unavailable. Cannot queue job for interaction '%s'.", name ? name : "(null)");
|
log_error("[bot] Worker pool unavailable. Cannot queue job for interaction '%s'.", name ? name : "(null)");
|
||||||
hammy_job_reply(job, client, "Hammy is not accepting commands right now. Please try again shortly.");
|
hammy_job_reply(job, client, "Bot is Offline", "Hammy is not accepting commands right now. Please try again shortly.", true);
|
||||||
hammy_job_destroy(&job);
|
hammy_job_destroy(&job);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
+52
-18
@@ -7,6 +7,7 @@
|
|||||||
#include <hammy/bot.h>
|
#include <hammy/bot.h>
|
||||||
#include <hammy/command.h>
|
#include <hammy/command.h>
|
||||||
#include <hammy/job.h>
|
#include <hammy/job.h>
|
||||||
|
#include <hammy/embeds.h>
|
||||||
|
|
||||||
// strdup() is POSIX, so we'll keep a local and keep the code portable.
|
// strdup() is POSIX, so we'll keep a local and keep the code portable.
|
||||||
static char* hammy_strdup(const char* src) {
|
static char* hammy_strdup(const char* src) {
|
||||||
@@ -125,23 +126,10 @@ int64_t hammy_job_age_ms(const hammy_job_t* job, struct discord* client) {
|
|||||||
return (int64_t)discord_timestamp(client) - job->queuedAt;
|
return (int64_t)discord_timestamp(client) - job->queuedAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void hammy_job_reply(const hammy_job_t* job, struct discord* client, const char* content) {
|
void hammy_job_reply(const hammy_job_t* job, struct discord* client, const char* title, const char* content, bool isError) {
|
||||||
if (!job || !client || !content) { return; }
|
if (!job || !client || !content || !title) { return; }
|
||||||
|
|
||||||
// TODO: Embeds
|
|
||||||
struct discord_edit_original_interaction_response params = {
|
|
||||||
.content = (char*)content
|
|
||||||
};
|
|
||||||
|
|
||||||
CCORDcode code = discord_edit_original_interaction_response(client, job->appId, job->token, ¶ms, NULL);
|
|
||||||
if (code != CCORD_OK) {
|
|
||||||
log_warn("[job] Failed to edit response for interaction %" PRIu64 ": %d", job->id, code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void hammy_job_respond(const hammy_job_t* job, struct discord* client, const char* content) {
|
|
||||||
if (!job || !client || !content) { return; }
|
|
||||||
|
|
||||||
|
if (hammy_embeds_customembed(client, NULL, NULL, title, content, NULL, 0, isError ? 0xFF0000 : 0x00FF00)) {
|
||||||
struct discord_interaction_response params = {
|
struct discord_interaction_response params = {
|
||||||
.type = DISCORD_INTERACTION_CHANNEL_MESSAGE_WITH_SOURCE,
|
.type = DISCORD_INTERACTION_CHANNEL_MESSAGE_WITH_SOURCE,
|
||||||
.data = &(struct discord_interaction_callback_data){
|
.data = &(struct discord_interaction_callback_data){
|
||||||
@@ -150,10 +138,56 @@ void hammy_job_respond(const hammy_job_t* job, struct discord* client, const cha
|
|||||||
};
|
};
|
||||||
|
|
||||||
CCORDcode code = discord_create_interaction_response(client, job->id, job->token, ¶ms, NULL);
|
CCORDcode code = discord_create_interaction_response(client, job->id, job->token, ¶ms, NULL);
|
||||||
|
|
||||||
if (code != CCORD_OK) {
|
if (code != CCORD_OK) {
|
||||||
log_warn("[job] Failed to send interaction response for interaction %" PRIu64 ": %d", job->id, code);
|
log_warn("[job] Failed to send interaction response for interaction %" PRIu64 ": %d", job->id, code);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
struct discord_interaction_response params = {
|
||||||
|
.type = DISCORD_INTERACTION_CHANNEL_MESSAGE_WITH_SOURCE,
|
||||||
|
.data = &(struct discord_interaction_callback_data){
|
||||||
|
.content = (char*)content
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CCORDcode code = discord_create_interaction_response(client, job->id, job->token, ¶ms, NULL);
|
||||||
|
if (code != CCORD_OK) {
|
||||||
|
log_warn("[job] Failed to send interaction response for interaction %" PRIu64 ": %d", job->id, code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hammy_job_respond(const hammy_job_t* job, struct discord* client, const char* title, const char* content, bool isError) {
|
||||||
|
if (!job || !client || !content || !title) { return; }
|
||||||
|
|
||||||
|
struct discord_embed embed[1];
|
||||||
|
if (hammy_embeds_customembed(client, embed, NULL, title, content, NULL, 0, isError ? 0xFF0000 : 0x00FF00)) {
|
||||||
|
struct discord_interaction_response params = {
|
||||||
|
.type = DISCORD_INTERACTION_CHANNEL_MESSAGE_WITH_SOURCE,
|
||||||
|
.data = &(struct discord_interaction_callback_data){
|
||||||
|
.embeds = &(struct discord_embeds){
|
||||||
|
.size = 1,
|
||||||
|
.array = embed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CCORDcode code = discord_create_interaction_response(client, job->id, job->token, ¶ms, NULL);
|
||||||
|
if (code != CCORD_OK) {
|
||||||
|
log_warn("[job] Failed to send interaction response for interaction %" PRIu64 ": %d", job->id, code);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
struct discord_interaction_response params = {
|
||||||
|
.type = DISCORD_INTERACTION_CHANNEL_MESSAGE_WITH_SOURCE,
|
||||||
|
.data = &(struct discord_interaction_callback_data){
|
||||||
|
.content = (char*)content
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CCORDcode code = discord_create_interaction_response(client, job->id, job->token, ¶ms, NULL);
|
||||||
|
if (code != CCORD_OK) {
|
||||||
|
log_warn("[job] Failed to send interaction response for interaction %" PRIu64 ": %d", job->id, code);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void hammy_job_run(hammy_job_t* job, struct discord* client) {
|
void hammy_job_run(hammy_job_t* job, struct discord* client) {
|
||||||
@@ -163,7 +197,7 @@ void hammy_job_run(hammy_job_t* job, struct discord* client) {
|
|||||||
const hammy_command_t* command = hammy_bot_find_command(job->bot, job->command);
|
const hammy_command_t* command = hammy_bot_find_command(job->bot, job->command);
|
||||||
if (!command || !command->handler) {
|
if (!command || !command->handler) {
|
||||||
log_warn("[job] No handler found for command '%s'.", job->command ? job->command : "unknown");
|
log_warn("[job] No handler found for command '%s'.", job->command ? job->command : "unknown");
|
||||||
hammy_job_reply(job, client, "I don't know that command!");
|
hammy_job_reply(job, client, "Unknown Command", "I don't know that command!", true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -134,7 +134,7 @@ static void hammy_pool_stop(hammy_pool_t* pool, bool drain) {
|
|||||||
// The job's bot back-reference carries the original client, which is
|
// The job's bot back-reference carries the original client, which is
|
||||||
// the one this thread is allowed to serialise through.
|
// the one this thread is allowed to serialise through.
|
||||||
if (job->bot && job->bot->client) {
|
if (job->bot && job->bot->client) {
|
||||||
hammy_job_reply(job, job->bot->client, "Hammy is shutting down, so this command was dropped. Please try again once it is back.");
|
hammy_job_reply(job, job->bot->client, "Bot is Shutting Down", "Hammy is shutting down, so this command was dropped. Please try again once it is back.", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
hammy_job_destroy(&job);
|
hammy_job_destroy(&job);
|
||||||
|
|||||||
+2
-2
@@ -48,12 +48,12 @@ static void* hammy_worker_main(void* arg) {
|
|||||||
pthread_mutex_unlock(&pool->lock);
|
pthread_mutex_unlock(&pool->lock);
|
||||||
|
|
||||||
// From here, the worker owns the job and is responsible for destroying it.
|
// From here, the worker owns the job and is responsible for destroying it.
|
||||||
log_info("[worker %d] Processing job %llu from user %llu", worker->id, job->id, job->user);
|
log_info("[worker %d] Processing job %lu from user %lu", worker->id, job->id, job->user);
|
||||||
int64_t age = hammy_job_age_ms(job, worker->clientCopy);
|
int64_t age = hammy_job_age_ms(job, worker->clientCopy);
|
||||||
|
|
||||||
if (age > HAMMY_JOB_MAX_AGE_MS) {
|
if (age > HAMMY_JOB_MAX_AGE_MS) {
|
||||||
log_warn("[worker %d] Dropping stale job '%s' (age %lld ms)", worker->id, job->command ? job->command : "unknown", (long long)age);
|
log_warn("[worker %d] Dropping stale job '%s' (age %lld ms)", worker->id, job->command ? job->command : "unknown", (long long)age);
|
||||||
hammy_job_reply(job, worker->clientCopy, "Sorry, your command took too long to process and was dropped. Please try again.");
|
hammy_job_reply(job, worker->clientCopy, "Command Timeout", "Sorry, your command took too long to process and was dropped. Please try again.", true);
|
||||||
} else {
|
} else {
|
||||||
hammy_job_run(job, worker->clientCopy);
|
hammy_job_run(job, worker->clientCopy);
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+1122
-2
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user