diff --git a/scripts/tinytinypy b/scripts/tinytinypy index 16e0448..e7c07bd 100644 --- a/scripts/tinytinypy +++ b/scripts/tinytinypy @@ -8,14 +8,31 @@ import json import os import re import sys +from typing import Sequence import xdg.BaseDirectory -from tinytinypy import Connection +from tinytinypy import Connection, UpdateMode, UpdateField APP_NAME = "tinytinypy" # For default config directories +UPDATE_MODES = { + "false": UpdateMode.SET_TO_FALSE, + "true": UpdateMode.SET_TO_TRUE, + "toggle": UpdateMode.TOGGLE, +} + +UPDATE_FIELDS = { + "published": UpdateField.PUBLISHED, + "starred": UpdateField.STARRED, + "unread": UpdateField.UNREAD, +} + +def comma_int_list(text: str) -> Sequence[int]: + return [int(i) for i in text.split(",")] + + class OutputMode(Enum): # enum values JSON = ('json', False) @@ -37,6 +54,39 @@ class OutputMode(Enum): def __str__(self): return self.mode_name +class ContentTTSParser(HTMLParser): + END_SENTENCE_CHARS = [".", ";", ":", "!", "?"] + ONLY_SEPARATION_CHARS = [","] + PUNCTATION_CHARS = END_SENTENCE_CHARS + ONLY_SEPARATION_CHARS + SENTENCE_TAG = ["p", "div"] + def __init__(self): + super().__init__() + self.extracted = "" + def append(self, text): + if self.extracted: + self.extracted += " " + self.extracted += ' '.join(text.split()) + def close_sentence(self): + if self.extracted: + last_char = self.extracted[-1] + if last_char not in self.END_SENTENCE_CHARS: + if last_char in self.ONLY_SEPARATION_CHARS: + self.extracted = self.extracted[:-1] + self.END_SENTENCE_CHARS[0] + else: + self.extracted += self.END_SENTENCE_CHARS[0] + def append_sentence(self, text): + self.close_sentence() + self.append(text) + self.close_sentence() + def handle_starttag(self, tag, attrs): + if tag in self.SENTENCE_TAG: + self.close_sentence() + def handle_endtag(self, tag): + if tag in self.SENTENCE_TAG: + self.close_sentence() + def handle_data(self, data): + self.append(data) + def func_articles(server: Connection, args: argparse.Namespace): filtered = {key: value for key, value in args.__dict__.items() if key in ["feed_id", "cat_id", "limit", "skip", "view_mode", "since_id", "include_nested", "order_by"]} headlines = server.getHeadlines(show_content=args.output_mode.requires_content, **filtered) @@ -60,6 +110,15 @@ def func_articles(server: Connection, args: argparse.Namespace): else: raise Exception(f'Not implemented output mode "{args.output_mode}"') +def func_update(server: Connection, args: argparse.Namespace): + # argpase should check that mode and field are valid + updated = server.updateArticle( + article_ids=args.article_ids, + mode=UPDATE_MODES[args.mode], + field=UPDATE_FIELDS[args.field], + ) + print(f"Updated {updated} article(s)") + def configure_parser(): parser = argparse.ArgumentParser(description="Allows access to feeds and articles of a Tiny Tiny RSS instance using the API.") #parser.add_argument('--proto', '--protocol', dest='proto', default='https', choices=['http', 'https'], help="The protocol used to access the api, defaults to https") @@ -78,6 +137,12 @@ def configure_parser(): p.add_argument('--view-mode', '--filter', dest='view_mode', choices=['all_articles', 'unread', 'adaptive', 'marked', 'updated'], default='all_articles', help='Only show articles of certain type') p.add_argument('--order', '--order-by', dest='order_by', choices=['feed_dates', 'date_reverse'], default='date_reverse') p.add_argument('--output-mode', dest='output_mode', choices=OutputMode, type=OutputMode.parse_mode, default='json', help='Define how the received articles should be outputed, in most modes except json and *-full modes, one line equals a single article') + #= Update Articles + p = sub.add_parser("update", help="Update articles") + p.set_defaults(func=func_update) + p.add_argument("-i", "--ids", "--article-ids", dest="article_ids", type=comma_int_list, help="Comma separated list of article ids to update") + p.add_argument("-m", "--mode", dest="mode", choices=UPDATE_MODES.keys()) + p.add_argument("-f", "--field", dest="field", choices=UPDATE_FIELDS.keys()) return parser def parse(args): diff --git a/tinytinypy/__init__.py b/tinytinypy/__init__.py index 0e03a7d..9a60757 100644 --- a/tinytinypy/__init__.py +++ b/tinytinypy/__init__.py @@ -1 +1 @@ -from .main import Connection +from .main import Connection, UpdateField, UpdateMode diff --git a/tinytinypy/main.py b/tinytinypy/main.py index d09f6f5..7befefd 100644 --- a/tinytinypy/main.py +++ b/tinytinypy/main.py @@ -1,12 +1,26 @@ #!/usr/bin/env python3 import atexit +from enum import IntEnum import http.client import json -from typing import Dict +from typing import Dict, Optional, Sequence from .JsonClass import JsonClass + +class UpdateMode(IntEnum): + SET_TO_FALSE = 0 + SET_TO_TRUE = 1 + TOGGLE = 2 + +class UpdateField(IntEnum): + STARRED = 0 + PUBLISHED = 1 + UNREAD = 2 + ARTICLE_NOTE = 3 + + # Class Invalid class TtRssCounters: def __init__(feeds=None, labels=None, categories=None, tags=None): @@ -259,3 +273,13 @@ class Connection: def getArticle(self, article_id): r = self._getSafe('getArticle', article_id=article_id) return Article.fromJson(r) + + def updateArticle(self, article_ids: Sequence[int], mode: UpdateMode, field: UpdateField, data: Optional[str] = None) -> int: + r = self._getSafe( + op='updateArticle', + article_ids=",".join(str(i) for i in article_ids), + mode=mode.value, + field=field.value, + data=data, + ) + return r["updated"]