A first attempt to make dialogue capability for module
This commit is contained in:
parent
8187d045cd
commit
ab9a0af05e
52
.clang-format
Normal file
52
.clang-format
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
BasedOnStyle: Google
|
||||||
|
|
||||||
|
# === Indentation ===
|
||||||
|
IndentWidth: 4
|
||||||
|
TabWidth: 4
|
||||||
|
UseTab: Never
|
||||||
|
|
||||||
|
# === Pointer/Reference Alignment ===
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
PointerAlignment: Left
|
||||||
|
|
||||||
|
# === Braces ===
|
||||||
|
BraceWrapping:
|
||||||
|
AfterClass: false
|
||||||
|
AfterControlStatement: false
|
||||||
|
AfterEnum: false
|
||||||
|
AfterFunction: false
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterStruct: false
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: false
|
||||||
|
IndentBraces: false
|
||||||
|
|
||||||
|
# === Spaces ===
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
SpacesInAngles: false
|
||||||
|
|
||||||
|
# === Line Breaking ===
|
||||||
|
AllowShortFunctionsOnASingleLine: Empty
|
||||||
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
|
BreakBeforeBraces: Attach
|
||||||
|
ColumnLimit: 120
|
||||||
|
|
||||||
|
# === Includes ===
|
||||||
|
IncludeBlocks: Preserve
|
||||||
|
IncludeCategories:
|
||||||
|
- Regex: '.*'
|
||||||
|
Priority: 1
|
||||||
|
SortIncludes: false
|
||||||
|
|
||||||
|
# === Misc ===
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlwaysBreakTemplateDeclarations: Yes
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
|
||||||
|
# === Constructor ===
|
||||||
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
BreakConstructorInitializers: AfterColon
|
12
README.md
12
README.md
@ -22,8 +22,14 @@ cmake --build .
|
|||||||
## Modules
|
## Modules
|
||||||
|
|
||||||
### Actor
|
### Actor
|
||||||
This module is designed to control access to Bot.
|
This module is designed to manage access to Bot.
|
||||||
|
|
||||||
#### Commands:
|
#### Commands:
|
||||||
- list: lists all known actors
|
- **list**: lists all known actors
|
||||||
- set `actor` `group`: assigns a group to an actor. `default` is a special actor name, group from it is assigned to all new actors, not listed in `actors` section of config
|
- **set** `actor` `group`: assigns a group to an actor. `default` is a special actor name, group from it is assigned to all new actors, not listed in `actors` section of config
|
||||||
|
|
||||||
|
### Publish
|
||||||
|
This module enables bot to publish to PubSub nodes
|
||||||
|
|
||||||
|
#### Commands:
|
||||||
|
- **to** `node@service` `title`\n`article`: Publishes `article` to the `node` of the `service`
|
@ -3,23 +3,21 @@
|
|||||||
|
|
||||||
#include "router.h"
|
#include "router.h"
|
||||||
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
#include "module/module.h"
|
#include "module/module.h"
|
||||||
#include "connection/connection.h"
|
#include "connection/connection.h"
|
||||||
|
|
||||||
Router::Router(const Shared::Logger& logger):
|
Router::Router(const Shared::Logger& logger) :
|
||||||
Shared::Loggable(logger, {"Router"}),
|
Shared::Loggable(logger, {"Router"}),
|
||||||
modules(),
|
modules(),
|
||||||
actors(),
|
actors(),
|
||||||
defaultGroup("Stranger"),
|
defaultGroup("Stranger"),
|
||||||
responses({
|
responses({
|
||||||
{Shared::forbidden, { "Forbidden" }},
|
{Shared::forbidden, {"Forbidden"}},
|
||||||
{Shared::error, { "Command error" }},
|
{Shared::error, {"Command error"}},
|
||||||
{Shared::unhandled, { "No such command" }},
|
{Shared::unhandled, {"No such command"}},
|
||||||
}),
|
}),
|
||||||
generator(std::random_device{}())
|
generator(std::random_device{}()),
|
||||||
{}
|
dialogs() {}
|
||||||
|
|
||||||
void Router::registerModule(const std::shared_ptr<Module::Module>& module) {
|
void Router::registerModule(const std::shared_ptr<Module::Module>& module) {
|
||||||
info("Registering module " + module->name + " as " + module->alias);
|
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) {
|
void Router::routeMessage(const std::string& sender, const std::string& body) {
|
||||||
Actors::iterator aItr = actors.find(sender);
|
Shared::VC::const_iterator dItr = dialogs.find(sender);
|
||||||
if (aItr == actors.end())
|
if (dItr != dialogs.end()) {
|
||||||
aItr = actors.emplace(sender, std::make_shared<Actor>(sender, defaultGroup)).first;
|
if (body != "cancel")
|
||||||
|
return routeToModule(dItr->second, sender, body);
|
||||||
|
|
||||||
|
onMessageResult(cancelDialog(sender), sender);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string_view view(body);
|
std::string_view view(body);
|
||||||
std::string_view aliasView = Module::Module::pop(view);
|
std::string_view aliasView = Module::Module::pop(view);
|
||||||
|
if (aliasView.size() > 100) {
|
||||||
if (aliasView.size() > 1000) {
|
warn("Received a message with the first word larger than 100 symbols from " + sender);
|
||||||
warn("Received a message with the first word larger than 1000 symbols, " + sender + "is clearly looking for a vulnerability");
|
onMessageResult(Shared::unhandled, sender);
|
||||||
return 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");
|
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);
|
Modules::iterator mItr = modules.find(alias);
|
||||||
if (mItr == modules.end()) {
|
if (mItr == modules.end()) {
|
||||||
debug("could not find module \"" + alias + "\" to handle message from " + sender);
|
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();
|
std::shared_ptr<Module::Module> module = mItr->second.lock();
|
||||||
if (!module) {
|
if (!module) {
|
||||||
error("could not lock module \"" + mItr->first + "\" to handle message from " + sender);
|
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 {
|
try {
|
||||||
result = module->message(aItr->second, view);
|
result = module->message(getActor(sender), message);
|
||||||
if (result == Shared::success)
|
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 (...) {
|
} catch (...) {
|
||||||
error("module \"" + mItr->first + "\" thew an unhandled exception handling message from " + sender);
|
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);
|
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> Router::getActors() const {
|
||||||
std::map<std::string, std::string> result;
|
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);
|
Responses::const_iterator rItr = responses.find(result);
|
||||||
if (rItr == responses.end() || rItr->second.empty())
|
if (rItr == responses.end() || rItr->second.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::shared_ptr<Connection> cn = connection.lock();
|
std::shared_ptr<Connection> cn = connection.lock();
|
||||||
if (!cn) {
|
if (!cn) {
|
||||||
warn("Couldn't send a message to " + sender + ", connection is not available");
|
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)]);
|
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
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
#include "shared/result.h"
|
#include "shared/result.h"
|
||||||
#include "shared/loggable.h"
|
#include "shared/loggable.h"
|
||||||
@ -27,6 +29,7 @@ public:
|
|||||||
void registerModule(const std::shared_ptr<Module::Module>& module);
|
void registerModule(const std::shared_ptr<Module::Module>& module);
|
||||||
void registerActor(const std::string& key, const std::string& group);
|
void registerActor(const std::string& key, const std::string& group);
|
||||||
void routeMessage(const std::string& sender, const std::string& body);
|
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::map<std::string, std::string> getActors() const;
|
||||||
std::string getDefaultGroup() const;
|
std::string getDefaultGroup() const;
|
||||||
@ -37,6 +40,11 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void onMessageResult(Shared::Result result, const std::string& sender);
|
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:
|
private:
|
||||||
typedef std::map<std::string, std::weak_ptr<Module::Module>> Modules;
|
typedef std::map<std::string, std::weak_ptr<Module::Module>> Modules;
|
||||||
@ -50,4 +58,5 @@ private:
|
|||||||
std::string defaultGroup;
|
std::string defaultGroup;
|
||||||
Responses responses;
|
Responses responses;
|
||||||
std::mt19937 generator;
|
std::mt19937 generator;
|
||||||
|
Shared::VC dialogs;
|
||||||
};
|
};
|
||||||
|
@ -26,6 +26,7 @@ modules:
|
|||||||
|
|
||||||
replies:
|
replies:
|
||||||
success: []
|
success: []
|
||||||
|
grab: []
|
||||||
forbidden: [Forbidden]
|
forbidden: [Forbidden]
|
||||||
error: [Command error]
|
error: [Command error]
|
||||||
unhandled: [No such command]
|
unhandled: [No such command]
|
||||||
|
@ -47,3 +47,7 @@ std::string_view Module::Module::pop(std::string_view& input, char delimiter) {
|
|||||||
|
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Module::Module::cancelDialog(const std::shared_ptr<::Actor>& actor) {
|
||||||
|
warn("Dialog with " + actor->jid + " was requested to be canceled, but this module doesn't handle canceling dialogs");
|
||||||
|
}
|
||||||
|
@ -32,6 +32,7 @@ public:
|
|||||||
static std::string_view pop(std::string_view& input, char delimiter = ' ');
|
static std::string_view pop(std::string_view& input, char delimiter = ' ');
|
||||||
|
|
||||||
virtual Shared::Result message(const std::shared_ptr<::Actor>& actor, std::string_view view) = 0;
|
virtual Shared::Result message(const std::shared_ptr<::Actor>& actor, std::string_view view) = 0;
|
||||||
|
virtual void cancelDialog(const std::shared_ptr<::Actor>& actor);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const std::string name;
|
const std::string name;
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace Shared {
|
namespace Shared {
|
||||||
enum Result {
|
enum Result {
|
||||||
success,
|
success,
|
||||||
|
grab,
|
||||||
forbidden,
|
forbidden,
|
||||||
unhandled,
|
unhandled,
|
||||||
error
|
error
|
||||||
@ -19,11 +20,12 @@ namespace Shared {
|
|||||||
|
|
||||||
typedef std::map<Result, Strings> Responses;
|
typedef std::map<Result, Strings> Responses;
|
||||||
|
|
||||||
constexpr std::array<std::pair<Result, std::string_view>, 4> results = {{
|
constexpr std::array<std::pair<Result, std::string_view>, 5> results = {{
|
||||||
{ success, "success" },
|
{ success, "success" },
|
||||||
|
{ grab, "grab" },
|
||||||
{ forbidden, "forbidden" },
|
{ forbidden, "forbidden" },
|
||||||
{ unhandled, "unhandled" },
|
{ unhandled, "unhandled" },
|
||||||
{ error, "error" },
|
{ error, "error" }
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user