Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

draft: add Webhook extension [draft] #249

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions xExtension-Webhook/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
MIT License

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
71 changes: 71 additions & 0 deletions xExtension-Webhook/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# FreshRSS Webhook

A FreshRSS extension for sending custom webhooks when new article appears (and matches custom criteria)

## Installation / Usage

Please follow official README: https://github.com/FreshRSS/Extensions?tab=readme-ov-file

## Documentation

You can define keywords to be used for matching new incoming article.
When article contains at least one defined keyword, the webhook will be sent.

Each line is checked individually. In addition to normal texts, RegEx expressions can also be defined. These must be able to be evaluated using the PHP function `preg_match`.

Examples:

```text
some keyword
important
/\p{Latin}/i
```

In addition, you can choose whether the matched articles will not be inserted into the database or whether they will be inserted into the database but marked as read (default).

## How it works

```
┌──────────────┐ ┌────────────────────────────────────┐ ┌───────┐
│ │ │ FreshRSS │ │ │
│ │ │ │ │ some │
│ INTERNET │ │ ┌────────┐ ┌─────↓─────┐ │ │ │
│ │ │ │FreshRSS│ │• Webhook •│ │ │service│
│ │ │ │ core │ │ extension │ │ │ │
└────┬─────────┘ └──┴──┬─────┴─────────┴─────┬─────┴──┘ └─────┬─┘
│ │ │ │
│ checks RSS │ │ │
│ for new articles │ │ │
│◄─────────────────────────┤ │ │
│ │ │ if some new article │
├─────────────────────────►│ │ matches custom criteria │
│ new articles ├────────────────────►│ │
│ │ new articles ├────────────────────────►│
│ │ │ HTTP request │
│ │ │ │
│ checks RSS │ │ │
│ or new articles │ │ │
│◄─────────────────────────┤ │ │
│ │ │ if no new article │
├─────────────────────────►│ │ matches custom criteria │
│ new articles ├────────────────────►│ no request will be sent │
│ │ new articles │ │
│ │ │ │
│ │ │ │
▼ ▼ ▼ ▼
```

- for every new article that matches custom criteria new HTTP request will be sent

- see also discussion: https://github.com/FreshRSS/FreshRSS/discussions/6480

## ⚠️ Limitations

- currently only GET, POST and PUT methods are supported
- there is no validation for configuration
- it's not fully tested and translated yet

## Special Thanks

- inspired by extension [**FilterTitle**](https://github.com/cn-tools/cntools_FreshRssExtensions/tree/master/xExtension-FilterTitle)
by [@cn-tools](https://github.com/cn-tools)
220 changes: 220 additions & 0 deletions xExtension-Webhook/configure.phtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
<?php
declare(strict_types=1);
/** @var WebhookExtension $this */
?>
<small>
Webhooks allow external services to be notified when certain events happen.
When the specified events happen, we'll send a HTTP request (usually POST) to the URL you provide.
</small>
<form action="<?php echo _url('extension', 'configure', 'e', urlencode($this->getName())); ?>" method="post">
<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />

<fieldset>
<legend><?= _t('ext.webhook.event_settings') ?> ⚙️</legend>
<details open>
<summary class="stick"><small><?= _t('ext.webhook.show_hide') ?></small></summary>

<div class="form-group">
<label class="group-name" for="keywords">Keywords in the new article</label>
<div class="group-controls">
<textarea name="keywords" id="keywords"><?php echo $this->getKeywordsData(); ?></textarea>
<br />
<small>
Each line is checked individually. In addition to normal texts, RegEx expressions can also be
defined
(they are evaluated using the PHP function <code>preg_match</code>).
</small>
</div>
</div>

<!-- Search in article's: -->
<div class="form-group">
<label class="group-name" for="search_in">Search in article's:</label>
<div class="group-controls">
<table>
<tr>
<td class="group">
<input type="checkbox" name="search_in_title" id="search_in_title" value="1"
<?= $this->getSystemConfigurationValue('search_in_title') ? 'checked' : '' ?>>

Check failure on line 38 in xExtension-Webhook/configure.phtml

View workflow job for this annotation

GitHub Actions / tests

Only booleans are allowed in a ternary operator condition, mixed given.
<label for="search_in_title"><b>🪧 title * </b>&nbsp; </label>
</td>
<td class="group">
<input type="checkbox" name="search_in_feed" id="search_in_feed" value="1"
<?= $this->getSystemConfigurationValue('search_in_feed') ? 'checked' : '' ?>>

Check failure on line 43 in xExtension-Webhook/configure.phtml

View workflow job for this annotation

GitHub Actions / tests

Only booleans are allowed in a ternary operator condition, mixed given.
<label for="search_in_feed">💼 feed &nbsp;</label>
</td>
<td class="group">
<input type="checkbox" name="search_in_authors" id="search_in_authors" value="1"
<?= $this->getSystemConfigurationValue('search_in_authors') ? 'checked' : '' ?>>

Check failure on line 48 in xExtension-Webhook/configure.phtml

View workflow job for this annotation

GitHub Actions / tests

Only booleans are allowed in a ternary operator condition, mixed given.
<label for="search_in_authors">👥 authors &nbsp;</label>
</td>
<td class="group">
<input type="checkbox" name="search_in_content" id="search_in_content" value="1"
<?= $this->getSystemConfigurationValue('search_in_content') ? 'checked' : '' ?>>

Check failure on line 53 in xExtension-Webhook/configure.phtml

View workflow job for this annotation

GitHub Actions / tests

Only booleans are allowed in a ternary operator condition, mixed given.
<label for="search_in_content">📄 content &nbsp;</label>
</td>
</tr>
</table>
</div>
</div>

<div class="form-group">
<label class="group-name" for="mark_as_read"><?= _t('ext.webhook.mark_as_read') ?></label>
<div class="group-controls">
<input type="checkbox" name="mark_as_read" id="mark_as_read" value="1"
<?= $this->getSystemConfigurationValue('mark_as_read') ? 'checked' : '' ?>>

Check failure on line 65 in xExtension-Webhook/configure.phtml

View workflow job for this annotation

GitHub Actions / tests

Only booleans are allowed in a ternary operator condition, mixed given.
</div>
</div>

</details>
</fieldset>

<!-- ---------------------[ HTTP settings ]-------------------------------------- -->

<fieldset>
<legend><?= _t('ext.webhook.webhook_settings') ?> 🌐</legend>
<details open>
<summary class="stick"> <small><?= _t('ext.webhook.show_hide') ?></small> </summary>

<div class="form-group">
<label class="group-name" for="webhook_url">Method / URL:</label>
<div class="group-controls">
<select id="webhook_method" name="webhook_method">
<?php if ($this->webhook_method === 'POST') { ?>
<option value="POST" selected>POST</option>
<?php } else { ?>
<option value="POST">POST</option>
<?php } ?>

<?php if ($this->webhook_method === 'GET') { ?>
<option value="GET" selected>GET</option>
<?php } else { ?>
<option value="GET">GET</option>
<?php } ?>

<?php if ($this->webhook_method === 'PUT') { ?>
<option value="PUT" selected>PUT</option>
<?php } else { ?>
<option value="PUT">PUT</option>
<?php } ?>
</select>
<input type="text" name="webhook_url" id="webhook_url"
value="<?= $this->getWebhookUrl() ?>"></input>
</div>
</div>

<div class="form-group">
<label class="group-name" for="webhook_body">HTTP Body</label>
<div class="group-controls">
<textarea name="webhook_body" id="webhook_body"><?= $this->getWebhookBody() ?></textarea>
<br />
<small>
Must be valid <b>JSON</b> or form data (<b>x-www-form-urlencoded</b>)
</small>

<details>
<summary><small><b class="">You can use special placeholders that will be replaced
by the actual values:</b></small>
</summary>
<div>
<div class="content">
<table>
<tr>
<th><b><code>Placeholder<code></b></th>
<th><b>Description</b></th>
</tr>
<tr>
<td><code>__TITLE__</code></td>
<td>Article title</td>
</tr>
<tr>
<td><code>__URL__</code></td>
<td>HTML-encoded link of the article</td>
</tr>
<tr>
<td><code>__CONTENT__</code></td>
<td>Content of the article (HTML format)</td>
</tr>
<tr>
<td><code>__DATE__</code></td>
<td>Date of the article (string)</td>
</tr>
<tr>
<td><code>__DATE_TIMESTAMP__</code></td>
<td>Date of the article as timestamp (number)</td>
</tr>
<tr>
<td><code>__AUTHORS__</code></td>
<td>Article authors</td>
</tr>
<tr>
<td><code>__THUMBNAIL_URL__</code></td>
<td>Thumbnail (image) URL</td>
</tr>
<tr>
<td><code>__FEED__</code></td>
<td>Feed name</td>
</tr>
<tr>
<td><code>__TAGS__</code></td>
<td>Article tags (string, separated by " #")</td>
</tr>
</table>
</div>

</div>
</details>

</div>
</div>

<!-- ---------------------[ More options ]-------------------------------------- -->

<details>
<summary><strong class=""><?= _t('ext.webhook.more_options') ?></strong></summary>

<div class="form-group">
<label class="group-name" for="webhook_headers">HTTP Headers<br />(one per line)</label>
<div class="group-controls">
<textarea name="webhook_headers"
id="webhook_headers"><?= $this->getWebhookHeaders() ?></textarea>
</div>
</div>
<div class="form-group">
<label class="group-name" for="webhook_body_type">HTTP Body type</label>
<div class="group-controls">
<input type="radio" id="webhook_body_type_json" name="webhook_body_type" value="json"
<?= $this->getWebhookBodyType() === 'json' ? 'checked' : '' ?>>
<label for="webhook_body_type_json" class="group"><code>JSON</code></label>

<input type="radio" id="webhook_body_type_form" name="webhook_body_type" value="form"
<?= $this->getWebhookBodyType() === 'form' ? 'checked' : '' ?>>
<label for="webhook_body_type_form" class="group"><code>x-www-form-urlencoded</code></label>

<details>
<summary><span class="">More info:</span></summary>
<small>When header contains
<b><code>Content-type: application/x-www-form-urlencoded</code></b>
the keys and values are encoded in key-value tuples separated by '&', with a '='
between the key and the value. Non-alphanumeric characters in both keys
and values are URL encoded
</small>
</details>
</div>
</div>
</details>

</details>
</fieldset>

<!-- ---------------------[ SUBMIT & CANCEL ]-------------------------------------- -->
<div class="form-group form-actions">
<div class="group-controls">
<button type="submit" class="btn btn-important"><?php echo _t('gen.action.submit'); ?></button>
<button type="reset" class="btn"><?php echo _t('gen.action.cancel'); ?></button>
<button type="menu" class="btn"><?= _t('ext.webhook.save_and_send_test_req') ?></button>
</div>
</div>

</form>

Loading
Loading