HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Reddit bot to gather listening suggestions

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
botlisteningsuggestionsredditgather

Problem

```
import praw
import time
import database

# Create a user agent and log into Reddit
UA = 'OZBOZZ v0.1'
r = praw.Reddit(UA)
# Log UA into reddit with my personal user name and password
r.login('', '', disable_warning='True')
subreddit = r.get_subreddit('listentothis')
current_time = int(time.time())
user = ''
submission_list = []
results = []
count = 0
genres = ["Big Band Funk", "progressive Rock", "rock", "progressive metal"]

def botstuff():
# while condition is true...
while True:
# Print Working so That I know the Next iteration has begun
print("Working...")

# For submission in subreddit /r/listen to this get up to 20 new posts
for submission in subreddit.get_new(limit=20):
# Create a variable called Sub_age that is current time minus the submission creation time divided by 60
# divided by 60 divided by 12
sub_age = (current_time - submission.created_utc) / 60 / 60 / 24
# If the submission age is less than one (posted in last 24 hours)...
if sub_age < 1:
# Add that submission to the submission list. Each entry contains the title and Link to post
submission_list.append([submission.title, submission.permalink])

# Print the submission list, so I know I got something. Debugging
print(submission_list)

# For each list entry in submission_list
for x in submission_list:
# any of element in the list genre are found in the 1st... element property(?)
if any(a in x[1] for a in genres):
# Append the element with matching genre tags in to the results list.
results.append(x)

# Print the submission list, so I know I got something. Debugging
print(results)

# if the results list is empty
if not results:
# Send the user a message letting them know I havent found something
r.send_message(user, 'Nothing Yet', 'Hey, I didn\'t find anything')
# Otherwise...
else:
# Create a message variable fro

Solution


  1. General issues



r.login ('','', disable_warning=True) #notice True is boolean and not string


if you did disable_warning='False' it would still disable warning. Every non-empty string in python when cast to boolean will return True.

Too many global objects. Reddit API client can stay global r, having current_time as global is horrific. Stuff like submission_list results count genres shouldn't be global either but I can understand that your intent was to get those things from database so this is only temporary. All of those things should be returned by their functions at the moment when they are needed.

You shouldn't use login. It is being depreciated soon. What it means is that if not for the fact that praw devs don't want to break every single bot there is they give you a little time to remove login() from old bots. New bots shouldn't use this at all, because by the time you have it up and working it might be broken again cause login() will no longer be there. Check OAuth2 - it's bit more that single call to login() but still can be done in 2 minutes (and it's one time).

  1. Multithreading



So there are multiple issues with multithreading here. First off you don't need it here. Your bot has only one functionality. Grab data, parse data, give output, repeat. Second one is that multiprocessing/multithreading in python is generally not too common unless there is actual need for that due to massive overhead, that usually makes things slower in the long run. So it would only be feasible if you want your program to extremely long stuff in parallel.

Lastly reddit bot is extremely bad example to try to multithread. The reason are reddit API restrictions. It will not respond to request more often than 0.5 sec. This is how developers have set it and also praw behaves by it. If you make 20 requests you will have to wait 10 seconds no matter how many threads try to do this. This is why it is always done in sequences with those.

  1. Modularity



First off your program should have more functions, just for the sake of readability. Like

def get_submission_list():
    submission_list = []
    for submission in sub.....
    ...
    return submission_list

def send_messsages(results):
   .....

def main_loop():
    while True:
        check_inbox_for_new_bot_subscribers()
        s_list = get_submission_list()
        subscribed_users = get_subscribed_users_and_genres_from_db()
        results = prepare_results(subscribed_users,s_list)
        send_messages(results)


submission_list is kinda obvious, subscribed_users could be a list of tuples or a dictionary with 'username':'genres,separated,by comma' or w/e you feel comfortable working with. results in this case would be (username,[list of links to send to this user])

For the sake of checking multiple users it would be beneficial to also reverse username-genres dictionary, so that we also have fast lookup dictionary of 'genre':[list of usernames subscribed to this genre]'. This would be fun to implement, but benefit is obvious since we have now \$\mathcal{O}(n\cdot{}m)\$ time instead of \$\mathcal{O}(n^2\cdot{}m^2)\$.

Simple database model for this would be:

class BotSubscriber(Model):
    redditor_id = CharField()
    genres = CharField()


You obviously could save other stuff like time of subscription, how many songs you recommended to this user and w/e you feel like.

  1. Clean out output



I believe '\n'.join(map(str,results)).strip('[]') or some variation of that would work as you want?

  1. String search efficiency and better matching



It would be beneficial to parse submission.title before searching. Band - song [genre] (optional year) is pretty much standard format. Move Band - Song and save it as title and [genre] as genre (stripping starting braces as well).

On top of that you definitely use genre.lower() as well as save submitted user genres as lowercase. No need to be case sensitive in this scenario.

Once you have your genre saved as genre you can do any([1 for a in user.genres if a==genre.lower()]) or use reversed dictionary idea that I've posted in 3.

Code Snippets

r.login ('','', disable_warning=True) #notice True is boolean and not string
def get_submission_list():
    submission_list = []
    for submission in sub.....
    ...
    return submission_list

def send_messsages(results):
   .....

def main_loop():
    while True:
        check_inbox_for_new_bot_subscribers()
        s_list = get_submission_list()
        subscribed_users = get_subscribed_users_and_genres_from_db()
        results = prepare_results(subscribed_users,s_list)
        send_messages(results)
class BotSubscriber(Model):
    redditor_id = CharField()
    genres = CharField()

Context

StackExchange Code Review Q#133351, answer score: 6

Revisions (0)

No revisions yet.