ircabot/applicationdata.cpp

319 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "applicationdata.h"
#include "global.h"
#include "version.h"
#include <QFile>
#include <QDir>
#include <QDebug>
#include <QRegularExpression>
void ApplicationData::createConfigExample(const QString &pathToConfig)
{
QFile file(pathToConfig);
if (not file.open(QIODevice::WriteOnly)) {
throw std::runtime_error("ApplicationData::createConfigExample(): Output file openning is failed");
}
const QString configExample {
"[GLOBAL]\n"
"log_path = /srv/ircabot/chatlog\n\n"
"# IRC options\n"
"nick = default_nickname\n"
"user = default_ident\n"
"real_name = default_real_name\n"
"# If password is empty logging in without password.\n"
"password = password_for_default_nickname_using_NickServ\n\n"
"# This triggers available in all chats.\n"
"# 'request' and 'answer' must be splitted by ':::'\n"
"# triggers splitting by '<!>'\n"
"triggers = request = version ::: answer = " + IRCABOT_VERSION + " <!> request=hello:::answer=hi\n\n"
"# Web interface options\n"
"bind_to_address = 127.0.0.1\n"
"bind_to_port = 8080\n\n"
"[Displayed server name]\n"
"address = 127.0.0.1\n"
"port = 6667\n"
"# Channels splitting with comma.\n"
"# Default channel for the web interface marked with an '@' at the end (globally only one).\n"
"channels = #general@,#acetonevideo\n"
"# This triggers available in current server only.\n"
"# 'request' and 'answer' must be splitted by ':::'\n"
"# triggers splitting by '<!>'\n"
"triggers = request = hi ::: answer = hello <!> request=developer:::answer=acetone\n\n"
"# Optional parameters if global is defined:\n"
"#nick = unique_nickname_for_this_server\n"
"#user = unique_ident_for_this_server\n"
"#real_name = unique_real_name_for_this_server\n"
"#password = password_for_this_user\n"
};
file.write(configExample.toUtf8());
file.close();
}
ApplicationData::ApplicationData(const QString& pathToConfig) : m_webInterfacePort(0)
{
if (not QFile::exists(pathToConfig)) {
throw std::runtime_error("Configuration file not exist (" + pathToConfig.toStdString() + ")");
}
m_file = pathToConfig;
readConfig();
}
std::pair<QString, quint16> ApplicationData::getWebInterfaceAddress()
{
if (m_webInterfacePort == 0) {
throw std::runtime_error("Web interface port is undefined");
}
if (m_webInterfaceAddress.isEmpty()) {
throw std::runtime_error("Web interface address is undefined");
}
return {m_webInterfaceAddress, m_webInterfacePort};
}
QList<ConnectionData> ApplicationData::getConnections()
{
return m_connections;
}
QString ApplicationData::getMainChannel()
{
return m_mainChannel;
}
QString ApplicationData::getLogFolder()
{
return m_logPath;
}
void ApplicationData::readConfig()
{
QFile file(m_file);
if (not file.open(QIODevice::ReadOnly)) {
throw std::runtime_error("ApplicationData::readConfig(): Can't open file for reading");
}
QString line {file.readLine()};
QString conffile;
while (not line.isEmpty()) {
line.remove('\n');
line.remove('\r');
if (line.startsWith('#') or line.isEmpty()) {
line = file.readLine();
continue;
}
conffile += line + '\n';
line = file.readLine();
}
file.close();
if (conffile.isEmpty()) {
throw std::runtime_error("ApplicationData::readConfig(): Empty data");
}
//// Парсинг GLOBAL
QString globalSection {conffile};
int globalBegin = conffile.indexOf("[GLOBAL]");
if (globalBegin == -1) {
throw std::runtime_error("ApplicationData::readConfig(): Wrong config. [GLOBAL] section not exist!");
}
int globalEnd = conffile.indexOf(QRegularExpression("\\[[^:]*\\]"), globalBegin+1); //IPv6 addresses safe
if (globalEnd != -1) {
// Удаление последующей [секции]
globalSection.remove(globalEnd, conffile.size()-globalEnd);
}
globalSection.remove(0, globalBegin);
//// Инициализация GLOBAL
// logPath
m_logPath = global::getValue(globalSection, "log_path");
if (m_logPath.isEmpty()) {
throw std::runtime_error("ApplicationData::readConfig(): 'log_path' in [GLOBAL] is undefined");
}
QDir logDir;
if (not QFile::exists(m_logPath)) {
if (not logDir.mkpath(m_logPath)) {
throw std::runtime_error("ApplicationData::readConfig(): Can't create " + m_logPath.toStdString());
}
}
logDir.setPath(m_logPath);
if (not logDir.mkdir("TeSt__FoLdEr")) {
throw std::runtime_error("ApplicationData::readConfig(): " + m_logPath.toStdString() + " is unwritable");
}
logDir.rmdir("TeSt__FoLdEr");
if (not m_logPath.endsWith(global::slash)) {
m_logPath += global::slash;
}
// web interface
m_webInterfaceAddress = global::getValue(globalSection, "bind_to_address");
if (m_webInterfaceAddress.isEmpty()) {
throw std::runtime_error("ApplicationData::readConfig(): 'bind_to_address' in [GLOBAL] is undefined");
}
bool success = false;
m_webInterfacePort = global::getValue(globalSection, "bind_to_port").toUInt(&success);
if (not success) {
throw std::runtime_error("ApplicationData::readConfig(): 'bind_to_port' in [GLOBAL] is incorrect");
}
QMap<QString, QString> globalTriggers;
QString triggersLine = global::getValue(globalSection, "triggers");
if (not triggersLine.isEmpty()) {
QStringList triggersPair = triggersLine.split("<!>");
for (auto &pair: triggersPair) {
QString request = global::getValue(pair, "request", global::Type::eForTriggers);
if (request.isEmpty()) continue;
QString answer = global::getValue(pair, "answer", global::Type::eForTriggers);
if (answer.isEmpty()) continue;
globalTriggers[request] = answer;
qInfo().noquote() << "[GLOBAL] Trigger (" << request << ":::" << answer << ")";
}
}
// other
m_nick = global::getValue(globalSection, "nick");
m_nick.replace(' ', '_');
m_user = global::getValue(globalSection, "user");
m_realName = global::getValue(globalSection, "real_name");
m_password = global::getValue(globalSection, "password");
conffile.remove(globalSection);
//// Парсинг подключений
// Цикл до тех пор, пока остались заголовки секций
while (conffile.contains(QRegularExpression("\\[[^\\n]*\\]"))) {
QString currentSection {conffile};
int begin = conffile.indexOf(QRegularExpression("\\[[^\\n]*\\]"));
int end = conffile.indexOf(QRegularExpression("\\[[^:]*\\]"), begin+1);
if (end != -1) {
currentSection.remove(end, currentSection.size() - end);
}
currentSection.remove(0, begin);
conffile.remove(currentSection);
//// Инициализация информации о подключениях
ConnectionData newConnection;
newConnection.displayName = currentSection.toStdString().substr(1, currentSection.indexOf(']')-1).c_str();
newConnection.address = global::getValue(currentSection, "address");
if (newConnection.address.isEmpty()) {
qInfo().noquote() << "[" + newConnection.displayName + "]" << "ignored (empty 'address')";
continue;
}
success = false;
newConnection.port = global::getValue(currentSection, "port").toUInt(&success);
if (not success) {
qInfo().noquote() << "[" + newConnection.displayName + "]" << "ignored (wrong 'port')";
continue;
}
QString channelsString = global::getValue(currentSection, "channels");
if (channelsString.isEmpty()) {
qInfo().noquote() << "[" + newConnection.displayName + "]" << "ignored (empty 'channels')";
continue;
}
newConnection.channels = channelsString.split(',');
for (auto &ch: newConnection.channels) {
ch.remove(' ');
if (not ch.startsWith('#')) {
ch = '#' + ch;
}
if (ch.endsWith('@')) {
ch.remove('@');
m_mainChannel = global::toLowerAndNoSpaces(newConnection.displayName) + "/";
m_mainChannel += global::toLowerAndNoSpaces(ch);
}
}
newConnection.user = global::getValue(currentSection, "user");
if (newConnection.user.isEmpty()) {
if (m_user.isEmpty()) {
qInfo().noquote() << "[" + newConnection.displayName + "]" << "ignored (empty local 'user' and global too)";
continue;
}
else {
newConnection.user = m_user;
}
}
newConnection.nick = global::getValue(currentSection, "nick");
if (newConnection.nick.isEmpty()) {
if (m_nick.isEmpty()) {
qInfo().noquote() << "[" + newConnection.displayName + "]" << "ignored (empty local 'nick' and global too)";
continue;
}
else {
newConnection.nick = m_nick;
}
}
else {
newConnection.nick.replace(' ', '_');
}
newConnection.realName = global::getValue(currentSection, "real_name");
if (newConnection.realName.isEmpty()) {
if (m_realName.isEmpty()) {
qInfo().noquote() << "[" + newConnection.displayName + "]" << "ignored (empty local 'real_name' and global too)";
continue;
}
else {
newConnection.realName = m_realName;
}
}
newConnection.password = global::getValue(currentSection, "password");
if (newConnection.password.isEmpty() and not m_password.isEmpty()) {
newConnection.password = m_password;
}
QString path {global::toLowerAndNoSpaces(newConnection.displayName)};
newConnection.logFolderPath = m_logPath + path;
QString triggersLine = global::getValue(currentSection, "triggers");
if (not triggersLine.isEmpty()) {
QStringList triggersPair = triggersLine.split("<!>");
for (auto &pair: triggersPair) {
QString request = global::getValue(pair, "request", global::Type::eForTriggers);
if (request.isEmpty()) continue;
QString answer = global::getValue(pair, "answer", global::Type::eForTriggers);
if (answer.isEmpty()) continue;
newConnection.triggers[request] = answer;
qInfo().noquote() << "[" + newConnection.displayName + "] Trigger (" <<
request << ":::" << answer << ")";
}
}
for (auto &glob: globalTriggers) {
bool detected = false;
for (auto local: newConnection.triggers) {
if (newConnection.triggers.key(local) == globalTriggers.key(glob)) {
qInfo().noquote() << "[" + newConnection.displayName + "]" <<
"Trigger '" + globalTriggers.key(glob) + "' ignored (GLOBAL)";
detected = true;
}
}
if (not detected) {
newConnection.triggers[globalTriggers.key(glob)] = glob;
}
}
m_connections.push_back(newConnection);
}
if (m_mainChannel.isEmpty()) {
m_mainChannel = global::toLowerAndNoSpaces(m_connections.first().displayName) + "/";
m_mainChannel += global::toLowerAndNoSpaces(m_connections.first().channels.first());
}
m_mainChannel.remove('#');
qInfo().noquote() << "[GLOBAL] Main channel:" << m_mainChannel;
}