add upstream source (adch++ 2.12.1 from sourceforge)
This commit is contained in:
5
src/plugins/Bloom/SConscript
Normal file
5
src/plugins/Bloom/SConscript
Normal file
@@ -0,0 +1,5 @@
|
||||
Import('dev source_path')
|
||||
|
||||
ret = dev.build('src/')
|
||||
|
||||
Return('ret')
|
||||
220
src/plugins/Bloom/src/BloomManager.cpp
Normal file
220
src/plugins/Bloom/src/BloomManager.cpp
Normal file
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2016 Jacek Sieka, arnetheduck on gmail point com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "stdinc.h"
|
||||
#include "BloomManager.h"
|
||||
|
||||
#include <adchpp/LogManager.h>
|
||||
#include <adchpp/Client.h>
|
||||
#include <adchpp/AdcCommand.h>
|
||||
#include <adchpp/Util.h>
|
||||
#include <adchpp/PluginManager.h>
|
||||
#include <adchpp/Core.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace std::placeholders;
|
||||
using namespace adchpp;
|
||||
|
||||
const string BloomManager::className = "BloomManager";
|
||||
|
||||
// TODO Make configurable
|
||||
const size_t h = 24;
|
||||
|
||||
struct PendingItem {
|
||||
PendingItem(size_t m_, size_t k_) : m(m_), k(k_) { buffer.reserve(m/8); }
|
||||
ByteVector buffer;
|
||||
size_t m;
|
||||
size_t k;
|
||||
};
|
||||
|
||||
BloomManager::BloomManager(Core &core) : searches(0), tthSearches(0), stopped(0), core(core) {
|
||||
LOG(className, "Starting");
|
||||
}
|
||||
|
||||
void BloomManager::init() {
|
||||
auto &cm = core.getClientManager();
|
||||
receiveConn = manage(cm.signalReceive().connect(std::bind(&BloomManager::onReceive, this, _1, _2, _3)));
|
||||
sendConn = manage(cm.signalSend().connect(std::bind(&BloomManager::onSend, this, _1, _2, _3)));
|
||||
|
||||
auto &pm = core.getPluginManager();
|
||||
bloomHandle = pm.registerPluginData(&PluginData::simpleDataDeleter<HashBloom>);
|
||||
pendingHandle = pm.registerPluginData(&PluginData::simpleDataDeleter<PendingItem>);
|
||||
|
||||
statsConn = manage(pm.onCommand("stats", std::bind(&BloomManager::onStats, this, _1)));
|
||||
}
|
||||
|
||||
bool BloomManager::hasBloom(Entity& c) const {
|
||||
return c.getPluginData(bloomHandle);
|
||||
}
|
||||
|
||||
bool BloomManager::hasTTH(Entity& c,const TTHValue& tth) const {
|
||||
HashBloom* bloom = reinterpret_cast<HashBloom*>(c.getPluginData(bloomHandle));
|
||||
return !bloom || bloom->match(tth);
|
||||
}
|
||||
|
||||
int64_t BloomManager::getSearches() const {
|
||||
return searches;
|
||||
}
|
||||
|
||||
int64_t BloomManager::getTTHSearches() const {
|
||||
return tthSearches;
|
||||
}
|
||||
|
||||
int64_t BloomManager::getStoppedSearches() const {
|
||||
return stopped;
|
||||
}
|
||||
|
||||
BloomManager::~BloomManager() {
|
||||
LOG(className, "Shutting down");
|
||||
}
|
||||
|
||||
static const uint32_t FEATURE = AdcCommand::toFourCC("BLO0");
|
||||
|
||||
void BloomManager::onReceive(Entity& e, AdcCommand& cmd, bool& ok) {
|
||||
string tmp;
|
||||
|
||||
Client* cc = dynamic_cast<Client*>(&e);
|
||||
if(!cc) {
|
||||
return;
|
||||
}
|
||||
|
||||
Client& c = *cc;
|
||||
if(cmd.getCommand() == AdcCommand::CMD_INF && c.hasSupport(FEATURE)) {
|
||||
if(cmd.getParam("SF", 0, tmp)) {
|
||||
if(e.getPluginData(pendingHandle)) {
|
||||
// Already getting a blom - we'll end up with an old bloom but there's no trivial
|
||||
// way to avoid it...
|
||||
// TODO Queue the blom get?
|
||||
return;
|
||||
}
|
||||
|
||||
size_t n = adchpp::Util::toInt(tmp);
|
||||
if(n == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.clearPluginData(bloomHandle);
|
||||
|
||||
size_t k = HashBloom::get_k(n, h);
|
||||
size_t m = HashBloom::get_m(n, k);
|
||||
|
||||
e.setPluginData(pendingHandle, new PendingItem(m, k));
|
||||
|
||||
AdcCommand get(AdcCommand::CMD_GET);
|
||||
get.addParam("blom");
|
||||
get.addParam("/");
|
||||
get.addParam("0");
|
||||
get.addParam(Util::toString(m/8));
|
||||
get.addParam("BK", Util::toString(k));
|
||||
get.addParam("BH", Util::toString(h));
|
||||
c.send(get);
|
||||
}
|
||||
} else if(cmd.getCommand() == AdcCommand::CMD_SND) {
|
||||
if(cmd.getParameters().size() < 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(cmd.getParam(0) != "blom") {
|
||||
return;
|
||||
}
|
||||
|
||||
PendingItem* pending = reinterpret_cast<PendingItem*>(e.getPluginData(pendingHandle));
|
||||
if(!pending) {
|
||||
c.send(AdcCommand(AdcCommand::SEV_FATAL, AdcCommand::ERROR_BAD_STATE, "Unexpected bloom filter update"));
|
||||
c.disconnect(Util::REASON_BAD_STATE);
|
||||
ok = false;
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t bytes = Util::toInt(cmd.getParam(3));
|
||||
|
||||
if(bytes != static_cast<int64_t>(pending->m / 8)) {
|
||||
dcdebug("Disconnecting for invalid number of bytes: %d, %d\n", (int)bytes, (int)pending->m / 8);
|
||||
c.send(AdcCommand(AdcCommand::SEV_FATAL, AdcCommand::ERROR_PROTOCOL_GENERIC, "Invalid number of bytes"));
|
||||
c.disconnect(Util::REASON_PLUGIN);
|
||||
ok = false;
|
||||
e.clearPluginData(pendingHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
c.setDataMode(bind(&BloomManager::onData, this, _1, _2, _3), bytes);
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
void BloomManager::onSend(Entity& c, const AdcCommand& cmd, bool& ok) {
|
||||
if(!ok)
|
||||
return;
|
||||
|
||||
if(cmd.getCommand() == AdcCommand::CMD_SCH) {
|
||||
searches++;
|
||||
string tmp;
|
||||
if(cmd.getParam("TR", 0, tmp)) {
|
||||
tthSearches++;
|
||||
if(!hasTTH(c,TTHValue(tmp)) || !adchpp::Util::toInt(c.getField("SF"))) {
|
||||
ok = false;
|
||||
stopped++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<size_t, size_t> BloomManager::getBytes() const {
|
||||
std::pair<size_t, size_t> bytes;
|
||||
auto &cm = core.getClientManager();
|
||||
for(auto i = cm.getEntities().begin(), iend = cm.getEntities().end(); i != iend; ++i) {
|
||||
auto bloom = reinterpret_cast<HashBloom*>(i->second->getPluginData(bloomHandle));
|
||||
if(bloom) {
|
||||
bytes.first++;
|
||||
bytes.second += bloom->size();
|
||||
}
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
void BloomManager::onData(Entity& c, const uint8_t* data, size_t len) {
|
||||
PendingItem* pending = reinterpret_cast<PendingItem*>(c.getPluginData(pendingHandle));
|
||||
if(!pending) {
|
||||
// Shouldn't happen
|
||||
return;
|
||||
}
|
||||
|
||||
pending->buffer.insert(pending->buffer.end(), data, data + len);
|
||||
|
||||
if(pending->buffer.size() == pending->m / 8) {
|
||||
HashBloom* bloom = new HashBloom();
|
||||
c.setPluginData(bloomHandle, bloom);
|
||||
bloom->reset(pending->buffer, pending->k, h);
|
||||
c.clearPluginData(pendingHandle);
|
||||
/* Mark the new filter as received */
|
||||
signalBloomReady_(c);
|
||||
}
|
||||
}
|
||||
|
||||
void BloomManager::onStats(Entity& c) {
|
||||
string stats = "\nBloom filter statistics:";
|
||||
stats += "\nTotal outgoing searches: " + Util::toString(searches);
|
||||
stats += "\nOutgoing TTH searches: " + Util::toString(tthSearches) + " (" + Util::toString(tthSearches * 100. / searches) + "% of total)";
|
||||
stats += "\nStopped outgoing searches: " + Util::toString(stopped) + " (" + Util::toString(stopped * 100. / searches) + "% of total, " + Util::toString(stopped * 100. / tthSearches) + "% of TTH searches";
|
||||
auto bytes = getBytes();
|
||||
size_t clients = core.getClientManager().getEntities().size();
|
||||
stats += "\nClient support: " + Util::toString(bytes.first) + "/" + Util::toString(clients) + " (" + Util::toString(bytes.first * 100. / clients) + "%)";
|
||||
stats += "\nApproximate memory usage: " + Util::formatBytes(bytes.second) + ", " + Util::formatBytes(static_cast<double>(bytes.second) / clients) + "/client";
|
||||
c.send(AdcCommand(AdcCommand::CMD_MSG).addParam(stats));
|
||||
}
|
||||
84
src/plugins/Bloom/src/BloomManager.h
Normal file
84
src/plugins/Bloom/src/BloomManager.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2016 Jacek Sieka, arnetheduck on gmail point com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef BLOOM_MANAGER_H
|
||||
#define BLOOM_MANAGER_H
|
||||
|
||||
#include <tuple>
|
||||
#include <adchpp/forward.h>
|
||||
#include <adchpp/Exception.h>
|
||||
#include <adchpp/ClientManager.h>
|
||||
#include <adchpp/Plugin.h>
|
||||
#include <adchpp/Signal.h>
|
||||
|
||||
#include "HashBloom.h"
|
||||
|
||||
STANDARD_EXCEPTION(BloomException);
|
||||
|
||||
class ADCHPP_VISIBLE BloomManager : public Plugin {
|
||||
public:
|
||||
BloomManager(Core &core);
|
||||
virtual ~BloomManager();
|
||||
|
||||
virtual int getVersion() { return 2; }
|
||||
|
||||
void init();
|
||||
|
||||
/*Check if the entity has a bloom filter*/
|
||||
PLUGIN_EXPORT bool hasBloom(Entity& c) const;
|
||||
|
||||
/*Check if the entity may have the desired TTH according to the filter*/
|
||||
PLUGIN_EXPORT bool hasTTH(Entity& c, const TTHValue& tth) const;
|
||||
|
||||
/*Get the number of searches sent (to clients)*/
|
||||
PLUGIN_EXPORT int64_t getSearches() const;
|
||||
/*Get the number of searches by TTH sent (to clients)*/
|
||||
PLUGIN_EXPORT int64_t getTTHSearches() const;
|
||||
/*Get the number of sent searches stopped*/
|
||||
PLUGIN_EXPORT int64_t getStoppedSearches() const;
|
||||
|
||||
static const std::string className;
|
||||
|
||||
/*This signal is sent when a BloomFilter has been received*/
|
||||
typedef SignalTraits<void (Entity&)> SignalBloomReady;
|
||||
PLUGIN_EXPORT SignalBloomReady::Signal& signalBloomReady() { return signalBloomReady_; }
|
||||
|
||||
private:
|
||||
PluginDataHandle bloomHandle;
|
||||
PluginDataHandle pendingHandle;
|
||||
|
||||
int64_t searches;
|
||||
int64_t tthSearches;
|
||||
int64_t stopped;
|
||||
|
||||
ClientManager::SignalReceive::ManagedConnection receiveConn;
|
||||
ClientManager::SignalSend::ManagedConnection sendConn;
|
||||
ClientManager::SignalReceive::ManagedConnection statsConn;
|
||||
|
||||
std::pair<size_t, size_t> getBytes() const;
|
||||
void onReceive(Entity& c, AdcCommand& cmd, bool&);
|
||||
void onSend(Entity& c, const AdcCommand& cmd, bool&);
|
||||
void onData(Entity& c, const uint8_t* data, size_t len);
|
||||
void onStats(Entity& c);
|
||||
|
||||
Core &core;
|
||||
|
||||
SignalBloomReady::Signal signalBloomReady_;
|
||||
};
|
||||
|
||||
#endif //ACCESSMANAGER_H
|
||||
48
src/plugins/Bloom/src/BloomPlugin.cpp
Normal file
48
src/plugins/Bloom/src/BloomPlugin.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2016 Jacek Sieka, arnetheduck on gmail point com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "stdinc.h"
|
||||
#include "BloomManager.h"
|
||||
|
||||
#include <adchpp/PluginManager.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
BOOL APIENTRY DllMain(HANDLE /*hModule */, DWORD /* reason*/, LPVOID /*lpReserved*/) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
|
||||
int PLUGIN_API pluginGetVersion() { return PLUGINVERSION; }
|
||||
|
||||
int PLUGIN_API pluginLoad(PluginManager *pm) {
|
||||
auto bm = make_shared<BloomManager>(pm->getCore());
|
||||
bm->init();
|
||||
pm->registerPlugin("BloomManager", bm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PLUGIN_API pluginUnload() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
95
src/plugins/Bloom/src/HashBloom.cpp
Normal file
95
src/plugins/Bloom/src/HashBloom.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2016 Jacek Sieka, arnetheduck on gmail point com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "stdinc.h"
|
||||
|
||||
#include "HashBloom.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
size_t HashBloom::get_k(size_t n, size_t h) {
|
||||
for(size_t k = TTHValue::BITS/h; k > 1; --k) {
|
||||
uint64_t m = get_m(n, k);
|
||||
if(m >> 24 == 0) {
|
||||
return k;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint64_t HashBloom::get_m(size_t n, size_t k) {
|
||||
uint64_t m = (static_cast<uint64_t>(ceil(static_cast<double>(n) * k / log(2.))));
|
||||
// 64-bit boundary as per spec
|
||||
return ((m + 63ULL) / 64ULL) * 64ULL;
|
||||
}
|
||||
|
||||
void HashBloom::add(const TTHValue& tth) {
|
||||
for(size_t i = 0; i < k; ++i) {
|
||||
bloom[pos(tth, i)] = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool HashBloom::match(const TTHValue& tth) const {
|
||||
if(bloom.empty()) {
|
||||
return false;
|
||||
}
|
||||
for(size_t i = 0; i < k; ++i) {
|
||||
if(!bloom[pos(tth, i)]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void HashBloom::push_back(bool v) {
|
||||
bloom.push_back(v);
|
||||
}
|
||||
|
||||
void HashBloom::reset(ByteVector& v, size_t k_, size_t h_) {
|
||||
k = k_;
|
||||
h = h_;
|
||||
|
||||
bloom.resize(v.size() * 8);
|
||||
for(size_t i = 0; i < v.size(); ++i) {
|
||||
for(size_t j = 0; j < 8; ++j) {
|
||||
bloom[i*8 + j] = (((v[i] >> j) & 1) != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t HashBloom::pos(const TTHValue& tth, size_t n) const {
|
||||
if((n+1)*h > TTHValue::BITS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t x = 0;
|
||||
|
||||
size_t start = n * h;
|
||||
|
||||
for(size_t i = 0; i < h; ++i) {
|
||||
size_t bit = start + i;
|
||||
size_t byte = bit / 8;
|
||||
size_t pos = bit % 8;
|
||||
|
||||
if(tth.data[byte] & (1 << pos)) {
|
||||
x |= (1LL << i);
|
||||
}
|
||||
}
|
||||
return x % bloom.size();
|
||||
}
|
||||
|
||||
58
src/plugins/Bloom/src/HashBloom.h
Normal file
58
src/plugins/Bloom/src/HashBloom.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2016 Jacek Sieka, arnetheduck on gmail point com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef HASHBLOOM_H_
|
||||
#define HASHBLOOM_H_
|
||||
|
||||
#include "HashValue.h"
|
||||
|
||||
/**
|
||||
* According to http://www.eecs.harvard.edu/~michaelm/NEWWORK/postscripts/BloomFilterSurvey.pdf
|
||||
* the optimal number of hashes k is (m/n)*ln(2), m = number of bits in the filter and n = number
|
||||
* of items added. The largest k that we can get from a single TTH value depends on the number of
|
||||
* bits we need to address the bloom structure, which in turn depends on m, so the optimal size
|
||||
* for our filter is m = n * k / ln(2) where n is the number of TTH values, or in our case, number of
|
||||
* files in share since each file is identified by one TTH value. We try that for each even dividend
|
||||
* of the key size (2, 3, 4, 6, 8, 12) and if m fits within the bits we're able to address (2^(keysize/k)),
|
||||
* we can use that value when requesting the bloom filter.
|
||||
*/
|
||||
class HashBloom {
|
||||
public:
|
||||
HashBloom() : k(0), h(0) { }
|
||||
|
||||
/** Return a suitable value for k based on n */
|
||||
static size_t get_k(size_t n, size_t h);
|
||||
/** Optimal number of bits to allocate for n elements when using k hashes */
|
||||
static uint64_t get_m(size_t n, size_t k);
|
||||
|
||||
void add(const TTHValue& tth);
|
||||
bool match(const TTHValue& tth) const;
|
||||
void reset(ByteVector& v, size_t k, size_t h);
|
||||
void push_back(bool v);
|
||||
|
||||
size_t size() const { return bloom.size(); }
|
||||
private:
|
||||
|
||||
size_t pos(const TTHValue& tth, size_t n) const;
|
||||
|
||||
std::vector<bool> bloom;
|
||||
size_t k;
|
||||
size_t h;
|
||||
};
|
||||
|
||||
#endif /*HASHBLOOM_H_*/
|
||||
54
src/plugins/Bloom/src/HashValue.h
Normal file
54
src/plugins/Bloom/src/HashValue.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2016 Jacek Sieka, arnetheduck on gmail point com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef BLOOM_HASH_VALUE_H
|
||||
#define BLOOM_HASH_VALUE_H
|
||||
|
||||
#include <adchpp/TigerHash.h>
|
||||
#include <adchpp/Encoder.h>
|
||||
|
||||
template<class Hasher>
|
||||
struct HashValue {
|
||||
static const size_t BITS = Hasher::BITS;
|
||||
static const size_t BYTES = Hasher::BYTES;
|
||||
|
||||
HashValue() { }
|
||||
explicit HashValue(uint8_t* aData) { memcpy(data, aData, BYTES); }
|
||||
explicit HashValue(const std::string& base32) { Encoder::fromBase32(base32.c_str(), data, BYTES); }
|
||||
HashValue(const HashValue& rhs) { memcpy(data, rhs.data, BYTES); }
|
||||
HashValue& operator=(const HashValue& rhs) { memcpy(data, rhs.data, BYTES); return *this; }
|
||||
bool operator!=(const HashValue& rhs) const { return !(*this == rhs); }
|
||||
bool operator==(const HashValue& rhs) const { return memcmp(data, rhs.data, BYTES) == 0; }
|
||||
bool operator<(const HashValue& rhs) const { return memcmp(data, rhs.data, BYTES) < 0; }
|
||||
|
||||
std::string toBase32() const { return Encoder::toBase32(data, BYTES); }
|
||||
std::string& toBase32(std::string& tmp) const { return Encoder::toBase32(data, BYTES, tmp); }
|
||||
|
||||
uint8_t data[BYTES];
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template<typename T>
|
||||
struct hash<HashValue<T> > {
|
||||
size_t operator()(const HashValue<T>& rhs) const { return *(size_t*)rhs.data; }
|
||||
};
|
||||
}
|
||||
|
||||
typedef HashValue<TigerHash> TTHValue;
|
||||
|
||||
#endif // !defined(HASH_VALUE_H)
|
||||
11
src/plugins/Bloom/src/SConscript
Normal file
11
src/plugins/Bloom/src/SConscript
Normal file
@@ -0,0 +1,11 @@
|
||||
Import('dev source_path')
|
||||
|
||||
env, target, sources = dev.prepare_build(source_path, 'Bloom', shared_precompiled_header = 'stdinc')
|
||||
env['SHLIBPREFIX'] = ''
|
||||
|
||||
env.Append(CPPPATH = ['#'])
|
||||
env.Append(LIBS = ['adchpp'])
|
||||
|
||||
ret = env.SharedLibrary(target, sources)
|
||||
|
||||
Return('ret')
|
||||
19
src/plugins/Bloom/src/stdinc.cpp
Normal file
19
src/plugins/Bloom/src/stdinc.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2016 Jacek Sieka, arnetheduck on gmail point com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "stdinc.h"
|
||||
33
src/plugins/Bloom/src/stdinc.h
Normal file
33
src/plugins/Bloom/src/stdinc.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2016 Jacek Sieka, arnetheduck on gmail point com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef BLOOM_STDINC_H
|
||||
#define BLOOM_STDINC_H
|
||||
|
||||
#include <adchpp/adchpp.h>
|
||||
#include <adchpp/common.h>
|
||||
|
||||
using namespace adchpp;
|
||||
|
||||
#ifdef _WIN32
|
||||
#define PLUGIN_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define PLUGIN_EXPORT __attribute__ ((visibility("default")))
|
||||
#endif
|
||||
|
||||
#endif
|
||||
5
src/plugins/Script/SConscript
Normal file
5
src/plugins/Script/SConscript
Normal file
@@ -0,0 +1,5 @@
|
||||
Import('dev source_path')
|
||||
|
||||
ret = dev.build('src/')
|
||||
|
||||
Return('ret')
|
||||
925
src/plugins/Script/examples/access.bans.lua
Normal file
925
src/plugins/Script/examples/access.bans.lua
Normal file
@@ -0,0 +1,925 @@
|
||||
-- This script contains commands and settings related to banning and muting users
|
||||
|
||||
local base=_G
|
||||
module("access.bans")
|
||||
|
||||
base.require("luadchpp")
|
||||
local adchpp = base.luadchpp
|
||||
local access = base.require("access")
|
||||
local aio = base.require('aio')
|
||||
local autil = base.require("autil")
|
||||
local json = base.require("json")
|
||||
local os = base.require("os")
|
||||
local string = base.require("string")
|
||||
local table = base.require("table")
|
||||
|
||||
-- Where to read/write ban database
|
||||
local bans_file = adchpp.Util_getCfgPath() .. "bans.txt"
|
||||
bans = {}
|
||||
bans.cids = {}
|
||||
bans.ips = {}
|
||||
bans.nicks = {}
|
||||
bans.nicksre = {}
|
||||
bans.msgsre = {}
|
||||
bans.schsre = {}
|
||||
bans.muted = {}
|
||||
|
||||
local settings = access.settings
|
||||
local commands = access.commands
|
||||
local is_op = access.is_op
|
||||
|
||||
local cm = adchpp.getCM()
|
||||
local sm = adchpp.getSM()
|
||||
local lm = adchpp.getLM()
|
||||
|
||||
local function log(message)
|
||||
lm:log(_NAME, message)
|
||||
end
|
||||
|
||||
local function load_bans()
|
||||
bans = {}
|
||||
bans.cids = {}
|
||||
bans.ips = {}
|
||||
bans.nicks = {}
|
||||
bans.nicksre = {}
|
||||
bans.msgsre = {}
|
||||
bans.schsre = {}
|
||||
bans.muted = {}
|
||||
|
||||
local ok, list, err = aio.load_file(bans_file, aio.json_loader)
|
||||
|
||||
if err then
|
||||
log('Ban loading: ' .. err)
|
||||
end
|
||||
if not ok then
|
||||
return
|
||||
end
|
||||
|
||||
bans = list
|
||||
if not bans.cids then
|
||||
bans.cids = {}
|
||||
end
|
||||
if not bans.ips then
|
||||
bans.ips = {}
|
||||
end
|
||||
if not bans.nicks then
|
||||
bans.nicks = {}
|
||||
end
|
||||
if not bans.nicksre then
|
||||
bans.nicksre = {}
|
||||
end
|
||||
if not bans.msgsre then
|
||||
bans.msgsre = {}
|
||||
end
|
||||
if not bans.schsre then
|
||||
bans.schsre = {}
|
||||
end
|
||||
if not bans.muted then
|
||||
bans.muted = {}
|
||||
end
|
||||
end
|
||||
|
||||
function save_bans()
|
||||
local err = aio.save_file(bans_file, json.encode(bans))
|
||||
if err then
|
||||
log('Bans not saved: ' .. err)
|
||||
end
|
||||
end
|
||||
|
||||
local function ban_expiration_diff(ban)
|
||||
return os.difftime(ban.expires, os.time())
|
||||
end
|
||||
|
||||
local function clear_expired_bans()
|
||||
local save = false
|
||||
|
||||
for _, ban_array in base.pairs(bans) do
|
||||
for k, ban in base.pairs(ban_array) do
|
||||
if ban.expires and ban_expiration_diff(ban) <= 0 then
|
||||
ban_array[k] = nil
|
||||
save = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if save then
|
||||
save_bans()
|
||||
end
|
||||
end
|
||||
|
||||
local function ban_expiration_string(ban)
|
||||
if ban.expires then
|
||||
local diff = ban_expiration_diff(ban)
|
||||
if diff > 0 then
|
||||
return "in " .. access.format_seconds(diff)
|
||||
else
|
||||
return "expired"
|
||||
end
|
||||
else
|
||||
return "never"
|
||||
end
|
||||
end
|
||||
|
||||
local function ban_info_string(ban, sep)
|
||||
if not sep then
|
||||
sep = "\t | "
|
||||
end
|
||||
|
||||
local str = "Level: " .. ban.level
|
||||
|
||||
if ban.reason then
|
||||
str = str .. sep .. "Reason: " .. ban.reason
|
||||
end
|
||||
|
||||
str = str .. sep .. "Expires: " .. ban_expiration_string(ban)
|
||||
|
||||
if ban.action then
|
||||
str = str .. sep .. "Bans for"
|
||||
if ban.action < 0 then
|
||||
str = str .. "ever"
|
||||
else
|
||||
str = str .. " " .. base.tostring(ban.action) .. " minutes"
|
||||
end
|
||||
end
|
||||
|
||||
return str
|
||||
end
|
||||
|
||||
local function ban_added_string(ban)
|
||||
return ban_info_string(ban, ") (")
|
||||
end
|
||||
|
||||
local function ban_return_info(ban)
|
||||
local str = " (expires: " .. ban_expiration_string(ban) .. ")"
|
||||
if ban.reason then
|
||||
str = str .. " (reason: " .. ban.reason .. ")"
|
||||
end
|
||||
return str
|
||||
end
|
||||
|
||||
local function dump_banned(c, ban)
|
||||
local str = "You are banned" .. ban_return_info(ban)
|
||||
|
||||
autil.dump(c, adchpp.AdcCommand_ERROR_BANNED_GENERIC, function(cmd)
|
||||
cmd:addParam("MS" .. str)
|
||||
|
||||
local expires
|
||||
if ban.expires then
|
||||
expires = ban_expiration_diff(ban)
|
||||
else
|
||||
expires = -1
|
||||
end
|
||||
cmd:addParam("TL" .. base.tostring(expires))
|
||||
end)
|
||||
end
|
||||
|
||||
local function make_ban(level, reason, minutes)
|
||||
local ban = { level = level }
|
||||
if string.len(reason) > 0 then
|
||||
ban.reason = reason
|
||||
end
|
||||
if minutes and string.len(minutes) > 0 then
|
||||
ban.expires = os.time() + minutes * 60
|
||||
end
|
||||
return ban
|
||||
end
|
||||
|
||||
commands.ban = {
|
||||
alias = { banuser = true },
|
||||
|
||||
command = function(c, parameters)
|
||||
local level = access.get_level(c)
|
||||
if level < settings.oplevel.value then
|
||||
return
|
||||
end
|
||||
|
||||
local minutes_pos, _, minutes = parameters:find(" (%d*)$")
|
||||
if minutes_pos then
|
||||
parameters = parameters:sub(1, minutes_pos - 1)
|
||||
if #parameters <= 0 then
|
||||
autil.reply(c, "Bad arguments")
|
||||
return
|
||||
end
|
||||
end
|
||||
local nick, reason = parameters:match("^(%S+) ?(.*)")
|
||||
if not nick then
|
||||
autil.reply(c, "You need to supply a nick")
|
||||
return
|
||||
end
|
||||
|
||||
local victim = cm:findByNick(nick)
|
||||
if victim then
|
||||
victim = victim:asClient()
|
||||
end
|
||||
if not victim then
|
||||
autil.reply(c, "No user nick-named \"" .. nick .. "\"")
|
||||
return
|
||||
end
|
||||
|
||||
local victim_cid = victim:getCID():toBase32()
|
||||
if level <= access.get_level(victim) then
|
||||
autil.reply(c, "You can't ban users whose level is higher or equal than yours")
|
||||
return
|
||||
end
|
||||
|
||||
if base.tonumber(minutes) ~= 0 then
|
||||
local ban = make_ban(level, reason, minutes)
|
||||
bans.cids[victim_cid] = ban
|
||||
save_bans()
|
||||
dump_banned(victim, ban)
|
||||
autil.reply(c, "\"" .. nick .. "\" (CID: " .. victim_cid .. ") is now banned (" .. ban_added_string(ban) .. ")")
|
||||
return
|
||||
end
|
||||
|
||||
if bans.cids[victim_cid] then
|
||||
bans.cids[victim_cid] = nil
|
||||
save_bans()
|
||||
autil.reply(c, "\"" .. nick .. "\" (CID: " .. victim_cid .. ") is now un-banned")
|
||||
else
|
||||
autil.reply(c, "\"" .. nick .. "\" (CID: " .. victim_cid .. ") is not in the banlist")
|
||||
end
|
||||
end,
|
||||
|
||||
help = "nick [reason] [minutes] - ban an online user (set minutes to 0 to un-ban)",
|
||||
|
||||
protected = is_op,
|
||||
|
||||
user_command = {
|
||||
hub_params = {
|
||||
autil.ucmd_line("Nick"),
|
||||
autil.ucmd_line("Reason (facultative)"),
|
||||
autil.ucmd_line("Minutes (facultative)")
|
||||
},
|
||||
name = "Hub management" .. autil.ucmd_sep .. "Punish" .. autil.ucmd_sep .. "Ban",
|
||||
user_params = {
|
||||
"%[userNI]",
|
||||
autil.ucmd_line("Reason (facultative)"),
|
||||
autil.ucmd_line("Minutes (facultative)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
commands.bancid = {
|
||||
command = function(c, parameters)
|
||||
local level = access.get_level(c)
|
||||
if level < settings.oplevel.value then
|
||||
return
|
||||
end
|
||||
|
||||
local minutes_pos, _, minutes = parameters:find(" (%d*)$")
|
||||
if minutes_pos then
|
||||
parameters = parameters:sub(1, minutes_pos - 1)
|
||||
if #parameters <= 0 then
|
||||
autil.reply(c, "Bad arguments")
|
||||
return
|
||||
end
|
||||
end
|
||||
local cid, reason = parameters:match("^(%S+) ?(.*)")
|
||||
if not cid then
|
||||
autil.reply(c, "You need to supply a CID")
|
||||
return
|
||||
end
|
||||
|
||||
if base.tonumber(minutes) ~= 0 then
|
||||
local ban = make_ban(level, reason, minutes)
|
||||
bans.cids[cid] = ban
|
||||
save_bans()
|
||||
autil.reply(c, "The CID \"" .. cid .. "\" is now banned (" .. ban_added_string(ban) .. ")")
|
||||
return
|
||||
end
|
||||
|
||||
if bans.cids[cid] then
|
||||
bans.cids[cid] = nil
|
||||
save_bans()
|
||||
autil.reply(c, "The CID \"" .. cid .. "\" is now un-banned")
|
||||
else
|
||||
autil.reply(c, "The CID \"" .. cid .. "\" is not in the banlist")
|
||||
end
|
||||
end,
|
||||
|
||||
help = "CID [reason] [minutes] (set minutes to 0 to un-ban)",
|
||||
|
||||
protected = is_op,
|
||||
|
||||
user_command = {
|
||||
hub_params = {
|
||||
autil.ucmd_line("CID"),
|
||||
autil.ucmd_line("Reason (facultative)"),
|
||||
autil.ucmd_line("Minutes (facultative)")
|
||||
},
|
||||
name = "Hub management" .. autil.ucmd_sep .. "Punish" .. autil.ucmd_sep .. "Ban CID",
|
||||
user_params = {
|
||||
"%[userCID]",
|
||||
autil.ucmd_line("Reason (facultative)"),
|
||||
autil.ucmd_line("Minutes (facultative)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
commands.banip = {
|
||||
command = function(c, parameters)
|
||||
local level = access.get_level(c)
|
||||
if level < settings.oplevel.value then
|
||||
return
|
||||
end
|
||||
|
||||
local minutes_pos, _, minutes = parameters:find(" (%d*)$")
|
||||
if minutes_pos then
|
||||
parameters = parameters:sub(1, minutes_pos - 1)
|
||||
if #parameters <= 0 then
|
||||
autil.reply(c, "Bad arguments")
|
||||
return
|
||||
end
|
||||
end
|
||||
local ip, reason = parameters:match("^(%S+) ?(.*)")
|
||||
if not ip then
|
||||
autil.reply(c, "You need to supply an IP address")
|
||||
return
|
||||
end
|
||||
|
||||
if base.tonumber(minutes) ~= 0 then
|
||||
local ban = make_ban(level, reason, minutes)
|
||||
bans.ips[ip] = ban
|
||||
save_bans()
|
||||
autil.reply(c, "The IP address \"" .. ip .. "\" is now banned (" .. ban_added_string(ban) .. ")")
|
||||
return
|
||||
end
|
||||
|
||||
if bans.ips[ip] then
|
||||
bans.ips[ip] = nil
|
||||
save_bans()
|
||||
autil.reply(c, "The IP address \"" .. ip .. "\" is now un-banned")
|
||||
else
|
||||
autil.reply(c, "The IP address \"" .. ip .. "\" is not found in the banlist")
|
||||
end
|
||||
end,
|
||||
|
||||
help = "IP [reason] [minutes] (set minutes to 0 to un-ban)",
|
||||
|
||||
protected = is_op,
|
||||
|
||||
user_command = {
|
||||
hub_params = {
|
||||
autil.ucmd_line("IP"),
|
||||
autil.ucmd_line("Reason (facultative)"),
|
||||
autil.ucmd_line("Minutes (facultative)")
|
||||
},
|
||||
name = "Hub management" .. autil.ucmd_sep .. "Punish" .. autil.ucmd_sep .. "Ban IP",
|
||||
user_params = {
|
||||
"%[userI4]",
|
||||
autil.ucmd_line("Reason (facultative)"),
|
||||
autil.ucmd_line("Minutes (facultative)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
commands.bannick = {
|
||||
command = function(c, parameters)
|
||||
local level = access.get_level(c)
|
||||
if level < settings.oplevel.value then
|
||||
return
|
||||
end
|
||||
|
||||
local minutes_pos, _, minutes = parameters:find(" (%d*)$")
|
||||
if minutes_pos then
|
||||
parameters = parameters:sub(1, minutes_pos - 1)
|
||||
if #parameters <= 0 then
|
||||
autil.reply(c, "Bad arguments")
|
||||
return
|
||||
end
|
||||
end
|
||||
local nick, reason = parameters:match("^(%S+) ?(.*)")
|
||||
if not nick then
|
||||
autil.reply(c, "You need to supply a nick")
|
||||
return
|
||||
end
|
||||
|
||||
if base.tonumber(minutes) ~= 0 then
|
||||
local ban = make_ban(level, reason, minutes)
|
||||
bans.nicks[nick] = ban
|
||||
save_bans()
|
||||
autil.reply(c, "The nick \"" .. nick .. "\" is now banned (" .. ban_added_string(ban) .. ")")
|
||||
return
|
||||
end
|
||||
|
||||
if bans.nicks[nick] then
|
||||
bans.nicks[nick] = nil
|
||||
save_bans()
|
||||
autil.reply(c, "The nick \"" .. nick .. "\" is now un-banned")
|
||||
else
|
||||
autil.reply(c, "The nick \"" .. nick .. "\" is not found in the banlist")
|
||||
end
|
||||
end,
|
||||
|
||||
help = "nick [reason] [minutes] (set minutes to 0 to un-ban)",
|
||||
|
||||
protected = is_op,
|
||||
|
||||
user_command = {
|
||||
hub_params = {
|
||||
autil.ucmd_line("Nick"),
|
||||
autil.ucmd_line("Reason (facultative)"),
|
||||
autil.ucmd_line("Minutes (facultative)")
|
||||
},
|
||||
name = "Hub management" .. autil.ucmd_sep .. "Punish" .. autil.ucmd_sep .. "Ban nick",
|
||||
user_params = {
|
||||
"%[userNI]",
|
||||
autil.ucmd_line("Reason (facultative)"),
|
||||
autil.ucmd_line("Minutes (facultative)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
commands.bannickre = {
|
||||
command = function(c, parameters)
|
||||
local level = access.get_level(c)
|
||||
if level < settings.oplevel.value then
|
||||
return
|
||||
end
|
||||
|
||||
local minutes_pos, _, minutes = parameters:find(" (%d*)$")
|
||||
if minutes_pos then
|
||||
parameters = parameters:sub(1, minutes_pos - 1)
|
||||
if #parameters <= 0 then
|
||||
autil.reply(c, "Bad arguments")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local action_pos, _, action = parameters:find("^(-?%d*) ")
|
||||
if action_pos then
|
||||
parameters = parameters:sub(action_pos + 2)
|
||||
if #parameters <= 0 then
|
||||
autil.reply(c, "Bad arguments")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local re, reason = parameters:match("<([^>]+)> ?(.*)")
|
||||
if not re then
|
||||
autil.reply(c, "You need to supply a reg exp (within '<' and '>' brackets)")
|
||||
return
|
||||
end
|
||||
|
||||
if base.tonumber(minutes) ~= 0 then
|
||||
local ban = make_ban(level, reason, minutes)
|
||||
if action_pos then
|
||||
ban.action = base.tonumber(action)
|
||||
end
|
||||
bans.nicksre[re] = ban
|
||||
save_bans()
|
||||
autil.reply(c, "Nicks that match \"" .. re .. "\" will be blocked (" .. ban_added_string(ban) .. ")")
|
||||
return
|
||||
end
|
||||
|
||||
if bans.nicksre[re] then
|
||||
bans.nicksre[re] = nil
|
||||
save_bans()
|
||||
autil.reply(c, "Nicks that match \"" .. re .. "\" won't be blocked anymore")
|
||||
else
|
||||
autil.reply(c, "Nicks that match \"" .. re .. "\" are not being blocked")
|
||||
end
|
||||
end,
|
||||
|
||||
help = "[action] <nick-reg-exp> [reason] [expiration] - block nicks that match the given reg exp (must be within '<' and '>' brackets); action is optional, skip for simple block, set to -1 to ban forever or >= 0 to set how many minutes to ban for; expiration is also optional, defines when this rule expires (in minutes), skip for permanent rule, set to 0 to remove banre",
|
||||
|
||||
protected = is_op,
|
||||
|
||||
user_command = {
|
||||
name = "Hub management" .. autil.ucmd_sep .. "Punish" .. autil.ucmd_sep .. "Ban nick (reg exp)",
|
||||
params = {
|
||||
autil.ucmd_line("Ban duration (facultative, in minutes; -1 = forever)"),
|
||||
"<" .. autil.ucmd_line("Reg exp of nicks to forbid") .. ">",
|
||||
autil.ucmd_line("Reason (facultative)"),
|
||||
autil.ucmd_line("Rule expiration (facultative, in minutes)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
commands.banmsgre = {
|
||||
command = function(c, parameters)
|
||||
local level = access.get_level(c)
|
||||
if level < settings.oplevel.value then
|
||||
return
|
||||
end
|
||||
|
||||
local minutes_pos, _, minutes = parameters:find(" (%d*)$")
|
||||
if minutes_pos then
|
||||
parameters = parameters:sub(1, minutes_pos - 1)
|
||||
if #parameters <= 0 then
|
||||
autil.reply(c, "Bad arguments")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local action_pos, _, action = parameters:find("^(-?%d*) ")
|
||||
if action_pos then
|
||||
parameters = parameters:sub(action_pos + 2)
|
||||
if #parameters <= 0 then
|
||||
autil.reply(c, "Bad arguments")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local re, reason = parameters:match("<([^>]+)> ?(.*)")
|
||||
if not re then
|
||||
autil.reply(c, "You need to supply a reg exp (within '<' and '>' brackets)")
|
||||
return
|
||||
end
|
||||
|
||||
if base.tonumber(minutes) ~= 0 then
|
||||
local ban = make_ban(level, reason, minutes)
|
||||
if action_pos then
|
||||
ban.action = base.tonumber(action)
|
||||
end
|
||||
re = string.lower(re)
|
||||
bans.msgsre[re] = ban
|
||||
save_bans()
|
||||
autil.reply(c, "Messages that match \"" .. re .. "\" will be blocked (" .. ban_added_string(ban) .. ")")
|
||||
return
|
||||
end
|
||||
|
||||
if bans.msgsre[re] then
|
||||
bans.msgsre[re] = nil
|
||||
save_bans()
|
||||
autil.reply(c, "Messages that match \"" .. re .. "\" won't be blocked anymore")
|
||||
else
|
||||
autil.reply(c, "Messages that match \"" .. re .. "\" are not being blocked")
|
||||
end
|
||||
end,
|
||||
|
||||
help = "[action] <chat-reg-exp> [reason] [expiration] - block chatmessages that match the given reg exp (must be within '<' and '>' brackets); action is optional, skip for simple block, set to -1 to ban forever or >= 0 to set how many minutes to ban for; expiration is also optional, defines when this rule expires (in minutes), skip for permanent rule, set to 0 to remove banre",
|
||||
|
||||
protected = is_op,
|
||||
|
||||
user_command = {
|
||||
name = "Hub management" .. autil.ucmd_sep .. "Punish" .. autil.ucmd_sep .. "Ban chat (reg exp)",
|
||||
params = {
|
||||
autil.ucmd_line("Ban duration (facultative, in minutes; -1 = forever)"),
|
||||
"<" .. autil.ucmd_line("Reg exp of chat messages to forbid") .. ">",
|
||||
autil.ucmd_line("Reason (facultative)"),
|
||||
autil.ucmd_line("Rule expiration (facultative, in minutes)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
commands.banschre = {
|
||||
command = function(c, parameters)
|
||||
local level = access.get_level(c)
|
||||
if level < settings.oplevel.value then
|
||||
return
|
||||
end
|
||||
|
||||
local minutes_pos, _, minutes = parameters:find(" (%d*)$")
|
||||
if minutes_pos then
|
||||
parameters = parameters:sub(1, minutes_pos - 1)
|
||||
if #parameters <= 0 then
|
||||
autil.reply(c, "Bad arguments")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local action_pos, _, action = parameters:find("^(-?%d*) ")
|
||||
if action_pos then
|
||||
parameters = parameters:sub(action_pos + 2)
|
||||
if #parameters <= 0 then
|
||||
autil.reply(c, "Bad arguments")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local re, reason = parameters:match("<([^>]+)> ?(.*)")
|
||||
if not re then
|
||||
autil.reply(c, "You need to supply a reg exp (within '<' and '>' brackets)")
|
||||
return
|
||||
end
|
||||
|
||||
if base.tonumber(minutes) ~= 0 then
|
||||
local ban = make_ban(level, reason, minutes)
|
||||
if action_pos then
|
||||
ban.action = base.tonumber(action)
|
||||
end
|
||||
bans.schsre[re] = ban
|
||||
save_bans()
|
||||
autil.reply(c, "Searches that match \"" .. re .. "\" will be blocked (" .. ban_added_string(ban) .. ")")
|
||||
return
|
||||
end
|
||||
|
||||
if bans.schsre[re] then
|
||||
bans.schsre[re] = nil
|
||||
save_bans()
|
||||
autil.reply(c, "Searches that match \"" .. re .. "\" won't be blocked anymore")
|
||||
else
|
||||
autil.reply(c, "Searches that match \"" .. re .. "\" are not being blocked")
|
||||
end
|
||||
end,
|
||||
|
||||
help = "[action] <search-reg-exp> [reason] [expiration] - block searches that match the given reg exp (must be within '<' and '>' brackets); action is optional, skip for simple block, set to -1 to ban forever or >= 0 to set how many minutes to ban for; expiration is also optional, defines when this rule expires (in minutes), skip for permanent rule, set to 0 to remove banre",
|
||||
|
||||
protected = is_op,
|
||||
|
||||
user_command = {
|
||||
name = "Hub management" .. autil.ucmd_sep .. "Punish" .. autil.ucmd_sep .. "Ban search (reg exp)",
|
||||
params = {
|
||||
autil.ucmd_line("Ban duration (facultative, in minutes; -1 = forever)"),
|
||||
"<" .. autil.ucmd_line("Reg exp of search messages to forbid") .. ">",
|
||||
autil.ucmd_line("Reason (facultative)"),
|
||||
autil.ucmd_line("Rule expiration (facultative, in minutes)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
commands.listbans = {
|
||||
alias = { listban = true, listbanned = true, showban = true, showbans = true, showbanned = true, banlist = true, banslist = true },
|
||||
|
||||
command = function(c)
|
||||
local level = access.get_level(c)
|
||||
if level < settings.oplevel.value then
|
||||
return
|
||||
end
|
||||
|
||||
local str = "\nCID bans:"
|
||||
for cid, ban in base.pairs(bans.cids) do
|
||||
str = str .. "\n\tCID: " .. cid .. "\t | " .. ban_info_string(ban)
|
||||
end
|
||||
|
||||
str = str .. "\n\nIP bans:"
|
||||
for ip, ban in base.pairs(bans.ips) do
|
||||
str = str .. "\n\tIP: " .. ip .. "\t | " .. ban_info_string(ban)
|
||||
end
|
||||
|
||||
str = str .. "\n\nNick bans:"
|
||||
for nick, ban in base.pairs(bans.nicks) do
|
||||
str = str .. "\n\tNick: " .. nick .. "\t | " .. ban_info_string(ban)
|
||||
end
|
||||
|
||||
str = str .. "\n\nNick bans (reg exp):"
|
||||
for nickre, ban in base.pairs(bans.nicksre) do
|
||||
str = str .. "\n\tReg exp: " .. nickre .. "\t | " .. ban_info_string(ban)
|
||||
end
|
||||
|
||||
str = str .. "\n\nMessage bans (reg exp):"
|
||||
for msgre, ban in base.pairs(bans.msgsre) do
|
||||
str = str .. "\n\tReg exp: " .. msgre .. "\t | " .. ban_info_string(ban)
|
||||
end
|
||||
|
||||
str = str .. "\n\nSearch bans (reg exp):"
|
||||
for schre, ban in base.pairs(bans.schsre) do
|
||||
str = str .. "\n\tReg exp: " .. schre .. "\t | " .. ban_info_string(ban)
|
||||
end
|
||||
|
||||
str = str .. "\n\nMuted:"
|
||||
for cid, ban in base.pairs(bans.muted) do
|
||||
str = str .. "\n\tCID: " .. cid .. "\t | " .. ban_info_string(ban)
|
||||
end
|
||||
|
||||
autil.reply(c, str)
|
||||
end,
|
||||
|
||||
protected = is_op,
|
||||
|
||||
user_command = { name = "Hub management" .. autil.ucmd_sep .. "List bans" }
|
||||
}
|
||||
|
||||
commands.loadbans = {
|
||||
alias = { reloadbans = true },
|
||||
|
||||
command = function(c)
|
||||
local level = access.get_level(c)
|
||||
if level < settings.oplevel.value then
|
||||
return
|
||||
end
|
||||
|
||||
load_bans()
|
||||
|
||||
autil.reply(c, "Ban list reloaded")
|
||||
end,
|
||||
|
||||
help = "- reload the ban list",
|
||||
|
||||
protected = is_op,
|
||||
|
||||
user_command = { name = "Hub management" .. autil.ucmd_sep .. "Reload bans" }
|
||||
}
|
||||
|
||||
commands.mute = {
|
||||
alias = { stfu = true },
|
||||
|
||||
command = function(c, parameters)
|
||||
local level = access.get_level(c)
|
||||
if level < settings.oplevel.value then
|
||||
return
|
||||
end
|
||||
|
||||
local minutes_pos, _, minutes = parameters:find(" (%d*)$")
|
||||
if minutes_pos then
|
||||
parameters = parameters:sub(1, minutes_pos - 1)
|
||||
if #parameters <= 0 then
|
||||
autil.reply(c, "Bad arguments")
|
||||
return
|
||||
end
|
||||
end
|
||||
local nick, reason = parameters:match("^(%S+) ?(.*)")
|
||||
if not nick then
|
||||
autil.reply(c, "You need to supply a nick")
|
||||
return
|
||||
end
|
||||
|
||||
local victim = cm:findByNick(nick)
|
||||
if victim then
|
||||
victim = victim:asClient()
|
||||
end
|
||||
if not victim then
|
||||
autil.reply(c, "No user nick-named \"" .. nick .. "\"")
|
||||
return
|
||||
end
|
||||
|
||||
local victim_cid = victim:getCID():toBase32()
|
||||
if level <= access.get_level(victim) then
|
||||
autil.reply(c, "You can't mute users whose level is higher or equal than yours")
|
||||
return
|
||||
end
|
||||
|
||||
if base.tonumber(minutes) ~= 0 then
|
||||
local ban = make_ban(level, reason, minutes)
|
||||
bans.muted[victim_cid] = ban
|
||||
save_bans()
|
||||
autil.reply(c, "\"" .. nick .. "\" (CID: " .. victim_cid .. ") is now muted (" .. ban_added_string(ban) .. ")")
|
||||
return
|
||||
end
|
||||
|
||||
if bans.muted[victim_cid] then
|
||||
bans.muted[victim_cid] = nil
|
||||
save_bans()
|
||||
autil.reply(c, "\"" .. nick .. "\" (CID: " .. victim_cid .. ") is now removed from the mutelist")
|
||||
else
|
||||
autil.reply(c, "\"" .. nick .. "\" (CID: " .. victim_cid .. ") is not found in the mutelist")
|
||||
end
|
||||
end,
|
||||
|
||||
help = "nick [reason] [minutes] - mute an online user (set minutes to 0 to un-mute)",
|
||||
|
||||
protected = access.is_op,
|
||||
|
||||
user_command = {
|
||||
hub_params = {
|
||||
autil.ucmd_line("Nick"),
|
||||
autil.ucmd_line("Reason (facultative)"),
|
||||
autil.ucmd_line("Minutes (facultative)")
|
||||
},
|
||||
name = "Hub management" .. autil.ucmd_sep .. "Punish" .. autil.ucmd_sep .. "Mute",
|
||||
user_params = {
|
||||
"%[userNI]",
|
||||
autil.ucmd_line("Reason (facultative)"),
|
||||
autil.ucmd_line("Minutes (facultative)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
local function onMSG(c, cmd)
|
||||
local muted = bans.muted[c:getCID():toBase32()]
|
||||
if muted then
|
||||
autil.reply(c, "You are muted" .. ban_return_info(muted))
|
||||
return false
|
||||
end
|
||||
|
||||
local level = access.get_level(c)
|
||||
local msg = string.lower(cmd:getParam(0))
|
||||
|
||||
for re, reban in base.pairs(bans.msgsre) do
|
||||
if reban.level > level and msg:match(re) then
|
||||
local str = "Message blocked"
|
||||
if reban.reason then
|
||||
str = str .. ": " .. reban.reason
|
||||
end
|
||||
if reban.action then
|
||||
local ban = { level = reban.level, reason = str }
|
||||
if reban.action == 0 then
|
||||
ban.expires = 0
|
||||
else
|
||||
if reban.action > 0 then
|
||||
ban.expires = os.time() + reban.action * 60
|
||||
end
|
||||
bans.cids[c:getCID():toBase32()] = ban
|
||||
save_bans()
|
||||
end
|
||||
dump_banned(c, ban)
|
||||
else
|
||||
autil.reply(c, str)
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function onSCH(c, cmd)
|
||||
local level = access.get_level(c)
|
||||
local sch
|
||||
|
||||
local tr = cmd:getParam('TR', 0)
|
||||
if #tr > 0 then
|
||||
return true
|
||||
else
|
||||
local vars = {}
|
||||
local params = cmd:getParameters()
|
||||
local params_size = params:size()
|
||||
if params_size > 0 then
|
||||
for i = 0, params_size - 1 do
|
||||
local param = params[i]
|
||||
if #param > 2 then
|
||||
local field = string.sub(param, 1, 2)
|
||||
if field == 'AN' then
|
||||
local var = string.sub(param, 3)
|
||||
table.insert(vars, string.lower(var))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
sch = table.concat(vars, ' ')
|
||||
end
|
||||
|
||||
for re, reban in base.pairs(bans.schsre) do
|
||||
if reban.level > level and sch:match(re) then
|
||||
local str = "Search blocked"
|
||||
if reban.reason then
|
||||
str = str .. ": " .. reban.reason
|
||||
end
|
||||
if reban.action then
|
||||
local ban = { level = reban.level, reason = str }
|
||||
if reban.action == 0 then
|
||||
ban.expires = 0
|
||||
else
|
||||
if reban.action > 0 then
|
||||
ban.expires = os.time() + reban.action * 60
|
||||
end
|
||||
bans.cids[c:getCID():toBase32()] = ban
|
||||
save_bans()
|
||||
end
|
||||
dump_banned(c, ban)
|
||||
else
|
||||
autil.reply(c, str)
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function onINF(c, cmd)
|
||||
local cid, nick
|
||||
|
||||
if c:getState() == adchpp.Entity_STATE_NORMAL then
|
||||
cid = c:getCID():toBase32()
|
||||
nick = c:getField("NI")
|
||||
else
|
||||
cid = cmd:getParam("ID", 0)
|
||||
nick = cmd:getParam("NI", 0)
|
||||
end
|
||||
|
||||
local ban = nil
|
||||
if bans.cids[cid] then
|
||||
ban = bans.cids[cid]
|
||||
elseif bans.ips[c:getIp()] then
|
||||
ban = bans.ips[c:getIp()]
|
||||
elseif bans.nicks[nick] then
|
||||
ban = bans.nicks[nick]
|
||||
else
|
||||
for re, reban in base.pairs(bans.nicksre) do
|
||||
if nick:match(re) and reban.level > access.get_level(c) then
|
||||
local str = "Nick blocked"
|
||||
if reban.reason then
|
||||
str = str .. ": " .. reban.reason
|
||||
end
|
||||
ban = { level = reban.level, reason = str }
|
||||
if reban.action and reban.action ~= 0 then
|
||||
if reban.action > 0 then
|
||||
ban.expires = os.time() + reban.action * 60
|
||||
end
|
||||
bans.cids[c:getCID():toBase32()] = ban
|
||||
save_bans()
|
||||
else
|
||||
ban.expires = 0
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if ban and ban.level > access.get_level(c) then
|
||||
dump_banned(c, ban)
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
load_bans()
|
||||
|
||||
access.register_handler(adchpp.AdcCommand_CMD_MSG, onMSG, true)
|
||||
access.register_handler(adchpp.AdcCommand_CMD_SCH, onSCH, true)
|
||||
access.register_handler(adchpp.AdcCommand_CMD_INF, onINF)
|
||||
|
||||
cancel_timer = sm:addTimedJob(1000, clear_expired_bans)
|
||||
autil.on_unloading(_NAME, cancel_timer)
|
||||
117
src/plugins/Script/examples/access.bot.lua
Normal file
117
src/plugins/Script/examples/access.bot.lua
Normal file
@@ -0,0 +1,117 @@
|
||||
-- This script contains settings and commands related to the bot. If this script is not loaded, the bot will not appear...
|
||||
-- The main bot managed by this script is stored in the public "main_bot" variable.
|
||||
|
||||
local base=_G
|
||||
module("access.bot")
|
||||
|
||||
base.require("luadchpp")
|
||||
local adchpp = base.luadchpp
|
||||
local access = base.require("access")
|
||||
local string = base.require('string')
|
||||
local autil = base.require("autil")
|
||||
|
||||
local settings = access.settings
|
||||
local commands = access.commands
|
||||
local cm = adchpp.getCM()
|
||||
|
||||
main_bot = nil
|
||||
|
||||
access.add_setting('botcid', {
|
||||
alias = { botid = true },
|
||||
|
||||
help = "CID of the bot, restart the hub after the change",
|
||||
|
||||
value = adchpp.CID_generate():toBase32(),
|
||||
|
||||
validate = function(new)
|
||||
if adchpp.CID(new.value):toBase32() ~= new.value then
|
||||
return "the CID must be a valid 39-byte base32 representation"
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
access.add_setting('botname', {
|
||||
alias = { botnick = true, botni = true },
|
||||
|
||||
change = function()
|
||||
if main_bot then
|
||||
main_bot:setField("NI", settings.botname.value)
|
||||
cm:sendToAll(adchpp.AdcCommand(adchpp.AdcCommand_CMD_INF, adchpp.AdcCommand_TYPE_BROADCAST, main_bot:getSID()):addParam("NI", settings.botname.value):getBuffer())
|
||||
end
|
||||
end,
|
||||
|
||||
help = "name of the hub bot",
|
||||
|
||||
value = "Bot",
|
||||
|
||||
validate = access.validate_ni
|
||||
})
|
||||
|
||||
access.add_setting('botdescription', {
|
||||
alias = { botdescr = true, botde = true },
|
||||
|
||||
change = function()
|
||||
if main_bot then
|
||||
main_bot:setField("DE", settings.botdescription.value)
|
||||
cm:sendToAll(adchpp.AdcCommand(adchpp.AdcCommand_CMD_INF, adchpp.AdcCommand_TYPE_BROADCAST, main_bot:getSID()):addParam("DE", settings.botdescription.value):getBuffer())
|
||||
end
|
||||
end,
|
||||
|
||||
help = "description of the hub bot",
|
||||
|
||||
value = "",
|
||||
|
||||
validate = access.validate_de
|
||||
})
|
||||
|
||||
access.add_setting('botemail', {
|
||||
alias = { botmail = true, botem = true },
|
||||
|
||||
change = function()
|
||||
if main_bot then
|
||||
main_bot:setField("EM", settings.botemail.value)
|
||||
cm:sendToAll(adchpp.AdcCommand(adchpp.AdcCommand_CMD_INF, adchpp.AdcCommand_TYPE_BROADCAST, main_bot:getSID()):addParam("EM", settings.botemail.value):getBuffer())
|
||||
end
|
||||
end,
|
||||
|
||||
help = "e-mail of the hub bot",
|
||||
|
||||
value = ""
|
||||
})
|
||||
|
||||
local function onMSG(c, cmd)
|
||||
if autil.reply_from and autil.reply_from:getSID() == main_bot:getSID() then
|
||||
|
||||
local msg = cmd:getParam(0)
|
||||
if access.handle_plus_command(c, msg) then
|
||||
return false
|
||||
end
|
||||
|
||||
autil.reply(c, 'Invalid command, send "+help" for a list of available commands')
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function makeBot()
|
||||
local bot = cm:createSimpleBot()
|
||||
bot:setCID(adchpp.CID(settings.botcid.value))
|
||||
bot:setField("ID", settings.botcid.value)
|
||||
bot:setField("NI", settings.botname.value)
|
||||
bot:setField("DE", settings.botdescription.value)
|
||||
bot:setField("EM", settings.botemail.value)
|
||||
bot:setFlag(adchpp.Entity_FLAG_OP)
|
||||
bot:setFlag(adchpp.Entity_FLAG_SU)
|
||||
bot:setFlag(adchpp.Entity_FLAG_OWNER)
|
||||
return bot
|
||||
end
|
||||
|
||||
main_bot = makeBot()
|
||||
cm:regBot(main_bot)
|
||||
|
||||
autil.on_unloaded(_NAME, function()
|
||||
main_bot:disconnect(adchpp.Util_REASON_PLUGIN)
|
||||
end)
|
||||
|
||||
access.register_handler(adchpp.AdcCommand_CMD_MSG, onMSG)
|
||||
5629
src/plugins/Script/examples/access.guard.lua
Normal file
5629
src/plugins/Script/examples/access.guard.lua
Normal file
File diff suppressed because it is too large
Load Diff
1801
src/plugins/Script/examples/access.lua
Normal file
1801
src/plugins/Script/examples/access.lua
Normal file
File diff suppressed because it is too large
Load Diff
219
src/plugins/Script/examples/access.op.lua
Normal file
219
src/plugins/Script/examples/access.op.lua
Normal file
@@ -0,0 +1,219 @@
|
||||
-- This script contains a few useful op commands such as kick and redirect
|
||||
|
||||
local base=_G
|
||||
module("access.op")
|
||||
|
||||
base.require("luadchpp")
|
||||
local adchpp = base.luadchpp
|
||||
local access = base.require("access")
|
||||
local autil = base.require("autil")
|
||||
local string = base.require("string")
|
||||
|
||||
local commands = access.commands
|
||||
local settings = access.settings
|
||||
local get_level = access.get_level
|
||||
local is_op = access.is_op
|
||||
|
||||
local cm = adchpp.getCM()
|
||||
|
||||
commands.kick = {
|
||||
alias = { drop = true, dropuser = true, kickuser = true },
|
||||
|
||||
command = function(c, parameters)
|
||||
local level = get_level(c)
|
||||
if level < settings.oplevel.value then
|
||||
return
|
||||
end
|
||||
|
||||
local nick, reason = parameters:match("^(%S+) ?(.*)")
|
||||
if not nick then
|
||||
autil.reply(c, "You need to supply a nick")
|
||||
return
|
||||
end
|
||||
|
||||
local victim = cm:findByNick(nick)
|
||||
if victim then
|
||||
victim = victim:asClient()
|
||||
end
|
||||
if not victim then
|
||||
autil.reply(c, "No user nick-named \"" .. nick .. "\"")
|
||||
return
|
||||
end
|
||||
|
||||
local victim_cid = victim:getCID():toBase32()
|
||||
if level <= get_level(victim) then
|
||||
autil.reply(c, "You can't kick users whose level is higher or equal than yours")
|
||||
return
|
||||
end
|
||||
|
||||
local text = "You have been kicked"
|
||||
if string.len(reason) > 0 then
|
||||
text = text .. " (reason: " .. reason .. ")"
|
||||
end
|
||||
autil.dump(victim, adchpp.AdcCommand_ERROR_BANNED_GENERIC, function(cmd)
|
||||
cmd:addParam("ID" .. adchpp.AdcCommand_fromSID(c:getSID()))
|
||||
:addParam("MS" .. text)
|
||||
end)
|
||||
autil.reply(c, "\"" .. nick .. "\" (CID: " .. victim_cid .. ") has been kicked")
|
||||
end,
|
||||
|
||||
help = "user [reason] - disconnect the user, she can reconnect whenever she wants to",
|
||||
|
||||
protected = is_op,
|
||||
|
||||
user_command = {
|
||||
hub_params = {
|
||||
autil.ucmd_line("User"),
|
||||
autil.ucmd_line("Reason (facultative)")
|
||||
},
|
||||
name = "Hub management" .. autil.ucmd_sep .. "Punish" .. autil.ucmd_sep .. "Kick",
|
||||
user_params = {
|
||||
"%[userNI]",
|
||||
autil.ucmd_line("Reason (facultative)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
commands.mass = {
|
||||
alias = { massmessage = true },
|
||||
|
||||
command = function(c, parameters)
|
||||
if not commands.mass.protected(c) then
|
||||
return
|
||||
end
|
||||
|
||||
local level_pos, _, level = parameters:find(" ?(%d*)$")
|
||||
local message = parameters:sub(0, level_pos - 1)
|
||||
if #message <= 0 then
|
||||
autil.reply(c, "You need to supply a message")
|
||||
return
|
||||
end
|
||||
if string.len(level) > 0 then
|
||||
level = base.tonumber(level)
|
||||
else
|
||||
level = 0
|
||||
end
|
||||
|
||||
local entities = cm:getEntities()
|
||||
local size = entities:size()
|
||||
if size == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
local mass_cmd
|
||||
if access.bot then
|
||||
mass_cmd = autil.pm(message, access.bot.main_bot:getSID(), 0)
|
||||
else
|
||||
mass_cmd = autil.info(message)
|
||||
end
|
||||
|
||||
local count = 0
|
||||
for i = 0, size - 1 do
|
||||
local other = entities[i]:asClient()
|
||||
if other then
|
||||
local ok = level <= 0
|
||||
if not ok then
|
||||
ok = get_level(other) >= level
|
||||
end
|
||||
|
||||
if ok then
|
||||
mass_cmd:setTo(other:getSID())
|
||||
other:send(mass_cmd)
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
autil.reply(c, "Message sent to " .. count .. " users")
|
||||
end,
|
||||
|
||||
help = "message [min-level] -no min-level means send to everyone",
|
||||
|
||||
protected = is_op,
|
||||
|
||||
user_command = {
|
||||
name = "Hub management" .. autil.ucmd_sep .. "Mass message",
|
||||
params = {
|
||||
autil.ucmd_line("Message"),
|
||||
autil.ucmd_line("Minimum level (facultative)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
commands.redirect = {
|
||||
alias = { forward = true },
|
||||
|
||||
command = function(c, parameters)
|
||||
local level = get_level(c)
|
||||
if level < settings.oplevel.value then
|
||||
return
|
||||
end
|
||||
|
||||
local nick_pos, _, nick = parameters:find("^(%S+)")
|
||||
|
||||
if nick_pos then
|
||||
parameters = parameters:sub(nick_pos + 2)
|
||||
if #parameters <= 0 then
|
||||
autil.reply(c, "Bad arguments")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if not nick then
|
||||
autil.reply(c, "You need to supply a nick")
|
||||
return
|
||||
end
|
||||
|
||||
local victim = cm:findByNick(nick)
|
||||
if victim then
|
||||
victim = victim:asClient()
|
||||
end
|
||||
if not victim then
|
||||
autil.reply(c, "No user nick-named \"" .. nick .. "\"")
|
||||
return
|
||||
end
|
||||
|
||||
local address, reason = parameters:match("<([^>]+)> ?(.*)")
|
||||
if not address then
|
||||
autil.reply(c, "You need to supply a redirect address (within '<' and '>' brackets)")
|
||||
return
|
||||
end
|
||||
|
||||
local victim_cid = victim:getCID():toBase32()
|
||||
if level <= get_level(victim) then
|
||||
autil.reply(c, "You can't redirect users whose level is higher or equal than yours")
|
||||
return
|
||||
end
|
||||
|
||||
local text = "You have been redirected"
|
||||
if string.len(reason) > 0 then
|
||||
text = text .. " (reason: " .. reason .. ")"
|
||||
end
|
||||
|
||||
autil.dump(victim, adchpp.AdcCommand_ERROR_BANNED_GENERIC, function(cmd)
|
||||
cmd:addParam("ID" .. adchpp.AdcCommand_fromSID(c:getSID()))
|
||||
:addParam("MS" .. text)
|
||||
cmd:addParam("RD" .. address)
|
||||
end)
|
||||
|
||||
autil.reply(c, "\"" .. nick .. "\" (CID: " .. victim_cid .. ") has been redirected to \"" .. address .. "\"")
|
||||
end,
|
||||
|
||||
help = "nick <address> [reason] - redirects the user to the given address (must be within '<' and '>' brackets); reason is optional ",
|
||||
|
||||
protected = is_op,
|
||||
|
||||
user_command = {
|
||||
hub_params = {
|
||||
autil.ucmd_line("Nick"),
|
||||
"<" .. autil.ucmd_line("Redirect Address") .. ">",
|
||||
autil.ucmd_line("Reason (facultative)")
|
||||
},
|
||||
name = "Hub management" .. autil.ucmd_sep .. "Punish" .. autil.ucmd_sep .. "Redirect",
|
||||
user_params = {
|
||||
"%[userNI]",
|
||||
"<" .. autil.ucmd_line("Redirect Address") .. ">",
|
||||
autil.ucmd_line("Reason (facultative)")
|
||||
}
|
||||
}
|
||||
}
|
||||
107
src/plugins/Script/examples/aio.lua
Normal file
107
src/plugins/Script/examples/aio.lua
Normal file
@@ -0,0 +1,107 @@
|
||||
-- I/O utilities for adchpp.
|
||||
|
||||
local base = _G
|
||||
module('aio')
|
||||
local io = base.require('io')
|
||||
local json = base.require('json')
|
||||
|
||||
-- forward declarations.
|
||||
local read_file, write_file
|
||||
|
||||
-- read a file. if reading the file fails, try to load its backup (.tmp file).
|
||||
-- post_load: optional function called after a successful read.
|
||||
-- return values:
|
||||
-- * boolean success flag.
|
||||
-- * file contents on success.
|
||||
-- * error / debug message.
|
||||
function load_file(path, post_load)
|
||||
|
||||
-- try to read the file.
|
||||
local ok, str = read_file(path, post_load)
|
||||
if ok then
|
||||
return true, str
|
||||
end
|
||||
|
||||
-- couldn't read the file; try to read the backup.
|
||||
local ok2, str2 = read_file(path .. '.tmp', post_load)
|
||||
if ok2 then
|
||||
|
||||
-- copy the backup to the actual file.
|
||||
local ok3, str3 = read_file(path .. '.tmp') -- read without post_load.
|
||||
if ok3 then
|
||||
write_file(path, str3)
|
||||
end
|
||||
|
||||
return true, str2, str .. '; loaded backup from ' .. path .. '.tmp'
|
||||
end
|
||||
|
||||
-- couldn't read anything.
|
||||
return false, nil, str .. '; ' .. str2
|
||||
end
|
||||
|
||||
-- write to a file after having backed it up to a .tmp file.
|
||||
-- return value: error message on failure, nothing otherwise.
|
||||
function save_file(path, contents)
|
||||
|
||||
-- start by saving a backup, in case writing fails.
|
||||
local ok, str = read_file(path)
|
||||
if ok then
|
||||
write_file(path .. '.tmp', str)
|
||||
end
|
||||
|
||||
-- the file has been backed up; now write to it.
|
||||
return write_file(path, contents)
|
||||
end
|
||||
|
||||
-- wrapper around a json decoder, suitable for use as the post_load param of load_file.
|
||||
function json_loader(str)
|
||||
local ok, ret = base.pcall(json.decode, str)
|
||||
if ok then
|
||||
return true, ret
|
||||
end
|
||||
return false, 'Corrupted file, unable to decode the JSON data'
|
||||
end
|
||||
|
||||
-- utility function that reads a file.
|
||||
-- post_load: optional function called after a successful read.
|
||||
-- return values:
|
||||
-- * boolean success flag.
|
||||
-- * file contents on success; error message otherwise.
|
||||
read_file = function(path, post_load)
|
||||
|
||||
local ok, file = base.pcall(io.open, path, 'r')
|
||||
if not ok or not file then
|
||||
return false, 'Unable to open ' .. path .. ' for reading'
|
||||
end
|
||||
|
||||
local str
|
||||
base.pcall(function()
|
||||
str = file:read('*a')
|
||||
file:close()
|
||||
end)
|
||||
|
||||
if not str or #str == 0 then
|
||||
return false, 'Unable to read ' .. path
|
||||
end
|
||||
|
||||
if post_load then
|
||||
return post_load(str)
|
||||
end
|
||||
|
||||
return true, str
|
||||
end
|
||||
|
||||
-- utility function that writes to a file.
|
||||
-- return value: error message on failure, nothing otherwise.
|
||||
write_file = function(path, contents)
|
||||
|
||||
local ok, file = base.pcall(io.open, path, 'w')
|
||||
if not ok or not file then
|
||||
return 'Unable to open ' .. path .. ' for writing'
|
||||
end
|
||||
|
||||
base.pcall(function()
|
||||
file:write(contents)
|
||||
file:close()
|
||||
end)
|
||||
end
|
||||
125
src/plugins/Script/examples/autil.lua
Normal file
125
src/plugins/Script/examples/autil.lua
Normal file
@@ -0,0 +1,125 @@
|
||||
-- Various utilities for adchpp
|
||||
|
||||
local base = _G
|
||||
|
||||
module("autil")
|
||||
|
||||
base.require('luadchpp')
|
||||
local adchpp = base.luadchpp
|
||||
local string = base.require('string')
|
||||
local table = base.require('table')
|
||||
|
||||
ucmd_sep = "/"
|
||||
|
||||
function ucmd_line(str)
|
||||
return "%[line:" .. str .. "]"
|
||||
end
|
||||
|
||||
function ucmd_list(title, options, selected)
|
||||
return ucmd_line(title .. "/" .. base.tostring(selected or 0) .. "/" .. table.concat(options, "/"))
|
||||
end
|
||||
|
||||
function info(m)
|
||||
return adchpp.AdcCommand(adchpp.AdcCommand_CMD_MSG, adchpp.AdcCommand_TYPE_INFO, adchpp.AdcCommand_HUB_SID)
|
||||
:addParam(m)
|
||||
end
|
||||
|
||||
-- "from" and "to" are SIDs (numbers).
|
||||
function pm(m, from, to)
|
||||
local command = adchpp.AdcCommand(adchpp.AdcCommand_CMD_MSG, adchpp.AdcCommand_TYPE_DIRECT, from)
|
||||
:addParam(m)
|
||||
:addParam("PM", adchpp.AdcCommand_fromSID(from))
|
||||
command:setTo(to)
|
||||
return command
|
||||
end
|
||||
|
||||
function reply(c, m)
|
||||
local command
|
||||
if reply_from then
|
||||
command = pm(m, reply_from:getSID(), c:getSID())
|
||||
else
|
||||
command = info(m)
|
||||
end
|
||||
c:send(command)
|
||||
end
|
||||
|
||||
-- params: either a message string or a function(AdcCommand QUI_command).
|
||||
function dump(c, code, params)
|
||||
local msg
|
||||
|
||||
local cmd = adchpp.AdcCommand(adchpp.AdcCommand_CMD_QUI, adchpp.AdcCommand_TYPE_INFO, adchpp.AdcCommand_HUB_SID)
|
||||
:addParam(adchpp.AdcCommand_fromSID(c:getSID())):addParam("DI1")
|
||||
if base.type(params) == "function" then
|
||||
params(cmd)
|
||||
msg = cmd:getParam("MS", 1)
|
||||
else
|
||||
msg = params
|
||||
cmd:addParam("MS" .. msg)
|
||||
end
|
||||
|
||||
c:send(adchpp.AdcCommand(adchpp.AdcCommand_CMD_STA, adchpp.AdcCommand_TYPE_INFO, adchpp.AdcCommand_HUB_SID)
|
||||
:addParam(adchpp.AdcCommand_SEV_FATAL .. code):addParam(msg))
|
||||
|
||||
c:send(cmd)
|
||||
c:disconnect(adchpp.Util_REASON_PLUGIN)
|
||||
end
|
||||
|
||||
local function file_of_name(file, name)
|
||||
return string.sub(file, -4 - #name) == name .. '.lua'
|
||||
end
|
||||
|
||||
local loading = {}
|
||||
function on_loading(name, f)
|
||||
table.insert(loading, { name = name, f = f })
|
||||
end
|
||||
base.loading = function(file)
|
||||
local ret = false
|
||||
for _, v in base.pairs(loading) do
|
||||
if file_of_name(file, v.name) then
|
||||
if v.f() then
|
||||
ret = true
|
||||
end
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
local loaded = {}
|
||||
function on_loaded(name, f)
|
||||
table.insert(loaded, { name = name, f = f })
|
||||
end
|
||||
base.loaded = function(file)
|
||||
for _, v in base.pairs(loaded) do
|
||||
if file_of_name(file, v.name) then
|
||||
v.f()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local unloading = {}
|
||||
function on_unloading(name, f)
|
||||
table.insert(unloading, { name = name, f = f })
|
||||
end
|
||||
base.unloading = function(file)
|
||||
local ret = false
|
||||
for _, v in base.pairs(unloading) do
|
||||
if file_of_name(file, v.name) then
|
||||
if v.f() then
|
||||
ret = true
|
||||
end
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
local unloaded = {}
|
||||
function on_unloaded(name, f)
|
||||
table.insert(unloaded, { name = name, f = f })
|
||||
end
|
||||
base.unloaded = function(file)
|
||||
for _, v in base.pairs(unloaded) do
|
||||
if file_of_name(file, v.name) then
|
||||
v.f()
|
||||
end
|
||||
end
|
||||
end
|
||||
31
src/plugins/Script/examples/checkemptyinbloom.lua
Normal file
31
src/plugins/Script/examples/checkemptyinbloom.lua
Normal file
@@ -0,0 +1,31 @@
|
||||
local base = _G
|
||||
|
||||
-- Notifies a user he may have empty files in his share according to his BLOOM
|
||||
-- Requires that the Bloom plugin is loaded
|
||||
|
||||
module("checkempty")
|
||||
base.require('luadchpp')
|
||||
local adchpp = base.luadchpp
|
||||
base.require('luadchppbloom')
|
||||
local badchpp = base.luadchppbloom
|
||||
|
||||
local autil = base.require('autil')
|
||||
|
||||
local bm = badchpp.getBM()
|
||||
|
||||
-- Checking function
|
||||
local function checker (entity)
|
||||
-- Only run the check in NORMAL state
|
||||
if entity:getState() == adchpp.Entity_STATE_NORMAL then
|
||||
-- If no bloom is available hasTTH has undefined behaviour
|
||||
if bm:hasBloom(entity) and bm:hasTTH(entity,"LWPNACQDBZRYXW3VHJVCJ64QBZNGHOHHHZWCLNQ") then
|
||||
autil.reply(entity, "It's possible you have empty files in your share")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Checks for possible bllom update that may happen before getting into NORMAL
|
||||
checkold = adchpp.getCM():signalState():connect(checker)
|
||||
|
||||
-- Checks for bloom updates happening after getting into NORMAL
|
||||
checkempty = bm:signalBloomReady():connect(checker)
|
||||
91
src/plugins/Script/examples/example.lua
Normal file
91
src/plugins/Script/examples/example.lua
Normal file
@@ -0,0 +1,91 @@
|
||||
-- This is an example script that scripters might want to use as a basis for their work. It
|
||||
-- documents the bare minimum needed to make an Lua script interface with ADCH++.
|
||||
|
||||
-- For more detailed information, peeking into other, more evolved (but less documented) example
|
||||
-- scripts as well as the Doxygen documentation of ADCH++ <http://adchpp.sourceforge.net/doc> would
|
||||
-- be a good idea.
|
||||
|
||||
-- Generally, to reach member "bar" of the class "foo" of ADCH++, one has to use: adchpp.foo_bar.
|
||||
-- Examples: adchpp.Util_getCfgPath(), adchpp.AdcCommand_CMD_MSG, adchpp.Entity_STATE_NORMAL...
|
||||
|
||||
-- Feel free to use <https://answers.launchpad.net/adchpp> or <www.adcportal.com> if you need help.
|
||||
|
||||
-- When multiple scripts are loaded within the same global Lua engine, they can access each other's
|
||||
-- public exports. It is therefore a convention to run each script in its own module.
|
||||
-- Global Lua functions can still be accessed via _G, but we alias it to "base" for conveniance.
|
||||
-- Global functions can then be called as such: base.print('blah'), base.pcall(protected_func)...
|
||||
local base = _G
|
||||
|
||||
module('example') -- Give each module a unique name so they don't clash.
|
||||
|
||||
-- Import the ADCH++ Lua DLL (luadchpp.dll).
|
||||
base.require('luadchpp')
|
||||
local adchpp = base.luadchpp
|
||||
|
||||
-- Import various base sets of Lua methods. Only import those you need for your specific module.
|
||||
local io = base.require('io')
|
||||
local math = base.require('math')
|
||||
local os = base.require('os')
|
||||
local string = base.require('string')
|
||||
local table = base.require('table')
|
||||
|
||||
-- Import some utilitary Lua Scripts; these don't need to be explicitly loaded by ADCH++ (eg if
|
||||
-- using the adchppd daemon, they don't need to be referenced in Scripts.xml).
|
||||
local autil = base.require('autil')
|
||||
local json = base.require('json')
|
||||
|
||||
-- Cache pointers to some managers of ADCH++ that we frequently use.
|
||||
local cm = adchpp.getCM() -- ClientManager
|
||||
local pm = adchpp.getPM() -- PluginManager
|
||||
|
||||
-- Listeners to connect to ADCH++. Define one unique identifier for each listener (i chose to call
|
||||
-- them example_1, example_2 and so on) to make sure the variable holding the listener doesn't get
|
||||
-- collected by Lua's garbage collector until the program is over.
|
||||
|
||||
-- ClientManager::signalConnected: called when an Entity entity has just connected.
|
||||
example_1 = cm:signalConnected():connect(function(entity)
|
||||
-- Process signalConnected here.
|
||||
end)
|
||||
|
||||
-- ClientManager::signalReady: called when an Entity entity is now ready for read / write
|
||||
-- operations (TLS handshake completed).
|
||||
example_2 = cm:signalReady():connect(function(entity)
|
||||
-- Process signalReady here.
|
||||
end)
|
||||
|
||||
-- ClientManager::signalReceive: called when an AdcCommand cmd is received from Entity entity.
|
||||
example_3 = cm:signalReceive():connect(function(entity, cmd, ok)
|
||||
local res = (function(entity, cmd, ok)
|
||||
-- Skip messages that have been handled and deemed as discardable by others.
|
||||
if not ok then
|
||||
return ok
|
||||
end
|
||||
-- Process signalReceive here.
|
||||
-- Return true to let the command be dispatched, false to block it.
|
||||
end)(entity, cmd, ok)
|
||||
if not res then
|
||||
cmd:setPriority(adchpp.AdcCommand_PRIORITY_IGNORE)
|
||||
end
|
||||
return res
|
||||
end)
|
||||
|
||||
-- ClientManager::signalState: called after the state of an online Entity entity has changed.
|
||||
example_4 = cm:signalState():connect(function(entity)
|
||||
-- Process signalState here.
|
||||
end)
|
||||
|
||||
-- ClientManager::signalDisconnected: called after an Entity entity has disconnected.
|
||||
example_5 = cm:signalDisconnected():connect(function(entity)
|
||||
-- Process signalDisconnected here.
|
||||
end)
|
||||
|
||||
-- PluginManager::getCommandSignal(string): called when a +command managed by another plugin is
|
||||
-- being executed.
|
||||
example_6 = pm:getCommandSignal("blah"):connect(function(entity, list, ok)
|
||||
-- Skip messages that have been handled and deemed as discardable by others.
|
||||
if not ok then
|
||||
return ok
|
||||
end
|
||||
-- Process getCommandSignal here.
|
||||
-- Return true to let the command be executed, false to block it.
|
||||
end)
|
||||
258
src/plugins/Script/examples/history.lua
Executable file
258
src/plugins/Script/examples/history.lua
Executable file
@@ -0,0 +1,258 @@
|
||||
-- Simple history script that displays the last n history items
|
||||
-- History is persisted across restarts
|
||||
|
||||
local base = _G
|
||||
|
||||
module("history")
|
||||
base.require('luadchpp')
|
||||
local adchpp = base.luadchpp
|
||||
|
||||
base.assert(base['access'], 'access.lua must be loaded and running before history.lua')
|
||||
local access = base.access
|
||||
|
||||
-- Where to read/write history file - set to nil to disable persistent history
|
||||
local history_file = adchpp.Util_getCfgPath() .. "history.txt"
|
||||
|
||||
local os = base.require('os')
|
||||
local json = base.require('json')
|
||||
local string = base.require('string')
|
||||
local aio = base.require('aio')
|
||||
local autil = base.require('autil')
|
||||
local table = base.require('table')
|
||||
|
||||
local cm = adchpp.getCM()
|
||||
local sm = adchpp.getSM()
|
||||
local lm = adchpp.getLM()
|
||||
|
||||
local pos = 1
|
||||
local messages_saved = true
|
||||
|
||||
local messages = {}
|
||||
|
||||
access.add_setting('history_max', {
|
||||
help = "number of messages to keep for +history",
|
||||
|
||||
value = 500
|
||||
})
|
||||
|
||||
access.add_setting('history_default', {
|
||||
help = "number of messages to display in +history if the user doesn't select anything else",
|
||||
|
||||
value = 50
|
||||
})
|
||||
|
||||
access.add_setting('history_connect', {
|
||||
help = "number of messages to display to the users on connect",
|
||||
|
||||
value = 10
|
||||
})
|
||||
|
||||
access.add_setting('history_method', {
|
||||
help = "strategy used by the +history script to record messages, restart the hub after the change, 1 = use a hidden bot, 0 = direct ADCH++ interface",
|
||||
|
||||
value = 1
|
||||
})
|
||||
|
||||
access.add_setting('history_prefix', {
|
||||
help = "prefix to put before each message in +history",
|
||||
|
||||
value = "[%Y-%m-%d %H:%M:%S] "
|
||||
})
|
||||
|
||||
local function log(message)
|
||||
lm:log(_NAME, message)
|
||||
end
|
||||
|
||||
local function get_items(c)
|
||||
local items = 1
|
||||
local user = access.get_user_c(c)
|
||||
local from = user.lastofftime
|
||||
if from then
|
||||
for hist, data in base.pairs(messages) do
|
||||
if data.htime > from then
|
||||
items = items + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
return items
|
||||
end
|
||||
|
||||
local function get_lines(num)
|
||||
if num > access.settings.history_max.value then
|
||||
num = access.settings.history_max.value + 1
|
||||
end
|
||||
|
||||
local s = 1
|
||||
|
||||
if table.getn(messages) > access.settings.history_max.value then
|
||||
s = pos - access.settings.history_max.value + 1
|
||||
end
|
||||
|
||||
if num < pos then
|
||||
s = pos - num + 1
|
||||
end
|
||||
|
||||
local e = pos
|
||||
|
||||
local lines = "Displaying the last " .. (e - s) .. " messages"
|
||||
|
||||
while s <= e and messages[s] do
|
||||
lines = lines .. "\r\n" .. messages[s].message
|
||||
s = s + 1
|
||||
end
|
||||
|
||||
return lines
|
||||
end
|
||||
|
||||
access.commands.history = {
|
||||
alias = { hist = true },
|
||||
|
||||
command = function(c, parameters)
|
||||
local items
|
||||
if #parameters > 0 then
|
||||
items = base.tonumber(parameters) + 1
|
||||
if not items then
|
||||
return
|
||||
end
|
||||
else
|
||||
if access.get_level(c) > 0 then
|
||||
items = get_items(c)
|
||||
end
|
||||
end
|
||||
if not items then
|
||||
items = access.settings.history_default.value + 1
|
||||
end
|
||||
|
||||
autil.reply(c, get_lines(items))
|
||||
end,
|
||||
|
||||
help = "[lines] - display main chat messages logged by the hub (no lines=default / since last logoff)",
|
||||
|
||||
user_command = {
|
||||
name = "Chat history",
|
||||
params = { autil.ucmd_line("Number of msg's to display (empty=default / since last logoff)") }
|
||||
}
|
||||
}
|
||||
|
||||
local function save_messages()
|
||||
if not history_file then
|
||||
return
|
||||
end
|
||||
|
||||
local s = 1
|
||||
local e = pos
|
||||
if table.getn(messages) >= access.settings.history_max.value then
|
||||
s = pos - access.settings.history_max.value
|
||||
e = table.getn(messages)
|
||||
end
|
||||
|
||||
local list = {}
|
||||
while s <= e and messages[s] do
|
||||
table.insert(list, messages[s])
|
||||
s = s + 1
|
||||
end
|
||||
messages = list
|
||||
pos = table.getn(messages) + 1
|
||||
|
||||
local err = aio.save_file(history_file, json.encode(list))
|
||||
if err then
|
||||
log('History not saved: ' .. err)
|
||||
else
|
||||
messages_saved = true
|
||||
end
|
||||
end
|
||||
|
||||
local function load_messages()
|
||||
if not history_file then
|
||||
return
|
||||
end
|
||||
|
||||
local ok, list, err = aio.load_file(history_file, aio.json_loader)
|
||||
|
||||
if err then
|
||||
log('History loading: ' .. err)
|
||||
end
|
||||
if not ok then
|
||||
return
|
||||
end
|
||||
|
||||
for k, v in base.pairs(list) do
|
||||
messages[k] = v
|
||||
pos = pos + 1
|
||||
end
|
||||
end
|
||||
|
||||
local function maybe_save_messages()
|
||||
if not messages_saved then
|
||||
save_messages()
|
||||
end
|
||||
end
|
||||
|
||||
local function parse(cmd)
|
||||
if cmd:getCommand() ~= adchpp.AdcCommand_CMD_MSG or cmd:getType() ~= adchpp.AdcCommand_TYPE_BROADCAST then
|
||||
return
|
||||
end
|
||||
|
||||
local from = cm:getEntity(cmd:getFrom())
|
||||
if not from then
|
||||
return
|
||||
end
|
||||
|
||||
local nick = from:getField("NI")
|
||||
if #nick < 1 then
|
||||
return
|
||||
end
|
||||
|
||||
local now = os.date(access.settings.history_prefix.value)
|
||||
local message
|
||||
|
||||
if cmd:getParam("ME", 1) == "1" then
|
||||
message = now .. '* ' .. nick .. ' ' .. cmd:getParam(0)
|
||||
else
|
||||
message = now .. '<' .. nick .. '> ' .. cmd:getParam(0)
|
||||
end
|
||||
|
||||
messages[pos] = { message = message, htime = os.time() }
|
||||
pos = pos + 1
|
||||
|
||||
messages_saved = false
|
||||
end
|
||||
|
||||
history_1 = cm:signalState():connect(function(entity)
|
||||
if access.settings.history_connect.value > 0 and entity:getState() == adchpp.Entity_STATE_NORMAL then
|
||||
autil.reply(entity, get_lines(access.settings.history_connect.value + 1))
|
||||
end
|
||||
end)
|
||||
|
||||
load_messages()
|
||||
|
||||
if access.settings.history_method.value == 0 then
|
||||
history_1 = cm:signalReceive():connect(function(entity, cmd, ok)
|
||||
if not ok then
|
||||
return ok
|
||||
end
|
||||
|
||||
parse(cmd)
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
else
|
||||
hidden_bot = cm:createBot(function(bot, buffer)
|
||||
parse(adchpp.AdcCommand(buffer))
|
||||
end)
|
||||
hidden_bot:setField('ID', hidden_bot:getCID():toBase32())
|
||||
hidden_bot:setField('NI', _NAME .. '-hidden_bot')
|
||||
hidden_bot:setField('DE', 'Hidden bot used by the ' .. _NAME .. ' script')
|
||||
hidden_bot:setFlag(adchpp.Entity_FLAG_HIDDEN)
|
||||
cm:regBot(hidden_bot)
|
||||
|
||||
autil.on_unloaded(_NAME, function()
|
||||
hidden_bot:disconnect(adchpp.Util_REASON_PLUGIN)
|
||||
end)
|
||||
end
|
||||
|
||||
save_messages_timer = sm:addTimedJob(900000, maybe_save_messages)
|
||||
autil.on_unloading(_NAME, save_messages_timer)
|
||||
|
||||
autil.on_unloading(_NAME, maybe_save_messages)
|
||||
376
src/plugins/Script/examples/json.lua
Normal file
376
src/plugins/Script/examples/json.lua
Normal file
@@ -0,0 +1,376 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- JSON4Lua: JSON encoding / decoding support for the Lua language.
|
||||
-- json Module.
|
||||
-- Author: Craig Mason-Jones
|
||||
-- Homepage: http://json.luaforge.net/
|
||||
-- Version: 0.9.20
|
||||
-- This module is released under the The GNU General Public License (GPL).
|
||||
-- Please see LICENCE.txt for details.
|
||||
--
|
||||
-- USAGE:
|
||||
-- This module exposes two functions:
|
||||
-- encode(o)
|
||||
-- Returns the table / string / boolean / number / nil / json.null value as a JSON-encoded string.
|
||||
-- decode(json_string)
|
||||
-- Returns a Lua object populated with the data encoded in the JSON string json_string.
|
||||
--
|
||||
-- REQUIREMENTS:
|
||||
-- compat-5.1 if using Lua 5.0
|
||||
--
|
||||
-- CHANGELOG
|
||||
-- 0.9.20 Introduction of local Lua functions for private functions (removed _ function prefix).
|
||||
-- Fixed Lua 5.1 compatibility issues.
|
||||
-- Introduced json.null to have null values in associative arrays.
|
||||
-- encode() performance improvement (more than 50%) through table.concat rather than ..
|
||||
-- Introduced decode ability to ignore /**/ comments in the JSON string.
|
||||
-- 0.9.10 Fix to array encoding / decoding to correctly manage nil/null values in arrays.
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Imports and dependencies
|
||||
-----------------------------------------------------------------------------
|
||||
local math = require('math')
|
||||
local string = require("string")
|
||||
local table = require("table")
|
||||
|
||||
local base = _G
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Module declaration
|
||||
-----------------------------------------------------------------------------
|
||||
module("json")
|
||||
|
||||
-- Public functions
|
||||
|
||||
-- Private functions
|
||||
local decode_scanArray
|
||||
local decode_scanComment
|
||||
local decode_scanConstant
|
||||
local decode_scanNumber
|
||||
local decode_scanObject
|
||||
local decode_scanString
|
||||
local decode_scanWhitespace
|
||||
local encodeString
|
||||
local isArray
|
||||
local isEncodable
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- PUBLIC FUNCTIONS
|
||||
-----------------------------------------------------------------------------
|
||||
--- Encodes an arbitrary Lua object / variable.
|
||||
-- @param v The Lua object / variable to be JSON encoded.
|
||||
-- @return String containing the JSON encoding in internal Lua string format (i.e. not unicode)
|
||||
function encode (v)
|
||||
-- Handle nil values
|
||||
if v==nil then
|
||||
return "null"
|
||||
end
|
||||
|
||||
local vtype = base.type(v)
|
||||
|
||||
-- Handle strings
|
||||
if vtype=='string' then
|
||||
return '"' .. encodeString(v) .. '"' -- Need to handle encoding in string
|
||||
end
|
||||
|
||||
-- Handle booleans
|
||||
if vtype=='number' or vtype=='boolean' then
|
||||
return base.tostring(v)
|
||||
end
|
||||
|
||||
-- Handle tables
|
||||
if vtype=='table' then
|
||||
local rval = {}
|
||||
-- Consider arrays separately
|
||||
local bArray, maxCount = isArray(v)
|
||||
if bArray then
|
||||
for i = 1,maxCount do
|
||||
table.insert(rval, encode(v[i]))
|
||||
end
|
||||
else -- An object, not an array
|
||||
for i,j in base.pairs(v) do
|
||||
if isEncodable(i) and isEncodable(j) then
|
||||
table.insert(rval, '"' .. encodeString(i) .. '":' .. encode(j))
|
||||
end
|
||||
end
|
||||
end
|
||||
if bArray then
|
||||
return '[' .. table.concat(rval,',') ..']'
|
||||
else
|
||||
return '{' .. table.concat(rval,',') .. '}'
|
||||
end
|
||||
end
|
||||
|
||||
-- Handle null values
|
||||
if vtype=='function' and v==null then
|
||||
return 'null'
|
||||
end
|
||||
|
||||
base.assert(false,'encode attempt to encode unsupported type ' .. vtype .. ':' .. base.tostring(v))
|
||||
end
|
||||
|
||||
|
||||
--- Decodes a JSON string and returns the decoded value as a Lua data structure / value.
|
||||
-- @param s The string to scan.
|
||||
-- @param [startPos] Optional starting position where the JSON string is located. Defaults to 1.
|
||||
-- @param Lua object, number The object that was scanned, as a Lua table / string / number / boolean or nil,
|
||||
-- and the position of the first character after
|
||||
-- the scanned JSON object.
|
||||
function decode(s, startPos)
|
||||
startPos = startPos and startPos or 1
|
||||
startPos = decode_scanWhitespace(s,startPos)
|
||||
base.assert(startPos<=string.len(s), 'Unterminated JSON encoded object found at position in [' .. s .. ']')
|
||||
local curChar = string.sub(s,startPos,startPos)
|
||||
-- Object
|
||||
if curChar=='{' then
|
||||
return decode_scanObject(s,startPos)
|
||||
end
|
||||
-- Array
|
||||
if curChar=='[' then
|
||||
return decode_scanArray(s,startPos)
|
||||
end
|
||||
-- Number
|
||||
if string.find("+-0123456789.e", curChar, 1, true) then
|
||||
return decode_scanNumber(s,startPos)
|
||||
end
|
||||
-- String
|
||||
if curChar==[["]] or curChar==[[']] then
|
||||
return decode_scanString(s,startPos)
|
||||
end
|
||||
if string.sub(s,startPos,startPos+1)=='/*' then
|
||||
return decode(s, decode_scanComment(s,startPos))
|
||||
end
|
||||
-- Otherwise, it must be a constant
|
||||
return decode_scanConstant(s,startPos)
|
||||
end
|
||||
|
||||
--- The null function allows one to specify a null value in an associative array (which is otherwise
|
||||
-- discarded if you set the value with 'nil' in Lua. Simply set t = { first=json.null }
|
||||
function null()
|
||||
return null -- so json.null() will also return null ;-)
|
||||
end
|
||||
-----------------------------------------------------------------------------
|
||||
-- Internal, PRIVATE functions.
|
||||
-- Following a Python-like convention, I have prefixed all these 'PRIVATE'
|
||||
-- functions with an underscore.
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
--- Scans an array from JSON into a Lua object
|
||||
-- startPos begins at the start of the array.
|
||||
-- Returns the array and the next starting position
|
||||
-- @param s The string being scanned.
|
||||
-- @param startPos The starting position for the scan.
|
||||
-- @return table, int The scanned array as a table, and the position of the next character to scan.
|
||||
function decode_scanArray(s,startPos)
|
||||
local array = {} -- The return value
|
||||
local stringLen = string.len(s)
|
||||
base.assert(string.sub(s,startPos,startPos)=='[','decode_scanArray called but array does not start at position ' .. startPos .. ' in string:\n'..s )
|
||||
startPos = startPos + 1
|
||||
-- Infinite loop for array elements
|
||||
repeat
|
||||
startPos = decode_scanWhitespace(s,startPos)
|
||||
base.assert(startPos<=stringLen,'JSON String ended unexpectedly scanning array.')
|
||||
local curChar = string.sub(s,startPos,startPos)
|
||||
if (curChar==']') then
|
||||
return array, startPos+1
|
||||
end
|
||||
if (curChar==',') then
|
||||
startPos = decode_scanWhitespace(s,startPos+1)
|
||||
end
|
||||
base.assert(startPos<=stringLen, 'JSON String ended unexpectedly scanning array.')
|
||||
object, startPos = decode(s,startPos)
|
||||
table.insert(array,object)
|
||||
until false
|
||||
end
|
||||
|
||||
--- Scans a comment and discards the comment.
|
||||
-- Returns the position of the next character following the comment.
|
||||
-- @param string s The JSON string to scan.
|
||||
-- @param int startPos The starting position of the comment
|
||||
function decode_scanComment(s, startPos)
|
||||
base.assert( string.sub(s,startPos,startPos+1)=='/*', "decode_scanComment called but comment does not start at position " .. startPos)
|
||||
local endPos = string.find(s,'*/',startPos+2)
|
||||
base.assert(endPos~=nil, "Unterminated comment in string at " .. startPos)
|
||||
return endPos+2
|
||||
end
|
||||
|
||||
--- Scans for given constants: true, false or null
|
||||
-- Returns the appropriate Lua type, and the position of the next character to read.
|
||||
-- @param s The string being scanned.
|
||||
-- @param startPos The position in the string at which to start scanning.
|
||||
-- @return object, int The object (true, false or nil) and the position at which the next character should be
|
||||
-- scanned.
|
||||
function decode_scanConstant(s, startPos)
|
||||
local consts = { ["true"] = true, ["false"] = false, ["null"] = nil }
|
||||
local constNames = {"true","false","null"}
|
||||
|
||||
for i,k in base.pairs(constNames) do
|
||||
--print ("[" .. string.sub(s,startPos, startPos + string.len(k) -1) .."]", k)
|
||||
if string.sub(s,startPos, startPos + string.len(k) -1 )==k then
|
||||
return consts[k], startPos + string.len(k)
|
||||
end
|
||||
end
|
||||
base.assert(nil, 'Failed to scan constant from string ' .. s .. ' at starting position ' .. startPos)
|
||||
end
|
||||
|
||||
--- Scans a number from the JSON encoded string.
|
||||
-- (in fact, also is able to scan numeric +- eqns, which is not
|
||||
-- in the JSON spec.)
|
||||
-- Returns the number, and the position of the next character
|
||||
-- after the number.
|
||||
-- @param s The string being scanned.
|
||||
-- @param startPos The position at which to start scanning.
|
||||
-- @return number, int The extracted number and the position of the next character to scan.
|
||||
function decode_scanNumber(s,startPos)
|
||||
local endPos = startPos+1
|
||||
local stringLen = string.len(s)
|
||||
local acceptableChars = "+-0123456789.e"
|
||||
while (string.find(acceptableChars, string.sub(s,endPos,endPos), 1, true)
|
||||
and endPos<=stringLen
|
||||
) do
|
||||
endPos = endPos + 1
|
||||
end
|
||||
local stringValue = 'return ' .. string.sub(s,startPos, endPos-1)
|
||||
local stringEval = base.loadstring(stringValue)
|
||||
base.assert(stringEval, 'Failed to scan number [ ' .. stringValue .. '] in JSON string at position ' .. startPos .. ' : ' .. endPos)
|
||||
return stringEval(), endPos
|
||||
end
|
||||
|
||||
--- Scans a JSON object into a Lua object.
|
||||
-- startPos begins at the start of the object.
|
||||
-- Returns the object and the next starting position.
|
||||
-- @param s The string being scanned.
|
||||
-- @param startPos The starting position of the scan.
|
||||
-- @return table, int The scanned object as a table and the position of the next character to scan.
|
||||
function decode_scanObject(s,startPos)
|
||||
local object = {}
|
||||
local stringLen = string.len(s)
|
||||
local key, value
|
||||
base.assert(string.sub(s,startPos,startPos)=='{','decode_scanObject called but object does not start at position ' .. startPos .. ' in string:\n' .. s)
|
||||
startPos = startPos + 1
|
||||
repeat
|
||||
startPos = decode_scanWhitespace(s,startPos)
|
||||
base.assert(startPos<=stringLen, 'JSON string ended unexpectedly while scanning object.')
|
||||
local curChar = string.sub(s,startPos,startPos)
|
||||
if (curChar=='}') then
|
||||
return object,startPos+1
|
||||
end
|
||||
if (curChar==',') then
|
||||
startPos = decode_scanWhitespace(s,startPos+1)
|
||||
end
|
||||
base.assert(startPos<=stringLen, 'JSON string ended unexpectedly scanning object.')
|
||||
-- Scan the key
|
||||
key, startPos = decode(s,startPos)
|
||||
base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key)
|
||||
startPos = decode_scanWhitespace(s,startPos)
|
||||
base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key)
|
||||
base.assert(string.sub(s,startPos,startPos)==':','JSON object key-value assignment mal-formed at ' .. startPos)
|
||||
startPos = decode_scanWhitespace(s,startPos+1)
|
||||
base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key)
|
||||
value, startPos = decode(s,startPos)
|
||||
object[key]=value
|
||||
until false -- infinite loop while key-value pairs are found
|
||||
end
|
||||
|
||||
--- Scans a JSON string from the opening inverted comma or single quote to the
|
||||
-- end of the string.
|
||||
-- Returns the string extracted as a Lua string,
|
||||
-- and the position of the next non-string character
|
||||
-- (after the closing inverted comma or single quote).
|
||||
-- @param s The string being scanned.
|
||||
-- @param startPos The starting position of the scan.
|
||||
-- @return string, int The extracted string as a Lua string, and the next character to parse.
|
||||
function decode_scanString(s,startPos)
|
||||
base.assert(startPos, 'decode_scanString(..) called without start position')
|
||||
local startChar = string.sub(s,startPos,startPos)
|
||||
base.assert(startChar==[[']] or startChar==[["]],'decode_scanString called for a non-string')
|
||||
local escaped = false
|
||||
local endPos = startPos + 1
|
||||
local bEnded = false
|
||||
local stringLen = string.len(s)
|
||||
repeat
|
||||
local curChar = string.sub(s,endPos,endPos)
|
||||
if not escaped then
|
||||
if curChar==[[\]] then
|
||||
escaped = true
|
||||
else
|
||||
bEnded = curChar==startChar
|
||||
end
|
||||
else
|
||||
-- If we're escaped, we accept the current character come what may
|
||||
escaped = false
|
||||
end
|
||||
endPos = endPos + 1
|
||||
base.assert(endPos <= stringLen+1, "String decoding failed: unterminated string at position " .. endPos)
|
||||
until bEnded
|
||||
local stringValue = 'return ' .. string.sub(s, startPos, endPos-1)
|
||||
local stringEval = base.loadstring(stringValue)
|
||||
base.assert(stringEval, 'Failed to load string [ ' .. stringValue .. '] in JSON4Lua.decode_scanString at position ' .. startPos .. ' : ' .. endPos)
|
||||
return stringEval(), endPos
|
||||
end
|
||||
|
||||
--- Scans a JSON string skipping all whitespace from the current start position.
|
||||
-- Returns the position of the first non-whitespace character, or nil if the whole end of string is reached.
|
||||
-- @param s The string being scanned
|
||||
-- @param startPos The starting position where we should begin removing whitespace.
|
||||
-- @return int The first position where non-whitespace was encountered, or string.len(s)+1 if the end of string
|
||||
-- was reached.
|
||||
function decode_scanWhitespace(s,startPos)
|
||||
local whitespace=" \n\r\t"
|
||||
local stringLen = string.len(s)
|
||||
while ( string.find(whitespace, string.sub(s,startPos,startPos), 1, true) and startPos <= stringLen) do
|
||||
startPos = startPos + 1
|
||||
end
|
||||
return startPos
|
||||
end
|
||||
|
||||
--- Encodes a string to be JSON-compatible.
|
||||
-- This just involves back-quoting inverted commas, back-quotes and newlines, I think ;-)
|
||||
-- @param s The string to return as a JSON encoded (i.e. backquoted string)
|
||||
-- @return The string appropriately escaped.
|
||||
function encodeString(s)
|
||||
s = string.gsub(s,'\\','\\\\')
|
||||
s = string.gsub(s,'"','\\"')
|
||||
s = string.gsub(s,"'","\\'")
|
||||
s = string.gsub(s,'\r','\\r')
|
||||
s = string.gsub(s,'\n','\\n')
|
||||
s = string.gsub(s,'\t','\\t')
|
||||
return s
|
||||
end
|
||||
|
||||
-- Determines whether the given Lua type is an array or a table / dictionary.
|
||||
-- We consider any table an array if it has indexes 1..n for its n items, and no
|
||||
-- other data in the table.
|
||||
-- I think this method is currently a little 'flaky', but can't think of a good way around it yet...
|
||||
-- @param t The table to evaluate as an array
|
||||
-- @return boolean, number True if the table can be represented as an array, false otherwise. If true,
|
||||
-- the second returned value is the maximum
|
||||
-- number of indexed elements in the array.
|
||||
function isArray(t)
|
||||
-- Next we count all the elements, ensuring that any non-indexed elements are not-encodable
|
||||
-- (with the possible exception of 'n')
|
||||
local maxIndex = 0
|
||||
for k,v in base.pairs(t) do
|
||||
if (base.type(k)=='number' and math.floor(k)==k and 1<=k) then -- k,v is an indexed pair
|
||||
if (not isEncodable(v)) then return false end -- All array elements must be encodable
|
||||
maxIndex = math.max(maxIndex,k)
|
||||
else
|
||||
if (k=='n') then
|
||||
if v ~= table.getn(t) then return false end -- False if n does not hold the number of elements
|
||||
else -- Else of (k=='n')
|
||||
if isEncodable(v) then return false end
|
||||
end -- End of (k~='n')
|
||||
end -- End of k,v not an indexed pair
|
||||
end -- End of loop across all pairs
|
||||
return true, maxIndex
|
||||
end
|
||||
|
||||
--- Determines whether the given Lua object / table / variable can be JSON encoded. The only
|
||||
-- types that are JSON encodable are: string, boolean, number, nil, table and json.null.
|
||||
-- In this implementation, all other types are ignored.
|
||||
-- @param o The object to examine.
|
||||
-- @return boolean True if the object should be JSON encoded, false if it should be ignored.
|
||||
function isEncodable(o)
|
||||
local t = base.type(o)
|
||||
return (t=='string' or t=='boolean' or t=='number' or t=='nil' or t=='table') or (t=='function' and o==null)
|
||||
end
|
||||
86
src/plugins/Script/examples/texts.lua
Normal file
86
src/plugins/Script/examples/texts.lua
Normal file
@@ -0,0 +1,86 @@
|
||||
-- Script to display the contents of text files:
|
||||
-- * When users connect.
|
||||
-- * On user commands.
|
||||
|
||||
-- TODO Command to reload texts.
|
||||
-- TODO Allow customizing the texts via user commands.
|
||||
|
||||
local base = _G
|
||||
module('texts')
|
||||
local this = _NAME
|
||||
base.require('luadchpp')
|
||||
local adchpp = base.luadchpp
|
||||
local aio = base.require('aio')
|
||||
local autil = base.require('autil')
|
||||
|
||||
for _, dep in base.ipairs({ 'access' }) do
|
||||
base.assert(base[dep], dep .. '.lua must be loaded and running before ' .. this .. '.lua')
|
||||
end
|
||||
|
||||
-- Store the texts and their settings here. Fields:
|
||||
-- * label: The user-friendly title of this text.
|
||||
-- * path: Where the text resides.
|
||||
-- * user_command: Add a user-command for this text.
|
||||
-- * user_connect: Display contents to users when they connect.
|
||||
-- * text: The contents.
|
||||
local texts = {}
|
||||
|
||||
texts.about = {
|
||||
label = 'About this hub',
|
||||
path = adchpp.Util_getCfgPath() .. 'about.txt',
|
||||
user_command = true,
|
||||
user_connect = true,
|
||||
}
|
||||
|
||||
texts.motd = {
|
||||
label = 'Message of the day',
|
||||
path = adchpp.Util_getCfgPath() .. 'motd.txt',
|
||||
user_command = true,
|
||||
user_connect = true,
|
||||
}
|
||||
|
||||
texts.rules = {
|
||||
label = 'Rules',
|
||||
path = adchpp.Util_getCfgPath() .. 'rules.txt',
|
||||
user_command = true,
|
||||
user_connect = true,
|
||||
}
|
||||
|
||||
-- Load texts when the hub starts.
|
||||
for text_name, text_info in base.pairs(texts) do
|
||||
local ok, str, err = aio.load_file(text_info.path)
|
||||
|
||||
if not ok then
|
||||
adchpp.getLM():log(_NAME, 'Could not load the text for "' .. text_name .. '": ' .. err)
|
||||
end
|
||||
|
||||
text_info.text = str
|
||||
end
|
||||
|
||||
-- Register user commands.
|
||||
for text_name, text_info in base.pairs(texts) do
|
||||
if text_info.user_command then
|
||||
base.access.commands[text_name] = {
|
||||
command = function(c)
|
||||
if text_info.text then
|
||||
autil.reply(c, text_info.text)
|
||||
else
|
||||
autil.reply(c, 'Sorry, this information is unavailable.')
|
||||
end
|
||||
end,
|
||||
help = 'Display "' .. text_info.label .. '"',
|
||||
user_command = { name = text_info.label },
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
-- Send texts when users connect.
|
||||
texts_signal_state = adchpp.getCM():signalState():connect(function(entity)
|
||||
if entity:getState() == adchpp.Entity_STATE_NORMAL then
|
||||
for _, text_info in base.pairs(texts) do
|
||||
if text_info.user_connect and text_info.text then
|
||||
autil.reply(entity, text_info.text)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
40
src/plugins/Script/src/Engine.h
Normal file
40
src/plugins/Script/src/Engine.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2016 Jacek Sieka, arnetheduck on gmail point com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef ENGINE_H_
|
||||
#define ENGINE_H_
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
class Script;
|
||||
|
||||
typedef std::unordered_map<std::string, std::string> ParameterMap;
|
||||
|
||||
class Engine {
|
||||
public:
|
||||
virtual ~Engine() { }
|
||||
|
||||
virtual Script* loadScript(const std::string& path, const std::string& filename, const ParameterMap& parameters) = 0;
|
||||
virtual void unloadScript(Script* script, bool force = false) = 0;
|
||||
|
||||
virtual void getStats(std::string& str) const = 0;
|
||||
private:
|
||||
};
|
||||
|
||||
#endif /*ENGINE_H_*/
|
||||
45
src/plugins/Script/src/LuaCommon.h
Normal file
45
src/plugins/Script/src/LuaCommon.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2016 Jacek Sieka, arnetheduck on gmail point com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef LUACOMMON_H_
|
||||
#define LUACOMMON_H_
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename T>
|
||||
struct pointer_wrapper {
|
||||
pointer_wrapper() : t(0) { }
|
||||
explicit pointer_wrapper(T* t_) : t(t_) { }
|
||||
|
||||
operator T*() { return t; }
|
||||
T* t;
|
||||
};
|
||||
}
|
||||
namespace luabind {
|
||||
template<typename T>
|
||||
pointer_wrapper<const T>*
|
||||
get_const_holder(pointer_wrapper<T>*) { return 0; }
|
||||
template<typename T>
|
||||
T* get_pointer(pointer_wrapper<T>& p) {
|
||||
return (T*)p;
|
||||
}
|
||||
}
|
||||
|
||||
#include <luabind/luabind.hpp>
|
||||
|
||||
#endif /*LUACOMMON_H_*/
|
||||
136
src/plugins/Script/src/LuaEngine.cpp
Normal file
136
src/plugins/Script/src/LuaEngine.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2016 Jacek Sieka, arnetheduck on gmail point com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "stdinc.h"
|
||||
#include "LuaEngine.h"
|
||||
|
||||
#include "LuaScript.h"
|
||||
|
||||
#include <adchpp/PluginManager.h>
|
||||
#include <adchpp/File.h>
|
||||
#include <adchpp/Util.h>
|
||||
#include <adchpp/Core.h>
|
||||
|
||||
extern "C" {
|
||||
#include <lua.h>
|
||||
#include <lualib.h>
|
||||
#include <lauxlib.h>
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace {
|
||||
void prepare_cpath(lua_State* L, const string& path) {
|
||||
lua_getfield(L, LUA_GLOBALSINDEX, "package");
|
||||
if (!lua_istable(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
return;
|
||||
}
|
||||
lua_getfield(L, -1, "cpath");
|
||||
if (!lua_isstring(L, -1)) {
|
||||
lua_pop(L, 2);
|
||||
return;
|
||||
}
|
||||
|
||||
string oldpath = lua_tostring(L, -1);
|
||||
oldpath += ";" + path + "?.so";
|
||||
lua_pushstring(L, oldpath.c_str());
|
||||
lua_setfield(L, -3, "cpath");
|
||||
|
||||
// Pop table
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
|
||||
void setScriptPath(lua_State* L, const string& path) {
|
||||
lua_pushstring(L, path.c_str());
|
||||
lua_setglobal(L, "scriptPath");
|
||||
}
|
||||
}
|
||||
|
||||
LuaEngine::LuaEngine(Core &core) : core(core) {
|
||||
l = lua_open();
|
||||
luaL_openlibs(l);
|
||||
lua_pushlightuserdata(l, &core);
|
||||
lua_setglobal(l, "currentCore");
|
||||
prepare_cpath(l, core.getPluginManager().getPluginPath());
|
||||
|
||||
setScriptPath(l, Util::emptyString);
|
||||
}
|
||||
|
||||
LuaEngine::~LuaEngine() {
|
||||
std::vector<LuaScript*>::reverse_iterator it;
|
||||
while((it = scripts.rbegin()) != scripts.rend())
|
||||
unloadScript(*it, true);
|
||||
|
||||
if(l)
|
||||
lua_close(l);
|
||||
}
|
||||
|
||||
/// @todo lambda
|
||||
void LuaEngine::loadScript_(const string& path, const string& filename, LuaScript* script) {
|
||||
script->loadFile(path, filename);
|
||||
scripts.push_back(script);
|
||||
}
|
||||
|
||||
Script* LuaEngine::loadScript(const string& path, const string& filename, const ParameterMap&) {
|
||||
setScriptPath(l, File::makeAbsolutePath(path));
|
||||
|
||||
if(call("loading", filename))
|
||||
return 0;
|
||||
|
||||
LuaScript* script = new LuaScript(this);
|
||||
core.getPluginManager().attention(std::bind(&LuaEngine::loadScript_, this, path, filename, script));
|
||||
return script;
|
||||
}
|
||||
|
||||
void LuaEngine::unloadScript(Script* s, bool force) {
|
||||
if(call("unloading", dynamic_cast<LuaScript*>(s)->filename) && !force)
|
||||
return;
|
||||
|
||||
scripts.erase(remove(scripts.begin(), scripts.end(), s), scripts.end());
|
||||
delete s;
|
||||
}
|
||||
|
||||
void LuaEngine::getStats(string& str) const {
|
||||
str += "Lua engine\n";
|
||||
str += "\tUsed memory: " + Util::toString(lua_gc(l, LUA_GCCOUNT, 0)) + " KiB\n";
|
||||
str += "The following Lua scripts are loaded:\n";
|
||||
for(vector<LuaScript*>::const_iterator i = scripts.begin(); i != scripts.end(); ++i) {
|
||||
str += "\t";
|
||||
(*i)->getStats(str);
|
||||
}
|
||||
}
|
||||
|
||||
bool LuaEngine::call(const string& f, const string& arg) {
|
||||
lua_getfield(l, LUA_GLOBALSINDEX, f.c_str());
|
||||
if(!lua_isfunction(l, -1)) {
|
||||
lua_pop(l, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
lua_pushstring(l, arg.c_str());
|
||||
|
||||
if(lua_pcall(l, 1, 1, 0) != 0) {
|
||||
lua_pop(l, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ret = lua_toboolean(l, -1);
|
||||
lua_pop(l, 1);
|
||||
return ret;
|
||||
}
|
||||
50
src/plugins/Script/src/LuaEngine.h
Normal file
50
src/plugins/Script/src/LuaEngine.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2016 Jacek Sieka, arnetheduck on gmail point com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef LUAENGINE_H_
|
||||
#define LUAENGINE_H_
|
||||
|
||||
#include <adchpp/forward.h>
|
||||
|
||||
#include "Engine.h"
|
||||
|
||||
struct lua_State;
|
||||
class LuaScript;
|
||||
|
||||
class LuaEngine : public Engine {
|
||||
public:
|
||||
LuaEngine(adchpp::Core &core);
|
||||
virtual ~LuaEngine();
|
||||
|
||||
virtual Script* loadScript(const std::string& path, const std::string& filename, const ParameterMap& parameters);
|
||||
virtual void unloadScript(Script* script, bool force = false);
|
||||
|
||||
virtual void getStats(std::string& str) const;
|
||||
|
||||
private:
|
||||
friend class LuaScript;
|
||||
|
||||
bool call(const std::string& f, const std::string& arg);
|
||||
void loadScript_(const std::string& path, const std::string& filename, LuaScript* script);
|
||||
|
||||
lua_State* l;
|
||||
|
||||
std::vector<LuaScript*> scripts;
|
||||
adchpp::Core &core;
|
||||
};
|
||||
#endif /*LUAENGINE_H_*/
|
||||
88
src/plugins/Script/src/LuaScript.cpp
Normal file
88
src/plugins/Script/src/LuaScript.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2016 Jacek Sieka, arnetheduck on gmail point com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "stdinc.h"
|
||||
#include "LuaScript.h"
|
||||
|
||||
#include "LuaEngine.h"
|
||||
|
||||
#include <adchpp/File.h>
|
||||
#include <adchpp/LogManager.h>
|
||||
#include <adchpp/Util.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
#else
|
||||
#ifndef MAX_PATH
|
||||
#define MAX_PATH PATH_MAX
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
#include <lua.h>
|
||||
#include <lualib.h>
|
||||
#include <lauxlib.h>
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
||||
const string LuaScript::className = "LuaScript";
|
||||
|
||||
LuaScript::LuaScript(Engine* engine) : Script(engine) {
|
||||
}
|
||||
|
||||
LuaScript::~LuaScript() {
|
||||
getEngine()->call("unloaded", filename);
|
||||
}
|
||||
|
||||
void LuaScript::loadFile(const string& path, const string& filename_) {
|
||||
filename = filename_;
|
||||
char old_dir[MAX_PATH];
|
||||
if(!getcwd(old_dir, MAX_PATH)) {
|
||||
old_dir[0] = 0;
|
||||
}
|
||||
|
||||
auto absPath = File::makeAbsolutePath(path);
|
||||
if(chdir(absPath.c_str()) != 0) {
|
||||
//LOG(className, "Unable to change to directory " + absPath);
|
||||
} else {
|
||||
int error = luaL_loadfile(getEngine()->l, filename.c_str()) || lua_pcall(getEngine()->l, 0, 0, 0);
|
||||
|
||||
if(error) {
|
||||
fprintf(stderr, "Error loading file: %s\n", lua_tostring(getEngine()->l, -1));
|
||||
//LOG(className, string("Error loading file: ") + lua_tostring(getEngine()->l, -1));
|
||||
} else {
|
||||
//LOG(className, "Loaded " + filename);
|
||||
getEngine()->call("loaded", filename);
|
||||
}
|
||||
|
||||
if(old_dir[0]) {
|
||||
chdir(old_dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LuaScript::getStats(string& str) const {
|
||||
str += filename + "\n";
|
||||
}
|
||||
|
||||
LuaEngine* LuaScript::getEngine() const {
|
||||
return static_cast<LuaEngine*>(engine);
|
||||
}
|
||||
|
||||
44
src/plugins/Script/src/LuaScript.h
Normal file
44
src/plugins/Script/src/LuaScript.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2016 Jacek Sieka, arnetheduck on gmail point com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef LUASCRIPT_H_
|
||||
#define LUASCRIPT_H_
|
||||
|
||||
#include "Script.h"
|
||||
|
||||
class Engine;
|
||||
class LuaEngine;
|
||||
|
||||
class LuaScript : public Script {
|
||||
public:
|
||||
LuaScript(Engine* engine);
|
||||
virtual ~LuaScript();
|
||||
|
||||
void loadFile(const std::string& path, const std::string& filename);
|
||||
|
||||
void getStats(std::string& str) const;
|
||||
|
||||
static const std::string className;
|
||||
|
||||
private:
|
||||
friend class LuaEngine;
|
||||
LuaEngine* getEngine() const;
|
||||
std::string filename;
|
||||
};
|
||||
|
||||
#endif /*LUASCRIPT_H_*/
|
||||
13
src/plugins/Script/src/SConscript
Normal file
13
src/plugins/Script/src/SConscript
Normal file
@@ -0,0 +1,13 @@
|
||||
Import('dev source_path setLuaEnv')
|
||||
|
||||
env, target, sources = dev.prepare_build(source_path, 'Script', shared_precompiled_header = 'stdinc')
|
||||
env['SHLIBPREFIX'] = ''
|
||||
|
||||
env.Append(CPPPATH = ['.', '#'])
|
||||
env.Append(LIBS = ['adchpp'])
|
||||
|
||||
setLuaEnv(env)
|
||||
|
||||
ret = env.SharedLibrary(target, sources)
|
||||
|
||||
Return('ret')
|
||||
34
src/plugins/Script/src/Script.h
Normal file
34
src/plugins/Script/src/Script.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2016 Jacek Sieka, arnetheduck on gmail point com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef SCRIPT_H_
|
||||
#define SCRIPT_H_
|
||||
|
||||
class Engine;
|
||||
|
||||
class Script {
|
||||
public:
|
||||
Script(Engine* engine_) : engine(engine_) { }
|
||||
virtual ~Script() { }
|
||||
|
||||
void unload();
|
||||
|
||||
protected:
|
||||
Engine* engine;
|
||||
};
|
||||
#endif /*SCRIPT_H_*/
|
||||
103
src/plugins/Script/src/ScriptManager.cpp
Normal file
103
src/plugins/Script/src/ScriptManager.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2016 Jacek Sieka, arnetheduck on gmail point com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "stdinc.h"
|
||||
#include "ScriptManager.h"
|
||||
|
||||
#include "Engine.h"
|
||||
#include "LuaEngine.h"
|
||||
|
||||
#include <adchpp/SimpleXML.h>
|
||||
#include <adchpp/File.h>
|
||||
#include <adchpp/LogManager.h>
|
||||
#include <adchpp/Util.h>
|
||||
#include <adchpp/AdcCommand.h>
|
||||
#include <adchpp/Client.h>
|
||||
#include <adchpp/PluginManager.h>
|
||||
#include <adchpp/Core.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace std::placeholders;
|
||||
|
||||
const string ScriptManager::className = "ScriptManager";
|
||||
|
||||
ScriptManager::ScriptManager(Core &core) : core(core) {
|
||||
LOG(className, "Starting");
|
||||
|
||||
auto &pm = core.getPluginManager();
|
||||
reloadConn = manage(pm.onCommand("reload", std::bind(&ScriptManager::onReload, this, _1)));
|
||||
statsConn = manage(pm.onCommand("stats", std::bind(&ScriptManager::onStats, this, _1)));
|
||||
}
|
||||
|
||||
ScriptManager::~ScriptManager() {
|
||||
LOG(className, "Shutting down");
|
||||
clearEngines();
|
||||
}
|
||||
|
||||
void ScriptManager::clearEngines() {
|
||||
engines.clear();
|
||||
}
|
||||
|
||||
void ScriptManager::load() {
|
||||
try {
|
||||
SimpleXML xml;
|
||||
|
||||
xml.fromXML(File(core.getConfigPath() + "Script.xml", File::READ).read());
|
||||
xml.stepIn();
|
||||
while(xml.findChild("Engine")) {
|
||||
const std::string& scriptPath = xml.getChildAttrib("scriptPath");
|
||||
const std::string& language = xml.getChildAttrib("language");
|
||||
|
||||
if(language.empty() || language == "lua") {
|
||||
engines.push_back(std::unique_ptr<LuaEngine>(new LuaEngine(core)));
|
||||
} else {
|
||||
LOG(className, "Unrecognized language " + language);
|
||||
continue;
|
||||
}
|
||||
|
||||
xml.stepIn();
|
||||
while(xml.findChild("Script")) {
|
||||
engines.back()->loadScript(scriptPath, xml.getChildData(), ParameterMap());
|
||||
}
|
||||
xml.stepOut();
|
||||
}
|
||||
xml.stepOut();
|
||||
} catch(const Exception& e) {
|
||||
LOG(className, "Failed to load settings: " + e.getError());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::reload() {
|
||||
clearEngines();
|
||||
load();
|
||||
}
|
||||
|
||||
void ScriptManager::onReload(Entity& c) {
|
||||
core.getPluginManager().attention(std::bind(&ScriptManager::reload, this));
|
||||
c.send(AdcCommand(AdcCommand::CMD_MSG).addParam("Reloading scripts"));
|
||||
}
|
||||
|
||||
void ScriptManager::onStats(Entity& c) {
|
||||
string tmp("Currently loaded scripts:\n");
|
||||
for(auto i = engines.begin(), iend = engines.end(); i != iend; ++i) {
|
||||
(*i)->getStats(tmp);
|
||||
}
|
||||
|
||||
c.send(AdcCommand(AdcCommand::CMD_MSG).addParam(tmp));
|
||||
}
|
||||
65
src/plugins/Script/src/ScriptManager.h
Normal file
65
src/plugins/Script/src/ScriptManager.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2016 Jacek Sieka, arnetheduck on gmail point com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef SCRIPT_MANAGER_H
|
||||
#define SCRIPT_MANAGER_H
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif // _MSC_VER > 1000
|
||||
|
||||
#include <adchpp/Exception.h>
|
||||
#include <adchpp/ClientManager.h>
|
||||
#include <adchpp/Plugin.h>
|
||||
|
||||
STANDARD_EXCEPTION(ScriptException);
|
||||
class Engine;
|
||||
|
||||
namespace adchpp {
|
||||
class SimpleXML;
|
||||
class Client;
|
||||
class AdcCommand;
|
||||
}
|
||||
|
||||
class ScriptManager : public Plugin {
|
||||
public:
|
||||
ScriptManager(Core &core);
|
||||
virtual ~ScriptManager();
|
||||
|
||||
virtual int getVersion() { return 0; }
|
||||
|
||||
void load();
|
||||
|
||||
static const std::string className;
|
||||
private:
|
||||
|
||||
std::vector<std::unique_ptr<Engine>> engines;
|
||||
|
||||
void reload();
|
||||
void clearEngines();
|
||||
|
||||
ClientManager::SignalReceive::ManagedConnection reloadConn;
|
||||
ClientManager::SignalReceive::ManagedConnection statsConn;
|
||||
|
||||
void onReload(Entity& c);
|
||||
void onStats(Entity& c);
|
||||
|
||||
Core &core;
|
||||
};
|
||||
|
||||
#endif //ACCESSMANAGER_H
|
||||
46
src/plugins/Script/src/ScriptPlugin.cpp
Normal file
46
src/plugins/Script/src/ScriptPlugin.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2016 Jacek Sieka, arnetheduck on gmail point com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "stdinc.h"
|
||||
#include "ScriptManager.h"
|
||||
|
||||
#include <adchpp/PluginManager.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
BOOL APIENTRY DllMain(HANDLE /*hModule */, DWORD /* reason*/, LPVOID /*lpReserved*/) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
int PLUGIN_API pluginGetVersion() { return PLUGINVERSION; }
|
||||
|
||||
int PLUGIN_API pluginLoad(PluginManager *pm) {
|
||||
auto sm = make_shared<ScriptManager>(pm->getCore());
|
||||
sm->load();
|
||||
|
||||
pm->registerPlugin("ScriptManager", sm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PLUGIN_API pluginUnload() {
|
||||
}
|
||||
}
|
||||
19
src/plugins/Script/src/stdinc.cpp
Normal file
19
src/plugins/Script/src/stdinc.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2016 Jacek Sieka, arnetheduck on gmail point com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "stdinc.h"
|
||||
27
src/plugins/Script/src/stdinc.h
Normal file
27
src/plugins/Script/src/stdinc.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2016 Jacek Sieka, arnetheduck on gmail point com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef SCRIPT_STDINC_H
|
||||
#define SCRIPT_STDINC_H
|
||||
|
||||
#include <adchpp/adchpp.h>
|
||||
#include <adchpp/common.h>
|
||||
|
||||
using namespace adchpp;
|
||||
|
||||
#endif //ACCESS_STDINC_H
|
||||
Reference in New Issue
Block a user