mirror of
https://github.com/DIYgod/RSSHub-Radar.git
synced 2026-09-19 02:21:46 +08:00
feat: parse rss
This commit is contained in:
+6
-1
@@ -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",
|
||||
|
||||
Generated
+69
-9
@@ -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
|
||||
|
||||
@@ -19,8 +19,10 @@ const getRSS = async (tabId, url) => {
|
||||
target: "offscreen",
|
||||
data: {
|
||||
name: "requestRSS",
|
||||
html,
|
||||
url,
|
||||
body: {
|
||||
html,
|
||||
url,
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
@@ -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 [];
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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 (
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user