diff --git a/publish_meetups/__init__.py b/publish_meetups/__init__.py index 0462fcc..8585402 100644 --- a/publish_meetups/__init__.py +++ b/publish_meetups/__init__.py @@ -4,73 +4,4 @@ import logging import toml -from .utils import Paths, PROGRAM_NAME -from .feeds.mastodon_feed import MastodonFeed -from .feeds.twitter_feed import TwitterFeed - - - -NAME_TO_FEED = { - "mastodon": MastodonFeed, - "twitter": TwitterFeed, -} - - -class PublishMeetups: - def __init__(self): - self.config_file: Path = Path(Paths.CONFIG_PATH, "publish-meetups.toml") - print(self.config_file) - self.config = {} - - self.config = { - "active_feeds": [ - "mastodon", - "twitter", - ], - "mastodon": {}, - "twitter": {}, - } - - self.logger = logging.getLogger(__name__) - - def __enter__(self): - if self.config_file.exists(): - with self.config_file.open("r", encoding="utf-8") as f: - self.config.update(toml.load(f)) - - return self - - def run(self): - for feed in self.config['active_feeds']: - self.run_feed(feed) - - def run_feed(self, feed: str): - if feed not in NAME_TO_FEED: - self.logger.error(f"Feed {feed} is not implemented.") - return - - - feed_config = self.config.get(feed, {}) - feed_class = NAME_TO_FEED[feed] - - if not len(feed_config): - feed_config.update(feed_class.prompt_auth(feed_config)) - self.config[feed] = feed_config - - with self.config_file.open("w", encoding="utf-8") as f: - toml.dump(self.config, f) - - - with feed_class(**feed_config, config=feed_config) as feed_instance: - self.config[feed].update(feed_instance.config) - - with self.config_file.open("w", encoding="utf-8") as f: - toml.dump(self.config, f) - - feed_instance.run() - - - def __exit__(self, exc_type, exc_val, exc_tb): - print("Exiting") - with self.config_file.open("w", encoding="utf-8") as f: - toml.dump(self.config, f) +from .utils import config diff --git a/publish_meetups/__main__.py b/publish_meetups/__main__.py index e212366..6bd1127 100644 --- a/publish_meetups/__main__.py +++ b/publish_meetups/__main__.py @@ -1,9 +1,8 @@ -from . import PublishMeetups +from .routines.development import Development def cli(): - with PublishMeetups() as p: - p.run() + Development().run() if __name__ == "__main__": diff --git a/publish_meetups/feeds/__init__.py b/publish_meetups/feeds/__init__.py index a5c9c74..144b9c3 100644 --- a/publish_meetups/feeds/__init__.py +++ b/publish_meetups/feeds/__init__.py @@ -1,23 +1,11 @@ import logging +from .interface import Feed +from .mastodon_feed import MastodonFeed +from .twitter_feed import TwitterFeed -class Feed: - @classmethod - def prompt_auth(cls, existing_config: dict) -> dict: - return existing_config - def __init__(self, config: dict = None, **kwargs): - self.logger = logging.getLogger(self.__class__.__name__) - self.config = config or {} - def __enter__(self): - return self - def post(self, message: str, **kwargs): - print(f"Posting {message}") - def run(self): - self.post("Hello, World!") - - def __exit__(self, exc_type, exc_value, traceback): - pass +__all__ = ["Feed", "MastodonFeed", "TwitterFeed"] diff --git a/publish_meetups/feeds/interface.py b/publish_meetups/feeds/interface.py new file mode 100644 index 0000000..b217624 --- /dev/null +++ b/publish_meetups/feeds/interface.py @@ -0,0 +1,17 @@ +import logging + + +class Feed: + __config_name__ = "feed" + + @classmethod + def prompt_auth(cls, existing_config: dict) -> dict: + return existing_config + + def __init__(self): + self.logger = logging.getLogger(self.__class__.__name__) + + + def post(self, message: str): + self.logger.info(f"Posting message to {self.__class__.__name__}: {message}") + pass \ No newline at end of file diff --git a/publish_meetups/feeds/mastodon_feed.py b/publish_meetups/feeds/mastodon_feed.py index 0ac044e..1bea007 100644 --- a/publish_meetups/feeds/mastodon_feed.py +++ b/publish_meetups/feeds/mastodon_feed.py @@ -3,14 +3,15 @@ from pathlib import Path from mastodon import Mastodon from . import Feed -from ..utils import Paths, PROGRAM_NAME, prompt +from ..utils import PROGRAM_NAME, prompt, config class MastodonFeed(Feed): - CLIENTCRED_PATH: Path = Paths.CONFIG_PATH.joinpath("mastodon_clientcred.secret") + __config_name__ = "mastodon" + CLIENTCRED_PATH: Path = config.CONFIG_PATH.joinpath("mastodon_clientcred.secret") @classmethod - def prompt_auth(cls, existing_config: dict) -> dict: + def prompt_auth(cls) -> dict: """ mastodon needs: - the instance used @@ -18,7 +19,6 @@ class MastodonFeed(Feed): """ return { - **existing_config, "api_base_url": prompt.for_string("The instance you use", "https://mastodon.social"), "access_token": prompt.for_password("Access token"), } @@ -30,11 +30,9 @@ class MastodonFeed(Feed): access_token=access_token, ) - super().__init__(**kwargs) + super().__init__() def post(self, message: str): - kwargs = locals().copy() - self.mastodon.toot(message) - Feed.post(**kwargs) + super().post(message) diff --git a/publish_meetups/feeds/twitter_feed.py b/publish_meetups/feeds/twitter_feed.py index 0189aea..36a286b 100644 --- a/publish_meetups/feeds/twitter_feed.py +++ b/publish_meetups/feeds/twitter_feed.py @@ -4,14 +4,14 @@ from twikit import Client from twikit.errors import Forbidden, Unauthorized from . import Feed -from ..utils import Paths, PROGRAM_NAME, prompt +from ..utils import config, PROGRAM_NAME, prompt class TwitterFeed(Feed): - CLIENTCRED_PATH: Path = Paths.CONFIG_PATH.joinpath("mastodon_clientcred.secret") + __config_name__ = "twitter" @classmethod - def prompt_auth(cls, existing_config: dict) -> dict: + def prompt_auth(cls) -> dict: """ client.login( auth_info_1=USERNAME , @@ -28,13 +28,12 @@ class TwitterFeed(Feed): # https://github.com/d60/twikit def __init__(self, auth_info_1: str, auth_info_2: str, password: str, cookies: dict = None, **kwargs): - super().__init__(**kwargs) + super().__init__() self.client = Client('en-US') logged_in = False - cookies = cookies or {} if cookies is not None: self.client.http.client.cookies = cookies try: @@ -53,16 +52,14 @@ class TwitterFeed(Feed): ) logged_in = True - self.config['cookies'] = dict(self.client.http.client.cookies) - print(self.client.user_id()) - print(self.client._base_headers) - + config.set_field(self.__config_name__, { + 'cookies': dict(self.client.http.client.cookies) + }, update_dict=True) def post(self, message: str): - kwargs = locals().copy() self.client.create_tweet( text=message, ) - Feed.post(**kwargs) + super().post(message) diff --git a/publish_meetups/routines/__init__.py b/publish_meetups/routines/__init__.py new file mode 100644 index 0000000..7e41cd9 --- /dev/null +++ b/publish_meetups/routines/__init__.py @@ -0,0 +1,41 @@ +from typing import Generator, Type, Dict + +from ..utils import config, error +from ..feeds import * + + +NAME_TO_FEED: Dict[str, Type[Feed]] = { + "mastodon": MastodonFeed, + "twitter": TwitterFeed, +} + + +class Routine: + @staticmethod + def iter_feeds() -> Generator[Type[Feed], None, None]: + for feed in config.get_field("active_feeds", []): + if feed not in NAME_TO_FEED: + raise ValueError(f"Feed {feed} is not implemented.") + + yield NAME_TO_FEED[feed] + + @staticmethod + def init_feed(feed: Type[Feed]) -> Feed: + feed_config = config.get_field(feed.__config_name__, {}) + + try: + feed_instance = feed(**feed_config) + except (TypeError, error.InvalidCredential) as e: + print(feed_config) + raise e + config.set_field(feed.__name__, feed.prompt_auth(), update_dict=True) + feed_config = config.get_field(feed.__config_name__, {}) + + try: + return feed(**feed_config) + except (TypeError, error.InvalidCredential): + raise error.InvalidCredential(f"Invalid credentials for {feed.__name__}.") + + + def run(self): + pass diff --git a/publish_meetups/routines/development.py b/publish_meetups/routines/development.py new file mode 100644 index 0000000..9eaeb30 --- /dev/null +++ b/publish_meetups/routines/development.py @@ -0,0 +1,10 @@ +from . import Routine +from ..feeds import Feed + + +class Development(Routine): + def run(self): + for feed_class in self.iter_feeds(): + feed: Feed = self.init_feed(feed_class) + + feed.post(message="This worked second try!") diff --git a/publish_meetups/utils/__init__.py b/publish_meetups/utils/__init__.py index 285a1c7..2c3f772 100644 --- a/publish_meetups/utils/__init__.py +++ b/publish_meetups/utils/__init__.py @@ -1,19 +1,9 @@ -from pathlib import Path +import logging -import platformdirs + +logging.basicConfig(level=logging.INFO) PROGRAM_NAME: str = "publish-meetups" - -class Paths: - CONFIG_PATH: Path = Path(platformdirs.user_config_path(appname=PROGRAM_NAME)) - - -Paths.CONFIG_PATH.mkdir(parents=True, exist_ok=True) - - - -__all__ = ["prompt", "CONFIG_PATH", "PROGRAM_NAME", "errors"] - - +__all__ = ["prompt", "CONFIG_PATH", "PROGRAM_NAME", "errors", "config"] diff --git a/publish_meetups/utils/config.py b/publish_meetups/utils/config.py new file mode 100644 index 0000000..d05bff1 --- /dev/null +++ b/publish_meetups/utils/config.py @@ -0,0 +1,57 @@ +import logging + +import toml +import platformdirs + +from . import PROGRAM_NAME + +logger = logging.getLogger("config") +_config: dict = { + "active_feeds": [ + "mastodon", + "twitter", + ], + "mastodon": {}, + "twitter": {}, + "lemmy": {}, +} + + +CONFIG_PATH = platformdirs.user_config_path(appname=PROGRAM_NAME) +CONFIG_PATH.mkdir(parents=True, exist_ok=True) +CONFIG_FILE = CONFIG_PATH / f"{PROGRAM_NAME}.toml" + + +logger.info(f"Config file: {CONFIG_FILE}") + + +def read(): + global _config, CONFIG_FILE + if CONFIG_FILE.exists(): + with CONFIG_FILE.open("r", encoding="utf-8") as f: + _config.update(toml.load(f)) + + +def write(): + global _config, CONFIG_FILE + with CONFIG_FILE.open("w", encoding="utf-8") as f: + toml.dump(_config, f) + + +def get_field(__name: str, default=None): + global _config + return _config.get(__name, default) + + +def set_field(__name: str, __value, update_dict=False): + global _config + + if update_dict and isinstance(__value, dict) and isinstance(_config.get(__name), dict): + _config[__name].update(__value) + else: + _config[__name] = __value + + write() + + +read()