''' Command line module for calling tootapalooza to do its work ''' __all__ = ['tootapalooza'] __author__ = 'Paco Hope ' __date__ = '25 November 2022' __version__ = '1.0' __copyright__ = 'Copyright © 2022 Paco Hope. See LICENSE for details.' from mastodon import Mastodon import toml import os import time import argparse import sys from pathlib import Path import random import re args=None class Tooter(Mastodon): credentials: dict = {} hostname : str = '' files : dict = {} client_id : str = '.tootapalooza.env' def __init__(self, name: str): cred_dict = self.credentials[name] self.name = name self.username = cred_dict['addr'] self.password = cred_dict['pass'] self.displayname = cred_dict['name'] self.cred_file = f'.tootapalooza-usercred-{self.name}.env' super().__init__(client_id=self.client_id) self.log_in( self.username, self.password, to_file=self.cred_file ) @classmethod def load_credentials(cls, file: str) -> None: as_dict = toml.load(file) for username, fields in as_dict.items(): if not isinstance(fields, dict): raise TypeError(f'{username} has no key/value pairs') if 'addr' not in fields: raise KeyError(f'`addr` field missing from {username}') if 'pass' not in fields: raise KeyError(f'`pass` field missing from {username}') cls.credentials = as_dict @classmethod def load_src_files(cls, dir: Path) -> None: for item in dir.iterdir(): if not item.is_file(): continue with item.open('r') as f: cls.files[f.name] = f.readlines() def tagged_public_toot(self): message = self.new_message() if( args.dry_run ): print(f"tagged toot message: \"{message}\"") else: if( args.debug ): print(f"{self.name} tagged toots \"{message}\"") self.toot(message) return 0 def plain_public_toot(self): message = self.new_message() if( args.dry_run ): print(f"toot message: \"{message}\"") else: if( args.debug ): print(f"{self.name} toots \"{message}\"") self.toot(message) return 0 @classmethod def new_message(cls): sourcefile = random.choice(list(cls.files.values())) startline = random.randint(0,len(sourcefile)) sourceline = '' i=0 # Starting at a random line, keep adding more lines to my # toot until I get over 400 characters (max is 500 on most # mastodon servers) while(len(sourceline) < 400 and startline+i < len(sourcefile)): sourceline=sourceline+sourcefile[startline+i] i+=1 # Try to find a 400-odd character string that ends in a full stop tootline = re.search( '((\s|\S){,400})\.', sourceline ) if( tootline ): message=tootline.group(0).strip() else: message=sourceline.strip() return(message) def random_interaction(self): """Choose one possible interaction according to the weights, and do it.""" interactions = [ self.plain_public_toot, self.tagged_public_toot ] weights = [1, 1] chosen = random.choices(population=interactions, weights=weights)[0] chosen() def daemon_main(tooter: Tooter): """Run from a command line.""" while True: # do a thing time.sleep(600) def main(): global args parser = argparse.ArgumentParser( description='Randomly interact with a Mastodon timeline.') parser.add_argument( '-d', '--debug', action='store_true', help='Enable debugging messages.') parser.add_argument( '-o', '--once', action='store_true', help='Run once and exit. Default is to run as a daemon.') parser.add_argument( '-n', '--dry-run', action='store_true', help='Don\'t toot. Just show what would be done.') parser.add_argument( '-D', '--directory', default='text', help='Directory with text source files for toot bodies.') parser.add_argument( 'file', type=argparse.FileType('r'), help='TOML file with user credentials (see server-util/README.md).') args = parser.parse_args() p = Path(args.directory) if not p.exists(): print(f'{sys.argv[0]}: {args.directory}: No such file or directory', file=sys.stderr) return 2 if not p.is_dir(): print(f'{sys.argv[0]}: {args.directory}: Is not a directory', file=sys.stderr) return 2 Tooter.load_src_files(p) Tooter.load_credentials(args.file) if args.once: for name in Tooter.credentials: t = Tooter(name) t.random_interaction() return 0 daemon_main(t) if __name__ == '__main__': sys.exit(main())