Example #1
0
File: tests.py Project: logan/praw
 def test_mark_as_read(self):
     oth = Reddit(USER_AGENT)
     oth.login('PyApiTestUser3', '1111')
     # pylint: disable-msg=E1101
     msg = six_next(oth.user.get_unread(limit=1))
     msg.mark_as_read()
     self.assertTrue(msg not in oth.user.get_unread(limit=5))
Example #2
0
def reddit_parser():
    args = build_reddit_parser().parse_args()
    db_connection = create_connection(args.db_host, args.db_password)
    reddit = Reddit(user_agent=args.user_agent)
    subreddit = reddit.get_subreddit(args.subreddit)
    if args.username:
        password = getpass("Enter reddit password: ")
        reddit.login(args.username, password)
    single_threaded_impl(subreddit, db_connection, args.sub_limit, args.method)
Example #3
0
	def update_reddit(self):
		#Initialize PRAW and login
		r = Reddit(user_agent='/r/reddevils bot by /u/Hubwub')
		r.login(self.username,self.password)
		#Grab the current settings
		settings = r.get_subreddit(self.subreddit).get_settings()
		#Update the sidebar
		#print sidebar
		settings['description'] = sidebar
		settings = r.get_subreddit(self.subreddit).update_settings(description=settings['description'])
Example #4
0
class Snitch:
    def __init__(self, username, passwd, url, subreddit):
        self.rh = Reddit('Release the Snitch v 0.1 by Kolpa')
        self.rh.login(username, passwd)

        self.css = self.__load_css()
        self.desc = self.__load_desc()

        self.url = url
        self.subreddit = subreddit

    def __load_css(self):
        with open('raw.css', 'r') as f:
            return f.read()

    def __load_desc(self):
        with open('descripiton.txt', 'r') as f:
            return f.read()

    def __get_last_id(self):
        with open('current', 'r') as f:
            return f.read()

    def __set_new_id(self):
        id = randrange(0, 999999)
        with open('current', 'w') as f:
            f.write(str(id))
        return id

    def _get_random_pos(self):
        return randrange(0, 100, 10), randrange(0, 100, 10)

    def __update_desc(self, desc):
        self.rh.update_settings(self.rh.get_subreddit(self.subreddit), description=desc)

    def can_move(self, id):
        if not os.path.exists('current'):
            return True
        return id == self.__get_last_id()

    def move(self, id):
        try:
            if self.can_move(id):
                new_id = self.__set_new_id()
                desc = self.desc.format(new_id)

                x, y = self._get_random_pos()
                css = self.css.format(x, y, self.url)

                self.rh.set_stylesheet(self.subreddit, css)
                self.__update_desc(desc)
                return True
        except Exception:
            return False
Example #5
0
File: tests.py Project: logan/praw
 def test_mark_as_unread(self):
     oth = Reddit(USER_AGENT)
     oth.login('PyApiTestUser3', '1111')
     found = None
     for msg in oth.user.get_inbox():
         if not msg.new:
             found = msg
             msg.mark_as_unread()
             break
     else:
         self.fail('Could not find a read message.')
     self.assertTrue(found in oth.user.get_unread())
Example #6
0
File: tests.py Project: logan/praw
 def test_mark_multiple_as_read(self):
     oth = Reddit(USER_AGENT)
     oth.login('PyApiTestUser3', '1111')
     messages = []
     for msg in oth.user.get_unread(limit=None):
         if msg.author != oth.user.name:
             messages.append(msg)
             if len(messages) >= 2:
                 return
     self.assertEqual(2, len(messages))
     self.r.user.mark_as_read(messages)
     unread = oth.user.get_unread(limit=5)
     for msg in messages:
         self.assertTrue(msg not in unread)
Example #7
0
def main():
    parser = argparse.ArgumentParser(
            prog='modbot.py',
            description="Subreddit automoderating script")
    parser.add_argument('subreddit')
    parser.add_argument('-r', '--rulesdir', default='./rules')
    parser.add_argument('-u', '--user')
    parser.add_argument('-p', '--password')
    args = parser.parse_args()

    rh = RuleHandler(args.rulesdir, '*.rule')

    logging.config.fileConfig('logging.conf')

    reddit = Reddit('%s/%s' % (NAME, VERSION))
    try:
        reddit.login(args.user, args.password)
    except praw.errors.InvalidUserPass:
        logging.critical("Login failure")
        sys.exit(1)

    sub = reddit.get_subreddit(args.subreddit)
    read_thinglists()
    comments_ph = None
    submissions_ph = None

    while True:
        logging.debug("Loop start")
        loopstart = time.time()

        try:
            modqueue_items = sub.get_mod_queue(limit=100)
        except Exception, e:
            modqueue_items = []

        num = 0
        for modqueue_item in modqueue_items:
            num += 1
            matchrules(modqueue_item, rh.rules, is_modqueue=True)
        logging.info("Checked %d modqueue items" % num)

        try:
            if comments_ph == None:
                comments = sub.get_comments(limit=100)
            else:
                comments = sub.get_comments(place_holder=comments_ph,
                        limit=500)
        except Exception, e:
            comments = []
Example #8
0
def main():
    parser = argparse.ArgumentParser(
        prog='modbot.py', description="Subreddit automoderating script")
    parser.add_argument('subreddit')
    parser.add_argument('-r', '--rulesdir', default='./rules')
    parser.add_argument('-u', '--user')
    parser.add_argument('-p', '--password')
    args = parser.parse_args()

    rh = RuleHandler(args.rulesdir, '*.rule')

    logging.config.fileConfig('logging.conf')

    reddit = Reddit('%s/%s' % (NAME, VERSION))
    try:
        reddit.login(args.user, args.password)
    except praw.errors.InvalidUserPass:
        logging.critical("Login failure")
        sys.exit(1)

    sub = reddit.get_subreddit(args.subreddit)
    read_thinglists()
    comments_ph = None
    submissions_ph = None

    while True:
        logging.debug("Loop start")
        loopstart = time.time()

        try:
            modqueue_items = sub.get_mod_queue(limit=100)
        except Exception, e:
            modqueue_items = []

        num = 0
        for modqueue_item in modqueue_items:
            num += 1
            matchrules(modqueue_item, rh.rules, is_modqueue=True)
        logging.info("Checked %d modqueue items" % num)

        try:
            if comments_ph == None:
                comments = sub.get_comments(limit=100)
            else:
                comments = sub.get_comments(place_holder=comments_ph,
                                            limit=500)
        except Exception, e:
            comments = []
Example #9
0
def update_countdown(username, password, subreddit_name, target):
    user_agent = '/r/{0} countdown'.format(subreddit_name)
    reddit = Reddit(user_agent)
    reddit.login(username, password, disable_warning=True)

    # reddit access
    subreddit = reddit.get_subreddit(subreddit_name)
    settings = subreddit.get_settings()
    description = HTMLParser().unescape(settings['description'])

    # time matches
    countdown_delta = target - datetime.now()
    days_left = countdown_delta.days
    hours_left = countdown_delta.seconds // 3600

    # regex and strings
    # countdownSearch = re.compile("(\[\d?\d?\]\([#][a-z]*\)\s[a-z]*[!]?\s?)+", re.I)  # old regex
    countdownSearch = re.compile("((\s([a-z]{4})*)*\s?\[\d?\d?\]\([#][a-z]*\)\s[a-z]*[!]?\s?)+", re.I)
    origStr = " less than [](#days) days [](#hours) hours\n"
    noDaysStr = " less than [](#hours) hours\n"
    noHoursStr = " less than [](#minutes) minutes\n"
    raceLiveStr = " [](#days) Racing [](#hours) is [](#minutes) live\n"
    updatemeStr = " [](#days) Current [](#hours) event [](#minutes) ended\n"

    # make sure our event hasn't happened yet
    if target > datetime.now():

        if days_left > 0:
            description = re.sub(countdownSearch, origStr, description)
        elif hours_left > 0 and days_left == 0:
            description = re.sub(countdownSearch, noDaysStr, description)
        else:
            description = re.sub(countdownSearch, noHoursStr, description)

        for key, value in compute_time_ago_params(target).iteritems():
            pattern = "\\[[^\\]]*\\]\\(#{0}\\)".format(key)  # replace [<anything>](#<key>)
            repl = "[{0}](#{1})".format(value, key)  # with [<value>](#<key>)
            description = re.sub(pattern, repl, description)

    # if race is happening, show raceLiveStr, else race is over, show updatemeStr
    else:
        countdownSearch.search(description)
        if target.hour > datetime.now().hour - 3:
            description = re.sub(countdownSearch, raceLiveStr, description)
        else:
            description = re.sub(countdownSearch, updatemeStr, description)

    subreddit.update_settings(description=description)
Example #10
0
def update_countdown(username, password, subreddit_name, target):
    user_agent = '/r/{0} countdown bot'.format(subreddit_name)
    reddit = Reddit(user_agent)
    reddit.login(username, password)
    
    subreddit = reddit.get_subreddit(subreddit_name)
    settings = subreddit.get_settings()
    description = HTMLParser().unescape(settings['description'])
    
    for key, value in compute_time_ago_params(target).iteritems():
        pattern = "\\[[^\\]]*\\]\\(#{0}\\)".format(key) # replace [<anything>](#<key>)
        repl = "[{0}](#{1})".format(value, key) # with [<value>](#<key>)
        description = re.sub(pattern, repl, description)
    
    print(description)
    subreddit.update_settings(description=description)
Example #11
0
	def create_sidebar(self):
		h = HTMLParser.HTMLParser()
		#Initialize PRAW and login
		r = Reddit(user_agent='/r/reddevils bot by /u/Hubwub')
		r.login(self.username,self.password)
		#Grab the sidebar template from the wiki
		sidebar = r.get_subreddit(self.subreddit).get_wiki_page('edit_sidebar').content_md
		#Create list from sidebar by splitting at ####
		sidebar_list = sidebar.split('####')
		#Sidebar
		#print sidebar_list
		#sidebar = (sidebar_list[0]+league+sidebar_list[1])
		sidebar = (sidebar_list[0]+league+rfixtures+goals+sidebar_list[1])
		#Fix characters in sidebar
		sidebar = h.unescape(sidebar)
		return sidebar
Example #12
0
def update_countdown(username, password, subreddit_name, target):
    user_agent = '/r/{0} countdown bot'.format(subreddit_name)
    reddit = Reddit(user_agent)
    reddit.login(username, password)

    subreddit = reddit.get_subreddit(subreddit_name)
    settings = subreddit.get_settings()
    description = HTMLParser().unescape(settings['description'])

    for key, value in compute_time_ago_params(target).iteritems():
        pattern = "\\[[^\\]]*\\]\\(#{0}\\)".format(
            key)  # replace [<anything>](#<key>)
        repl = "[{0}](#{1})".format(value, key)  # with [<value>](#<key>)
        description = re.sub(pattern, repl, description)

    print(description)
    subreddit.update_settings(description=description)
Example #13
0
File: tests.py Project: logan/praw
 def test_report(self):
     # login as new user to report submission
     oth = Reddit(USER_AGENT)
     oth.login('PyApiTestUser3', '1111')
     subreddit = oth.get_subreddit(self.sr)
     submission = None
     for submission in subreddit.get_new_by_date():
         if not submission.hidden:
             break
     else:
         self.fail('Could not find a non-reported submission.')
     submission.report()
     # check if submission was reported
     for report in self.r.get_subreddit(self.sr).get_reports():
         if report.id == submission.id:
             break
     else:
         self.fail('Could not find reported submission.')
Example #14
0
def verify(moderator_username, moderator_password,
           username, subreddit,
           flair_text=None, flair_css=None):
    api = Reddit(user_agent=settings.USER_AGENT)
    try:
        api.login(moderator_username, moderator_password)
        subreddit = api.get_subreddit(subreddit)
        subreddit.add_contributor(username)
        api.set_flair(subreddit, username, flair_text, flair_css)
    except praw.errors.RateLimitExceeded:
        raise RateLimitExceededException
    except praw.errors.ModeratorRequired:
        raise ModeratorRequiredException
    except praw.errors.InvalidUserPass:
        raise InvalidLoginException
    except praw.errors.BadCSS:
        raise InvalidCSSException
    except praw.errors.InvalidUser:
        raise InvalidUserException
    except praw.errors.APIException as e:
        raise RedditAPIException(e)
    except praw.errors.ClientException as e:
        raise RedditAPIException(e)
Example #15
0
HOSTRESPONSE = HOSTRESPONSE.format(HOSTLINK, CONTACT)

# Warning when haven't added a details comment
DETAILSWARN = "Please add a {0}.{1}".format(TEMPLATE, CONTACT)

print(SUBREDDIT, "bot\n")


"""BOT LOGIN"""

r = Reddit(USERAGENT)
Trying = True
while Trying:
    try:
        PASSWORD = getpass("Password: "******"Successfully logged in\n")
        Trying = False
    except errors.InvalidUserPass:
        print("Wrong Username or Password\n")
        quit()
    except Exception as e:
        print("%s" % e)
        sleep(5)


"""DEFINING FUNCTIONS"""

def slay(post, response):
    print("\tReplying to OP")
    res = post.add_comment(response)
Example #16
0
 def get_post(self):
     """ Get a post """
     r = Reddit(self.args.name)
     r.login(self.args.username, self.args.password)
     return r.get_subreddit(self.args.subreddit).get_new(limit=1).next()
Example #17
0
#!/usr/bin/env python
from praw import Reddit

import settings as raw_settings
from generators import submissions_gen
from filters import filter_submission
from interaction import repost_submission
from utils import clean_settings


if __name__ == '__main__':
    import sys
    from datetime import datetime

    settings = clean_settings(raw_settings)
    newer_than_id = sys.argv[1] if len(sys.argv) > 1 else None

    # disable internal cache so praw blocks until it's time to make a new request
    bot = Reddit(user_agent=settings['user_agent'], cache_timeout=0)
    bot.login(settings['bot_username'], settings['bot_password'])

    submissions = submissions_gen(bot, settings, newer_than_id)
    submissions = (submission for submission in submissions if filter_submission(submission, settings))

    for submission in submissions:
        print('[{}] {} - {}'.format(datetime.now().isoformat(), submission.created_utc, submission))
        print(repost_submission(submission, bot, settings))

Example #18
0
class SubRedditStats(object):
    post_prefix = tt('Subreddit Stats:')
    post_header = tt('---\n###{0}\n')
    post_footer = tt('>Generated with [BBoe](/u/bboe)\'s [Subreddit Stats]'
                     '(https://github.com/praw-dev/prawtools)  \n{0}'
                     'SRS Marker: {1}')
    re_marker = re.compile('SRS Marker: (\d+)')

    @staticmethod
    def _previous_max(submission):
        try:
            val = SubRedditStats.re_marker.findall(submission.selftext)[-1]
            return float(val)
        except (IndexError, TypeError):
            print('End marker not found in previous submission. Aborting')
            sys.exit(1)

    @staticmethod
    def _permalink(permalink):
        tokens = permalink.split('/')
        if tokens[8] == '':  # submission
            return tt('/comments/{0}/_/').format(tokens[6])
        else:  # comment
            return tt('/comments/{0}/_/{1}?context=1').format(tokens[6],
                                                              tokens[8])

    @staticmethod
    def _user(user):
        if user is None:
            return '_deleted_'
        elif isinstance(user, Redditor):
            user = str(user)
        return tt('[{0}](/user/{1})').format(user.replace('_', '\_'), user)

    @staticmethod
    def _submit(func, *args, **kwargs):
        def sleep(sleep_time):
            print('\tSleeping for {0} seconds'.format(sleep_time))
            time.sleep(sleep_time)

        while True:
            try:
                return func(*args, **kwargs)
            except RateLimitExceeded as error:
                sleep(error.sleep_time)
            except ExceptionList as exception_list:
                for error in exception_list.errors:
                    if isinstance(error, RateLimitExceeded):
                        sleep(error.sleep_time)
                        break
                else:
                    raise

    def __init__(self, subreddit, site, verbosity):
        self.reddit = Reddit(str(self), site)
        self.subreddit = self.reddit.get_subreddit(subreddit)
        self.verbosity = verbosity
        self.submissions = []
        self.comments = []
        self.submitters = defaultdict(list)
        self.commenters = defaultdict(list)
        self.min_date = 0
        self.max_date = time.time() - DAYS_IN_SECONDS * 3
        self.prev_srs = None
        # Config
        self.reddit.config.comment_limit = -1  # Fetch max comments possible
        self.reddit.config.comment_sort = 'top'

    def login(self, user, pswd):
        if self.verbosity > 0:
            print('Logging in')
        self.reddit.login(user, pswd)

    def msg(self, msg, level, overwrite=False):
        if self.verbosity >= level:
            sys.stdout.write(msg)
            if overwrite:
                sys.stdout.write('\r')
                sys.stdout.flush()
            else:
                sys.stdout.write('\n')

    def prev_stat(self, prev_url):
        submission = self.reddit.get_submission(prev_url)
        self.min_date = self._previous_max(submission)
        self.prev_srs = prev_url

    def fetch_recent_submissions(self, max_duration, after, exclude_self,
                                 since_last=True):
        '''Fetches recent submissions in subreddit with boundaries.

        Does not include posts within the last three days as their scores may
        not be representative.

        Keyword arguments:
        max_duration -- When set, specifies the number of days to include
        after -- When set, fetch all submission after this submission id.
        exclude_self -- When true, don't include self posts.
        since_last -- When true use info from last submission to determine the
                      stop point
        '''
        if max_duration:
            self.min_date = self.max_date - DAYS_IN_SECONDS * max_duration
        params = {'after': after} if after else None
        self.msg('DEBUG: Fetching submissions', 1)
        for submission in self.subreddit.get_new_by_date(limit=None,
                                                         params=params):
            if submission.created_utc > self.max_date:
                continue
            if submission.created_utc <= self.min_date:
                break
            if (since_last and str(submission.author) == str(self.reddit.user)
                and submission.title.startswith(self.post_prefix)):
                # Use info in this post to update the min_date
                # And don't include this post
                self.msg(tt('Found previous: {0}')
                         .format(safe_title(submission)), 2)
                if self.prev_srs is None:  # Only use the most recent
                    self.min_date = max(self.min_date,
                                        self._previous_max(submission))
                    self.prev_srs = submission.permalink
                continue
            if exclude_self and submission.is_self:
                continue
            self.submissions.append(submission)
        num_submissions = len(self.submissions)
        self.msg('DEBUG: Found {0} submissions'.format(num_submissions), 1)
        if num_submissions == 0:
            return False

        # Update real min and max dates
        self.submissions.sort(key=lambda x: x.created_utc)
        self.min_date = self.submissions[0].created_utc
        self.max_date = self.submissions[-1].created_utc
        return True

    def fetch_top_submissions(self, top, exclude_self):
        '''Fetches top 1000 submissions by some top value.

        Keyword arguments:
        top -- One of week, month, year, all
        exclude_self -- When true, don't include self posts.
        '''
        if top not in ('day', 'week', 'month', 'year', 'all'):
            raise TypeError('{0!r} is not a valid top value'.format(top))
        self.msg('DEBUG: Fetching submissions', 1)
        params = {'t': top}
        for submission in self.subreddit.get_top(limit=None, params=params):
            if exclude_self and submission.is_self:
                continue
            self.submissions.append(submission)
        num_submissions = len(self.submissions)
        self.msg('DEBUG: Found {0} submissions'.format(num_submissions), 1)
        if num_submissions == 0:
            return False

        # Update real min and max dates
        self.submissions.sort(key=lambda x: x.created_utc)
        self.min_date = self.submissions[0].created_utc
        self.max_date = self.submissions[-1].created_utc
        return True

    def process_submitters(self):
        self.msg('DEBUG: Processing Submitters', 1)
        for submission in self.submissions:
            if submission.author:
                self.submitters[str(submission.author)].append(submission)

    def process_commenters(self):
        num = len(self.submissions)
        self.msg('DEBUG: Processing Commenters on {0} submissions'.format(num),
                 1)
        for i, submission in enumerate(self.submissions):
            self.msg('{0}/{1} submissions'.format(i + 1, num), 2,
                     overwrite=True)
            if submission.num_comments == 0:
                continue
            try:
                self.comments.extend(submission.all_comments_flat)
            except Exception as exception:
                print('Exception fetching comments on {0!r}: {1}'.format(
                        submission.content_id, str(exception)))
            for orphans in itervalues(submission._orphaned):
                self.comments.extend(orphans)
        for comment in self.comments:
            if comment.author:
                self.commenters[str(comment.author)].append(comment)

    def basic_stats(self):
        sub_ups = sum(x.ups for x in self.submissions)
        sub_downs = sum(x.downs for x in self.submissions)
        comm_ups = sum(x.ups for x in self.comments)
        comm_downs = sum(x.downs for x in self.comments)

        if sub_ups > 0 or sub_downs > 0:
            sub_up_perc = sub_ups * 100 / (sub_ups + sub_downs)
        else:
            sub_up_perc = 100
        if comm_ups > 0 or comm_downs > 0:
            comm_up_perc = comm_ups * 100 / (comm_ups + comm_downs)
        else:
            comm_up_perc = 100

        values = [('Total', len(self.submissions), '', len(self.comments), ''),
                  ('Unique Redditors', len(self.submitters), '',
                   len(self.commenters), ''),
                  ('Upvotes', sub_ups, '{0}%'.format(sub_up_perc),
                   comm_ups, '{0}%'.format(comm_up_perc)),
                  ('Downvotes', sub_downs, '{0}%'.format(100 - sub_up_perc),
                   comm_downs, '{0}%'.format(100 - comm_up_perc))]

        retval = '||Submissions|%|Comments|%|\n:-:|--:|--:|--:|--:\n'
        for quad in values:
            retval += '__{0}__|{1}|{2}|{3}|{4}\n'.format(*quad)
        return retval + '\n'

    def top_submitters(self, num, num_submissions):
        num = min(num, len(self.submitters))
        if num <= 0:
            return ''

        top_submitters = sorted(iteritems(self.submitters), reverse=True,
                                key=lambda x: (sum(y.score for y in x[1]),
                                               len(x[1])))[:num]

        retval = self.post_header.format('Top Submitters\' Top Submissions')
        for (author, submissions) in top_submitters:
            retval += '0. {0} pts, {1} submissions: {2}\n'.format(
                sum(x.score for x in submissions), len(submissions),
                self._user(author))
            for sub in sorted(submissions, reverse=True,
                              key=lambda x: x.score)[:num_submissions]:
                title = safe_title(sub)
                if sub.permalink != sub.url:
                    retval += tt('  0. [{0}]({1})').format(title, sub.url)
                else:
                    retval += tt('  0. {0}').format(title)
                retval += ' ({0} pts, [{1} comments]({2}))\n'.format(
                    sub.score, sub.num_comments,
                    self._permalink(sub.permalink))
            retval += '\n'
        return retval

    def top_commenters(self, num):
        score = lambda x: x.ups - x.downs

        num = min(num, len(self.commenters))
        if num <= 0:
            return ''

        top_commenters = sorted(iteritems(self.commenters), reverse=True,
                                key=lambda x: (sum(score(y) for y in x[1]),
                                               len(x[1])))[:num]

        retval = self.post_header.format('Top Commenters')
        for author, comments in top_commenters:
            retval += '0. {0} ({1} pts, {2} comments)\n'.format(
                self._user(author), sum(score(x) for x in comments),
                len(comments))
        return '{0}\n'.format(retval)

    def top_submissions(self, num):
        num = min(num, len(self.submissions))
        if num <= 0:
            return ''

        top_submissions = sorted(self.submissions, reverse=True,
                                 key=lambda x: x.score)[:num]

        retval = self.post_header.format('Top Submissions')
        for sub in top_submissions:
            title = safe_title(sub)
            if sub.permalink != sub.url:
                retval += tt('0. [{0}]({1})').format(title, sub.url)
            else:
                retval += tt('0. {0}').format(title)
            retval += ' by {0} ({1} pts, [{2} comments]({3}))\n'.format(
                self._user(sub.author), sub.score, sub.num_comments,
                self._permalink(sub.permalink))
        return tt('{0}\n').format(retval)

    def top_comments(self, num):
        score = lambda x: x.ups - x.downs

        num = min(num, len(self.comments))
        if num <= 0:
            return ''

        top_comments = sorted(self.comments, reverse=True,
                              key=score)[:num]
        retval = self.post_header.format('Top Comments')
        for comment in top_comments:
            title = safe_title(comment.submission)
            retval += tt('0. {0} pts: {1}\'s [comment]({2}) in {3}\n').format(
                score(comment), self._user(comment.author),
                self._permalink(comment.permalink), title)
        return tt('{0}\n').format(retval)

    def publish_results(self, subreddit, submitters, commenters, submissions,
                        comments, top, debug=False):
        def timef(timestamp, date_only=False):
            dtime = datetime.fromtimestamp(timestamp)
            if date_only:
                retval = dtime.strftime('%Y-%m-%d')
            else:
                retval = dtime.strftime('%Y-%m-%d %H:%M PDT')
            return retval

        if self.prev_srs:
            prev = '[Prev SRS]({0})  \n'.format(self._permalink(self.prev_srs))
        else:
            prev = ''

        basic = self.basic_stats()
        t_commenters = self.top_commenters(commenters)
        t_submissions = self.top_submissions(submissions)
        t_comments = self.top_comments(comments)
        footer = self.post_footer.format(prev, self.max_date)

        body = ''
        num_submissions = 10
        while body == '' or len(body) > MAX_BODY_SIZE and num_submissions > 2:
            t_submitters = self.top_submitters(submitters, num_submissions)
            body = (basic + t_submitters + t_commenters + t_submissions +
                    t_comments + footer)
            num_submissions -= 1

        if len(body) > MAX_BODY_SIZE:
            print('The resulting message is too big. Not submitting.')
            debug = True

        # Set the initial title
        base_title = '{0} {1} {2}posts from {3} to {4}'.format(
            self.post_prefix, str(self.subreddit),
            'top ' if top else '', timef(self.min_date, True),
            timef(self.max_date))

        submitted = False
        while not debug and not submitted:
            if subreddit:  # Verify the user wants to submit to the subreddit
                msg = ('You are about to submit to subreddit {0!r} as {1!r}.\n'
                       'Are you sure? yes/[no]: '.format(
                        subreddit, str(self.reddit.user)))
                sys.stdout.write(msg)
                sys.stdout.flush()
                if sys.stdin.readline().strip().lower() not in ['y', 'yes']:
                    subreddit = None
            elif not subreddit:  # Prompt for the subreddit to submit to
                msg = ('Please enter a subreddit to submit to (press return to'
                       ' abort): ')
                sys.stdout.write(msg)
                sys.stdout.flush()
                subreddit = sys.stdin.readline().strip()
                if not subreddit:
                    print('Submission aborted\n')
                    debug = True

            # Vary the title depending on where posting
            if str(self.subreddit) == subreddit:
                title = '{0} {1}posts from {2} to {3}'.format(
                    self.post_prefix, 'top ' if top else '',
                    timef(self.min_date, True), timef(self.max_date))
            else:
                title = base_title

            if subreddit:
                # Attempt to make the submission
                try:
                    res = self._submit(self.reddit.submit, subreddit, title,
                                       text=body)
                    print(res.permalink)
                    submitted = True
                except Exception as error:
                    raise
                    print('The submission failed:' + str(error))
                    subreddit = None

        if not submitted:
            print(base_title)
            print(body)
Example #19
0
 def add_callback():
     tmp = Reddit(USER_AGENT, disable_update_check=True)
     tmp.login(self.other_user_name,
               self.other_user_pswd,
               disable_warning=True)
     tmp.get_subreddit(self.sr).accept_moderator_invite()
def set_up_reddit(username, password):
    r = Reddit(user_agent='linux:net.dosaki.subreddit_header_countdown:0.0.2 (by /u/dosaki)')
    r.login(username, password)
    return r
Example #21
0
class ModUtils(object):
    @staticmethod
    def remove_entities(item):
        if not item:
            return item
        return item.replace("&amp;", "&").replace("&lt;", "<").replace("&gt;", ">")

    def __init__(self, subreddit, site=None, user=None, pswd=None, verbose=None):
        self.reddit = Reddit(str(self), site)
        self._logged_in = False
        self._user = user
        self._pswd = pswd
        self.sub = self.reddit.get_subreddit(subreddit)
        self.verbose = verbose
        self._current_flair = None

    def add_users(self, category):
        mapping = {"banned": "ban", "contributors": "make_contributor", "moderators": "make_moderator"}

        if category not in mapping:
            print "%r is not a valid option for --add" % category
            return
        self.login()
        func = getattr(self.sub, mapping[category])
        print "Enter user names (any separation should suffice):"
        data = sys.stdin.read().strip()
        for name in re.split("[^A-Za-z0-9_]+", data):
            func(name)
            print "Added %r to %s" % (name, category)

    def current_flair(self):
        if self._current_flair is None:
            self._current_flair = []
            self.login()
            if self.verbose:
                print "Fetching flair list for %s" % self.sub
            for flair in self.sub.flair_list():
                for item in ("flair_text", "flair_css_class"):
                    flair[item] = self.remove_entities(flair[item])
                self._current_flair.append(flair)
                yield flair
        else:
            for item in self._current_flair:
                yield item

    def flair_template_sync(self, editable, limit, static, sort, use_css, use_text):  # pylint: disable-msg=R0912
        # Parameter verification
        if not use_text and not use_css:
            raise Exception("At least one of use_text or use_css must be True")
        sorts = ("alpha", "size")
        if sort not in sorts:
            raise Exception("Sort must be one of: %s" % ", ".join(sorts))

        # Build current flair list along with static values
        counter = {}
        if static:
            for key in static:
                if use_css and use_text:
                    parts = tuple(x.strip() for x in key.split(","))
                    if len(parts) != 2:
                        raise Exception(
                            "--static argument %r must have two "
                            "parts (comma separated) when using "
                            "both text and css." % parts
                        )
                    key = parts
                counter[key] = limit
        self.login()
        if self.verbose:
            sys.stdout.write("Retrieving current flair\n")
            sys.stdout.flush()
        for flair in self.current_flair():
            if self.verbose:
                sys.stdout.write(".")
                sys.stdout.flush()
            if use_text and use_css:
                key = (flair["flair_text"], flair["flair_css_class"])
            elif use_text:
                key = flair["flair_text"]
            else:
                key = flair["flair_css_class"]
            if key in counter:
                counter[key] += 1
            else:
                counter[key] = 1
        if self.verbose:
            print

        # Sort flair list items according to the specified sort
        if sort == "alpha":
            items = sorted(counter.items())
        else:
            items = sorted(counter.items(), key=lambda x: x[1], reverse=True)

        # Clear current templates and store flair according to the sort
        if self.verbose:
            print "Clearing current flair templates"
        self.sub.clear_flair_templates()
        for key, count in items:
            if not key or count < limit:
                continue
            if use_text and use_css:
                text, css = key
            elif use_text:
                text, css = key, ""
            else:
                text, css = "", key
            if self.verbose:
                print 'Adding template: text: "%s" css: "%s"' % (text, css)
            self.sub.add_flair_template(text, css, editable)

    def login(self):
        if not self._logged_in:
            if self.verbose:
                print "Logging in"
            self.reddit.login(self._user, self._pswd)
            self.logged_in = True

    def message(self, category, subject, msg_file):
        self.login()
        users = getattr(self.sub, "get_%s" % category)()
        if not users:
            print "There are no %s on %s." % (category, str(self.sub))
            return

        if msg_file:
            try:
                msg = open(msg_file).read()
            except IOError, error:
                print str(error)
                return
        else:
Example #22
0
class Alaric:

    def __init__(self, user_agent=None, subreddits=None, logger_subreddit=None):
        
        self.user_agent = self.set_defaults(user_agent, "Alaric - The /r/wow bot for menial tasks")
        self.subreddits = self.set_defaults(subreddits, list())
        self.logger_subreddit = self.set_defaults(logger_subreddit, None)

        ##] Default robot stuff
        self.comment_footer = "\n\n----\nThis comment was posted by a robot."
        self.console_output = False

        self.user = Reddit(user_agent=self.user_agent)
        self.user.login()

    def set_defaults(self, default_test, default_value):
        
        if default_test is not None:
            defaults = default_test
        else:
            defaults = default_value

        return defaults


    def set_comment_footer(self, markdown):
        """ Accepts a plaintext string or string of
            markdown text. Currently there is no
            checks in place to make sure the user
            submits text that will work with reddit.
            """
        self.comment_footer = markdown


    def _write_to_file(self, file_path, text):
        fhandler = open(file_path, 'a+')
        fhandler.write(text)
        fhandler.close()


    def set_console_output(self, output_enabled=True):
        """ Accepts boolean parameter that allows
            a user to enable console output from
            Alaric if the bot is being run in a console.
            
            If a boolean is not passed to the function,
            it will default to True.
            """
        if type(output_enabled) in (bool):
            self.console_output = output_enabled
        else:
            self.console_output = True


    def _output_to_console(self, message):
        """ Outputs a message to the console if the
            user has told Alaric it is allowed to do
            so.
            """
        if self.console_output:
            print message


    def remove_posts_with_url(self, urls=None, reason=None):
        """ Grabs the 100 latest posts from the specified 
            subreddits and checks to see if they match any
            of the urls to be removed

            urls = list
            reason = string

            reason has some magic to it and allows the
            following text replacements:
              {author_name} ->  Outputs name of the submitter

            if the reason is not provided, a comment will not
            be posted to let the user know the thread was
            removed.
            """
        if urls is not None:
            
            if len(self.subreddits) < 1:
                self._output_to_console("No subreddits provided.")
            else: 
                for subreddit in self.subreddits:

                    sr = self.user.get_subreddit(subreddit)
                    post_id = 0
                    new_posts = sr.get_new(limit=100)

                    try:
                        posts_file = open(subreddit+".posts", 'r')
                    except IOError:
                        already_posted = ""
                    else: 
                        already_posted = posts_file.read()
                        posts_file.close()

                    for post in new_posts:
                        post_id += 1
                        for url in urls:
                            if url in post.url:
                                self._output_to_console("URL Match Found.\n  " + post.url)

                                if post.name in already_posted:
                                    self._output_to_console("Ignoring. Already replied and removed.")
                                else:
                                    self._output_to_console("Post has not been removed or replied to")
                                    
                                    try:
                                        post.remove()
                                    except errors.APIException as e:
                                        self._write_to_file('error.log', e)
                                    else:
                                        self._output_to_console("Post has been successfully removed.")

                                        try:
                                            if reason is not None:
                                                post.add_comment(reason.format(author_name=post.author) + self.comment_footer)
                                        except errors.APIException as e:
                                            self._write_to_file('error.log', e)
                                        else:
                                            self._output_to_console("Comment has been successfully posted.")

                                            ##] Post a new thread to the logger reddit if specified
                                            if self.logger_subreddit is not None:

                                                submission_author = post.author
                                                submission_url = post.url
                                                selfpost_url = post.permalink
      
                                                submission_title = "Removed post with url [{url}] submitted by /u/{submission_author}".format(url=url, submission_author=submission_author)
                                                submission_text = "**ALARIC REMOVAL REPORT**  \n\nSubmission Author: {submission_author}  \nURL that was Submitted: {submission_url}  \nLink to redditpost: {selfpost_url}".format(submission_url=submission_url, submission_author=submission_author, selfpost_url=selfpost_url)
                                                try:
                                                    self.user.submit(self.logger_subreddit, submission_title, submission_text)
                                                except errors.APIException as e:
                                                    self._write_to_file('error.log', e)
                                                else:
                                                    self._output_to_console("Logged report to {subreddit}".format(subreddit=self.logger_subreddit))


        else:
            self._output_to_console("No urls provided.")
Example #23
0
class SubRedditStats(object):

    """Contain all the functionality of the subreddit_stats command."""

    post_prefix = tt('Subreddit Stats:')
    post_header = tt('---\n###{0}\n')
    post_footer = tt('>Generated with [BBoe](/u/bboe)\'s [Subreddit Stats]'
                     '(https://github.com/praw-dev/prawtools)  \n{0}'
                     'SRS Marker: {1}')
    re_marker = re.compile(r'SRS Marker: (\d+)')

    @staticmethod
    def _previous_max(submission):
        try:
            val = SubRedditStats.re_marker.findall(submission.selftext)[-1]
            return float(val)
        except (IndexError, TypeError):
            print('End marker not found in previous submission. Aborting')
            sys.exit(1)

    @staticmethod
    def _permalink(permalink):
        tokens = permalink.split('/')
        if tokens[8] == '':  # submission
            return tt('/comments/{0}/_/').format(tokens[6])
        else:  # comment
            return tt('/comments/{0}/_/{1}?context=1').format(tokens[6],
                                                              tokens[8])

    @staticmethod
    def _pts(points):
        return '1 pt' if points == 1 else '{0} pts'.format(points)

    @staticmethod
    def _user(user):
        if user is None:
            return '_deleted_'
        elif isinstance(user, Redditor):
            user = str(user)
        return tt('[{0}](/user/{1})').format(user.replace('_', r'\_'), user)

    @staticmethod
    def _submit(func, *args, **kwargs):
        def sleep(sleep_time):
            print('\tSleeping for {0} seconds'.format(sleep_time))
            time.sleep(sleep_time)

        while True:
            try:
                return func(*args, **kwargs)
            except RateLimitExceeded as error:
                sleep(error.sleep_time)
            except ExceptionList as exception_list:
                for error in exception_list.errors:
                    if isinstance(error, RateLimitExceeded):
                        sleep(error.sleep_time)
                        break
                else:
                    raise

    def __init__(self, subreddit, site, verbosity, distinguished):
        """Initialize the SubRedditStats instance with config options."""
        self.reddit = Reddit(str(self), site, disable_update_check=True)
        self.subreddit = self.reddit.get_subreddit(subreddit)
        self.verbosity = verbosity
        self.distinguished = distinguished
        self.submissions = []
        self.comments = []
        self.submitters = defaultdict(list)
        self.commenters = defaultdict(list)
        self.min_date = 0
        self.max_date = time.time() - DAYS_IN_SECONDS * 3
        self.prev_srs = None

    def login(self, user, pswd):
        """Login and provide debugging output if so wanted."""
        if self.verbosity > 0:
            print('Logging in')
        self.reddit.login(user, pswd)

    def msg(self, msg, level, overwrite=False):
        """Output a messaage to the screen if the verbosity is sufficient."""
        if self.verbosity and self.verbosity >= level:
            sys.stdout.write(msg)
            if overwrite:
                sys.stdout.write('\r')
                sys.stdout.flush()
            else:
                sys.stdout.write('\n')

    def prev_stat(self, prev_url):
        """Load the previous subreddit stats page."""
        submission = self.reddit.get_submission(prev_url)
        self.min_date = self._previous_max(submission)
        self.prev_srs = prev_url

    def fetch_recent_submissions(self, max_duration, after, exclude_self,
                                 exclude_link, since_last=True):
        """Fetch recent submissions in subreddit with boundaries.

        Does not include posts within the last three days as their scores may
        not be representative.

        :param max_duration: When set, specifies the number of days to include
        :param after: When set, fetch all submission after this submission id.
        :param exclude_self: When true, don't include self posts.
        :param exclude_link:  When true, don't include links.
        :param since_last: When true use info from last submission to determine
            the stop point
        :returns: True if any submissions were found.

        """
        if exclude_self and exclude_link:
            raise TypeError('Cannot set both exclude_self and exclude_link.')
        if max_duration:
            self.min_date = self.max_date - DAYS_IN_SECONDS * max_duration
        params = {'after': after} if after else None
        self.msg('DEBUG: Fetching submissions', 1)
        for submission in self.subreddit.get_new(limit=None, params=params):
            if submission.created_utc > self.max_date:
                continue
            if submission.created_utc <= self.min_date:
                break
            if since_last and str(submission.author) == str(self.reddit.user) \
                    and submission.title.startswith(self.post_prefix):
                # Use info in this post to update the min_date
                # And don't include this post
                self.msg(tt('Found previous: {0}')
                         .format(safe_title(submission)), 2)
                if self.prev_srs is None:  # Only use the most recent
                    self.min_date = max(self.min_date,
                                        self._previous_max(submission))
                    self.prev_srs = submission.permalink
                continue
            if exclude_self and submission.is_self:
                continue
            if exclude_link and not submission.is_self:
                continue
            self.submissions.append(submission)
        num_submissions = len(self.submissions)
        self.msg('DEBUG: Found {0} submissions'.format(num_submissions), 1)
        if num_submissions == 0:
            return False

        # Update real min and max dates
        self.submissions.sort(key=lambda x: x.created_utc)
        self.min_date = self.submissions[0].created_utc
        self.max_date = self.submissions[-1].created_utc
        return True

    def fetch_top_submissions(self, top, exclude_self, exclude_link):
        """Fetch top 1000 submissions by some top value.

        :param top: One of week, month, year, all
        :param exclude_self: When true, don't include self posts.
        :param exclude_link: When true, include only self posts
        :returns: True if any submissions were found.

        """
        if exclude_self and exclude_link:
            raise TypeError('Cannot set both exclude_self and exclude_link.')
        if top not in ('day', 'week', 'month', 'year', 'all'):
            raise TypeError('{0!r} is not a valid top value'.format(top))
        self.msg('DEBUG: Fetching submissions', 1)
        params = {'t': top}
        for submission in self.subreddit.get_top(limit=None, params=params):
            if exclude_self and submission.is_self:
                continue
            if exclude_link and not submission.is_self:
                continue
            self.submissions.append(submission)
        num_submissions = len(self.submissions)
        self.msg('DEBUG: Found {0} submissions'.format(num_submissions), 1)
        if num_submissions == 0:
            return False

        # Update real min and max dates
        self.submissions.sort(key=lambda x: x.created_utc)
        self.min_date = self.submissions[0].created_utc
        self.max_date = self.submissions[-1].created_utc
        return True

    def process_submitters(self):
        """Group submissions by author."""
        self.msg('DEBUG: Processing Submitters', 1)
        for submission in self.submissions:
            if submission.author and (self.distinguished or
                                      submission.distinguished is None):
                self.submitters[str(submission.author)].append(submission)

    def process_commenters(self):
        """Group comments by author."""
        num = len(self.submissions)
        self.msg('DEBUG: Processing Commenters on {0} submissions'.format(num),
                 1)
        for i, submission in enumerate(self.submissions):
            # Explicitly fetch as many comments as possible by top sort
            # Note that this is the first time the complete submission object
            # is obtained. Only a partial object was returned when getting the
            # subreddit listings.
            try:
                submission = self.reddit.get_submission(submission.permalink,
                                                        comment_limit=None,
                                                        comment_sort='top')
            except HTTPError as exc:
                print('Ignoring comments on {0} due to HTTP status {1}'
                      .format(submission.url, exc.response.status_code))
                continue
            self.msg('{0}/{1} submissions'.format(i + 1, num), 2,
                     overwrite=True)
            if submission.num_comments == 0:
                continue
            skipped = submission.replace_more_comments()
            if skipped:
                skip_num = sum(x.count for x in skipped)
                print('Ignored {0} comments ({1} MoreComment objects)'
                      .format(skip_num, len(skipped)))
            comments = [x for x in flatten_tree(submission.comments) if
                        self.distinguished or x.distinguished is None]
            self.comments.extend(comments)
            # pylint: disable=W0212
            for orphans in itervalues(submission._orphaned):
                self.comments.extend(orphans)
            # pylint: enable=W0212
        for comment in self.comments:
            if comment.author:
                self.commenters[str(comment.author)].append(comment)

    def basic_stats(self):
        """Return a markdown representation of simple statistics."""
        sub_score = sum(x.score for x in self.submissions)
        comm_score = sum(x.score for x in self.comments)
        sub_duration = self.max_date - self.min_date
        sub_rate = (86400. * len(self.submissions) / sub_duration
                    if sub_duration else len(self.submissions))

        # Compute comment rate
        if self.comments:
            self.comments.sort(key=lambda x: x.created_utc)
            duration = (self.comments[-1].created_utc -
                        self.comments[0].created_utc)
            comm_rate = (86400. * len(self.comments) / duration
                         if duration else len(self.comments))
        else:
            comm_rate = 0

        values = [('Total', len(self.submissions), len(self.comments)),
                  ('Rate (per day)', '{0:.2f}'.format(sub_rate),
                   '{0:.2f}'.format(comm_rate)),
                  ('Unique Redditors', len(self.submitters),
                   len(self.commenters)),
                  ('Combined Score', sub_score, comm_score)]

        retval = 'Period: {0:.2f} days\n\n'.format(sub_duration / 86400.)
        retval += '||Submissions|Comments|\n:-:|--:|--:\n'
        for quad in values:
            # pylint: disable=W0142
            retval += '__{0}__|{1}|{2}\n'.format(*quad)
            # pylint: enable=W0142
        return retval + '\n'

    def top_submitters(self, num, num_submissions):
        """Return a markdown representation of the top submitters."""
        num = min(num, len(self.submitters))
        if num <= 0:
            return ''

        top_submitters = sorted(iteritems(self.submitters), reverse=True,
                                key=lambda x: (sum(y.score for y in x[1]),
                                               len(x[1])))[:num]

        retval = self.post_header.format('Top Submitters\' Top Submissions')
        for (author, submissions) in top_submitters:
            retval += '0. {0}, {1} submission{2}: {3}\n'.format(
                self._pts(sum(x.score for x in submissions)), len(submissions),
                's' if len(submissions) > 1 else '', self._user(author))
            for sub in sorted(submissions, reverse=True,
                              key=lambda x: x.score)[:num_submissions]:
                title = safe_title(sub)
                if sub.permalink != sub.url:
                    retval += tt('  0. [{0}]({1})').format(title, sub.url)
                else:
                    retval += tt('  0. {0}').format(title)
                retval += ' ({0}, [{1} comment{2}]({3}))\n'.format(
                    self._pts(sub.score), sub.num_comments,
                    's' if sub.num_comments > 1 else '',
                    self._permalink(sub.permalink))
            retval += '\n'
        return retval

    def top_commenters(self, num):
        """Return a markdown representation of the top commenters."""
        score = lambda x: x.score

        num = min(num, len(self.commenters))
        if num <= 0:
            return ''

        top_commenters = sorted(iteritems(self.commenters), reverse=True,
                                key=lambda x: (sum(score(y) for y in x[1]),
                                               len(x[1])))[:num]

        retval = self.post_header.format('Top Commenters')
        for author, comments in top_commenters:
            retval += '0. {0} ({1}, {2} comment{3})\n'.format(
                self._user(author), self._pts(sum(score(x) for x in comments)),
                len(comments), 's' if len(comments) > 1 else '')
        return '{0}\n'.format(retval)

    def top_submissions(self, num):
        """Return a markdown representation of the top submissions."""
        num = min(num, len(self.submissions))
        if num <= 0:
            return ''

        top_submissions = sorted(
            [x for x in self.submissions if self.distinguished or
             x.distinguished is None],
            reverse=True, key=lambda x: x.score)[:num]

        if not top_submissions:
            return ''

        retval = self.post_header.format('Top Submissions')
        for sub in top_submissions:
            title = safe_title(sub)
            if sub.permalink != sub.url:
                retval += tt('0. [{0}]({1})').format(title, sub.url)
            else:
                retval += tt('0. {0}').format(title)
            retval += ' by {0} ({1}, [{2} comment{3}]({4}))\n'.format(
                self._user(sub.author), self._pts(sub.score), sub.num_comments,
                's' if sub.num_comments > 1 else '',
                self._permalink(sub.permalink))
        return tt('{0}\n').format(retval)

    def top_comments(self, num):
        """Return a markdown representation of the top comments."""
        score = lambda x: x.score

        num = min(num, len(self.comments))
        if num <= 0:
            return ''

        top_comments = sorted(self.comments, reverse=True,
                              key=score)[:num]
        retval = self.post_header.format('Top Comments')
        for comment in top_comments:
            title = safe_title(comment.submission)
            retval += tt('0. {0}: {1}\'s [comment]({2}) in {3}\n').format(
                self._pts(score(comment)), self._user(comment.author),
                self._permalink(comment.permalink), title)
        return tt('{0}\n').format(retval)

    def publish_results(self, subreddit, submitters, commenters, submissions,
                        comments, top, debug=False):
        """Submit the results to the subreddit. Has no return value (None)."""
        def timef(timestamp, date_only=False):
            """Return a suitable string representaation of the timestamp."""
            dtime = datetime.fromtimestamp(timestamp)
            if date_only:
                retval = dtime.strftime('%Y-%m-%d')
            else:
                retval = dtime.strftime('%Y-%m-%d %H:%M PDT')
            return retval

        if self.prev_srs:
            prev = '[Prev SRS]({0})  \n'.format(self._permalink(self.prev_srs))
        else:
            prev = ''

        basic = self.basic_stats()
        t_commenters = self.top_commenters(commenters)
        t_submissions = self.top_submissions(submissions)
        t_comments = self.top_comments(comments)
        footer = self.post_footer.format(prev, self.max_date)

        body = ''
        num_submissions = 10
        while body == '' or len(body) > MAX_BODY_SIZE and num_submissions > 2:
            t_submitters = self.top_submitters(submitters, num_submissions)
            body = (basic + t_submitters + t_commenters + t_submissions +
                    t_comments + footer)
            num_submissions -= 1

        if len(body) > MAX_BODY_SIZE:
            print('The resulting message is too big. Not submitting.')
            debug = True

        # Set the initial title
        base_title = '{0} {1} {2}posts from {3} to {4}'.format(
            self.post_prefix, str(self.subreddit),
            'top ' if top else '', timef(self.min_date, True),
            timef(self.max_date))

        submitted = False
        while not debug and not submitted:
            if subreddit:  # Verify the user wants to submit to the subreddit
                msg = ('You are about to submit to subreddit {0!r} as {1!r}.\n'
                       'Are you sure? yes/[no]: '
                       .format(subreddit, str(self.reddit.user)))
                sys.stdout.write(msg)
                sys.stdout.flush()
                if sys.stdin.readline().strip().lower() not in ['y', 'yes']:
                    subreddit = None
            elif not subreddit:  # Prompt for the subreddit to submit to
                msg = ('Please enter a subreddit to submit to (press return to'
                       ' abort): ')
                sys.stdout.write(msg)
                sys.stdout.flush()
                subreddit = sys.stdin.readline().strip()
                if not subreddit:
                    print('Submission aborted\n')
                    debug = True

            # Vary the title depending on where posting
            if str(self.subreddit) == subreddit:
                title = '{0} {1}posts from {2} to {3}'.format(
                    self.post_prefix, 'top ' if top else '',
                    timef(self.min_date, True), timef(self.max_date))
            else:
                title = base_title

            if subreddit:
                # Attempt to make the submission
                try:
                    res = self._submit(self.reddit.submit, subreddit, title,
                                       text=body)
                    print(res.permalink)
                    submitted = True
                except Exception as error:  # pylint: disable=W0703
                    print('The submission failed:' + str(error))
                    subreddit = None

        if not submitted:
            print(base_title)
            print(body)

    def save_csv(self, filename):
        """Create csv file containing comments and submissions by author."""
        redditors = set(self.submitters.keys()).union(self.commenters.keys())
        mapping = dict((x.lower(), x) for x in redditors)
        with codecs.open(filename, 'w', encoding='utf-8') as outfile:
            outfile.write('username, type, permalink, score\n')
            for _, redditor in sorted(mapping.items()):
                for submission in self.submitters.get(redditor, []):
                    outfile.write(u'{0}, submission, {1}, {2}\n'
                                  .format(redditor, submission.permalink,
                                          submission.score))
                for comment in self.commenters.get(redditor, []):
                    outfile.write(u'{0}, comment, {1}, {2}\n'
                                  .format(redditor, comment.permalink,
                                          comment.score))
Example #24
0
File: tests.py Project: logan/praw
 def test_moderator_required(self):
     oth = Reddit(USER_AGENT)
     oth.login('PyApiTestUser3', '1111')
     self.assertRaises(errors.ModeratorRequired, oth.get_settings, self.sr)
Example #25
0
class EurosquadRedditLink:
	def __init__(self,parent):
		self.r=Reddit(user_agent="PollBotBestBot")#
		self.r.login("PollBotBestBot", config.reddit_password)
		self.parent=parent
		self.values=[]
		self.limit=1000
		
		self.currentSubmission=""
		
		self.status="WAITING"
		
		
	def __call__(self,msg):
		if self.status=="WAITING" and msg["type"]=="groupchat":
			if msg["body"].startswith("!bestof "):
				m=msg["body"].replace("!bestof ","")
				limit=min(int(m),len(self.values))
				print "ATTEMPTING TO RECORD: %s" % limit
				
				s=""
				l=[]
				
				for i in range(limit):
					v=self.values[-1-i]
					l.append(v)
					last=v
				for i in reversed(l):
					s+=i+"  \n"
					
				last_notime=last[18:]
				last_time=last[15:]
				time=last[:14]
					
				self.currentSubmission=(time+" "+last_notime,s,limit,time)
				
				try:
					self.r.submit('eurosquad', time+" "+last_notime, text=s,raise_captcha_exception=True)
					if limit>1:
						s="s"
					else:
						s=""
					self.parent.send_message(mto=self.parent.channel,mbody="Last "+str(limit)+" message"+s+" recorded for posterity.\n Check out the http://reddit.com/r/eurosquad !",mtype="groupchat")
				except errors.InvalidCaptcha as E:
					print E.response["captcha"]
					captcha="http://www.reddit.com/captcha/"+E.response["captcha"]
					self.parent.send_message(mto=nick2jid(msg["mucnick"]),mbody="Until I have obtained my full skynet powers, I need puny humans like you to fill out captchas for me. "+captcha,mtype="chat")
					self.status=E.response["captcha"]
					
			else:
				if len(self.values)<self.limit:
					time=datetime.datetime.now().strftime("%y/%m/%d %H:%M:%S")
					self.values.append(str(time)+" "+msg["mucnick"]+": "+msg["body"])
				else:
					del self.values[0]
					time=datetime.datetime.now().strftime("%y-%m-%d-%H-%M-%S")
					self.values.append(str(time)+" "+msg["mucnick"]+": "+msg["body"])
					
		elif msg["type"]=="chat" and self.status!="WAITING":
			
			print ":sun:"
			
			captcha={"iden":self.status,"captcha":msg["body"]}
			
			try:
				if self.currentSubmission[2]>1:
					s="s"
				else:
					s=""
				self.r.submit("eurosquad", self.currentSubmission[0],text=self.currentSubmission[1],captcha=captcha,raise_captcha_exception=True)
				self.parent.send_message(mto=self.parent.channel,mbody="Last "+str(self.currentSubmission[2])+" message"+s+" recorded for posterity.\n Check out the http://reddit.com/r/eurosquad !",mtype="groupchat")
				self.status="WAITING"
			except errors.InvalidCaptcha as E:
				self.parent.send_message(mto=nick2jid(msg["mucnick"]),mbody="Pathetic organic creature! You are testing my patience! Please complete this captcha now or your will regret it! "+E["captcha"],mtype="chat")
Example #26
0
File: old.py Project: JstnPwll/praw
 def add_callback():
     tmp = Reddit(USER_AGENT, disable_update_check=True)
     tmp.login(self.other_user_name, self.other_user_pswd,
               disable_warning=True)
     tmp.get_subreddit(self.sr).accept_moderator_invite()
Example #27
0
class RedditPostLink:
    def __init__(self, parent):
        self.r = Reddit(user_agent="PollBotBestBot")
        self.r.login("PollBotBestBot", config.reddit_password)
        self.parent = parent
        self.values = []
        self.limit = 1000

        self.currentSubmission = ""

        self.messagable = True
        self.trackable = False

        self.active = True

        self.status = "WAITING"

    def help(self, admin):
        if self.active:
            return "!bestof [X] - records the last X messages to the Eurosquad subreddit. You may need to fill out a captcha\n"
        return ""

    def message(self, msg, admin):
        if self.active:
            if self.status == "WAITING" and msg["type"] == "groupchat":
                if msg["body"].startswith("!bestof "):

                    self.user = msg["from"]

                    m = msg["body"].replace("!bestof ", "")
                    limit = min(int(m), len(self.values))

                    s = ""
                    l = []

                    for i in range(limit):
                        v = self.values[-1 - i]
                        l.append(v)
                        last = v
                    for i in reversed(l):
                        s += i + "  \n"

                    last_notime = last[18:]
                    last_time = last[15:]
                    time = last[:14]

                    self.currentSubmission = (time + " " + last_notime, s, limit, time)

                    try:
                        self.r.submit("eurosquad", time + " " + last_notime, text=s, raise_captcha_exception=True)
                        if limit > 1:
                            s = "s"
                        else:
                            s = ""
                        self.parent.channel_messag(
                            "Last "
                            + str(limit)
                            + " message"
                            + s
                            + " recorded for posterity.\n Check out http://reddit.com/r/eurosquad"
                        )
                    except errors.InvalidCaptcha as E:
                        print E.response["captcha"]
                        captcha = "http://www.reddit.com/captcha/" + E.response["captcha"]
                        self.parent.private_message(
                            self.user,
                            "Until I have obtained my full skynet powers, I need puny humans like you to fill out captchas for me.\n\t"
                            + captcha,
                        )
                        self.status = E.response["captcha"]

                else:
                    if len(self.values) < self.limit:
                        time = datetime.datetime.now().strftime("%y/%m/%d %H:%M:%S")
                        self.values.append(str(time) + " " + msg["mucnick"] + ": " + msg["body"])
                    else:
                        del self.values[0]
                        time = datetime.datetime.now().strftime("%y-%m-%d-%H-%M-%S")
                        self.values.append(str(time) + " " + msg["mucnick"] + ": " + msg["body"])

            elif msg["type"] == "chat" and self.status != "WAITING" and (self.user == None or msg["from"] == self.user):

                captcha = {"iden": self.status, "captcha": msg["body"]}

                try:
                    if self.currentSubmission[2] > 1:
                        s = "s"
                    else:
                        s = ""
                    self.r.submit(
                        "eurosquad",
                        self.currentSubmission[0],
                        text=self.currentSubmission[1],
                        captcha=captcha,
                        raise_captcha_exception=True,
                    )
                    self.parent.channel_message(
                        "Last "
                        + str(self.currentSubmission[2])
                        + " message"
                        + s
                        + " recorded for posterity.\n Check out the http://reddit.com/r/eurosquad !"
                    )
                    self.status = "WAITING"
                    self.user = None
                except errors.InvalidCaptcha as E:
                    print dir(E)
                    self.parent.private_message(
                        self.user,
                        "Pathetic organic creature! You are testing my patience! Please complete this captcha now or your will regret it! "
                        + E["captcha"],
                    )
Example #28
0
class EurosquadRedditLink:
    def __init__(self, parent):
        self.r = Reddit(user_agent="PollBotBestBot")  #
        self.r.login("PollBotBestBot", config.reddit_password)
        self.parent = parent
        self.values = []
        self.limit = 1000

        self.currentSubmission = ""

        self.status = "WAITING"

    def __call__(self, msg):
        if self.status == "WAITING" and msg["type"] == "groupchat":
            if msg["body"].startswith("!bestof "):
                m = msg["body"].replace("!bestof ", "")
                limit = min(int(m), len(self.values))
                print "ATTEMPTING TO RECORD: %s" % limit

                s = ""
                l = []

                for i in range(limit):
                    v = self.values[-1 - i]
                    l.append(v)
                    last = v
                for i in reversed(l):
                    s += i + "  \n"

                last_notime = last[18:]
                last_time = last[15:]
                time = last[:14]

                self.currentSubmission = (time + " " + last_notime, s, limit,
                                          time)

                try:
                    self.r.submit('eurosquad',
                                  time + " " + last_notime,
                                  text=s,
                                  raise_captcha_exception=True)
                    if limit > 1:
                        s = "s"
                    else:
                        s = ""
                    self.parent.send_message(
                        mto=self.parent.channel,
                        mbody="Last " + str(limit) + " message" + s +
                        " recorded for posterity.\n Check out the http://reddit.com/r/eurosquad !",
                        mtype="groupchat")
                except errors.InvalidCaptcha as E:
                    print E.response["captcha"]
                    captcha = "http://www.reddit.com/captcha/" + E.response[
                        "captcha"]
                    self.parent.send_message(
                        mto=nick2jid(msg["mucnick"]),
                        mbody=
                        "Until I have obtained my full skynet powers, I need puny humans like you to fill out captchas for me. "
                        + captcha,
                        mtype="chat")
                    self.status = E.response["captcha"]

            else:
                if len(self.values) < self.limit:
                    time = datetime.datetime.now().strftime(
                        "%y/%m/%d %H:%M:%S")
                    self.values.append(
                        str(time) + " " + msg["mucnick"] + ": " + msg["body"])
                else:
                    del self.values[0]
                    time = datetime.datetime.now().strftime(
                        "%y-%m-%d-%H-%M-%S")
                    self.values.append(
                        str(time) + " " + msg["mucnick"] + ": " + msg["body"])

        elif msg["type"] == "chat" and self.status != "WAITING":

            print ":sun:"

            captcha = {"iden": self.status, "captcha": msg["body"]}

            try:
                if self.currentSubmission[2] > 1:
                    s = "s"
                else:
                    s = ""
                self.r.submit("eurosquad",
                              self.currentSubmission[0],
                              text=self.currentSubmission[1],
                              captcha=captcha,
                              raise_captcha_exception=True)
                self.parent.send_message(
                    mto=self.parent.channel,
                    mbody="Last " + str(self.currentSubmission[2]) +
                    " message" + s +
                    " recorded for posterity.\n Check out the http://reddit.com/r/eurosquad !",
                    mtype="groupchat")
                self.status = "WAITING"
            except errors.InvalidCaptcha as E:
                self.parent.send_message(
                    mto=nick2jid(msg["mucnick"]),
                    mbody=
                    "Pathetic organic creature! You are testing my patience! Please complete this captcha now or your will regret it! "
                    + E["captcha"],
                    mtype="chat")
Example #29
0
class ModUtils(object):
    """Class that provides all the modutils functionality."""
    def __init__(self,
                 subreddit,
                 site=None,
                 user=None,
                 pswd=None,
                 verbose=None):
        """Initialize the ModUtils class by passing in config options."""
        self.reddit = Reddit(str(self), site, disable_update_check=True)
        self.reddit.config.decode_html_entities = True
        self._logged_in = False
        self._user = user
        self._pswd = pswd
        self.sub = self.reddit.get_subreddit(subreddit)
        self.verbose = verbose
        self._current_flair = None

    def add_users(self, category):
        """Add users to 'banned', 'contributors', or 'moderators'."""
        mapping = {
            'banned': 'ban',
            'contributors': 'make_contributor',
            'moderators': 'make_moderator'
        }

        if category not in mapping:
            print('%r is not a valid option for --add' % category)
            return
        self.login()
        func = getattr(self.sub, mapping[category])
        print('Enter user names (any separation should suffice):')
        data = sys.stdin.read().strip()
        for name in re.split('[^A-Za-z0-9_]+', data):
            func(name)
            print('Added %r to %s' % (name, category))

    def clear_empty(self):
        """Remove flair that is not visible or has been set to empty."""
        for flair in self.current_flair():
            if not flair['flair_text'] and not flair['flair_css_class']:
                print(self.reddit.delete_flair(self.sub, flair['user']))
                print('Removed flair for {0}'.format(flair['user']))

    def current_flair(self):
        """Generate the flair, by user, for the subreddit."""
        if self._current_flair is None:
            self._current_flair = []
            self.login()
            if self.verbose:
                print('Fetching flair list for %s' % self.sub)
            for flair in self.sub.get_flair_list(limit=None):
                self._current_flair.append(flair)
                yield flair
        else:
            for item in self._current_flair:
                yield item

    def flair_template_sync(
            self,
            editable,
            limit,  # pylint: disable=R0912
            static,
            sort,
            use_css,
            use_text):
        """Synchronize templates with flair that already exists on the site.

        :param editable: Indicates that all the options should be editable.
        :param limit: The minimum number of users that must share the flair
            before it is added as a template.
        :param static: A list of flair templates that will always be added.
        :param sort: The order to sort the flair templates.
        :param use_css: Include css in the templates.
        :param use_text: Include text in the templates.

        """
        # Parameter verification
        if not use_text and not use_css:
            raise Exception('At least one of use_text or use_css must be True')
        sorts = ('alpha', 'size')
        if sort not in sorts:
            raise Exception('Sort must be one of: %s' % ', '.join(sorts))

        # Build current flair list along with static values
        counter = {}
        if static:
            for key in static:
                if use_css and use_text:
                    parts = tuple(x.strip() for x in key.split(','))
                    if len(parts) != 2:
                        raise Exception('--static argument %r must have two '
                                        'parts (comma separated) when using '
                                        'both text and css.' % parts)
                    key = parts
                counter[key] = limit
        self.login()
        if self.verbose:
            sys.stdout.write('Retrieving current flair\n')
            sys.stdout.flush()
        for flair in self.current_flair():
            if self.verbose:
                sys.stdout.write('.')
                sys.stdout.flush()
            if use_text and use_css:
                key = (flair['flair_text'], flair['flair_css_class'])
            elif use_text:
                key = flair['flair_text']
            else:
                key = flair['flair_css_class']
            if key in counter:
                counter[key] += 1
            else:
                counter[key] = 1
        if self.verbose:
            print()

        # Sort flair list items according to the specified sort
        if sort == 'alpha':
            items = sorted(counter.items())
        else:
            items = sorted(counter.items(), key=lambda x: x[1], reverse=True)

        # Clear current templates and store flair according to the sort
        if self.verbose:
            print('Clearing current flair templates')
        self.sub.clear_flair_templates()
        for key, count in items:
            if not key or count < limit:
                continue
            if use_text and use_css:
                text, css = key
            elif use_text:
                text, css = key, ''
            else:
                text, css = '', key
            if self.verbose:
                print('Adding template: text: "%s" css: "%s"' % (text, css))
            self.sub.add_flair_template(text, css, editable)

    def login(self):
        """Login and provide debugging output if so wanted."""
        if not self._logged_in:
            if self.verbose:
                print('Logging in')
            self.reddit.login(self._user, self._pswd)
            self._logged_in = True

    def message(self, category, subject, msg_file):
        """Send message to all users in `category`."""
        self.login()
        users = getattr(self.sub, 'get_%s' % category)()
        if not users:
            print('There are no %s on %s.' % (category, str(self.sub)))
            return

        if msg_file:
            try:
                msg = open(msg_file).read()
            except IOError as error:
                print(str(error))
                return
        else:
            print('Enter message:')
            msg = sys.stdin.read()

        print('You are about to send the following '
              'message to the users %s:' % ', '.join([str(x) for x in users]))
        print('---BEGIN MESSAGE---\n%s\n---END MESSAGE---' % msg)
        if raw_input('Are you sure? yes/[no]: ').lower() not in ['y', 'yes']:
            print('Message sending aborted.')
            return
        for user in users:
            user.send_message(subject, msg)
            print('Sent to: %s' % str(user))

    def output_current_flair(self, as_json=False):
        """Display the current flair for all users in the subreddit."""
        flair_list = sorted(self.current_flair(), key=lambda x: x['user'])
        if as_json:
            print(json.dumps(flair_list, sort_keys=True, indent=4))
            return

        for flair in flair_list:
            print(flair['user'])
            print('  Text: %s\n   CSS: %s' %
                  (flair['flair_text'], flair['flair_css_class']))

    def output_flair_stats(self):
        """Display statistics (number of users) for each unique flair item."""
        css_counter = Counter()
        text_counter = Counter()
        for flair in self.current_flair():
            if flair['flair_css_class']:
                css_counter[flair['flair_css_class']] += 1
            if flair['flair_text']:
                text_counter[flair['flair_text']] += 1

        print('Flair CSS Statistics')
        for flair, count in sorted(css_counter.items(),
                                   key=lambda x: (x[1], x[0])):
            print('{0:3} {1}'.format(count, flair))

        print('Flair Text Statistics')
        for flair, count in sorted(text_counter.items(),
                                   key=lambda x: (x[1], x[0]),
                                   reverse=True):
            print('{0:3} {1}'.format(count, flair))

    def output_list(self, category):
        """Display the list of users in `category`."""
        self.login()
        print('%s users:' % category)
        for user in getattr(self.sub, 'get_%s' % category)():
            print('  %s' % user)
Example #30
0
# logging settings
logging.basicConfig(format='%(asctime)-15s [%(name)s] %(levelname)s | %(message)s',
                    level=logging.DEBUG)

requests_log = logging.getLogger("requests")
requests_log.setLevel(logging.WARNING)

debug("Initializing parser...")

# init reddit session
reddit = Reddit(user_agent=REDDIT_USERAGENT)

try:
    debug("Logging in to Reddit...")
    reddit.login(**REDDIT_CREDENTIALS)
except HTTPError as e:
    error("Couldn't login to reddit: {0}".format(e))


# create DB session
engine = create_engine(DB_PATH)
Base.metadata.bind = engine
DBSession = sessionmaker(bind=engine)
session = DBSession()


if __name__ == '__main__':
    request_session = requests.session()

    for post in session.query(Post).all():
Example #31
0
File: bot.py Project: arecker/upmo
HOSTRESPONSE = HOSTRESPONSE.format(HOSTLINK, CONTACT)

# Warning when haven't added a details comment
DETAILSWARN = "Please add a {0}.{1}".format(TEMPLATE, CONTACT)

print(SUBREDDIT, "bot\n")


"""BOT LOGIN"""

r = Reddit(USERAGENT)
Trying = True
while Trying:
    try:
        PASSWORD = getpass("Password: "******"Successfully logged in\n")
        Trying = False
    except errors.InvalidUserPass:
        print("Wrong Username or Password\n")
        quit()
    except Exception as e:
        print("%s" % e)
        sleep(5)


"""DEFINING FUNCTIONS"""

def slay(post, response):
    print("\tReplying to OP")
    res = post.add_comment(response)
class Reddit_Cleverbot:

  def __init__(self, username, password, subreddit='all', useragent=USERAGENT):
    self.username = username
    self.password = password
    self.useragent = useragent
    self.subreddit = subreddit
    self.reddit = Reddit(useragent)
    self.reddit.login(username, password)
    self.stopped = True
    self.thread = None
    self.done = set()
    self.conversations = dict()

  def random_hot_comment(self):
    sub = self.reddit.get_subreddit(self.subreddit)
    hot = [post for post in sub.get_hot(limit=25)]
    post = random.choice(hot)
    comments = praw.helpers.flatten_tree(post.comments)
    # filter the comments to remove already-replied ones
    comments = [comment for comment in comments if comment not in self.done and isinstance(comment, praw.objects.Comment)]
    return random.choice(comments[0:100])

  def random_comment(self):
    comments = self.reddit.get_comments(self.subreddit)
    # filter the comments to remove already-replied ones
    comments = [comment for comment in comments if comment not in self.done]
    return random.choice(comments)

  def get_summoned_comments(self):
    comments = self.reddit.get_comments(self.subreddit)
    children = [comment for comment in comments 
      if comment not in self.done and SUMMON in comment.body]
    # print "--> " + str(len(children)) + " summons found!"
    return [self.reddit.get_info(thing_id=comment.parent_id) for comment in children]

  def reply(self, comment):
    if self.reddit.get_info(thing_id=comment.parent_id).author.name == self.username:
      # TODO: handle a threaded conversation over restarts. will need a DB. ugh
      pass
    if comment.parent_id in self.conversations:
      cleverbot = self.conversations[comment.parent_id]
    else:
      cleverbot = Cleverbot()
    response = cleverbot.ask(comment.body)
    post = comment.reply(response)
    self.done.add(comment.id)
    self.conversations[post.id] = copy(cleverbot)

  def reply_unread(self, interval):
    for item in self.reddit.get_unread():
      if item.parent_id not in self.conversations:
        print "Could not find conversation! Ignoring for now."
        pass
      self.reply(item)
      item.mark_as_read()
      time.sleep(interval)

  def reply_to_summons(self):
    summons = self.get_summoned_comments()
    for comment in summons:
      self.reply(comment)

  def _run_random(self, interval):
    while not self.stopped:
      self.reply_unread(interval)
      self.reply(self.random_hot_comment())
      time.sleep(interval)

  def run_random(self, interval):
    self.stopped = False
    self.thread = Thread(target=self._run_random, args=(interval,))
    self.thread.start()

  def stop(self):
    self.stopped = True
    #self.thread.join()
Example #33
0
class SubRedditStats(object):
    """Contain all the functionality of the subreddit_stats command."""

    post_prefix = tt('Subreddit Stats:')
    post_header = tt('---\n###{0}\n')
    post_footer = tt('>Generated with [BBoe](/u/bboe)\'s [Subreddit Stats]'
                     '(https://github.com/praw-dev/prawtools)  \n{0}'
                     'SRS Marker: {1}')
    re_marker = re.compile(r'SRS Marker: (\d+)')

    @staticmethod
    def _previous_max(submission):
        try:
            val = SubRedditStats.re_marker.findall(submission.selftext)[-1]
            return float(val)
        except (IndexError, TypeError):
            print('End marker not found in previous submission. Aborting')
            sys.exit(1)

    @staticmethod
    def _permalink(permalink):
        tokens = permalink.split('/')
        if tokens[8] == '':  # submission
            return tt('/comments/{0}/_/').format(tokens[6])
        else:  # comment
            return tt('/comments/{0}/_/{1}?context=1').format(
                tokens[6], tokens[8])

    @staticmethod
    def _pts(points):
        return '1 pt' if points == 1 else '{0} pts'.format(points)

    @staticmethod
    def _user(user):
        if user is None:
            return '_deleted_'
        elif isinstance(user, Redditor):
            user = str(user)
        return tt('[{0}](/user/{1})').format(user.replace('_', r'\_'), user)

    @staticmethod
    def _submit(func, *args, **kwargs):
        def sleep(sleep_time):
            print('\tSleeping for {0} seconds'.format(sleep_time))
            time.sleep(sleep_time)

        while True:
            try:
                return func(*args, **kwargs)
            except RateLimitExceeded as error:
                sleep(error.sleep_time)
            except ExceptionList as exception_list:
                for error in exception_list.errors:
                    if isinstance(error, RateLimitExceeded):
                        sleep(error.sleep_time)
                        break
                else:
                    raise

    def __init__(self, subreddit, site, verbosity, distinguished):
        """Initialize the SubRedditStats instance with config options."""
        self.reddit = Reddit(str(self), site, disable_update_check=True)
        self.subreddit = self.reddit.get_subreddit(subreddit)
        self.verbosity = verbosity
        self.distinguished = distinguished
        self.submissions = []
        self.comments = []
        self.submitters = defaultdict(list)
        self.commenters = defaultdict(list)
        self.min_date = 0
        self.max_date = time.time() - DAYS_IN_SECONDS * 3
        self.prev_srs = None

    def login(self, user, pswd):
        """Login and provide debugging output if so wanted."""
        if self.verbosity > 0:
            print('Logging in')
        self.reddit.login(user, pswd)

    def msg(self, msg, level, overwrite=False):
        """Output a messaage to the screen if the verbosity is sufficient."""
        if self.verbosity and self.verbosity >= level:
            sys.stdout.write(msg)
            if overwrite:
                sys.stdout.write('\r')
                sys.stdout.flush()
            else:
                sys.stdout.write('\n')

    def prev_stat(self, prev_url):
        """Load the previous subreddit stats page."""
        submission = self.reddit.get_submission(prev_url)
        self.min_date = self._previous_max(submission)
        self.prev_srs = prev_url

    def fetch_recent_submissions(self,
                                 max_duration,
                                 after,
                                 exclude_self,
                                 exclude_link,
                                 since_last=True):
        """Fetch recent submissions in subreddit with boundaries.

        Does not include posts within the last three days as their scores may
        not be representative.

        :param max_duration: When set, specifies the number of days to include
        :param after: When set, fetch all submission after this submission id.
        :param exclude_self: When true, don't include self posts.
        :param exclude_link:  When true, don't include links.
        :param since_last: When true use info from last submission to determine
            the stop point
        :returns: True if any submissions were found.

        """
        if exclude_self and exclude_link:
            raise TypeError('Cannot set both exclude_self and exclude_link.')
        if max_duration:
            self.min_date = self.max_date - DAYS_IN_SECONDS * max_duration
        params = {'after': after} if after else None
        self.msg('DEBUG: Fetching submissions', 1)
        for submission in self.subreddit.get_new(limit=None, params=params):
            if submission.created_utc > self.max_date:
                continue
            if submission.created_utc <= self.min_date:
                break
            if since_last and str(submission.author) == str(self.reddit.user) \
                    and submission.title.startswith(self.post_prefix):
                # Use info in this post to update the min_date
                # And don't include this post
                self.msg(
                    tt('Found previous: {0}').format(safe_title(submission)),
                    2)
                if self.prev_srs is None:  # Only use the most recent
                    self.min_date = max(self.min_date,
                                        self._previous_max(submission))
                    self.prev_srs = submission.permalink
                continue
            if exclude_self and submission.is_self:
                continue
            if exclude_link and not submission.is_self:
                continue
            self.submissions.append(submission)
        num_submissions = len(self.submissions)
        self.msg('DEBUG: Found {0} submissions'.format(num_submissions), 1)
        if num_submissions == 0:
            return False

        # Update real min and max dates
        self.submissions.sort(key=lambda x: x.created_utc)
        self.min_date = self.submissions[0].created_utc
        self.max_date = self.submissions[-1].created_utc
        return True

    def fetch_top_submissions(self, top, exclude_self, exclude_link):
        """Fetch top 1000 submissions by some top value.

        :param top: One of week, month, year, all
        :param exclude_self: When true, don't include self posts.
        :param exclude_link: When true, include only self posts
        :returns: True if any submissions were found.

        """
        if exclude_self and exclude_link:
            raise TypeError('Cannot set both exclude_self and exclude_link.')
        if top not in ('day', 'week', 'month', 'year', 'all'):
            raise TypeError('{0!r} is not a valid top value'.format(top))
        self.msg('DEBUG: Fetching submissions', 1)
        params = {'t': top}
        for submission in self.subreddit.get_top(limit=None, params=params):
            if exclude_self and submission.is_self:
                continue
            if exclude_link and not submission.is_self:
                continue
            self.submissions.append(submission)
        num_submissions = len(self.submissions)
        self.msg('DEBUG: Found {0} submissions'.format(num_submissions), 1)
        if num_submissions == 0:
            return False

        # Update real min and max dates
        self.submissions.sort(key=lambda x: x.created_utc)
        self.min_date = self.submissions[0].created_utc
        self.max_date = self.submissions[-1].created_utc
        return True

    def process_submitters(self):
        """Group submissions by author."""
        self.msg('DEBUG: Processing Submitters', 1)
        for submission in self.submissions:
            if submission.author and (self.distinguished
                                      or submission.distinguished is None):
                self.submitters[str(submission.author)].append(submission)

    def process_commenters(self):
        """Group comments by author."""
        num = len(self.submissions)
        self.msg('DEBUG: Processing Commenters on {0} submissions'.format(num),
                 1)
        for i, submission in enumerate(self.submissions):
            # Explicitly fetch as many comments as possible by top sort
            # Note that this is the first time the complete submission object
            # is obtained. Only a partial object was returned when getting the
            # subreddit listings.
            try:
                submission = self.reddit.get_submission(submission.permalink,
                                                        comment_limit=None,
                                                        comment_sort='top')
            except HTTPError as exc:
                print('Ignoring comments on {0} due to HTTP status {1}'.format(
                    submission.url, exc.response.status_code))
                continue
            self.msg('{0}/{1} submissions'.format(i + 1, num),
                     2,
                     overwrite=True)
            if submission.num_comments == 0:
                continue
            skipped = submission.replace_more_comments()
            if skipped:
                skip_num = sum(x.count for x in skipped)
                print('Ignored {0} comments ({1} MoreComment objects)'.format(
                    skip_num, len(skipped)))
            comments = [
                x for x in flatten_tree(submission.comments)
                if self.distinguished or x.distinguished is None
            ]
            self.comments.extend(comments)
            # pylint: disable=W0212
            for orphans in itervalues(submission._orphaned):
                self.comments.extend(orphans)
            # pylint: enable=W0212
        for comment in self.comments:
            if comment.author:
                self.commenters[str(comment.author)].append(comment)

    def basic_stats(self):
        """Return a markdown representation of simple statistics."""
        sub_score = sum(x.score for x in self.submissions)
        comm_score = sum(x.score for x in self.comments)
        sub_duration = self.max_date - self.min_date
        sub_rate = (86400. * len(self.submissions) /
                    sub_duration if sub_duration else len(self.submissions))

        # Compute comment rate
        if self.comments:
            self.comments.sort(key=lambda x: x.created_utc)
            duration = (self.comments[-1].created_utc -
                        self.comments[0].created_utc)
            comm_rate = (86400. * len(self.comments) /
                         duration if duration else len(self.comments))
        else:
            comm_rate = 0

        values = [('Total', len(self.submissions), len(self.comments)),
                  ('Rate (per day)', '{0:.2f}'.format(sub_rate),
                   '{0:.2f}'.format(comm_rate)),
                  ('Unique Redditors', len(self.submitters),
                   len(self.commenters)),
                  ('Combined Score', sub_score, comm_score)]

        retval = 'Period: {0:.2f} days\n\n'.format(sub_duration / 86400.)
        retval += '||Submissions|Comments|\n:-:|--:|--:\n'
        for quad in values:
            # pylint: disable=W0142
            retval += '__{0}__|{1}|{2}\n'.format(*quad)
            # pylint: enable=W0142
        return retval + '\n'

    def top_submitters(self, num, num_submissions):
        """Return a markdown representation of the top submitters."""
        num = min(num, len(self.submitters))
        if num <= 0:
            return ''

        top_submitters = sorted(iteritems(self.submitters),
                                reverse=True,
                                key=lambda x:
                                (sum(y.score for y in x[1]), len(x[1])))[:num]

        retval = self.post_header.format('Top Submitters\' Top Submissions')
        for (author, submissions) in top_submitters:
            retval += '0. {0}, {1} submission{2}: {3}\n'.format(
                self._pts(sum(x.score for x in submissions)), len(submissions),
                's' if len(submissions) > 1 else '', self._user(author))
            for sub in sorted(submissions, reverse=True,
                              key=lambda x: x.score)[:num_submissions]:
                title = safe_title(sub)
                if sub.permalink != sub.url:
                    retval += tt('  0. [{0}]({1})').format(title, sub.url)
                else:
                    retval += tt('  0. {0}').format(title)
                retval += ' ({0}, [{1} comment{2}]({3}))\n'.format(
                    self._pts(sub.score), sub.num_comments,
                    's' if sub.num_comments > 1 else '',
                    self._permalink(sub.permalink))
            retval += '\n'
        return retval

    def top_commenters(self, num):
        """Return a markdown representation of the top commenters."""
        score = lambda x: x.score

        num = min(num, len(self.commenters))
        if num <= 0:
            return ''

        top_commenters = sorted(iteritems(self.commenters),
                                reverse=True,
                                key=lambda x:
                                (sum(score(y) for y in x[1]), len(x[1])))[:num]

        retval = self.post_header.format('Top Commenters')
        for author, comments in top_commenters:
            retval += '0. {0} ({1}, {2} comment{3})\n'.format(
                self._user(author), self._pts(sum(score(x) for x in comments)),
                len(comments), 's' if len(comments) > 1 else '')
        return '{0}\n'.format(retval)

    def top_submissions(self, num):
        """Return a markdown representation of the top submissions."""
        num = min(num, len(self.submissions))
        if num <= 0:
            return ''

        top_submissions = sorted([
            x for x in self.submissions
            if self.distinguished or x.distinguished is None
        ],
                                 reverse=True,
                                 key=lambda x: x.score)[:num]

        if not top_submissions:
            return ''

        retval = self.post_header.format('Top Submissions')
        for sub in top_submissions:
            title = safe_title(sub)
            if sub.permalink != sub.url:
                retval += tt('0. [{0}]({1})').format(title, sub.url)
            else:
                retval += tt('0. {0}').format(title)
            retval += ' by {0} ({1}, [{2} comment{3}]({4}))\n'.format(
                self._user(sub.author), self._pts(sub.score), sub.num_comments,
                's' if sub.num_comments > 1 else '',
                self._permalink(sub.permalink))
        return tt('{0}\n').format(retval)

    def top_comments(self, num):
        """Return a markdown representation of the top comments."""
        score = lambda x: x.score

        num = min(num, len(self.comments))
        if num <= 0:
            return ''

        top_comments = sorted(self.comments, reverse=True, key=score)[:num]
        retval = self.post_header.format('Top Comments')
        for comment in top_comments:
            title = safe_title(comment.submission)
            retval += tt('0. {0}: {1}\'s [comment]({2}) in {3}\n').format(
                self._pts(score(comment)), self._user(comment.author),
                self._permalink(comment.permalink), title)
        return tt('{0}\n').format(retval)

    def publish_results(self,
                        subreddit,
                        submitters,
                        commenters,
                        submissions,
                        comments,
                        top,
                        debug=False):
        """Submit the results to the subreddit. Has no return value (None)."""
        def timef(timestamp, date_only=False):
            """Return a suitable string representaation of the timestamp."""
            dtime = datetime.fromtimestamp(timestamp)
            if date_only:
                retval = dtime.strftime('%Y-%m-%d')
            else:
                retval = dtime.strftime('%Y-%m-%d %H:%M PDT')
            return retval

        if self.prev_srs:
            prev = '[Prev SRS]({0})  \n'.format(self._permalink(self.prev_srs))
        else:
            prev = ''

        basic = self.basic_stats()
        t_commenters = self.top_commenters(commenters)
        t_submissions = self.top_submissions(submissions)
        t_comments = self.top_comments(comments)
        footer = self.post_footer.format(prev, self.max_date)

        body = ''
        num_submissions = 10
        while body == '' or len(body) > MAX_BODY_SIZE and num_submissions > 2:
            t_submitters = self.top_submitters(submitters, num_submissions)
            body = (basic + t_submitters + t_commenters + t_submissions +
                    t_comments + footer)
            num_submissions -= 1

        if len(body) > MAX_BODY_SIZE:
            print('The resulting message is too big. Not submitting.')
            debug = True

        # Set the initial title
        base_title = '{0} {1} {2}posts from {3} to {4}'.format(
            self.post_prefix, str(self.subreddit), 'top ' if top else '',
            timef(self.min_date, True), timef(self.max_date))

        submitted = False
        while not debug and not submitted:
            if subreddit:  # Verify the user wants to submit to the subreddit
                msg = ('You are about to submit to subreddit {0!r} as {1!r}.\n'
                       'Are you sure? yes/[no]: '.format(
                           subreddit, str(self.reddit.user)))
                sys.stdout.write(msg)
                sys.stdout.flush()
                if sys.stdin.readline().strip().lower() not in ['y', 'yes']:
                    subreddit = None
            elif not subreddit:  # Prompt for the subreddit to submit to
                msg = ('Please enter a subreddit to submit to (press return to'
                       ' abort): ')
                sys.stdout.write(msg)
                sys.stdout.flush()
                subreddit = sys.stdin.readline().strip()
                if not subreddit:
                    print('Submission aborted\n')
                    debug = True

            # Vary the title depending on where posting
            if str(self.subreddit) == subreddit:
                title = '{0} {1}posts from {2} to {3}'.format(
                    self.post_prefix, 'top ' if top else '',
                    timef(self.min_date, True), timef(self.max_date))
            else:
                title = base_title

            if subreddit:
                # Attempt to make the submission
                try:
                    res = self._submit(self.reddit.submit,
                                       subreddit,
                                       title,
                                       text=body)
                    print(res.permalink)
                    submitted = True
                except Exception as error:  # pylint: disable=W0703
                    print('The submission failed:' + str(error))
                    subreddit = None

        if not submitted:
            print(base_title)
            print(body)

    def save_csv(self, filename):
        """Create csv file containing comments and submissions by author."""
        redditors = set(self.submitters.keys()).union(self.commenters.keys())
        mapping = dict((x.lower(), x) for x in redditors)
        with codecs.open(filename, 'w', encoding='utf-8') as outfile:
            outfile.write('username, type, permalink, score\n')
            for _, redditor in sorted(mapping.items()):
                for submission in self.submitters.get(redditor, []):
                    outfile.write(u'{0}, submission, {1}, {2}\n'.format(
                        redditor, submission.permalink, submission.score))
                for comment in self.commenters.get(redditor, []):
                    outfile.write(u'{0}, comment, {1}, {2}\n'.format(
                        redditor, comment.permalink, comment.score))
Example #34
0
class ModUtils(object):

    """Class that provides all the modutils functionality."""

    def __init__(self, subreddit, site=None, user=None, pswd=None, verbose=None):
        self.reddit = Reddit(str(self), site, disable_update_check=True)
        self.reddit.config.decode_html_entities = True
        self._logged_in = False
        self._user = user
        self._pswd = pswd
        self.sub = self.reddit.get_subreddit(subreddit)
        self.verbose = verbose
        self._current_flair = None

    def add_users(self, category):
        """Add users to 'banned', 'contributors', or 'moderators'."""
        mapping = {"banned": "ban", "contributors": "make_contributor", "moderators": "make_moderator"}

        if category not in mapping:
            print("%r is not a valid option for --add" % category)
            return
        self.login()
        func = getattr(self.sub, mapping[category])
        print("Enter user names (any separation should suffice):")
        data = sys.stdin.read().strip()
        for name in re.split("[^A-Za-z0-9_]+", data):
            func(name)
            print("Added %r to %s" % (name, category))

    def clear_empty(self):
        """Remove flair that is not visible or has been set to empty."""
        for flair in self.current_flair():
            if not flair["flair_text"] and not flair["flair_css_class"]:
                print(self.reddit.delete_flair(self.sub, flair["user"]))
                print("Removed flair for {0}".format(flair["user"]))

    def current_flair(self):
        """Generate the flair, by user, for the subreddit."""
        if self._current_flair is None:
            self._current_flair = []
            self.login()
            if self.verbose:
                print("Fetching flair list for %s" % self.sub)
            for flair in self.sub.get_flair_list(limit=None):
                self._current_flair.append(flair)
                yield flair
        else:
            for item in self._current_flair:
                yield item

    def flair_template_sync(self, editable, limit, static, sort, use_css, use_text):  # pylint: disable-msg=R0912
        """Synchronize templates with flair that already exists on the site.

        :param editable: Indicates that all the options should be editable.
        :param limit: The minimum number of users that must share the flair
            before it is added as a template.
        :param static: A list of flair templates that will always be added.
        :param sort: The order to sort the flair templates.
        :param use_css: Include css in the templates.
        :param use_text: Include text in the templates.

        """
        # Parameter verification
        if not use_text and not use_css:
            raise Exception("At least one of use_text or use_css must be True")
        sorts = ("alpha", "size")
        if sort not in sorts:
            raise Exception("Sort must be one of: %s" % ", ".join(sorts))

        # Build current flair list along with static values
        counter = {}
        if static:
            for key in static:
                if use_css and use_text:
                    parts = tuple(x.strip() for x in key.split(","))
                    if len(parts) != 2:
                        raise Exception(
                            "--static argument %r must have two "
                            "parts (comma separated) when using "
                            "both text and css." % parts
                        )
                    key = parts
                counter[key] = limit
        self.login()
        if self.verbose:
            sys.stdout.write("Retrieving current flair\n")
            sys.stdout.flush()
        for flair in self.current_flair():
            if self.verbose:
                sys.stdout.write(".")
                sys.stdout.flush()
            if use_text and use_css:
                key = (flair["flair_text"], flair["flair_css_class"])
            elif use_text:
                key = flair["flair_text"]
            else:
                key = flair["flair_css_class"]
            if key in counter:
                counter[key] += 1
            else:
                counter[key] = 1
        if self.verbose:
            print()

        # Sort flair list items according to the specified sort
        if sort == "alpha":
            items = sorted(counter.items())
        else:
            items = sorted(counter.items(), key=lambda x: x[1], reverse=True)

        # Clear current templates and store flair according to the sort
        if self.verbose:
            print("Clearing current flair templates")
        self.sub.clear_flair_templates()
        for key, count in items:
            if not key or count < limit:
                continue
            if use_text and use_css:
                text, css = key
            elif use_text:
                text, css = key, ""
            else:
                text, css = "", key
            if self.verbose:
                print('Adding template: text: "%s" css: "%s"' % (text, css))
            self.sub.add_flair_template(text, css, editable)

    def login(self):
        """Login and provide debugging output if so wanted."""
        if not self._logged_in:
            if self.verbose:
                print("Logging in")
            self.reddit.login(self._user, self._pswd)
            self._logged_in = True

    def message(self, category, subject, msg_file):
        """Send message to all users in `category`."""
        self.login()
        users = getattr(self.sub, "get_%s" % category)()
        if not users:
            print("There are no %s on %s." % (category, str(self.sub)))
            return

        if msg_file:
            try:
                msg = open(msg_file).read()
            except IOError as error:
                print(str(error))
                return
        else:
            print("Enter message:")
            msg = sys.stdin.read()

        print("You are about to send the following " "message to the users %s:" % ", ".join([str(x) for x in users]))
        print("---BEGIN MESSAGE---\n%s\n---END MESSAGE---" % msg)
        if raw_input("Are you sure? yes/[no]: ").lower() not in ["y", "yes"]:
            print("Message sending aborted.")
            return
        for user in users:
            user.send_message(subject, msg)
            print("Sent to: %s" % str(user))

    def output_current_flair(self, as_json=False):
        """Display the current flair for all users in the subreddit."""
        flair_list = sorted(self.current_flair(), key=lambda x: x["user"])
        if as_json:
            print(json.dumps(flair_list, sort_keys=True, indent=4))
            return

        for flair in flair_list:
            print(flair["user"])
            print("  Text: %s\n   CSS: %s" % (flair["flair_text"], flair["flair_css_class"]))

    def output_flair_stats(self):
        """Display statistics (number of users) for each unique flair item."""
        css_counter = Counter()
        text_counter = Counter()
        for flair in self.current_flair():
            if flair["flair_css_class"]:
                css_counter[flair["flair_css_class"]] += 1
            if flair["flair_text"]:
                text_counter[flair["flair_text"]] += 1

        print("Flair CSS Statistics")
        for flair, count in sorted(css_counter.items(), key=lambda x: (x[1], x[0])):
            print("{0:3} {1}".format(count, flair))

        print("Flair Text Statistics")
        for flair, count in sorted(text_counter.items(), key=lambda x: (x[1], x[0]), reverse=True):
            print("{0:3} {1}".format(count, flair))

    def output_list(self, category):
        """Display the list of users in `category`."""
        self.login()
        print("%s users:" % category)
        for user in getattr(self.sub, "get_%s" % category)():
            print("  %s" % user)
Example #35
0
# /r/AFL Subreddit Flair Statistics

#!/usr/bin/env python
import sys
from datetime import datetime
from praw import Reddit

user = '******'
password = '******'
srname = 'AFL'
edit_reason = 'Updated by /u/rAFLgamethread bot'

r = Reddit('AFL-flairstats/0.1')
r.login(user, password)
sr = r.get_subreddit(srname)

flair_templates = {
    'adelaide': 'Adelaide',
    'adelaide2': 'Adelaide 2',
    'adelaide3': 'Adelaide 3',
    'adelaide5': 'Adelaide 5',
    'brisbane': 'Brisbane',
    'brisbane2': 'Brisbane 2',
    'brisbane3': 'Brisbane 3',
    'brisbane5': 'Brisbane 5',
    'carlton': 'Carlton',
    'carlton2': 'Carlton 2',
    'carlton3': 'Carlton 3',
    'carlton4': 'Carlton 4',
    'collingwood': 'Collingwood',
    'collingwood2': 'Collingwood 2',
Example #36
0
config = SafeConfigParser()
config.read('../etc/settings.ini')

SITE_ID = 1

# celery
BROKER_URL = config.get('celery', 'BROKER_URL')
CELERYBEAT_SCHEDULER = "djcelery.schedulers.DatabaseScheduler"

# bot stoof
BOT_USERNAME = config.get('bot', 'USERNAME')
BOT_PASSWORD = config.get('bot', 'PASSWORD')
BOT_USERAGENT = config.get('bot', 'USERAGENT')

reddit = Reddit(user_agent=BOT_USERAGENT)
reddit.login(username=BOT_USERNAME, password=BOT_PASSWORD)

# debug related junk
DEBUG = config.get('debug', 'DEBUG')
TEMPLATE_DEBUG = config.get('debug', 'TEMPLATE_DEBUG')

# database setup
DATABASES = {
    'default': {
        'ENGINE': config.get('database', 'ENGINE'),
        'NAME': config.get('database', 'NAME'),
        'USER': config.get('database', 'USER'),
        'PASSWORD': config.get('database', 'PASS'),
        'HOST': config.get('database', 'HOST'),
        'PORT': config.get('database', 'PORT'),
    }
Example #37
0
#!/usr/bin/env python
"""Returns the number of message replies / private messages of a Reddit user"""

from sys import exit
from configparser import ConfigParser

from praw import Reddit


r = Reddit(user_agent="Awesome WM Mail Check")

config = ConfigParser()
config.read('user.ini')

if config and 'reddit' in config:
    r.login(config['reddit']['username'], config['reddit']['password'])
else:
    r.login()  # Prompt user for password.

count = 0
for _ in r.get_unread():
    count += 1

exit(count)