From 218f4b599c8a15e70396d7287f1635fce8894efe Mon Sep 17 00:00:00 2001
From: user12986714 <65436504+user12986714@users.noreply.github.com>
Date: Tue, 26 May 2020 15:06:06 -0400
Subject: [PATCH] Minor fixes

---
 chatcommands.py           | 55 ++++++++++++++++++++++++++++-----------
 test/test_chatcommands.py | 29 ++++++++++++++-------
 2 files changed, 59 insertions(+), 25 deletions(-)

diff --git a/chatcommands.py b/chatcommands.py
index 8382d6fd13e..a958faac180 100644
--- a/chatcommands.py
+++ b/chatcommands.py
@@ -1705,7 +1705,7 @@ def allspam(msg, url, alias_used="allspam"):
         if 'items' not in res or len(res['items']) == 0:
             raise CmdException("The specified user does not appear to exist.")
         if res['has_more']:
-            # Too many posts
+            # Too many accounts
             raise CmdException("The specified user has an abnormally high number of accounts. Please consider flagging "
                                "for moderator attention, otherwise use !!/report on the user's posts individually.")
         # Add accounts with posts
@@ -1714,7 +1714,6 @@ def allspam(msg, url, alias_used="allspam"):
                 user_sites.append((site['user_id'], get_api_sitename_from_url(site['site_url'])))
     else:
         user_sites.append((user[0], get_api_sitename_from_url(user[1])))
-    total_posts = 0  # Tracking total amount of posts by that user
     # Fetch posts
     for u_id, u_site in user_sites:
         # Respect backoffs etc
@@ -1764,14 +1763,42 @@ def allspam(msg, url, alias_used="allspam"):
             post_data.score = post['score']
             post_data.up_vote_count = post['up_vote_count']
             post_data.down_vote_count = post['down_vote_count']
-            # Report that post
-            current_output = report_post(post_data, msg.owner, msg.room.name, message_url, operation, custom_reason)
-            if current_output:
-                output.append(current_output)
-            total_posts += 1
-    if total_posts == 0:
+            if post_data.post_type == "answer":
+                # Annoyingly we have to make another request to get the question ID, since it is only returned by the
+                # /answers route
+                # Respect backoffs etc
+                GlobalVars.api_request_lock.acquire()
+                if GlobalVars.api_backoff_time > time.time():
+                    time.sleep(GlobalVars.api_backoff_time - time.time() + 2)
+                # Fetch posts
+                req_url = "https://api.stackexchange.com/2.2/answers/{}".format(post['post_id'])
+                params = {
+                    'filter': '!*Jxb9s5EOrE51WK*',
+                    'key': api_key,
+                    'site': u_site
+                }
+                answer_res = requests.get(req_url, params=params).json()
+                if "backoff" in answer_res:
+                    if GlobalVars.api_backoff_time < time.time() + answer_res["backoff"]:
+                        GlobalVars.api_backoff_time = time.time() + answer_res["backoff"]
+                GlobalVars.api_request_lock.release()
+                # Finally, set the attribute
+                post_data.question_id = answer_res['items'][0]['question_id']
+                post_data.is_answer = True
+            # Add the post to report queue
+            user_posts.append(post_data)
+    if len(user_posts) == 0:
         raise CmdException("The user has no post yet.")
-    if total_posts > 2:
+    if len(user_posts) > 15:
+        raise CmdException("The specified user has an abnormally high number of spam posts. Please consider flagging "
+                           "for moderator attention, otherwise use !!/report on the posts individually.")
+    # Report posts
+    for current_post_data in user_posts:
+        # Report that post
+        current_output = report_post(current_post_data, msg.owner, msg.room.name, message_url, operation, custom_reason)
+        if current_output:
+            output.append(current_output)
+    if len(user_posts) > 2:
         add_or_update_multiple_reporter(msg.owner.id, msg._client.host, time.time())
     if len(output):
         return "\n".join(output)
@@ -1806,7 +1833,7 @@ def report_post(post_data, reported_by, reported_in=None, blacklist_by=None, ope
     abs_url = post_data.post_url
     url = to_protocol_relative(abs_url)
 
-    if have_already_been_posted(post_data.site, post_data.post_id, post_data.title) and not is_forced:
+    if has_already_been_posted(post_data.site, post_data.post_id, post_data.title) and not is_forced:
         # Recently reported and is not forced
         if GlobalVars.metasmoke_key is not None:
             ms_link = resolve_ms_link(url) or to_metasmoke_link(url)
@@ -1859,7 +1886,7 @@ def report_post(post_data, reported_by, reported_in=None, blacklist_by=None, ope
         else:
             processed_why = report_info + "This post would not have been caught otherwise."
         handle_spam(post=post,  # Reported posts are always treated as spam.
-                    reasons=["Manually reported" + post_data.post_type],
+                    reasons=["Manually reported " + post_data.post_type],
                     why=processed_why)
         if custom_reason:
             Tasks.later(Metasmoke.post_auto_comment, custom_reason, reported_by, url=url, after=15)
@@ -1870,12 +1897,10 @@ def report_post(post_data, reported_by, reported_in=None, blacklist_by=None, ope
     # Also the operation must be either "scan" or "scan-force"
     if scan_why:
         # Post is ignored
-        output.append("[Post]({}): Looks like spam but is ignored.".format(abs_url))
-        return None
+        return "[Post]({}): Looks like spam but is ignored.".format(abs_url)
     else:
         # Is not spam
-        output.append("[Post]({}): Does not look like spam.".format(abs_url))
-        return None
+        return "[Post]({}): Does not look like spam.".format(abs_url)
 
 
 @command(str, str, privileged=True, whole_msg=True)
diff --git a/test/test_chatcommands.py b/test/test_chatcommands.py
index 2f51ded9104..f522e8f1b6c 100644
--- a/test/test_chatcommands.py
+++ b/test/test_chatcommands.py
@@ -289,7 +289,7 @@ def test_report(handle_spam):
             "id": 1337
         })
 
-        assert chatcommands.report("test", original_msg=msg, alias_used="report") == "Post 1: That does not look like a valid post URL."
+        assert chatcommands.report("test", original_msg=msg, alias_used="report") == "[Post](test): Invalid url."
 
         assert chatcommands.report("one two three four five plus-an-extra", original_msg=msg, alias_used="report") == (
             "To avoid SmokeDetector reporting posts too slowly, you can report at most 5 posts at a time. This is to avoid "
@@ -300,11 +300,11 @@ def test_report(handle_spam):
         #     .startswith("You cannot provide multiple custom report reasons.")
 
         assert chatcommands.report('https://stackoverflow.com/q/1', original_msg=msg) == \
-            "Post 1: Could not find data for this post in the API. It may already have been deleted."
+            "[Post](https://stackoverflow.com/q/1): No data fetched from API. It may have been deleted."
 
         # Valid post
         assert chatcommands.report('https://stackoverflow.com/a/1732454', original_msg=msg, alias_used="scan") == \
-            "Post 1: This does not look like spam"
+            "[Post](https://stackoverflow.com/a/1732454): Does not look like spam."
         assert chatcommands.report('https://stackoverflow.com/a/1732454 "~o.O~"', original_msg=msg, alias_used="report") is None
 
         _, call = handle_spam.call_args_list[-1]
@@ -338,7 +338,7 @@ def test_report(handle_spam):
 
         # Don't re-report
         GlobalVars.latest_questions = [('stackoverflow.com', '1732454', 'RegEx match open tags except XHTML self-contained tags')]
-        assert chatcommands.report('https://stackoverflow.com/a/1732454', original_msg=msg).startswith("Post 1: Already recently reported")
+        assert chatcommands.report('https://stackoverflow.com/a/1732454', original_msg=msg).startswith("[Post](https://stackoverflow.com/a/1732454): Already recently reported")
 
         # Can use report command multiple times in 30s if only one URL was used
         assert chatcommands.report('https://stackoverflow.com/q/1732348', original_msg=msg, alias_used="report") is None
@@ -369,7 +369,7 @@ def test_allspam(handle_spam):
             "id": 1337
         })
 
-        assert chatcommands.allspam("test", original_msg=msg) == "That doesn't look like a valid user URL."
+        assert chatcommands.allspam("test", original_msg=msg) == "[User](test): Invalid url."
 
         # If this code lasts long enough to fail, I'll be happy
         assert chatcommands.allspam("https://stackexchange.com/users/10000000000", original_msg=msg) == \
@@ -391,10 +391,10 @@ def test_allspam(handle_spam):
         )
 
         assert chatcommands.allspam("https://stackexchange.com/users/12108751", original_msg=msg) == \
-            "The specified user hasn't posted anything."
+            "The user has no post yet."
 
         assert chatcommands.allspam("https://stackoverflow.com/users/8846458", original_msg=msg) == \
-            "The specified user has no posts on this site."
+            "The user has no post yet."
 
         # This test is for users with <100rep but >15 posts
         # If this breaks in the future because the below user eventually gets 100 rep (highly unlikely), use the following
@@ -412,16 +412,25 @@ def test_allspam(handle_spam):
         _, call = handle_spam.call_args_list[0]
         assert isinstance(call["post"], Post)
         assert call["reasons"] == ["Manually reported answer"]
-        assert call["why"] == "User manually reported by *ArtOfCode* in room *Charcoal HQ*.\n"
+        assert call["why"].startswith(
+            "Post manually reported by user *ArtOfCode* in room *Charcoal HQ*."
+            "\n\nThis post would not have been caught otherwise."
+        )
 
         handle_spam.reset_mock()
+
         assert chatcommands.allspam("https://meta.stackexchange.com/users/373807", original_msg=msg) is None
 
         assert handle_spam.call_count == 1
         _, call = handle_spam.call_args_list[0]
         assert isinstance(call["post"], Post)
-        assert call["reasons"] == ["Manually reported answer"]
-        assert call["why"] == "User manually reported by *ArtOfCode* in room *Charcoal HQ*.\n"
+        # We expect "blacklisted user" here, as the blacklist is dumped to a pickle
+        # Hence when the pickle is loaded again, the user is blacklisted again
+        assert call["reasons"] == ["blacklisted user"]
+        assert call["why"].startswith(
+            "Post manually reported by user *ArtOfCode* in room *Charcoal HQ*."
+            "\n\nThis post would have also been caught for:"
+        )
 
     finally:
         GlobalVars.blacklisted_users.clear()