/ blottertrax / applications / submissions.py
submissions.py
  1  import traceback
  2  from multiprocessing import Lock
  3  
  4  from praw import Reddit
  5  
  6  from blottertrax.config import Config
  7  from blottertrax.database import Database
  8  from blottertrax.description_provider import DescriptionProvider
  9  from blottertrax.exceptions.description_exception import DescriptionException
 10  from blottertrax.helper import templates
 11  from blottertrax.helper.excluded_artists import ExcludedArtists
 12  from blottertrax.helper.self_promo_detector import SelfPromoDetector
 13  from blottertrax.helper.title_parser import TitleParser
 14  from blottertrax.logger import Logger
 15  from blottertrax.services.lastfm import LastFM
 16  from blottertrax.services.overall_threshold_service import OverallThresholdService
 17  from blottertrax.services.soundcloud import Soundcloud
 18  from blottertrax.services.youtube import Youtube
 19  
 20  
 21  class Submissions:
 22      threshold_services: list = []
 23  
 24      def __init__(self, lock: Lock):
 25          try:
 26              self.config = Config()
 27              self.logger = Logger()
 28              self.database = Database(lock)
 29              self.description_provider = DescriptionProvider()
 30              self.threshold_services = [Youtube(), Soundcloud(), LastFM()]
 31  
 32              self.reddit = Reddit(client_id=self.config.REDDIT.CLIENT_ID, client_secret=self.config.REDDIT.CLIENT_SECRET,
 33                                   password=self.config.REDDIT.PASSWORD, user_agent=self.config.REDDIT.USER_AGENT,
 34                                   username=self.config.REDDIT.USER_NAME)
 35  
 36          except KeyError:
 37              self.logger.exception('Check if the configuration is set right')
 38              exit()
 39  
 40      def run(self):
 41          for submission in self._get_submissions():
 42              parsed_submission = TitleParser.create_parsed_submission_from_submission(submission)
 43  
 44              if ExcludedArtists.is_excluded(parsed_submission):
 45                  continue
 46  
 47              if SelfPromoDetector.is_self_promo(parsed_submission, submission):
 48                  reply = templates.self_promotion.format(submission.author.name)
 49                  submission.mod.remove(False, mod_note="Self promotion")
 50                  self._reply_with_sticky_post(submission, reply)
 51                  continue
 52  
 53              exceeds_threshold = self._do_service_checks(parsed_submission, submission)
 54  
 55              try:
 56                  if exceeds_threshold is False and parsed_submission.success is True:
 57                      # Yeey this post probably isn't breaking the rules 🌈
 58                      self._reply_with_sticky_post(submission, self.description_provider.get_reply(parsed_submission))
 59              except DescriptionException:
 60                  # Can't find recording on Musicbrainz, skipping
 61                  self.logger.error(f"Can't find musicbrainz info for {submission.permalink}")
 62                  pass
 63  
 64      def _do_service_checks(self, parsed_submission, submission) -> bool:
 65          """
 66          Loop through all services and check them to determine if we need to remove the post for exceeding thresholds.
 67          """
 68          exceeds_threshold = False
 69          overall_threshold_service = OverallThresholdService()
 70  
 71          for service in self.threshold_services:
 72              try:
 73                  # Some services can run without a successful parsed submission and just need the url
 74                  # If this is not the case, lets skip it now.
 75                  if service.requires_fully_parsed_submission() and not parsed_submission.success:
 76                      continue
 77  
 78                  result = service.get_service_result(parsed_submission)
 79  
 80                  if result.exceeds_threshold is True:
 81                      self._perform_exceeds_threshold_mod_action(submission, result)
 82                      exceeds_threshold = True
 83                      break
 84                  else:
 85                      overall_threshold_service.add_service_result(result)
 86  
 87              except Exception:
 88                  # Go ahead and continue execution, don't want to fail completely just because one service failed.
 89                  self.logger.exception(f"Getting {type(service).__name__} failed")
 90                  self.database.log_error_causing_submission(parsed_submission, submission, traceback.format_exc())
 91  
 92          # TODO: Enable this when new rules roll out.
 93          # # If none of the other checkers exceeded the threshold, check all the combined listeners.
 94          # if not exceeds_threshold:
 95          #     result = overall_threshold_service.get_service_result()
 96          #     if result.exceeds_threshold:
 97          #         self._perform_exceeds_threshold_mod_action(submission, result)
 98  
 99          return exceeds_threshold
100  
101      def _get_submissions(self):
102          for submission in self.reddit.subreddit(self.config.REDDIT.SUBREDDIT).stream.submissions():
103              if submission is None:
104                  return None
105  
106              if self.database.known_submission(submission) is True:
107                  continue
108  
109              self.logger.info(f"Handling {submission.title} - http://reddit.com{submission.permalink}")
110  
111              # Always save submissions, in case of errors log it and continue
112              self.database.save_submission(submission)
113  
114              yield submission
115  
116      def _perform_exceeds_threshold_mod_action(self, submission, service):
117          if self.config.REDDIT.REMOVE_SUBMISSIONS_ON_ERROR is True:
118              self._remove_submission_exceeding_threshold(submission, service)
119          else:
120              submission.report(templates.mod_note_exceeding_threshold.format(service.service_name, service.threshold,
121                                                                              service.listeners_count))
122  
123      def _remove_submission_exceeding_threshold(self, submission, service):
124          reply = templates.submission_exceeding_threshold.format(
125              submission.author.name, service.service_name, service.threshold, service.listeners_count, submission.id
126          )
127          # This is *theoretically* supposed to add a modnote to the removal reason so mods know why.
128          # Currently not working?
129          mod_message = templates.mod_note_exceeding_threshold.format(service.service_name, service.threshold,
130                                                                      service.listeners_count)
131          submission.mod.remove(False, mod_note=mod_message)
132          self._reply_with_sticky_post(submission, reply)
133  
134      @staticmethod
135      def _reply_with_sticky_post(submission, reply_text):
136          comment = submission.reply(reply_text)
137          comment.mod.distinguish("yes", sticky=True)