mirror of
https://github.com/MeshEnvy/lobbs.git
synced 2026-11-07 13:32:23 +01:00
feat(meshtastic): extract LoBBS plugin library
This commit is contained in:
1 parent
1f7ded8f3f
commit
5bffe8d259
124 files changed
+10907
-39
No files matched your search
@@ -0,0 +1,9 @@
|
||||
#if !MESHTASTIC_EXCLUDE_LOBBS
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
#include "LoBBSStackGuard.h"
|
||||
|
||||
ConfigApp::ConfigApp(LoDb &lodb) : dal_(lodb) {}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#if !MESHTASTIC_EXCLUDE_LOBBS
|
||||
|
||||
#include "ConfigDal.h"
|
||||
#include <lodb/LoDB.h>
|
||||
|
||||
class ConfigApp
|
||||
{
|
||||
public:
|
||||
explicit ConfigApp(LoDb &lodb);
|
||||
ConfigDal &dal() { return dal_; }
|
||||
|
||||
private:
|
||||
ConfigDal dal_;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,200 @@
|
||||
#if !MESHTASTIC_EXCLUDE_LOBBS
|
||||
|
||||
#include "ConfigCommands.h"
|
||||
#include "../../LoBBSCommandRegistry.h"
|
||||
#include "../../LoBBSHooks.h"
|
||||
#include "../../LoBBSModule.h"
|
||||
#include "../../LoBBSReplyCache.h"
|
||||
#include "../../LoBBSResponse.h"
|
||||
#include "../AppUtil.h"
|
||||
#include "ConfigCommon.h"
|
||||
#include "ConfigDal.h"
|
||||
#include "ConfigRecords.h"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <strings.h>
|
||||
|
||||
#include "LoBBSStackGuard.h"
|
||||
|
||||
static uint32_t gLobbsPagerTtlSec = LOBBS_REPLY_CACHE_TTL_SEC;
|
||||
|
||||
uint32_t lobbsReplyCacheTtlSec()
|
||||
{
|
||||
return gLobbsPagerTtlSec;
|
||||
}
|
||||
|
||||
static void filterConfigKeysPager(LoBBSCommandCtx *ctx, std::vector<LoScalar> &keys, const LoScalar &args)
|
||||
{
|
||||
(void)ctx;
|
||||
(void)args;
|
||||
lobbsConfigPushKey(keys, "pager.ttl", LOBBS_REPLY_CACHE_TTL_SEC, 60, 3600, "Seconds /pN cache lasts");
|
||||
}
|
||||
|
||||
static void filterConfigValidateRange(LoBBSCommandCtx *ctx, LoScalar &value, const LoScalar &args)
|
||||
{
|
||||
(void)ctx;
|
||||
std::string existing;
|
||||
if (value.getString(LODB_F_ERROR, existing) && !existing.empty())
|
||||
return;
|
||||
uint32_t proposed = 0;
|
||||
uint32_t minV = 0;
|
||||
uint32_t maxV = 0;
|
||||
if (!value.getUint32(ConfigValidateField::FIELD_VALUE, proposed))
|
||||
return;
|
||||
if (!args.getUint32(ConfigKeyField::FIELD_MIN, minV) || !args.getUint32(ConfigKeyField::FIELD_MAX, maxV))
|
||||
return;
|
||||
if (proposed < minV || proposed > maxV) {
|
||||
char buf[48];
|
||||
snprintf(buf, sizeof(buf), "Must be %u-%u.", (unsigned)minV, (unsigned)maxV);
|
||||
value.setString(LODB_F_ERROR, buf);
|
||||
}
|
||||
}
|
||||
|
||||
static void actionConfigChangedPager(LoBBSCommandCtx *ctx, const LoScalar &args)
|
||||
{
|
||||
(void)ctx;
|
||||
std::string key;
|
||||
if (!args.getString(LODB_F_TITLE, key) || key != "pager.ttl")
|
||||
return;
|
||||
uint32_t ttl = LOBBS_REPLY_CACHE_TTL_SEC;
|
||||
args.getUint32(ConfigValidateField::FIELD_VALUE, ttl);
|
||||
gLobbsPagerTtlSec = ttl;
|
||||
}
|
||||
|
||||
static void handleConfig(LoBBSCommandCtx &ctx)
|
||||
{
|
||||
if (!lobbsCommandRequireSysop(ctx))
|
||||
return;
|
||||
ConfigDal &cfg = ctx.mod->config().dal();
|
||||
const char *key = lobbsArgShift(ctx);
|
||||
if (!key) {
|
||||
LoBBSResponse resp;
|
||||
lobbsRecordPush(resp.records, "Use /help config key for details");
|
||||
for (const LoScalar &def : cfg.keyDefs(ctx)) {
|
||||
std::string name;
|
||||
if (!def.getString(LODB_F_TITLE, name))
|
||||
continue;
|
||||
uint32_t minV = 0;
|
||||
uint32_t maxV = 0;
|
||||
def.getUint32(ConfigKeyField::FIELD_MIN, minV);
|
||||
def.getUint32(ConfigKeyField::FIELD_MAX, maxV);
|
||||
uint32_t val = cfg.effectiveValue(ctx, name.c_str());
|
||||
char line[96];
|
||||
snprintf(line, sizeof(line), "%s %u (%u-%u)", name.c_str(), (unsigned)val, (unsigned)minV, (unsigned)maxV);
|
||||
lobbsRecordPush(resp.records, line);
|
||||
}
|
||||
lobbsCommandReplyResponse(ctx, resp);
|
||||
return;
|
||||
}
|
||||
|
||||
const char *next = lobbsArgPeek(ctx);
|
||||
if (!next) {
|
||||
cfg.ensureRegistry(ctx);
|
||||
const LoScalar *def = cfg.findKeyDef(key);
|
||||
if (!def) {
|
||||
lobbsCommandReplyError(ctx, "Unknown setting.");
|
||||
return;
|
||||
}
|
||||
uint32_t val = cfg.effectiveValue(ctx, key);
|
||||
uint32_t defV = 0;
|
||||
uint32_t minV = 0;
|
||||
uint32_t maxV = 0;
|
||||
std::string help;
|
||||
def->getUint32(ConfigKeyField::FIELD_DEFAULT, defV);
|
||||
def->getUint32(ConfigKeyField::FIELD_MIN, minV);
|
||||
def->getUint32(ConfigKeyField::FIELD_MAX, maxV);
|
||||
def->getString(LODB_F_DESCRIPTION, help);
|
||||
char line[128];
|
||||
snprintf(line, sizeof(line), "%s = %u (default %u, %u-%u). %s", key, (unsigned)val, (unsigned)defV, (unsigned)minV,
|
||||
(unsigned)maxV, help.c_str());
|
||||
lobbsCommandReply(ctx, line);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcasecmp(next, "reset") == 0) {
|
||||
lobbsArgShift(ctx);
|
||||
if (lobbsArgHasMore(ctx)) {
|
||||
lobbsCommandReplyError(ctx, "Usage: /config key reset");
|
||||
return;
|
||||
}
|
||||
if (const char *err = cfg.resetKey(ctx, key)) {
|
||||
lobbsCommandReplyError(ctx, err);
|
||||
return;
|
||||
}
|
||||
char reply[64];
|
||||
snprintf(reply, sizeof(reply), "%s reset.", key);
|
||||
lobbsCommandReply(ctx, reply);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t value = 0;
|
||||
if (!lobbsArgShiftUint(ctx, value) || lobbsArgHasMore(ctx)) {
|
||||
lobbsCommandReplyError(ctx, "Usage: /config key value");
|
||||
return;
|
||||
}
|
||||
if (const char *err = cfg.validateAndSet(ctx, key, value)) {
|
||||
lobbsCommandReplyError(ctx, err);
|
||||
return;
|
||||
}
|
||||
char reply[64];
|
||||
snprintf(reply, sizeof(reply), "%s = %u.", key, (unsigned)value);
|
||||
lobbsCommandReply(ctx, reply);
|
||||
}
|
||||
|
||||
static void slashConfig(LoBBSCommandCtx *ctx, const LoScalar &args)
|
||||
{
|
||||
if (!ctx || !lobbsSlashVerbIs(args, "config"))
|
||||
return;
|
||||
handleConfig(*ctx);
|
||||
}
|
||||
|
||||
static const LoBBSVerb configHelpVerbs[] = {
|
||||
{"config", nullptr, LOBBS_V_SYSOP, "config — sysop: list/get/set/reset settings (/help config key explains one)"},
|
||||
};
|
||||
|
||||
static void filterConfigHelpTopics(LoBBSCommandCtx *ctx, std::vector<LoScalar> &topics, const LoScalar &args)
|
||||
{
|
||||
(void)ctx;
|
||||
(void)args;
|
||||
lobbsRecordPush(topics, "config", "sysop settings");
|
||||
}
|
||||
|
||||
static void filterConfigHelpForTopic(LoBBSCommandCtx *ctx, LoScalar &value, const LoScalar &args)
|
||||
{
|
||||
lobbsHelpForTable(ctx, value, args, "config", configHelpVerbs, sizeof(configHelpVerbs) / sizeof(configHelpVerbs[0]));
|
||||
if (!ctx || !ctx->mod || value.has(LODB_F_DESCRIPTION) || !lobbsCtxIsSysop(*ctx))
|
||||
return;
|
||||
std::string query;
|
||||
if (!args.getString(LODB_F_TITLE, query) || strncasecmp(query.c_str(), "config ", 7) != 0)
|
||||
return;
|
||||
const char *key = query.c_str() + 7;
|
||||
ConfigDal &cfg = ctx->mod->config().dal();
|
||||
cfg.ensureRegistry(*ctx);
|
||||
const LoScalar *def = cfg.findKeyDef(key);
|
||||
if (!def)
|
||||
return;
|
||||
std::string help;
|
||||
uint32_t defV = 0;
|
||||
uint32_t minV = 0;
|
||||
uint32_t maxV = 0;
|
||||
def->getString(LODB_F_DESCRIPTION, help);
|
||||
def->getUint32(ConfigKeyField::FIELD_DEFAULT, defV);
|
||||
def->getUint32(ConfigKeyField::FIELD_MIN, minV);
|
||||
def->getUint32(ConfigKeyField::FIELD_MAX, maxV);
|
||||
char buf[128];
|
||||
snprintf(buf, sizeof(buf), "%s: %s. Default %u, range %u-%u.", key, help.c_str(), (unsigned)defV, (unsigned)minV,
|
||||
(unsigned)maxV);
|
||||
value.setString(LODB_F_DESCRIPTION, buf);
|
||||
}
|
||||
|
||||
void lobbsConfigRegisterCommands()
|
||||
{
|
||||
lobbsAddFilter("config_keys", filterConfigKeysPager, LOBBS_HOOK_PRIORITY_HELP);
|
||||
lobbsAddFilter("config_validate", filterConfigValidateRange, LOBBS_HOOK_PRIORITY_HELP);
|
||||
lobbsAddAction("config_changed", actionConfigChangedPager, LOBBS_HOOK_PRIORITY_HELP);
|
||||
lobbsAddAction("slash_cmd", slashConfig, LOBBS_HOOK_PRIORITY_FEATURE);
|
||||
lobbsAddFilter("help_topics", filterConfigHelpTopics, LOBBS_HOOK_PRIORITY_FEATURE);
|
||||
lobbsAddFilter("help_for_topic", filterConfigHelpForTopic, LOBBS_HOOK_PRIORITY_FEATURE);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#if !MESHTASTIC_EXCLUDE_LOBBS
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
void lobbsConfigRegisterCommands();
|
||||
uint32_t lobbsReplyCacheTtlSec();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,31 @@
|
||||
#if !MESHTASTIC_EXCLUDE_LOBBS
|
||||
|
||||
#include "ConfigCommon.h"
|
||||
#include "../../LoBBSModule.h"
|
||||
#include "ConfigDal.h"
|
||||
#include <cstring>
|
||||
|
||||
#include "LoBBSStackGuard.h"
|
||||
|
||||
void lobbsConfigPushKey(std::vector<LoScalar> &keys, const char *key, uint32_t def, uint32_t min, uint32_t max, const char *help)
|
||||
{
|
||||
if (!key)
|
||||
return;
|
||||
LoScalar rec;
|
||||
rec.setString(LODB_F_TITLE, key);
|
||||
if (help)
|
||||
rec.setString(LODB_F_DESCRIPTION, help);
|
||||
rec.setUint32(ConfigKeyField::FIELD_DEFAULT, def);
|
||||
rec.setUint32(ConfigKeyField::FIELD_MIN, min);
|
||||
rec.setUint32(ConfigKeyField::FIELD_MAX, max);
|
||||
keys.push_back(rec);
|
||||
}
|
||||
|
||||
uint32_t lobbsConfigGet(LoBBSCommandCtx &ctx, const char *key)
|
||||
{
|
||||
if (!ctx.mod || !key)
|
||||
return 0;
|
||||
return ctx.mod->config().dal().effectiveValue(ctx, key);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#if !MESHTASTIC_EXCLUDE_LOBBS
|
||||
|
||||
#include "../../LoBBSCommandCtx.h"
|
||||
#include "ConfigRecords.h"
|
||||
#include <lodb/LoDB.h>
|
||||
#include <loscalar/LoScalar.h>
|
||||
#include <vector>
|
||||
|
||||
void lobbsConfigPushKey(std::vector<LoScalar> &keys, const char *key, uint32_t def, uint32_t min, uint32_t max, const char *help);
|
||||
|
||||
uint32_t lobbsConfigGet(LoBBSCommandCtx &ctx, const char *key);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,158 @@
|
||||
#if !MESHTASTIC_EXCLUDE_LOBBS
|
||||
|
||||
#include "ConfigDal.h"
|
||||
#include "../../LoBBSCommandCtx.h"
|
||||
#include "../../LoBBSHooks.h"
|
||||
#include "../../LoBBSModule.h"
|
||||
#include "../AppUtil.h"
|
||||
#include "ConfigRecords.h"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include "LoBBSStackGuard.h"
|
||||
|
||||
ConfigDal::ConfigDal(LoDb &lodb) : lodb_(lodb)
|
||||
{
|
||||
lodb_.registerTable("config");
|
||||
}
|
||||
|
||||
lodb_uuid_t ConfigDal::rowUuid(const char *key) const
|
||||
{
|
||||
return lodb_new_uuid(key, 0);
|
||||
}
|
||||
|
||||
void ConfigDal::resetCache()
|
||||
{
|
||||
overrides_.clear();
|
||||
registry_.clear();
|
||||
registryBuilt_ = false;
|
||||
overridesLoaded_ = false;
|
||||
}
|
||||
|
||||
void ConfigDal::reloadOverrides()
|
||||
{
|
||||
overrides_.clear();
|
||||
overridesLoaded_ = true;
|
||||
auto rows = lodb_.select(
|
||||
"config", [](const LoScalar &) { return true; }, LoDbComparator());
|
||||
for (const LoScalar &rec : rows) {
|
||||
std::string key;
|
||||
if (!rec.getString(LODB_F_TITLE, key) || key.empty())
|
||||
continue;
|
||||
uint32_t val = 0;
|
||||
if (rec.getUint32(ConfigStoreField::FIELD_VALUE, val))
|
||||
overrides_[key] = val;
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigDal::ensureRegistry(LoBBSCommandCtx &ctx)
|
||||
{
|
||||
if (registryBuilt_)
|
||||
return;
|
||||
registryBuilt_ = true;
|
||||
registry_.clear();
|
||||
lobbsApplyFilter("config_keys", ctx, registry_, LoScalar());
|
||||
}
|
||||
|
||||
const std::vector<LoScalar> &ConfigDal::keyDefs(LoBBSCommandCtx &ctx)
|
||||
{
|
||||
ensureRegistry(ctx);
|
||||
return registry_;
|
||||
}
|
||||
|
||||
const LoScalar *ConfigDal::findKeyDef(const char *key)
|
||||
{
|
||||
if (!key)
|
||||
return nullptr;
|
||||
for (const LoScalar &rec : registry_) {
|
||||
std::string title;
|
||||
if (rec.getString(LODB_F_TITLE, title) && strcasecmp(title.c_str(), key) == 0)
|
||||
return &rec;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t ConfigDal::effectiveValue(LoBBSCommandCtx &ctx, const char *key)
|
||||
{
|
||||
ensureRegistry(ctx);
|
||||
const LoScalar *def = findKeyDef(key);
|
||||
if (!def)
|
||||
return 0;
|
||||
uint32_t fallback = 0;
|
||||
def->getUint32(ConfigKeyField::FIELD_DEFAULT, fallback);
|
||||
if (!overridesLoaded_)
|
||||
return fallback;
|
||||
auto it = overrides_.find(key);
|
||||
if (it != overrides_.end())
|
||||
return it->second;
|
||||
return fallback;
|
||||
}
|
||||
|
||||
void ConfigDal::fireConfigChanged(LoBBSCommandCtx &ctx, const char *key, uint32_t value)
|
||||
{
|
||||
LoScalar args;
|
||||
args.setString(LODB_F_TITLE, key);
|
||||
args.setUint32(ConfigValidateField::FIELD_VALUE, value);
|
||||
lobbsDoAction("config_changed", ctx, args);
|
||||
}
|
||||
|
||||
const char *ConfigDal::validateAndSet(LoBBSCommandCtx &ctx, const char *key, uint32_t value)
|
||||
{
|
||||
ensureRegistry(ctx);
|
||||
const LoScalar *def = findKeyDef(key);
|
||||
if (!def)
|
||||
return "Unknown setting.";
|
||||
|
||||
LoScalar probe;
|
||||
probe.setString(LODB_F_TITLE, key);
|
||||
probe.setUint32(ConfigValidateField::FIELD_VALUE, value);
|
||||
lobbsApplyFilter("config_validate", ctx, probe, *def);
|
||||
|
||||
std::string err;
|
||||
if (probe.getString(LODB_F_ERROR, err) && !err.empty()) {
|
||||
strncpy(lastValidateError_, err.c_str(), sizeof(lastValidateError_) - 1);
|
||||
lastValidateError_[sizeof(lastValidateError_) - 1] = '\0';
|
||||
return lastValidateError_;
|
||||
}
|
||||
|
||||
LoScalar row;
|
||||
row.setString(LODB_F_TITLE, key);
|
||||
row.setUint32(ConfigStoreField::FIELD_VALUE, value);
|
||||
LoDbError dbErr = lodb_.upsert("config", rowUuid(key), row);
|
||||
if (dbErr != LODB_OK)
|
||||
return lobbsDbErrorText(dbErr);
|
||||
overrides_[key] = value;
|
||||
fireConfigChanged(ctx, key, value);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char *ConfigDal::resetKey(LoBBSCommandCtx &ctx, const char *key)
|
||||
{
|
||||
ensureRegistry(ctx);
|
||||
const LoScalar *def = findKeyDef(key);
|
||||
if (!def)
|
||||
return "Unknown setting.";
|
||||
uint32_t fallback = 0;
|
||||
def->getUint32(ConfigKeyField::FIELD_DEFAULT, fallback);
|
||||
lodb_.deleteRecord("config", rowUuid(key));
|
||||
overrides_.erase(key);
|
||||
fireConfigChanged(ctx, key, fallback);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ConfigDal::notifyDatabaseOpened(LoBBSModule &mod)
|
||||
{
|
||||
resetCache();
|
||||
reloadOverrides();
|
||||
LoBBSCommandCtx ctx;
|
||||
ctx.mod = &mod;
|
||||
ensureRegistry(ctx);
|
||||
for (const LoScalar &rec : registry_) {
|
||||
std::string key;
|
||||
if (!rec.getString(LODB_F_TITLE, key) || key.empty())
|
||||
continue;
|
||||
fireConfigChanged(ctx, key.c_str(), effectiveValue(ctx, key.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#if !MESHTASTIC_EXCLUDE_LOBBS
|
||||
|
||||
#include <lodb/LoDB.h>
|
||||
#include <loscalar/LoScalar.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class LoBBSModule;
|
||||
struct LoBBSCommandCtx;
|
||||
|
||||
class ConfigDal
|
||||
{
|
||||
public:
|
||||
explicit ConfigDal(LoDb &lodb);
|
||||
|
||||
void reloadOverrides();
|
||||
void resetCache();
|
||||
void ensureRegistry(LoBBSCommandCtx &ctx);
|
||||
const LoScalar *findKeyDef(const char *key);
|
||||
const std::vector<LoScalar> &keyDefs(LoBBSCommandCtx &ctx);
|
||||
uint32_t effectiveValue(LoBBSCommandCtx &ctx, const char *key);
|
||||
const char *validateAndSet(LoBBSCommandCtx &ctx, const char *key, uint32_t value);
|
||||
const char *resetKey(LoBBSCommandCtx &ctx, const char *key);
|
||||
void notifyDatabaseOpened(LoBBSModule &mod);
|
||||
|
||||
private:
|
||||
lodb_uuid_t rowUuid(const char *key) const;
|
||||
void fireConfigChanged(LoBBSCommandCtx &ctx, const char *key, uint32_t value);
|
||||
|
||||
LoDb &lodb_;
|
||||
std::map<std::string, uint32_t> overrides_;
|
||||
std::vector<LoScalar> registry_;
|
||||
bool registryBuilt_ = false;
|
||||
bool overridesLoaded_ = false;
|
||||
char lastValidateError_[128] = {0};
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#if !MESHTASTIC_EXCLUDE_LOBBS
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
/** Stored override row in table `config`. */
|
||||
namespace ConfigStoreField
|
||||
{
|
||||
inline constexpr uint32_t FIELD_VALUE = 0;
|
||||
} // namespace ConfigStoreField
|
||||
|
||||
/** Key definition rows from `config_keys`. */
|
||||
namespace ConfigKeyField
|
||||
{
|
||||
inline constexpr uint32_t FIELD_DEFAULT = 0;
|
||||
inline constexpr uint32_t FIELD_MIN = 1;
|
||||
inline constexpr uint32_t FIELD_MAX = 2;
|
||||
} // namespace ConfigKeyField
|
||||
|
||||
/** Proposed value in `config_validate` value record. */
|
||||
namespace ConfigValidateField
|
||||
{
|
||||
inline constexpr uint32_t FIELD_VALUE = 0;
|
||||
} // namespace ConfigValidateField
|
||||
|
||||
#endif
|
||||
Reference in new issue
Block a user