A first attempt to make dialogue capability for module
This commit is contained in:
parent
8187d045cd
commit
ab9a0af05e
8 changed files with 190 additions and 30 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue