131 lines
4.1 KiB
C++
131 lines
4.1 KiB
C++
// SPDX-FileCopyrightText: 2024 Yury Gubich <blue@macaw.me>
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
#include "router.h"
|
|
|
|
#include <string_view>
|
|
|
|
#include "module/module.h"
|
|
#include "connection/connection.h"
|
|
|
|
Router::Router(const Shared::Logger& logger):
|
|
Shared::Loggable(logger, {"Router"}),
|
|
modules(),
|
|
actors(),
|
|
defaultGroup("Stranger"),
|
|
responses({
|
|
{Shared::forbidden, { "Forbidden" }},
|
|
{Shared::error, { "Command error" }},
|
|
{Shared::unhandled, { "No such command" }},
|
|
}),
|
|
generator(std::random_device{}())
|
|
{}
|
|
|
|
void Router::registerModule(const std::shared_ptr<Module::Module>& module) {
|
|
info("Registering module " + module->name + " as " + module->alias);
|
|
modules[module->alias] = module;
|
|
}
|
|
|
|
void Router::registerActor(const std::string& key, const std::string& group) {
|
|
if (key == "default") {
|
|
defaultGroup = group;
|
|
return;
|
|
}
|
|
|
|
Actors::iterator act = actors.find(key);
|
|
if (act == actors.end())
|
|
actors.emplace(key, std::make_shared<Actor>(key, group));
|
|
else
|
|
act->second->setGroup(group);
|
|
}
|
|
|
|
void Router::routeMessage(const std::string& sender, const std::string& body) {
|
|
Actors::iterator aItr = actors.find(sender);
|
|
if (aItr == actors.end())
|
|
aItr = actors.emplace(sender, std::make_shared<Actor>(sender, defaultGroup)).first;
|
|
|
|
std::string_view view(body);
|
|
std::string_view aliasView = Module::Module::pop(view);
|
|
|
|
if (aliasView.size() > 1000) {
|
|
warn("Received a message with the first word larger than 1000 symbols, " + sender + "is clearly looking for a vulnerability");
|
|
return onMessageResult(Shared::unhandled, sender);
|
|
}
|
|
|
|
if (view.empty()) {
|
|
debug("Incoming message from " + sender + " consists of only one word, modules have nothing to process");
|
|
return onMessageResult(Shared::unhandled, sender);
|
|
}
|
|
|
|
std::string alias(aliasView);
|
|
Modules::iterator mItr = modules.find(alias);
|
|
if (mItr == modules.end()) {
|
|
debug("could not find module \"" + alias + "\" to handle message from " + sender);
|
|
return onMessageResult(Shared::unhandled, sender);
|
|
}
|
|
|
|
std::shared_ptr<Module::Module> module = mItr->second.lock();
|
|
if (!module) {
|
|
error("could not lock module \"" + mItr->first + "\" to handle message from " + sender);
|
|
return onMessageResult(Shared::error, sender);
|
|
}
|
|
|
|
Shared::Result result;
|
|
try {
|
|
result = module->message(aItr->second, view);
|
|
if (result == Shared::success)
|
|
debug("module \"" + mItr->first + "\" successfully handled message from " + sender);
|
|
} catch (...) {
|
|
error("module \"" + mItr->first + "\" thew an unhandled exception handling message from " + sender);
|
|
result = Shared::error;
|
|
}
|
|
|
|
onMessageResult(result, sender);
|
|
}
|
|
|
|
std::map<std::string, std::string> Router::getActors() const {
|
|
std::map<std::string, std::string> result;
|
|
|
|
for (const std::pair<const std::string, std::shared_ptr<Actor>>& pair : actors)
|
|
result.emplace(pair.first, pair.second->getGroup());
|
|
|
|
return result;
|
|
}
|
|
|
|
std::string Router::getDefaultGroup() const {
|
|
return defaultGroup;
|
|
}
|
|
|
|
void Router::setConnection(const std::shared_ptr<Connection>& cn) {
|
|
connection = cn;
|
|
}
|
|
|
|
void Router::setResponses(Shared::Result result, const Shared::Strings& options) {
|
|
responses[result] = options;
|
|
}
|
|
|
|
std::string Router::getGroup(const std::string& key) const {
|
|
Actors::const_iterator itr = actors.find(key);
|
|
if (itr == actors.end())
|
|
return defaultGroup;
|
|
|
|
return itr->second->getGroup();
|
|
}
|
|
|
|
void Router::onMessageResult(Shared::Result result, const std::string& sender) {
|
|
Responses::const_iterator rItr = responses.find(result);
|
|
if (rItr == responses.end() || rItr->second.empty())
|
|
return;
|
|
|
|
std::shared_ptr<Connection> cn = connection.lock();
|
|
if (!cn) {
|
|
warn("Couldn't send a message to " + sender + ", connection is not available");
|
|
return;
|
|
}
|
|
|
|
const List& options = rItr->second;
|
|
std::uniform_int_distribution<std::size_t> dist(0, options.size() - 1);
|
|
cn->send(sender, options[dist(generator)]);
|
|
}
|
|
|