mirror of
https://github.com/driftywinds/rssrise.git
synced 2025-12-19 03:23:32 +00:00
Add files via upload
This commit is contained in:
5
.env
Normal file
5
.env
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
FRESHRSS_URL=https://frss.example.com/api/greader.php
|
||||||
|
FRESHRSS_USERNAME=YourUsername
|
||||||
|
FRESHRSS_PASSWORD=YourPassword
|
||||||
|
POLL_INTERVAL=60
|
||||||
|
APPRISE_URLS=discord://webhook_id/webhook_token,gotifys://gotify.example.com/token,tgram://bot_token/chat_id1/chat_id2/chat_idN
|
||||||
12
Dockerfile
Normal file
12
Dockerfile
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
FROM python:3.11-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
COPY main.py .
|
||||||
|
|
||||||
|
CMD ["python3", "main.py"]
|
||||||
8
compose.yml
Normal file
8
compose.yml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
version: "3.9"
|
||||||
|
services:
|
||||||
|
rssrise:
|
||||||
|
image: ghcr.io/driftywinds/rssrise:latest
|
||||||
|
container_name: rssrise
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./.env:/app/.env
|
||||||
137
main.py
Normal file
137
main.py
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
import os
|
||||||
|
import time
|
||||||
|
import requests
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
import subprocess
|
||||||
|
import re
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
FRESHRSS_URL = os.getenv("FRESHRSS_URL") # e.g. https://freshrss.example.net/api/greader.php
|
||||||
|
USERNAME = os.getenv("FRESHRSS_USERNAME")
|
||||||
|
PASSWORD = os.getenv("FRESHRSS_PASSWORD")
|
||||||
|
POLL_INTERVAL = int(os.getenv("POLL_INTERVAL", "60")) # seconds
|
||||||
|
|
||||||
|
APPRISE_CONFIG = os.getenv("APPRISE_CONFIG") # apprise URLs or config file, optional
|
||||||
|
|
||||||
|
session = requests.Session()
|
||||||
|
auth_token = None
|
||||||
|
|
||||||
|
def login():
|
||||||
|
global auth_token
|
||||||
|
login_url = f"{FRESHRSS_URL}/accounts/ClientLogin"
|
||||||
|
params = {
|
||||||
|
"Email": USERNAME,
|
||||||
|
"Passwd": PASSWORD
|
||||||
|
}
|
||||||
|
resp = session.get(login_url, params=params)
|
||||||
|
if resp.status_code != 200:
|
||||||
|
print(f"Login failed: {resp.status_code} {resp.text}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Response is like:
|
||||||
|
# SID=alice/8e6845e089457af25303abc6f53356eb60bdb5f8
|
||||||
|
# Auth=alice/8e6845e089457af25303abc6f53356eb60bdb5f8
|
||||||
|
# We need the Auth line
|
||||||
|
for line in resp.text.splitlines():
|
||||||
|
if line.startswith("Auth="):
|
||||||
|
auth_token = line[len("Auth="):].strip()
|
||||||
|
break
|
||||||
|
if not auth_token:
|
||||||
|
print("Auth token not found in login response")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(f"Logged in, got auth token")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_headers():
|
||||||
|
return {
|
||||||
|
"Authorization": f"GoogleLogin auth={auth_token}"
|
||||||
|
}
|
||||||
|
|
||||||
|
def fetch_unread_items():
|
||||||
|
url = f"{FRESHRSS_URL}/reader/api/0/stream/contents/reading-list?output=json&n=20"
|
||||||
|
resp = session.get(url, headers=get_headers())
|
||||||
|
if resp.status_code != 200:
|
||||||
|
print(f"Failed to fetch unread items: {resp.status_code} {resp.text}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
data = resp.json()
|
||||||
|
items = data.get("items", [])
|
||||||
|
unread_items = []
|
||||||
|
for item in items:
|
||||||
|
categories = item.get("categories", [])
|
||||||
|
# Skip items marked as read
|
||||||
|
if "user/-/state/com.google/read" not in categories:
|
||||||
|
unread_items.append(item)
|
||||||
|
return unread_items
|
||||||
|
|
||||||
|
def clean_html(raw_html):
|
||||||
|
cleanr = re.compile('<.*?>')
|
||||||
|
cleantext = re.sub(cleanr, '', raw_html)
|
||||||
|
return cleantext.strip()
|
||||||
|
|
||||||
|
def format_message(item):
|
||||||
|
title = item.get("title", "No title").strip()
|
||||||
|
url = item.get("alternate", [{}])[0].get("href", "").strip()
|
||||||
|
categories = item.get("categories", [])
|
||||||
|
fresh_categories = [c for c in categories if not c.startswith("user/-/state/com.google")]
|
||||||
|
category = fresh_categories[0] if fresh_categories else "Uncategorized"
|
||||||
|
|
||||||
|
message_body = (
|
||||||
|
f"---------\n"
|
||||||
|
f"URL: {url}\n\n"
|
||||||
|
f"Category: {category}\n"
|
||||||
|
f"---------"
|
||||||
|
)
|
||||||
|
|
||||||
|
return title, message_body
|
||||||
|
|
||||||
|
|
||||||
|
def send_notification(title, body):
|
||||||
|
# Compose apprise command
|
||||||
|
cmd = ["apprise"]
|
||||||
|
if APPRISE_CONFIG:
|
||||||
|
cmd += ["-q", "-c", APPRISE_CONFIG] # quiet mode, config file
|
||||||
|
cmd += ["-t", title, "-b", body]
|
||||||
|
print(f"Sending notification: {title}")
|
||||||
|
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||||
|
if result.returncode != 0:
|
||||||
|
print(f"Failed to send notification: {result.stderr}")
|
||||||
|
else:
|
||||||
|
print("Notification sent.")
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if not login():
|
||||||
|
return
|
||||||
|
|
||||||
|
sent_ids = set()
|
||||||
|
|
||||||
|
# On first run, send all unread items
|
||||||
|
unread_items = fetch_unread_items()
|
||||||
|
print(f"[{datetime.datetime.now().isoformat()}] Fetched {len(unread_items)} unread items on startup")
|
||||||
|
for item in unread_items:
|
||||||
|
item_id = item.get("id")
|
||||||
|
if item_id in sent_ids:
|
||||||
|
continue
|
||||||
|
title, body = format_message(item)
|
||||||
|
send_notification(title, body)
|
||||||
|
sent_ids.add(item_id)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
time.sleep(POLL_INTERVAL)
|
||||||
|
now = datetime.datetime.now().isoformat()
|
||||||
|
unread_items = fetch_unread_items()
|
||||||
|
print(f"[{now}] Polled and found {len(unread_items)} unread items")
|
||||||
|
|
||||||
|
for item in unread_items:
|
||||||
|
item_id = item.get("id")
|
||||||
|
if item_id in sent_ids:
|
||||||
|
continue
|
||||||
|
title, body = format_message(item)
|
||||||
|
send_notification(title, body)
|
||||||
|
sent_ids.add(item_id)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
3
requirements.txt
Normal file
3
requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
requests
|
||||||
|
apprise
|
||||||
|
python-dotenv
|
||||||
Reference in New Issue
Block a user