Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57ea6e681e | ||
|
|
170703ce80 | ||
|
|
396ee45e8c | ||
|
|
9026a08298 | ||
|
|
57112b916b | ||
|
|
2896dfa108 | ||
|
|
b76fc32c9a | ||
|
|
668f6da981 | ||
|
|
b45fd547ab | ||
|
|
afc15b1ed6 | ||
|
|
0f3b282c12 |
14
.github/workflows/issue-translator.yml
vendored
Normal file
14
.github/workflows/issue-translator.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
name: 'issue-translator'
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: usthe/issues-translate-action@v2.7
|
||||
with:
|
||||
IS_MODIFY_TITLE: true
|
||||
@@ -10,10 +10,14 @@ RUN GOOS=linux go build -ldflags="-s -w -X main.version=${VERSION}" -o /app/zenf
|
||||
|
||||
FROM alpine:latest
|
||||
|
||||
ARG VERSION=dev
|
||||
LABEL org.opencontainers.image.version=${VERSION}
|
||||
|
||||
RUN apk add --no-cache ca-certificates tzdata && \
|
||||
mkdir -p /app/data
|
||||
|
||||
COPY --from=builder /app/zenfeed /app/
|
||||
|
||||
WORKDIR /app
|
||||
ENTRYPOINT ["/app/zenfeed"]
|
||||
CMD ["--config", "/app/config/config.yaml"]
|
||||
74
README.md
74
README.md
@@ -59,83 +59,43 @@ zenfeed 是你的智能信息助手。它自动收集、筛选并总结关注的
|
||||
|
||||
### 1. 安装
|
||||
|
||||
替换下方 APIKey 等参数,并完整复制到终端一键执行。注意:
|
||||
默认使用硅基流动的 Qwen/Qwen2.5-7B-Instruct(免费) 和 Pro/BAAI/bge-m3。如果你还没有硅基账号,使用 [邀请链接](https://cloud.siliconflow.cn/i/U2VS0Q5A) 得 14 元额度
|
||||
|
||||
1. `provider` 除了硅基还支持 openai, openrouter, deepseek, gemini, volc(火山(keng)引擎)。也可自定义,参考 [配置文档](docs/config-zh.md)。需要自定义其它参数的大佬也可参考
|
||||
|
||||
2. `llms[0].model` 默认会用来总结内容,相对耗费 Token,一般 Qwen/Qwen2.5-7B-Instruct(免费!!!)足够,当然米够的话越强越好。如果你还没有硅基账号,使用 [邀请链接](https://cloud.siliconflow.cn/i/U2VS0Q5A) 得 14 元额度
|
||||
支持使用其他厂商或模型,按下方提示操作即可
|
||||
|
||||
#### Mac/Linux
|
||||
|
||||
```bash
|
||||
docker run --rm \
|
||||
-v "$(PWD):/app" \
|
||||
-w /app \
|
||||
--entrypoint sh \
|
||||
mikefarah/yq -c '
|
||||
set -e
|
||||
mkdir -p zenfeed/config && cd zenfeed
|
||||
TEMPLATE_URL="https://raw.githubusercontent.com/glidea/zenfeed/main/install/config-template.yaml"
|
||||
COMPOSE_URL="https://raw.githubusercontent.com/glidea/zenfeed/main/install/docker-compose.yml"
|
||||
CONFIG_OUTPUT="config/config.yaml"
|
||||
COMPOSE_OUTPUT="docker-compose.yml"
|
||||
curl -L -O https://raw.githubusercontent.com/glidea/zenfeed/main/docker-compose.yml
|
||||
|
||||
wget -qO- "$TEMPLATE_URL" | yq \
|
||||
".timezone = \"Asia/Shanghai\" |
|
||||
.llms[0].provider = \"siliconflow\" |
|
||||
.llms[0].model = \"Qwen/Qwen2.5-7B-Instruct\" |
|
||||
.llms[0].api_key = \"your_api_key\" | # <<<--- 替换 API Key! 其它参数按需选择
|
||||
.llms[1].provider = \"siliconflow\" |
|
||||
.llms[1].embedding_model = \"Pro/BAAI/bge-m3\" |
|
||||
.llms[1].api_key = \"your_api_key\" | # <<<--- 替换 API Key!
|
||||
.storage.feed.rewrites[0].transform.to_text.prompt = \"{{.summary_html_snippet}}使用中文回复\"" \
|
||||
> "$CONFIG_OUTPUT"
|
||||
|
||||
wget -qO "$COMPOSE_OUTPUT" "$COMPOSE_URL"
|
||||
' && cd zenfeed && docker compose up -d --wait
|
||||
# 如果你需要自定义更多配置参数,请直接编辑(执行下方命令前) docker-compose.yml#configs.zenfeed_config.content
|
||||
# 配置文档 https://github.com/glidea/zenfeed/blob/main/docs/config-zh.md
|
||||
API_KEY=your_apikey docker-compose -p zenfeed up -d
|
||||
```
|
||||
|
||||
#### Windows
|
||||
> 使用 PowerShell 执行
|
||||
```powershell
|
||||
docker run --rm `
|
||||
-v "${PWD}:/app" `
|
||||
-w /app `
|
||||
--entrypoint sh `
|
||||
mikefarah/yq -c '
|
||||
set -e
|
||||
mkdir -p zenfeed/config && cd zenfeed
|
||||
TEMPLATE_URL="https://raw.githubusercontent.com/glidea/zenfeed/main/install/config-template.yaml"
|
||||
COMPOSE_URL="https://raw.githubusercontent.com/glidea/zenfeed/main/install/docker-compose.yml"
|
||||
CONFIG_OUTPUT="config/config.yaml"
|
||||
COMPOSE_OUTPUT="docker-compose.yml"
|
||||
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/glidea/zenfeed/main/docker-compose.yml" -OutFile ([System.IO.Path]::GetFileName("https://raw.githubusercontent.com/glidea/zenfeed/main/docker-compose.yml"))
|
||||
|
||||
wget -qO- "$TEMPLATE_URL" | yq \
|
||||
".timezone = \"Asia/Shanghai\" |
|
||||
.llms[0].provider = \"siliconflow\" |
|
||||
.llms[0].model = \"Qwen/Qwen2.5-7B-Instruct\" |
|
||||
.llms[0].api_key = \"your_api_key\" | # <<<--- 替换 API Key! 其它参数按需选择
|
||||
.llms[1].provider = \"siliconflow\" |
|
||||
.llms[1].embedding_model = \"Pro/BAAI/bge-m3\" |
|
||||
.llms[1].api_key = \"your_api_key\" | # <<<--- 替换 API Key!
|
||||
.storage.feed.rewrites[0].transform.to_text.prompt = \"{{.summary_html_snippet}}使用中文回复\"" \
|
||||
> "$CONFIG_OUTPUT"
|
||||
|
||||
wget -qO "$COMPOSE_OUTPUT" "$COMPOSE_URL"
|
||||
' ; cd zenfeed; docker compose up -d --wait
|
||||
# 如果你需要自定义更多配置参数,请直接编辑(执行下方命令前) docker-compose.yml#configs.zenfeed_config.content
|
||||
# 配置文档 https://github.com/glidea/zenfeed/blob/main/docs/config-zh.md
|
||||
$env:API_KEY = "your_apikey"; docker-compose -p zenfeed up -d
|
||||
```
|
||||
|
||||
### 2. 使用 Web 端
|
||||
|
||||
访问 https://zenfeed-web.pages.dev
|
||||
|
||||
> 会默认连接本地的 zenfeed
|
||||
> 如果部署在 VPS 等环境请访问 https://vps_public_ip:1400(记得开放安全组端口),不要使用上方的公共前端
|
||||
> ⚠️ zenfeed 尚无认证手段,暴露到公网可能会泄露 APIKey,请小心设置安全组。如果你有这方面的安全需求请提 Issue
|
||||
|
||||
#### 添加 RSS 订阅源
|
||||
|
||||
<img src="docs/images/web-add-source.png" alt="" width="400">
|
||||
|
||||
> 从 Follow 迁移过来,参考 [migrate-from-follow.md](docs/migrate-from-follow.md)
|
||||
> 需要访问对应的源站,请保证网络畅通
|
||||
> 添加后稍等几分钟,特别模型有严格速率限制的情况下
|
||||
|
||||
#### 配置每日简报,监控等
|
||||
|
||||
@@ -173,6 +133,12 @@ docker run --rm `
|
||||
* 商用请联系报备,可提供合理范围内的支持。注意是合法商用哦,不欢迎搞灰色
|
||||
* 数据不会永久保存,默认只存储 8 天
|
||||
|
||||
## 鸣谢
|
||||
* 感谢 [eryajf](https://github.com/eryajf) 提供的 [Compose Inline Config](https://github.com/glidea/zenfeed/issues/1) 让部署更易理解
|
||||
|
||||
## 👏🏻 欢迎贡献
|
||||
* 目前还没有规范,只要求一点,“代码一致性”,很重要
|
||||
|
||||
## 免责声明 (Disclaimer)
|
||||
|
||||
**在使用 `zenfeed` 软件(以下简称“本软件”)前,请仔细阅读并理解本免责声明。您的下载、安装、使用本软件或任何相关服务的行为,即表示您已阅读、理解并同意接受本声明的所有条款。如果您不同意本声明的任何内容,请立即停止使用本软件。**
|
||||
|
||||
76
docker-compose.yml
Normal file
76
docker-compose.yml
Normal file
@@ -0,0 +1,76 @@
|
||||
services:
|
||||
zenfeed-web:
|
||||
image: glidea/zenfeed-web:latest
|
||||
ports:
|
||||
- "1400:1400"
|
||||
environment:
|
||||
- PUBLIC_DEFAULT_API_URL=http://zenfeed:1300
|
||||
depends_on:
|
||||
- zenfeed
|
||||
|
||||
zenfeed:
|
||||
image: glidea/zenfeed:latest
|
||||
entrypoint: >
|
||||
sh -c "
|
||||
if [ ! -f /app/config/config.yaml ]; then
|
||||
echo 'Config file not found in volume, initializing from base config...'
|
||||
cp /app/config.base.yaml /app/config/config.yaml;
|
||||
else
|
||||
echo 'Existing config file found in volume.'
|
||||
fi &&
|
||||
echo 'Starting Zenfeed...' &&
|
||||
exec /app/zenfeed --config /app/config/config.yaml
|
||||
"
|
||||
configs:
|
||||
- source: zenfeed_config_base
|
||||
target: /app/config.base.yaml
|
||||
volumes:
|
||||
- data:/app/data
|
||||
- config:/app/config
|
||||
ports:
|
||||
- "1300:1300"
|
||||
- "1301:1301"
|
||||
depends_on:
|
||||
- rsshub
|
||||
|
||||
rsshub:
|
||||
image: diygod/rsshub:2024-12-14
|
||||
ports:
|
||||
- "1200:1200"
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
|
||||
volumes:
|
||||
data: {}
|
||||
config: {}
|
||||
|
||||
configs:
|
||||
zenfeed_config_base:
|
||||
content: |
|
||||
timezone: ${TZ:-Asia/Shanghai}
|
||||
llms:
|
||||
- name: general
|
||||
default: true
|
||||
provider: siliconflow
|
||||
model: Qwen/Qwen2.5-7B-Instruct
|
||||
api_key: ${API_KEY:-your-api-key}
|
||||
- name: embed
|
||||
provider: siliconflow
|
||||
embedding_model: Pro/BAAI/bge-m3
|
||||
api_key: ${API_KEY:-your-api-key}
|
||||
scrape:
|
||||
rsshub_endpoint: http://rsshub:1200
|
||||
storage:
|
||||
feed:
|
||||
rewrites:
|
||||
- transform:
|
||||
to_text:
|
||||
prompt: |
|
||||
{{ .summary_html_snippet }} Respond in ${LANG:-Chinese}
|
||||
label: summary_html_snippet
|
||||
embedding_llm: embed
|
||||
notify:
|
||||
channels:
|
||||
email:
|
||||
feed_html_snippet_template: |
|
||||
{{ .summary_html_snippet }}
|
||||
BIN
docs/images/upgrade-from-v0.1.0-backup.png
Normal file
BIN
docs/images/upgrade-from-v0.1.0-backup.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 131 KiB |
28
docs/upgrade-from-v0.1.0.md
Normal file
28
docs/upgrade-from-v0.1.0.md
Normal file
@@ -0,0 +1,28 @@
|
||||
## 0. Check your current version
|
||||
```bash
|
||||
# Mac/Linux
|
||||
docker inspect glidea/zenfeed:latest | grep version
|
||||
|
||||
# Windows PowerShell
|
||||
docker inspect glidea/zenfeed:latest | Select-String -Pattern 'version'
|
||||
```
|
||||
|
||||
If you **don't see any results**, it means you're using version v0.1.0. This is because the first version didn't include version information. Therefore, **this document applies to you.**
|
||||
|
||||
## 1. Move your data to the correct volume path
|
||||
```bash
|
||||
docker-compose -p zenfeed exec zenfeed cp -a /data/. /app/data/
|
||||
```
|
||||
|
||||
## 2. Backup your config
|
||||
Access: http://localhost:1400
|
||||

|
||||
|
||||
## 3. Upgrade
|
||||
See [upgrade](./upgrade.md)
|
||||
|
||||
## 4. Resave your config
|
||||
Access: http://localhost:1400
|
||||
Resave your config.
|
||||
|
||||
These tedious steps are due to the oversight in the deployment form of the first version, and I apologize for that. Subsequent versions will not require these extra steps.
|
||||
19
docs/upgrade.md
Normal file
19
docs/upgrade.md
Normal file
@@ -0,0 +1,19 @@
|
||||
**NOTE:** If you are upgrading from v0.1.0, which is the first version, please refer to [upgrade-from-v0.1.0.md](./upgrade-from-v0.1.0.md)
|
||||
|
||||
```bash
|
||||
# Ensure compose yml up to date.
|
||||
## Mac/Linux
|
||||
curl -L -O https://raw.githubusercontent.com/glidea/zenfeed/main/docker-compose.yml
|
||||
## Windows PowerShell
|
||||
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/glidea/zenfeed/main/docker-compose.yml" -OutFile ([System.IO.Path]::GetFileName("https://raw.githubusercontent.com/glidea/zenfeed/main/docker-compose.yml"))
|
||||
|
||||
|
||||
# Ensure images up to date.
|
||||
docker-compose -p zenfeed pull
|
||||
|
||||
|
||||
# Upgrading without reconfiguring, etc APIKey.
|
||||
docker-compose -p zenfeed up -d
|
||||
```
|
||||
|
||||
Then all the feed data and configurations should be intact.
|
||||
@@ -1,25 +0,0 @@
|
||||
timezone: Asia/Shanghai
|
||||
llms:
|
||||
- name: general
|
||||
default: true
|
||||
provider: siliconflow
|
||||
model: Qwen/Qwen2.5-7B-Instruct
|
||||
- name: embed
|
||||
provider: siliconflow
|
||||
embedding_model: Pro/BAAI/bge-m3
|
||||
scrape:
|
||||
rsshub_endpoint: http://rsshub:1200
|
||||
storage:
|
||||
feed:
|
||||
rewrites:
|
||||
- transform:
|
||||
to_text:
|
||||
prompt: |
|
||||
{{ .summary_html_snippet }}
|
||||
label: summary_html_snippet
|
||||
embedding_llm: embed
|
||||
notify:
|
||||
channels:
|
||||
email:
|
||||
feed_html_snippet_template: |
|
||||
{{ .summary_html_snippet }}
|
||||
@@ -1,24 +0,0 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
zenfeed:
|
||||
image: glidea/zenfeed:latest
|
||||
volumes:
|
||||
- data:/app/data
|
||||
- type: bind
|
||||
source: ./config
|
||||
target: /app/config
|
||||
ports:
|
||||
- "1300:1300"
|
||||
- "1301:1301"
|
||||
depends_on:
|
||||
- rsshub
|
||||
|
||||
rsshub:
|
||||
image: diygod/rsshub:latest
|
||||
ports:
|
||||
- "1200:1200"
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
|
||||
volumes:
|
||||
data: {}
|
||||
@@ -1,131 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
YQ_IMAGE="mikefarah/yq:latest"
|
||||
|
||||
template_source=""
|
||||
values_args=()
|
||||
|
||||
# --- Parse command line arguments ---
|
||||
while [[ $# -gt 0 ]]; do
|
||||
key="$1"
|
||||
case $key in
|
||||
--template)
|
||||
template_source="$2"
|
||||
shift # past argument
|
||||
shift # past value
|
||||
;;
|
||||
--values)
|
||||
# Collect all arguments after --values until next -- argument or end
|
||||
shift # past --values
|
||||
while [[ $# -gt 0 ]] && [[ ! "$1" =~ ^-- ]]; do
|
||||
values_args+=("$1")
|
||||
shift # past value argument
|
||||
done
|
||||
;;
|
||||
*) # Unknown option
|
||||
echo "Error: Unknown option $1" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# --- Get template content ---
|
||||
current_yaml=""
|
||||
if [[ -z "$template_source" ]]; then
|
||||
# If no template provided, start with empty YAML
|
||||
current_yaml="{}"
|
||||
elif [[ "$template_source" =~ ^https?:// ]]; then
|
||||
# Download from URL
|
||||
# Use curl, exit if fails
|
||||
if ! command -v curl &> /dev/null; then
|
||||
echo "Error: curl command required to download URL template." >&2
|
||||
exit 1
|
||||
fi
|
||||
template_content=$(curl -sfL "$template_source")
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "Error: Failed to download template from URL: $template_source" >&2
|
||||
exit 1
|
||||
fi
|
||||
# Check if downloaded content is empty
|
||||
if [[ -z "$template_content" ]]; then
|
||||
current_yaml="{}"
|
||||
else
|
||||
current_yaml="$template_content"
|
||||
fi
|
||||
|
||||
elif [[ -f "$template_source" ]]; then
|
||||
# Read from local file
|
||||
current_yaml=$(cat "$template_source")
|
||||
# Check if file content is empty
|
||||
if [[ -z "$current_yaml" ]]; then
|
||||
current_yaml="{}"
|
||||
fi
|
||||
else
|
||||
# Invalid template source
|
||||
echo "Error: Invalid template source '$template_source'. Please provide valid file path or HTTP/HTTPS URL." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
# --- Check if Docker is available ---
|
||||
if ! command -v docker &> /dev/null; then
|
||||
echo "Error: docker command required to run yq." >&2
|
||||
exit 1
|
||||
fi
|
||||
# Try pulling or verifying yq image (helps catch issues early)
|
||||
docker pull $YQ_IMAGE > /dev/null
|
||||
|
||||
# --- Apply values ---
|
||||
if [[ ${#values_args[@]} -gt 0 ]]; then
|
||||
for val_arg in "${values_args[@]}"; do
|
||||
# Parse key=value
|
||||
if [[ ! "$val_arg" =~ ^([^=]+)=(.*)$ ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# BASH_REMATCH is result array from =~ operator
|
||||
yaml_path="${BASH_REMATCH[1]}"
|
||||
raw_value="${BASH_REMATCH[2]}"
|
||||
|
||||
# Prepare yq value (try handling basic types, otherwise treat as string)
|
||||
yq_value=""
|
||||
if [[ "$raw_value" == "true" || "$raw_value" == "false" || "$raw_value" == "null" ]]; then
|
||||
yq_value="$raw_value"
|
||||
# Check if integer or float (simple regex)
|
||||
elif [[ "$raw_value" =~ ^-?[0-9]+(\.[0-9]+)?$ ]]; then
|
||||
# If value starts with 0 but isn't 0 itself and has no decimal point, force string to prevent octal interpretation
|
||||
if [[ "$raw_value" =~ ^0[0-9]+$ ]]; then
|
||||
# Need to escape internal double quotes
|
||||
escaped_value=$(echo "$raw_value" | sed 's/"/\\"/g')
|
||||
yq_value="\"$escaped_value\""
|
||||
else
|
||||
yq_value="$raw_value"
|
||||
fi
|
||||
else
|
||||
# Treat as string, need to escape internal double quotes
|
||||
escaped_value=$(echo "$raw_value" | sed 's/"/\\"/g')
|
||||
yq_value="\"$escaped_value\""
|
||||
fi
|
||||
|
||||
# Build yq expression
|
||||
yq_expression=".$yaml_path = $yq_value"
|
||||
|
||||
# Apply update via docker run yq
|
||||
# Pass current YAML via stdin to yq, get stdout as new YAML
|
||||
# Use <<< for here-string input to avoid temp files
|
||||
new_yaml=$(docker run --rm -i "$YQ_IMAGE" "$yq_expression" <<< "$current_yaml")
|
||||
yq_exit_code=$?
|
||||
|
||||
if [[ $yq_exit_code -ne 0 ]]; then
|
||||
echo "Error: yq execution failed (exit code: $yq_exit_code). Expression: '$yq_expression'" >&2
|
||||
# Could output yq error message, but requires more complex docker run call to capture stderr
|
||||
exit 1
|
||||
fi
|
||||
current_yaml="$new_yaml"
|
||||
done
|
||||
fi
|
||||
|
||||
# --- Output final result ---
|
||||
printf "%s\n" "$current_yaml"
|
||||
|
||||
exit 0
|
||||
@@ -138,7 +138,7 @@ type QueryRSSHubRoutesResponse struct {
|
||||
type RSSHubRoute struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Path any `json:"path,omitempty"`
|
||||
Example string `json:"example,omitempty"`
|
||||
Parameters map[string]any `json:"parameters,omitempty"`
|
||||
Features map[string]any `json:"features,omitempty"`
|
||||
|
||||
Reference in New Issue
Block a user