supporting my chocobot

This commit is contained in:
Adam D. Ruppe 2024-07-14 19:53:48 -04:00
parent 09af87971f
commit f5d4818975
1 changed files with 95 additions and 20 deletions

101
discord.d
View File

@ -99,6 +99,14 @@ class DiscordChannel : DiscordEntity {
override string restType() { override string restType() {
return "channels"; return "channels";
} }
void sendMessage(string message) {
if(message.length == 0)
message = "empty message specified";
var msg = var.emptyObject;
msg.content = message;
rest.messages.POST(msg).result;
}
} }
/++ /++
@ -158,6 +166,22 @@ class DiscordUser : DiscordMentionable {
auto result = api.rest.guilds[role.guild.id].members[this.id].roles[role.id].DELETE().result; auto result = api.rest.guilds[role.guild.id].members[this.id].roles[role.id].DELETE().result;
} }
private DiscordChannel dmChannel_;
DiscordChannel dmChannel() {
if(dmChannel_ is null) {
var obj = var.emptyObject;
obj.recipient_id = this.id;
var result = this.api.rest.users["@me"].channels.POST(obj).result;
dmChannel_ = new DiscordChannel(api, result.id.get!string);//, result);
}
return dmChannel_;
}
void sendMessage(string what) {
dmChannel.sendMessage(what);
}
} }
/++ /++
@ -235,26 +259,44 @@ class SlashCommandHandler {
/++ /++
+/ +/
void reply(string message) scope { void reply(string message, bool ephemeral = false) scope {
replyLowLevel(message); replyLowLevel(message, ephemeral);
} }
/++ /++
+/ +/
void replyWithError(in char[] message) scope { void replyWithError(scope const(char)[] message) scope {
replyLowLevel(message.idup); if(message.length == 0)
message = "I am error.";
replyLowLevel(message.idup, true);
} }
void replyLowLevel(string message) scope { enum MessageFlags : uint {
SUPPRESS_EMBEDS = (1 << 2), // skip the embedded content
EPHEMERAL = (1 << 6), // only visible to you
LOADING = (1 << 7), // the bot is "thinking"
SUPPRESS_NOTIFICATIONS = (1 << 12) // skip push/desktop notifications
}
void replyLowLevel(string message, bool ephemeral) scope {
if(message.length == 0)
message = "empty message";
var reply = var.emptyObject; var reply = var.emptyObject;
reply.type = 4; // chat response in message. 5 can be answered quick and edited later if loading, 6 if quick answer, no loading message reply.type = 4; // chat response in message. 5 can be answered quick and edited later if loading, 6 if quick answer, no loading message
var replyData = var.emptyObject; var replyData = var.emptyObject;
replyData.content = message; replyData.content = message;
replyData.flags = ephemeral ? (1 << 6) : 0;
reply.data = replyData; reply.data = replyData;
try {
var result = api.rest. var result = api.rest.
interactions[commandArgs.interactionId][commandArgs.interactionToken].callback interactions[commandArgs.interactionId][commandArgs.interactionToken].callback
.POST(reply).result; .POST(reply).result;
writeln(result.toString);
} catch(Exception e) {
import std.stdio; writeln(commandArgs);
writeln(e.toString());
}
} }
} }
@ -281,12 +323,22 @@ class SlashCommandHandler {
} }
private { private {
static void validateDiscordSlashCommandName(string name) {
foreach(ch; name) {
if(ch != '_' && !(ch >= 'a' && ch <= 'z'))
throw new InvalidArgumentsException("name", "discord names must be all lower-case with only letters and underscores", LimitedVariant(name));
}
}
static HandlerInfo makeHandler(alias handler, T)(T slashThis) { static HandlerInfo makeHandler(alias handler, T)(T slashThis) {
HandlerInfo info; HandlerInfo info;
// must be all lower case! // must be all lower case!
info.name = __traits(identifier, handler); info.name = __traits(identifier, handler);
validateDiscordSlashCommandName(info.name);
var cmd = var.emptyObject(); var cmd = var.emptyObject();
cmd.name = info.name; cmd.name = info.name;
version(D_OpenD) version(D_OpenD)
@ -309,6 +361,7 @@ class SlashCommandHandler {
foreach(idx, param; Params) { foreach(idx, param; Params) {
var option = var.emptyObject; var option = var.emptyObject;
auto name = __traits(identifier, Params[idx .. idx + 1]); auto name = __traits(identifier, Params[idx .. idx + 1]);
validateDiscordSlashCommandName(name);
names ~= name; names ~= name;
option.name = name; option.name = name;
option.description = "desc"; option.description = "desc";
@ -329,9 +382,9 @@ class SlashCommandHandler {
static if(is(typeof(handler) Return == return)) { static if(is(typeof(handler) Return == return)) {
static if(is(Return == void)) { static if(is(Return == void)) {
__traits(child, slashThis, handler)(fargsFromJson!Params(api, names, args.interactionData, args).tupleof); __traits(child, slashThis, handler)(fargsFromJson!Params(api, names, args.interactionData, args).tupleof);
sendHandlerReply("OK", replyHelper); sendHandlerReply("OK", replyHelper, true);
} else { } else {
sendHandlerReply(__traits(child, slashThis, handler)(fargsFromJson!Params(api, names, args.interactionData, args).tupleof), replyHelper); sendHandlerReply(__traits(child, slashThis, handler)(fargsFromJson!Params(api, names, args.interactionData, args).tupleof), replyHelper, false);
} }
} else static assert(0); } else static assert(0);
}; };
@ -439,9 +492,9 @@ ync def something(interaction:discord.Interaction):
} }
} }
static void sendHandlerReply(T)(T ret, scope InteractionReplyHelper replyHelper) { static void sendHandlerReply(T)(T ret, scope InteractionReplyHelper replyHelper, bool ephemeral) {
import std.conv; // FIXME import std.conv; // FIXME
replyHelper.reply(to!string(ret)); replyHelper.reply(to!string(ret), ephemeral);
} }
void registerAll(T)(T t) { void registerAll(T)(T t) {
@ -482,7 +535,7 @@ class SendingUser : DiscordUser {
} }
} }
class SendingChannel : DiscordUser { class SendingChannel : DiscordChannel {
private this(DiscordRestApi api, string id, var initialCache) { private this(DiscordRestApi api, string id, var initialCache) {
super(api, id); super(api, id);
} }
@ -733,24 +786,46 @@ class DiscordGatewayConnection {
scope SlashCommandHandler.InteractionReplyHelper replyHelper = new SlashCommandHandler.InteractionReplyHelper(api, commandArgs); scope SlashCommandHandler.InteractionReplyHelper replyHelper = new SlashCommandHandler.InteractionReplyHelper(api, commandArgs);
Exception throwExternally;
try { try {
if(slashCommandHandler_ is null) if(slashCommandHandler_ is null)
throw ArsdException!"No slash commands registered"(); throwExternally = ArsdException!"No slash commands registered"();
else {
auto cmdName = commandArgs.interactionData.name.get!string; auto cmdName = commandArgs.interactionData.name.get!string;
if(auto pHandler = cmdName in slashCommandHandler_.handlers) { if(auto pHandler = cmdName in slashCommandHandler_.handlers) {
(*pHandler)(commandArgs, replyHelper, api); (*pHandler)(commandArgs, replyHelper, api);
} else { } else {
throw ArsdException!"Unregistered slash command"(cmdName); throwExternally = ArsdException!"Unregistered slash command"(cmdName);
} }
}
} catch(ArsdExceptionBase e) {
const(char)[] msg = e.message;
if(msg.length == 0)
msg = "I am error.";
e.getAdditionalPrintableInformation((string name, in char[] value) {
msg ~= ("\n");
msg ~= (name);
msg ~= (": ");
msg ~= (value);
});
replyHelper.replyWithError(msg);
} catch(Exception e) { } catch(Exception e) {
replyHelper.replyWithError(e.message); replyHelper.replyWithError(e.message);
} }
if(throwExternally !is null)
throw throwExternally;
break; break;
case "READY": case "READY":
this.session_id = data.session_id.get!string; this.session_id = data.session_id.get!string;
this.resume_gateway_url = data.resume_gateway_url.get!string; this.resume_gateway_url = data.resume_gateway_url.get!string;
this.applicationId_ = data.application.id.get!string; this.applicationId_ = data.application.id.get!string;
if(slashCommandHandler_ !is null && applicationId.length)
slashCommandHandler_.register(api, applicationId);
break; break;
default: default: