diff --git a/package.json b/package.json index 905a9a22..50c66299 100644 --- a/package.json +++ b/package.json @@ -85,6 +85,7 @@ "react": "17", "react-best-gradient-color-picker": "^3.0.10", "react-dom": "17", + "rss-parser": "^3.13.0", "sortablejs": "^1.15.3", "svelte": "^5.1.9", "tailwindcss": "^3.4.11", diff --git a/src/SEQTA.ts b/src/SEQTA.ts index ac3fecf6..a8edaa4a 100644 --- a/src/SEQTA.ts +++ b/src/SEQTA.ts @@ -346,7 +346,7 @@ export function OpenWhatsNewPopup() { - + @@ -480,7 +480,7 @@ export function OpenAboutPage() { - + @@ -2818,7 +2818,7 @@ export async function SendNewsPage() { (titlediv! as HTMLElement).innerText = 'News' AppendLoadingSymbol('newsloading', '#news-container') - const response = await browser.runtime.sendMessage({ type: 'sendNews' }) + const response = await browser.runtime.sendMessage({ type: 'sendNews', source: settingsState.newsSource }) const newscontainer = document.querySelector('#news-container') document.getElementById('newsloading')?.remove() @@ -2835,7 +2835,7 @@ export async function SendNewsPage() { const articleimage = document.createElement('div') articleimage.classList.add('articleimage') - if (article.urlToImage == 'null') { + if (article.urlToImage == 'null' || article.urlToImage == null) { articleimage.style.cssText = ` background-image: url(${browser.runtime.getURL(LogoLightOutline)}); width: 20%; @@ -2854,6 +2854,8 @@ export async function SendNewsPage() { title.target = '_blank' const description = document.createElement('p') + + article.description = article.description.length > 400 ? article.description.substring(0, 400) + '...' : article.description description.innerHTML = article.description articletext.append(title, description) diff --git a/src/background.ts b/src/background.ts index 8fe1c907..f88e2369 100644 --- a/src/background.ts +++ b/src/background.ts @@ -1,5 +1,6 @@ import browser from 'webextension-polyfill' import type { SettingsState } from "@/types/storage"; +import { fetchNews } from './background/news'; function reloadSeqtaPages() { const result = browser.tabs.query({}) @@ -48,18 +49,8 @@ browser.runtime.onMessage.addListener((request: any, _: any, sendResponse: (resp break; case 'sendNews': - const date = new Date(); - const from = - date.getFullYear() + - '-' + - (date.getMonth() + 1) + - '-' + - (date.getDate() - 5); - - const url = `https://newsapi.org/v2/everything?domains=abc.net.au&from=${from}&apiKey=17c0da766ba347c89d094449504e3080`; - - GetNews(sendResponse, url); + fetchNews(request.source ?? 'australia', sendResponse); return true; default: @@ -67,18 +58,6 @@ browser.runtime.onMessage.addListener((request: any, _: any, sendResponse: (resp } }); -function GetNews(sendResponse: any, url: string) { - fetch(url) - .then((result) => result.json()) - .then((response) => { - if (response.code == 'rateLimited') { - GetNews(sendResponse, url += '%00'); - } else { - sendResponse({ news: response }); - } - }); -} - const DefaultValues: SettingsState = { onoff: true, animatedbk: true, @@ -166,6 +145,7 @@ const DefaultValues: SettingsState = { ], customshortcuts: [], lettergrade: false, + newsSource: 'australia', }; function SetStorageValue(object: any) { diff --git a/src/background/news.ts b/src/background/news.ts new file mode 100644 index 00000000..a480f3f4 --- /dev/null +++ b/src/background/news.ts @@ -0,0 +1,107 @@ +import Parser from 'rss-parser'; + +const fetchAustraliaNews = async (url: string, sendResponse: any) => { + fetch(url) + .then((result) => result.json()) + .then((response) => { + if (response.code == 'rateLimited') { + fetchAustraliaNews(url += '%00', sendResponse); + } else { + sendResponse({ news: response }); + } + }); +}; + +const rssFeedsByCountry: Record = { + usa: [ + "https://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml", + "https://www.huffpost.com/section/front-page/feed", + "https://www.npr.org/rss/rss.php", + ], + taiwan: [ + "https://focustaiwan.tw/rss", + "https://www.taipeitimes.com/rss/all.xml", + "https://international.thenewslens.com/rss", + ], + hong_kong: [ + "https://news.rthk.hk/rthk/en/rss.htm", + "https://www.scmp.com/rss/91/feed", + ], + panama: [ + "http://www.panama-guide.com/backend.php", + ], + canada: [ + "https://www.cbc.ca/cmlink/rss-topstories", + "https://www.theglobeandmail.com/?service=rss", + ], + singapore: [ + "https://www.straitstimes.com/news/singapore/rss.xml", + "https://www.channelnewsasia.com/rssfeeds/8395986", + ], + uk: [ + "http://feeds.bbci.co.uk/news/rss.xml", + "https://www.theguardian.com/uk/rss", + ], + japan: [ + "https://www.japantimes.co.jp/feed/topstories.xml", + "https://www3.nhk.or.jp/nhkworld/en/news/feeds/", + ], + netherlands: [ + "https://www.dutchnews.nl/feed/", + "http://feeds.nos.nl/nosnieuwsalgemeen", + ], +}; + +export async function fetchNews(source: string, sendResponse: any) { + const parser = new Parser(); + let feeds: string[]; + + if (source === "australia") { + const date = new Date(); + + const from = + date.getFullYear() + + '-' + + (date.getMonth() + 1) + + '-' + + (date.getDate() - 5); + + const url = `https://newsapi.org/v2/everything?domains=abc.net.au&from=${from}&apiKey=17c0da766ba347c89d094449504e3080`; + fetchAustraliaNews(url, sendResponse); + + return; + } + + if (rssFeedsByCountry[source.toLowerCase()]) { + // If the source is a country, fetch from predefined feeds + feeds = rssFeedsByCountry[source.toLowerCase()]; + } else if (source.startsWith("http")) { + // If the source is a URL, use it directly + feeds = [source]; + } else { + throw new Error("Invalid source. Provide a country code or a valid RSS feed URL."); + } + + const articlesPromises = feeds.map(async (feedUrl) => { + try { + const response = await fetch(feedUrl); + const feedString = await response.text(); + const feed = await parser.parseString(feedString); + + return feed.items.map((item) => ({ + title: item.title || "", + description: item.contentSnippet || "", + url: item.link || "", + urlToImage: null, + })); + } catch (error) { + console.error(`Failed to fetch RSS feed: ${feedUrl}`, error); + return []; + } + }); + + const articlesArray = await Promise.all(articlesPromises); + const articles = articlesArray.flat(); + + sendResponse({ news: { articles } }); +} diff --git a/src/interface/pages/settings.svelte b/src/interface/pages/settings.svelte index a2f98aa8..1c3bc2ad 100644 --- a/src/interface/pages/settings.svelte +++ b/src/interface/pages/settings.svelte @@ -61,8 +61,8 @@
-
-
+
+
Light logo @@ -71,8 +71,8 @@ {#if !standalone} - - + + {/if}
diff --git a/src/interface/pages/settings/general.svelte b/src/interface/pages/settings/general.svelte index 32c5ead7..7b90e277 100644 --- a/src/interface/pages/settings/general.svelte +++ b/src/interface/pages/settings/general.svelte @@ -15,7 +15,7 @@ {#snippet Setting({ title, description, Component, props }: SettingsList) } -
+

{title}

{description}

@@ -157,10 +157,32 @@ ] } }, + { + title: "News Feed Source", + description: "Choose sources of your news feed.", + id: 11, + Component: Select, + props: { + state: $settingsState.newsSource, + onChange: (value: string) => settingsState.newsSource = value, + options: [ + { value: "australia", label: "Australia" }, + { value: "usa", label: "USA" }, + { value: "taiwan", label: "Taiwan" }, + { value: "hong_kong", label: "Hong Kong" }, + { value: "panama", label: "Panama" }, + { value: "canada", label: "Canada" }, + { value: "singapore", label: "Singapore" }, + { value: "uk", label: "UK" }, + { value: "japan", label: "Japan" }, + { value: "netherlands", label: "Netherlands" } + ] + } + }, { title: "BetterSEQTA+", description: "Enables BetterSEQTA+ features", - id: 11, + id: 12, Component: Switch, props: { state: $settingsState.onoff, @@ -181,7 +203,7 @@ settingsState.devMode = isOn} />
-
+

Sensitive Hider

Replace sensitive content with mock data

diff --git a/src/types/storage.ts b/src/types/storage.ts index e5af03de..ee42560d 100644 --- a/src/types/storage.ts +++ b/src/types/storage.ts @@ -40,6 +40,7 @@ export interface SettingsState { originalDarkMode?: boolean; assessmentsAverage?: boolean; lettergrade: boolean; + newsSource?: string; } interface ToggleItem {