RSSWidget

Script Widget - RSS Feed.

python · Common Widgets

Source

import email.utils
from time import mktime

import demistomock as demisto  # noqa: F401
import feedparser
from CommonServerPython import *  # noqa: F401
from feedparser.util import FeedParserDict
from markdownify import markdownify


class Client(BaseClient):
    """Client for RSS Feed.
    Attributes:
        server_url(str): The RSS URL.
        use_ssl: Whether to use ssl.
        proxy(str): Use system proxy.
    """

    def __init__(self, server_url, use_ssl, proxy):
        super().__init__(base_url=server_url, proxy=proxy, verify=use_ssl)

    def get_feed_data(self) -> str:
        """Retrieves the data from the RSS feed."""
        return self._http_request(method="GET", resp_type="text")


def parse_feed_data(feed_response: str) -> FeedParserDict:
    """Parses the data from the RSS feed.

    Args:
        feed_response (str): The raw data from the RSS feed.

    Returns:
        FeedParserDict: Parsed RSS feed data.
    """
    try:
        return feedparser.parse(feed_response)
    except Exception as err:
        raise DemistoException(f"Failed to parse feed.\nError:\n{err!s}", exception=err)


def collect_entries_data_from_response(parsed_feed_data: FeedParserDict, limit: Union[int, None]) -> List[Dict[str, Any]]:
    """Collects relevant data from the parsed RSS feed entries.

    Args:
        parsed_feed_data (FeedParserDict): Parsed RSS feed data.
        limit (Union[int, None]): Maximum number of results to return.

    Returns:
        List[Dict[str, Any]]: The data from the RSS feed relevant for the widget.
    """
    entries_data: List[Dict[str, Any]] = []
    if not parsed_feed_data:
        raise DemistoException("Could not parse feed data.")

    if not limit:
        return entries_data

    for entry in parsed_feed_data.entries:
        if entry:
            published = email.utils.parsedate(entry.published)
            if not published:
                continue

            published_dt = datetime.fromtimestamp(mktime(published))
            published_formatted = published_dt.strftime("%B %-d, %Y %-I:%M %p")

            entries_data.append(
                {
                    "timestamp": published_formatted,
                    "link": entry.get("link"),
                    "title": entry.get("title"),
                    "summary": markdownify(entry.get("summary")),
                    "author": entry.get("author"),
                }
            )

            if limit != "all":
                limit -= 1

                if limit == 0:
                    break

    return entries_data


def create_widget_content(entries_data: List[Dict[str, Any]]) -> str:
    """Creates the human readable text for the widget.

    Args:
        entries_data (List[Dict[str, Any]]): The data from the  RSS feed relevant for the widget.

    Returns:
        str: The widget's content.
    """
    content: str = ""

    for entry_data in entries_data:
        content += f'**[{entry_data["title"]}]({entry_data["link"]})**\n'

        # Markdown formatting is supported from 6.5
        if is_demisto_version_ge("6.5"):
            content += "{{color:#89A5C1}}" + f'(*Posted {entry_data["timestamp"]} by {entry_data["author"]}*)\n'
        else:
            content += f'*Posted {entry_data["timestamp"]} by {entry_data["author"]}*\n'

        content += f'{entry_data["summary"]}\n'
        content += "\n\n"

    if not content:
        content = "## No entries were found."

    return content


def main():
    args = demisto.args()

    client = Client(
        server_url=args["url"],
        use_ssl=not args.get("insecure", False),
        proxy=args.get("proxy", False),
    )

    limit = sys.maxsize if not args.get("limit") else arg_to_number(args.get("limit"))

    try:
        rss_raw_data = client.get_feed_data()
        parsed_feed_data = parse_feed_data(rss_raw_data)
        entries_data = collect_entries_data_from_response(parsed_feed_data, limit)
        content = create_widget_content(entries_data)
    except Exception as e:
        return_error(str(e))

    return_results(
        {
            "Type": EntryType.NOTE,
            "ContentsFormat": EntryFormat.MARKDOWN,
            "Contents": content,
        }
    )


if __name__ in ("__builtin__", "builtins", "__main__"):  # pragma: no cover
    main()

README

RSSWidget Script

Script Data


Name Description
Script Type python3
Cortex XSOAR Version 5.5.0

Inputs


Argument Name Description
url The URL of the RSS feed.
limit Maximum number of entries to return.
insecure Trust any certificate (not secure). Possible values: “true” and “false”. Default: “false”.
proxy Use system proxy settings. Possible values: “true” and “false”. Default: “false”.

Outputs

There are no outputs for this script.

Script Example


!RSSWidget url=https://threatpost.com/feed/

Human Readable Output


Article Title #1

Posted June 25, 2021 3:35 PM by Timor

Article #1 Summary

Article Title #2

Posted June 18, 2021 3:35 PM by Shai

Article #2 Summary