A first attempt to make dialogue capability for module

This commit is contained in:
Blue 2025-04-01 19:46:35 +03:00
parent 8187d045cd
commit ab9a0af05e
Signed by: blue
GPG key ID: 9B203B252A63EE38
8 changed files with 190 additions and 30 deletions

View file

@ -3,23 +3,21 @@
#include "router.h"
#include <string_view>
#include "module/module.h"
#include "connection/connection.h"
Router::Router(const Shared::Logger& logger):
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" }},
{Shared::forbidden, {"Forbidden"}},
{Shared::error, {"Command error"}},
{Shared::unhandled, {"No such command"}},
}),
generator(std::random_device{}())
{}
generator(std::random_device{}()),
dialogs() {}
void Router::registerModule(const std::shared_ptr<Module::Module>& module) {
info("Registering module " + module->name + " as " + module->alias);
@ -40,49 +38,129 @@ void Router::registerActor(const std::string& key, const std::string& 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;
Shared::VC::const_iterator dItr = dialogs.find(sender);
if (dItr != dialogs.end()) {
if (body != "cancel")
return routeToModule(dItr->second, sender, body);
onMessageResult(cancelDialog(sender), sender);
return;
}
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 (aliasView.size() > 100) {
warn("Received a message with the first word larger than 100 symbols from " + sender);
onMessageResult(Shared::unhandled, sender);
return;
}
if (view.empty()) {
routeToModule(std::string(aliasView), sender, view);
}
void Router::routeToModule(const std::string& alias, const std::string& sender, const std::string_view& message) {
if (message.empty()) {
debug("Incoming message from " + sender + " consists of only one word, modules have nothing to process");
return onMessageResult(Shared::unhandled, sender);
onMessageResult(Shared::unhandled, sender);
return;
}
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);
onMessageResult(Shared::unhandled, sender);
return;
}
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);
onMessageResult(Shared::error, sender);
return;
}
Shared::Result result;
Shared::Result result = Shared::error;
try {
result = module->message(aItr->second, view);
result = module->message(getActor(sender), message);
if (result == Shared::success)
debug("module \"" + mItr->first + "\" successfully handled message from " + sender);
debug("module \"" + mItr->first + "\" successfully handled message from " + sender);
} catch (const std::exception& e) {
error("module \"" + mItr->first + "\" thew an exception handling message from " + sender + ": " + e.what());
} catch (...) {
error("module \"" + mItr->first + "\" thew an unhandled exception handling message from " + sender);
result = Shared::error;
}
switch (result) {
case Shared::grab:
startDialog(sender, alias);
break;
default:
finishDialog(sender);
break;
}
onMessageResult(result, sender);
}
bool Router::startDialog(const std::string& actor, const std::string& alias) {
std::pair<Shared::VC::iterator, bool> result = dialogs.emplace(actor, alias);
if (alias == result.first->second) {
if (result.second)
debug("Starting dialog of " + actor + " and " + alias);
else
debug("Continuing dialog of " + actor + " and " + alias);
result.second = true;
}
if (!result.second)
warn("There was a request from the module " + alias + " to start dialog with " + actor + " but the module " +
result.first->second + " is already in a dialog with this user right now");
return result.second;
}
void Router::finishDialog(const std::string& actor) {
Shared::VC::const_iterator itr = dialogs.find(actor);
if (itr != dialogs.end())
return debug("Dialogue of " + actor + " and " + itr->second + " is complete");
dialogs.erase(itr);
}
Shared::Result Router::cancelDialog(const std::string& actor) {
Shared::VC::const_iterator dItr = dialogs.find(actor);
if (dItr == dialogs.end()) {
warn("There was a request to cancel dialog with " + actor + " but there is no dialog with him open right now");
return Shared::unhandled;
}
Modules::iterator mItr = modules.find(dItr->second);
if (mItr == modules.end()) {
error("could not find module \"" + dItr->second + "\" to cancel dialog with " + actor);
return Shared::unhandled;
}
std::shared_ptr<Module::Module> module = mItr->second.lock();
if (!module) {
error("could not lock module \"" + mItr->first + "\" to cancel dialog with " + actor);
return Shared::error;
}
debug("canceling dialog with " + actor);
try {
module->cancelDialog(getActor(actor));
dialogs.erase(dItr);
return Shared::success;
} catch (const std::exception& e) {
error("Caught exception canceling dialog in module " + dItr->second + ": " + e.what());
} catch (...) {
error("Caught unhandled exception canceling dialog in module " + dItr->second);
}
return Shared::error;
}
std::map<std::string, std::string> Router::getActors() const {
std::map<std::string, std::string> result;
@ -116,7 +194,7 @@ 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");
@ -128,3 +206,10 @@ void Router::onMessageResult(Shared::Result result, const std::string& sender) {
cn->send(sender, options[dist(generator)]);
}
std::shared_ptr<::Actor> Router::getActor(const std::string& jid) {
Actors::iterator itr = actors.find(jid);
if (itr == actors.end())
itr = actors.emplace(jid, std::make_shared<Actor>(jid, defaultGroup)).first;
return itr->second;
}

View file

@ -4,10 +4,12 @@
#pragma once
#include <string>
#include <string_view>
#include <map>
#include <vector>
#include <memory>
#include <random>
#include <exception>
#include "shared/result.h"
#include "shared/loggable.h"
@ -27,6 +29,7 @@ public:
void registerModule(const std::shared_ptr<Module::Module>& module);
void registerActor(const std::string& key, const std::string& group);
void routeMessage(const std::string& sender, const std::string& body);
void routeToModule(const std::string& alias, const std::string& sender, const std::string_view& message);
std::map<std::string, std::string> getActors() const;
std::string getDefaultGroup() const;
@ -37,6 +40,11 @@ public:
private:
void onMessageResult(Shared::Result result, const std::string& sender);
std::shared_ptr<::Actor> getActor(const std::string& jid);
bool startDialog(const std::string& actor, const std::string& alias);
void finishDialog(const std::string& alias);
Shared::Result cancelDialog(const std::string& actor);
private:
typedef std::map<std::string, std::weak_ptr<Module::Module>> Modules;
@ -50,4 +58,5 @@ private:
std::string defaultGroup;
Responses responses;
std::mt19937 generator;
Shared::VC dialogs;
};