6 Commits

Author SHA1 Message Date
glidea
094600ee26 update README
Removed sponsorship section and updated images with links.
2025-11-22 14:59:24 +08:00
glidea
c03e4c8359 Merge pull request #31 from Twelveeee/main
feat:add RSSHub RSSHubAccessKey
2025-11-07 15:59:20 +08:00
Twelveeee
584f94e1ef feat:add RSSHub RSSHubAccessKey 2025-11-07 14:27:29 +08:00
Twelveeee
6c4223de92 feat:add RSSHub RSSHubAccessKey 2025-11-06 15:58:11 +08:00
Twelveeee
f67db8ea86 feat:add RSSHub RSSHubAccessKey 2025-11-06 11:06:26 +08:00
Twelveeee
bc54cc852e feat:add RSSHub RSSHubAccessKey 2025-11-05 14:55:01 +00:00
7 changed files with 82 additions and 17 deletions

View File

@@ -1,5 +1,3 @@
[Nano Banana🍌 公益站](https://image-generation.zenfeed.xyz/):集成 Twitter 热门 Prompt轻松玩转各种姿势
---
[English](README-en.md)
---
@@ -55,14 +53,6 @@ zenfeed 是你的 <strong>AI 信息中枢</strong>。它既是<strong>智能 RSS
---
**赞助项目可以领取 Gemini Key**
<a href="https://afdian.com/a/glidea"><img src="docs/images/sponsor.png" width="500"></a>
<br/>
<a href="https://afdian.com/a/glidea">赞助项目,支持发展</a>
---
## 💡 前言
RSS简易信息聚合诞生于 Web 1.0 时代,旨在解决信息分散的问题,让用户能在一个地方聚合、追踪多个网站的更新,无需频繁访问。它将网站更新以摘要形式推送给订阅者,便于快速获取信息。
@@ -215,18 +205,26 @@ $env:API_KEY = "sk-..."; docker-compose -p zenfeed up -d
<table>
<tr>
<td align="center">
<img src="docs/images/wechat.png" alt="Wechat QR Code" width="300">
<img src="https://github.com/glidea/zenfeed/blob/main/docs/images/wechat.png?raw=true" alt="Wechat QR Code" width="300">
<br>
<strong>AI 学习交流社群</strong>
</td>
<td align="center">
<img src="docs/images/sponsor.png" width="500">
<img src="https://github.com/glidea/banana-prompt-quicker/blob/main/images/glidea.png?raw=true" width="250">
<br>
<strong><a href="https://afdian.com/a/glidea">请杯奶茶 🧋</a></strong>
<strong><a href="https://glidea.zenfeed.xyz/">我的其它项目</a></strong>
</td>
</tr>
<tr>
<td align="center" colspan="2">
<img src="https://github.com/glidea/banana-prompt-quicker/blob/main/images/readnote.png?raw=true" width="400">
<br>
<strong><a href="https://www.xiaohongshu.com/user/profile/5f7dc54d0000000001004afb">📕 小红书账号 - 持续分享 AI 原创</a></strong>
</td>
</tr>
</table>
都看到这里了,顺手点个 **Star ⭐️** 呗,这是我持续维护的最大动力!
有好玩的 AI 工作也请联系我!

View File

@@ -59,6 +59,7 @@
| `scrape.past` | `time.Duration` | 抓取 Feed 的回溯时间窗口。例如 `1h` 表示只抓取过去 1 小时的 Feed。 | `24h` | 否 |
| `scrape.interval` | `time.Duration` | 抓取每个源的频率 (全局默认值)。例如 `1h`。 | `1h` | 否 |
| `scrape.rsshub_endpoint` | `string` | RSSHub 的端点。你可以部署自己的 RSSHub 服务器或使用公共实例 (参见 [RSSHub 文档](https://docs.rsshub.app/guide/instances))。例如 `https://rsshub.app`。 | | 是 (如果使用了 `rsshub_route_path`) |
| `scrape.rsshub_access_key` | `string` | RSSHub 的访问密钥。用于访问控制。(详情见 [RSSHub文档访问控制](https://docs.rsshub.app/deploy/config#access-control-configurations)) | | 否 |
| `scrape.sources` | `对象列表` | 用于抓取 Feed 的源列表。详见下方的 **抓取源配置**。 | `[]` | 是 (至少一个) |
### 抓取源配置 (`scrape.sources[]`)

View File

@@ -59,6 +59,7 @@ This section configures parameters related to the Jina AI Reader API, primarily
| `scrape.past` | `time.Duration` | Time window to look back when scraping feeds. E.g., `1h` means only scrape feeds from the past 1 hour. | `24h` | No |
| `scrape.interval` | `time.Duration` | Frequency to scrape each source (global default). E.g., `1h`. | `1h` | No |
| `scrape.rsshub_endpoint` | `string` | Endpoint for RSSHub. You can deploy your own RSSHub server or use a public instance (see [RSSHub Documentation](https://docs.rsshub.app/guide/instances)). E.g., `https://rsshub.app`. | | Yes (if `rsshub_route_path` is used) |
| `scrape.rsshub_access_key` | `string` | The access key for RSSHub. Used for access control. (see [RSSHub config](https://docs.rsshub.app/deploy/config#access-control-configurations))| | No |
| `scrape.sources` | `list of objects` | List of sources to scrape feeds from. See **Scrape Source Configuration** below. | `[]` | Yes (at least one) |
### Scrape Source Configuration (`scrape.sources[]`)

View File

@@ -95,10 +95,11 @@ type LLM struct {
}
type Scrape struct {
Past timeutil.Duration `yaml:"past,omitempty" json:"past,omitempty" desc:"The lookback time window for scraping feeds. e.g. 1h means only scrape feeds in the past 1 hour. Default: 3d"`
Interval timeutil.Duration `yaml:"interval,omitempty" json:"interval,omitempty" desc:"How often to scrape each source, it is a global interval. e.g. 1h. Default: 1h"`
RSSHubEndpoint string `yaml:"rsshub_endpoint,omitempty" json:"rsshub_endpoint,omitempty" desc:"The endpoint of the RSSHub. You can deploy your own RSSHub server or use the public one (https://docs.rsshub.app/guide/instances). e.g. https://rsshub.app. It is required when sources[].rss.rsshub_route_path is set."`
Sources []ScrapeSource `yaml:"sources,omitempty" json:"sources,omitempty" desc:"The sources for scraping feeds."`
Past timeutil.Duration `yaml:"past,omitempty" json:"past,omitempty" desc:"The lookback time window for scraping feeds. e.g. 1h means only scrape feeds in the past 1 hour. Default: 3d"`
Interval timeutil.Duration `yaml:"interval,omitempty" json:"interval,omitempty" desc:"How often to scrape each source, it is a global interval. e.g. 1h. Default: 1h"`
RSSHubEndpoint string `yaml:"rsshub_endpoint,omitempty" json:"rsshub_endpoint,omitempty" desc:"The endpoint of the RSSHub. You can deploy your own RSSHub server or use the public one (https://docs.rsshub.app/guide/instances). e.g. https://rsshub.app. It is required when sources[].rss.rsshub_route_path is set."`
RSSHubAccessKey string `yaml:"rsshub_access_key,omitempty" json:"rsshub_access_key,omitempty" desc:"The access key for RSSHub. Used for access control. (see [RSSHub config](https://docs.rsshub.app/deploy/config#access-control-configurations))"`
Sources []ScrapeSource `yaml:"sources,omitempty" json:"sources,omitempty" desc:"The sources for scraping feeds."`
}
type Storage struct {

View File

@@ -80,6 +80,7 @@ func (c *Config) From(app *config.App) {
URL: app.Scrape.Sources[i].RSS.URL,
RSSHubEndpoint: app.Scrape.RSSHubEndpoint,
RSSHubRoutePath: app.Scrape.Sources[i].RSS.RSSHubRoutePath,
RSSHubAccessKey: app.Scrape.RSSHubAccessKey,
}
}
}

View File

@@ -33,6 +33,7 @@ type ScrapeSourceRSS struct {
URL string
RSSHubEndpoint string
RSSHubRoutePath string
RSSHubAccessKey string
}
func (c *ScrapeSourceRSS) Validate() error {
@@ -46,9 +47,22 @@ func (c *ScrapeSourceRSS) Validate() error {
return errors.New("URL must be a valid HTTP/HTTPS URL")
}
// Append access key as query parameter if provided
c.appendAccessKey()
return nil
}
func (c *ScrapeSourceRSS) appendAccessKey() {
if c.RSSHubEndpoint != "" && c.RSSHubAccessKey != "" && !strings.Contains(c.URL, "key=") {
if strings.Contains(c.URL, "?") {
c.URL += "&key=" + c.RSSHubAccessKey
} else {
c.URL += "?key=" + c.RSSHubAccessKey
}
}
}
// --- Factory code block ---
func newRSSReader(config *ScrapeSourceRSS) (reader, error) {
if err := config.Validate(); err != nil {

View File

@@ -122,6 +122,55 @@ func TestNewRSS(t *testing.T) {
},
},
},
{
Scenario: "Valid Configuration - RSSHub with Access Key",
Given: "a valid configuration with RSSHub details and access key",
When: "creating a new RSS reader",
Then: "should succeed, construct the URL with access key, and return a valid reader",
GivenDetail: givenDetail{
config: &ScrapeSourceRSS{
RSSHubEndpoint: "http://rsshub.app/",
RSSHubRoutePath: "/_/test",
RSSHubAccessKey: "testkey",
},
},
WhenDetail: whenDetail{},
ThenExpected: thenExpected{
wantErr: false,
validateFunc: func(t *testing.T, r reader) {
Expect(r).NotTo(BeNil())
rssReader, ok := r.(*rssReader)
Expect(ok).To(BeTrue())
Expect(rssReader.config.URL).To(Equal("http://rsshub.app/_/test?key=testkey"))
Expect(rssReader.config.RSSHubEndpoint).To(Equal("http://rsshub.app/"))
Expect(rssReader.config.RSSHubRoutePath).To(Equal("/_/test"))
Expect(rssReader.config.RSSHubAccessKey).To(Equal("testkey"))
},
},
},
{
Scenario: "Valid Configuration - URL with Access Key",
Given: "a valid configuration with URL and access key",
When: "creating a new RSS reader",
Then: "should succeed, append access key to URL, and return a valid reader",
GivenDetail: givenDetail{
config: &ScrapeSourceRSS{
URL: "http://example.com/feed",
RSSHubAccessKey: "testkey",
},
},
WhenDetail: whenDetail{},
ThenExpected: thenExpected{
wantErr: false,
validateFunc: func(t *testing.T, r reader) {
Expect(r).NotTo(BeNil())
rssReader, ok := r.(*rssReader)
Expect(ok).To(BeTrue())
Expect(rssReader.config.URL).To(Equal("http://example.com/feed"))
Expect(rssReader.config.RSSHubAccessKey).To(Equal("testkey"))
},
},
},
}
// --- Run tests ---