feat: parse rss

This commit is contained in:
DIYgod
2023-12-11 02:50:46 +08:00
parent 64918e370b
commit 911c2c7395
12 changed files with 17948 additions and 24 deletions
+6 -1
View File
@@ -12,9 +12,14 @@
"dependencies": {
"@iconify-json/mingcute": "^1.1.13",
"@plasmohq/messaging": "^0.6.1",
"@plasmohq/storage": "^1.9.0",
"lodash": "^4.17.21",
"plasmo": "0.84.0",
"psl": "^1.9.0",
"react": "18.2.0",
"react-dom": "18.2.0"
"react-dom": "18.2.0",
"route-recognizer": "^0.3.4",
"rss-parser": "^3.13.0"
},
"devDependencies": {
"@ianvs/prettier-plugin-sort-imports": "4.1.1",
+69 -9
View File
@@ -11,15 +11,30 @@ dependencies:
'@plasmohq/messaging':
specifier: ^0.6.1
version: 0.6.1(react@18.2.0)
'@plasmohq/storage':
specifier: ^1.9.0
version: 1.9.0(react@18.2.0)
lodash:
specifier: ^4.17.21
version: 4.17.21
plasmo:
specifier: 0.84.0
version: 0.84.0(postcss@8.4.32)(react-dom@18.2.0)(react@18.2.0)
version: 0.84.0(lodash@4.17.21)(postcss@8.4.32)(react-dom@18.2.0)(react@18.2.0)
psl:
specifier: ^1.9.0
version: 1.9.0
react:
specifier: 18.2.0
version: 18.2.0
react-dom:
specifier: 18.2.0
version: 18.2.0(react@18.2.0)
route-recognizer:
specifier: ^0.3.4
version: 0.3.4
rss-parser:
specifier: ^3.13.0
version: 3.13.0
devDependencies:
'@ianvs/prettier-plugin-sort-imports':
@@ -1724,7 +1739,7 @@ packages:
nullthrows: 1.1.1
dev: false
/@plasmohq/consolidate@0.17.0(react-dom@18.2.0)(react@18.2.0):
/@plasmohq/consolidate@0.17.0(lodash@4.17.21)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-Na8imBnvzYPtzkK+9Uv9hPZ/oJti/0jgiQWD222SHxHw2QCVuR4KzslxXCy/rS8gGluSiTs1BGVvc3d2O6aJCA==}
engines: {node: '>= 0.10.0'}
peerDependencies:
@@ -1869,6 +1884,7 @@ packages:
optional: true
dependencies:
bluebird: 3.7.2
lodash: 4.17.21
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
@@ -1911,7 +1927,7 @@ packages:
- '@parcel/core'
dev: false
/@plasmohq/parcel-config@0.40.0(postcss@8.4.32)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2):
/@plasmohq/parcel-config@0.40.0(lodash@4.17.21)(postcss@8.4.32)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2):
resolution: {integrity: sha512-aDyZIL3ScTmA1CsB/Sym7SxOMVSFUgNQrFovD+sl1M2nrYytqkLDFs7mspCpBlsxCGt97s8rD/kufke21UUHRA==}
dependencies:
'@parcel/compressor-raw': 2.9.3(@parcel/core@2.9.3)
@@ -1948,7 +1964,7 @@ packages:
'@plasmohq/parcel-transformer-inline-css': 0.3.9
'@plasmohq/parcel-transformer-manifest': 0.17.8
'@plasmohq/parcel-transformer-svelte': 0.5.2
'@plasmohq/parcel-transformer-vue': 0.5.0(react-dom@18.2.0)(react@18.2.0)
'@plasmohq/parcel-transformer-vue': 0.5.0(lodash@4.17.21)(react-dom@18.2.0)(react@18.2.0)
transitivePeerDependencies:
- '@swc/core'
- '@swc/helpers'
@@ -2166,7 +2182,7 @@ packages:
svelte: 4.0.1
dev: false
/@plasmohq/parcel-transformer-vue@0.5.0(react-dom@18.2.0)(react@18.2.0):
/@plasmohq/parcel-transformer-vue@0.5.0(lodash@4.17.21)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-/3oVbajt+DRqtbM0RkKFtfyZR8DVjcsYpj1jHqPParGVBiXwgP0D/8Bj5p5/5Iqihs08gzasTcjKcwQKKdj0og==}
engines: {parcel: '>= 2.7.0'}
dependencies:
@@ -2176,7 +2192,7 @@ packages:
'@parcel/source-map': 2.1.1
'@parcel/types': 2.9.3(@parcel/core@2.9.3)
'@parcel/utils': 2.9.3
'@plasmohq/consolidate': 0.17.0(react-dom@18.2.0)(react@18.2.0)
'@plasmohq/consolidate': 0.17.0(lodash@4.17.21)(react-dom@18.2.0)(react@18.2.0)
'@vue/compiler-sfc': 3.3.4
nullthrows: 1.1.1
semver: 7.5.4
@@ -2230,6 +2246,18 @@ packages:
- whiskers
dev: false
/@plasmohq/storage@1.9.0(react@18.2.0):
resolution: {integrity: sha512-mntoJ0EVh7JfYyMKWKnt6yqVlJnwavEkwdXssSOxS1CEeyNX2GPkXQfChvlGhuEJplqcRhLaym6rEc690Ao0fg==}
peerDependencies:
react: ^16.8.6 || ^17 || ^18
peerDependenciesMeta:
react:
optional: true
dependencies:
pify: 6.1.0
react: 18.2.0
dev: false
/@pnpm/config.env-replace@1.1.0:
resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==}
engines: {node: '>=12.22.0'}
@@ -4749,11 +4777,16 @@ packages:
dev: false
optional: true
/pify@6.1.0:
resolution: {integrity: sha512-KocF8ve28eFjjuBKKGvzOBGzG8ew2OqOOSxTTZhirkzH7h3BI1vyzqlR0qbfcDBve1Yzo3FVlWUAtCRrbVN8Fw==}
engines: {node: '>=14.16'}
dev: false
/pirates@4.0.6:
resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
engines: {node: '>= 6'}
/plasmo@0.84.0(postcss@8.4.32)(react-dom@18.2.0)(react@18.2.0):
/plasmo@0.84.0(lodash@4.17.21)(postcss@8.4.32)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-SK6A/uX5mo4EsYiF7JySTOCp2aMo3ejfpbNey89M0zat9p0yDTKEClvMs9MWKXld5Ae0CXPaMYcZPAMvCMngPQ==}
hasBin: true
dependencies:
@@ -4763,7 +4796,7 @@ packages:
'@parcel/package-manager': 2.9.3(@parcel/core@2.9.3)
'@parcel/watcher': 2.2.0
'@plasmohq/init': 0.7.0
'@plasmohq/parcel-config': 0.40.0(postcss@8.4.32)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)
'@plasmohq/parcel-config': 0.40.0(lodash@4.17.21)(postcss@8.4.32)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)
'@plasmohq/parcel-core': 0.1.8
buffer: 6.0.3
chalk: 5.3.0
@@ -4983,6 +5016,10 @@ packages:
dev: false
optional: true
/psl@1.9.0:
resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}
dev: false
/pump@3.0.0:
resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
dependencies:
@@ -5141,6 +5178,17 @@ packages:
fsevents: 2.3.3
dev: false
/route-recognizer@0.3.4:
resolution: {integrity: sha512-2+MhsfPhvauN1O8KaXpXAOfR/fwe8dnUXVM+xw7yt40lJRfPVQxV6yryZm0cgRvAj5fMF/mdRZbL2ptwbs5i2g==}
dev: false
/rss-parser@3.13.0:
resolution: {integrity: sha512-7jWUBV5yGN3rqMMj7CZufl/291QAhvrrGpDNE4k/02ZchL0npisiYYqULF71jCEKoIiHvK/Q2e6IkDwPziT7+w==}
dependencies:
entities: 2.2.0
xml2js: 0.5.0
dev: false
/run-async@3.0.0:
resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==}
engines: {node: '>=0.12.0'}
@@ -5179,7 +5227,6 @@ packages:
resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==}
requiresBuild: true
dev: false
optional: true
/scheduler@0.23.0:
resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==}
@@ -5701,6 +5748,19 @@ packages:
/wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
/xml2js@0.5.0:
resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==}
engines: {node: '>=4.0.0'}
dependencies:
sax: 1.3.0
xmlbuilder: 11.0.1
dev: false
/xmlbuilder@11.0.1:
resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==}
engines: {node: '>=4.0'}
dev: false
/xxhash-wasm@0.4.2:
resolution: {integrity: sha512-/eyHVRJQCirEkSZ1agRSCwriMhwlyUcFkXD5TPVSLP+IPzjsqMVzZwdoczLp1SoQU0R3dxz1RpIK+4YNQbCVOA==}
dev: false
+4 -2
View File
@@ -19,8 +19,10 @@ const getRSS = async (tabId, url) => {
target: "offscreen",
data: {
name: "requestRSS",
html,
url,
body: {
html,
url,
}
}
})
}
+59
View File
@@ -0,0 +1,59 @@
import { Storage } from "@plasmohq/storage"
const storage = new Storage()
export const defaultConfig = {
rsshubDomain: 'https://rsshub.app',
rsshubAccessControl: {
enabled: false,
accessKey: '',
useCode: false,
},
notice: {
badge: true,
},
submitto: {
ttrss: false,
ttrssDomain: '',
checkchan: false,
checkchanBase: '',
miniflux: false,
minifluxDomain: '',
freshrss: false,
freshrssDomain: '',
nextcloudnews: false,
nextcloudnewsDomain: '',
feedly: true,
inoreader: false,
inoreaderDomain: 'https://www.inoreader.com',
feedbin: false,
feedbinDomain: 'https://feedbin.com',
theoldreader: false,
feedspub: false,
bazqux: false,
local: false,
},
refreshTimeout: 5 * 60 * 60,
// typical UA:
// Firefox: Mozilla/5.0 (X11; Linux x86_64; rv:99.0) Gecko/20100101 Firefox/99.0
// Chromium/Chrome: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36
// Some Chromium: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/80.0.3987.87 Chrome/80.0.3987.87 Safari/537.36
// Safari: Mozilla/5.0 (Macintosh; Intel Mac OS X 12_3_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.3 Safari/605.1.15
// Edge: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36 Edg/99.0.1150.36
enableFullRemoteRules: !(navigator.userAgent.match(/firefox/i) || (navigator.userAgent.match(/safari/i) && !navigator.userAgent.match(/chrome/i))),
};
export async function getConfig() {
return Object.assign({}, defaultConfig, await storage.get("config"));
}
export function saveConfig(config: typeof defaultConfig) {
if (!config.rsshubDomain) {
config.rsshubDomain = defaultConfig.rsshubDomain;
}
config.rsshubDomain = config.rsshubDomain.replace(/\/$/, '');
if (config.rsshubDomain === defaultConfig.rsshubDomain) {
config.rsshubAccessControl.enabled = defaultConfig.rsshubAccessControl.enabled;
}
return storage.set("config", config)
}
+26
View File
@@ -0,0 +1,26 @@
import { defaultConfig } from './config';
import { Storage } from "@plasmohq/storage"
const storage = new Storage({
area: "local"
})
export function refreshRules() {
return new Promise<void>((resolve) => {
const done = async (response) => {
const text = await response.text()
await storage.set("rules", text)
await storage.set("rulesDate", +new Date())
resolve();
};
fetch(`https://rsshub.js.org/build/radar-rules.${defaultConfig.enableFullRemoteRules ? 'js' : 'json'}`)
.then((response) => {
done(response);
})
.catch(() => {
fetch(`https://fastly.jsdelivr.net/gh/DIYgod/RSSHub@gh-pages/build/radar-rules.${defaultConfig.enableFullRemoteRules ? 'js' : 'json'}`).then((response) => {
done(response);
});
});
});
}
+35 -4
View File
@@ -1,13 +1,44 @@
export {}
import type { Rules } from "~types"
import { getPageRSS } from "./rss"
import { getPageRSSHub, getWebsiteRSSHub } from "./rsshub"
export { }
console.log("HELLO WORLD FROM SANDBOXES")
window.addEventListener("message", (event) => {
window.addEventListener("message", async (event: MessageEvent<{
name: string
body: {
html: string
url: string
rules: Rules
}
}>) => {
if (event.data?.name === "requestRSS") {
const pageRSS = await getPageRSS({
html: event.data.body.html,
url: event.data.body.url,
})
const pageRSSHub = getPageRSSHub({
html: event.data.body.html,
url: event.data.body.url,
rules: event.data.body.rules,
})
const websiteRSSHub = getWebsiteRSSHub({
url: event.data.body.url,
rules: event.data.body.rules,
})
console.log("RSS", pageRSS, pageRSSHub, websiteRSSHub)
event.source.postMessage({
name: "responseRSS",
url: event.data.url,
result: ["1"],
body: {
url: event.data.body.url,
rss: {
pageRSS,
pageRSSHub,
websiteRSSHub,
}
},
}, event.origin as any)
}
})
File diff suppressed because it is too large Load Diff
+159
View File
@@ -0,0 +1,159 @@
import RSSParser from "rss-parser"
const rssParser = new RSSParser()
const parser = new DOMParser()
export async function getPageRSS(data: {
html: string
url: string
}) {
const document = parser.parseFromString(data.html, "text/html")
const location = new URL(data.url)
const defaultTitle =
document.querySelector("title") &&
document.querySelector("title").innerHTML &&
document
.querySelector("title")
.innerHTML.replace(/<!\[CDATA\[(.*)]]>/, (match, p1) => p1)
.trim()
const image =
(document.querySelector('link[rel~="icon"]') &&
handleUrl(
document.querySelector('link[rel~="icon"]').getAttribute("href")
)) ||
location.origin + "/favicon.ico"
function handleUrl(url) {
return new URL(url, location.href).toString()
}
let pageRSS = []
const unique = {
data: {},
save: function (url) {
this.data[url.replace(/^(https?:)?\/\//, "")] = 1
},
check: function (url) {
return this.data[url.replace(/^(https?:)?\/\//, "")]
}
}
// links
const types = [
"application/rss+xml",
"application/atom+xml",
"application/rdf+xml",
"application/rss",
"application/atom",
"application/rdf",
"text/rss+xml",
"text/atom+xml",
"text/rdf+xml",
"text/rss",
"text/atom",
"text/rdf",
"application/feed+json"
]
const links = document.querySelectorAll("link[type]")
for (let i = 0; i < links.length; i++) {
if (
links[i].hasAttribute("type") &&
types.indexOf(links[i].getAttribute("type")) !== -1
) {
const feed_url = links[i].getAttribute("href")
if (feed_url) {
const feed = {
url: handleUrl(feed_url),
title: links[i].getAttribute("title") || defaultTitle,
image
}
if (!unique.check(feed.url)) {
pageRSS.push(feed)
unique.save(feed.url)
}
}
}
}
// a
const aEles = document.querySelectorAll("a")
const check = /([^a-zA-Z]|^)rss([^a-zA-Z]|$)/i
const uncertain = [];
for (let i = 0; i < aEles.length; i++) {
if (aEles[i].hasAttribute("href")) {
const href = aEles[i].getAttribute("href")
if (
href.match(/\/(feed|rss|atom)(\.(xml|rss|atom))?\/?$/) ||
(aEles[i].hasAttribute("title") &&
aEles[i].getAttribute("title").match(check)) ||
(aEles[i].hasAttribute("class") &&
aEles[i].getAttribute("class").match(check)) ||
(aEles[i].innerText && aEles[i].innerText.match(check))
) {
const feed = {
url: handleUrl(href),
title:
aEles[i].innerText ||
aEles[i].getAttribute("title") ||
defaultTitle,
image,
}
if (!unique.check(feed.url)) {
uncertain.push(feed)
}
}
}
}
await Promise.all(uncertain.map((feed) => {
return new Promise<void>(async (resolve) => {
try {
const result = await rssParser.parseURL(feed.url)
feed.title = result.title;
pageRSS.push(feed);
unique.save(feed.url)
resolve()
} catch (error) {
resolve()
}
})
}))
// whole page
if (!unique.check(location.href)) {
let html
if (
document.body &&
document.body.childNodes &&
document.body.childNodes.length === 1 &&
// @ts-ignore
document.body.childNodes[0].tagName &&
// @ts-ignore
document.body.childNodes[0].tagName.toLowerCase()
) {
// @ts-ignore
html = document.body.childNodes[0].innerText
} else if (document.querySelector("#webkit-xml-viewer-source-xml")) {
html = document.querySelector("#webkit-xml-viewer-source-xml").innerHTML
}
if (html) {
rssParser.parseString(html, (err, result) => {
if (!err) {
chrome.runtime.sendMessage(null, {
text: "addPageRSS",
feed: {
url: location.href,
title: result.title,
image
}
})
}
})
}
}
unique.save(location.href)
return pageRSS
}
+215
View File
@@ -0,0 +1,215 @@
import psl from 'psl';
import RouteRecognizer from 'route-recognizer';
import _ from 'lodash';
import { parseRules } from './rules';
import { type Rule, type Rules } from '../types';
function ruleHandler(rule: Rule, params, url, html, success, fail) {
const run = () => {
let resultWithParams;
if (typeof rule.target === 'function') {
const parser = new DOMParser();
const document = parser.parseFromString(html, 'text/html');
try {
resultWithParams = rule.target(params, url, document);
} catch (error) {
console.warn(error);
resultWithParams = '';
}
} else if (typeof rule.target === 'string') {
resultWithParams = rule.target;
}
if (resultWithParams) {
// if no :param in resultWithParams, requiredParams will be null
// in that case, just skip the following steps and return resultWithParams
const requiredParams = resultWithParams.match(/\/:\w+\??(?=\/|$)/g)?.map((param) => ({
name: param.slice(2).replace(/\?$/, ''),
optional: param.endsWith('?'),
}));
if (!requiredParams) {
return resultWithParams;
}
for (const param of requiredParams) {
if (params[param.name]) {
// successfully matched
const regex = new RegExp(`/:${param.name}\\??(?=/|$)`);
resultWithParams = resultWithParams.replace(regex, `/${params[param.name]}`);
} else if (param.optional) {
// missing optional parameter, drop all following parameters, otherwise the route will be invalid
const regex = new RegExp(`/:${param.name}\\?(/.*)?$`);
resultWithParams = resultWithParams.replace(regex, '');
break;
} else {
// missing necessary parameter, fail
resultWithParams = '';
break;
}
}
// bypassing double-check since `:` maybe a part of parameter value
// if (resultWithParams && resultWithParams.includes(':')) {
// // double-check
// resultWithParams = '';
// }
}
return resultWithParams;
};
const resultWithParams = run();
if (resultWithParams) {
success(resultWithParams);
} else {
fail();
}
}
function formatBlank(str1, str2) {
if (str1 && str2) {
return str1 + (str1[str1.length - 1].match(/[a-zA-Z0-9]/) || str2[0].match(/[a-zA-Z0-9]/) ? ' ' : '') + str2;
} else {
return (str1 || '') + (str2 || '');
}
}
export function getPageRSSHub(data: {
url: string;
html: string;
rules: Rules;
}) {
const { url, html } = data;
const rules = parseRules(data.rules);
let parsedDomain;
try {
parsedDomain = psl.parse(new URL(url).hostname);
} catch (error) {
return [];
}
if (parsedDomain && parsedDomain.domain) {
const subdomain = parsedDomain.subdomain;
const domain = parsedDomain.domain;
if (rules[domain]) {
let rule = rules[domain][subdomain || '.'];
if (!rule) {
if (subdomain === 'www') {
rule = rules[domain]['.'];
} else if (!subdomain) {
rule = rules[domain].www;
}
}
if (rule) {
const recognized = [];
rule.forEach((ru, index) => {
const oriSources = Object.prototype.toString.call(ru.source) === '[object Array]' ? ru.source : typeof ru.source === 'string' ? [ru.source] : [];
let sources = [];
// route-recognizer do not support optional segments or partial matching
// thus, we need to manually handle it
// allowing partial matching is necessary, since many rule authors did not mark optional segments
oriSources.forEach((source) => {
// trimming `?` is necessary, since route-recognizer considers it as a part of segment
source = source.replace(/(\/:\w+)\?(?=\/|$)/g, '$1');
sources.push(source);
let tailMatch;
do {
tailMatch = source.match(/\/:\w+$/);
if (tailMatch) {
const tail = tailMatch[0];
source = source.slice(0, source.length - tail.length);
sources.push(source);
}
} while (tailMatch);
});
// deduplicate (some rule authors may already have done similar job)
sources = sources.filter((item, index) => sources.indexOf(item) === index);
// match!
sources.forEach((source) => {
const router = new RouteRecognizer();
router.add([
{
path: source,
handler: index,
},
]);
const result = router.recognize(new URL(url).pathname.replace(/\/$/, ''));
if (result && result[0]) {
recognized.push(result[0]);
}
});
});
const result = [];
Promise.all(
recognized.map(
(recog) =>
new Promise<void>((resolve) => {
ruleHandler(
rule[recog.handler],
recog.params,
url,
html,
(parsed) => {
if (parsed) {
result.push({
title: formatBlank(rules[domain]._name ? 'Current' : '', rule[recog.handler].title),
url: '{rsshubDomain}' + parsed,
path: parsed,
});
} else {
result.push({
title: formatBlank(rules[domain]._name ? 'Current' : '', rule[recog.handler].title),
url: rule[recog.handler].docs,
isDocs: true,
});
}
resolve();
},
() => {
resolve();
}
);
})
)
);
return result;
} else {
return [];
}
} else {
return [];
}
} else {
return [];
}
}
export function getWebsiteRSSHub(data: {
url: string;
rules: Rules;
}) {
const { url } = data;
const rules = parseRules(data.rules);
let parsedDomain;
try {
parsedDomain = psl.parse(new URL(url).hostname);
} catch (error) {
return [];
}
if (parsedDomain && parsedDomain.domain) {
const domain = parsedDomain.domain;
if (rules[domain]) {
const domainRules = [];
for (const subdomainRules in rules[domain]) {
if (subdomainRules[0] !== '_') {
domainRules.push(...rules[domain][subdomainRules]);
}
}
return domainRules.map((rule) => ({
title: formatBlank(rules[domain]._name, rule.title),
url: rule.docs,
isDocs: true,
}));
} else {
return [];
}
} else {
return [];
}
}
+39
View File
@@ -0,0 +1,39 @@
import _ from 'lodash';
import { defaultRules } from './radar-rules';
import { defaultConfig } from '../common/config';
export function parseRules(rules) {
let incomeRules = rules;
if (typeof rules === 'string') {
if (defaultConfig.enableFullRemoteRules) {
incomeRules = window['lave'.split('').reverse().join('')](rules);
} else {
incomeRules = JSON.parse(rules);
}
}
return _.mergeWith(defaultRules, incomeRules, (objValue, srcValue) => {
if (_.isFunction(srcValue)) {
return srcValue;
} else if (_.isFunction(objValue)) {
return objValue;
}
});
}
export function getList(data) {
const rules = parseRules(data.rules);
for (const rule in rules) {
for (const subrule in rules[rule]) {
if (subrule[0] !== '_') {
rules[rule][subrule].forEach((item) => {
delete item.source;
delete item.target;
delete item.script;
delete item.verification;
delete item.test;
});
}
}
}
return rules;
}
+16 -8
View File
@@ -1,25 +1,33 @@
import { useRef } from "react";
import { sendToBackground } from "@plasmohq/messaging"
import { Storage } from "@plasmohq/storage"
console.log("HELLO WORLD FROM OFFSCREEN")
const storage = new Storage({
area: "local"
})
window.addEventListener('message', (event) => {
if (event.data?.name === "responseRSS") {
chrome.runtime.sendMessage(event.data)
sendToBackground({
name: "responseRSS",
body: {
data: 1
}
})
sendToBackground(event.data)
}
});
function OffscreenPage() {
const iframeRef = useRef<HTMLIFrameElement>(null);
chrome.runtime.onMessage.addListener((msg) => {
iframeRef.current?.contentWindow?.postMessage(msg.data, "*");
chrome.runtime.onMessage.addListener(async (msg) => {
console.debug("Received message from background", msg);
const rules = await storage.get("rules");
iframeRef.current?.contentWindow?.postMessage({
name: msg.data.name,
body: {
...msg.data.body,
rules,
},
}, "*");
})
return (
+13
View File
@@ -0,0 +1,13 @@
export type Rule = {
title: string;
docs: string;
source: string[];
target: string | ((params: any, url: string, document: Document) => string);
};
export type Rules = {
[domain: string]: {
_name: string;
[subdomain: string]: Rule[] | string;
};
};