From e21e42e832e4ac633a9d48f6e30c34a9011008bc Mon Sep 17 00:00:00 2001 From: alyx Date: Sat, 25 Nov 2023 22:31:49 -0500 Subject: Main interface done. Up next: docs, logging, and polish --- src/bbss/__init__.py | 3 ++- src/bbss/friends.py | 16 ++++++++++++---- src/bbss/lists.py | 6 +++--- src/bbss/site.py | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/bbss/sizes.py | 8 ++++++++ 5 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 src/bbss/site.py diff --git a/src/bbss/__init__.py b/src/bbss/__init__.py index 6edc60d..98e45f5 100644 --- a/src/bbss/__init__.py +++ b/src/bbss/__init__.py @@ -1 +1,2 @@ -__all__ = ["lists", "buttons", "friends"] +__all__ = ["lists", "buttons", "friends", "site", "sizes", "DEFAULT_PATHS"] +DEFAULT_PATHS = ["/BBSS", "/.well-known/BBSS", "/badges", "/buttons", ""] diff --git a/src/bbss/friends.py b/src/bbss/friends.py index cd5e7cf..d0aa884 100644 --- a/src/bbss/friends.py +++ b/src/bbss/friends.py @@ -1,4 +1,6 @@ from .lists import BaseListFile, ListFileEntry, parse_listfile +from .site import Site +from . import DEFAULT_PATHS from dataclasses import dataclass from typing import cast, Optional from collections.abc import Sequence @@ -28,13 +30,19 @@ class FriendListFileEntry(ListFileEntry): self.__setattr__('scheme', m.group('scheme') or None) self.__setattr__('domain', m.group('domain')) self.__setattr__('path', m.group('path').removesuffix('/')) - self.__setattr__('url', (self.scheme if self.scheme else 'https') + '://' + self.domain + (self.path if self.path else '/BBSS') + '/') + self.__setattr__('url', (self.scheme if self.scheme else 'https') + '://' + self.domain + (self.path if self.path is not None else "")) def exists(self) -> bool: - return requests.head(self.url).ok + if self.path is not None: + return requests.head(self.url + "/sizes.txt").ok or requests.head(self.url + "/88x31/list.txt").ok + else: + for default in DEFAULT_PATHS: + if requests.head(default + "sizes.txt").ok or requests.head(default + "/88x31/list.txt").ok: + return True + return False - def get(self) -> requests.Response: - return requests.get(self.url) + def get(self) -> Site: + return Site(self.domain, self.path, scheme = self.scheme) class FriendListFile(BaseListFile[FriendListFileEntry]): def __init__(self, contents: str): diff --git a/src/bbss/lists.py b/src/bbss/lists.py index ba6955a..5bcdddf 100644 --- a/src/bbss/lists.py +++ b/src/bbss/lists.py @@ -6,14 +6,14 @@ import requests T = TypeVar('T') def parse_listfile(contents: str, ctor: Callable[[str, Optional[str]], T]) -> List[T]: acc = [] - comment_acc = "" + comment_acc = [] for line in contents.splitlines(): if line.startswith("#"): continue elif line.startswith("##"): - comment_acc += line[2:].strip() + comment_acc.append(line[2:].strip()) else: - acc.append(ctor(line, comment_acc if comment_acc else None)) + acc.append(ctor(line, " ".join(comment_acc) if len(comment_acc) else None)) return acc @dataclass(frozen=True) diff --git a/src/bbss/site.py b/src/bbss/site.py new file mode 100644 index 0000000..7e8871f --- /dev/null +++ b/src/bbss/site.py @@ -0,0 +1,51 @@ +from .sizes import SizeListFile +from .friends import FriendListFile +from . import DEFAULT_PATHS +from typing import Optional +from collections.abc import Sequence +import requests + +class Site: + def __init__(self, domain: str, path: Optional[str], *, scheme: Optional[str]): + base = (scheme if scheme is not None else "https") + "://" + domain + + hit_sizefile = False + + def check_sizefile(path: str) -> bool: + return requests.head(base + path + "/sizes.txt").ok + def check_defaultlist(path: str) -> bool: + return requests.head(base + path + "/88x31/list.txt").ok + + def check_path(path: str): + if check_sizefile(path): + hit_sizefile = True + return True + elif check_defaultlist(path): + return True + + if path is not None: + if not check_path(path): + raise Exception("Oh noes there's nothing.") + else: + self.using_default = False + else: + for potential in DEFAULT_PATHS: + if check_path(potential): + path = potential + self.using_default = True + break + else: + raise Exception("Oh noes there's nothing(no path specified).") + + self.root = base + path + + if hit_sizefile: + self.has_sizes_txt = True + self.sizes = SizeListFile.from_url(self.root + "/sizes.txt") + else: + self.has_sizes_txt = False + self.sizes = SizeListFile("88x31", self.root) + + self.friends = FriendListFile.from_url(self.root + "/friends.txt") + +__all__ = ["Site"] diff --git a/src/bbss/sizes.py b/src/bbss/sizes.py index 26818e9..0451458 100644 --- a/src/bbss/sizes.py +++ b/src/bbss/sizes.py @@ -27,6 +27,14 @@ class SizeListFile(BaseListFile[SizeListFileEntry]): return SizeListFileEntry(entry, comment, root) self._entries = parse_listfile(contents, construct_entry) + for entry in self._entries: + if entry.entry == "88x31": + break + else: + default = SizeListFileEntry("88x31", None, root) + if default.exists(): + self._entries.append(default) + @classmethod def from_url(cls, url: str) -> Optional[Self]: (root, _, _) = url.rpartition('/') -- cgit v1.2.3-54-g00ecf