mirror of
https://gitlab.com/niansa/SomeBot.git
synced 2025-03-06 20:48:26 +01:00
308 lines
11 KiB
C++
308 lines
11 KiB
C++
#include <string>
|
||
#include <string_view>
|
||
#include <sstream>
|
||
#include <fstream>
|
||
#include <vector>
|
||
#include <unordered_map>
|
||
#include <thread>
|
||
#ifdef __linux__
|
||
# include <sys/sysinfo.h>
|
||
# include <sys/utsname.h>
|
||
#endif
|
||
|
||
#include <dpp/dpp.h>
|
||
#include "sqlite_modern_cpp/sqlite_modern_cpp.h"
|
||
#include <ai.hpp>
|
||
|
||
|
||
|
||
const std::string_view ai_init_prompt =
|
||
R"(<question> Hello
|
||
<answer> Hi!
|
||
<question> How are you?
|
||
<answer> Great!
|
||
<question> What is your favorite color?
|
||
<answer> Green!
|
||
<question> What is your name?
|
||
<answer> I don't have a name.
|
||
<question> How are you?
|
||
<answer> Good.
|
||
<question> )";
|
||
|
||
|
||
template<typename intT>
|
||
std::string intAsHex(intT i) {
|
||
std::ostringstream sstream;
|
||
sstream << "0x" << std::hex << i;
|
||
return std::move(sstream).str();
|
||
}
|
||
|
||
uint32_t getColorOfUser(const dpp::user& user) {
|
||
uint32_t colors[] = {
|
||
0x424cb6,
|
||
0x585f68,
|
||
0x2c7c45,
|
||
0xbc7d14,
|
||
0xb23234
|
||
};
|
||
return colors[user.discriminator % 5];
|
||
}
|
||
|
||
namespace SysInfo { // TODO: This currently works on linux only
|
||
# ifdef __linux__
|
||
# define SYSINFO_WORKS
|
||
size_t memUsed() {
|
||
const std::string identifier = "VmData:\t";
|
||
std::ifstream pstatus("/proc/self/status");
|
||
// Check for success
|
||
if (not pstatus) {
|
||
return 0;
|
||
}
|
||
// Find line
|
||
std::string currline;
|
||
while (currline.find(identifier) != 0) {
|
||
std::getline(pstatus, currline);
|
||
}
|
||
// Close pstatus
|
||
pstatus.close();
|
||
// Get number
|
||
size_t res = 0;
|
||
for (const auto& character : currline) {
|
||
// Check if character is digit
|
||
if (character> '9' or character < '0') {
|
||
continue;
|
||
}
|
||
// Append digit to res
|
||
res += static_cast<size_t>(character - '0');
|
||
res *= 10;
|
||
}
|
||
res /= 10;
|
||
// Return result in MB
|
||
return res / 1000;
|
||
}
|
||
|
||
size_t memTotal() {
|
||
// Get sysinfo
|
||
struct sysinfo i;
|
||
sysinfo(&i);
|
||
// Get total ram in MB
|
||
return i.totalram / 1000000;
|
||
}
|
||
|
||
std::string kernelNameAndVersion() {
|
||
struct utsname name;
|
||
uname(&name);
|
||
return std::string(name.sysname)+" "+std::string(name.release);
|
||
}
|
||
# endif
|
||
}
|
||
|
||
|
||
int main(int argc, char **argv) {
|
||
// Caches
|
||
std::vector<dpp::slashcommand> slashcommandCache; // Without IDs
|
||
std::unordered_map<dpp::snowflake, dpp::guild> guildCache;
|
||
|
||
// Initialize AI
|
||
Ai ai;
|
||
|
||
auto chat_reply = [&](const std::string& message) -> std::string {
|
||
auto prompt = std::string(ai_init_prompt)+message+"\n<answer>";
|
||
return ai.complete(prompt, '\n');
|
||
};
|
||
|
||
// Initialize database
|
||
sqlite::database db("db.sqlite3");
|
||
db << "CREATE TABLE IF NOT EXISTS guilds ("
|
||
" id TEXT PRIMARY KEY NOT NULL,"
|
||
" globalchat_channel TEXT,"
|
||
" UNIQUE(id)"
|
||
");";
|
||
|
||
// Initialize bot
|
||
dpp::cluster bot(argv[1]);
|
||
|
||
auto broadcast_message = [&](const dpp::message_create_t& message) {
|
||
dpp::message msg;
|
||
const auto& guild = guildCache.at(message.msg.guild_id);
|
||
auto messageCode = intAsHex(message.msg.id)+intAsHex(message.msg.channel_id)+std::to_string(message.msg.guild_id);
|
||
msg.add_embed(dpp::embed()
|
||
.set_title(":earth_africa: Globalchat")
|
||
.set_description(":speaking_head: ["+message.msg.author.format_username()+"](https://internal.tuxifan.net/discordInspectionData/"+messageCode+" 'Inspection Data')")
|
||
.set_thumbnail(message.msg.author.get_avatar_url())
|
||
.set_color(getColorOfUser(message.msg.author))
|
||
.add_field(message.msg.member.nickname.empty()?message.msg.author.username:message.msg.member.nickname, message.msg.content+"\n⠀")
|
||
.set_footer(dpp::embed_footer().set_text(guild.name))
|
||
);
|
||
db << "SELECT globalchat_channel FROM guilds;"
|
||
>> [&](std::string channel_id_str) {
|
||
if (channel_id_str.empty())
|
||
return;
|
||
msg.set_channel_id(std::stoul(channel_id_str));
|
||
bot.message_create(msg);
|
||
};
|
||
};
|
||
|
||
bot.on_log(dpp::utility::cout_logger());
|
||
|
||
bot.on_slashcommand([&](const dpp::slashcommand_t& event) {
|
||
// Help
|
||
if (event.command.get_command_name() == "help") {
|
||
// Help command
|
||
dpp::embed embed;
|
||
embed.set_title("Help")
|
||
.set_description("Commands")
|
||
.set_footer(dpp::embed_footer().set_text("Bot made by 353535#3535"));
|
||
for (const auto& command : slashcommandCache) {
|
||
embed.add_field(command.name, command.description, true);
|
||
}
|
||
event.reply(dpp::message().add_embed(embed));
|
||
} else if (event.command.get_command_name() == "about") {
|
||
// Info command
|
||
std::ostringstream info;
|
||
info << "**Peak server count**: " << guildCache.size() << "\n"
|
||
# ifdef SYSINFO_WORKS
|
||
"**VM memory usage**: " << SysInfo::memUsed() << " MB / " << SysInfo::memTotal() << " MB\n"
|
||
"**Kernel**: " << SysInfo::kernelNameAndVersion() << "\n"
|
||
# endif
|
||
"**Compiler**: " COMPILER_ID " " COMPILER_VERSION " (" COMPILER_PLATFORM ")\n"
|
||
"**C++ standard**: " << __cplusplus << "\n"
|
||
;
|
||
event.reply(dpp::message()
|
||
.add_embed(dpp::embed()
|
||
.set_title("about")
|
||
.set_description(std::move(info).str())
|
||
));
|
||
}
|
||
|
||
// Fun
|
||
else if (event.command.get_command_name() == "ping") {
|
||
// Ping command
|
||
event.reply("Pong!");
|
||
} else if (event.command.get_command_name() == "chat") {
|
||
// Chat command
|
||
auto& content = std::get<std::string>(event.get_parameter("message"));
|
||
event.thinking(false);
|
||
std::thread([=, &bot]() {
|
||
auto reply = chat_reply(content);
|
||
event.delete_original_response();
|
||
bot.message_create(dpp::message(event.command.channel_id, event.command.member.get_mention()+" asked:\n> "+content+"\nI reply:\n> "+reply));
|
||
}).detach();
|
||
}
|
||
|
||
// Global chat
|
||
else if (event.command.get_command_name() == "set_gc") {
|
||
// Set global chat command
|
||
auto channel_id = std::get<dpp::snowflake>(event.get_parameter("channel"));
|
||
// Update globalchat ID
|
||
db << "UPDATE guilds "
|
||
"SET globalchat_channel = ? "
|
||
"WHERE id = ?;"
|
||
<< std::to_string(channel_id)
|
||
<< std::to_string(event.command.guild_id);
|
||
// Reply
|
||
event.reply("Globalchat is now in <#"+std::to_string(channel_id)+">");
|
||
} else if (event.command.get_command_name() == "reset_gc") {
|
||
// Update globalchat ID
|
||
db << "UPDATE guilds "
|
||
"SET globalchat_channel = NULL "
|
||
"WHERE id = ?;"
|
||
<< std::to_string(event.command.guild_id);
|
||
// Reply
|
||
event.reply("Globalchat reset");
|
||
}
|
||
});
|
||
bot.on_message_create([&](const dpp::message_create_t& message) {
|
||
// Make sure sender isn't a bot
|
||
if (message.msg.author.is_bot())
|
||
return;
|
||
|
||
// Chat with bot
|
||
if (message.msg.content[0] == '-') {
|
||
auto content = message.msg.content;
|
||
content.erase(0, 1);
|
||
for (auto& c : content) {
|
||
if (c == '\n')
|
||
c = ' ';
|
||
}
|
||
auto reply = chat_reply(content);
|
||
message.reply(reply);
|
||
return;
|
||
}
|
||
|
||
// Global chat
|
||
{
|
||
// Get current globalchat channel for server
|
||
dpp::snowflake globalchat_channel;
|
||
{
|
||
std::string globalchat_channel_str;
|
||
db << "SELECT globalchat_channel FROM guilds "
|
||
"WHERE id = ?;"
|
||
<< std::to_string(message.msg.guild_id)
|
||
>> std::tie(globalchat_channel_str);
|
||
globalchat_channel = std::stoul(globalchat_channel_str);
|
||
}
|
||
// If this channel is globalchat channel, broadcast message
|
||
if (message.msg.channel_id == globalchat_channel) {
|
||
broadcast_message(message);
|
||
bot.message_delete(message.msg.id, message.msg.channel_id);
|
||
return;
|
||
}
|
||
}
|
||
});
|
||
bot.on_guild_create([&](const dpp::guild_create_t& guild) {
|
||
// Make sure guild is in database
|
||
db << "INSERT OR IGNORE INTO guilds (id) VALUES (?);"
|
||
<< std::to_string(guild.created->id);
|
||
// Add guild to cache
|
||
guildCache[guild.created->id] = *guild.created;
|
||
});
|
||
|
||
bot.on_ready([&](const dpp::ready_t& event) {
|
||
// Register bot commands once
|
||
if (dpp::run_once<struct register_bot_commands>()) {
|
||
// Clear all commands first
|
||
for (const auto& command : bot.global_commands_get_sync()) {
|
||
bot.global_command_delete_sync(command.first);
|
||
}
|
||
slashcommandCache.clear();
|
||
// Help
|
||
{
|
||
dpp::slashcommand sc("help", "Get help", bot.me.id);
|
||
bot.global_command_create(sc);
|
||
slashcommandCache.push_back(sc);
|
||
}
|
||
{
|
||
dpp::slashcommand sc("about", "About me", bot.me.id);
|
||
bot.global_command_create(sc);
|
||
slashcommandCache.push_back(sc);
|
||
}
|
||
// Fun
|
||
{
|
||
dpp::slashcommand sc("ping", "Ping pong!", bot.me.id);
|
||
bot.global_command_create(sc);
|
||
slashcommandCache.push_back(sc);
|
||
}{
|
||
dpp::slashcommand sc("chat", "Chat with the bot", bot.me.id);
|
||
sc.add_option(dpp::command_option(dpp::command_option_type::co_string, "message", "Your message", true));
|
||
bot.global_command_create(sc);
|
||
slashcommandCache.push_back(sc);
|
||
}
|
||
// Global chat
|
||
{
|
||
dpp::slashcommand sc("set_gc", "Set global chat location", bot.me.id);
|
||
sc.add_option(dpp::command_option(dpp::command_option_type::co_channel, "channel", "Channel", true));
|
||
bot.global_command_create(sc);
|
||
slashcommandCache.push_back(sc);
|
||
}{
|
||
dpp::slashcommand sc("reset_gc", "Reset global chat location", bot.me.id);
|
||
bot.global_command_create(sc);
|
||
slashcommandCache.push_back(sc);
|
||
}
|
||
}
|
||
});
|
||
|
||
// Configure intents and start bot
|
||
bot.intents |= dpp::intents::i_message_content | dpp::intents::i_guilds;
|
||
bot.start(dpp::st_wait);
|
||
}
|