FreshRSS-Extensions/xExtension-Webhook/extension.php
2024-09-08 18:25:20 +02:00

246 lines
9.6 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
include __DIR__ . "/request.php";
enum BODY_TYPE: string {
case JSON = "json";
case FORM = "form";
}
enum HTTP_METHOD: string {
case GET = "GET";
case POST = "POST";
case PUT = "PUT";
case DELETE = "DELETE";
case PATCH = "PATCH";
case OPTIONS = "OPTIONS";
case HEAD = "HEAD";
}
class WebhookExtension extends Minz_Extension {
public bool $logsEnabled = false;
public HTTP_METHOD $webhook_method = HTTP_METHOD::POST;
public BODY_TYPE $webhook_body_type = BODY_TYPE::JSON;
public string $webhook_url = "http://<WRITE YOUR URL HERE>";
/** * @var string[] $webhook_headers as array of strings */
public array $webhook_headers = ["User-Agent: FreshRSS", "Content-Type: application/x-www-form-urlencoded"];
public string $webhook_body = '{
"title": "__TITLE__",
"feed": "__FEED__",
"url": "__URL__",
"created": "__DATE_TIMESTAMP__"
}';
#[\Override]
public function init(): void {
$this->registerTranslates();
$this->registerHook("entry_before_insert", [$this, "processArticle"]);
}
public function handleConfigureAction(): void {
$this->registerTranslates();
if (Minz_Request::isPost()) {
$conf = [
"keywords" => array_filter(Minz_Request::paramTextToArray("keywords", [])),
"search_in_title" => Minz_Request::paramString("search_in_title"),
"search_in_feed" => Minz_Request::paramString("search_in_feed"),
"search_in_authors" => Minz_Request::paramString("search_in_authors"),
"search_in_content" => Minz_Request::paramString("search_in_content"),
"mark_as_read" => (bool) Minz_Request::paramString("mark_as_read"),
"ignore_updated" => (bool) Minz_Request::paramString("ignore_updated"),
"webhook_url" => Minz_Request::paramString("webhook_url"),
"webhook_method" => Minz_Request::paramString("webhook_method"),
"webhook_headers" => array_filter(Minz_Request::paramTextToArray("webhook_headers", [])),
"webhook_body" => html_entity_decode(Minz_Request::paramString("webhook_body")),
"webhook_body_type" => Minz_Request::paramString("webhook_body_type"),
"enable_logging" => (bool) Minz_Request::paramString("enable_logging"),
];
$this->setSystemConfiguration($conf);
$this->$logsEnabled = $conf["enable_logging"];
_LOG($this->$logsEnabled, "saved config: ✅ " . json_encode($conf));
try {
if (Minz_Request::paramString("test_request")) {
sendReq(
$conf["webhook_url"],
$conf["webhook_method"],
$conf["webhook_body_type"],
$conf["webhook_body"],
$conf["webhook_headers"],
$conf["enable_logging"],
);
}
} catch (Throwable $err) {
_LOG_ERR($this->$logsEnabled, "Error when sending TEST webhook. " . $err);
}
}
}
public function processArticle($entry) {
if (!is_object($entry)) {
return;
}
if ($this->getSystemConfigurationValue("ignore_updated") && $entry->isUpdated()) {
_LOG(true, "⚠️ ignore_updated: " . $entry->link() . " ♦♦ " . $entry->title());
return $entry;
}
$searchInTitle = $this->getSystemConfigurationValue("search_in_title") ?? false;
$searchInFeed = $this->getSystemConfigurationValue("search_in_feed") ?? false;
$searchInAuthors = $this->getSystemConfigurationValue("search_in_authors") ?? false;
$searchInContent = $this->getSystemConfigurationValue("search_in_content") ?? false;
$patterns = $this->getSystemConfigurationValue("keywords") ?? [];
$markAsRead = $this->getSystemConfigurationValue("mark_as_read") ?? false;
$logsEnabled = (bool) $this->getSystemConfigurationValue("enable_logging") ?? false;
$this->$logsEnabled = (bool) $this->getSystemConfigurationValue("enable_logging") ?? false;
//-- do check keywords: ---------------------------
if (!is_array($patterns)) {
_LOG_ERR($logsEnabled, "❗️ No keywords defined in Webhook extension settings.");
return;
}
$title = "NOT INITIALIZED";
$link = "NOT INITIALIZED";
$additionalLog = "";
try {
$title = $entry->title();
$link = $entry->link();
foreach ($patterns as $pattern) {
if ($searchInTitle && self::isPatternFound("/{$pattern}/", $title)) {
_LOG($logsEnabled, "matched item by title ✔️ \"{$title}\" ❖ link: {$link}");
$additionalLog = "✔️ matched item with pattern: /{$pattern}/ ❖ title \"{$title}\" ❖ link: {$link}";
break;
}
if ($searchInFeed && (is_object($entry->feed()) && self::isPatternFound("/{$pattern}/", $entry->feed()->name()))) {
_LOG($logsEnabled, "matched item with pattern: /{$pattern}/ ❖ feed \"{$entry->feed()->name()}\", (title: \"{$title}\") ❖ link: {$link}");
$additionalLog = "✔️ matched item with pattern: /{$pattern}/ ❖ feed \"{$entry->feed()->name()}\", (title: \"{$title}\") ❖ link: {$link}";
break;
}
if ($searchInAuthors && self::isPatternFound("/{$pattern}/", $entry->authors(true))) {
_LOG($logsEnabled, "✔️ matched item with pattern: /{$pattern}/ ❖ authors \"{$entry->authors(true)}\", (title: {$title}) ❖ link: {$link}");
$additionalLog = "✔️ matched item with pattern: /{$pattern}/ ❖ authors \"{$entry->authors(true)}\", (title: {$title}) ❖ link: {$link}";
break;
}
if ($searchInContent && self::isPatternFound("/{$pattern}/", $entry->content())) {
_LOG($logsEnabled, "✔️ matched item with pattern: /{$pattern}/ ❖ content (title: \"{$title}\") ❖ link: {$link}");
$additionalLog = "✔️ matched item with pattern: /{$pattern}/ ❖ content (title: \"{$title}\") ❖ link: {$link}";
break;
}
}
if ($markAsRead) {
$entry->_isRead($markAsRead);
}
$this->sendArticle($entry, $additionalLog);
} catch (Throwable $err) {
_LOG_ERR($logsEnabled, "Error during sending article ({$link}\"{$title}\") ERROR: {$err}");
}
return $entry;
}
private function sendArticle($entry, string $additionalLog = ""): void {
try {
$webhookBodyType = $this->getSystemConfigurationValue("webhook_body_type");
$headers = $this->getSystemConfigurationValue("webhook_headers");
$bodyStr = $this->getSystemConfigurationValue("webhook_body");
$bodyStr = str_replace("__TITLE__", self::toSafeJsonStr($entry->title()), $bodyStr);
$bodyStr = str_replace("__FEED__", self::toSafeJsonStr($entry->feed()->name()), $bodyStr);
$bodyStr = str_replace("__URL__", self::toSafeJsonStr($entry->link()), $bodyStr);
$bodyStr = str_replace("__CONTENT__", self::toSafeJsonStr($entry->content()), $bodyStr);
$bodyStr = str_replace("__DATE__", self::toSafeJsonStr($entry->date()), $bodyStr);
$bodyStr = str_replace("__DATE_TIMESTAMP__", self::toSafeJsonStr($entry->date(true)), $bodyStr);
$bodyStr = str_replace("__AUTHORS__", self::toSafeJsonStr($entry->authors(true)), $bodyStr);
$bodyStr = str_replace("__TAGS__", self::toSafeJsonStr($entry->tags(true)), $bodyStr);
sendReq(
$this->getSystemConfigurationValue("webhook_url"),
$this->getSystemConfigurationValue("webhook_method"),
$this->getSystemConfigurationValue("webhook_body_type"),
$bodyStr,
$this->getSystemConfigurationValue("webhook_headers"),
(bool) $this->getSystemConfigurationValue("enable_logging"),
$additionalLog,
);
} catch (Throwable $err) {
_LOG_ERR($this->$logsEnabled, "ERROR in sendArticle: {$err}");
}
}
private function toSafeJsonStr(string|int $str): string {
$output = $str;
if (is_numeric($str)) {
$output = "{$str}";
} else {
$output = str_replace("/\"/", "", html_entity_decode($output));
}
return $output;
}
private function isPatternFound(string $pattern, string $text): bool {
if (empty($text) || empty($pattern)) {
return false;
}
try {
if (1 === preg_match($pattern, $text)) {
return true;
} elseif (strpos($text, $pattern) !== false) {
return true;
}
return false;
} catch (Throwable $err) {
_LOG_ERR($this->$logsEnabled, "ERROR in isPatternFound: (pattern: {$pattern}) {$err}");
return false;
}
}
public function getKeywordsData() {
return implode(PHP_EOL, $this->getSystemConfigurationValue("keywords") ?? []);
}
public function getWebhookHeaders() {
return implode(
PHP_EOL,
$this->getSystemConfigurationValue("webhook_headers") ?? ($this->webhook_headers ?? []),
);
}
public function getWebhookUrl() {
return $this->getSystemConfigurationValue("webhook_url") ?? $this->webhook_url;
}
public function getWebhookBody() {
$body = $this->getSystemConfigurationValue("webhook_body");
return !$body || $body === "" ? $this->webhook_body : $body;
}
public function getWebhookBodyType() {
return $this->getSystemConfigurationValue("webhook_body_type") ?? $this->webhook_body_type;
}
}
function _LOG(bool $logEnabled, $data): void {
if ($logEnabled) {
Minz_Log::warning("[WEBHOOK] " . $data);
}
}
function _LOG_ERR(bool $logEnabled, $data): void {
if ($logEnabled) {
Minz_Log::error("[WEBHOOK] ❌ " . $data);
}
}