/ blottertrax / config.py
config.py
  1  import configparser
  2  import os
  3  
  4  
  5  class _ConfigBase:
  6      # We don't want to accidentally add anything to this config.
  7      # This allows us to throw and error in the __new__ call above if a programmer makes a typo.
  8      # For instance, using cls._instance.YOUTUBE_CLIENT_SECRET rather than cls._instance.YOUTUBE.CLIENT_SECRET
  9      def __setattr__(self, key, value):
 10          if hasattr(self, key):
 11              object.__setattr__(self, key, value)
 12          else:
 13              raise TypeError(
 14                  "{} is a frozen class.  You cannot dynamically add properties.  Property attempted: {}, value: {}".format(
 15                      self, key, value))
 16  
 17  
 18  class Config(_ConfigBase):
 19      _instance = None
 20  
 21      # Note: You must specify a default value.  The value of None is fine.
 22      class _REDDIT(_ConfigBase):
 23          CLIENT_ID: str = None
 24          CLIENT_SECRET: str = None
 25          USER_NAME: str = None
 26          PASSWORD: str = None
 27  
 28          SUBREDDIT: str = None
 29          REMOVE_SUBMISSIONS_ON_ERROR: bool = False
 30          MINIMUM_ACCOUNT_AGE = 200
 31          MINIMUM_COMMENT_KARMA = 50
 32          USER_AGENT = "BlotterTrax /r/listentothis submission bot"
 33  
 34      class _YOUTUBE(_ConfigBase):
 35          KEY: str = None
 36          THRESHOLD: int = 1_000_000
 37  
 38      class _LASTFM(_ConfigBase):
 39          KEY: str = None
 40          SECRET: str = None
 41          USERNAME: str = None
 42          PASSWORD: str = None
 43          LISTENER_THRESHOLD: int = 500_000
 44          SCROBBLE_THRESHOLD: int = 4_000_000
 45  
 46      class _SOUNDCLOUD(_ConfigBase):
 47          KEY: str = None
 48          THRESHOLD: int = 1_000_000
 49  
 50      class _MUSICBRAINZ(_ConfigBase):
 51          USER: str = None
 52          PASSWORD: str = None
 53  
 54      class _APP(_ConfigBase):
 55          OVERALL_LISTEN_THRESHOLD: int = 2_000_000
 56  
 57      REDDIT: _REDDIT = _REDDIT()
 58      YOUTUBE: _YOUTUBE = _YOUTUBE()
 59      LASTFM: _LASTFM = _LASTFM()
 60      SOUNDCLOUD: _SOUNDCLOUD = _SOUNDCLOUD()
 61      MUSICBRAINZ: _MUSICBRAINZ = _MUSICBRAINZ()
 62      APP: _APP = _APP()
 63  
 64      # Ensure singleton so we aren't reading the config over and over for each service using it.
 65      def __new__(cls):
 66          if cls._instance is None:
 67              cls._instance = super(Config, cls).__new__(cls)
 68              config = configparser.ConfigParser(strict=False, interpolation=None)
 69  
 70              try:
 71                  config.read('{}/../conf/config.ini'.format(os.path.dirname(os.path.realpath(__file__))))
 72                  cls._instance.REDDIT.CLIENT_ID = config.get('REDDIT', 'CLIENT_ID')
 73                  cls._instance.REDDIT.CLIENT_SECRET = config.get('REDDIT', 'CLIENT_SECRET')
 74                  cls._instance.REDDIT.PASSWORD = config.get('REDDIT', 'PASSWORD')
 75                  cls._instance.REDDIT.USER_NAME = config.get('REDDIT', 'USER_NAME')
 76                  cls._instance.REDDIT.SUBREDDIT = config.get('REDDIT', 'SUBREDDIT')
 77  
 78                  cls._instance.REDDIT.MINIMUM_ACCOUNT_AGE = config.getint('REDDIT', 'MINIMUM_ACCOUNT_AGE')
 79                  cls._instance.REDDIT.MINIMUM_COMMENT_KARMA = config.getint('REDDIT', 'MINIMUM_COMMENT_KARMA')
 80                  cls._instance.REDDIT.USER_AGENT = config.get('REDDIT', 'USER_AGENT')
 81  
 82                  cls._instance.REDDIT.REMOVE_SUBMISSIONS_ON_ERROR = config.getboolean('REDDIT', 'REMOVE_SUBMISSIONS')
 83  
 84                  cls._instance.YOUTUBE.KEY = config.get('YOUTUBE', 'KEY')
 85  
 86                  cls._instance.LASTFM.KEY = config.get('LASTFM', 'KEY')
 87                  cls._instance.LASTFM.SECRET = config.get('LASTFM', 'SECRET')
 88                  cls._instance.LASTFM.USERNAME = config.get('LASTFM', 'USERNAME')
 89                  cls._instance.LASTFM.PASSWORD = config.get('LASTFM', 'PASSWORD')
 90  
 91                  cls._instance.LASTFM.LISTENER_THRESHOLD = config.getint('LASTFM', 'LISTENER_THRESHOLD',
 92                                                                          fallback=500_000)
 93                  cls._instance.LASTFM.SCROBBLE_THRESHOLD = config.getint('LASTFM', 'SCROBBLE_THRESHOLD',
 94                                                                          fallback=4_000_000)
 95  
 96                  cls._instance.SOUNDCLOUD.KEY = config.get('SOUNDCLOUD', 'KEY')
 97                  cls._instance.SOUNDCLOUD.THRESHOLD = config.getint('SOUNDCLOUD', 'THRESHOLD', fallback=1_000_000)
 98  
 99                  cls._instance.MUSICBRAINZ.USER = config.get('MUSICBRAINZ', 'USER')
100                  cls._instance.MUSICBRAINZ.PASSWORD = config.get('MUSICBRAINZ', 'PASSWORD')
101  
102                  cls._instance.APP.OVERALL_LISTEN_THRESHOLD = config.getint('APP',
103                                                                             'OVERALL_LISTEN_THRESHOLD',
104                                                                             fallback=3_000_000)
105  
106              except TypeError as e:
107                  print(e)
108                  exit('Additional field detected on config class.  Please add this in the proper way.')
109              except Exception:
110                  exit("Please make sure conf/config.ini is set")
111  
112          return cls._instance
113  
114      def is_config_valid(self) -> bool:
115          return True