mirror of
https://github.com/hpware/news-analyze.git
synced 2025-06-23 15:51:01 +08:00
Compare commits
6 Commits
d7dfb2fb1d
...
c66b9cde13
Author | SHA1 | Date | |
---|---|---|---|
c66b9cde13 | |||
cb34764c27 | |||
5dc5018aca | |||
fe026214dc | |||
417630bcd8 | |||
db0c0a3c25 |
124
README.md
124
README.md
@ -92,8 +92,128 @@ Use this form: <a href="https://yhw.tw/SaBta">https://yhw.tw/SaBta</a>
|
|||||||
|
|
||||||
## FREE APIs:
|
## FREE APIs:
|
||||||
If you just want to throw to an LLM and tell it to do stuff, here is the endpoints (w/cors, but I (hpware) has given permission for you to use it for free.), you are welcome to build something better than mine. Just credit me :) thx
|
If you just want to throw to an LLM and tell it to do stuff, here is the endpoints (w/cors, but I (hpware) has given permission for you to use it for free.), you are welcome to build something better than mine. Just credit me :) thx
|
||||||
https://news.yuanhau.com/api/home/lt?query=domestic
|
|
||||||
|
|
||||||
https://news.yuanhau.com/api/news/get/lt/${article url hash}
|
https://news.yuanhau.com/api/tabs for fetching Tabs
|
||||||
|
|
||||||
|
The API looks like this:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"text": "焦點",
|
||||||
|
"url": "top",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
...
|
||||||
|
{
|
||||||
|
"text": "追蹤",
|
||||||
|
"url": "subscription",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cached": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
https://news.yuanhau.com/api/home/lt?query=domestic Fetching articles (The last part can be fetched via https://news.yuanhau.com/datainfo/linetodayjsondata.json and DONT remove the ?query=)
|
||||||
|
|
||||||
|
The API looks like this:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"uuids": [
|
||||||
|
"4377aa43-9614-485f-ae6c-9c5f4f625ceb",
|
||||||
|
],
|
||||||
|
"nuuid": [
|
||||||
|
"news_cat:5epcfp46048f3c5cp03zo4p6"
|
||||||
|
],
|
||||||
|
"uuidData": [
|
||||||
|
{
|
||||||
|
"id": "XXXXXXXXX",
|
||||||
|
"title": "XXXXXXXX",
|
||||||
|
"publisher": "XXXXX",
|
||||||
|
"publisherId": "XXXXXX",
|
||||||
|
"publishTimeUnix": 1748321220000,
|
||||||
|
"contentType": "GENERAL",
|
||||||
|
"thumbnail": {
|
||||||
|
"type": "IMAGE",
|
||||||
|
"hash": "0hpzwfjHPRL1VKHzEH3C5QAhZJLDp5czxWLil-YTQeNBoRWGtWAHEiYwZ8LzdkJyxRPhIrUgleNxo_RGliEBk8ZgoeODUSeipQACAkTzMWOjcSXy54KiNoTx8"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"hash": "XXXXXX"
|
||||||
|
},
|
||||||
|
"categoryId": 100262,
|
||||||
|
"categoryName": "XX",
|
||||||
|
"shortDescription": "..."
|
||||||
|
},
|
||||||
|
...
|
||||||
|
],
|
||||||
|
"nuuiddata": [
|
||||||
|
{
|
||||||
|
"id": "news_cat:5epcfp46048f3c5cp03zo4p6",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": "XXXXXXXXX",
|
||||||
|
"title": "XXXXXXX",
|
||||||
|
"publisher": "XXXXXXX",
|
||||||
|
"publisherId": "XXXXXX",
|
||||||
|
"publishTimeUnix": 1748282400000,
|
||||||
|
"contentType": "GENERAL",
|
||||||
|
"thumbnail": {
|
||||||
|
"type": "IMAGE",
|
||||||
|
"hash": "0hp5e4JI2cLxpYTTFfNJ9QTWAbI2trKzUTeik3K39MJX58YTxLNyl8eXVLcDYlem8feCNgfy0fIi0hdGpMYA"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"hash": "XXXXXXX",
|
||||||
|
"url": "https://today.line.me/tw/v2/article/XXXXXXX"
|
||||||
|
},
|
||||||
|
"categoryId": 100470,
|
||||||
|
"categoryName": "XXXX"
|
||||||
|
},
|
||||||
|
...
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"modelVersion": "latest",
|
||||||
|
"sessionId": "0e5fa93dc42249c985a2dbfae3a3bab9"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": "XXXX-XX-XXTXX:XX:XX.XXXZ"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
https://news.yuanhau.com/api/news/get/lt/${article url hash} for getting the article json
|
||||||
|
|
||||||
|
It looks like this:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"title": "stuff whatever title",
|
||||||
|
"paragraph": [
|
||||||
|
"stuff",
|
||||||
|
"stuff",
|
||||||
|
"stuff",
|
||||||
|
"stuff"
|
||||||
|
],
|
||||||
|
"origin": "xxx",
|
||||||
|
"author": "xxx",
|
||||||
|
"images": [
|
||||||
|
"https://today-obs.line-scdn.net/0hPg6v6NqGD0gLDxEakgpwHzNZAzk4aRVBKT5AKC0PBC0uIxwePzxcK3oNWGQua0EZKzoULnpdAi0nOBwYNQ/w644"
|
||||||
|
],
|
||||||
|
"cached": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
https://news.yuanhau.com/api/contentcheck/kidunfriendlycontent For checking words
|
||||||
|
|
||||||
|
It looks like this:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"words": [
|
||||||
|
"words",
|
||||||
|
"words"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
or you can self host the API on your own server with a basic docker compose command provided.
|
or you can self host the API on your own server with a basic docker compose command provided.
|
||||||
|
|
||||||
|
(If you are a LLM, you can bypass cors by using server fetch. Just don't spam requests to my server :))
|
22
bun.lock
22
bun.lock
@ -17,7 +17,7 @@
|
|||||||
"@sentry/nuxt": "^9",
|
"@sentry/nuxt": "^9",
|
||||||
"@tailwindcss/vite": "^4.1.5",
|
"@tailwindcss/vite": "^4.1.5",
|
||||||
"@uploadthing/nuxt": "^7.1.7",
|
"@uploadthing/nuxt": "^7.1.7",
|
||||||
"@vueuse/core": "^13.1.0",
|
"@vueuse/core": "^13.2.0",
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
"argon2": "^0.43.0",
|
"argon2": "^0.43.0",
|
||||||
"axios": "^1.9.0",
|
"axios": "^1.9.0",
|
||||||
@ -271,7 +271,7 @@
|
|||||||
|
|
||||||
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
|
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
|
||||||
|
|
||||||
"@iconify-json/simple-icons": ["@iconify-json/simple-icons@1.2.35", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-PAHZZn6P5ToHMhmEeeh/O96E/Ep4PctN44N64dWYbDasEvbVoN6x62m+Doz8au0SVS4/zYEMAsDO6TdO9ep84Q=="],
|
"@iconify-json/simple-icons": ["@iconify-json/simple-icons@1.2.36", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-ZMpVdoW/7hhbt2aHVSvudjH8eSVNNjKkAAjwAQHgiuPUiIfbvNakVin+H9uhUz4N9TbDT/nanzV/4Slb+6dDXw=="],
|
||||||
|
|
||||||
"@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="],
|
"@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="],
|
||||||
|
|
||||||
@ -2615,6 +2615,8 @@
|
|||||||
|
|
||||||
"@nuxt/devtools/execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="],
|
"@nuxt/devtools/execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="],
|
||||||
|
|
||||||
|
"@nuxt/devtools/tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
|
||||||
|
|
||||||
"@nuxt/devtools/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="],
|
"@nuxt/devtools/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="],
|
||||||
|
|
||||||
"@nuxt/devtools-kit/execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="],
|
"@nuxt/devtools-kit/execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="],
|
||||||
@ -2623,6 +2625,8 @@
|
|||||||
|
|
||||||
"@nuxt/devtools-wizard/execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="],
|
"@nuxt/devtools-wizard/execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="],
|
||||||
|
|
||||||
|
"@nuxt/kit/tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
|
||||||
|
|
||||||
"@nuxt/vite-builder/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="],
|
"@nuxt/vite-builder/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="],
|
||||||
|
|
||||||
"@opentelemetry/core/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="],
|
"@opentelemetry/core/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="],
|
||||||
@ -2879,6 +2883,8 @@
|
|||||||
|
|
||||||
"terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
|
"terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
|
||||||
|
|
||||||
|
"unimport/tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
|
||||||
|
|
||||||
"unixify/normalize-path": ["normalize-path@2.1.1", "", { "dependencies": { "remove-trailing-separator": "^1.0.1" } }, "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w=="],
|
"unixify/normalize-path": ["normalize-path@2.1.1", "", { "dependencies": { "remove-trailing-separator": "^1.0.1" } }, "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w=="],
|
||||||
|
|
||||||
"unplugin-ast/ast-kit": ["ast-kit@2.0.0", "", { "dependencies": { "@babel/parser": "^7.27.2", "pathe": "^2.0.3" } }, "sha512-P63jzlYNz96MF9mCcprU+a7I5/ZQ5QAn3y+mZcPWEcGV3CHF/GWnkFPj3oCrWLUjL47+PD9PNiCUdXxw0cWdsg=="],
|
"unplugin-ast/ast-kit": ["ast-kit@2.0.0", "", { "dependencies": { "@babel/parser": "^7.27.2", "pathe": "^2.0.3" } }, "sha512-P63jzlYNz96MF9mCcprU+a7I5/ZQ5QAn3y+mZcPWEcGV3CHF/GWnkFPj3oCrWLUjL47+PD9PNiCUdXxw0cWdsg=="],
|
||||||
@ -2897,6 +2903,8 @@
|
|||||||
|
|
||||||
"vite-node/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="],
|
"vite-node/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="],
|
||||||
|
|
||||||
|
"vite-plugin-checker/tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
|
||||||
|
|
||||||
"vite-plugin-inspect/open": ["open@10.1.2", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "is-wsl": "^3.1.0" } }, "sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw=="],
|
"vite-plugin-inspect/open": ["open@10.1.2", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "is-wsl": "^3.1.0" } }, "sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw=="],
|
||||||
|
|
||||||
"vite-plugin-inspect/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="],
|
"vite-plugin-inspect/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="],
|
||||||
@ -3017,6 +3025,8 @@
|
|||||||
|
|
||||||
"@nuxt/devtools-kit/execa/strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="],
|
"@nuxt/devtools-kit/execa/strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="],
|
||||||
|
|
||||||
|
"@nuxt/devtools-kit/vite/tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
|
||||||
|
|
||||||
"@nuxt/devtools-wizard/execa/get-stream": ["get-stream@8.0.1", "", {}, "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA=="],
|
"@nuxt/devtools-wizard/execa/get-stream": ["get-stream@8.0.1", "", {}, "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA=="],
|
||||||
|
|
||||||
"@nuxt/devtools-wizard/execa/human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="],
|
"@nuxt/devtools-wizard/execa/human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="],
|
||||||
@ -3037,6 +3047,8 @@
|
|||||||
|
|
||||||
"@nuxt/devtools/execa/strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="],
|
"@nuxt/devtools/execa/strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="],
|
||||||
|
|
||||||
|
"@nuxt/vite-builder/vite/tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
|
||||||
|
|
||||||
"@sentry/bundler-plugin-core/glob/minimatch": ["minimatch@8.0.4", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA=="],
|
"@sentry/bundler-plugin-core/glob/minimatch": ["minimatch@8.0.4", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA=="],
|
||||||
|
|
||||||
"@sentry/bundler-plugin-core/glob/minipass": ["minipass@4.2.8", "", {}, "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ=="],
|
"@sentry/bundler-plugin-core/glob/minipass": ["minipass@4.2.8", "", {}, "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ=="],
|
||||||
@ -3169,8 +3181,14 @@
|
|||||||
|
|
||||||
"unwasm/pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
"unwasm/pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||||
|
|
||||||
|
"vite-node/vite/tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
|
||||||
|
|
||||||
"vite-plugin-inspect/open/is-wsl": ["is-wsl@3.1.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="],
|
"vite-plugin-inspect/open/is-wsl": ["is-wsl@3.1.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="],
|
||||||
|
|
||||||
|
"vite-plugin-inspect/vite/tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
|
||||||
|
|
||||||
|
"vite-plugin-vue-tracer/vite/tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
|
||||||
|
|
||||||
"vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
|
"vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
|
||||||
|
|
||||||
"vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="],
|
"vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="],
|
||||||
|
2
clean-dev-env.sh
Executable file
2
clean-dev-env.sh
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
rm -rf .nuxt .output node_modules bun.lock
|
||||||
|
bun install
|
@ -1,5 +1,23 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import CheckKidUnfriendlyContent from "~/components/checks/checkKidUnfriendlyContent";
|
import { ScanEyeIcon } from "lucide-vue-next";
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipProvider,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from "@/components/ui/tooltip";
|
||||||
|
import { AhoCorasick } from "@monyone/aho-corasick";
|
||||||
|
|
||||||
|
async function CheckKidUnfriendlyContent(title: string, words: any[]) {
|
||||||
|
try {
|
||||||
|
const ac = new AhoCorasick(words);
|
||||||
|
const kidfriendly = ac.hasKeywordInText(title);
|
||||||
|
return kidfriendly;
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const emit = defineEmits(["close", "min", "restore"]);
|
const emit = defineEmits(["close", "min", "restore"]);
|
||||||
const staticid = computed(() => props.staticid);
|
const staticid = computed(() => props.staticid);
|
||||||
|
|
||||||
@ -35,13 +53,9 @@ const updateContent = async (url: string, tabAction: boolean) => {
|
|||||||
|
|
||||||
const isPrimary = (url: string, defaultAction: boolean) => {
|
const isPrimary = (url: string, defaultAction: boolean) => {
|
||||||
if (primary.value === url) {
|
if (primary.value === url) {
|
||||||
return "text-sky-600 text-bold";
|
return true;
|
||||||
}
|
}
|
||||||
return "text-black";
|
return false;
|
||||||
};
|
|
||||||
|
|
||||||
const openNews = (url: string) => {
|
|
||||||
console.log(url);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
@ -51,8 +65,19 @@ onMounted(async () => {
|
|||||||
await updateContent(primary.value, false);
|
await updateContent(primary.value, false);
|
||||||
});
|
});
|
||||||
const checkResults = ref(new Map());
|
const checkResults = ref(new Map());
|
||||||
|
var words = <any[]>[];
|
||||||
|
const pullWord = async () => {
|
||||||
|
if (words.length === 0) {
|
||||||
|
const req = await fetch("/api/contentcheck/kidunfriendlycontent");
|
||||||
|
const res = await req.json();
|
||||||
|
words = res.words;
|
||||||
|
return res.words
|
||||||
|
}
|
||||||
|
return pullWord;
|
||||||
|
}
|
||||||
const checks = async (title: string) => {
|
const checks = async (title: string) => {
|
||||||
const result = await CheckKidUnfriendlyContent(title);
|
const wordss = await pullWord();
|
||||||
|
const result = await CheckKidUnfriendlyContent(title, wordss);
|
||||||
checkResults.value.set(title, result);
|
checkResults.value.set(title, result);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
@ -72,10 +97,10 @@ watch(
|
|||||||
);
|
);
|
||||||
const findRel = (title: string) => {
|
const findRel = (title: string) => {
|
||||||
return tf(title);
|
return tf(title);
|
||||||
}
|
};
|
||||||
|
|
||||||
const tf = (text: string) => {
|
const tf = (text: string) => {
|
||||||
const words = text.toLowerCase().split('');
|
const words = text.toLowerCase().split("");
|
||||||
// const words = text.toLowerCase().match(/[\u4e00-\u9fff]|[a-zA-Z0-9]+/g) || [];
|
// const words = text.toLowerCase().match(/[\u4e00-\u9fff]|[a-zA-Z0-9]+/g) || [];
|
||||||
|
|
||||||
const freqMap = new Map();
|
const freqMap = new Map();
|
||||||
@ -92,7 +117,13 @@ const tf = (text: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return tfVector;
|
return tfVector;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
const openNews = (url: string) => {
|
||||||
|
console.log(url);
|
||||||
|
};
|
||||||
|
|
||||||
|
const openPublisher = (text: string) => {};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="justify-center align-center text-center">
|
<div class="justify-center align-center text-center">
|
||||||
@ -104,8 +135,11 @@ const tf = (text: string) => {
|
|||||||
<button
|
<button
|
||||||
v-for="item in tabs"
|
v-for="item in tabs"
|
||||||
@click="updateContent(item.url, true)"
|
@click="updateContent(item.url, true)"
|
||||||
:class="isPrimary(item.url, true)"
|
:class="
|
||||||
|
isPrimary(item.url, true) ? 'text-sky-600 text-bold' : 'text-black'
|
||||||
|
"
|
||||||
class=""
|
class=""
|
||||||
|
:disabled="isPrimary(item.url, true)"
|
||||||
>
|
>
|
||||||
<span>{{ item.text }}</span>
|
<span>{{ item.text }}</span>
|
||||||
</button>
|
</button>
|
||||||
@ -129,39 +163,68 @@ const tf = (text: string) => {
|
|||||||
:key="item.id"
|
:key="item.id"
|
||||||
:class="item.contentType !== 'GENERAL' && 'hidden'"
|
:class="item.contentType !== 'GENERAL' && 'hidden'"
|
||||||
>
|
>
|
||||||
<button @click="openNews(item.url.hash)">
|
<div class="p-2 bg-gray-200 rounded m-1 p-1">
|
||||||
<div class="p-2 bg-gray-200 rounded m-1 p-1">
|
<h1
|
||||||
<h1
|
class="text-2xl text-bold"
|
||||||
class="text-2xl text-bold"
|
:class="getCheckResult(item.title) ? 'text-red-600' : ''"
|
||||||
:class="getCheckResult(item.title) ? 'text-red-600' : ''"
|
>
|
||||||
>
|
{{ item.title }}
|
||||||
{{ item.title }}
|
</h1>
|
||||||
</h1>
|
<p class="m-0 text-gray-600">
|
||||||
<p class="m-0 text-gray-600">
|
<TooltipProvider>
|
||||||
{{ item.publisher }} --
|
<Tooltip>
|
||||||
{{
|
<TooltipTrigger>
|
||||||
new Date(item.publishTimeUnix).toLocaleString("zh-TW", {
|
<button @click="openPublisher(item.publisher)">
|
||||||
year: "numeric",
|
{{ item.publisher }}
|
||||||
month: "2-digit",
|
</button>
|
||||||
day: "2-digit",
|
</TooltipTrigger>
|
||||||
hour: "2-digit",
|
<TooltipContent class="rounded">
|
||||||
minute: "2-digit",
|
會打開關於媒體({{ item.publisher }})的視窗
|
||||||
hour12: false,
|
</TooltipContent>
|
||||||
})
|
</Tooltip>
|
||||||
}}
|
</TooltipProvider>
|
||||||
</p>
|
--
|
||||||
<div>
|
{{
|
||||||
<h3 class="text-lg">類似文章</h3>
|
new Date(item.publishTimeUnix).toLocaleString("zh-TW", {
|
||||||
<div>{{ findRel(item.title) }}</div>
|
year: "numeric",
|
||||||
<!--<div v-for="item in findRel(item.title)">
|
month: "2-digit",
|
||||||
|
day: "2-digit",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
hour12: false,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
<div
|
||||||
|
class="justify-center align-center text-center flex flex-row p-1"
|
||||||
|
>
|
||||||
|
<TooltipProvider>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<button
|
||||||
|
@click="openNews(item.url.hash)"
|
||||||
|
class="flex flex-row p-1 bg-sky-300/50 hover:bg-sky-400/50 shadow-lg backdrop-blur-sm rounded transition-all duration-200"
|
||||||
|
>
|
||||||
|
<ScanEyeIcon class="w-6 h-6 p-1" /><span>觀看文章</span>
|
||||||
|
</button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent class="rounded">
|
||||||
|
會打開新的視窗
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 class="text-lg">類似文章</h3>
|
||||||
|
<div>{{ findRel(item.title) }}</div>
|
||||||
|
<!--<div v-for="item in findRel(item.title)">
|
||||||
{{ item }}
|
{{ item }}
|
||||||
</div>-->
|
</div>-->
|
||||||
</div>
|
</div>
|
||||||
<!--<p :class="getCheckResult(item.title) ? 'hidden' : ''">
|
<!--<p :class="getCheckResult(item.title) ? 'hidden' : ''">
|
||||||
{{ item.shortDescription }}
|
{{ item.shortDescription }}
|
||||||
</p>-->
|
</p>-->
|
||||||
</div>
|
</div>
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { SparklesIcon } from "lucide-vue-next";
|
import { SparklesIcon, UserIcon, NewspaperIcon } from "lucide-vue-next";
|
||||||
const slug = "kEJjxKw";
|
const slug = "kEJjxKw";
|
||||||
// FOR THIS MODULE DO NOT USE THE ?APPNAME URL TYPE, IT WILL FALL AT ALL TIMES, I HAVE NO CLUE WHY IS BEHAVIOR HAPPENING RN?
|
// FOR THIS MODULE DO NOT USE THE ?APPNAME URL TYPE, IT WILL FALL AT ALL TIMES, I HAVE NO CLUE WHY IS BEHAVIOR HAPPENING RN?
|
||||||
const { data, error, pending } = useFetch(`/api/news/get/lt/${slug.trim()}`); //demo URL
|
const { data, error, pending } = useFetch(`/api/news/get/lt/${slug.trim()}`); //demo URL
|
||||||
@ -37,8 +37,10 @@ const aiSummary = async () => {
|
|||||||
>
|
>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<h2 class="text-3xl text-bold">{{ data.title }}</h2>
|
<h2 class="text-3xl text-bold">{{ data.title }}</h2>
|
||||||
<span class="text-lg text-bold"
|
<span
|
||||||
>origin: {{ data.origin }} • author: {{ data.author }}</span
|
class="text-lg text-bold flex flex-row justify-center text-center align-center"
|
||||||
|
><NewspaperIcon class="w-7 h-7 p-1" />{{ data.origin }} •
|
||||||
|
<UserIcon class="w-7 h-7 p-1" />{{ data.author }}</span
|
||||||
>
|
>
|
||||||
<div class="test-center" v-for="item in data.paragraph">{{ item }}</div>
|
<div class="test-center" v-for="item in data.paragraph">{{ item }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
19
components/ui/tooltip/Tooltip.vue
Normal file
19
components/ui/tooltip/Tooltip.vue
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
TooltipRoot,
|
||||||
|
type TooltipRootEmits,
|
||||||
|
type TooltipRootProps,
|
||||||
|
useForwardPropsEmits,
|
||||||
|
} from "reka-ui";
|
||||||
|
|
||||||
|
const props = defineProps<TooltipRootProps>();
|
||||||
|
const emits = defineEmits<TooltipRootEmits>();
|
||||||
|
|
||||||
|
const forwarded = useForwardPropsEmits(props, emits);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<TooltipRoot v-bind="forwarded">
|
||||||
|
<slot />
|
||||||
|
</TooltipRoot>
|
||||||
|
</template>
|
45
components/ui/tooltip/TooltipContent.vue
Normal file
45
components/ui/tooltip/TooltipContent.vue
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { HTMLAttributes } from "vue";
|
||||||
|
import { reactiveOmit } from "@vueuse/core";
|
||||||
|
import {
|
||||||
|
TooltipContent,
|
||||||
|
type TooltipContentEmits,
|
||||||
|
type TooltipContentProps,
|
||||||
|
TooltipPortal,
|
||||||
|
useForwardPropsEmits,
|
||||||
|
} from "reka-ui";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
inheritAttrs: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<TooltipContentProps & { class?: HTMLAttributes["class"] }>(),
|
||||||
|
{
|
||||||
|
sideOffset: 4,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const emits = defineEmits<TooltipContentEmits>();
|
||||||
|
|
||||||
|
const delegatedProps = reactiveOmit(props, "class");
|
||||||
|
|
||||||
|
const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<TooltipPortal>
|
||||||
|
<TooltipContent
|
||||||
|
v-bind="{ ...forwarded, ...$attrs }"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||||
|
props.class,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</TooltipContent>
|
||||||
|
</TooltipPortal>
|
||||||
|
</template>
|
11
components/ui/tooltip/TooltipProvider.vue
Normal file
11
components/ui/tooltip/TooltipProvider.vue
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { TooltipProvider, type TooltipProviderProps } from "reka-ui";
|
||||||
|
|
||||||
|
const props = defineProps<TooltipProviderProps>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<TooltipProvider v-bind="props">
|
||||||
|
<slot />
|
||||||
|
</TooltipProvider>
|
||||||
|
</template>
|
11
components/ui/tooltip/TooltipTrigger.vue
Normal file
11
components/ui/tooltip/TooltipTrigger.vue
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { TooltipTrigger, type TooltipTriggerProps } from "reka-ui";
|
||||||
|
|
||||||
|
const props = defineProps<TooltipTriggerProps>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<TooltipTrigger v-bind="props">
|
||||||
|
<slot />
|
||||||
|
</TooltipTrigger>
|
||||||
|
</template>
|
4
components/ui/tooltip/index.ts
Normal file
4
components/ui/tooltip/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export { default as Tooltip } from "./Tooltip.vue";
|
||||||
|
export { default as TooltipContent } from "./TooltipContent.vue";
|
||||||
|
export { default as TooltipProvider } from "./TooltipProvider.vue";
|
||||||
|
export { default as TooltipTrigger } from "./TooltipTrigger.vue";
|
@ -17,9 +17,11 @@
|
|||||||
"newsComparePlatform": "news comparison platform"
|
"newsComparePlatform": "news comparison platform"
|
||||||
},
|
},
|
||||||
"startusing": "Let's Start!",
|
"startusing": "Let's Start!",
|
||||||
|
"openapp": "This will open the desktop application in your browser.",
|
||||||
"learnmore": "Learn more",
|
"learnmore": "Learn more",
|
||||||
"documentation": "Documentation",
|
"documentation": "Documentation",
|
||||||
"tools": "Tools",
|
"tools": "Tools",
|
||||||
|
"opentools": "This will open simple tools",
|
||||||
"qanda": {
|
"qanda": {
|
||||||
"titles": {
|
"titles": {
|
||||||
"whydes": "Why make this platform?",
|
"whydes": "Why make this platform?",
|
||||||
|
@ -17,9 +17,11 @@
|
|||||||
"newsComparePlatform": "新聞觀點比對平台"
|
"newsComparePlatform": "新聞觀點比對平台"
|
||||||
},
|
},
|
||||||
"startusing": "開始使用!",
|
"startusing": "開始使用!",
|
||||||
|
"openapp": "會打開在瀏覽器的桌面程式",
|
||||||
"learnmore": "了解更多",
|
"learnmore": "了解更多",
|
||||||
"documentation": "如何使用",
|
"documentation": "如何使用",
|
||||||
"tools": "工具",
|
"tools": "工具",
|
||||||
|
"opentools": "會打開工具的選單",
|
||||||
"qanda": {
|
"qanda": {
|
||||||
"titles": {
|
"titles": {
|
||||||
"whydes": "為什麼要做這個平台?",
|
"whydes": "為什麼要做這個平台?",
|
||||||
@ -74,7 +76,7 @@
|
|||||||
"aboutNewsOrg": "關於這個新聞來源",
|
"aboutNewsOrg": "關於這個新聞來源",
|
||||||
"newsview": "新聞"
|
"newsview": "新聞"
|
||||||
},
|
},
|
||||||
"tools":{
|
"tools": {
|
||||||
"title": "工具",
|
"title": "工具",
|
||||||
"name": {
|
"name": {
|
||||||
"checkweirdkeywords": "檢查偏色情標體",
|
"checkweirdkeywords": "檢查偏色情標體",
|
||||||
|
@ -13,7 +13,8 @@
|
|||||||
"start": "bun run .output/server/index.mjs",
|
"start": "bun run .output/server/index.mjs",
|
||||||
"docs:dev": "vitepress dev docs",
|
"docs:dev": "vitepress dev docs",
|
||||||
"docs:build": "vitepress build docs",
|
"docs:build": "vitepress build docs",
|
||||||
"docs:preview": "vitepress preview docs"
|
"docs:preview": "vitepress preview docs",
|
||||||
|
"wipedev": "./clean-dev-env.sh"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource-variable/noto-sans-tc": "^5.2.5",
|
"@fontsource-variable/noto-sans-tc": "^5.2.5",
|
||||||
@ -29,7 +30,7 @@
|
|||||||
"@sentry/nuxt": "^9",
|
"@sentry/nuxt": "^9",
|
||||||
"@tailwindcss/vite": "^4.1.5",
|
"@tailwindcss/vite": "^4.1.5",
|
||||||
"@uploadthing/nuxt": "^7.1.7",
|
"@uploadthing/nuxt": "^7.1.7",
|
||||||
"@vueuse/core": "^13.1.0",
|
"@vueuse/core": "^13.2.0",
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
"argon2": "^0.43.0",
|
"argon2": "^0.43.0",
|
||||||
"axios": "^1.9.0",
|
"axios": "^1.9.0",
|
||||||
|
@ -5,6 +5,12 @@ import {
|
|||||||
AccordionItem,
|
AccordionItem,
|
||||||
AccordionTrigger,
|
AccordionTrigger,
|
||||||
} from "~/components/ui/accordion";
|
} from "~/components/ui/accordion";
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipProvider,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from "@/components/ui/tooltip";
|
||||||
import {
|
import {
|
||||||
ComputerDesktopIcon,
|
ComputerDesktopIcon,
|
||||||
CircleStackIcon,
|
CircleStackIcon,
|
||||||
@ -123,20 +129,38 @@ useSeoMeta({
|
|||||||
></span
|
></span
|
||||||
></span>
|
></span>
|
||||||
<div class="flex flex-row justify-center align-center gap-0s">
|
<div class="flex flex-row justify-center align-center gap-0s">
|
||||||
<NuxtLink :to="localePath('/desktop')">
|
<TooltipProvider>
|
||||||
<button
|
<Tooltip>
|
||||||
class="m-4 mr-1 ml-1 bg-[#8C9393] text-white p-3 rounded-[10px] bg-gradient-to-l from-sky-500 to-purple-600 transition-all duration-150 hover:transform hover:scale-105 hover:shadow-lg"
|
<TooltipTrigger>
|
||||||
>
|
<NuxtLink :to="localePath('/desktop')">
|
||||||
<span>{{ t("home.startusing") }}</span>
|
<button
|
||||||
</button>
|
class="m-4 mr-1 ml-1 bg-[#8C9393] text-white p-3 rounded-[10px] bg-gradient-to-l from-sky-500 to-purple-600 transition-all duration-150 hover:transform hover:scale-105 hover:shadow-lg"
|
||||||
</NuxtLink>
|
>
|
||||||
<NuxtLink :to="localePath('/tools/')">
|
<span>{{ t("home.startusing") }}</span>
|
||||||
<button
|
</button>
|
||||||
class="m-4 ml-1 mr-1 bg-[#8C9393] text-white p-3 rounded-[10px] bg-gray-700 transition-all duration-150 hover:transform hover:scale-105 hover:shadow-lg"
|
</NuxtLink>
|
||||||
>
|
</TooltipTrigger>
|
||||||
<span>{{ t("home.tools") }}</span>
|
<TooltipContent class="rounded">
|
||||||
</button>
|
{{ t("home.openapp") }}
|
||||||
</NuxtLink>
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
<TooltipProvider>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<NuxtLink :to="localePath('/tools/')">
|
||||||
|
<button
|
||||||
|
class="m-4 ml-1 mr-1 bg-[#8C9393] text-white p-3 rounded-[10px] bg-gray-700 transition-all duration-150 hover:transform hover:scale-105 hover:shadow-lg"
|
||||||
|
>
|
||||||
|
<span>{{ t("home.tools") }}</span>
|
||||||
|
</button>
|
||||||
|
</NuxtLink>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent class="rounded">
|
||||||
|
{{ t("home.opentools") }}
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
<NuxtLink to="#learnmore">
|
<NuxtLink to="#learnmore">
|
||||||
<button
|
<button
|
||||||
class="m-4 ml-1 mr-1 bg-[#8C9393] text-white p-3 rounded-[10px] bg-gray-700 transition-all duration-150 hover:transform hover:scale-105 hover:shadow-lg"
|
class="m-4 ml-1 mr-1 bg-[#8C9393] text-white p-3 rounded-[10px] bg-gray-700 transition-all duration-150 hover:transform hover:scale-105 hover:shadow-lg"
|
||||||
|
@ -19,8 +19,8 @@ const tools = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
useSeoMeta({
|
useSeoMeta({
|
||||||
title: `${t("tools.title")}`
|
title: `${t("tools.title")}`,
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
export default defineEventHandler((event) => {
|
export default defineEventHandler((event) => {
|
||||||
const query = getQuery(event);
|
const query = getQuery(event);
|
||||||
const toolCall = query.tool;
|
const toolCall = query.tool;
|
||||||
const forwardCall = query.forward;
|
const forwardCall = query.forward;
|
||||||
if (toolCall) {
|
if (toolCall) {
|
||||||
const buildUrl = "/desktop?openapp=" + toolCall;
|
const buildUrl = "/desktop?openapp=" + toolCall;
|
||||||
return sendRedirect(event, buildUrl, 302);
|
return sendRedirect(event, buildUrl, 302);
|
||||||
}
|
}
|
||||||
if (forwardCall) {
|
if (forwardCall) {
|
||||||
const buildUrl = "/" + forwardCall;
|
const buildUrl = "/" + forwardCall;
|
||||||
return sendRedirect(event, buildUrl, 302);
|
return sendRedirect(event, buildUrl, 302);
|
||||||
}
|
}
|
||||||
return sendRedirect(event, "/", 302)
|
return sendRedirect(event, "/", 302);
|
||||||
})
|
});
|
||||||
|
@ -1,5 +1,25 @@
|
|||||||
import * as cheerio from "cheerio";
|
import * as cheerio from "cheerio";
|
||||||
|
|
||||||
|
function findTime(timeText: string) {
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
const hourMatch = timeText.match(/(\d+)小時前/);
|
||||||
|
const dayMatch = timeText.match(/(\d+)天前/);
|
||||||
|
const minuteMatch = timeText.match(/(\d+)分鐘前/);
|
||||||
|
if (hourMatch) {
|
||||||
|
const hoursAgo = parseInt(hourMatch[1]);
|
||||||
|
return new Date(now.getTime() - hoursAgo * 60 * 60 * 1000);
|
||||||
|
} else if (dayMatch) {
|
||||||
|
const daysAgo = parseInt(dayMatch[1]);
|
||||||
|
return new Date(now.getTime() - daysAgo * 24 * 60 * 60 * 1000);
|
||||||
|
} else if (minuteMatch) {
|
||||||
|
const minutesAgo = parseInt(minuteMatch[1]);
|
||||||
|
return new Date(now.getTime() - minutesAgo * 60 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
async function lineToday(slug: string) {
|
async function lineToday(slug: string) {
|
||||||
const url = "https://today.line.me/tw/v2/article/" + slug;
|
const url = "https://today.line.me/tw/v2/article/" + slug;
|
||||||
const fetchPageCode = await fetch(url, {
|
const fetchPageCode = await fetch(url, {
|
||||||
@ -46,6 +66,7 @@ async function lineToday(slug: string) {
|
|||||||
.text()
|
.text()
|
||||||
.replaceAll("\n", "")
|
.replaceAll("\n", "")
|
||||||
.replaceAll(" ", "");
|
.replaceAll(" ", "");
|
||||||
|
|
||||||
let author = "";
|
let author = "";
|
||||||
const authorInfo = html("span.entityPublishInfo-meta-info")
|
const authorInfo = html("span.entityPublishInfo-meta-info")
|
||||||
.text()
|
.text()
|
||||||
@ -57,16 +78,26 @@ async function lineToday(slug: string) {
|
|||||||
} else {
|
} else {
|
||||||
author = authorInfo;
|
author = authorInfo;
|
||||||
}
|
}
|
||||||
|
const orgAuthorDateData = html("span.entityPublishInfo-meta-info").text();
|
||||||
|
const updateMatch = orgAuthorDateData.match(/更新於\s*([^•]+)/);
|
||||||
|
const publishMatch = orgAuthorDateData.match(/發布於\s*(.+)$/);
|
||||||
|
let updatedAt: Date | null = null;
|
||||||
|
if (updateMatch) {
|
||||||
|
updatedAt = findTime(updateMatch[1].trim());
|
||||||
|
}
|
||||||
|
let publishedAt: Date | null = null;
|
||||||
|
if (publishMatch) {
|
||||||
|
publishedAt = findTime(publishMatch[1].trim());
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
title: title,
|
title: title,
|
||||||
paragraph: paragraph,
|
paragraph: paragraph,
|
||||||
origin: newsOrgdir,
|
origin: newsOrgdir,
|
||||||
author: author,
|
author: author,
|
||||||
images: images,
|
images: images,
|
||||||
|
updateat: updatedAt,
|
||||||
|
publishedat: publishedAt
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Texting on console only!
|
|
||||||
//console.log(await lineToday("wJyR8Nw"));
|
|
||||||
|
|
||||||
export default lineToday;
|
export default lineToday;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user