mirror of
https://github.com/martinrotter/rssguard.git
synced 2025-01-01 02:48:05 +01:00
utility functions for filters
This commit is contained in:
parent
2c3fa36b9a
commit
5ecd47daec
@ -30,7 +30,7 @@
|
||||
<url type="donation">https://martinrotter.github.io/donate/</url>
|
||||
<content_rating type="oars-1.1" />
|
||||
<releases>
|
||||
<release version="3.9.0" date="2021-03-10"/>
|
||||
<release version="3.9.0" date="2021-03-11"/>
|
||||
</releases>
|
||||
<content_rating type="oars-1.0">
|
||||
<content_attribute id="violence-cartoon">none</content_attribute>
|
||||
|
@ -363,7 +363,7 @@ Here is the rough workflow which is performed when you hit `Feeds & categories -
|
||||
* c. Sequentially, for each downloaded message, do:
|
||||
* 1. Sanitize title of the message. This includes replacing all non-breaking spaces with normal spaces, removing all leading spaces, replacing all multiple consecutive spaces with single space. Contents of message are converted from [percent-encoding](https://en.wikipedia.org/wiki/Percent-encoding).
|
||||
* 2. Run all [message filters](#message-filtering), one by one, one the message. Cache read/important message attributes (or labels changes) changed by filters to queue which is later synchronized back to online feed service.
|
||||
* 3. Store the message into RSS Guard's database, creating completely new DB entry for it, or replacing existing message. **Note that two messages are considered as the same message if they have identical URL, author and title and they belong to the same feed.** This does not stand for synchronized feeds (TT-RSS, Inoreader and others) where each message has assigned special ID which identifies the message uniquely.
|
||||
* 3. Store the message into RSS Guard's [database](#database-backends), creating completely new DB entry for it, or replacing existing message. **Note that two messages are considered as the same message if they have identical URL, author and title and they belong to the same feed.** This does not stand for synchronized feeds (TT-RSS, Inoreader and others) where each message has assigned special ID which identifies the message uniquely.
|
||||
|
||||
## Generating debug log file
|
||||
If you run into problems with RSS Guard and you need your problems fixed, you should provide log file from the time when problem occurred. RSS Guard writes all important information to standard output, which is usually calling terminal.
|
||||
|
@ -24,6 +24,8 @@ Each message is accessible in your script via global variable named `msg` of typ
|
||||
|
||||
You can use [special placeholders](Documentation.md#data-placeholder) within message filter.
|
||||
|
||||
Also, there is a special variable named `utils`. This variable is of type `FilterUtils` and offers some useful utility [functions](#utils) for you to use in your filters.
|
||||
|
||||
RSS Guard also offers list of labels assigned to each message. You can therefore do actions in your filtering script based on which labels are assigned to the message. The property is called `assignedLabels` and is array of `Label` objects. If you change assigned labels to the message, then the change will be eventually synchronized back to server if respective plugin supports it.
|
||||
|
||||
Passed message also offers special function
|
||||
@ -71,20 +73,26 @@ Here is the reference of methods and properties of some types available in your
|
||||
### `FilteringAction` enum
|
||||
| Enumerant name | Integer value | Description |
|
||||
|---|---|---|
|
||||
| Accept | 1 | Message is accepted and will be added to DB or updated in DB. |
|
||||
| Ignore | 2 | Message is ignored and will be **NOT** added to DB or updated in DB, but is not purged from DB if already exists. |
|
||||
| Purge | 4 | Existing message is purged from the DB completely. |
|
||||
| `Accept` | 1 | Message is accepted and will be added to DB or updated in DB. |
|
||||
| `Ignore` | 2 | Message is ignored and will be **NOT** added to DB or updated in DB, but is not purged from DB if already exists. |
|
||||
| `Purge` | 4 | Existing message is purged from the DB completely. |
|
||||
|
||||
Note that `MessageObject` attributes which can be synchronized back to service are synchronized even if you return `Purge` or `Ignore`. In other words: even if you filter ignores the message you can still tweak its properties which will get synchronized back to your server.
|
||||
|
||||
### `DuplicationAttributeCheck` enum
|
||||
| Enumerant name | Integer value | Description |
|
||||
|---|---|---|
|
||||
| SameTitle | 1 | Check if message has same title as some another messages. |
|
||||
| SameUrl | 2 | Check if message has same URL as some another messages. |
|
||||
| SameAuthor | 4 | Check if message has same author as some another messages. |
|
||||
| SameDateCreated | 8 | Check if message has same date of creation as some another messages. |
|
||||
| AllFeedsSameAccount | 16 | Perform the check across all feeds from your account, not just "current" feed. |
|
||||
| `SameTitle` | 1 | Check if message has same title as some another messages. |
|
||||
| `SameUrl` | 2 | Check if message has same URL as some another messages. |
|
||||
| `SameAuthor` | 4 | Check if message has same author as some another messages. |
|
||||
| `SameDateCreated` | 8 | Check if message has same date of creation as some another messages. |
|
||||
| `AllFeedsSameAccount` | 16 | Perform the check across all feeds from your account, not just "current" feed. |
|
||||
|
||||
## Utils
|
||||
| Method | How to call | Description |
|
||||
|---|---|---|
|
||||
| `String hostname()` | `utils.hostname()` | Returns name of your PC. |
|
||||
| `String fromXmlToJson(String)` | `utils.fromXmlToJson('<h1>hello</h1>')` | Converts `XML` string into `JSON`. |
|
||||
|
||||
## Examples
|
||||
Accept only messages from "Bob" while also marking them important.
|
||||
|
72
src/librssguard/core/filterutils.cpp
Executable file
72
src/librssguard/core/filterutils.cpp
Executable file
@ -0,0 +1,72 @@
|
||||
// For license of this file, see <project-root-folder>/LICENSE.md.
|
||||
|
||||
#include "core/filterutils.h"
|
||||
|
||||
#include "definitions/definitions.h"
|
||||
|
||||
#include <QDomDocument>
|
||||
#include <QHostInfo>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
|
||||
FilterUtils::FilterUtils(QObject* parent) : QObject(parent) {}
|
||||
|
||||
FilterUtils::~FilterUtils() {
|
||||
qDebugNN << "Destroying FilterUtils instance.";
|
||||
}
|
||||
|
||||
QString FilterUtils::hostname() const {
|
||||
return QHostInfo::localHostName();
|
||||
}
|
||||
|
||||
QString jsonEscapeString(const QString& s) {
|
||||
return QString(QJsonDocument(QJsonArray() << s).toJson(QJsonDocument::JsonFormat::Compact)).mid(2).chopped(2);
|
||||
}
|
||||
|
||||
QString jsonProcessXmlElement(const QDomElement& elem) {
|
||||
QStringList attrs;
|
||||
|
||||
for (int i = 0; i < elem.attributes().size(); i++) {
|
||||
attrs << QSL("\"%1\": \"%2\"").arg(jsonEscapeString(elem.attributes().item(i).toAttr().name()),
|
||||
jsonEscapeString(elem.attributes().item(i).toAttr().value()));
|
||||
}
|
||||
|
||||
QStringList elems;
|
||||
|
||||
for (int i = 0; i < elem.childNodes().size(); i++) {
|
||||
if (!elem.childNodes().at(i).isElement()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
elems << QSL("\"%1\": %2").arg(elem.childNodes().at(i).toElement().tagName(),
|
||||
jsonProcessXmlElement(elem.childNodes().at(i).toElement()));
|
||||
}
|
||||
|
||||
if (attrs.isEmpty()) {
|
||||
if (elems.isEmpty()) {
|
||||
return QSL("\"%1\"").arg(jsonEscapeString(elem.text()));
|
||||
}
|
||||
else {
|
||||
return QSL("{%1}").arg(elems.join(QSL(",\n")));
|
||||
}
|
||||
}
|
||||
else if (elems.isEmpty()) {
|
||||
return QSL("{%1, \"__text\": \"%2\"}").arg(attrs.join(QSL(",\n")),
|
||||
jsonEscapeString(elem.text()));
|
||||
}
|
||||
else {
|
||||
return QSL("{%1, %2}").arg(attrs.join(QSL(",\n")),
|
||||
elems.join(QSL(",\n")));
|
||||
}
|
||||
}
|
||||
|
||||
QString FilterUtils::fromXmlToJson(const QString& xml) const {
|
||||
QDomDocument xml_doc;
|
||||
|
||||
xml_doc.setContent(xml);
|
||||
|
||||
QString json = QSL("%1").arg(jsonProcessXmlElement(xml_doc.documentElement()));
|
||||
|
||||
return QSL("{\"%1\": %2}").arg(xml_doc.documentElement().tagName(),
|
||||
json);
|
||||
}
|
23
src/librssguard/core/filterutils.h
Executable file
23
src/librssguard/core/filterutils.h
Executable file
@ -0,0 +1,23 @@
|
||||
// For license of this file, see <project-root-folder>/LICENSE.md.
|
||||
|
||||
#ifndef FILTERUTILS_H
|
||||
#define FILTERUTILS_H
|
||||
|
||||
#include <QDomElement>
|
||||
#include <QObject>
|
||||
|
||||
class FilterUtils : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FilterUtils(QObject* parent = nullptr);
|
||||
~FilterUtils();
|
||||
|
||||
// Returns hostname or empty string if failed.
|
||||
Q_INVOKABLE QString hostname() const;
|
||||
|
||||
// Converts XML -> JSON or returns empty string if failed.
|
||||
Q_INVOKABLE QString fromXmlToJson(const QString& xml) const;
|
||||
};
|
||||
|
||||
#endif // FILTERUTILS_H
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include "core/messagefilter.h"
|
||||
|
||||
#include "core/filterutils.h"
|
||||
#include "core/message.h"
|
||||
#include "exceptions/filteringexception.h"
|
||||
#include "miscellaneous/application.h"
|
||||
@ -52,15 +53,21 @@ void MessageFilter::setScript(const QString& script) {
|
||||
|
||||
void MessageFilter::initializeFilteringEngine(QJSEngine& engine, MessageObject* message_wrapper) {
|
||||
engine.installExtensions(QJSEngine::Extension::ConsoleExtension);
|
||||
engine.globalObject().setProperty("MSG_ACCEPT", int(MessageObject::FilteringAction::Accept));
|
||||
engine.globalObject().setProperty("MSG_IGNORE", int(MessageObject::FilteringAction::Ignore));
|
||||
engine.globalObject().setProperty(QSL("MSG_ACCEPT"), int(MessageObject::FilteringAction::Accept));
|
||||
engine.globalObject().setProperty(QSL("MSG_IGNORE"), int(MessageObject::FilteringAction::Ignore));
|
||||
|
||||
// Register the wrapper.
|
||||
auto js_object = engine.newQObject(message_wrapper);
|
||||
auto js_meta_object = engine.newQMetaObject(&message_wrapper->staticMetaObject);
|
||||
|
||||
engine.globalObject().setProperty("msg", js_object);
|
||||
engine.globalObject().setProperty(QSL("msg"), js_object);
|
||||
engine.globalObject().setProperty(message_wrapper->staticMetaObject.className(), js_meta_object);
|
||||
|
||||
// Register "utils".
|
||||
auto* utils = new FilterUtils(&engine);
|
||||
auto js_utils = engine.newQObject(utils);
|
||||
|
||||
engine.globalObject().setProperty(QSL("utils"), js_utils);
|
||||
}
|
||||
|
||||
void MessageFilter::setId(int id) {
|
||||
|
@ -35,6 +35,7 @@ mac|os2|win32 {
|
||||
HEADERS += core/feeddownloader.h \
|
||||
core/feedsmodel.h \
|
||||
core/feedsproxymodel.h \
|
||||
core/filterutils.h \
|
||||
core/message.h \
|
||||
core/messagefilter.h \
|
||||
core/messageobject.h \
|
||||
@ -217,6 +218,7 @@ HEADERS += core/feeddownloader.h \
|
||||
SOURCES += core/feeddownloader.cpp \
|
||||
core/feedsmodel.cpp \
|
||||
core/feedsproxymodel.cpp \
|
||||
core/filterutils.cpp \
|
||||
core/message.cpp \
|
||||
core/messagefilter.cpp \
|
||||
core/messageobject.cpp \
|
||||
|
Loading…
Reference in New Issue
Block a user