-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
--- We've added the RSS feed searcher. --- Type: add Breaking: False Doc Required: True Part: 1/1
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
// | ||
// Nettify Copyright (C) 2023-2024 Aptivi | ||
// | ||
// This file is part of Nettify | ||
// | ||
// Nettify is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
// | ||
// Nettify is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY, without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
// | ||
// You should have received a copy of the GNU General Public License | ||
// along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
// | ||
|
||
using Nettify.Rss.Searcher; | ||
using System; | ||
|
||
namespace Nettify.Demo.Fixtures.Cases | ||
{ | ||
internal class RssFeedSearcher : IFixture | ||
{ | ||
public string FixtureID => "RssFeedSearcher"; | ||
public void RunFixture() | ||
{ | ||
// Prompt for search term | ||
Console.Write("Enter search term: "); | ||
string address = Console.ReadLine(); | ||
|
||
// Populate the feed info | ||
var feeds = SearcherTools.GetRssFeeds(address); | ||
foreach (var feed in feeds) | ||
{ | ||
Console.WriteLine(" Title: {0}", feed.Title); | ||
Console.WriteLine(" Description: {0}", feed.Description); | ||
Console.WriteLine(" URL: {0}\n", feed.Id); | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
// | ||
// Nettify Copyright (C) 2023-2024 Aptivi | ||
// | ||
// This file is part of Nettify | ||
// | ||
// Nettify is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
// | ||
// Nettify is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY, without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
// | ||
// You should have received a copy of the GNU General Public License | ||
// along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
// | ||
|
||
using Newtonsoft.Json; | ||
using System.Diagnostics; | ||
|
||
namespace Nettify.Rss.Searcher | ||
{ | ||
/// <summary> | ||
/// Feedly search result class instance | ||
/// </summary> | ||
[DebuggerDisplay("{title}: {description} [{website} | {id}]")] | ||
public class SearcherInstance | ||
{ | ||
[JsonProperty("score")] | ||
private readonly double score; | ||
Check warning on line 32 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (ubuntu-latest)
Check warning on line 32 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (windows-latest)
|
||
[JsonProperty("lastUpdated")] | ||
private readonly double lastUpdated; | ||
Check warning on line 34 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (macos-latest)
Check warning on line 34 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (ubuntu-latest)
|
||
[JsonProperty("estimatedEngagement")] | ||
private readonly int estimatedEngagement; | ||
[JsonProperty("description")] | ||
private readonly string description; | ||
Check warning on line 38 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (macos-latest)
Check warning on line 38 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (macos-latest)
Check warning on line 38 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (ubuntu-latest)
Check warning on line 38 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / Make API Reference
|
||
[JsonProperty("language")] | ||
private readonly string language; | ||
Check warning on line 40 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (macos-latest)
Check warning on line 40 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / Make API Reference
|
||
[JsonProperty("id")] | ||
private readonly string id; | ||
Check warning on line 42 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (macos-latest)
Check warning on line 42 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (ubuntu-latest)
Check warning on line 42 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / Make API Reference
|
||
[JsonProperty("title")] | ||
private readonly string title; | ||
Check warning on line 44 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (macos-latest)
Check warning on line 44 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (ubuntu-latest)
Check warning on line 44 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / Make API Reference
|
||
[JsonProperty("coverUrl")] | ||
private readonly string coverUrl; | ||
Check warning on line 46 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / Make API Reference
Check warning on line 46 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (windows-latest)
|
||
[JsonProperty("website")] | ||
private readonly string website; | ||
Check warning on line 48 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (macos-latest)
Check warning on line 48 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (macos-latest)
Check warning on line 48 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (ubuntu-latest)
Check warning on line 48 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (ubuntu-latest)
Check warning on line 48 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / Make API Reference
|
||
[JsonProperty("topics")] | ||
private readonly string[] topics; | ||
Check warning on line 50 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (ubuntu-latest)
Check warning on line 50 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (windows-latest)
|
||
[JsonProperty("subscribers")] | ||
private readonly int subscribers; | ||
Check warning on line 52 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (macos-latest)
Check warning on line 52 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (ubuntu-latest)
Check warning on line 52 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (ubuntu-latest)
Check warning on line 52 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / Make API Reference
Check warning on line 52 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (windows-latest)
|
||
[JsonProperty("feedId")] | ||
private readonly string feedId; | ||
Check warning on line 54 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (ubuntu-latest)
|
||
[JsonProperty("updated")] | ||
private readonly double updated; | ||
Check warning on line 56 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (macos-latest)
Check warning on line 56 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (ubuntu-latest)
|
||
[JsonProperty("velocity")] | ||
private readonly double velocity; | ||
Check warning on line 58 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (macos-latest)
|
||
[JsonProperty("iconUrl")] | ||
private readonly string iconUrl; | ||
Check warning on line 60 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (macos-latest)
|
||
[JsonProperty("visualUrl")] | ||
private readonly string visualUrl; | ||
[JsonProperty("partial")] | ||
private readonly bool partial; | ||
Check warning on line 64 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (macos-latest)
Check warning on line 64 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (ubuntu-latest)
|
||
[JsonProperty("logo")] | ||
private readonly string logo; | ||
Check warning on line 66 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (macos-latest)
Check warning on line 66 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (ubuntu-latest)
|
||
[JsonProperty("relatedLayout")] | ||
private readonly string relatedLayout; | ||
Check warning on line 68 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (macos-latest)
Check warning on line 68 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (macos-latest)
Check warning on line 68 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (ubuntu-latest)
Check warning on line 68 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (ubuntu-latest)
Check warning on line 68 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / Make API Reference
|
||
[JsonProperty("relatedTarget")] | ||
private readonly string relatedTarget; | ||
Check warning on line 70 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (macos-latest)
Check warning on line 70 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (macos-latest)
Check warning on line 70 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (ubuntu-latest)
|
||
[JsonProperty("accentColor")] | ||
private readonly string accentColor; | ||
Check warning on line 72 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (macos-latest)
Check warning on line 72 in Nettify/Rss/Searcher/SearcherInstance.cs GitHub Actions / build (macos-latest)
|
||
|
||
/// <summary> | ||
/// Feed score | ||
/// </summary> | ||
public double Score => | ||
score; | ||
|
||
/// <summary> | ||
/// Last updated time in Unix time (milliseconds) | ||
/// </summary> | ||
public double LastUpdated => | ||
lastUpdated; | ||
|
||
/// <summary> | ||
/// Estimated feed engagement rate | ||
/// </summary> | ||
public int EstimatedEngagement => | ||
estimatedEngagement; | ||
|
||
/// <summary> | ||
/// Description of the feed | ||
/// </summary> | ||
public string Description => | ||
description; | ||
|
||
/// <summary> | ||
/// Language of the feed in two-letter language code, such as en, de, nl, ... | ||
/// </summary> | ||
public string Language => | ||
language; | ||
|
||
/// <summary> | ||
/// Feed ID, including a link to the feed itself | ||
/// </summary> | ||
public string Id => | ||
id; | ||
|
||
/// <summary> | ||
/// Feed title | ||
/// </summary> | ||
public string Title => | ||
title; | ||
|
||
/// <summary> | ||
/// Cover URL | ||
/// </summary> | ||
public string CoverUrl => | ||
coverUrl; | ||
|
||
/// <summary> | ||
/// Website of a feed (main home page) | ||
/// </summary> | ||
public string Website => | ||
website; | ||
|
||
/// <summary> | ||
/// Feed topics and categories | ||
/// </summary> | ||
public string[] Topics => | ||
topics; | ||
|
||
/// <summary> | ||
/// Number of Feedly subscribers | ||
/// </summary> | ||
public int Subscribers => | ||
subscribers; | ||
|
||
/// <summary> | ||
/// Feed ID, including a link to the feed itself | ||
/// </summary> | ||
public string FeedId => | ||
feedId; | ||
|
||
/// <summary> | ||
/// Updated time in Unix time (milliseconds) | ||
/// </summary> | ||
public double Updated => | ||
updated; | ||
|
||
/// <summary> | ||
/// How frequent does this feed get updated? | ||
/// </summary> | ||
public double Velocity => | ||
velocity; | ||
|
||
/// <summary> | ||
/// Icon URL | ||
/// </summary> | ||
public string IconUrl => | ||
iconUrl; | ||
|
||
/// <summary> | ||
/// Visual URL | ||
/// </summary> | ||
public string VisualUrl => | ||
visualUrl; | ||
|
||
/// <summary> | ||
/// Whether this syndication is partial or not | ||
/// </summary> | ||
public bool Partial => | ||
partial; | ||
|
||
/// <summary> | ||
/// Logo URL | ||
/// </summary> | ||
public string Logo => | ||
logo; | ||
|
||
/// <summary> | ||
/// Related layout | ||
/// </summary> | ||
public string RelatedLayout => | ||
relatedLayout; | ||
|
||
/// <summary> | ||
/// Related target | ||
/// </summary> | ||
public string RelatedTarget => | ||
relatedTarget; | ||
|
||
/// <summary> | ||
/// Accent color in hexadecimals | ||
/// </summary> | ||
public string AccentColor => | ||
accentColor; | ||
|
||
[JsonConstructor] | ||
private SearcherInstance() | ||
{ } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
// | ||
// Nettify Copyright (C) 2023-2024 Aptivi | ||
// | ||
// This file is part of Nettify | ||
// | ||
// Nettify is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
// | ||
// Nettify is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY, without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
// | ||
// You should have received a copy of the GNU General Public License | ||
// along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
// | ||
|
||
using Nettify.Rss.Instance; | ||
using Newtonsoft.Json; | ||
using Newtonsoft.Json.Linq; | ||
using System; | ||
using System.Net.Http; | ||
using System.Threading.Tasks; | ||
|
||
namespace Nettify.Rss.Searcher | ||
{ | ||
/// <summary> | ||
/// RSS feed searcher tools powered by Feedly | ||
/// </summary> | ||
public static class SearcherTools | ||
{ | ||
private static readonly HttpClient client = new(); | ||
private const string feedlySearchLink = "https://cloud.feedly.com/v3/search/feeds/?n=100000&query="; | ||
|
||
/// <summary> | ||
/// Gets RSS feed list from search query (up to 100,000 feeds) | ||
/// </summary> | ||
/// <param name="searchTerm">Search term</param> | ||
/// <returns>List of RSS feeds</returns> | ||
public static SearcherInstance[] GetRssFeeds(string searchTerm) | ||
{ | ||
string feedsJson = InitializeFeedJsonAsync(searchTerm).Result; | ||
return DeserializeFeedJson(feedsJson); | ||
} | ||
|
||
/// <summary> | ||
/// Gets RSS feed list from search query asynchronously (up to 100,000 feeds) | ||
/// </summary> | ||
/// <param name="searchTerm">Search term</param> | ||
/// <returns>List of RSS feeds</returns> | ||
public static async Task<SearcherInstance[]> GetRssFeedsAsync(string searchTerm) | ||
{ | ||
string feedsJson = await InitializeFeedJsonAsync(searchTerm); | ||
return DeserializeFeedJson(feedsJson); | ||
} | ||
|
||
/// <summary> | ||
/// Gets a working <see cref="RSSFeed"/> instance from one of the search results | ||
/// </summary> | ||
/// <param name="feed">Feed obtained from <see cref="GetRssFeeds(string)"/> or <see cref="GetRssFeedsAsync(string)"/></param> | ||
/// <returns>A working RSS feed instance</returns> | ||
public static RSSFeed GetFeedFromSearcher(SearcherInstance feed) | ||
{ | ||
// Strip the "feed/" prefix | ||
string id = feed.FeedId ?? feed.Id; | ||
id = id.Substring(5); | ||
|
||
// Now, try to get the RSS feed | ||
var rssFeed = new RSSFeed(id, RSSFeedType.Infer); | ||
rssFeed.Refresh(); | ||
|
||
// If successful, return it | ||
return rssFeed; | ||
} | ||
|
||
/// <summary> | ||
/// Gets a working <see cref="RSSFeed"/> instance from one of the search results asynchronously | ||
/// </summary> | ||
/// <param name="feed">Feed obtained from <see cref="GetRssFeeds(string)"/> or <see cref="GetRssFeedsAsync(string)"/></param> | ||
/// <returns>A working RSS feed instance</returns> | ||
public static async Task<RSSFeed> GetFeedFromSearcherAsync(SearcherInstance feed) | ||
{ | ||
// Strip the "feed/" prefix | ||
string id = feed.FeedId ?? feed.Id; | ||
id = id.Substring(5); | ||
|
||
// Now, try to get the RSS feed | ||
var rssFeed = new RSSFeed(id, RSSFeedType.Infer); | ||
await rssFeed.RefreshAsync(); | ||
|
||
// If successful, return it | ||
return rssFeed; | ||
} | ||
|
||
private static async Task<string> InitializeFeedJsonAsync(string searchTerm) | ||
{ | ||
if (string.IsNullOrWhiteSpace(searchTerm)) | ||
searchTerm = "tech"; | ||
Uri feedsUri = new(feedlySearchLink + searchTerm); | ||
string response = await client.GetStringAsync(feedsUri); | ||
return response; | ||
} | ||
|
||
private static SearcherInstance[] DeserializeFeedJson(string feedsJson) | ||
{ | ||
var token = JObject.Parse(feedsJson); | ||
var feedsToken = (JArray)token["results"]; | ||
var instances = JsonConvert.DeserializeObject<SearcherInstance[]>(feedsToken.ToString()); | ||
return instances; | ||
} | ||
} | ||
} |