Merge branch 'master' into kicking-changes

This commit is contained in:
MangosArentLiterature 2021-05-01 19:08:07 -05:00
commit 8cb4ccd847
14 changed files with 253 additions and 22 deletions

View File

@ -40,6 +40,7 @@ SOURCES += src/advertiser.cpp \
src/commands/roleplay.cpp \
src/config_manager.cpp \
src/db_manager.cpp \
src/discord.cpp \
src/logger.cpp \
src/main.cpp \
src/packets.cpp \
@ -55,6 +56,7 @@ HEADERS += include/advertiser.h \
include/area_data.h \
include/config_manager.h \
include/db_manager.h \
include/discord.h \
include/logger.h \
include/server.h \
include/ws_client.h \

View File

@ -23,3 +23,8 @@ maximum_characters=256
[Dice]
max_value=100
max_dice=100
[Discord]
webhook_enabled=false
webhook_url=Your webhook url here.
webhook_sendfile=false

View File

@ -161,6 +161,13 @@ class AOClient : public QObject {
*/
bool global_enabled = true;
/**
* @brief If true, the client's messages will be sent in first-person mode.
*
* @see AOClient::cmdFirstPerson
*/
bool first_person = false;
/**
* @brief If true, the client may not use in-character chat.
*/
@ -1542,6 +1549,15 @@ class AOClient : public QObject {
*/
void cmdS(int argc, QStringList argv);
/**
* @brief Toggle whether the client's messages will be sent in first person mode.
*
* @details No arguments.
*
* @iscommand
*/
void cmdFirstPerson(int argc, QStringList argv);
///@}
/**
@ -1999,7 +2015,8 @@ class AOClient : public QObject {
{"a", {ACLFlags.value("NONE"), 2, &AOClient::cmdA}},
{"s", {ACLFlags.value("NONE"), 0, &AOClient::cmdS}},
{"kickuid", {ACLFlags.value("NONE"), 2, &AOClient::cmdKickUid}},
{"kick_uid", {ACLFlags.value("NONE"), 2, &AOClient::cmdKickUid}}
{"kick_uid", {ACLFlags.value("NONE"), 2, &AOClient::cmdKickUid}},
{"firstperson", {ACLFlags.value("NONE"), 0, &AOClient::cmdFirstPerson}},
};
/**

View File

@ -68,7 +68,6 @@ class ConfigManager {
QString name; //!< The name of the server as advertised on the server browser.
QString description; //!< The description of the server as advertised on the server browser.
bool advertise_server; //!< The server will only be announced to the master server (and thus appear on the master server list) if this is true.
int zalgo_tolerance; //!< The amount of subscripts zalgo is stripped by.
};
/**

67
include/discord.h Normal file
View File

@ -0,0 +1,67 @@
//////////////////////////////////////////////////////////////////////////////////////
// akashi - a server for Attorney Online 2 //
// Copyright (C) 2020 scatterflower //
// //
// This program is free software: you can redistribute it and/or modify //
// it under the terms of the GNU Affero General Public License as //
// published by the Free Software Foundation, either version 3 of the //
// License, or (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU Affero General Public License for more details. //
// //
// You should have received a copy of the GNU Affero General Public License //
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
//////////////////////////////////////////////////////////////////////////////////////
#ifndef DISCORD_H
#define DISCORD_H
#include <QtNetwork>
#include <QCoreApplication>
#include "server.h"
class Server;
class Discord : public QObject {
Q_OBJECT
public:
/**
* @brief Creates an instance of the Discord class.
*
* @param p_server A pointer to the Server instance Discord is constructed by.
* @param parent Qt-based parent, passed along to inherited constructor from QObject.
*/
Discord(Server* p_server, QObject* parent = nullptr)
: QObject(parent), server(p_server) {
};
public slots:
/**
* @brief Sends a modcall to a discord webhook.
*
* @param name The character or OOC name of the client who sent the modcall.
* @param area The area name of the area the modcall was sent from.
* @param reason The reason the client specified for the modcall.
* @param current_area The index of the area the modcall is made.
*/
void postModcallWebhook(QString name, QString reason, int current_area);
/**
* @brief Sends the reply to the POST request sent by Discord::postModcallWebhook.
*/
void onFinish(QNetworkReply *reply);
private:
/**
* @brief A pointer to the Server.
*/
Server* server;
};
#endif // DISCORD_H

View File

@ -98,6 +98,11 @@ public:
*/
void flush();
/**
*@brief Returns the current area buffer
*/
QQueue<QString> getBuffer();
private:
/**
* @brief Convenience function to format entries to the acceptable standard for logging.

View File

@ -23,6 +23,7 @@
#include "include/area_data.h"
#include "include/ws_proxy.h"
#include "include/db_manager.h"
#include "include/discord.h"
#include <QCoreApplication>
#include <QDebug>
@ -37,6 +38,7 @@
class AOClient;
class DBManager;
class AreaData;
class Discord;
/**
* @brief The class that represents the actual server as it is.
@ -217,11 +219,6 @@ class Server : public QObject {
*/
QString modpass;
/**
* @brief The amount of subscripts zalgo is stripped by.
*/
int zalgo_tolerance;
/**
* @brief The highest value dice can have.
*/
@ -237,6 +234,21 @@ class Server : public QObject {
*/
int afk_timeout;
/**
* @brief Whether discord webhooks are enabled on this server.
*/
bool webhook_enabled;
/**
* @brief Requires an https Webhook link, including both ID and Token in the link.
*/
QString webhook_url;
/**
* @brief If the modcall buffer is sent as a file.
*/
bool webhook_sendfile;
/**
* @brief The server-wide global timer.
*/
@ -310,6 +322,15 @@ class Server : public QObject {
*/
void reloadRequest(QString p_name, QString p_desc);
/**
* @brief Sends a modcall webhook request, emitted by AOClient::pktModcall.
*
* @param name The character or OOC name of the client who sent the modcall.
* @param reason The reason the client specified for the modcall.
* @param current_area Integer ID of the area the modcall is made.
*/
void webhookRequest(QString name, QString reason, int current_area);
private:
/**
* @brief The proxy used for WebSocket connections.
@ -332,6 +353,11 @@ class Server : public QObject {
* @brief The port through which the server will accept WebSocket connections.
*/
int ws_port;
/**
* @brief Handles discord webhooks.
*/
Discord* discord;
};
#endif // SERVER_H

View File

@ -260,14 +260,14 @@ void AOClient::cmdBgLock(int argc, QStringList argv)
{
AreaData* area = server->areas[current_area];
area->bg_locked = true;
server->broadcast(AOPacket("CT", {"Server", current_char + " locked the background.", "1"}), current_area);
server->broadcast(AOPacket("CT", {server->server_name, current_char + " locked the background.", "1"}), current_area);
}
void AOClient::cmdBgUnlock(int argc, QStringList argv)
{
AreaData* area = server->areas[current_area];
area->bg_locked = false;
server->broadcast(AOPacket("CT", {"Server", current_char + " unlocked the background.", "1"}), current_area);
server->broadcast(AOPacket("CT", {server->server_name, current_char + " unlocked the background.", "1"}), current_area);
}
void AOClient::cmdStatus(int argc, QStringList argv)
@ -291,7 +291,7 @@ void AOClient::cmdStatus(int argc, QStringList argv)
return;
}
arup(ARUPType::STATUS, true);
sendServerMessageArea(ooc_name + " changed status to " + arg);
server->broadcast(AOPacket("CT", {server->server_name, current_char + " changed status to " + arg.toUpper(), "1"}), current_area);
}
void AOClient::cmdJudgeLog(int argc, QStringList argv)

View File

@ -445,3 +445,10 @@ void AOClient::cmdS(int argc, QStringList argv)
server->broadcast(AOPacket("CT", {"[CM]" + sender_name, ooc_message}), i);
}
}
void AOClient::cmdFirstPerson(int argc, QStringList argv)
{
first_person = !first_person;
QString str_en = first_person ? "enabled" : "disabled";
sendServerMessage("First person mode " + str_en + ".");
}

View File

@ -144,7 +144,6 @@ bool ConfigManager::loadServerSettings(server_settings* settings)
bool port_conversion_success;
bool ws_port_conversion_success;
bool local_port_conversion_success;
bool zalgo_tolerance_conversion_success;
config.beginGroup("Options");
settings->ms_ip =
config.value("ms_ip", "master.aceattorneyonline.com").toString();
@ -158,8 +157,6 @@ bool ConfigManager::loadServerSettings(server_settings* settings)
settings->description =
config.value("server_description", "This is my flashy new server")
.toString();
settings->zalgo_tolerance =
config.value("zalgo_tolerance", "3").toInt(&zalgo_tolerance_conversion_success);
config.endGroup();
if (!port_conversion_success || !ws_port_conversion_success ||
!local_port_conversion_success) {

73
src/discord.cpp Normal file
View File

@ -0,0 +1,73 @@
//////////////////////////////////////////////////////////////////////////////////////
// akashi - a server for Attorney Online 2 //
// Copyright (C) 2020 scatterflower //
// //
// This program is free software: you can redistribute it and/or modify //
// it under the terms of the GNU Affero General Public License as //
// published by the Free Software Foundation, either version 3 of the //
// License, or (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU Affero General Public License for more details. //
// //
// You should have received a copy of the GNU Affero General Public License //
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
//////////////////////////////////////////////////////////////////////////////////////
#include "include/discord.h"
void Discord::postModcallWebhook(QString name, QString reason, int current_area)
{
if (!QUrl (server->webhook_url).isValid()) {
qWarning() << "Invalid webhook url!";
return;
}
QNetworkRequest request(QUrl (server->webhook_url));
QNetworkAccessManager* nam = new QNetworkAccessManager();
connect(nam, &QNetworkAccessManager::finished,
this, &Discord::onFinish);
// This is the kind of garbage Qt makes me write.
// I am so tired. Qt has broken me.
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QJsonObject json;
QJsonArray jsonArray;
QJsonObject jsonObject {
{"color", "13312842"},
{"title", name + " filed a modcall in " + server->areas[current_area]->name},
{"description", reason}
};
jsonArray.append(jsonObject);
json["embeds"] = jsonArray;
nam->post(request, QJsonDocument(json).toJson());
if (server->webhook_sendfile) {
QHttpMultiPart* construct = new QHttpMultiPart();
request.setHeader(QNetworkRequest::ContentTypeHeader, "multipart/form-data; boundary=" + construct->boundary());
//This cost me two days of my life. Thanks Qt and Discord. You have broken me.
QHttpPart file;
file.setRawHeader(QByteArray("Content-Disposition"), QByteArray("form-data; name=\"file\"; filename=\"log.txt\""));
file.setRawHeader(QByteArray("Content-Type"), QByteArray("plain/text"));
QQueue<QString> buffer = server->areas[current_area]->logger->getBuffer(); // I feel no shame for doing this
QString log;
while (!buffer.isEmpty()) {
log.append(buffer.dequeue() + "\n");
}
file.setBody(log.toUtf8());
construct->append(file);
nam->post(request, construct);
}
}
void Discord::onFinish(QNetworkReply *reply)
{
QByteArray data = reply->readAll();
QString str_reply = data;
qDebug() << str_reply;
}

View File

@ -113,3 +113,8 @@ void Logger::flush()
}
logfile.close();
}
QQueue<QString> Logger::getBuffer()
{
return buffer;
}

View File

@ -109,7 +109,7 @@ void AOClient::pktLoadingDone(AreaData* area, int argc, QStringList argv, AOPack
sendPacket("OPPASS", {"DEADBEEF"});
sendPacket("DONE");
sendPacket("BN", {area->background});
sendServerMessage("=== MOTD ===\r\n" + server->MOTD + "\r\n=============");
fullArup(); // Give client all the area data
@ -185,7 +185,7 @@ void AOClient::pktOocChat(AreaData* area, int argc, QStringList argv, AOPacket p
sendServerMessage("Your name is too long! Please limit it to under 30 characters.");
return;
}
QString message = dezalgo(argv[1]);
if (message.length() == 0 || message.length() > server->max_chars)
return;
@ -327,6 +327,14 @@ void AOClient::pktModCall(AreaData* area, int argc, QStringList argv, AOPacket p
client->sendPacket(packet);
}
area->logger->logModcall(this, &packet);
if (server->webhook_enabled) {
QString name = ooc_name;
if (ooc_name.isEmpty())
name = current_char;
server->webhookRequest(name, packet.contents[0], current_area);
}
area->logger->flush();
}
@ -502,6 +510,8 @@ AOPacket AOClient::validateIcPacket(AOPacket packet)
// emote
emote = incoming_args[3].toString();
if (first_person)
emote = "";
args.append(emote);
// message text
@ -680,8 +690,16 @@ AOPacket AOClient::validateIcPacket(AOPacket packet)
// immediate text processing
int immediate = incoming_args[18].toInt();
if (area->force_immediate)
immediate = 1;
if (area->force_immediate) {
if (args[7] == "1" || args[7] == "2") {
args[7] = "0";
immediate = 1;
}
else if (args[7] == "6") {
args[7] = "5";
immediate = 1;
}
}
if (immediate != 1 && immediate != 0)
return invalid;
args.append(QString::number(immediate));
@ -770,7 +788,7 @@ AOPacket AOClient::validateIcPacket(AOPacket packet)
QString AOClient::dezalgo(QString p_text)
{
QRegExp rxp("([\u0300-\u036f\u1ab0-\u1aff\u1dc0-\u1dff\u20d0-\u20ff\ufe20-\ufe2f\u115f\u1160\u3164]{" + QRegExp::escape(QString::number(server->zalgo_tolerance)) + ",})");
QRegularExpression rxp("([̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡])");
QString filtered = p_text.replace(rxp, "");
return filtered;
}

View File

@ -53,6 +53,13 @@ void Server::start()
loadServerConfig();
loadCommandConfig();
if (webhook_enabled) {
discord = new Discord(this, this);
connect(this, &Server::webhookRequest,
discord, &Discord::postModcallWebhook);
}
proxy = new WSProxy(port, ws_port, this);
if(ws_port != -1)
@ -267,10 +274,6 @@ void Server::loadServerConfig()
MOTD = config.value("motd","MOTD is not set.").toString();
auth_type = config.value("auth","simple").toString();
modpass = config.value("modpass","").toString();
bool zalgo_tolerance_conversion_success;
zalgo_tolerance = config.value("zalgo_tolerance", "3").toInt(&zalgo_tolerance_conversion_success);
if (!zalgo_tolerance_conversion_success)
zalgo_tolerance = 3;
bool maximum_statements_conversion_success;
maximum_statements = config.value("maximum_statements", "10").toInt(&maximum_statements_conversion_success);
if (!maximum_statements_conversion_success)
@ -294,6 +297,13 @@ void Server::loadServerConfig()
dice_value = config.value("value_type", "100").toInt();
max_dice = config.value("max_dice","100").toInt();
config.endGroup();
//Load discord webhook
config.beginGroup("Discord");
webhook_enabled = config.value("webhook_enabled", "false").toBool();
webhook_url = config.value("webhook_url", "Your webhook url here.").toString();
webhook_sendfile = config.value("webhook_sendfile", false).toBool();
config.endGroup();
}
Server::~Server()