Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ db/forward.db1
**/venv/
**/env/
**/ENV/
**/.venv/

# 忽略 Telethon 会话文件
*.session
Expand Down
14 changes: 13 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@ CHAT_UPDATE_TIME=03:00
# 数据库配置
DATABASE_URL=sqlite:///./db/forward.db

######### 网络代理配置 #########
# 代理类型 (http/socks5/socks4/none)
PROXY_TYPE=none
# 代理地址
PROXY_HOST=
# 代理端口
PROXY_PORT=
# 代理用户名
PROXY_USERNAME=
# 代理密码
PROXY_PASSWORD=

######### UI 布局配置 #########
AI_MODELS_PER_PAGE=10
KEYWORDS_PER_PAGE=10
Expand Down Expand Up @@ -65,7 +77,7 @@ MEDIA_EXTENSIONS_COLS=6
# 每页显示的规则数量
RULES_PER_PAGE=20

######### AI设置 #########
######### AI设置 #########

# 默认AI模型
DEFAULT_AI_MODEL=gemini-2.0-flash
Expand Down
170 changes: 170 additions & 0 deletions .github/workflows/publish-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
name: Publish Image

on:
push:
branches: ["main"]
tags: ["v*.*.*"]

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build:
name: Build (${{ matrix.arch }})
runs-on: ${{ matrix.runs_on }}
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
include:
- platform: linux/amd64
arch: amd64
runs_on: ubuntu-latest
- platform: linux/arm64
arch: arm64
runs_on: ubuntu-24.04-arm
steps:
- name: Checkout
uses: actions/checkout@v5

- name: Normalize image name
id: vars
shell: bash
run: |
echo "image=${REGISTRY}/${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT"

- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ steps.vars.outputs.image }}
# latest 的默认行为是 auto;会在 semver/tag 等场景自动生成 latest
flavor: |
latest=auto

# ✅ Git tag = v1.2.3 时,生成 1.2.3 / 1.2 / 1(注意:{{version}} 会去掉前缀 v)
# ✅ push 到 main 时也打 latest(推荐写法:用 is_default_branch 判断)
# ✅ 每次都生成提交 SHA tag(默认形如 sha-860c190)
tags: |
type=raw,value=latest,enable={{is_default_branch}}

type=semver,pattern={{version}} # 1.2.3
type=semver,pattern={{major}}.{{minor}} # 1.2
type=semver,pattern={{major}} # 1

type=sha,format=short

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
with:
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,name=${{ steps.vars.outputs.image }},push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=${{ matrix.arch }}
cache-to: type=gha,mode=max,scope=${{ matrix.arch }}
provenance: false
sbom: false

- name: Export digest
run: |
mkdir -p digests
echo "${{ steps.build.outputs.digest }}" > "digests/${{ matrix.arch }}.digest"

- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digests-${{ matrix.arch }}
path: digests/${{ matrix.arch }}.digest
if-no-files-found: error
retention-days: 1

merge:
name: Merge manifest
runs-on: ubuntu-latest
needs: build
permissions:
contents: read
packages: write
steps:
- name: Download digests
uses: actions/download-artifact@v4
with:
pattern: digests-*
path: digests
merge-multiple: true

- name: Normalize image name
id: vars
shell: bash
run: |
echo "image=${REGISTRY}/${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT"

- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ steps.vars.outputs.image }}
# latest 的默认行为是 auto;会在 semver/tag 等场景自动生成 latest
flavor: |
latest=auto

# ✅ Git tag = v1.2.3 时,生成 1.2.3 / 1.2 / 1(注意:{{version}} 会去掉前缀 v)
# ✅ push 到 main 时也打 latest(推荐写法:用 is_default_branch 判断)
# ✅ 每次都生成提交 SHA tag(默认形如 sha-860c190)
tags: |
type=raw,value=latest,enable={{is_default_branch}}

type=semver,pattern={{version}} # 1.2.3
type=semver,pattern={{major}}.{{minor}} # 1.2
type=semver,pattern={{major}} # 1

type=sha,format=short

- name: Create multi-arch manifest and push tags
env:
TAGS: ${{ steps.meta.outputs.tags }}
run: |
set -euo pipefail

IMAGE="${{ steps.vars.outputs.image }}"

refs=()
for f in digests/*.digest; do
digest="$(cat "$f")"
refs+=("${IMAGE}@${digest}")
done

tag_args=()
while IFS= read -r tag; do
[[ -n "$tag" ]] || continue
tag_args+=("-t" "$tag")
done <<< "$TAGS"

docker buildx imagetools create "${tag_args[@]}" "${refs[@]}"

first_tag="$(printf '%s\n' "$TAGS" | sed -n '1p')"
docker buildx imagetools inspect "$first_tag"
33 changes: 16 additions & 17 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,34 +1,33 @@
FROM python:3.11-slim AS builder

WORKDIR /build

RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
python3-dev \
&& rm -rf /var/lib/apt/lists/*

COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt

FROM python:3.11-slim

# 设置工作目录
WORKDIR /app

# 设置Docker日志配置
ENV DOCKER_LOG_MAX_SIZE=10m
ENV DOCKER_LOG_MAX_FILE=3
ENV DOCKER_LOG_MAX_SIZE=10m DOCKER_LOG_MAX_FILE=3

# 安装系统依赖
RUN apt-get update && apt-get install -y \
RUN apt-get update && apt-get install -y --no-install-recommends \
tzdata \
&& ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& dpkg-reconfigure -f noninteractive tzdata \
&& apt-get install -y \
gcc \
python3-dev \
&& rm -rf /var/lib/apt/lists/*

# 复制依赖文件并安装
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY --from=builder /install /usr/local

# 创建临时文件目录
RUN mkdir -p /app/temp

# 复制应用代码
COPY . .

# 设置环境变量
ENV PYTHONUNBUFFERED=1

# 启动命令
CMD ["python", "main.py"]
CMD ["python", "main.py"]
15 changes: 13 additions & 2 deletions filters/media_filter.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging
import os
import asyncio
from telethon.tl.types import DocumentAttributeSticker
from utils.media import get_media_size
from utils.constants import TEMP_DIR
from filters.base_filter import BaseFilter
Expand Down Expand Up @@ -252,7 +253,14 @@ async def _process_single_media(self, context):
# 记录这是纯链接预览消息
context.is_pure_link_preview = True
logger.info('这是一条纯链接预览消息')


def _is_sticker(self, media):
doc = getattr(media, 'document', None)
attributes = getattr(doc, 'attributes', None) if doc else None
if not attributes:
return False
return any(isinstance(attr, DocumentAttributeSticker) for attr in attributes)

async def _is_media_type_blocked(self, media, media_types):
"""
检查媒体类型是否被屏蔽
Expand All @@ -268,6 +276,10 @@ async def _is_media_type_blocked(self, media, media_types):
if getattr(media, 'photo', None) and media_types.photo:
logger.info('媒体类型为图片,已被屏蔽')
return True

if getattr(media, 'document', None) and self._is_sticker(media) and getattr(media_types, 'sticker', False):
logger.info('媒体类型为贴纸,已被屏蔽')
return True

if getattr(media, 'document', None) and media_types.document:
logger.info('媒体类型为文档,已被屏蔽')
Expand Down Expand Up @@ -360,4 +372,3 @@ async def _is_media_extension_allowed(self, rule, media):
session.close()

return allowed

Loading