def _send_private_message(self, user: Redditor, message_body, subject: Text = 'Repost Check', source: Text = None, post_id: Text = None, comment_id: Text = None) -> NoReturn: if not user: log.error('No user provided to send private message') return try: start_time = perf_counter() user.message(subject, message_body) self._record_api_event( float(round(perf_counter() - start_time, 2)), 'private_message', self.reddit.reddit.auth.limits['remaining']) log.info('Sent PM to %s. ', user.name) except Exception as e: log.exception('Failed to send PM to %s', user.name, exc_info=True) raise self._save_private_message( BotPrivateMessage(subject=subject, body=message_body, in_response_to_post=post_id, in_response_to_comment=comment_id, triggered_from=source, recipient=user.name))
def test_construct_failure(self): message = 'Either `name` or `_data` must be provided.' with pytest.raises(TypeError) as excinfo: Redditor(self.reddit) assert str(excinfo.value) == message with pytest.raises(TypeError) as excinfo: Redditor(self.reddit, 'dummy', {'id': 'dummy'}) assert str(excinfo.value) == message
def test_hash(self): redditor1 = Redditor(self.reddit, _data={'name': 'dummy1', 'n': 1}) redditor2 = Redditor(self.reddit, _data={'name': 'Dummy1', 'n': 2}) redditor3 = Redditor(self.reddit, _data={'name': 'dummy3', 'n': 2}) assert hash(redditor1) == hash(redditor1) assert hash(redditor2) == hash(redditor2) assert hash(redditor3) == hash(redditor3) assert hash(redditor1) == hash(redditor2) assert hash(redditor2) != hash(redditor3) assert hash(redditor1) != hash(redditor3)
def test_hash(self): redditor1 = Redditor(self.reddit, _data={"name": "dummy1", "n": 1}) redditor2 = Redditor(self.reddit, _data={"name": "Dummy1", "n": 2}) redditor3 = Redditor(self.reddit, _data={"name": "dummy3", "n": 2}) assert hash(redditor1) == hash(redditor1) assert hash(redditor2) == hash(redditor2) assert hash(redditor3) == hash(redditor3) assert hash(redditor1) == hash(redditor2) assert hash(redditor2) != hash(redditor3) assert hash(redditor1) != hash(redditor3)
def reddit_activity(username: str): """Requests comment and submission data for the given user and any other users they have interacted with. Interactions between users are when the given user has replied to a submission or comment by another user. Writes data to out/{username}.csv. """ reddit = Reddit(client_id=config.client_id, client_secret=config.client_secret, user_agent=config.user_agent) # Build queue of users to collect activity of: provided user and users they have interacted via replying # Also append users who have interacted with provided user (they replied to a post/comment) users = exactlyOnceQueue() users.append(username) redditor = Redditor(reddit, name=username) for comment in redditor.comments.new(): if comment.parent().author: users.append(str(comment.parent().author)) # Add users who have replied to this comment for reply in comment.replies: if reply.author: users.append(str(reply.author)) for submission in redditor.submissions.new(): # Add users who have replied to this submission for reply in submission.comments: if reply.author: users.append(str(reply.author)) # Get comment and submission data of each user and append to list data = [] print(f'users to process:{str(len(users))}') while len(users) > 0: redditor = Redditor(reddit, name=users.pop()) # type: Redditor try: for submission in redditor.submissions.new(): # type: Submission row = parse_submission(submission) data.append(row) for comment in redditor.comments.new(): # type: Comment row = parse_comment(comment) data.append(row) except exceptions.NotFound: print(f'Exception occured on user {str(redditor)}') except exceptions.Forbidden: print(f'Exception occured on user {str(redditor)}') print(f'user complete, remaining = {str(len(users))}') # Construct DataFrame from list and write to CSV df = pandas.DataFrame(data=data, columns=['id', 'author', 'date', 'subreddit', 'replyingTo', 'polarity', 'subjectivity', 'replyCount']) df.set_index('id', inplace=True) df.to_csv(f'out/{username}.csv')
def test_equality(self): redditor1 = Redditor(self.reddit, _data={"name": "dummy1", "n": 1}) redditor2 = Redditor(self.reddit, _data={"name": "Dummy1", "n": 2}) redditor3 = Redditor(self.reddit, _data={"name": "dummy3", "n": 2}) assert redditor1 == redditor1 assert redditor2 == redditor2 assert redditor3 == redditor3 assert redditor1 == redditor2 assert redditor2 != redditor3 assert redditor1 != redditor3 assert "dummy1" == redditor1 assert redditor2 == "dummy1"
def test_equality(self): redditor1 = Redditor(self.reddit, _data={'name': 'dummy1', 'n': 1}) redditor2 = Redditor(self.reddit, _data={'name': 'Dummy1', 'n': 2}) redditor3 = Redditor(self.reddit, _data={'name': 'dummy3', 'n': 2}) assert redditor1 == redditor1 assert redditor2 == redditor2 assert redditor3 == redditor3 assert redditor1 == redditor2 assert redditor2 != redditor3 assert redditor1 != redditor3 assert 'dummy1' == redditor1 assert redditor2 == 'dummy1'
def test_construct_failure(self): message = "Exactly one of `name`, `fullname`, or `_data` must be provided." with pytest.raises(TypeError) as excinfo: Redditor(self.reddit) assert str(excinfo.value) == message with pytest.raises(TypeError) as excinfo: Redditor(self.reddit, "dummy", _data={"id": "dummy"}) assert str(excinfo.value) == message with pytest.raises(TypeError) as excinfo: Redditor(self.reddit, name="dummy", fullname="t2_dummy") assert str(excinfo.value) == message with pytest.raises(TypeError) as excinfo: Redditor( self.reddit, name="dummy", fullname="t2_dummy", _data={"id": "dummy"}, ) assert str(excinfo.value) == message with pytest.raises(AssertionError): Redditor(self.reddit, _data=[{"name": "dummy"}]) with pytest.raises(AssertionError): Redditor(self.reddit, _data={"notname": "dummy"}) with pytest.raises(ValueError): Redditor(self.reddit, "") with pytest.raises(ValueError): Redditor(self.reddit, fullname="")
def test_construct_failure(self): message = "Either `name` or `_data` must be provided." with pytest.raises(TypeError) as excinfo: Redditor(self.reddit) assert str(excinfo.value) == message with pytest.raises(TypeError) as excinfo: Redditor(self.reddit, "dummy", {"id": "dummy"}) assert str(excinfo.value) == message with pytest.raises(AssertionError): Redditor(self.reddit, _data=[{"name": "dummy"}]) with pytest.raises(AssertionError): Redditor(self.reddit, _data={"notname": "dummy"})
def test_remove_invite__redditor(self): self.reddit.read_only = False thread = LiveThread(self.reddit, "xyu8kmjvfrww") redditor = Redditor(self.reddit, _data={"name": "nmtake", "id": "ll32z"}) with self.recorder.use_cassette( "TestLiveContributorRelationship_test_remove_invite__redditor" ): thread.contributor.remove_invite(redditor)
def test__params_not_modified_in_mixed_listing(self): params = {"dummy": "value"} redditor = Redditor(self.reddit, name="spez") for listing in ["controversial", "hot", "new", "top"]: generator = getattr(redditor, listing)(params=params) assert params == {"dummy": "value"} assert listing == generator.params["sort"] assert "value" == generator.params["dummy"]
def test__params_not_modified_in_mixed_listing(self): params = {'dummy': 'value'} redditor = Redditor(self.reddit, name='spez') for listing in ['controversial', 'hot', 'new', 'top']: generator = getattr(redditor, listing)(params=params) assert params == {'dummy': 'value'} assert listing == generator.params['sort'] assert 'value' == generator.params['dummy']
def test_remove__redditor(self): self.reddit.read_only = False thread = LiveThread(self.reddit, "xyu8kmjvfrww") redditor = Redditor(self.reddit, _data={ "name": "nmtake", "id": "ll32z" }) with self.use_cassette(): thread.contributor.remove(redditor)
def test_remove_invite__redditor(self): self.reddit.read_only = False thread = LiveThread(self.reddit, 'xyu8kmjvfrww') redditor = Redditor(self.reddit, _data={ 'name': 'nmtake', 'id': 'll32z' }) with self.recorder.use_cassette('TestLiveContributorRelationship_' 'test_remove_invite__redditor'): thread.contributor.remove_invite(redditor)
def test_repr(self): redditor = Redditor(self.reddit, name="RedditorName") assert repr(redditor) == "Redditor(name='RedditorName')"
def test_pickle(self): redditor = Redditor(self.reddit, _data={"name": "name", "id": "dummy"}) for level in range(pickle.HIGHEST_PROTOCOL + 1): other = pickle.loads(pickle.dumps(redditor, protocol=level)) assert redditor == other
def CreateRedditor(user_name: str): reddit = praw.Reddit("scraper_tool", user_agent='Python 3.8') return Redditor(reddit, user_name)
def test_guild__max(self): with pytest.raises(TypeError) as excinfo: Redditor(self.reddit, name="RedditorName").gild(37) assert str(excinfo.value) == "months must be between 1 and 36"
def test_fullname(self): redditor = Redditor(self.reddit, _data={"name": "name", "id": "dummy"}) assert redditor.fullname == "t2_dummy"
def test_fullname(self): redditor = Redditor(self.reddit, _data={'name': 'name', 'id': 'dummy'}) assert redditor.fullname == 't2_dummy'
def test_str(self): redditor = Redditor(self.reddit, _data={'name': 'name', 'id': 'dummy'}) assert str(redditor) == 'name'
def test_repr(self): redditor = Redditor(self.reddit, name='RedditorName') assert repr(redditor) == 'Redditor(name=\'RedditorName\')'
def gold(reddit, msg, tx_queue, failover_time): user = models.User(msg.author.name) if user.is_registered(): gold_month = number_gold_credit() if msg.body.strip() == 'buy': # Number of month quantity = 1 # check if we have enough credits if not gold_month >= quantity: # store in db want an gold, when bot have new credits a PM can be send db = TinyDB(config.DATA_PATH + 'reddit_gold_empty.json') db.insert({ "user": user.username, "quantity": quantity, 'time': datetime.datetime.now().isoformat(), }) db.close() msg.reply(Template(lang.message_gold_no_more).render(username=user.username)) return False # check user confirmed balance is ok if user.get_balance_confirmed() >= config.price_reddit_gold: msg.reply(Template(lang.message_gold_no_enough_pivx).render(username=user.username)) return False # send amount of one month of gold to address tx_id = crypto.tip_user(user.address, config.gold_address, config.price_reddit_gold, tx_queue, failover_time) if tx_id: # send gold reddit Redditor(reddit, user.username).gild(months=quantity) # update gold reddit table store_user_buy(user, quantity, tx_id) # update user history models.HistoryStorage.add_to_history(user, sender=user.username, receiver="Reddit", amount=config.price_reddit_gold, action="buy reddit gold") # send succes message msg.reply(Template(lang.message_buy_gold_success).render(username=user.username)) else: # send error message msg.reply(Template(lang.message_buy_gold_error).render(username=user.username)) elif msg.body.strip() == 'remind': # store in db want an gold, when bot have new credits a PM can be send db = TinyDB(config.DATA_PATH + 'reddit_gold_remind.json') db.insert({ "user": user.username, "remind": "True", 'time': datetime.datetime.now().isoformat(), }) db.close() else: # send info on reddit gold msg.reply(Template(lang.message_buy_gold).render(username=user.username, gold_credit=gold_month, price=config.price_reddit_gold)) else: bot_logger.logger.info('user %s not registered (command : donate) ' % user.username) msg.reply(Template(lang.message_need_register + lang.message_footer).render(username=user.username))
def a_user(wsb_reddit_client) -> Redditor: yield Redditor(wsb_reddit_client.reddit, name='WSBTickerBotHandler')
def test_guild__min(self): with pytest.raises(TypeError) as excinfo: Redditor(self.reddit, name='RedditorName').gild(0) assert str(excinfo.value) == 'months must be between 1 and 36'
def test_str(self): redditor = Redditor(self.reddit, _data={"name": "name", "id": "dummy"}) assert str(redditor) == "name"
def test_pickle(self): redditor = Redditor(self.reddit, _data={'name': 'name', 'id': 'dummy'}) for level in range(pickle.HIGHEST_PROTOCOL + 1): other = pickle.loads(pickle.dumps(redditor, protocol=level)) assert redditor == other
async def on_message(message): server = message.server author = message.author name = get_disc_name(author) if message.channel.id == config.VERIFICATION_CHANNEL and message.content.startswith( "!verify"): logging.debug("Verify started by {}".format(name)) arguments = message.content.split(" ") if len(arguments) < 2: await log("No URL from %user%", author) await answer(message, "Usage: !verify <cckufi comment url>") return if name in discord_blacklist: await log( "Blacklisted discord user %user% just tried to register!", author) await answer(message, "Invalid URL!") return raw_url = arguments[1] url = url_fix(raw_url) if not url.startswith(config.POST_URL): await log("Invalid URL from %user%", author) await answer(message, "Invalid URL!") return comment_id = Comment.id_from_url(url) logging.debug("Reading comment {}".format(comment_id)) comment = get_comment(comment_id) if comment is None: await log( "User %user% linked comment {} which does not exist".format( comment_id), author) await answer(message, "that comment does not exist!") return reddit_user = comment.author reddit_name = reddit_user.name if reddit_name in reddit_blacklist: await log( "Blacklisted reddit user {} (discord %user%) just tried to register!" .format(reddit_name), author) await answer( message, "that comment does not just contain your discord ID!") return comment_body = comment.body if comment_body.startswith(name): # Check CoT submissions members, joined, betrayed = analyze_circle_flair( reddit, reddit_user, cot) if not members: await log( "Denied discord user %user% (reddit {}), they haven't posted on CoT" .format(reddit_name), author) await answer( message, "you must have posted or commented on /r/CircleofTrust to be verified." ) return if betrayed: await log( "Denied discord user %user% (reddit {}), they have betrayed!" .format(reddit_name), author) await answer( message, "you have betrayed {} times! Please ask for manual verification." .format(joined)) return await client.change_nickname(author, "/u/{}".format(reddit_name)) await client.add_roles(author, verified_role) await log("Verified %user%, member of {} circles".format(joined), author) await answer(message, "you have been successfully verified!") else: await log( "Comment does not start with their discord ID: {}, got '{}' instead." .format(url, comment_body)) await answer(message, "that comment does not start with your discord ID!") elif message.content.startswith("!flair"): logging.debug("Circle command ran by {}".format(name)) arguments = message.content.split(" ") if len(arguments) < 2: await answer(message, "Usage !circles <reddit user>") return logging.info(arguments[1]) reddit_name = arguments[1] # Check for discord mentions discord_id = re.search(r"<@!(\d{17})>", arguments[1]) if discord_id: discord_user = server.get_member(discord_id.group(1)) logging.info(discord_user) if discord_user: reddit_name = discord_user.name logging.info(reddit_name) reddit_name = re.sub(r"(@?/?u/)", "", reddit_name) reddit_user = Redditor(reddit, name=reddit_name) members, joined, betrayed = analyze_circle_flair( reddit, reddit_user, cot) if not members: await log( "Lookup by %user% for {} failed, they haven't posted on CoT". format(reddit_name), author) await answer(message, "that user has not posted on /r/CircleofTrust.") return await log("Lookup by %user% for {}.".format(reddit_name), author) await answer( message, "Users in circle: {}, member of circles: {}".format( members, joined) + (" BETRAYER!" if betrayed else "")) return