1# �� RSS Guard Documentation ��
2
3Welcome to RSS Guard documentation. You can find everything about the application right here. \
4There is a [Discord server](https://discord.gg/7xbVMPPNqH) for user communication.
5
6## Table of Contents
7- [What is RSS Guard?](#wirss)
8- [Downloads](#dwn)
9- [Supported Operating Systems](#sos)
10- [Major Features](#mfe)
11    - [Supported Feed Readers](#sfr)
12    - [Article Filtering](#fltr)
13    - [Websites Scraping](#scrap)
14    - [Notifications](#notif)
15    - [Database Backends](#datab)
16    - [User Data Portability](#userd)
17    - [Built-in Web Browser with AdBlock](#webb)
18- [Minor Features](#mife)
19    - [Files Downloader](#downl)
20    - [Labels](#lbls)
21    - [Skins](#skin)
22    - [GUI Tweaking](#guit)
23    - [Command Line Interface (CLI)](#cli)
24- [For Contributors and Other Topics](#contrib)
25    - [Donations](#donat)
26    - [Compiling RSS Guard](#compil)
27    - [Plugin API](#papi)
28    - [Reporting Bugs or Feature Requests](#reprt)
29    - [Localization](#locali)
30    - [Migrating data](#migratt)
31
32<hr style="margin: 40px;"/>
33
34## What is RSS Guard? <a id="wirss" />
35RSS Guard is an [open-source](https://en.wikipedia.org/wiki/Open_source) [cross-platform](#sos) [multi-protocol](#sfr) desktop feed reader. It is able to fetch almost any of the known feed formats, including RSS/RDF/ATOM/JSON. RSS Guard is developed on top of the [Qt library](http://qt-project.org).
36
37## Downloads <a id="dwn"></a>
38Official place to download RSS Guard is at [Github Releases](https://github.com/martinrotter/rssguard/releases). You can also download the [development (beta) build](https://github.com/martinrotter/rssguard/releases/tag/devbuild), which is updated automatically every time the source code is updated.
39
40Also, RSS Guard is available for [many Linux distributions](https://repology.org/project/rssguard/versions), and even via [Flathub](https://flathub.org/apps/details/com.github.rssguard).
41
42I highly recommend to download RSS Guard only from reputable sources.
43
44## Supported Operating Systems <a id="sos"></a>
45RSS Guard is a cross-platform application, and at this point it is known to work on:
46* Windows 7+
47* GNU/Linux (including PinePhone and other Linux-based phone operating systems)
48* macOS 10.10+
49* OS/2 (ArcaOS, eComStation)
50
51## Major Features <a id="mfe"></a>
52
53### Supported Feed Readers <a id="sfr"></a>
54RSS Guard is a multi-account application and supports many web-based feed readers via [built-in plugins](#papi). One of the plugins, of course, provides the support for standard **RSS/ATOM/JSON** feeds with the set of features everyone would expect from classic feed reader, like OPML support, etc.
55
56I organized the supported web-based feed readers into an elegant table:
57
58| Service           | Two-way Synchronization | [Intelligent Synchronization Algorithm](#intel) (ISA) <sup>1</sup>  | Synchronized Labels <sup>2</sup> <a id="sfrl"></a>    | OAuth     |
59| :---              | :---:  | :---: | :---: | :---:
60| Feedly            | ✅ | ❌ | ✅ | ✅ (only for official binaries)
61| Gmail             | ✅ | ❌ | ❌ | ✅
62| Google Reader API <sup>3</sup> | ✅ | ✅ | ✅ | ✅ (only for Inoreader)
63| Nextcloud News    | ✅ | ❌ | ❌ | ❌
64| Tiny Tiny RSS     | ✅ | ❌ | ✅ | ❌
65
66<sup>1</sup> Some plugins support next-gen intelligent synchronization algorithm (ISA) which has some benefits, as it usually offers superior synchronization speed, and transfers much less data over your network connection. <a id="intel"></a>
67
68<img alt="alt-img" src="images/intel.png" width="350px">
69
70With ISA, RSS Guard only downloads articles which are new or were updated. While the old algorithm usually always fetch all available articles, even if they are not needed, which leads to unnecessary overload of your network connection and RSS Guard.
71
72<sup>2</sup> Note that [labels](#lbls) are supported for all plugins, but for some plugins they are local-only, and are not synchronized with the service. Usually because service itself does not support the feature.
73
74<sup>3</sup> Tested services are:
75* Bazqux
76* Reedah
77* Inoreader
78* TheOldReader
79* FreshRSS
80
81### Article Filtering <a id="fltr"></a>
82Sometimes you need to tweak incoming article - mark them starred, remove ads from their contents or simply ignore them. That's where filtering feature comes in.
83
84<img alt="alt-img" src="images/filters-dialog.png" width="600px">
85
86#### Writing article filter
87Article filters are fully scriptable and consist of arbitrary JavaScript code which must provide function with prototype:
88
89```js
90function filterMessage() { }
91```
92
93The function should be fast enough, and must return values which belong to enumeration [`FilteringAction`](#FilteringAction-enum). You can either use direct numerical value of each enumerant, for example `2`, or you can use self-descriptive name, for example `MessageObject.Ignore`. There are also names `MSG_ACCEPT` and `MSG_IGNORE` as aliases for `MessageObject.Accept` and `MessageObject.Ignore`.
94
95Each message/article is accessible in your script via global variable named `msg` of type `MessageObject`, see [this file](https://github.com/martinrotter/rssguard/blob/master/src/librssguard/core/messageobject.h) for the declaration. Some properties are writeable, allowing you to change contents of the message before it is written to DB. You can mark message important, parse its description, perhaps change author name or even assign some label to it!!!
96
97You can use [special placeholders](#userd-plac) within article filter.
98
99Also, there is a special variable named `utils`. This variable is of type `FilterUtils` and offers some useful [utility functions](#utils-object) for you to use in your filters.
100
101RSS Guard also allows to use the list of labels assigned to each message. You can, therefore, execute actions in your filtering script, based on which labels are assigned to the message. The property is called `assignedLabels` and is array of [`Label`](#Label-class) objects. If you change assigned labels to the message, then the change will be eventually [synchronized](#sfrl) back to server if respective plugin supports it.
102
103Passed message also offers special function:
104```js
105Boolean MessageObject.isDuplicateWithAttribute(DuplicationAttributeCheck)
106```
107
108Which allows you to perform runtime check for existence of the message in RSS Guard's database. The parameter is integer value from enumeration [`DuplicationAttributeCheck`](#DuplicationAttributeCheck-enum) and specifies how exactly you want to determine if given message is "duplicate". Again, you can use direct integer values or enumerant names.
109
110For example, if you want to check if there is already another message with same author in database, you should call `msg.isDuplicateWithAttribute(MessageObject.SameAuthor)`. Values of the enumeration can be combined via bitwise `|` operation in single call, for example like this: `msg.isDuplicateWithAttribute(MessageObject.SameAuthor | MessageObject.SameUrl)`.
111
112Here is the reference of methods and properties of some types available in your filtering scripts.
113
114#### `MessageObject` class
115| Type      | Name(Parameter)               | Return value  | Read-only | Description
116| :---      | :---                          | :---          | :---:     | ---
117| Property  | `assignedLabels`              | `Array<Label>`| ✅         | List of labels assigned to the message/article.
118| Property  | `availableLabels`             | `Array<Label>`| ✅         | List of labels which are currently available and can be assigned to the message. Available in RSS Guard 3.8.1+.
119| Property  | `feedCustomId`                | `String`      | ✅         | Service-specific ID of the feed which this message belongs to.
120| Property  | `accountId`                   | `Number`      | ✅         | RSS Guard's ID of the account activated in the program. This property is highly advanced and you probably do not need to use it at all.
121| Property  | `id`                          | `Number`      | ✅         | ID assigned to the message in RSS Guard local database.
122| Property  | `customId`                    | `String`      | ✅         | ID of the message as provided by the remote service.
123| Property  | `title`                       | `String`      |           | Title of the message.
124| Property  | `url`                         | `String`      |           | URL of the message.
125| Property  | `author`                      | `String`      |           | Author of the message.
126| Property  | `contents`                    | `String`      |           | Contents of the message.
127| Property  | `rawContents`                 | `String`      |           | This is RAW contents of the message as it was obtained from remote service/feed. You can expect raw `XML` or `JSON` element data here. Note that this attribute has some value only if `runningFilterWhenFetching` returns `true`. In other words, this attribute is not persistently stored inside RSS Guard's DB. Also, this attribute is artificially filled with ATOM-like data when testing the filter.
128| Property  | `score`                       | `Number`      |           | Arbitrary number in range \<0.0, 100.0\>. You can use this number to sort messages in a custom fashion as this attribute also has its own column in messages list.
129| Property  | `created`                     | `Date`        |           | Date/time of the message.
130| Property  | `isRead`                      | `Boolean`     |           | Is message read?
131| Property  | `isImportant`                 | `Boolean`     |           | Is message important?
132| Property  | `isDeleted`                   | `Boolean`     |           | Is message placed in recycle bin?
133| Method    | `isDuplicate(DuplicationAttributeCheck)` | `Boolean` |   | Allows you to test if this particular message is already stored in RSS Guard's DB.
134| Method    | `assignLabel(String)`         | `Boolean`     |           | Assigns label to this message. The passed `String` value is the `customId` property of `Label` type. See its API reference for relevant info.
135| Method    | `deassignLabel(String)`       | `Boolean`     |           | Removes label from this message. The passed `String` value is the `customId` property of `Label` type. See its API reference for relevant info.
136| Property  | `runningFilterWhenFetching`   | `Boolean`     | ✅         | Returns `true` if current run of the message filter is done when message is fetched. Returns `false` if message filter runs manually, for example from `Article filters` window.
137
138#### `Label` class
139| Type      | Name          | Return value  | Read-only | Description
140| :---      | :---          | :---          | :---:     | ---
141| Property  | `title`       | `String`      | ✅        | Label title.
142| Property  | `customId`    | `String`      | ✅        | Service-specific ID of this label. This ID is used as unique identifier for the label and is particularly useful if you want to assign/unassign the message label.
143| Property  | `color`       | `Color`       | ✅        | Label color. Note that type `color` has its documentation [here](https://doc.qt.io/qt-5/qml-color.html).
144
145#### `FilteringAction` enum
146| Enumerant name    | Integer value | Description
147| :---              | :---          | ---
148| `Accept`          | 1             | Message is accepted and will be added to DB or updated in DB.
149| `Ignore`          | 2             | Message is ignored and will **NOT** be added or updated in DB, but will also not be purged if already exists.
150| `Purge`           | 4             | Existing message is purged from the DB completely. Behavior is the same as `Ignore` when there is new incoming message.
151
152Note that `MessageObject` attributes which can be synchronized with service are synchronized even if you return `Purge` or `Ignore`. In other words, even if your filter ignores the message, you can still tweak its properties which would be synchronized back to your server.
153
154#### `DuplicationAttributeCheck` enum
155| Enumerant name    | Integer value | Description
156| :---              | :---          | ---
157| `SameTitle`       | 1             | Check if message has same title as some another messages.
158| `SameUrl`         | 2             | Check if message has same URL as some another messages.
159| `SameAuthor`      | 4             | Check if message has same author as some another messages.
160| `SameDateCreated` | 8             | Check if message has same date of creation as some another messages.
161| `AllFeedsSameAccount` | 16        | Perform the check across all feeds from your account, not just "current" feed.
162| `SameCustomId`    | 32            | Check if message with same custom ID exists in RSS Guard's DB.
163
164#### `utils` object
165| Type      | Name(Parameter)           | Return value  | How to call                               | Description
166| :---      | :---                      | :---          | :---                                      | ---
167| Method    | `hostname()`              | `String`      | `utils.hostname()`                        | Returns name of your PC.
168| Method    | `fromXmlToJson(String)`   | `String`      | `utils.fromXmlToJson('<h1>hello</h1>')`   | Converts `XML` string into `JSON`.
169| Method    | `parseDateTime(String)`   | `Date`        | `utils.parseDateTime('2020-02-24T08:00:00')`  | Converts textual date/time representation into proper `Date` object.
170
171#### Examples
172Accept only messages/articles from "Bob", while also mark them "important":
173```js
174function filterMessage() {
175  if (msg.author == "Bob") {
176    msg.isImportant = true;
177    return MessageObject.Accept;
178  }
179  else {
180    return MessageObject.Ignore;
181  }
182}
183```
184
185Replace all "dogs" with "cats"!
186```js
187function filterMessage() {
188  msg.title = msg.title.replace("dogs", "cats");
189  return MessageObject.Accept;
190}
191```
192
193Use published element instead of updated element (for ATOM entries only):
194```js
195function filterMessage() {
196  // Read raw contents of message and
197  // convert to JSON.
198  json = utils.fromXmlToJson(msg.rawContents);
199  jsonObj = JSON.parse(json)
200
201  // Read published date and parse it.
202  publishedDate = jsonObj.entry.published.__text;
203  parsedDate = utils.parseDateTime(publishedDate);
204
205  // Set new date/time for message and
206  // proceed.
207  msg.created = parsedDate;
208  return MessageObject.Accept;
209}
210```
211
212Dump RAW data of each message to RSS Guard's [debug output](#reprt):
213```js
214function filterMessage() {
215  console.log(msg.rawContents);
216  return MessageObject.Accept;
217}
218```
219
220When running the above script for Tiny Tiny RSS, it produces the following debug output:
221```
222...
223time="    34.360" type="debug" -> feed-downloader: Hooking message took 4 microseconds.
224time="    34.361" type="debug" -> {"always_display_attachments":false,"attachments":[],"author":"Aleš Kapica","comments_count":0,"comments_link":"","content":"<p>\nNaposledy jsem psal o čuňačení v MediaWiki asi před půl rokem, kdy jsem chtěl upozornit na to, že jsem přepracoval svoji původní šablonu Images tak, aby bylo možné používat výřezy z obrázků a stránek generovaných z DjVu a PDF dokumentů. Blogpost nebyl nijak extra hodnocen, takže mě vcelku nepřekvapuje, jak se do hlavní vývojové větve MediaWiki dostávají čím dál větší prasečiny.\n</p>","feed_id":"5903","feed_title":"abclinuxu - blogy","flavor_image":"","flavor_stream":"","guid":"{\"ver\":2,\"uid\":\"52\",\"hash\":\"SHA1:5b49e4d8f612984889ba25e7834e80604c795ff8\"}","id":6958843,"is_updated":false,"labels":[],"lang":"","link":"http://www.abclinuxu.cz/blog/kenyho_stesky/2021/1/cunacime-v-mediawiki-responzivni-obsah-ii","marked":false,"note":null,"published":false,"score":0,"tags":[""],"title":"Čuňačíme v MediaWiki - responzivní obsah II.","unread":true,"updated":1610044674}
225time="    34.361" type="debug" -> feed-downloader: Running filter script, it took 348 microseconds.
226time="    34.361" type="debug" -> feed-downloader: Hooking message took 4 microseconds.
227time="    34.361" type="debug" -> {"always_display_attachments":false,"attachments":[],"author":"kol-ouch","comments_count":0,"comments_link":"","content":"Ahoj, 1. 6. se blíží, tak začínám řešit co s bambilionem fotek na google photos. \n<p class=\"separator\"></p>\nZa sebe můžu říct, že gp mi vyhovují - ne snad úplně tím, že jsou zadarmo, ale hlavně způsobem práce s fotkami, možnostmi vyhledávání v nich podle obsahu, vykopírování textu z nich, provázaností s mapami, recenzemi, možnostmi sdílení, automatickým seskupováním a podobně.","feed_id":"5903","feed_title":"abclinuxu - blogy","flavor_image":"","flavor_stream":"","guid":"{\"ver\":2,\"uid\":\"52\",\"hash\":\"SHA1:1277107408b159882b95ca7151a0ec0160a3971a\"}","id":6939327,"is_updated":false,"labels":[],"lang":"","link":"http://www.abclinuxu.cz/blog/Co_to_je/2021/1/kam-s-fotkama","marked":false,"note":null,"published":false,"score":0,"tags":[""],"title":"Kam s fotkama?","unread":true,"updated":1609750800}
228...
229```
230
231For RSS 2.0 message, the result might look as follows:
232```
233...
234time="     3.568" type="debug" -> feed-downloader: Hooking message took 6 microseconds.
235time="     3.568" type="debug" -> <item>
236<title><![CDATA[Man Utd's Cavani 'not comfortable' in England, says father]]></title>
237<description><![CDATA[Manchester United striker Edinson Cavani "does not feel comfortable" and could move back to his native South America, his father said.]]></description>
238<link>https://www.bbc.co.uk/sport/football/56341983</link>
239<guid isPermaLink="true">https://www.bbc.co.uk/sport/football/56341983</guid>
240<pubDate>Tue, 09 Mar 2021 23:46:03 GMT</pubDate>
241</item>
242
243time="     3.568" type="debug" -> feed-downloader: Running filter script, it took 416 microseconds.
244...
245```
246
247Write details of available labels and assign the first label to the message:
248```js
249function filterMessage() {
250  console.log('Number of assigned labels: ' + msg.assignedLabels.length);
251  console.log('Number of available labels: ' + msg.availableLabels.length);
252
253  var i;
254  for (i = 0; i < msg.availableLabels.length; i++) {
255    var lbl = msg.availableLabels[i];
256
257    console.log('Available label:');
258    console.log('  Title: \'' + lbl.title + '\' ID: \'' + lbl.customId + '\'');
259  }
260
261  if (msg.availableLabels.length > 0) {
262    console.log('Assigning first label to message...');
263    msg.assignLabel(msg.availableLabels[0].customId);
264
265    console.log('Number of assigned labels ' + msg.assignedLabels.length);
266  }
267
268  console.log();
269  return MessageObject.Accept;
270}
271```
272
273Make sure that your receive only one message/article with particular URL across all your feeds (from same plugin) and all other messages with same URL are subsequently ignored:
274```js
275function filterMessage() {
276  if (msg.isDuplicateWithAttribute(MessageObject.SameUrl | MessageObject.AllFeedsSameAccount)) {
277    return MessageObject.Ignore;
278  }
279  else {
280    return MessageObject.Accept;
281  }
282}
283```
284
285Remove "ads" from messages received from Inoreader. Method simply removes `div` which contains the advertisement:
286```js
287function filterMessage() {
288  msg.contents = msg.contents.replace(/<div>\s*Ads[\S\s]+Remove<\/a>[\S\s]+adv\/www\/delivery[\S\s]+?<\/div>/im, '');
289
290  return MessageObject.Accept;
291}
292```
293
294### Websites Scraping <a id="scrap"></a>
295> **[CAUTION!] Only proceed if you consider yourself a power user and you know what you are doing!**
296
297RSS Guard offers additional advanced features inspired by [Liferea](https://lzone.de/liferea/).
298
299You can select source type of each feed. If you select `URL`, then RSS Guard simply downloads feed file from given location and behaves like everyone would expect.
300
301However, if you choose `Script` option, then you cannot provide URL of your feed and you rely on custom script to generate feed file and provide its contents to **standard output**. Resulting data written to standard output should be valid feed file, for example RSS or ATOM XML file.
302
303`Fetch it now` button also works with `Script` option. Therefore, if your source script and (optional) post-process script in cooperation deliver a valid feed file to the output, then all important metadata, like title or icon of the feed, can be discovered :sparkles: automagically :sparkles:.
304
305<img alt="alt-img" src="images/scrape-source-type.png" width="350px">
306
307Any errors in your script must be written to **error output**.
308
309Note that you must provide full execution line to your custom script, including interpreter binary path and name and all that must be written in special format `<interpreter>#<argument1>#<argument2>#....`. The `#` character is there to separate interpreter and individual arguments. I had to select some character as separator because simply using space ` ` is not that easy as it might sound, as sometimes space could be a part of an argument itself.
310
311If everything went well, script must return `0` as the process exit code, or a non-zero exit code if some error happened.
312
313Binary name (interpreter) must be always be specified, while arguments not. Be very careful when quoting arguments. Some examples of valid and tested execution lines are:
314
315| Command       | Explanation   |
316| :---          | ---           |
317| `bash#-c#curl https://github.com/martinrotter.atom`   | Download ATOM feed file using Bash and Curl. |
318| `Powershell#Invoke-WebRequest 'https://github.com/martinrotter.atom' \| Select-Object -ExpandProperty Content` | Download ATOM feed file with Powershell. |
319| `php#tweeper.php#-v#0#https://twitter.com/NSACareers` | Scrape Twitter RSS feed file with [Tweeper](https://git.ao2.it/tweeper.git). Tweeper is the utility which is able to produce RSS feed from Twitter and other similar social platforms. |
320
321<img alt="alt-img" src="images/scrape-source.png" width="350px">
322
323Note that the above examples are cross-platform and you can use the exact same command on Windows, Linux or Mac OS X, if your operating system is properly configured.
324
325RSS Guard offers [placeholder](#userd-plac) `%data%` which is automatically replaced with full path to RSS Guard's [user data folder](#userd), allowing you to make your configuration fully portable. You can, therefore, use something like this as source script line: `bash#%data%/scripts/download-feed.sh`.
326
327Also, working directory of process executing the script is set to point to RSS Guard's user data folder.
328
329There are some examples of website scrapers [here](https://github.com/martinrotter/rssguard/tree/master/resources/scripts/scrapers), most of them are written in Python 3, thus their execution line is similar to `python#script.py`. Make sure to examine each script for more information on how to use it.
330
331After your source feed data are downloaded either via URL or custom script, you can optionally post-process the data with one more custom script, which will take **raw source data as input** and must produce processed valid feed data to **standard output** while printing all error messages to **error output**.
332
333Format of post-process script execution line is the same as above.
334
335<img alt="alt-img" src="images/scrape-post.png" width="350px">
336
337Typical post-processing filter might do things like advanced CSS formatting, localization of content to another language, downloading of full articles, some kind of filtering or removing ads.
338
339It's completely up to you if you decide to only use script as `Source` of the script or separate your custom functionality between `Source` script and `Post-process` script. Sometimes you might need different `Source` scripts for different online sources and the same `Post-process` script and vice versa.
340
341### Notifications <a id="notif"></a>
342RSS Guard allows you to configure behavior of desktop notifications. There is a number of events which can be configured:
343* New (unread) articles fetched
344* Fetching of articles is started
345* Login OAuth tokens are refreshed
346* New RSS Guard version is available
347* etc.
348
349<img alt="alt-img" src="images/notif.png" width="600px">
350
351Your notification can also play `.wav` sounds which you can place under your [user data folder](#userd) and use them via special [placeholder](#userd-plac). Other audio formats are not supported.
352
353### Database Backends <a id="datab"></a>
354RSS Guard offers switchable database backends which hold your data. At this point, two backends are available:
355* MariaDB
356* SQLite (default)
357
358SQLite backend is very simple to use, no further configuration is needed and all your data is stored in single file
359```
360<user-data-root-folder>\database\local\database.db
361```
362Check `About RSS Guard -> Resources` dialog to find more info on significant paths used. This backend offers "in-memory" database option, which automatically copies all your data into RAM when application launches, and then works solely with that RAM data, which makes RSS Guard incredibly fast. Data is also written back to database file when application exits. Note that this option should be used very rarely because RSS Guard should be fast enough with classic SQLite persistent DB files. Only use this with a huge amount of article data, and when you know what you are doing.
363
364MariaDB (MySQL) backend is there for users, who want to store their data in a centralized way. You can have single server in your network and use multiple RSS Guard instances to access the data.
365
366For database-related configuration see `Settings -> Data storage` dialog.
367
368### User Data Portability <a id="userd"></a>
369One of the main goals of RSS Guard is to have local application data portable (relocatable) so that they can be use across all [supported operating systems](#sos).
370
371RSS Guard is able to run in two modes.
372
373Default mode is *"non-portable"* mode, where user data folder is placed in user-wide "config directory" (this is `C:\Users\<user>\AppData\Local` on Windows). If subfolder with file
374```
375RSS Guard 4\data\config\config.ini
376```
377exists, then this user folder is used.
378
379The other mode is that user data folder is placed in subfolder `data4` in the same directory as RSS Guard binary (`rssguard.exe` on Windows). This *"portable"* mode is automatically enabled if "non-portable" mode detection fails.
380
381#### `%data%` placeholder <a id="userd-plac"></a>
382RSS Guard stores its data and settings in single folder. What exact folder it is is described [here](#portable-user-data). RSS Guard allows you to use the folder programmatically in some special contexts via `%data%` placeholder. You can use this placeholder in these RSS Guard contexts:
383* Contents of your [article filters](#fltr) - you can, therefore, place some scripts under your user data folder and include them via `JavaScript` into your article filter.
384* Contents of each file included in your custom [skins](#skin). Note that in this case, the semantics of `%data%` are little changed and `%data%` points directly to base folder of your skin.
385* `source` and `post-process script` attributes of for [scraping](#scrap) feed - you can use the placeholder to load scripts to generate/process feed from user data folder.
386* Notifications also support the placeholder in path to audio files which are to be played when some event happens. For example you could place audio files in your data folder and then use them in notification with `%data%\audio\new-messages.wav`. See more about notifications [here](#notif).
387
388### Built-in Web Browser with AdBlock <a id="webb"></a>
389RSS Guard is distributed in two variants:
390* **Standard package with WebEngine-based bundled article viewer**: This variant displays messages/articles with their full formatting and layout in embedded Chromium-based web viewer. This variant of RSS Guard should be nice for everyone. Also, installation packages are relatively big.
391
392<img alt="alt-img" src="images/webengine-view.png" width="600px">
393
394* **Lite package with simple text-based article viewer**: This variant displays message/article in much simpler and more lightweight text-based component. All packages of this variant have `nowebengine` keyword in their names. Layout and formatting of displayed message is simplified, no big external web viewers are used, which results in much smaller installation packages, much smaller memory footprint and increased privacy of the user, because many web resources are not downloaded by default like pictures, JavaScript and so on. This variant of RSS Guard is meant for advanced users.
395
396<img alt="alt-img" src="images/nonwebengine-view.png" width="600px">
397
398If you're not sure which version to use, **use the WebEngine-based RSS Guard**.
399
400#### AdBlock
401[Web-based variant](#webb) of RSS Guard offers ad-blocking functionality via [Adblocker](https://github.com/cliqz-oss/adblocker). Adblocker offers similar performance to [uBlock Origin](https://github.com/gorhill/uBlock).
402
403You need to have have [Node.js](https://nodejs.org) with [NPM](https://www.npmjs.com) (which is usually included in Node.js installer) installed to have ad-blocking in RSS Guard working. Also, the implementation requires additional [npm](https://www.npmjs.com) modules to be installed. You see the list of needed modules near the top of [this](https://github.com/martinrotter/rssguard/blob/master/resources/scripts/adblock/adblock-server.js) file.
404
405I understand that the above installation of needed dependencies is not trivial, but it is necessary evil to have up-to-date and modern implementation of AdBlock in RSS Guard. Previous, "C++"-based, implementation was buggy, quite slow, and hard to maintain.
406
407You can find elaborate lists of AdBlock rules [here](https://easylist.to). You can just copy direct hyperlinks to those lists and paste them into the "Filter lists" text-box as shown below. Remember to always separate individual links with newlines. Same applies to "Custom filters", where you can insert individual filters, for example [filter](https://adblockplus.org/filter-cheatsheet) "idnes" to block all URLs with "idnes" in them.
408
409<img alt="alt-img" src="images/adblock.png" width="350px">
410
411The way ad-blocking internally works is that RSS Guard starts local HTTP browser which provides ad-blocking API, which is subsequently called by RSS Guard. There is some caching done in between, which speeds up some ad-blocking decisions.
412
413## Minor Features <a id="mife"></a>
414
415### Files Downloader <a id="downl"></a>
416RSS Guard offers simple embedded file downloader.
417
418<img alt="alt-img" src="images/downloader-window.png" width="600px">
419
420You can right click on any item in embedded web browser and hit `Save as` button. RSS Guard will then automatically display the downloader, and will download your file. This feature works in [both RSS Guard variants](#webb).
421
422<img alt="alt-img" src="images/downloader-view.png" width="600px">
423
424You can download up to 6 files simultaneously.
425
426### Labels <a id="lbls"></a>
427RSS Guard supports labels (tags). Any number of tags can be assigned to any article.
428
429Note that tags in some plugins are [synchronizable](#sfrl). While labels are synchronized with these services, sometimes they cannot be directly created via RSS Guard. In this case, you have to create them via web interface of the respective service, and only after that perform `Synchronize folders & other items`, which will fetch newly created labels too.
430
431Labels can be easily added via `Labels` root item.
432
433<img alt="alt-img" src="images/label-menu.png" width="600px">
434
435New label's title and color can also be chosen.
436
437<img alt="alt-img" src="images/label-dialog.png" width="200px">
438
439Unassigning a message label might easily be done through the message viewer.
440
441<img alt="alt-img" src="images/label-assign.png" width="600px">
442
443Note that unassigning a message labels is also synchronized at regular intervals (with services that support label synch).
444
445Also, [message filters](#fltr) can assign or remove labels to/from messages.
446
447### Skins <a id="skin"></a>
448RSS Guard is a skin-able application. Its GUI can be almost completely styled with [stylesheets](https://doc.qt.io/qt-5/stylesheet.html).
449
450<img alt="alt-img" src="images/gui-dark.png" width="600px">
451
452You can select style and skin in settings category `User interface`.
453
454RSS Guard encapsulates styling capabilities via *skins* feature. Each skin is placed in its own folder and must contain several [files](https://github.com/martinrotter/rssguard/tree/master/resources/skins/vergilius). There are some [built-in](https://github.com/martinrotter/rssguard/tree/master/resources/skins) skins, but you can place your custom skins in your [user data folder](#userd). You can find exact path to your user data folder in `About` dialog. Note that there must be subfolder `skins`. Create it if it does not exist and place your custom skins inside.
455
456The base for your custom skin may serve an empty ["plain" skin](https://github.com/martinrotter/rssguard/tree/master/resources/skins/plain). Look the [README file](https://github.com/martinrotter/rssguard/tree/master/resources/skins/plain/README) there, for some commentaries to it.
457
458<img alt="alt-img" src="images/about-skins.png" width="600px">
459
460So for example if your new skin is called `greenland`, you must place it in folder
461
462```
463<user-data-path>\skins\greenland
464```
465
466As stated above, there are several files in each skin:
467* `metadata.xml` - XML file with some basic information about the skin's name, author etc. Also,
468* `theme.css` - [Qt stylesheet](https://doc.qt.io/qt-5/stylesheet.html) file.
469* `html_*.html` - These are (partial) HTML files which are used by RSS Guard in various situations like displaying article or error page.
470
471### GUI Tweaking <a id="guit"></a>
472RSS Guard's main window appearance can be tweaked in many ways. You can hide menu, toolbars, status bar, you can also change orientation of article viewer to suit widescreen devices.
473
474<img alt="alt-img" src="images/gui-hiding.png" width="600px">
475<img alt="alt-img" src="images/gui-hiding-all.png" width="600px">
476<img alt="alt-img" src="images/gui-layout-orientation.png" width="600px">
477<img alt="alt-img" src="images/gui-dark.png" width="600px">
478<img alt="alt-img" src="images/gui-dark2.png" width="600px">
479
480### <a id="cli"></a>Command Line Interface
481RSS Guard offers CLI (command line interface). For overview of its features, run `rssguard --help` in your terminal. You will see the overview of the interface.
482
483```
484rssguard [options] [url-1 ... url-n]
485
486Options:
487  -l, --log <log-file>           Write application debug log to file. Note that
488                                 logging to file may slow application down.
489  -d, --data <user-data-folder>  Use custom folder for user data and disable
490                                 single instance application mode.
491  -s, --no-single-instance       Allow running of multiple application
492                                 instances.
493  -n, --no-debug-output          Completely disable stdout/stderr outputs.
494  -?, -h, --help                 Displays help on commandline options.
495  --help-all                     Displays help including Qt specific options.
496  -v, --version                  Displays version information.
497
498Arguments:
499  urls                           List of URL addresses pointing to individual
500                                 online feeds which should be added.
501```
502
503RSS Guard can add feeds passed as URLs via command line arguments. Feed URI [scheme](https://en.wikipedia.org/wiki/Feed_URI_scheme) is supported, so that you can call RSS Guard like this:
504
505```powershell
506rssguard.exe "feed://archlinux.org/feeds/news"
507rssguard.exe "feed:https//archlinux.org/feeds/news"
508rssguard.exe "https://archlinux.org/feeds/news"
509```
510
511So in order to comfortably add feed directly to RSS Guard from you browser without copying its URL manually, you have to "open" RSS Guard "with" feed URL passed as parameter. There are [extensions](https://addons.mozilla.org/en-GB/firefox/addon/open-with/) which can do it.
512
513## <a id="contrib"></a>For Contributors
514
515### <a id="donat"></a>Donations
516You can support author of RSS Guard via [donations](https://github.com/sponsors/martinrotter).
517
518### <a id="compil"></a>Compiling RSS Guard
519RSS Guard is C++ application and all common build instructions can be found in top of [project file](https://github.com/martinrotter/rssguard/blob/master/build.pro).
520
521### <a id="papi"></a>Plugin API
522RSS Guard offers simple C++ API for creating new service plugins. All base API classes are in folder [`abstract`](https://github.com/martinrotter/rssguard/tree/master/src/librssguard/services/abstract). User must subclass and implement all interface classes:
523
524| Class                 | Purpose   |
525| :---                  | ---       |
526| `ServiceEntryPoint`   | Very base class which provides basic information about the plugin name, author, etc. It also provides methods which are called when new account should be created and when existing accounts should be loaded from database. |
527| `ServiceRoot`         | This is the core "account" class which represents an account node in feed's list, and offers interface for all critical functionality of a plugin, including handlers which are being called with a plugin's start/stop, marking messages as read/unread/starred/deleted, unassigning labels, etc. |
528
529API is reasonably simple to understand but relatively large. Sane default behavior is employed where it makes sense.
530
531Perhaps the best approach to use when writing new plugin is to copy [existing](https://github.com/martinrotter/rssguard/tree/master/src/librssguard/services/greader) one and start from there.
532
533Note that RSS Guard can support loading of plugins from external libraries (`.dll`, `.so`, etc.) but the functionality must be polished because so far all plugins are directly bundled into the application as no one really requested run-time loading of plugins so far.
534
535### <a id="reprt"></a>Reporting Bugs or Feature Requests
536Please report all issues/bugs/ideas to [Issues](https://github.com/martinrotter/rssguard/issues) section. Describe your problem as precisely as possible, along with steps taken leading up to the issue occurring.
537
538If you report any bug, you must provide application debug log. So make sure to start RSS Guard from command line (`cmd.exe` on Windows) with `--log` switch and path where you want to store log file, for example `rssguard.exe --log '.\rssguard.log'` which will save log file into your RSS Guard folder. After you've started RSS Guard this way, then reproduce your problem and upload log file to the ticket.
539
540Also, for some broader questions or general ideas, use [discussions](https://github.com/martinrotter/rssguard/discussions) rather than [issues](https://github.com/martinrotter/rssguard/issues).
541
542### <a id="locali"></a>Localization
543RSS Guard currently includes [many localizations](http://www.transifex.com/projects/p/rssguard).
544
545If you are interested in creating translations for RSS Guard, then do this:
5461. Go [here](http://www.transifex.com/projects/p/rssguard) and check status of currently supported localizations.
5472. [Login](http://www.transifex.com/signin) (you can use social networks to login) and work on existing translations. If no translation team for your country/language exists, then ask for creating of localization team via the website.
548
549**All translators commit themselves to keep their translations up-to-date. If some translations are not updated by their authors regularly, and only a small number of strings is translated then those translations along with their teams will eventually be REMOVED from the project!!! At least 50% of strings must be translated for translation to be added to project.**
550
551### <a id="migratt"></a>Migrating data
552RSS Guard automatically migrates all your [user data](#userd) if you install newer minor version, for example if you update from `3.7.5` to `3.9.1`.
553
554If you decide to upgrade to new major version, for example from `3.x.x` to `4.x.x`, then you cannot use your existing user data as major versions are declared as backwards incompatible, so such data transition are not supported.
555
556### Migrating user data from `3.9.2` to `4.x.x`
557> Only proceed if you consider yourself to be a SQL power user and you know what you are doing!
558>
559> Also, make sure that last RSS Guard from `3.x.x` line you used with your data was the most up-to-date `3.9.2` version.
560
561Here is short DIY manual on how to manually update your `database.db` file to `4.x.x` format. Similar approach can be taken if you use `MariaDB` [database backend](#datab).
562
563Here are SQLs for [old](https://github.com/martinrotter/rssguard/blob/3.9.2/resources/sql/db_init_sqlite.sql) schema and [new](https://github.com/martinrotter/rssguard/blob/4.0.0/resources/sql/db_init_sqlite.sql) schema.
564
565### Converting `*Accounts` tables
566***
567In `3.x.x` each plugin/account type had its own table where it kept your login usernames, service URLs etc. In `4.x.x` all plugins share one table `Accounts` and place account-specific data into `custom_data` column. You simply can take all rows from any `*Accounts` table (for example `TtRssAccounts`) and insert them into `Accounts`, keeping all columns their default values, except of `type`, which must have on of these values:
568* `std-rss` - for standard RSS/ATOM feeds.
569* `tt-rss` - for Tiny Tiny RSS.
570* `owncloud` - for Nextcloud News.
571* `greader` - For all Google Reader API services, including Inoreader.
572* `feedly` - for Feedly.
573* `gmail` - for Gmail.
574
575Then you need to go to `Edit` dialog of your account in RSS Guard (once you complete this migration guide) and check for all missing login information etc.
576
577<a id="accid"></a>Also, once you add any rows the `Accounts` table, your row will be assigned unique `id` value which is integer and is used as foreign key in other DB tables, via column `account_id`.
578
579### Converting `Feeds` table
580***
581There are some changes in `Feeds` table:
582* `url` column is now named `source`,
583* `source_type`, `post_process`, `encoding`, `type`, `protected`, `username`, `password` columns are removed and their data are now stored in JSON-serialized form in new column `custom_data`. Here is sample value of `custom_data`:
584
585```json
586{
587  "encoding": "UTF-8",
588  "password": "AwUujeO2efOgYpX3g1/zoOTp9JULcLTZzwfY",
589  "post_process": "",
590  "protected": false,
591  "source_type": 0,
592  "type": 3,
593  "username": ""
594}
595```
596
597Pay attention to `account_id` column as this column is the ID of your account as stated in the above [section](#accid).
598
599### Converting `Messages` table
600***
601Columns were reordered and other than that new column `score` with sane default value was added. Therefore you can simply copy your data in a column-to-column mode.
602
603Pay attention to `account_id` column as this column is the ID of your account as stated in the above [section](#accid).
604
605### Other tables
606***
607Other tables like `Labels` or `MessageFilters` are unchanged between these two major RSS Guard versions. But you might need to adjust `account_id` to match DB ID of your account.
608