Added Discord chat support.

This commit is contained in:
Devn00b 2024-02-05 19:04:10 -08:00
parent a55fda41b5
commit 78f4a6e63b
8 changed files with 289 additions and 13 deletions

View file

@ -0,0 +1,4 @@
INSERT INTO `ruleset_details` (`ruleset_id`, `rule_category`, `rule_type`, `rule_value`, `description`) VALUES (1, 'R_Discord', 'DiscordEnabled', '1', 'Enable (1) or Disable(0) the Discord Bridge System.');
INSERT INTO `ruleset_details` (`ruleset_id`, `rule_category`, `rule_type`, `rule_value`, `description`) VALUES (1, 'R_Discord', 'DiscordWebhookURL', 'https://example.com', 'Webhook url for EQ2 -> Discord coms.');
INSERT INTO `ruleset_details` (`ruleset_id`, `rule_category`, `rule_type`, `rule_value`, `description`) VALUES (1, 'R_Discord', 'DiscordBotToken', '0', 'This is the token for the bot, given in the discord developer site.');
INSERT INTO `ruleset_details` (`ruleset_id`, `rule_category`, `rule_type`, `rule_value`, `description`) VALUES (1, 'R_Discord', 'DiscordListenChan', '0', 'Channe ID you want to listen to chat from. this is for Discord -> EQ2 coms.');

View file

@ -22,9 +22,24 @@
#include "../../common/Log.h"
#include "../../common/ConfigReader.h"
#include "../../common/PacketStruct.h"
#include "../Rules/Rules.h"
extern RuleManager rule_manager;
//devn00b
#ifdef DISCORD
#ifndef WIN32
#include <dpp/dpp.h>
#include "ChatChannel.h"
extern ChatChannel channel;
#endif
#endif
extern ConfigReader configReader;
Chat::Chat() {
m_channels.SetName("Chat::Channels");
}
@ -262,6 +277,8 @@ bool Chat::LeaveAllChannels(Client *client) {
bool Chat::TellChannel(Client *client, const char *channel_name, const char *message, const char* name) {
vector<ChatChannel *>::iterator itr;
bool ret = false;
bool enablediscord = rule_manager.GetGlobalRule(R_Discord, DiscordEnabled)->GetBool();
const char* discordchan = rule_manager.GetGlobalRule(R_Discord, DiscordChannel)->GetString();
m_channels.readlock(__FUNCTION__, __LINE__);
for (itr = channels.begin(); itr != channels.end(); itr++) {
@ -271,6 +288,21 @@ bool Chat::TellChannel(Client *client, const char *channel_name, const char *mes
else
ret = (*itr)->TellChannel(client, message, name);
if(enablediscord == true && client){
if (strcmp(channel_name, discordchan) != 0){
m_channels.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
#ifdef DISCORD
if (client) {
std::string whofrom = client->GetPlayer()->GetName();
std::string msg = string(message);
ret = PushDiscordMsg(msg.c_str(), whofrom.c_str());
}
#endif
}
break;
}
}
@ -310,3 +342,31 @@ ChatChannel* Chat::GetChannel(const char *channel_name) {
return ret;
}
#ifdef DISCORD
//this sends chat from EQ2EMu to Discord. Currently using webhooks. Makes things simpler code wise.
int Chat::PushDiscordMsg(const char* msg, const char* from) {
bool enablediscord = rule_manager.GetGlobalRule(R_Discord, DiscordEnabled)->GetBool();
if(enablediscord == false) {
LogWrite(INIT__INFO, 0,"Discord","Bot Disabled By Rule...");
return 0;
}
m_channels.readlock(__FUNCTION__, __LINE__);
const char* hook = rule_manager.GetGlobalRule(R_Discord, DiscordWebhookURL)->GetString();
std::string servername = net.GetWorldName();
char ourmsg[4096];
//form our message
sprintf(ourmsg,"[%s] [%s] Says: %s",from, servername.c_str(), msg);
/* send a message with this webhook */
dpp::cluster bot("");
dpp::webhook wh(hook);
bot.execute_webhook(wh, dpp::message(ourmsg));
m_channels.releasereadlock(__FUNCTION__, __LINE__);
return 1;
}
#endif

View file

@ -27,6 +27,13 @@
#include "../client.h"
#include "ChatChannel.h"
#ifdef DISCORD
#ifndef WIN32
#pragma once
#include <dpp/dpp.h>
#endif
#endif
using namespace std;
/*
@ -100,6 +107,8 @@ public:
bool LeaveAllChannels(Client *client);
bool TellChannel(Client *client, const char *channel_name, const char *message, const char* name = 0);
bool SendChannelUserList(Client *client, const char *channel_name);
//devn00b
int PushDiscordMsg(const char*, const char*);
ChatChannel* GetChannel(const char* channel_name);
private:

View file

@ -135,23 +135,36 @@ bool ChatChannel::TellChannel(Client *client, const char *message, const char* n
packet_struct->setDataByName("from_spawn_id", 0xFFFFFFFF);
packet_struct->setDataByName("to_spawn_id", 0xFFFFFFFF);
if (client)
if (client != NULL){
packet_struct->setDataByName("from", client->GetPlayer()->GetName());
else
packet_struct->setDataByName("from", name2);
} else {
char name3[128];
sprintf(name3,"[%s] from discord",name2);
packet_struct->setDataByName("from", name3);
}
packet_struct->setDataByName("to", to_client->GetPlayer()->GetName());
packet_struct->setDataByName("channel", to_client->GetMessageChannelColor(CHANNEL_CUSTOM_CHANNEL));
packet_struct->setDataByName("language", client->GetPlayer()->GetCurrentLanguage());
if(client != NULL){
packet_struct->setDataByName("language", client->GetPlayer()->GetCurrentLanguage());
}else{
packet_struct->setDataByName("language", 0);
}
packet_struct->setDataByName("message", message);
packet_struct->setDataByName("channel_name", name);
packet_struct->setDataByName("show_bubble", 1);
if (client->GetPlayer()->GetCurrentLanguage() == 0 || to_client->GetPlayer()->HasLanguage(client->GetPlayer()->GetCurrentLanguage())) {
if(client != NULL){
if (client->GetPlayer()->GetCurrentLanguage() == 0 || to_client->GetPlayer()->HasLanguage(client->GetPlayer()->GetCurrentLanguage())) {
packet_struct->setDataByName("understood", 1);
}
} else {
packet_struct->setDataByName("understood", 1);
}
packet_struct->setDataByName("unknown4", 0);
to_client->QueuePacket(packet_struct->serialize());
safe_delete(packet_struct);
}
@ -210,4 +223,5 @@ bool ChatChannel::SendChannelUserList(Client *client) {
safe_delete(packet_struct);
return true;
}
}

View file

@ -376,6 +376,12 @@ void RuleManager::Init()
RULE_INIT(R_World, DatabaseVersion, "0");
//devn00b
RULE_INIT(R_Discord, DiscordEnabled, "0"); //Enable/Disable built in discord bot.
RULE_INIT(R_Discord, DiscordWebhookURL, "None"); //Webhook url used for server -> discord messages.
RULE_INIT(R_Discord, DiscordBotToken, "None"); //Bot token used to connect to discord and provides discord -> server messages.
RULE_INIT(R_Discord, DiscordChannel, "Discord"); // in-game channel used for server -> discord messages.
RULE_INIT(R_Discord, DiscordListenChan, "0"); // Discord ChannelID used for discord->server messages.
#undef RULE_INIT
}

View file

@ -40,7 +40,8 @@ enum RuleCategory {
R_Zone,
R_Loot,
R_Spells,
R_Expansion
R_Expansion,
R_Discord
};
enum RuleType {
@ -231,7 +232,12 @@ enum RuleType {
DatabaseVersion,
SkipLootGrayMob,
LootDistributionTime
LootDistributionTime,
DiscordEnabled,
DiscordWebhookURL,
DiscordBotToken,
DiscordChannel,
DiscordListenChan
};
class Rule {

View file

@ -0,0 +1,97 @@
# Programs
CC = gcc
CXX = g++
LINKER = g++
# Configuration
Build_Dir = build
Source_Dir = ..
APP = eq2world
# LUA flags
Lua_C_Flags = -DLUA_COMPAT_ALL -DLUA_USE_LINUX
Lua_W_Flags = -Wall
# World flags
C_Flags = -I/usr/include/mariadb -I../depends/fmt/include -I../depends/recastnavigation/Detour/Include -I/usr/local/include/boost -I../depends/glm/ -march=native -pipe -pthread -std=c++17
LD_Flags = -L/usr/lib/x86_64-linux-gnu -lz -lpthread -lmariadbclient -L../depends/recastnavigation/RecastDemo/Build/gmake/lib/Debug -lDebugUtils -lDetour -lDetourCrowd -lDetourTileCache -lRecast -L/usr/local/lib -rdynamic -lm -Wl,-E -ldl -lreadline -lboost_system -lboost_filesystem -lboost_iostreams -lboost_regex
W_Flags = -Wall -Wno-reorder
D_Flags = -DEQ2 -DWORLD -D_GNU_SOURCE
# Setup Debug or Release build
ifeq ($(BUILD),debug)
# "Debug" build - minimum optimization, and debugging symbols
C_Flags += -O -ggdb
D_Flags += -DDEBUG -DDISCORD
LD_Flags += -ldpp
Current_Build_Dir := $(Build_Dir)/debug
App_Filename = $(APP)_debug
else
# "Release" build - optimization, and no debug symbols
C_Flags += -O2 -s -DNDEBUG
Current_Build_Dir := $(Build_Dir)/release
App_Filename = $(APP)
endif
# File lists
World_Source = $(wildcard $(Source_Dir)/WorldServer/*.cpp) $(wildcard $(Source_Dir)/WorldServer/*/*.cpp)
World_Objects = $(patsubst %.cpp,$(Current_Build_Dir)/%.o,$(subst $(Source_Dir)/,,$(World_Source)))
Common_Source = $(wildcard $(Source_Dir)/common/*.cpp)
Common_Objects = $(patsubst %.cpp,$(Current_Build_Dir)/%.o,$(subst $(Source_Dir)/,,$(Common_Source)))
Lua_Source = $(wildcard $(Source_Dir)/LUA/*.c)
Lua_Objects = $(patsubst %.c,$(Current_Build_Dir)/%.o,$(subst $(Source_Dir)/,,$(Lua_Source)))
# Receipes
all: $(APP)
$(APP): $(Common_Objects) $(World_Objects) $(Lua_Objects)
@echo Linking...
@$(LINKER) $(W_Flags) $^ $(LD_Flags) -o $(App_Filename)
@test -e $(APP) || /bin/true
#@ln -s $(App_Filename) $(APP) || /bin/true
@echo Finished building world.
$(Current_Build_Dir)/LUA/%.o: $(Source_Dir)/LUA/%.c
@mkdir -p $(dir $@)
$(CC) -c $(Lua_C_Flags) $(Lua_W_Flags) $< -o $@
$(Current_Build_Dir)/%.o: $(Source_Dir)/%.cpp
@mkdir -p $(dir $@)
$(CXX) -c $(C_Flags) $(D_Flags) $(W_Flags) $< -o $@
#setup:
# @test ! -e volumes.phys && ln -s $(Conf_Dir)/volumes.phys . || /bin/true
# @test ! -e vgemu-structs.xml && ln -s $(Conf_Dir)/vgemu-structs.xml . || /bin/true
# @$(foreach folder,$(wildcard $(Content_Dir)/scripts/*),test -d $(Content_Dir)/scripts && test ! -e $(notdir $(folder)) && ln -s $(folder) . || /bin/true)
# @echo "Symlinks have been created."
# @cp -n $(Conf_Dir)/vgemu-world.xml .
# @echo "You need to edit your config file: vgemu-world.xml"
release:
@$(MAKE) "BUILD=release"
debug:
@$(MAKE) "BUILD=debug"
clean:
rm -rf $(filter-out %Lua,$(foreach folder,$(wildcard $(Current_Build_Dir)/*),$(folder))) $(App_Filename) $(APP)
cleanlua:
rm -rf $(Current_Build_Dir)/Lua
cleanall:
rm -rf $(Build_Dir) $(App_Filename) $(APP)
#cleansetup:
# rm volumes.phys vgemu-structs.xml $(foreach folder,$(wildcard $(Content_Dir)/scripts/*),$(notdir $(folder)))
#docs: docs-world
#docs-world:
# @cd ../../doc; doxygen Doxyfile-World

View file

@ -57,6 +57,16 @@ using namespace std;
#include "Transmute.h"
#include "Zone/ChestTrap.h"
//devn00b
#ifdef DISCORD
//linux only for the moment.
#ifndef WIN32
#include <dpp/dpp.h>
#include "Chat/Chat.h"
extern Chat chat;
#endif
#endif
double frame_time = 0.0;
#ifdef WIN32
@ -113,6 +123,12 @@ extern map<int16, int16> EQOpcodeVersions;
ThreadReturnType ItemLoad (void* tmp);
ThreadReturnType AchievmentLoad (void* tmp);
ThreadReturnType SpellLoad (void* tmp);
//devn00b
#ifdef DISCORD
#ifndef WIN32
ThreadReturnType StartDiscord (void* tmp);
#endif
#endif
int main(int argc, char** argv) {
#ifdef PROFILER
@ -238,9 +254,12 @@ int main(int argc, char** argv) {
pthread_t thread2;
pthread_create(&thread2, NULL, SpellLoad, &world);
pthread_detach(thread2);
//pthread_t thread3;
//pthread_create(&thread3, NULL, AchievmentLoad, &world);
//pthread_detach(thread3);
//devn00b
#ifdef DISCORD
pthread_t thread3;
pthread_create(&thread3, NULL, StartDiscord, &world);
pthread_detach(thread3);
#endif
#endif
}
@ -930,3 +949,64 @@ void NetConnection::WelcomeHeader()
fflush(stdout);
}
#ifdef DISCORD
ThreadReturnType StartDiscord(void* tmp)
{
#ifndef DISCORD
THREAD_RETURN(NULL);
#endif
if (tmp == 0) {
ThrowError("StartDiscord: tmp = 0!");
THREAD_RETURN(NULL);
}
#ifdef WIN32
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
#endif
bool enablediscord = rule_manager.GetGlobalRule(R_Discord, DiscordEnabled)->GetBool();
if(enablediscord == false) {
LogWrite(INIT__INFO, 0,"Discord","Bot Disabled By Rule...");
THREAD_RETURN(NULL);
}
LogWrite(INIT__INFO, 0, "Discord", "Starting Discord Bridge...");
const char* bottoken = rule_manager.GetGlobalRule(R_Discord, DiscordBotToken)->GetString();
if(strlen(bottoken)== 0) {
LogWrite(INIT__INFO, 0,"Discord","Bot Token Was Empty...");
THREAD_RETURN(NULL);
}
dpp::cluster bot(bottoken, dpp::i_default_intents | dpp::i_message_content);
//if we have debug on, go ahead and show DPP logs.
#ifdef DEBUG
bot.on_log([&bot](const dpp::log_t & event) {
std::cout << "[" << dpp::utility::loglevel(event.severity) << "] " << event.message << "\n";
});
#endif
bot.on_message_create([&bot](const dpp::message_create_t& event) {
if (event.msg.author.is_bot() == false) {
std::string chanid = event.msg.channel_id.str();
std::string listenchan = rule_manager.GetGlobalRule(R_Discord, DiscordListenChan)->GetString();
if(chanid.compare(listenchan) != 0 || !chanid.size() || !listenchan.size()) {
return;
}
chat.TellChannel(NULL, listenchan.c_str(), event.msg.content.c_str(), event.msg.author.username.c_str());
}
});
while(true) {
bot.start(dpp::st_wait);
//wait 30s for reconnect. prevents hammering discord and a potential ban.
std::this_thread::sleep_for(std::chrono::milliseconds(30000));
}
THREAD_RETURN(NULL);
}
#endif