summaryrefslogtreecommitdiff
path: root/src/services
diff options
context:
space:
mode:
Diffstat (limited to 'src/services')
-rw-r--r--src/services/news.ts109
1 files changed, 109 insertions, 0 deletions
diff --git a/src/services/news.ts b/src/services/news.ts
new file mode 100644
index 0000000..a08f19c
--- /dev/null
+++ b/src/services/news.ts
@@ -0,0 +1,109 @@
+import { notify } from "@/utils/system";
+import { execAsync, GLib, GObject, property, readFileAsync, register, writeFileAsync } from "astal";
+import { news as config } from "config";
+
+export interface Article {
+ title: string;
+ link: string;
+ keywords: string;
+ creator: string;
+ description: string;
+ pubDate: string;
+}
+
+@register({ GTypeName: "News" })
+export default class News extends GObject.Object {
+ static instance: News;
+ static get_default() {
+ if (!this.instance) this.instance = new News();
+
+ return this.instance;
+ }
+
+ readonly #cachePath = `${CACHE}/news.json`;
+
+ #loading: boolean = false;
+ #articles: Article[] = [];
+
+ @property(Boolean)
+ get loading() {
+ return this.#loading;
+ }
+
+ @property(Object)
+ get articles() {
+ return this.#articles;
+ }
+
+ async getNews() {
+ if (!config.apiKey.get()) {
+ notify({
+ summary: "A newsdata.io API key is required",
+ body: "You can get one by creating an account at https://newsdata.io",
+ icon: "dialog-error-symbolic",
+ urgency: "critical",
+ actions: {
+ "Get API key": () => execAsync("app2unit -O -- https://newsdata.io").catch(console.error),
+ },
+ });
+ return;
+ }
+
+ this.#loading = true;
+ this.notify("loading");
+
+ let countries = config.countries.get().join(",");
+ const categories = config.categories.get().join(",");
+ const languages = config.languages.get().join(",");
+ const domains = config.domains.get().join(",");
+ const timezone = config.timezone.get();
+
+ if (countries.includes("current")) {
+ const out = JSON.parse(await execAsync("curl ipinfo.io")).country.toLowerCase();
+ countries = countries.replace("current", out);
+ }
+
+ let args = "removeduplicate=1&prioritydomain=top";
+ if (countries) args += `&country=${countries}`;
+ if (categories) args += `&category=${categories}`;
+ if (languages) args += `&language=${languages}`;
+ if (domains) args += `&domain=${domains}`;
+ if (timezone) args += `&timezone=${timezone}`;
+
+ const url = `https://newsdata.io/api/1/latest?apikey=${config.apiKey.get()}&${args}`;
+ try {
+ const res = JSON.parse(await execAsync(["curl", url]));
+ this.#articles = res.results;
+
+ let page = res.nextPage;
+ for (let i = 0; i < 3; i++) {
+ const res = JSON.parse(await execAsync(["curl", `${url}&page=${page}`]));
+ this.#articles.push(...res.results);
+ page = res.nextPage;
+ }
+
+ writeFileAsync(this.#cachePath, JSON.stringify(this.#articles)).catch(console.error);
+ } catch (e) {
+ console.error(e);
+
+ if (GLib.file_test(this.#cachePath, GLib.FileTest.EXISTS))
+ this.#articles = JSON.parse(await readFileAsync(this.#cachePath));
+ }
+ this.notify("articles");
+
+ this.#loading = false;
+ this.notify("loading");
+ }
+
+ constructor() {
+ super();
+
+ this.getNews().catch(console.error);
+ config.apiKey.subscribe(() => this.getNews().catch(console.error));
+ config.countries.subscribe(() => this.getNews().catch(console.error));
+ config.categories.subscribe(() => this.getNews().catch(console.error));
+ config.languages.subscribe(() => this.getNews().catch(console.error));
+ config.domains.subscribe(() => this.getNews().catch(console.error));
+ config.timezone.subscribe(() => this.getNews().catch(console.error));
+ }
+}