From dc5139849271b4e4d66899f39902fbe5408d6de6 Mon Sep 17 00:00:00 2001 From: Collin R Date: Fri, 7 Feb 2020 21:31:52 -0800 Subject: [PATCH] Allowed mods to comment /solved in any scenario --- docs/TESTS.md | 5 +++ docs/TODO.md | 9 ++-- pointsbot/bot.py | 104 +++++++++++++++++++------------------------- tests/context.py | 3 +- tests/test_reply.py | 19 ++++++++ 5 files changed, 77 insertions(+), 63 deletions(-) create mode 100644 docs/TESTS.md diff --git a/docs/TESTS.md b/docs/TESTS.md new file mode 100644 index 0000000..5a33193 --- /dev/null +++ b/docs/TESTS.md @@ -0,0 +1,5 @@ +# TESTS + +## End-to-end Testing Scenarios + + diff --git a/docs/TODO.md b/docs/TODO.md index 9cb661d..fd05e97 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -20,6 +20,7 @@ - https://developer.github.com/v3/repos/contents/#get-archive-link - https://developer.github.com/v3/repos/commits/ - https://developer.github.com/v3/repos/releases/ + - https://developer.github.com/v3/#timezones - Webhooks: - https://developer.github.com/webhooks/ - https://developer.github.com/v3/repos/hooks/ @@ -40,6 +41,7 @@ ### bot.py +* [ ] Allow mods to use "/[Ss]olved" in any context * [ ] Allow mods and/or bot owner to add or remove points from specific users * [ ] Make the algorithm for determining the problem solver more sophisticated - e.g. check entire comment tree instead of just ignoring if the OP also @@ -79,18 +81,19 @@ ### database.py +* [ ] Store date for each "!solved" comment * [ ] Possibly refactor for a datastore type thing instead of database - Maybe even make models like Redditor to combine data storage/access with logic, e.g. determining current level ### reply.py -* [ ] For the footer section of the reply comment regarding the bot, could have the +* [X] Fix progress bar +* ~~For the footer section of the reply comment regarding the bot, could have the bot make a post on its account explaining itself, and link to that (and then also link to the source code separately, and perhaps in that post, too). Could even have the bot make this post automatically if it doesn't have a - link to the post in its config, and then store the link for future use. -* [X] Fix progress bar + link to the post in its config, and then store the link for future use.~~ ## Ideas diff --git a/pointsbot/bot.py b/pointsbot/bot.py index 8f3a20f..d37704e 100644 --- a/pointsbot/bot.py +++ b/pointsbot/bot.py @@ -13,8 +13,6 @@ USER_AGENT = 'PointsBot (by u/GlipGlorp7)' SOLVED_PAT = re.compile('![Ss]olved') MOD_SOLVED_PAT = re.compile('/[Ss]olved') -TEST_COMMENTS = False - ### Main Function ### @@ -35,10 +33,6 @@ def run(): is_mod = bool(subreddit.moderator(redditor=reddit.user.me())) print_level(1, f'Is mod? {is_mod}') - if TEST_COMMENTS: - make_comments(subreddit, levels) - return - db = database.Database(cfg.database_path) # Monitor new comments for confirmed solutions @@ -51,43 +45,46 @@ def run(): print_level(0, '\nFound comment') print_level(1, f'Comment text: "{comm.body}"') - if marks_as_solved(comm): - if not is_first_solution(comm): - # Skip this "!solved" comment and wait for the next - print_level(1, 'Not the first solution') - continue + if not marks_as_solved(comm): + print_level(1, 'Not a "![Ss]olved" comment') + continue - print_level(1, 'This is the first solution') - print_solution_info(comm) + if is_mod_comment(comm): + print_level(1, 'Mod comment') + elif not is_first_solution(comm): + # Skip this "!solved" comment and wait for the next + print_level(1, 'Not the first solution') + continue - solver = comm.parent().author - print_level(1, f'Adding point for {solver.name}') - db.add_point(solver) - points = db.get_points(solver) - print_level(1, f'Points for {solver.name}: {points}') + print_level(1, 'This is the first solution found') + print_solution_info(comm) - level_info = level.user_level_info(points, levels) + solver = find_solver(comm) + db.add_point(solver) + print_level(1, f'Added point for {solver.name}') - # Reply to the comment marking the submission as solved - reply_body = reply.make(solver, points, level_info) - print_level(1, f'Replying with: "{reply_body}"') - comm.reply(reply_body) + points = db.get_points(solver) + print_level(1, f'Total points for {solver.name}: {points}') + level_info = level.user_level_info(points, levels) - # Check if (non-mod) user flair should be updated to new level - lvl = level_info.current - if lvl and lvl.points == points: - print_level(1, f'User reached level: {lvl.name}') - if not subreddit.moderator(redditor=solver): - print_level(2, 'Setting flair') - print_level(3, f'Flair text: {lvl.name}') - print_level(3, f'Flair template ID: {lvl.flair_template_id}') - subreddit.flair.set(solver, - text=lvl.name, - flair_template_id=lvl.flair_template_id) - else: - print_level(2, 'Solver is mod; don\'t alter flair') - else: - print_level(1, 'Not a "!solved" comment') + # Reply to the comment marking the submission as solved + reply_body = reply.make(solver, points, level_info) + comm.reply(reply_body) + print_level(1, f'Replied to comment with: "{reply_body}"') + + # Check if (non-mod) user flair should be updated to new level + lvl = level_info.current + if lvl and lvl.points == points: + print_level(1, f'User reached level: {lvl.name}') + if not subreddit.moderator(redditor=solver): + print_level(2, 'Setting flair') + print_level(3, f'Flair text: {lvl.name}') + print_level(3, f'Flair template ID: {lvl.flair_template_id}') + subreddit.flair.set(solver, + text=lvl.name, + flair_template_id=lvl.flair_template_id) + else: + print_level(2, 'Solver is mod; don\'t alter flair') ### Reddit Comment Functions ### @@ -110,9 +107,13 @@ def marks_as_solved(comment): return op_resp_to_solver or mod_resp_to_solver +def is_mod_comment(comment): + return comment.subreddit.moderator(redditor=comment.author) + + def is_first_solution(solved_comment): - # Retrieve any comments hidden by "more comments" - # Passing limit=0 will replace all "more comments" + '''Return True if this solved comment is the first, False otherwise.''' + # Retrieve any comments hidden by "more comments" by passing limit=0 submission = solved_comment.submission submission.comments.replace_more(limit=0) @@ -127,6 +128,10 @@ def is_first_solution(solved_comment): return True +def find_solver(solved_comment): + return solved_comment.parent().author + + ### Debugging & Logging ### @@ -144,22 +149,3 @@ def print_solution_info(comm): print_level(3, f'Body: {comm.body}') -def make_comments(subreddit, levels): - testpoints = [1, 3, 5, 10, 15, 30, 45, 75] + list(range(100, 551, 50)) - - for sub in subreddit.new(): - if sub.title == 'Testing comment scenarios': - redditor = sub.author - for points in testpoints: - body = f'Solver: {redditor}\n\nTotal points after solving: {points}' - print_level(0, body) - comm = sub.reply(body) - if comm: - level_info = level.user_level_info(points, levels) - body = reply.make(redditor, points, level_info) - comm.reply(body) - else: - print_level(1, 'ERROR: Unable to comment') - break - - diff --git a/tests/context.py b/tests/context.py index 371019c..60f4a52 100644 --- a/tests/context.py +++ b/tests/context.py @@ -1,5 +1,6 @@ -from os.path import abspath, dirname, join import sys +from os.path import abspath, dirname, join + sys.path.insert(0, abspath(join(dirname(__file__), '..'))) import pointsbot diff --git a/tests/test_reply.py b/tests/test_reply.py index b3f206f..d6e3ef9 100644 --- a/tests/test_reply.py +++ b/tests/test_reply.py @@ -13,6 +13,25 @@ def leftpad(msg, num_indents=1): return '\n'.join([('\t' * num_indents + l) for l in msg.split('\n')]) +def make_comments(subreddit, levels): + testpoints = [1, 3, 5, 10, 15, 30, 45, 75] + list(range(100, 551, 50)) + + for sub in subreddit.new(): + if sub.title == 'Testing comment scenarios': + redditor = sub.author + for points in testpoints: + body = f'Solver: {redditor}\n\nTotal points after solving: {points}' + print_level(0, body) + comm = sub.reply(body) + if comm: + level_info = level.user_level_info(points, levels) + body = reply.make(redditor, points, level_info) + comm.reply(body) + else: + print_level(1, 'ERROR: Unable to comment') + break + + ### Tests ### levels = [