Rewrite channel topics code to support JSON format
This commit is contained in:
parent
ca61400e5d
commit
7901f666a3
15 changed files with 322 additions and 317 deletions
|
@ -3,7 +3,7 @@ set(OUTPUT_CONFS ad.json anongame_infos.conf address_translation.conf
|
|||
autoupdate.conf bnalias.conf bnban.conf bnetd_default_user.plain
|
||||
bnissue.txt bnmaps.conf bnxpcalc.conf
|
||||
bnxplevel.conf channel.conf command_groups.conf realm.conf
|
||||
sql_DB_layout.conf supportfile.conf topics.conf
|
||||
sql_DB_layout.conf supportfile.conf topics.json
|
||||
tournament.conf versioncheck.json icons.conf
|
||||
email_verification.conf cacert.pem)
|
||||
foreach(CONF ${OUTPUT_CONFS})
|
||||
|
@ -20,7 +20,7 @@ if(WITH_BNETD)
|
|||
autoupdate.conf bnalias.conf bnban.conf
|
||||
bnetd_default_user.plain bnissue.txt bnmaps.conf
|
||||
bnxpcalc.conf bnxplevel.conf channel.conf command_groups.conf
|
||||
realm.conf sql_DB_layout.conf supportfile.conf topics.conf
|
||||
realm.conf sql_DB_layout.conf supportfile.conf topics.json
|
||||
tournament.conf versioncheck.json icons.conf
|
||||
email_verification.conf cacert.pem)
|
||||
endif(WITH_BNETD)
|
||||
|
|
|
@ -76,7 +76,7 @@ i18ndir = "${SYSCONFDIR}/i18n"
|
|||
issuefile = "${SYSCONFDIR}/bnissue.txt"
|
||||
channelfile = "${SYSCONFDIR}/channel.conf"
|
||||
adfile = "${SYSCONFDIR}/ad.json"
|
||||
topicfile = "${SYSCONFDIR}/topics.conf"
|
||||
topicfile = "${SYSCONFDIR}/topics.json"
|
||||
ipbanfile = "${SYSCONFDIR}/bnban.conf"
|
||||
mpqfile = "${SYSCONFDIR}/autoupdate.conf"
|
||||
logfile = "${LOCALSTATEDIR}/bnetd.log"
|
||||
|
|
|
@ -62,7 +62,7 @@ i18ndir = conf\i18n
|
|||
issuefile = conf\bnissue.txt
|
||||
channelfile = conf\channel.conf
|
||||
adfile = conf\ad.json
|
||||
topicfile = conf\topics.conf
|
||||
topicfile = conf\topics.json
|
||||
ipbanfile = conf\bnban.conf
|
||||
transfile = conf\address_translation.conf
|
||||
mpqfile = conf\autoupdate.conf
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
Chat This is the public chat channel. Feel free to chat...
|
||||
Moderated Support Support is provided in this channel...
|
6
conf/topics.json.in
Normal file
6
conf/topics.json.in
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"topics": {
|
||||
"Chat": "This is the public chat channel. Feel free to chat...",
|
||||
"Moderated Support": "Support is provided in this channel..."
|
||||
}
|
||||
}
|
|
@ -30,6 +30,8 @@
|
|||
|
||||
# include "account.h"
|
||||
|
||||
#include <optional.hpp>
|
||||
|
||||
#ifdef CHANNEL_INTERNAL_ACCESS
|
||||
|
||||
#include <cstdio>
|
||||
|
@ -163,6 +165,9 @@ namespace pvpgn
|
|||
extern int channel_conn_has_tmpVOICE(t_channel const * channel, t_connection * c);
|
||||
extern t_connection * channel_get_first(t_channel const * channel);
|
||||
extern t_connection * channel_get_next(void);
|
||||
extern nonstd::optional<std::string> channel_get_topic(const t_channel* channel); // in topic.cpp
|
||||
extern bool channel_set_topic(t_channel* channel, const std::string& topic); // in topic.cpp
|
||||
extern bool channel_display_topic(t_channel* channel, t_connection* conn); // in topic.cpp
|
||||
|
||||
extern int channellist_create(void);
|
||||
extern int channellist_destroy(void);
|
||||
|
|
|
@ -4918,51 +4918,48 @@ namespace pvpgn
|
|||
|
||||
static int _handle_topic_command(t_connection * c, char const * text)
|
||||
{
|
||||
std::vector<std::string> args = split_command(text, 1);
|
||||
std::string topicstr = args[1];
|
||||
|
||||
|
||||
t_channel * channel = conn_get_channel(c);
|
||||
t_channel* channel = conn_get_channel(c);
|
||||
if (channel == nullptr)
|
||||
{
|
||||
message_send_text(c, message_type_error, c, localize(c, "This command can only be used inside a channel."));
|
||||
return -1;
|
||||
}
|
||||
|
||||
class_topic Topic;
|
||||
char const * channel_name = channel_get_name(channel);
|
||||
|
||||
// set channel topic
|
||||
if (!topicstr.empty())
|
||||
const char* channel_name = channel_get_name(channel);
|
||||
if (channel_name == nullptr)
|
||||
{
|
||||
if ((topicstr.size() + 1) > MAX_TOPIC_LEN)
|
||||
{
|
||||
msgtemp = localize(c, "Max topic length exceeded (max {} symbols)", MAX_TOPIC_LEN);
|
||||
message_send_text(c, message_type_error, c, msgtemp);
|
||||
return -1;
|
||||
}
|
||||
message_send_text(c, message_type_error, c, localize(c, "An error has occurred."));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (std::strlen(text) > std::strlen("/topic "))
|
||||
{
|
||||
// set channel topic
|
||||
|
||||
if (!(account_is_operator_or_admin(conn_get_account(c), channel_name)))
|
||||
{
|
||||
msgtemp = localize(c, "You must be at least a Channel Operator of {} to set the topic", channel_name);
|
||||
message_send_text(c, message_type_error, c, msgtemp);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool do_save;
|
||||
if (channel_get_permanent(channel))
|
||||
do_save = true;
|
||||
else
|
||||
do_save = false;
|
||||
const char* topic = text + std::strlen("/topic ");
|
||||
if ((std::strlen(topic) + 1) > MAX_TOPIC_LEN)
|
||||
{
|
||||
msgtemp = localize(c, "Max topic length exceeded (max {} symbols)", MAX_TOPIC_LEN);
|
||||
message_send_text(c, message_type_error, c, msgtemp);
|
||||
|
||||
Topic.set(std::string(channel_name), std::string(topicstr), do_save);
|
||||
return -1;
|
||||
}
|
||||
|
||||
channel_set_topic(channel, topic);
|
||||
}
|
||||
|
||||
// display channel topic
|
||||
if (Topic.display(c, std::string(channel_name)) == false)
|
||||
else
|
||||
{
|
||||
msgtemp = localize(c, "{} topic: no topic", channel_name);
|
||||
message_send_text(c, message_type_info, c, msgtemp);
|
||||
// get channel topic
|
||||
|
||||
channel_display_topic(channel, c);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -2038,8 +2038,7 @@ namespace pvpgn
|
|||
|
||||
if (conn_is_irc_variant(c) == 0)
|
||||
{
|
||||
class_topic Topic;
|
||||
Topic.display(c, std::string(channel_get_name(c->protocol.chat.channel)));
|
||||
channel_display_topic(c->protocol.chat.channel, c);
|
||||
}
|
||||
|
||||
if (c->protocol.chat.channel && (channel_get_flags(c->protocol.chat.channel) & channel_flags_moderated))
|
||||
|
|
|
@ -24,6 +24,9 @@
|
|||
#include <cstring>
|
||||
#include <cctype>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
#include <optional.hpp>
|
||||
|
||||
#include "compat/strcasecmp.h"
|
||||
#include "common/irc_protocol.h"
|
||||
|
@ -433,21 +436,19 @@ namespace pvpgn
|
|||
|
||||
static int _handle_list_command(t_connection * conn, int numparams, char ** params, char * text)
|
||||
{
|
||||
std::string tmp;
|
||||
irc_send(conn, RPL_LISTSTART, "Channel :Users Names"); /* backward compatibility */
|
||||
|
||||
if (numparams == 0)
|
||||
{
|
||||
t_elem const * curr;
|
||||
class_topic Topic;
|
||||
LIST_TRAVERSE_CONST(channellist(), curr)
|
||||
{
|
||||
t_channel const * channel = (const t_channel*)elem_get_data(curr);
|
||||
char const * tempname = irc_convert_channel(channel, conn);
|
||||
std::string topicstr = Topic.get(channel_get_name(channel));
|
||||
nonstd::optional<std::string> topic = channel_get_topic(channel);
|
||||
|
||||
/* FIXME: AARON: only list channels like in /channels command */
|
||||
tmp = std::string(tempname) + " " + std::to_string(channel_get_length(channel)) + " :" + topicstr;
|
||||
std::string tmp = fmt::format("{} {} :{}", tempname, channel_get_length(channel), topic.value_or(""));
|
||||
|
||||
if (tmp.length() > MAX_IRC_MESSAGE_LEN)
|
||||
eventlog(eventlog_level_warn, __FUNCTION__, "LISTREPLY length exceeded");
|
||||
|
@ -459,7 +460,6 @@ namespace pvpgn
|
|||
{
|
||||
int i;
|
||||
char ** e;
|
||||
class_topic Topic;
|
||||
|
||||
e = irc_get_listelems(params[0]);
|
||||
/* FIXME: support wildcards! */
|
||||
|
@ -474,10 +474,10 @@ namespace pvpgn
|
|||
if (!channel)
|
||||
continue; /* channel doesn't exist */
|
||||
|
||||
std::string topicstr = Topic.get(channel_get_name(channel));
|
||||
nonstd::optional<std::string> topic = channel_get_topic(channel);
|
||||
char const * tempname = irc_convert_channel(channel, conn);
|
||||
|
||||
tmp = std::string(tempname) + " " + std::to_string(channel_get_length(channel)) + " :" + topicstr;
|
||||
std::string tmp = fmt::format("{} {} :{}", tempname, channel_get_length(channel), topic.value_or(""));
|
||||
|
||||
if (tmp.length() > MAX_IRC_MESSAGE_LEN)
|
||||
eventlog(eventlog_level_warn, __FUNCTION__, "LISTREPLY length exceeded");
|
||||
|
|
|
@ -26,6 +26,9 @@
|
|||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include <optional.hpp>
|
||||
|
||||
#include "compat/strcasecmp.h"
|
||||
#include "common/irc_protocol.h"
|
||||
|
@ -482,20 +485,12 @@ namespace pvpgn
|
|||
return 0;
|
||||
}
|
||||
|
||||
class_topic Topic;
|
||||
topicstr = Topic.get(channel_get_name(gamechannel));
|
||||
nonstd::optional<std::string> topic = channel_get_topic(gamechannel);
|
||||
|
||||
if (topicstr.empty() == false) {
|
||||
if (std::strlen(gamename) + 1 + 20 + 1 + 1 + std::strlen(topicstr.c_str()) > MAX_IRC_MESSAGE_LEN) {
|
||||
WARN0("LISTREPLY length exceeded");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (std::strlen(gamename) + 1 + 20 + 1 + 1 > MAX_IRC_MESSAGE_LEN) {
|
||||
WARN0("LISTREPLY length exceeded");
|
||||
return 0;
|
||||
}
|
||||
if (std::strlen(gamename) + 1 + 20 + 1 + 1 + topic.value_or("").length() > MAX_IRC_MESSAGE_LEN)
|
||||
{
|
||||
WARN0("LISTREPLY length exceeded");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
|
@ -538,8 +533,9 @@ namespace pvpgn
|
|||
|
||||
std::strcat(temp, "::");
|
||||
|
||||
if (topicstr.c_str()) {
|
||||
std::snprintf(temp_a, sizeof(temp_a), "%s", topicstr.c_str()); /* topic */
|
||||
if (topic.has_value())
|
||||
{
|
||||
std::snprintf(temp_a, sizeof(temp_a), "%s", topic.value().c_str()); /* topic */
|
||||
std::strcat(temp, temp_a);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,8 +27,10 @@
|
|||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <optional.hpp>
|
||||
|
||||
#include "compat/strcasecmp.h"
|
||||
|
||||
|
@ -1740,13 +1742,12 @@ namespace pvpgn
|
|||
|
||||
extern int irc_send_topic(t_connection* c, t_channel const* channel)
|
||||
{
|
||||
class_topic Topic;
|
||||
std::string topicstr = Topic.get(channel_get_name(channel));
|
||||
nonstd::optional<std::string> topic = channel_get_topic(channel);
|
||||
char temp[MAX_IRC_MESSAGE_LEN];
|
||||
|
||||
if (topicstr.empty() == false)
|
||||
if (topic.has_value())
|
||||
{
|
||||
std::snprintf(temp, sizeof(temp), "%s :%s", irc_convert_channel(channel, c), topicstr.c_str());
|
||||
std::snprintf(temp, sizeof(temp), "%s :%s", irc_convert_channel(channel, c), topic.value().c_str());
|
||||
irc_send(c, RPL_TOPIC, temp);
|
||||
}
|
||||
else
|
||||
|
@ -1765,15 +1766,17 @@ namespace pvpgn
|
|||
{
|
||||
char** e = NULL;
|
||||
char temp[MAX_IRC_MESSAGE_LEN];
|
||||
class_topic Topic;
|
||||
|
||||
if (params && params[0])
|
||||
{
|
||||
if (conn_get_wol(conn) == 1) {
|
||||
t_channel* channel = conn_get_channel(conn);
|
||||
if (channel)
|
||||
Topic.set(std::string(channel_get_name(channel)), std::string(text), false);
|
||||
else {
|
||||
{
|
||||
channel_set_topic(channel, text);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::snprintf(temp, sizeof(temp), "%s :You're not on that channel", params[0]);
|
||||
irc_send(conn, ERR_NOTONCHANNEL, temp);
|
||||
}
|
||||
|
|
|
@ -420,7 +420,7 @@ int pre_server_startup(void)
|
|||
teamlist_load();
|
||||
if (realmlist_create(prefs_get_realmfile()) < 0)
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "could not load realm list");
|
||||
//topiclist_load(std::string(prefs_get_topicfile()));
|
||||
load_topic_conf(prefs_get_topicfile());
|
||||
userlog_init();
|
||||
if (prefs_get_verify_account_email() == 1)
|
||||
{
|
||||
|
@ -460,7 +460,7 @@ void post_server_shutdown(int status)
|
|||
switch (status)
|
||||
{
|
||||
case 0:
|
||||
//topiclist_unload();
|
||||
unload_topic_conf();
|
||||
account_email_verification_unload();
|
||||
smtp_cleanup();
|
||||
account_email_verification_unload();
|
||||
|
|
|
@ -1329,6 +1329,7 @@ namespace pvpgn
|
|||
clanlist_save();
|
||||
gamelist_check_voidgame();
|
||||
ladders.save();
|
||||
topiclist_save();
|
||||
next_savetime += prefs_get_user_sync_timer();
|
||||
}
|
||||
accountlist_save(FS_NONE);
|
||||
|
|
|
@ -1,248 +1,283 @@
|
|||
/*
|
||||
* Copyright (C) 2015 xboi209
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "common/setup_before.h"
|
||||
#include "topic.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include <optional.hpp>
|
||||
#include "json/json.hpp"
|
||||
|
||||
#include "compat/strcasecmp.h"
|
||||
|
||||
#include "common/eventlog.h"
|
||||
#include "common/field_sizes.h"
|
||||
|
||||
#include "channel.h"
|
||||
#include "i18n.h"
|
||||
#include "message.h"
|
||||
#include "prefs.h"
|
||||
|
||||
#include "common/setup_after.h"
|
||||
|
||||
|
||||
using nlohmann::json;
|
||||
using nonstd::optional;
|
||||
using nonstd::nullopt;
|
||||
|
||||
namespace pvpgn
|
||||
{
|
||||
|
||||
namespace bnetd
|
||||
{
|
||||
std::vector<std::shared_ptr<class_topic::t_topic>> class_topic::class_topiclist::Head;
|
||||
static std::unordered_map<std::string, std::string> topic_head;
|
||||
|
||||
bool class_topic::class_topiclist::IsHeadLoaded = false;
|
||||
static bool is_topic_conf_loaded = false;
|
||||
static std::string topic_conf_filename;
|
||||
|
||||
class_topic::class_topic()
|
||||
bool load_topic_conf(const std::string& filename)
|
||||
{
|
||||
//Already loaded, do not load again
|
||||
if (this->topiclist.IsHeadLoaded == true)
|
||||
return;
|
||||
|
||||
std::ifstream topicfile_stream(prefs_get_topicfile());
|
||||
if (!topicfile_stream)
|
||||
if (is_topic_conf_loaded)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "couldn't open topic file");
|
||||
return;
|
||||
eventlog(eventlog_level_warn, __FUNCTION__, "topic conf already loaded");
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string strLine;
|
||||
std::smatch match;
|
||||
const std::regex rgx("(.*)\t(.*)"); // tab character separates the channel name and topic
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
|
||||
//loop through each line in topic file
|
||||
while (std::getline(topicfile_stream, strLine))
|
||||
try
|
||||
{
|
||||
//skip empty lines
|
||||
if (strLine.empty() == true)
|
||||
continue;
|
||||
|
||||
if (!std::regex_search(strLine, match, rgx))
|
||||
std::ifstream topicfile_stream(filename);
|
||||
if (!topicfile_stream.is_open())
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "Invalid line in topic file ({})", strLine.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
//check if current line in file already exists in Head
|
||||
if (this->topiclist.get(match[1].str()) != nullptr)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "Duplicate line for channel {} in topic file", match[1].str().c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
//save current line to Head
|
||||
this->topiclist.add(match[1].str(), match[2].str(), true);
|
||||
}
|
||||
|
||||
this->topiclist.IsHeadLoaded = true;
|
||||
}
|
||||
|
||||
//Get channel_name's topic string
|
||||
std::string class_topic::get(const std::string channel_name)
|
||||
{
|
||||
if ((channel_name.size() + 1) > MAX_CHANNELNAME_LEN || channel_name.empty() == true)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got invalid channel name length");
|
||||
return std::string();
|
||||
}
|
||||
|
||||
auto topic = this->topiclist.get(channel_name);
|
||||
if (topic == nullptr)
|
||||
return std::string();
|
||||
|
||||
return topic->topicstr;
|
||||
}
|
||||
|
||||
//Sets channel_name's topic
|
||||
bool class_topic::set(const std::string channel_name, const std::string topic_text, bool do_save)
|
||||
{
|
||||
if ((channel_name.size() + 1) > MAX_CHANNELNAME_LEN || channel_name.empty() == true)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got invalid channel name length");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((topic_text.size() + 1) > MAX_TOPIC_LEN || topic_text.empty() == true)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got invalid topic length");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto topic = this->topiclist.get(channel_name);
|
||||
|
||||
if (topic != nullptr)
|
||||
{
|
||||
eventlog(eventlog_level_trace, __FUNCTION__, "Setting <{}>'s topic to <{}>", channel_name.c_str(), topic_text.c_str());
|
||||
topic->topicstr = topic_text;
|
||||
}
|
||||
else
|
||||
{
|
||||
eventlog(eventlog_level_trace, __FUNCTION__, "Adding <{}:{}> to topiclist", channel_name.c_str(), topic_text.c_str());
|
||||
this->topiclist.add(channel_name, topic_text, do_save);
|
||||
}
|
||||
|
||||
if (do_save == true)
|
||||
{
|
||||
if (this->topiclist.save() == false)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "error saving topic list");
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "couldn't open topic file \"{}\"", filename);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
json jconf;
|
||||
topicfile_stream >> jconf;
|
||||
|
||||
// Displays channel_name's topic to connection c
|
||||
bool class_topic::display(t_connection * c, const std::string channel_name)
|
||||
{
|
||||
if ((channel_name.size() + 1) > MAX_CHANNELNAME_LEN || channel_name.empty() == true)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got invalid channel name length");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto topic = this->topiclist.get(channel_name);
|
||||
if (topic == nullptr)
|
||||
return false;
|
||||
|
||||
auto topicstr = topic->topicstr;
|
||||
|
||||
if (topicstr.empty())
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "topic is empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
//send parts of topic string as separate message if there's a newline character
|
||||
std::regex rgx("\\\\n+");
|
||||
std::sregex_token_iterator iter(topicstr.begin(), topicstr.end(), rgx, -1), end;
|
||||
for (bool first = true; iter != end; ++iter)
|
||||
{
|
||||
std::string msg(iter->str());
|
||||
if (first == true)
|
||||
for (auto& entry : jconf.at("topics").items())
|
||||
{
|
||||
msg.insert(0, topic->channel_name + " topic: ");
|
||||
first = false;
|
||||
}
|
||||
message_send_text(c, message_type_info, c, msg);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//Get t_topic pointer of channel_name
|
||||
std::shared_ptr<class_topic::t_topic> class_topic::class_topiclist::get(const std::string channel_name)
|
||||
{
|
||||
for (auto topic : this->Head)
|
||||
{
|
||||
if (strcasecmp(channel_name.c_str(), topic->channel_name.c_str()) == 0)
|
||||
return topic;
|
||||
}
|
||||
eventlog(eventlog_level_debug, __FUNCTION__, "returning nullptr");
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//Saves data from Head vector to topic file
|
||||
bool class_topic::class_topiclist::save()
|
||||
{
|
||||
std::fstream topicfile_stream(prefs_get_topicfile(), std::ofstream::app);
|
||||
if (!topicfile_stream)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "couldn't open topic file");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string strLine;
|
||||
std::smatch match;
|
||||
const std::regex rgx("(.*)\t(.*)"); // tab character separates the channel name and topic
|
||||
|
||||
//Check if data in Head vector already exists in topic file
|
||||
while (std::getline(topicfile_stream, strLine))
|
||||
{
|
||||
if (!std::regex_search(strLine, match, rgx))
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "Invalid line in topic file ({})", strLine.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto topic : this->Head)
|
||||
{
|
||||
if (topic->save == true)
|
||||
try
|
||||
{
|
||||
if (match[1].str() == topic->channel_name)
|
||||
auto success = topic_head.emplace(entry.key(), entry.value());
|
||||
if (!success.second)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
topicfile_stream << topic->channel_name << "\t" << topic->topicstr << std::endl;
|
||||
break;
|
||||
eventlog(eventlog_level_warn, __FUNCTION__, "failed to load topic for channel \"{}\" (possible duplicate?)", entry.key());
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "could not load topic for channel \"{}\"", entry.key());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "failed to load {} ({})", filename, e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto t1 = std::chrono::steady_clock::now();
|
||||
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "Successfully loaded {} channel topics in {} milliseconds", topic_head.size(), std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0).count());
|
||||
|
||||
topic_conf_filename = filename;
|
||||
|
||||
is_topic_conf_loaded = true;
|
||||
|
||||
return is_topic_conf_loaded;
|
||||
}
|
||||
|
||||
//Adds a new pointer to the Head vector
|
||||
void class_topic::class_topiclist::add(std::string channel_name, std::string topic_text, bool do_save)
|
||||
void unload_topic_conf()
|
||||
{
|
||||
auto topic = std::make_shared<class_topic::t_topic>(class_topic::t_topic{ channel_name, topic_text, do_save });
|
||||
this->Head.push_back(std::move(topic));
|
||||
topiclist_save();
|
||||
|
||||
topic_head.clear();
|
||||
|
||||
topic_conf_filename.clear();
|
||||
|
||||
is_topic_conf_loaded = false;
|
||||
|
||||
eventlog(eventlog_level_info, __FUNCTION__, "Successfully unloaded all channel topics");
|
||||
}
|
||||
|
||||
void topiclist_save()
|
||||
{
|
||||
if (!is_topic_conf_loaded)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "topic conf not loaded");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
std::ofstream topicfile_stream(topic_conf_filename);
|
||||
if (!topicfile_stream.is_open())
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "couldn't open topic file");
|
||||
return;
|
||||
}
|
||||
|
||||
json jconf;
|
||||
jconf["topics"] = topic_head;
|
||||
|
||||
topicfile_stream << jconf.dump(1, '\t');
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "failed to save topic_head to {} ({})", topic_conf_filename, e.what());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
nonstd::optional<std::string> channel_get_topic(const t_channel* channel)
|
||||
{
|
||||
if (channel == nullptr)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel");
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
const char* channelname = channel_get_name(channel);
|
||||
if (channelname == nullptr)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel name");
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto search = topic_head.find(channelname);
|
||||
if (search == topic_head.end())
|
||||
{
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
return nonstd::optional<std::string>{search->second};
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "failed to get channel topic for channel \"{}\"", channelname);
|
||||
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
bool channel_set_topic(t_channel* channel, const std::string& topic)
|
||||
{
|
||||
if (channel == nullptr)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel");
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* channelname = channel_get_name(channel);
|
||||
if (channelname == nullptr)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel name");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto success = topic_head.emplace(channelname, topic);
|
||||
if (success.second == false)
|
||||
{
|
||||
// topic was not inserted because a topic for the channel already exists in topic_head
|
||||
|
||||
success.first->second = topic;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "failed to set channel topic for channel \"{}\"", channelname);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool channel_display_topic(t_channel* channel, t_connection* conn)
|
||||
{
|
||||
if (channel == nullptr)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (conn == nullptr)
|
||||
{
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "got NULL conn");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
nonstd::optional<std::string> topic = channel_get_topic(channel);
|
||||
if (topic.has_value())
|
||||
{
|
||||
const std::string delimiter = "\\n";
|
||||
const std::size_t delimiter_len = delimiter.length();
|
||||
const std::string topicstr = topic.value();
|
||||
|
||||
// support "\n" (not '\n') by individually sending substrings
|
||||
std::size_t old_newline_pos = 0;
|
||||
std::size_t curr_newline_pos = topicstr.find(delimiter, old_newline_pos);
|
||||
while (curr_newline_pos != std::string::npos)
|
||||
{
|
||||
if (old_newline_pos == curr_newline_pos)
|
||||
{
|
||||
old_newline_pos += delimiter_len;
|
||||
curr_newline_pos = topicstr.find(delimiter, old_newline_pos);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string substr = topicstr.substr(old_newline_pos, curr_newline_pos - old_newline_pos);
|
||||
message_send_text(conn, message_type_info, conn, substr);
|
||||
|
||||
old_newline_pos = curr_newline_pos + delimiter_len;
|
||||
curr_newline_pos = topicstr.find(delimiter, old_newline_pos);
|
||||
}
|
||||
|
||||
message_send_text(conn, message_type_info, conn, topicstr.substr(old_newline_pos, std::string::npos));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
message_send_text(conn, message_type_error, conn, localize(conn, "An error has occurred."));
|
||||
|
||||
const char* channelname = channel_get_name(channel);
|
||||
eventlog(eventlog_level_error, __FUNCTION__, "failed to display channel topic for channel \"{}\"", channelname ? channelname : "?");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,72 +1,37 @@
|
|||
/*
|
||||
* Copyright (C) 2015 xboi209
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDED_TOPIC_TYPES
|
||||
#define INCLUDED_TOPIC_TYPES
|
||||
#ifndef INCLUDED_TOPIC_H
|
||||
#define INCLUDED_TOPIC_H
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "connection.h"
|
||||
#include "common/list.h"
|
||||
#endif
|
||||
|
||||
#ifndef JUST_NEED_TYPES
|
||||
#ifndef INCLUDED_TOPIC_PROTOS
|
||||
#define INCLUDED_TOPIC_PROTOS
|
||||
namespace pvpgn
|
||||
{
|
||||
|
||||
namespace bnetd
|
||||
{
|
||||
|
||||
class class_topic
|
||||
{
|
||||
//all public member functions of class_topic verify channel name and topic length
|
||||
public:
|
||||
class_topic();
|
||||
bool load_topic_conf(const std::string& filename);
|
||||
void unload_topic_conf();
|
||||
void topiclist_save();
|
||||
|
||||
std::string get(const std::string channel_name);
|
||||
bool set(const std::string channel_name, const std::string topic_text, bool do_save);
|
||||
bool display(t_connection * c, const std::string channel_name);
|
||||
private:
|
||||
struct t_topic
|
||||
{
|
||||
std::string channel_name;
|
||||
std::string topicstr;
|
||||
bool save;
|
||||
};
|
||||
}
|
||||
|
||||
class class_topiclist
|
||||
{
|
||||
public:
|
||||
static bool IsHeadLoaded;
|
||||
std::shared_ptr<class_topic::t_topic> get(const std::string channel_name);
|
||||
bool save();
|
||||
void add(std::string channel_name, std::string topic_text, bool do_save);
|
||||
private:
|
||||
static std::vector<std::shared_ptr<class_topic::t_topic>> Head;
|
||||
} topiclist;
|
||||
};
|
||||
}
|
||||
|
||||
} //namespace bnetd
|
||||
|
||||
} //namespace pvpgn
|
||||
|
||||
#endif
|
||||
#endif
|
Loading…
Reference in a new issue