PSFeedHandler.psm1

function Find-PSFHCryptoRssRepositories {
    <#
    .SYNOPSIS
    Retrieves repository URLs from a specified text file.
 
    .DESCRIPTION
    This function reads a text file and retrieves the repository URLs listed within the file.
 
    .PARAMETER ListPath
    Specifies the path to the text file containing repository URLs.
     
    .NOTES
    Author: Your Name
    Date: Insert Date
 
    .EXAMPLE
    PS> Find-PSFHCryptoRssRepositories -ListPath "C:\Repositories.txt"
    Retrieves and displays the repository URLs listed in the specified text file.
 
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateScript({ Test-Path $_ -PathType Leaf })]
        [ValidatePattern('.*\.txt$')]
        [string]$ListPath
    )

    try {
        # Retrieve repository URLs from the text file
        $repositoryUrls = Get-Content -Path $ListPath

        # Output the retrieved repository URLs
        return $repositoryUrls
    }
    catch {
        # Handle errors
        Write-information "Failed to retrieve repository URLs from '$ListPath': $($_.Exception.Message)" -InformationAction Continue
        
        break
    }
}

function Get-PSFHFeedInfo {
    <#
    .SYNOPSIS
    Retrieves information about an RSS or Atom feed from a specified URL.
 
    .DESCRIPTION
    This function retrieves the feed type (RSS or Atom) and feed version for the specified URL.
 
    .PARAMETER Url
    Specifies the URL of the feed.
 
    .NOTES
    Author: Your Name
    Date: Insert Date
 
    .EXAMPLE
    PS> Get-PSFHFeedInfo -Url "https://example.com/feed"
    Retrieves and displays information about the RSS or Atom feed from the specified URL.
 
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [string]$Url
    )

    try {
        # Get the feed type (RSS or Atom)
        $feedType = Get-PSFHFeedType -Url $Url -XMLReader

        if (-not ($feedType -eq 'unknown')) {

            write-verbose $feedType
    
            # Get the feed version
            $feedVersion = Get-PSFHFeedVersion -Url $Url -FeedType $feedType

        }
        else {

            $feedVersion = 'unknown'

        }       

        $feed = Get-PSFHFeed -Url $Url
        $feedNewsCount = Invoke-PSFHFeedAnalysis -Feed $feed

        $LastPublishedDate = Invoke-PSFHFeedAnalysis -Feed $feed -LastPublishedDate

        $isoDateTime = Get-Date -Format 'yyyy-MM-ddTHH:mm:ss'
        Write-Verbose "ISO 8601 format with time: $isoDateTime"

        #$feed
        #$feedNewsCount = Invoke-PSFHFeedAnalysis -Feed

        # Create a custom object with feed information
        return [PSCustomObject]@{
            'Url'            = $Url
            'FeedType'       = $feedType
            'FeedVersion'    = $feedVersion
            'News count'     = $FeedNewsCount
            'Published date' = $LastPublishedDate
            'Test date'      = $isoDateTime
        }
    }
    catch {
        # Handle errors
        Write-information "Failed to retrieve feed info for '$Url': $($_.Exception.Message)" -InformationAction Continue
        break
    }
}

function Get-PSFHFeedType {
    <#
    .SYNOPSIS
    Retrieves the type (RSS or Atom) of a feed from a specified URL.
 
    .DESCRIPTION
    This function determines the type of feed (RSS or Atom) based on the response content type or the XML elements present in the feed.
 
    .PARAMETER Url
    Specifies the URL of the feed.
 
    .PARAMETER XMLReader
    Specifies whether to use XmlReader for parsing XML. Default is False.
 
    .NOTES
    Author: Your Name
    Date: Insert Date
 
    .EXAMPLE
    PS> Get-PSFHFeedType -Url "https://example.com/feed"
    Retrieves and returns the type of the feed from the specified URL.
 
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]$Url,

        [Parameter(Mandatory = $false)]
        [switch]$XMLReader
    )

    # Validate input
    #if (-not (Test-Connection -ComputerName $Url -Quiet)) {
    # Write-Error "Invalid URL: $Url"
    # return
    #}

    # Determine the feed type
    if ($XMLReader.IsPresent) {
        $feedType = Get-PSFHFeedTypeUsingXmlReader $Url
    }
    else {
        $feedType = Get-PSFHFeedTypeUsingWebRequest $Url
    }

    # Return the feed type
    return $feedType
}

function Get-PSFHFeedTypeUsingXmlReader {
    param (
        [Parameter(Mandatory = $true)]
        [string]$Url
    )

    try {
        # Create an XmlReader for the URL
        $ReaderSettings = New-Object System.Xml.XmlReaderSettings
        $ReaderSettings.IgnoreComments = $true
        $ReaderSettings.IgnoreWhitespace = $true
        $ReaderSettings.DtdProcessing = 'Parse'
        $Reader = [System.Xml.XmlReader]::Create($Url, $ReaderSettings)

        # Track the presence of required elements
        $atomElements = @("feed", "title", "link", "entry")
        $rssElements = @("rss", "channel", "title", "link", "description", "item")
        $hasAtomElements = $true
        $hasRSSElements = $true

        # Read the XML until reaching the end or missing required elements
        while ($Reader.Read() -and ($hasAtomElements -or $hasRSSElements)) {
            # Check if the current node is an element
            if ($Reader.NodeType -eq [System.Xml.XmlNodeType]::Element) {
                # Check if the element name matches Atom elements
                if ($hasAtomElements -and $Reader.Name -in $atomElements) {
                    $atomElements = $atomElements | Where-Object { $_ -ne $Reader.Name }

                    if (-not $atomElements) {
                        $hasAtomElements = $false
                        return "Atom"
                    }
                }

                # Check if the element name matches RSS elements
                if ($hasRSSElements -and $Reader.Name -in $rssElements) {
                    $rssElements = $rssElements | Where-Object { $_ -ne $Reader.Name }

                    if (-not $rssElements) {
                        $hasRSSElements = $false
                        return "RSS"
                    }
                }
            }
        }

        # Dispose the XmlReader when finished
        $xmlReader.Dispose()

        # If required elements are not found, return "Unknown"
        return "Unknown"
    }
    catch {
        # Handle errors
        Write-Error "Failed to retrieve feed type for '$Url': $($_.Exception.Message)"
        return "Unknown"
    }
}

function Get-PSFHFeedTypeUsingWebRequest {
    param (
        [Parameter(Mandatory = $true)]
        [string]$Url
    )

    try {
        # Create a web request to the specified URL
        $webRequest = [System.Net.WebRequest]::Create($Url)
        $response = $webRequest.GetResponse()
        $contentType = $response.ContentType
        $stream = $response.GetResponseStream()

        # Read the response stream using a StreamReader
        $streamReader = [System.IO.StreamReader]::new($stream)
        $rssContent = $streamReader.ReadToEnd()

        # Close the StreamReader and response
        $streamReader.Close()
        $response.Close()

        # Determine the feed type based on the content type or RSS elements
        if ($contentType -match 'atom') {
            return "Atom"
        }
        elseif ($contentType -match 'rss' -or ([xml]$rssContent).rss -or ([xml]$rssContent).rss.content -match 'rss') {
            return "RSS"
        }
        else {
            return "Unknown"
        }
    }
    catch {
        # Handle errors
        Write-Error "Failed to retrieve feed type for '$Url': $($_.Exception.Message)"
        return "Unknown"
    }
}

function Get-PSFHFeedVersion {
    <#
    .SYNOPSIS
    Retrieves the version of an RSS or Atom feed from a specified URL.
 
    .DESCRIPTION
    This function loads the XML document from the specified URL and examines the document's structure and namespaces to determine the feed version.
 
    .PARAMETER Url
    Specifies the URL of the feed.
 
    .PARAMETER FeedType
    Specifies the type of the feed (RSS or Atom).
 
    .NOTES
    Author: Your Name
    Date: Insert Date
 
    .EXAMPLE
    PS> Get-PSFHFeedVersion -Url "https://example.com/feed" -FeedType "RSS"
    Retrieves and returns the version of the RSS feed from the specified URL.
 
    #>

    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$Url,
        [Parameter(Mandatory = $true)]
        [string]$FeedType
    )

    try {
        # Create an XML document and load the feed from the URL
        #$xmlDocument = New-Object System.Xml.XmlDocument
        #$xmlDocument.Load($Url)
        [System.Xml.XmlDocument]$xmlDocument = Import-PSFHXmlDocument -Url $Url -TimeoutInSeconds 10

        
        # Create a namespace manager for the XML document
        $namespaceManager = New-Object System.Xml.XmlNamespaceManager($xmlDocument.NameTable)
        $namespaceManager.AddNamespace("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
        $namespaceManager.AddNamespace("atom", "http://www.w3.org/2005/Atom")

        if ($FeedType -eq "RSS") {
            write-verbose $feedType

            # Check for RSS version 2.0
            if ($xmlDocument.SelectSingleNode('//rss/@version', $namespaceManager)."#text" -eq "2.0") {
                write-verbose "2.0"

                return "2.0"
            }
            # Check for RSS version 1.0
            elseif ($null -ne $xmlDocument.SelectSingleNode('//rdf:RDF', $namespaceManager)) {
                write-verbose "1.0"

                return "1.0"
            }
        }
        elseif ($FeedType -eq "Atom") {

            $rootNode = $xmlDocument.DocumentElement
            
            # Check for Atom version 1.0
            if ($null -ne $rootNode.SelectSingleNode('//atom:feed[@xmlns="http://www.w3.org/2005/Atom"]', $namespaceManager)) {
                return "1.0"
            }
            # Check for Atom version 1.0
            elseif ($rootNode.NamespaceURI -contains "http://www.w3.org/2005/Atom") {
                return "1.0"
            }
            
            # Check for Atom version 0.3
            elseif ($null -ne $rootNode.SelectSingleNode('//atom:feed[starts-with(@xmlns,"http://purl.org/atom/")]', $namespaceManager)) {
                return "0.3"
            }
        }

        return "Unknown"
    }
    catch {
        # Handle errors
        Write-Information "Failed to retrieve feed version for '$Url': $($_.Exception.Message)" -InformationAction Continue
        break
    }
}

function Import-PSFHXmlDocument {
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]$Url,
        [Parameter(Mandatory = $true, Position = 1)]
        [ValidateNotNullOrEmpty()]
        [int]$TimeoutInSeconds,
        [Parameter(Mandatory = $false)]
        [ValidateSet("WebRequestClass", "InvokeWebRequest")]
        [string]$RequestType = "InvokeWebRequest"
    )

    try {

        if ($RequestType -eq "WebRequestClass") {
            # Create a web request to the specified URL
            $webRequest = [System.Net.WebRequest]::Create($Url)
            $webRequest.Timeout = $TimeoutInSeconds * 1000  # Convert timeout to milliseconds

            # Get the response from the web request
            $response = $webRequest.GetResponse()

            # Create an XML document
            $xmlDocument = New-Object System.Xml.XmlDocument

            # Load the XML document from the response stream
            $xmlDocument.Load($response.GetResponseStream())

            # Close the response
            $response.Close()

            # Validate XML structure
            if (!$xmlDocument.DocumentElement) {
                throw "Invalid XML structure in the RSS feed."
            }


            return $xmlDocument

        }
        elseif ($RequestType -eq "InvokeWebRequest") {
            $feed = [xml](Invoke-WebRequest -Uri $Url -TimeoutSec $TimeoutInSeconds).Content

            return $feed
        }
    }
    catch {
        
        Write-Information "Failed to load XML document from '$Url': $($_.Exception.Message)" -InformationAction Continue
        
        continue
    }
}

function Test-PSFHUrlAccessibility {
    <#
    .SYNOPSIS
    Tests the accessibility of a URL by sending a web request.
 
    .DESCRIPTION
    This function sends a web request to the specified URL to check its accessibility. If the request is successful and a response is received, it returns $true. Otherwise, it returns $false.
 
    .PARAMETER Url
    Specifies the URL to test for accessibility.
 
    .NOTES
    Author: Your Name
    Date: Insert Date
 
    .EXAMPLE
    PS> Test-PSFHUrlAccessibility -Url "https://example.com"
    Tests the accessibility of the specified URL and returns $true if accessible, otherwise returns $false.
 
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]$Url,

        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [int]$timeout = 5
    )

    Write-Verbose "Timeout: ${timeout}"

    try {
        # Create a web request to the specified URL
        $request = [System.Net.WebRequest]::Create($Url)
        $request.Timeout = $timeout * 1000  # Timeout value in milliseconds (e.g., 5 seconds)
        # Send the request and get the response
        $response = $request.GetResponse()

        # Close the response
        $response.Close()

        # Return $true to indicate accessibility
        return $true
    }
    catch {
        # Return $false to indicate inaccessibility
        return $false
    }
}

function Get-PSFHFeed {
    <#
    .SYNOPSIS
    Retrieves an feed from a specified URL.
 
    .DESCRIPTION
    This function uses Invoke-WebRequest cmdlet to fetch an feed from the specified URL. It expects the response content type to be 'application/xml'.
 
    .PARAMETER Url
    Specifies the URL of the feed.
 
    .NOTES
    Author: Your Name
    Date: Insert Date
 
    .EXAMPLE
    PS> Get-PSFHFeed -Url "https://example.com/rss"
    Retrieves and returns the feed from the specified URL.
 
    #>

    param (
        [Parameter(Mandatory = $true)]
        [string]$Url
    )

    try {
        # Fetch the feed using Invoke-WebRequest
        $Feed = Invoke-WebRequest -Uri $Url -ContentType 'application/xml'

        # Return the feed object
        return $Feed
    }
    catch {
        # Handle errors and display a message
        Write-Information "Failed to fetch the feed. Please check the URL and try again." -InformationAction Continue

        # Return $null to indicate failure
        return $null
    }
}

function Remove-PSFHDuplicateLines {
    param (
        [Parameter(Mandatory = $true)]
        [string]$InputFile,
        
        [Parameter(Mandatory = $true)]
        [string]$OutputFile
    )
    
    try {
        # Read the contents of the input file
        $lines = [System.IO.File]::ReadAllLines($InputFile)

        # Remove empty lines and whitespace from the lines
        $lines = $lines -replace '\s+', '' | Where-Object { $_ }

        # Sort the unique lines
        $sortedLines = $lines | Sort-Object -Unique

        # Write the sorted unique lines to the output file
        [System.IO.File]::WriteAllLines($OutputFile, $sortedLines)
    }
    catch {
        Write-Warning "Failed to deduplicate, sort data: $($_.Exception.Message)"
    }
}

function Remove-PSFHDuplicateCSVRows {
    <#
    .SYNOPSIS
    Removes duplicate rows from a CSV file based on a specific column.
 
    .DESCRIPTION
    This function imports a CSV file, groups the data by a specified column (e.g., "Feed URL"), and creates a new collection containing only the first occurrence of each unique value in that column. The resulting unique data is then exported to a new CSV file.
 
    .PARAMETER InputPath
    Specifies the path to the input CSV file.
 
    .PARAMETER OutputPath
    Specifies the path to the output CSV file containing the unique rows.
 
    .NOTES
    Author: Your Name
    Date: Insert Date
 
    .EXAMPLE
    PS> Remove-PSFHDuplicateCSVRows -InputPath "C:\data.csv" -OutputPath "C:\unique_data.csv"
    Removes duplicate rows based on the "Feed URL" column from the input CSV file and exports the unique data to the output CSV file.
 
    #>

    param (
        [Parameter(Mandatory = $true)]
        [string]$InputPath,

        [Parameter(Mandatory = $true)]
        [string]$OutputPath
    )

    # Import the CSV file
    $data = Import-Csv -Path $InputPath

    # Group the data by the specified column
    $groupedData = $data | Group-Object -Property "Feed URL"

    # Create a new collection for unique rows
    $uniqueData = @()

    # Iterate over the grouped data and add the first occurrence of each value to the unique collection
    foreach ($group in $groupedData) {
        $uniqueData += $group.Group[0]
    }

    # Export the unique data to a new CSV file
    $uniqueData | Export-Csv -Path $OutputPath -NoTypeInformation
}

function Test-PSFHUrlFormat {
    <#
    .SYNOPSIS
    Validates the format of a URL using regular expressions.
 
    .DESCRIPTION
    This function performs URL format validation using regular expressions. It checks if the provided URL matches the expected format: starting with "http://" or "https://", followed by a domain name, optional port number, and optional path.
 
    .PARAMETER Url
    Specifies the URL to validate.
 
    .NOTES
    Author: Your Name
    Date: Insert Date
 
    .EXAMPLE
    PS> Test-PSFHUrlFormat -Url "https://example.com"
    Validates the format of the specified URL and returns $true if the format is valid, otherwise returns $false.
 
    #>

    param (
        [Parameter(Mandatory = $true)]
        [string]$Url
    )

    # Perform URL format validation using regular expressions
    #$urlRegex = '^https?://([A-Za-z0-9]+\.[A-Za-z]{2,}|localhost)(:[0-9]+)?([/?].*)?$'
    #$urlRegex = '^https?://([A-Za-z0-9]+\.[A-Za-z0-9]{1,}|localhost)(:[0-9]+)?([/?].*)?$'
    $urlRegex = '^https?://[A-Za-z0-9.-]+(:[0-9]+)?(/.*)?$'

    if ($Url -match $urlRegex -and ([System.Uri]::IsWellFormedUriString($Url, [System.UriKind]::Absolute))) {
        return $true
    }
    else {
        return $false
    }
}

function Test-PSFHRssFeedValidity {
    <#
    .SYNOPSIS
    Checks the validity of an RSS or Atom feed.
 
    .DESCRIPTION
    This function checks the validity of an RSS or Atom feed by retrieving the feed content from the specified URL, analyzing its format, and counting the number of news items within a specified number of days.
 
    .PARAMETER FeedUrl
    Specifies the URL of the RSS or Atom feed to validate.
 
    .PARAMETER DaysToCount
    Specifies the number of days to consider when counting the news items in the feed. Default value is 7.
 
    .NOTES
    Author: Your Name
    Date: Insert Date
 
    .EXAMPLE
    PS> Test-PSFHRssFeedValidity -FeedUrl "https://example.com/feed" -DaysToCount 14
    Checks the validity of the specified RSS or Atom feed, considering news items published within the last 14 days. Returns $true if the feed is valid and contains news items, otherwise returns $false.
 
    #>

    param (
        [Parameter(ParameterSetName = 'FeedUrl', Mandatory = $true, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]$FeedUrl,

        [Parameter(ParameterSetName = 'FeedUrl', Mandatory = $false)]
        [int]$DaysToCount = 7,

        [Parameter(ParameterSetName = 'SaveFeedData')]
        [Parameter(ParameterSetName = 'FeedUrl')]
        [switch]$SaveFeedData,

        [Parameter(ParameterSetName = 'SaveFeedData', Mandatory = $true)]
        [Parameter(ParameterSetName = 'FeedUrl')]
        [ValidateNotNullOrEmpty()]
        [string]$OutPath

    )

    try {
        # Validate and sanitize the input URL
        $validatedUrl = [System.Uri]::EscapeUriString($FeedUrl)

        $feed = [xml](Invoke-WebRequest -Uri $validatedUrl).Content

        $feedType = Get-PSFHFeedType -Url $validatedUrl -XMLReader

        if ($feedType -eq "RSS") {
            $version = Get-PSFHFeedVersion -Url $validatedUrl -FeedType "RSS"
            $newsCount = ($feed.SelectNodes('//item') | Where-Object { [datetime]$_.pubDate -ge (Get-Date).AddDays(-$DaysToCount) }).Count
        }
        elseif ($feedType -eq "Atom") {
            $version = Get-PSFHFeedVersion -Url $validatedUrl -feedType "Atom"

            try {
                # https://www.rfc-editor.org/rfc/rfc4287.html#section-4.2.9
                $newsCount = ($feed.feed.entry | Where-Object { [DateTime]$_.published -gt (Get-Date).AddDays(-$DaysToCount) }).Count
    
            }
            catch {
                if ($null -eq $newsCount -or $newsCount -eq 0) {

                    # https://www.rfc-editor.org/rfc/rfc4287.html#section-4.2.15
                    $newsCount = ($feed.feed.entry | Where-Object { [DateTime]$_.updated -gt (Get-Date).AddDays(-$DaysToCount) }).Count

                }
            }
        }
        else {
            Write-information 'Invalid feed format: The feed does not conform to the expected RSS or Atom format.' -InformationAction Continue

            break
        }

        Write-Verbose "newsCount: ${newsCount}" 


        if ($newsCount -gt 0) {

            $isoDateTime = Get-Date -Format 'yyyy-MM-ddTHH:mm:ss'
            Write-Verbose "ISO 8601 format with time: $isoDateTime"
            
            # Save feed information to CSV
            $csvData = [PSCustomObject]@{
                'Feed URL'           = $validatedUrl
                'Type'               = $feedType
                'Version'            = $version
                'Days to count news' = $DaysToCount
                'News Count'         = $newsCount
                'Date check'         = $isoDateTime
            }

            if ($SaveFeedData.IsPresent) {
                Write-Verbose "save data" 
                $csvData | Export-Csv -Path $OutPath -Append -NoTypeInformation -Encoding UTF8
            }
            else {
                Write-Verbose "display data" 
                Write-information ($csvData | out-string) -InformationAction Continue
            }

            return $true 
        }
        else {
            return $false
        }
    }
    catch {
        Write-Error "Error occurred while checking the feed validity: $_"
        return $false
    }
}

function Export-PSFHFeedCSV {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateNotNullOrEmpty()]
        $FeedData,
        [Parameter(Mandatory = $true, Position = 1)]
        [ValidateNotNullOrEmpty()]
        [string]$OutPath
    )

    try {
        $FeedData | Export-Csv -Path $OutPath -Append -NoTypeInformation -Encoding UTF8
    
        return $true
    }
    catch {

        return $false

    }
    
}

# Function to analyze the feed and count the number of new items published in the last specified number of days
function Invoke-PSFHFeedAnalysis {
    <#
    .SYNOPSIS
    Counts the number of new items in a given feed within a specified number of days.
 
    .DESCRIPTION
    The Invoke-PSFHFeedAnalysis function takes a feed (Atom or RSS) and counts the number of new items published or updated within a specified number of days.
 
    .PARAMETER Feed
    The feed object to analyze. It can be an Atom feed or an RSS feed.
 
    .PARAMETER LastDaysElements
    The number of days to consider for counting new items.
 
    .EXAMPLE
    $rssFeed = Get-RssFeed -Url "http://example.com/rss-feed"
    Invoke-PSFHFeedAnalysis -Feed $rssFeed -LastDaysElements 7
 
    This example retrieves an RSS feed from "http://example.com/rss-feed" and counts the number of new items within the last 7 days.
 
    .EXAMPLE
    $atomFeed = Get-AtomFeed -Url "http://example.com/atom-feed"
    Invoke-PSFHFeedAnalysis -Feed $atomFeed -LastDaysElements 30
 
    This example retrieves an Atom feed from "http://example.com/atom-feed" and counts the number of new items within the last 30 days.
 
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [Microsoft.PowerShell.Commands.HtmlWebResponseObject]$Feed,

        [Parameter(Mandatory = $false)]
        [switch]$LastPublishedDate,

        [Parameter(Mandatory = $false)]
        $LastDaysElements = 0
    )

    if ($LastPublishedDate.IsPresent) {
        Write-Verbose "Invoke-PSFHFeedAnalysis feed.rss.channel: $(([xml]$feed).rss.channel | out-string) "
    
    }
    $currentDate = Get-Date
    $newItemCount = 0

    if ($LastDaysElements -gt 0) {
        # Check if the feed is Atom or RSS format
        if ($Feed.rss) {
            $_lastpublisheddate = ""
            if ($LastPublishedDate.IsPresent) {
                Write-Verbose $Feed.rss
                $_lastpublisheddate = ([xml]($Feed)).rss.channel.lastbuilddate
                return $_lastpublisheddate
            }
            # Iterate over each item in the RSS feed
            foreach ($item in $Feed.rss.channel.item) {
                $itemDate = Get-Date $item.pubDate
   
                # Calculate the number of days between the current date and the item's publication date
                $daysDifference = ($currentDate - $itemDate).TotalDays
   
                # Check if the item was published within the specified number of days
                if ($daysDifference -le $LastDaysElements) {
                    $newItemCount++
                }
            }
        }
        elseif ($Feed.feed) {
            # Iterate over each entry in the Atom feed
            foreach ($entry in $Feed.feed.entry) {
                $entryDate = Get-Date $entry.updated
   
                # Calculate the number of days between the current date and the entry's updated date
                $daysDifference = ($currentDate - $entryDate).TotalDays
   
                # Check if the entry was updated within the specified number of days
                if ($daysDifference -le $LastDaysElements) {
                    $newItemCount++
                }
            }
        }
   
        # Display the number of new items found within the specified number of days
        Write-verbose "Number of new items in the last ${LastDaysElements} days: ${newItemCount}"

        return $newItemCount
    
    }
    else {
        # Check if the feed is Atom or RSS format
        try {
    
            # Convert the HTML response content to an XmlDocument
            $xmlDocument = New-Object System.Xml.XmlDocument
            $xmlDocument.LoadXml($Feed.Content)

            # Get the item elements in the XmlDocument
            $rssitems = $xmlDocument.SelectNodes("//item")
            #$atomitems = $xmlDocument.SelectNodes("//entry")
            $atomitems = $xmlDocument.feed.entry

            
            # Count the number of items
            $RSSitemCount = $rssitems.Count
            $ATOMitemCount = $atomitems.Count

            if ($RSSitemCount -gt 0) {
                # Return the item count
                $newItemCount = $RSSitemCount        

                if ($LastPublishedDate.IsPresent) {
                    if (([xml]$Feed).rss.channel.lastBuildDate) {
                        Write-Verbose "Invoke-PSFHFeedAnalysis LastPublishedDate: $(([xml]$Feed).rss.channel.lastBuildDate)"
                        $_lastpublisheddate = ([xml]$Feed).rss.channel.lastBuildDate
                        return $_lastpublisheddate
                    }
                    if ((([xml]$Feed).rss.channel.item | Select-Object -First 1).pubdate) {
                        Write-Verbose "Invoke-PSFHFeedAnalysis LastPublishedDate: $((([xml]$Feed).rss.channel.item | Select-Object -First 1).pubdate)"
                        $_lastpublisheddate = (([xml]$Feed).rss.channel.item | select-object -first 1).pubdate  
                        return $_lastpublisheddate
                    }
                    Write-Verbose "Invoke-PSFHFeedAnalysis LastPublishedDate: $(([xml]$Feed).rss.channel.pubdate)"
                    $_lastpublisheddate = ([xml]$Feed).rss.channel.pubdate
                    return $_lastpublisheddate
                }
    

            }
            elseif ($ATOMitemCount -gt 0) {
                $newItemCount = $ATOMitemCount        
                if ($LastPublishedDate.IsPresent) {
                    return ([xml]$feed).feed.updated
                }
            }
            else {
                $newItemCount = 0
            }
        }
        catch {
            Write-Output "An error occurred while processing the feed: $($_.Exception.Message)"
            return -1  # Return -1 to indicate an error occurred
        }
    
        # Iterate over each entry in the Atom feed
   
        # Display the number of new items found within the specified number of days
        return $newItemCount
    }  
}

# The purpose of this function is to retrieve and display news items from an RSS feed.
function Get-PSFHFeedNews {
    <#
    .SYNOPSIS
    Retrieves and displays news items from an RSS feed.
 
    .DESCRIPTION
    The Get-PSFHFeedNews function retrieves and displays news items from an RSS feed. It can retrieve the RSS feed from a URL or use an existing XML object. The function returns an array of custom objects containing the title, published date, and link of the news items.
 
    .PARAMETER RSSUrl
    The URL of the RSS feed to retrieve and display news items from.
 
    .PARAMETER Feed
    An existing XML object representing the RSS feed to retrieve and display news items from.
 
    .PARAMETER LastItems
    The number of latest news items to retrieve and display. Defaults to 10 if not specified.
 
    .EXAMPLE
    Get-PSFHFeedNews -RSSUrl 'https://www.example.com/rss' -LastItems 5
 
    This command retrieves and displays the 5 latest news items from the RSS feed located at the specified URL.
 
    .EXAMPLE
    $xml = [xml](Get-Content 'feed.xml')
    Get-PSFHFeedNews -Feed $xml
 
    This command retrieves and displays the latest news items from an existing XML object representing an RSS feed.
 
    .NOTES
    Version: 2.0
    Author: Wojciech NapieraÅ‚a
    #>

    [CmdletBinding()]
    param (
        [Parameter(ParameterSetName = 'Url', Mandatory = $true, Position = 0, HelpMessage = 'The URL of the RSS feed.')]
        [ValidatePattern('^https?://.*')]
        [ValidateNotNullOrEmpty()]
        [string]$RSSUrl,

        [Parameter(ParameterSetName = 'Feed', Mandatory = $true, Position = 0, HelpMessage = 'The RSS feed as an XML object.')]
        [ValidateNotNullOrEmpty()]
        [xml]$Feed,

        [Parameter(Mandatory = $false, HelpMessage = 'The number of news items to display. Default is 10.')]
        [ValidateNotNullOrEmpty()]
        [ValidateRange(1, 100)]
        [int]$LastItems = 10
    )

    try {
        if ($PSCmdlet.ParameterSetName -eq 'Url') {
            # Validate URL
            if (![System.Uri]::IsWellFormedUriString($RSSUrl, [System.UriKind]::Absolute)) {
                throw "Invalid URL format: $RSSUrl"
            }

            # Create web request
            $webRequest = [System.Net.WebRequest]::Create($RSSUrl)
            $webRequest.Timeout = 10000  # Timeout set to 10 seconds

            # Get web response
            $webResponse = $webRequest.GetResponse()
            $stream = $webResponse.GetResponseStream()

            # Create XML reader
            $xmlReaderSettings = New-Object System.Xml.XmlReaderSettings
            $xmlReaderSettings.DtdProcessing = 'Ignore'
            $xmlReader = [System.Xml.XmlReader]::Create($stream, $xmlReaderSettings)

            # Read XML document
            $rssFeed = New-Object System.Xml.XmlDocument
            $rssFeed.Load($xmlReader)

            # Close resources
            $xmlReader.Close()
            $stream.Close()
            $webResponse.Close()
        }
        elseif ($PSCmdlet.ParameterSetName -eq 'Feed') {
            $rssFeed = $Feed
        }

        # Validate XML structure
        if (!$rssFeed.DocumentElement) {
            throw "Invalid XML structure in the RSS feed."
        }

        # Get news items
        $newsItems = $rssFeed.SelectNodes("//item")

        if ($newsItems.Count -gt 0) {
            # Create list of news items
            $newsList = foreach ($item in $newsItems | Select-Object -Last $LastItems) {
                $title = $item.SelectSingleNode("title").InnerText
                $pubDate = $item.SelectSingleNode("pubDate").InnerText
                $link = $item.SelectSingleNode("link").InnerText

                # Sanitize inputs
                $sanitizedTitle = $title.Replace("`n", "").Replace("`r", "")
                $sanitizedPubDate = $pubDate.Replace("`n", "").Replace("`r", "")
                $sanitizedLink = $link.Replace("`n", "").Replace("`r", "")

                # Create custom object
                [PSCustomObject]@{
                    Title         = $sanitizedTitle
                    PublishedDate = [datetime]$sanitizedPubDate
                    Link          = $sanitizedLink
                }
            }

            # Sort news items by published date
            $sortedNews = $newsList | Sort-Object -Property PublishedDate -Descending

            return $sortedNews
        }
        else {
            Write-Warning "No news items found in the RSS feed."
            return
        }
    }
    catch {
        Write-Error "Failed to retrieve and display news from the RSS feed: $($_.Exception.Message)"
    }
}

function Get-PSFHFeedDataFromFile {
    <#
    .SYNOPSIS
    Retrieves feed data from a file.
     
    .DESCRIPTION
    This function reads the contents of a file containing feed data and returns an XML object.
     
    .PARAMETER FeedFileFullName
    The full name of the file containing the feed data.
     
    .PARAMETER FeedType
    The type of feed data contained in the file (e.g. RSS, Atom).
     
    .EXAMPLE
    Get-PSFHFeedDataFromFile -FeedFileFullName "C:\feeds\myfeed.xml" -FeedType "RSS"
     
    This example retrieves the contents of the file "myfeed.xml" located in the "C:\feeds" directory and returns an XML object representing an RSS feed.
     
    .OUTPUTS
    System.Xml.XmlDocument
     
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]$FeedFileFullName,
    
        [Parameter(Mandatory = $false)]
        [ValidateSet("RSS", "Atom")]
        [string]$FeedType = "RSS"
    )
    
    try {
        Write-Verbose "Get-PSFHFeedDataFromFile FeedFileFullName: '${FeedFileFullName}'"
    
        if (-not (Test-Path $FeedFileFullName)) {
            throw "File not found: $FeedFileFullName"
        }
    
        <#
            $xmlSettings = New-Object System.Xml.XmlReaderSettings
            $xmlSettings.Schemas.Add($null, "$FeedType.xsd")
            $xmlSettings.ValidationType = [System.Xml.ValidationType]::Schema
        #>


        $xmlReader = [System.Xml.XmlReader]::Create($FeedFileFullName, $xmlSettings)
        $feedContent = New-Object System.Xml.XmlDocument
        $feedContent.Load($xmlReader)
    
        return $feedContent
    
    }
    catch {
        Write-Warning "Failed to get content of saved feed data: $($_.Exception.Message)"
    
        return $false
    }
}

# Downloads a feed from a given URL and saves it to a file.
function Save-PSFHFeed {
    <#
    .SYNOPSIS
    Downloads a feed from a given URL and saves it to a file.
 
    .DESCRIPTION
    The Save-PSFHFeed function downloads a feed from a given URL and saves it to a file. The function takes a mandatory URL parameter, an optional output file path parameter, and an optional timeout parameter. If the output file path is not specified, the function creates a temporary folder and saves the feed to a file with a unique name in that folder. If the function fails to save the feed data to a file, it returns false and displays a warning message.
 
    .PARAMETER Url
    The URL of the feed to download.
 
    .PARAMETER outputFilePath
    The path of the file to save the feed to. If not specified, a temporary folder is created and the feed is saved to a file with a unique name in that folder.
 
    .PARAMETER Timeout
    The timeout value in seconds for the web request. Default is 5 seconds.
 
    .EXAMPLE
    Save-PSFHFeed -Url "https://example.com/feed.xml" -outputFilePath "C:\feeds\example.xml"
 
    Downloads the feed from https://example.com/feed.xml and saves it to C:\feeds\example.xml.
 
    .NOTES
    Author: PowerShell Team
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidatePattern('^https?://.*')]
        [ValidateNotNullOrEmpty()]
        [string]$Url,

        [Parameter(Mandatory = $false)]
        [ValidateScript({
                if ($_ -ne $null) {
                    $isValid = $true
                    try {
                        [System.IO.Path]::GetFullPath($_) | Out-Null
                    }
                    catch {
                        $isValid = $false
                    }
                    if (-not $isValid) {
                        throw "Invalid file path: $_"
                    }
                }
                $true
            })]
        [string]$outputFilePath,

        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [int]$Timeout = 5
    )

    Write-Verbose "Downloading feed from '$Url'..."

    $uri = [System.Uri]$Url

    $uri = $uri.host + $uri.PathAndQuery

    # Remove any leading "www." from the domain if present
    $uri = $uri -replace '^www\.', ''
    $uri = $uri -replace '^https?:\/\/', ''

    # Replace any invalid characters with underscores
    $validFileName = $uri -replace '[^\w\d-]', '_'

    if (-not $outputFilePath) {
        $tempFolder = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath "FeedNewsTool"
        if (-not (Test-Path -Path $tempFolder -PathType Container)) {
            try {
                New-Item -Path $tempFolder -ItemType Directory -Force | Out-Null
            }
            catch {
                Write-Warning "Failed to create temporary folder: $($_.Exception.Message)"
                return $false
            }
        }
        $outputFilePath = Join-Path -Path $tempFolder -ChildPath "$validFileName.feed.tmp"
    }

    try {
        Invoke-WebRequest -Uri $Url -TimeoutSec $Timeout -OutFile $outputFilePath | Out-Null

        if (Test-Path -Path $outputFilePath -PathType Leaf) {
            Write-Verbose "Feed downloaded and saved to '$outputFilePath'."
            return $true
        }
        else {
            Write-Warning "Failed to save feed data to file: $outputFilePath"
            return $false
        }
    }
    catch {
        Write-Warning "Failed to download feed data: $($_.Exception.Message)"
        return $false
    }
}

function Get-PSFHRandomFile {
    <#
    .SYNOPSIS
    Retrieves a random file from a specified folder path.
     
    .DESCRIPTION
    This function retrieves a random file from a specified folder path. It uses the Get-ChildItem cmdlet to retrieve all files in the folder and returns a random file if there are any.
     
    .PARAMETER FolderPath
    The path of the folder to retrieve files from.
     
    .PARAMETER Count
    The number of random files to retrieve. Default is 1.
     
    .EXAMPLE
    Get-PSFHRandomFile -FolderPath "C:\Users\JohnDoe\Documents"
     
    This example retrieves a random file from the "Documents" folder of the "JohnDoe" user.
     
    .EXAMPLE
    Get-PSFHRandomFile -FolderPath "C:\Users\JohnDoe\Documents" -Count 5
     
    This example retrieves 5 random files from the "Documents" folder of the "JohnDoe" user.
     
    .NOTES
    Author: Wojciech NapieraÅ‚a
    Date: 26/06/2023
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateScript({ Test-Path $_ -PathType 'Container' })]
        [string]$FolderPath,
        [Parameter()]
        [ValidateRange(1, [int]::MaxValue)]
        [int]$Count = 1
    )
    
    try {
        $files = Get-ChildItem -Path $FolderPath -File
    }
    catch {
        Write-Error "An error occurred while retrieving files from the folder. Error message: $($_.Exception.Message)"
        return
    }
    
    if ($files.Count -eq 0) {
        Write-Warning "No files were found in the specified folder path. Please check if the path is correct and if there are files in the folder."
        return
    }
    
    $randomFiles = $files | Get-Random -Count $Count
    return $randomFiles.FullName
}

function Add-PSFHUrl {
    <#
.SYNOPSIS
    Adds a URL to a file.
.DESCRIPTION
    Adds a URL to a file.
.PARAMETER Url
    The URL to add.
.PARAMETER FilePath
    The path to the file.
.EXAMPLE
    Add-PSFHUrl -Url "https://www.google.com" -FilePath "C:\Urls.txt"
#>

    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$Url,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$FilePath
    )

    if (Test-Path -Path $FilePath) {
        $existingUrls = Get-PSFHUrl -FilePath $FilePath
        if ($existingUrls -contains $Url) {
            Write-Warning "URL already exists."
            return
        }
    }

    if ($PSCmdlet.ShouldProcess("$Url", "Add URL to file")) {
        Add-Content -Path $FilePath -Value $Url
        Write-Verbose "URL added successfully."
    }
}

<#
.SYNOPSIS
    Removes a URL from a file.
.DESCRIPTION
    Removes a URL from a file.
.PARAMETER Url
    The URL to remove.
.PARAMETER FilePath
    The path to the file.
.EXAMPLE
    Remove-PSFHUrl -Url "https://www.google.com" -FilePath "C:\Urls.txt"
#>

function Remove-PSFHUrl {
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$Url,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$FilePath
    )

    if (Test-Path -Path $FilePath) {
        $existingUrls = Get-PSFHUrl -FilePath $FilePath
        if ($existingUrls -notcontains $Url) {
            Write-Warning "URL does not exist."
            return
        }
    }

    if ($PSCmdlet.ShouldProcess("$Url", "Remove URL from file")) {
        $updatedUrls = $existingUrls | Where-Object { $_ -ne $Url }
        $updatedUrls | Set-Content -Path $FilePath
        Write-Verbose "URL removed successfully."
    }
}

<#
.SYNOPSIS
    Gets URLs from a file.
.DESCRIPTION
    Gets URLs from a file.
.PARAMETER FilePath
    The path to the file.
.EXAMPLE
    Get-PSFHUrl -FilePath "C:\Urls.txt"
#>

function Get-PSFHUrl {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$FilePath
    )

    if (-not (Test-Path -Path $FilePath)) {
        Write-Warning "File does not exist."
        return
    }

    Get-Content -Path $FilePath
}

function Format-PSFHFileUrls {
    <#
.SYNOPSIS
    Sorts URLs in a file.
.DESCRIPTION
    Sorts URLs in a file.
.PARAMETER FilePath
    The path to the file.
.EXAMPLE
    Format-PSFHFileUrls -FilePath "C:\Urls.txt"
#>

    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$FilePath
    )

    if (Test-Path -Path $FilePath) {
        $existingUrls = Get-PSFHUrl -FilePath $FilePath
    }

    if ($PSCmdlet.ShouldProcess("Sort URLs in file")) {
        $sortedUrls = $existingUrls | Sort-Object -Unique
        $sortedUrls | Set-Content -Path $FilePath
        Write-Verbose "URLs sorted successfully."
    }
}

<#
## References for Sort-Urls, Get-PSFHUrl, Remove-PSFHUrl, Add-PSFHUrl
 
- [Approved Verbs for Windows PowerShell Commands](https://docs.microsoft.com/en-us/powershell/scripting/developer/cmdlet/approved-verbs-for-windows-powershell-commands?view=powershell-7.1)
- [Writing a PowerShell Function](https://docs.microsoft.com/en-us/powershell/scripting/developer/module/writing-a-powershell-module-manifest?view=powershell-7.1)
- [PowerShell Best Practices and Style Guide](https://poshcode.gitbooks.io/powershell-practice-and-style/content/)
- [PowerShell Scripting Best Practices](https://docs.microsoft.com/en-us/powershell/scripting/best-practices/best-practices?view=powershell-7.1)
- [PowerShell Scripting Standards](https://docs.microsoft.com/en-us/powershell/scripting/developer/scripting-guidelines?view=powershell-7.1)
- [PowerShell Scripting Conventions](https://docs.microsoft.com/en-us/powershell/scripting/developer/scripting-conventions?view=powershell-7.1)
- [PowerShell Scripting Tips and Tricks](https://docs.microsoft.com/en-us/powershell/scripting/developer/tips-and-tricks/tips-and-tricks?view=powershell-7.1)
#>


function New-PSFHTempFeedFolder {
    param (
        [string]$FolderName
    )
    try {
        $tempfolder = [System.IO.Path]::GetTempPath()
        $feednewstoolfolderFullName = Join-Path $tempfolder $FolderName
        if (-not (Test-Path -Path $feednewstoolfolderFullName)) {
            [void](New-Item -Path $feednewstoolfolderFullName -ItemType Directory)
        }
        return $feednewstoolfolderFullName
    }
    catch {
        Write-Error "An error creating '$feednewstoolfolderFullName': $_"
    }
}

function Start-PSFeedHandler {
    <#
.SYNOPSIS
    Performs various operations related to news feeds.
 
.DESCRIPTION
    The 'FeedNewsTool' function allows you to process and analyze news feeds from either a file or a URL. It provides functionality for validating and analyzing feeds, checking URL accessibility, and removing duplicate rows from a CSV file.
 
.PARAMETER validateFeedListFromFilename
    Specifies that the function should process data from a file containing a list of feed URLs.
 
.PARAMETER validateFeedListFilename
    Specifies the filename of the feed list file. This parameter is mandatory when 'validateFeedListFromFilename' is used.
 
.PARAMETER TestFeedFromUrl
    Specifies that the function should process data from a feed URL.
 
.PARAMETER TestFeedUrl
    Specifies the URL of the feed. This parameter is mandatory when 'TestFeedFromUrl' is used.
 
.PARAMETER LastDaysElements
    Specifies the number of past days' elements to analyze in the feed. This parameter is optional and only applicable when 'validateFeedFromUrl' is used.
 
.PARAMETER RemoveDuplicateCSVRows
    Specifies that the function should process data in a file and remove duplicate rows.
 
.PARAMETER InputPath
    Specifies the input file path containing the CSV data. This parameter is mandatory when 'RemoveDuplicateCSVRows' is used.
 
.PARAMETER OutputPath
    Specifies the output file path where the CSV data without duplicate rows will be saved. This parameter is mandatory when 'RemoveDuplicateCSVRows' is used.
 
.PARAMETER TestUrlFormat
    Specifies the URL to test for proper format.
 
.PARAMETER ShowNews
    Specifies that the function should display news from a feed URL.
 
.PARAMETER ShowNewsUrlFeed
    Specifies the URL of the feed to display news from. This parameter is mandatory when 'ShowNews' is used.
 
.PARAMETER LastNewsCount
    Specifies the number of news items to display. This parameter is optional and only applicable when 'ShowNews' is used.
 
.PARAMETER ShowNewsfromFeed
    Specifies that the function should display news from a saved feed.
 
.PARAMETER ShowNewsfromFeedfileRandom
    Specifies that the function should display news from a random saved feed.
 
.PARAMETER AddFeed
    Specifies that the function should add a feed to the saved feed list.
 
.PARAMETER FeedUrl
    Specifies the URL of the feed to add. This parameter is mandatory when 'AddFeed' is used.
 
.PARAMETER SavePath
    Specifies the path to save the feed data. This parameter is mandatory when 'AddFeed' is used.
 
.PARAMETER Timeout
    Specifies the timeout value for URL accessibility testing. This parameter is optional and only applicable when 'AddFeed' is used.
 
.PARAMETER SaveFeed
    Specifies that the function should save a feed to a file.
 
.PARAMETER SaveFeedUrl
    Specifies the URL of the feed to save. This parameter is mandatory when 'SaveFeed' is used.
 
.PARAMETER SaveFile
    Specifies the file path to save the feed data. This parameter is mandatory when 'SaveFeed' is used.
 
.PARAMETER SaveFeedTimeout
    Specifies the timeout value for saving the feed data. This parameter is optional and only applicable when 'SaveFeed' is used.
 
.PARAMETER GetSavedFeed
    Specifies that the function should retrieve a saved feed.
 
.PARAMETER GetSavedFeedFileFullName
    Specifies the file path of the saved feed to retrieve. This parameter is mandatory when 'GetSavedFeed' is used.
#>

    [CmdletBinding(DefaultParameterSetName = 'Default')]
    param (
        [Parameter(ParameterSetName = 'ValidateFeedListFromFilename', Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$ValidateFeedListFilename,

        [Parameter(ParameterSetName = 'ValidateFeedListFromFilename')]
        [switch]$SaveToTempFeedFolder,

        [Parameter(ParameterSetName = 'TestFeedFromUrl', Mandatory)]
        [ValidateNotNullOrEmpty()]
        [ValidatePattern('^https?://.*')]
        [string]$TestFeedUrl,

        [Parameter(ParameterSetName = 'TestFeedFromUrl', Mandatory = $false)]
        [int]$LastDaysElements = 0,

        [Parameter(ParameterSetName = 'RemoveDuplicateCSVRows', Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$InputPath,
        [Parameter(ParameterSetName = 'RemoveDuplicateCSVRows', Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$OutputPath,

        [Parameter(ParameterSetName = 'TestUrlFormat', Mandatory)]
        [ValidateNotNullOrEmpty()]
        [ValidatePattern('^https?://.*')]
        [string]$TestUrlFormat,

        [Parameter(ParameterSetName = 'ShowNews', Mandatory)]
        [ValidateNotNullOrEmpty()]
        [ValidatePattern('^https?://.*')]
        [string]$ShowNewsUrlFeed,
        [Parameter(ParameterSetName = 'ShowNews', Mandatory = $false)]
        [Parameter(ParameterSetName = 'ShowNewsfromFeed', Mandatory = $false)]
        [int]$LastNewsCount = 10,

        [Parameter(ParameterSetName = 'ShowNewsfromFeed', Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$ShowNewsfromFeed,

        [Parameter(ParameterSetName = 'ShowNewsfromFeedfileRandom', Mandatory)]
        [ValidateNotNullOrEmpty()]
        [switch]$ShowNewsfromFeedfileRandom,

        [Parameter(ParameterSetName = 'AddFeed', Mandatory)]
        [ValidateNotNullOrEmpty()]
        [ValidatePattern('^https?://.*')]
        [string]$FeedUrl,
        [Parameter(ParameterSetName = 'AddFeed', Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$SavePath,
        [Parameter(ParameterSetName = 'AddFeed', Mandatory = $false)]
        [int]$Timeout = 5,

        [Parameter(ParameterSetName = 'SaveFeed', Mandatory)]
        [ValidateNotNullOrEmpty()]
        [ValidatePattern('^https?://.*')]
        [string]$SaveFeedUrl,
        [Parameter(ParameterSetName = 'SaveFeed', Mandatory = $false)]
        [int]$SaveFeedTimeout = 5,

        [Parameter(ParameterSetName = 'GetSavedFeed', Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$GetSavedFeedFileFullName,

        [Parameter(ParameterSetName = 'ShowCacheFolder')]
        [switch]$ShowCacheFolder
    )
    Get-PSFHBanner
    switch ($PSCmdlet.ParameterSetName) {
        'ValidateFeedListFromFilename' {
            # Process data from a file
            Write-Verbose "Processing data from file: ${ValidateFeedListFilename}"
            $newsDirectory = $PSScriptRoot

            $repositoryListPath = "${newsDirectory}\${ValidateFeedListFilename}"

            $cryptoRssRepos = Find-PSFHCryptoRssRepositories -ListPath $repositoryListPath

            foreach ($repo in $cryptoRssRepos) {
                # Prompt the user for an RSS URL
                $rssUrl = $repo

                # Validate the URL format
                if (Test-PSFHUrlFormat $rssUrl) {
                    # Fetch the RSS feed
                    [Microsoft.PowerShell.Commands.HtmlWebResponseObject]$responseFeed = Get-PSFHFeed $rssUrl
                    #$rssFeed.rss
                    if ($responseFeed) {
                        # Determine the type of feed and its version
                        #Get-FeedTypeAndVersion $responseFeed

                        $feedData = Get-PSFHFeedInfo $rssUrl

                        # Check the URL accessibility
                        #$feedData | Add-Member -TypeName noteproperty - Test-PSFHUrlAccessibility $rssUrl -timeout 2
                        $feedData | Add-Member -MemberType NoteProperty -NotePropertyName "UrlAccessibility" -NotePropertyValue (Test-PSFHUrlAccessibility $rssUrl -timeout 2)
                        #$feedData += Test-PSFHRssFeedValidity $rssUrl
                        $feedData | Add-Member -MemberType NoteProperty -NotePropertyName "RssFeedValidity" -NotePropertyValue (Test-PSFHRssFeedValidity $rssUrl)
                        # Analyze the feed
                        #$feedData += Invoke-PSFHFeedAnalysis $responseFeed
                        $feedData | Add-Member -MemberType NoteProperty -NotePropertyName "FeedAnalysis" -NotePropertyValue (Invoke-PSFHFeedAnalysis $responseFee)
                        if ($Save.IsPresent) {
                            try {
                                #Start-FeedNewsTool -SaveFeedUrl $rssUrl -SaveFeedTimeout 10
                                Save-PSFHFeed -Url $rssUrl -Timeout 10
                            }
                            catch {
                                $feedData
                                continue
                            }
                        }
                        $feedData
                    } 
                }
            }
            break
        }
        'TestFeedFromUrl' {
            # Process data from a URL
            Write-Verbose "Processing data from URL: ${TestFeedUrl}"
            if ((Test-PSFHUrlFormat -Url $TestFeedUrl) -and (Test-PSFHUrlAccessibility -Url $TestFeedUrl -Timeout 5)) {
                Write-Verbose "Test URL: OK"
                $objectfeed = Get-PSFHFeedInfo $TestFeedUrl

                $objectfeed | Format-List *
            }
            else {
                Write-Verbose "Test URL: Failed"
                break
            }

            break
        }
        'TestUrlFormat' {
            Test-PSFHUrlFormat -Url $TestUrlFormat
            break
        }
        'RemoveDuplicateCSVRows' {
            # Process data in file - remove duplicates
            Write-Verbose "Processing data from file: ${InputPath} to file: ${OutputPath}"
            Remove-PSFHDuplicateCSVRows -InputPath $InputPath -OutputPath $OutputPath
            break
        }
        'ShowNews' {
            Get-PSFHFeedNews -RSSUrl $ShowNewsUrlFeed -LastItems $LastNewsCount

            break
        }
        'ShowNewsfromFeed' {
            $feedfromfile = Get-PSFHFeedDataFromFile -FeedFileFullName $ShowNewsfromFeed
            Get-PSFHFeedNews -Feed $feedfromfile -LastItems $LastNewsCount
            break
        }
        'ShowNewsfromFeedfileRandom' {
            #$tempfolder = [System.IO.Path]::GetTempPath()
            $feednewstoolfolder = "feednewstool"
            #$feednewstoolfolderFullName = Join-Path $tempfolder $feednewstoolfolder
            $tempFeedFolder = New-PSFHTempFeedFolder -FolderName $feednewstoolfolder
            write-host "News feed folder: ""$tempFeedFolder""" -ForegroundColor DarkYellow
            $RandomFeedFileFullName = Get-PSFHRandomFile -FolderPath $tempFeedFolder -Count 1
            if ($RandomFeedFileFullName.count -gt 0) {
                $feedfromfile = Get-PSFHFeedDataFromFile -FeedFileFullName $RandomFeedFileFullName
                Get-PSFHFeedNews -Feed $feedfromfile -LastItems $LastNewsCount
            }
            break
        }
        'AddFeed' {
            Write-Verbose "Adding feed: ${FeedUrl}"
            if ((Test-PSFHUrlFormat -Url $FeedUrl) -and (Test-PSFHUrlAccessibility -Url $FeedUrl -Timeout $Timeout)) {
                $feedData = Get-PSFHFeedInfo -Url $FeedUrl

                if ($feedData) {
                    Write-Verbose "Exporting feed data to CSV"
                    Export-PSFHFeedCSV -FeedData $feedData -OutPath $SavePath
                }
            }
            break
        }
        'SaveFeed' {
            Write-Verbose "Saving feed: ${SaveFeedUrl}"
            $savefeedout = Save-PSFHFeed -Url $SaveFeedUrl -Timeout $SaveFeedTimeout
            Write-Verbose "Save feed output: ${savefeedout}"
            break
        }
        'GetSavedFeed' {
            Get-PSFHFeedDataFromFile -FeedFileFullName $GetSavedFeedFileFullName
            break
        }
        'ShowCacheFolder' {
            $FolderName = "FeedNewsTool"
            $tempfolder = [System.IO.Path]::GetTempPath()
            $feednewstoolfolderFullName = Join-Path $tempfolder $FolderName
            Open-PSFHExplorer -PathToOpen $feednewstoolfolderFullName
        }
        default {
            $helpinfo = @'
How to use, examples:
PSFeedHandler -TestFeedUrl "http://allafrica.com/tools/headlines/rdf/latest/headlines.rdf"
PSFeedHandler -InputPath .\News\feed_info2.csv -OutputPath .\News\feed_info3.csv
PSFeedHandler -TestUrlFormat "http://gigaom.com/feed/"
PSFeedHandler -ShowNewsUrlFeed "http://allafrica.com/tools/headlines/rdf/latest/headlines.rdf" -LastNewsCount 5
PSFeedHandler -ShowNewsfromFeed 'C:\Users\voytas\AppData\Local\Temp\FeedNewsTool\allafrica_com_tools_headlines_rdf_latest_headlines_rdf.feed.tmp'
PSFeedHandler -ShowNewsfromFeedfileRandom
PSFeedHandler -FeedUrl "http://allafrica.com/tools/headlines/rdf/latest/headlines.rdf" -SavePath .\News\test.txt -Timeout 10
PSFeedHandler -SaveFeedUrl "http://allafrica.com/tools/headlines/rdf/latest/headlines.rdf" -SaveFeedTimeout 10
PSFeedHandler -GetSavedFeedFileFullName 'C:\Users\voytas\AppData\Local\Temp\FeedNewsTool\allafrica_com_tools_headlines_rdf_latest_headlines_rdf.feed.tmp'
PSFeedHandler -ValidateFeedListFilename "repository_list.txt" -SaveToTempFeedFolder
    -save - save to temp feed folder
PSFeedHandler -ShowCacheFolder
'@

            Write-Output $helpinfo
            break
        }
    }
}

# Function to display the PowerShell Awesome Framework banner
function Get-PSFHBanner {
    param (
        
    )
    
    $banner = get-content -Path "${PSScriptRoot}\images\PSFHbanner.txt"
    Write-Output $banner
    return

}

function Open-PSFHExplorer {
    param (
        [string]$PathToOpen
    )
    try {
        Start-Process explorer.exe -ArgumentList $PathToOpen
    }
    catch {
        Write-Error "An error starting process: $_"
    }
}


#news9 -TestFeedFromUrl -TestFeedUrl "https://www.theguardian.com/uk/rss"
#news9 -TestFeedFromUrl -TestFeedUrl "http://digiday.com/feed/"

# atom example: "https://github.com/voytas75/find-taskserviceuser/releases.atom"
# atom example: https://rsshub.app/github/repos/voytas75
# atom example: https://rsshub.app/github/trending/daily/powershell/en
# atom example: https://rsshub.app/github/topics/powershell
# atom example: https://rsshub.app/github/search/powershell/bestmatch/desc

# rss example: "http://digiday.com/feed/"


<#
 
Get-FeedType : Failed to retrieve feed type for 'http://news.nationalgeographic.com/index.rss': Exception calling "Read" with "0" argument(s): "doctype' to nieoczekiwany
token. Oczekiwany token to 'DOCTYPE'. wiersz 2, pozycja 11."
At D:\dane\voytas\Dokumenty\visual_studio_code\lokalne\SkryptyVoytasa_lokalnie\News\news9.ps1:831 char:25
+ $feedType = Get-FeedType -Url $TestFeedUrl -XMLReader
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Get-FeedType
 
 
 
 
 
    #>

#New-Alias -Name "FeedNewsTool" -Value "Start-FeedNewsTool" -Force
#FeedNewsTool


# Save the current TLS security protocol to restore it later
#$oldProtocol = [Net.ServicePointManager]::SecurityProtocol

# Switch to using TLS 1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
# Get the name of the current module
$ModuleName = "PSFeedHandler"

# Get the installed version of the module
$ModuleVersion = [version]"0.0.1"

# Find the latest version of the module in the PSGallery repository
$LatestModule = Find-Module -Name $ModuleName -Repository PSGallery

try {
    if ($ModuleVersion -lt $LatestModule.Version) {
        Write-Host "An update is available for $($ModuleName). Installed version: $($ModuleVersion). Latest version: $($LatestModule.Version)." -ForegroundColor Red
    } 
}
catch {
    Write-Error "An error occurred while checking for updates: $_"
}

Set-Alias -Name "PSFH" -Value Start-PSFeedHandler
Set-Alias -Name "PSFeedHandler" -Value Start-PSFeedHandler

Write-Host "Welcome to PSFeedHandler!" -ForegroundColor DarkYellow
Write-Host "Thank you for using PSFH ($($moduleVersion))." -ForegroundColor Yellow
#Write-Host "Some important changes and informations that may be of interest to you:" -ForegroundColor Yellow
#Write-Host "- You can filter the built-in snippets (category: 'Example') by setting 'ShowExampleSnippets' to '`$false' in config. Use: 'Save-PAFConfiguration -settingName ""ShowExampleSnippets"" -settingValue `$false'" -ForegroundColor Yellow