2020-01-02 18:09:25 +00:00
|
|
|
import PySimpleGUI as sg
|
|
|
|
import praw # The Reddit APIs
|
|
|
|
from webbrowser import open_new_tab
|
|
|
|
|
|
|
|
"""
|
|
|
|
Demo Reddit Searcher
|
|
|
|
|
2020-11-13 19:47:41 +00:00
|
|
|
Will search through a list of subreddits for a string(s) of your choice. You can search only the
|
2020-01-02 18:09:25 +00:00
|
|
|
posts or the posts and comments. When a match is found the title will be displayed in the window.
|
2020-11-13 19:47:41 +00:00
|
|
|
Two progress meters show the current progress.
|
|
|
|
Once completed, a listbox is populared with he responses. Click on the titles and a brower tab
|
|
|
|
is opened to the topic.
|
2020-01-02 18:09:25 +00:00
|
|
|
|
2020-11-13 19:47:41 +00:00
|
|
|
NOTE - you must register with Reddit as a developer. https://www.reddit.com/prefs/apps/
|
|
|
|
You can set these credentials using the "Settings Window".
|
2020-01-02 18:09:25 +00:00
|
|
|
|
|
|
|
Copyright 2020 PySimpleGUI
|
|
|
|
"""
|
|
|
|
|
2020-11-13 19:47:41 +00:00
|
|
|
settings = sg.UserSettings()
|
|
|
|
|
|
|
|
def make_search_row(item_number):
|
|
|
|
search_layout = [sg.Combo(sorted(settings.get('-search string-', [])), settings['-last search-'], size=(45,1), k=('-SEARCH STRING-', item_number)),
|
|
|
|
# sg.In(key=('-SEARCH STRING-', item_number)),
|
|
|
|
sg.CB('Require', key=('-SEARCH REQUIRED-', item_number))]
|
|
|
|
return search_layout
|
|
|
|
|
|
|
|
def settings_window():
|
|
|
|
def input_line(text, key, default):
|
|
|
|
return [sg.T(text, size=(15,1), justification='r'), sg.In(default, size=(20,1), k=key)]
|
|
|
|
|
|
|
|
layout = [[sg.T('Reddit PRAW Settings', font='default 15')],
|
|
|
|
[sg.T('Note - You must register with Reddit to obtain PRAW credentials')],
|
|
|
|
input_line('Client ID', '-CLIENT ID-', settings['client_id']),
|
|
|
|
input_line('Client Secret', '-CLIENT SECRET-', settings['client_secret']),
|
|
|
|
input_line('User Agent', '-USER AGENT-', settings['user_agent']),
|
|
|
|
input_line('Username', '-USERNAME-', settings['username']),
|
|
|
|
input_line('Password', '-PASSWORD-', settings['password']),
|
|
|
|
[sg.CB('Clear Search History', k='-CLEAR HISTORY-')],
|
|
|
|
]
|
|
|
|
layout += [[sg.Ok(), sg.Cancel()]]
|
|
|
|
|
|
|
|
event, values = sg.Window('Reddit Reader Settings', layout).read(close=True)
|
|
|
|
|
|
|
|
if event == 'Ok':
|
|
|
|
settings['client_id'] = values['-CLIENT ID-']
|
|
|
|
settings['client_secret'] = values['-CLIENT SECRET-']
|
|
|
|
settings['user_agent'] = values['-USER AGENT-']
|
|
|
|
settings['username'] = values['-USERNAME-']
|
|
|
|
settings['password'] = values['-PASSWORD-']
|
|
|
|
if values['-CLEAR HISTORY-']:
|
|
|
|
settings['-search string-'] = []
|
|
|
|
return True
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
while True:
|
|
|
|
# reddit_praw_parameters = settings.get_dict()
|
|
|
|
reddit_praw_parameters = {'client_id' : settings['client_id'], 'client_secret':settings['client_secret'],
|
|
|
|
'user_agent' : settings['user_agent'], 'username' : settings['username'], 'password': settings['password']}
|
|
|
|
try:
|
|
|
|
reddit = praw.Reddit(**reddit_praw_parameters)
|
|
|
|
break
|
|
|
|
except Exception as e:
|
|
|
|
sg.popup('Problem with your settings file', e)
|
|
|
|
if not settings_window():
|
|
|
|
sg.popup_error('Must set settings before can continue')
|
|
|
|
exit()
|
|
|
|
|
|
|
|
# Read your Reddit PRAW configuration from a json file
|
|
|
|
# try:
|
|
|
|
# with open(path.join(path.dirname(__file__), r'praw.cfg'), 'r') as f:
|
|
|
|
# reddit_praw_parameters = load(f)
|
|
|
|
# except:
|
|
|
|
# sg.popup_error('Failed loading the Reddit API login credential file.', 'The File should be named:', path.join(path.dirname(__file__), r'praw.cfg'))
|
|
|
|
# exit()
|
|
|
|
# To use the Reddit APIs you will need to sign up by visiting this site:
|
|
|
|
# https://www.reddit.com/prefs/apps/
|
|
|
|
# You will receive a client_id and client_secret string that you can
|
|
|
|
# enter along with your normal Reddit ID & Password and save into a file named praw.cfg
|
|
|
|
|
|
|
|
sub_names = ('Python', 'learnpython', 'learnprogramming', 'PySimpleGUI', 'madeinpython', 'AskProgramming', 'Coding', 'Programming', 'learnmachinelearning', 'MLQuestions', 'datascience', 'MachineLearning', 'pythontips', 'pystats', 'pythoncoding', 'pythondev', 'scipy')
|
|
|
|
|
|
|
|
sg.theme('Dark Red')
|
|
|
|
num_searches = 1
|
|
|
|
search_layout = [[sg.B('+'), sg.T('Add term')]]
|
|
|
|
search_layout += [make_search_row(i) for i in range(num_searches)]
|
|
|
|
layout = [[sg.Text('Reddit Searcher', font='Any 18')],
|
|
|
|
[sg.Frame('Choose Subs',
|
|
|
|
[[sg.Listbox(sub_names, size=(25, 7), select_mode=sg.SELECT_MODE_MULTIPLE, key='-SUBS-')]]),
|
|
|
|
sg.Frame('Options',
|
|
|
|
[[sg.Checkbox('Look in Comments', True, key='-COMMENTS-')],
|
|
|
|
[sg.Checkbox('Show finds in browser', key='-BROWSER-')],
|
|
|
|
[sg.Checkbox('Show popup', key='-POPUP-')],
|
|
|
|
[sg.Text('Limit: '), sg.Spin(list(range(200, 5000)), size=(4, 1), key='-LIMIT-')]])],
|
|
|
|
[sg.Frame('Search Terms', search_layout, key='-SEARCH FRAME-' )],
|
|
|
|
[sg.Frame('Status',[
|
|
|
|
[sg.Text('Reading Sub:'), sg.Text(size=(25, 1), key='-OUT SUB-')],
|
|
|
|
[sg.Text('Reading Post:'), sg.Text(size=(40, 1), key='-OUT POST-')],
|
|
|
|
[sg.Text('Posts Read:'), sg.Text(size=(25, 1), key='-NUM POSTS-')],
|
|
|
|
[sg.T('Sub Progress', size=(12,1)), sg.ProgressBar(100, orientation='horizontal', size=(30, 20), key='-PROG-')],
|
|
|
|
[sg.T('Overall Progress', size=(12,1)),sg.ProgressBar(100, orientation='horizontal', size=(30, 20), key='-PROG-TOTAL-')],])],
|
|
|
|
[sg.Frame('Results (Click to Lauch in Browser)',
|
|
|
|
[[sg.Listbox([], size=(60,10), key='-LISTBOX-', enable_events=True)]])],
|
|
|
|
[sg.Button('Start Search', bind_return_key=True), sg.B('Settings'), sg.Button('Exit')], ]
|
|
|
|
|
|
|
|
window = sg.Window('Reddit Reader', layout, icon=reddit_icon, use_default_focus=False)
|
|
|
|
|
|
|
|
results = {}
|
|
|
|
while True: # Event Loop
|
|
|
|
event, values = window.read()
|
|
|
|
if event in (None, 'Exit'):
|
|
|
|
break
|
|
|
|
if event == 'Settings':
|
|
|
|
if settings_window():
|
|
|
|
reddit = praw.Reddit(**reddit_praw_parameters)
|
|
|
|
|
|
|
|
subs_to_read = values['-SUBS-']
|
|
|
|
if event.startswith('Start'):
|
|
|
|
window['-LISTBOX-'].update([''])
|
|
|
|
results = {}
|
|
|
|
search_list = [] # make a list of tuples (search term, bool required)
|
|
|
|
for v in values:
|
|
|
|
if isinstance(v, tuple): # if value is a tuple
|
|
|
|
if v[0] == '-SEARCH STRING-':
|
|
|
|
search_list.append((values[v].lower(), values[('-SEARCH REQUIRED-', v[1])]))
|
|
|
|
settings['-search string-'] = list(set(settings.get('-search string-', []) + [values['-SEARCH STRING-', v[1]], ]))
|
|
|
|
settings['-last search-'] = search_list[0][0]
|
|
|
|
print('last search = ', settings['-last search-'])
|
|
|
|
print('Search list = ', search_list)
|
|
|
|
# Loop through the subs
|
|
|
|
for sub_count, sub in enumerate(subs_to_read):
|
|
|
|
window['-OUT SUB-'].update(sub)
|
|
|
|
subreddit = reddit.subreddit(sub)
|
|
|
|
submissions = subreddit.new(limit=int(values['-LIMIT-']))
|
|
|
|
num_submissions = int(values['-LIMIT-'])
|
|
|
|
# Loop through submissions
|
|
|
|
for num, submission in enumerate(submissions):
|
|
|
|
opened = False
|
|
|
|
text = ''.join([t.lower() for t in submission.selftext if ord(t) in range(65536)])
|
|
|
|
window['-PROG-'].update_bar(100 * (num + 1) // num_submissions)
|
|
|
|
title = ''.join([t for t in submission.title if ord(t) in range(65536)])
|
|
|
|
window['-NUM POSTS-'].update(num)
|
|
|
|
window.refresh()
|
|
|
|
found = False
|
|
|
|
for search_item in search_list:
|
|
|
|
if search_item[0] and search_item[0] in text:
|
|
|
|
found = True
|
|
|
|
elif search_item[1]:
|
|
|
|
found = False
|
|
|
|
break
|
|
|
|
if found:
|
|
|
|
opened = True
|
|
|
|
results[title] = submission.url
|
|
|
|
window['-LISTBOX-'].update(list(results.keys()))
|
|
|
|
if values['-BROWSER-']:
|
|
|
|
open_new_tab(submission.url)
|
|
|
|
elif values['-POPUP-']:
|
|
|
|
sg.popup_scrolled(f'Search found', submission.url, f'\nTITLE: {title}', str(text), title=title, non_blocking=True)
|
|
|
|
window['-OUT POST-'].update(str(title))
|
|
|
|
if values['-COMMENTS-']: # if should also search comments
|
|
|
|
for comment in submission.comments:
|
|
|
|
found = False
|
|
|
|
for search_item in search_list:
|
|
|
|
try:
|
|
|
|
if search_item[0] and search_item[0] in comment.body.lower():
|
|
|
|
found = True
|
|
|
|
elif search_item[1]:
|
|
|
|
found = False
|
|
|
|
break
|
|
|
|
except Exception as e:
|
|
|
|
print(f'Exception searching the comments:\n{e}')
|
|
|
|
if found:
|
|
|
|
results[title] = submission.url
|
|
|
|
window['-LISTBOX-'].update(list(results.keys()))
|
|
|
|
comment_text = ''.join([t for t in comment.body if ord(t) in range(65536)])
|
|
|
|
if values['-BROWSER-'] and not opened:
|
2020-01-02 18:09:25 +00:00
|
|
|
open_new_tab(submission.url)
|
|
|
|
opened = True
|
2020-11-13 19:47:41 +00:00
|
|
|
elif values['-POPUP-']:
|
|
|
|
sg.popup_scrolled(f'Search found in comment', submission.url, f'\nTITLE: {title}', comment_text, title=title, non_blocking=True)
|
|
|
|
window.refresh()
|
|
|
|
event, values = window.read(timeout=0)
|
|
|
|
if event == '-LISTBOX-': # experimental - see if clicked on an item in the list while it's still being built
|
|
|
|
url = results.get(values['-LISTBOX-'][0])
|
|
|
|
if url: open_new_tab(url)
|
|
|
|
if event in (None, 'Exit'):
|
|
|
|
break
|
|
|
|
# Done processing the single sub, so update the total progress bar
|
|
|
|
window['-PROG-TOTAL-'].update_bar(100 * (sub_count + 1) // len(subs_to_read))
|
|
|
|
if event in (None, 'Exit'):
|
|
|
|
window['-OUT SUB-'].update('*** Aborted ***')
|
2020-01-02 18:09:25 +00:00
|
|
|
break
|
2020-11-13 19:47:41 +00:00
|
|
|
else:
|
|
|
|
window['-OUT SUB-'].update('*** Done with this sub ***')
|
|
|
|
else: # if made it through the loop, then show a popup saying completed
|
|
|
|
window['-OUT SUB-'].update('*** DONE with all subs ***')
|
|
|
|
if event is None:
|
2020-01-02 18:09:25 +00:00
|
|
|
break
|
2020-11-13 19:47:41 +00:00
|
|
|
if event == '+':
|
|
|
|
window.extend_layout(window['-SEARCH FRAME-'], [make_search_row(num_searches)])
|
|
|
|
num_searches += 1
|
|
|
|
if event == '-LISTBOX-':
|
|
|
|
url = results.get(values['-LISTBOX-'][0])
|
|
|
|
if url: open_new_tab(url)
|
|
|
|
|
|
|
|
|
|
|
|
window.close()
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
reddit_icon = b'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'
|
2020-01-02 18:09:25 +00:00
|
|
|
|
2020-11-13 19:47:41 +00:00
|
|
|
main()
|