Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for blacklisting a user network-wide #13994

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
9 changes: 8 additions & 1 deletion apigetpost.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def __init__(self):
self.owner_url = None
self.owner_name = None
self.owner_rep = None
self.owner_acct_id = None
self.title = None
self.body = None
self.body_markdown = None
Expand All @@ -33,7 +34,12 @@ def as_dict(self):
'title': self.title,
'body': self.body,
'body_markdown': self.body_markdown,
'owner': {'display_name': self.owner_name, 'link': self.owner_url, 'reputation': self.owner_rep},
'owner': {
'display_name': self.owner_name,
'link': self.owner_url,
'reputation': self.owner_rep,
'account_id': self.owner_acct_id
},
'site': self.site,
'question_id': self.post_id,
'link': self.post_url,
Expand Down Expand Up @@ -82,6 +88,7 @@ def api_get_post(post_url):
post_data.owner_name = html.unescape(item['owner']['display_name'])
post_data.owner_url = item['owner']['link']
post_data.owner_rep = item['owner']['reputation']
post_data.owner_acct_id = item['owner']['account_id']
else:
post_data.owner_name = ""
post_data.owner_url = ""
Expand Down
66 changes: 61 additions & 5 deletions chatcommands.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,56 @@ def addblu(msg, user):
message_url = "https://chat.{}/transcript/{}?m={}".format(msg._client.host, msg.room.id, msg.id)

add_blacklisted_user((uid, val), message_url, "")
return "User blacklisted (`{}` on `{}`).".format(uid, val)
return f"User blacklisted {'network-wide ' if val == 'stackexchange.com' else ''}(`{uid}` on `{val}`)."
elif int(uid) == -2:
raise CmdException("Error: {}".format(val))
else:
raise CmdException("Invalid format. Valid format: `!!/addblu profileurl` *or* `!!/addblu userid sitename`.")


def user_to_net_acct_id(user: tuple[str, str]) -> str:
"""Find the network account ID of a user (e.g., a site profile)"""
uid, val = user

# SE user passed
if val == "stackexchange.com":
return uid
# network user passed
else:
# based on the allspam code
with GlobalVars.api_request_lock:
if GlobalVars.api_backoff_time > time.time():
time.sleep(GlobalVars.api_backoff_time - time.time() + 2)
request_url = get_se_api_url_for_route(f"users/{uid}")
params = get_se_api_default_params({
'filter': '!)scNT2zLcbOpD09vBxHM',
'site': val
})
res = requests.get(request_url, params=params, timeout=GlobalVars.default_requests_timeout).json()
if "backoff" in res:
if GlobalVars.api_backoff_time < time.time() + res["backoff"]:
GlobalVars.api_backoff_time = time.time() + res["backoff"]
if 'items' not in res or len(res['items']) == 0:
raise CmdException("The specified user does not appear to exist.")
return str(res['items'][0]['account_id'])


def is_user_net_blacklisted(user: tuple[str, str]) -> bool:
"""Given a user ID and a site, return if they are network-blacklisted"""
return (user_to_net_acct_id(user), 'stackexchange.com') in GlobalVars.blacklisted_users


@command(str, whole_msg=True, privileged=True)
def addnetblu(_msg, user):
"""
Provides directions to blacklist a user network-wide
:return: A string
"""
net_id: str = user_to_net_acct_id(get_user_from_list_command(user))

return f"`Run !!/addblu https://stackexchange.com/users/{net_id}`"


# noinspection PyIncorrectDocstring,PyMissingTypeHints
@command(str)
def isblu(user):
Expand All @@ -90,10 +133,22 @@ def isblu(user):
uid, val = get_user_from_list_command(user)

if int(uid) > -1 and val != "":
if is_blacklisted_user((uid, val)):
return "User is blacklisted (`{}` on `{}`).".format(uid, val)
is_site_wide: bool = is_blacklisted_user((uid, val))
is_network_wide: bool = is_user_net_blacklisted((uid, val))
# It could check what sites a user is blacklisted on, even if that
# site isn't passed. That functionality could be useful, but I didn't
# write the code to do that.

if is_site_wide and is_network_wide:
return f"User is blacklisted both on `{val}` (`{uid}`) and network-wide"
elif is_site_wide and not is_network_wide:
return f"User is blacklisted on `{val}` (`{uid}`), but not network-wide"
elif not is_site_wide and is_network_wide:
return f"User is blacklisted network-wide, but not on `{val}` (`{uid}`)"
elif not is_site_wide and not is_network_wide:
return f"User is neither blacklisted on `{val}` (`{uid}`) nor network-wide"
else:
return "User is not blacklisted (`{}` on `{}`).".format(uid, val)
return "This should never be reached"
elif int(uid) == -2:
raise CmdException("Error: {}".format(val))
else:
Expand All @@ -111,8 +166,9 @@ def rmblu(user):
uid, val = get_user_from_list_command(user)

if int(uid) > -1 and val != "":
net_wide: str = "network-wide " if (val == "stackexchange.com") else ""
if remove_blacklisted_user((uid, val)):
return "The user has been removed from the user-blacklist (`{}` on `{}`).".format(uid, val)
return "The user has been removed from the {}user-blacklist (`{}` on `{}`).".format(net_wide, uid, val)
else:
return "The user is not blacklisted. Perhaps they have already been removed from the blacklist. Please " \
"see: [Blacklists, watchlists, and the user-whitelist: User-blacklist and user-whitelist]" \
Expand Down
9 changes: 7 additions & 2 deletions classes/_Post.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import json
from helpers import log
import html
from typing import AnyStr, Union
from typing import AnyStr, Union, Optional


class PostParseError(Exception):
Expand Down Expand Up @@ -31,6 +31,7 @@ def __init__(self, json_data=None, api_response=None, parent=None):
self._user_url = ""
self._votes = {'downvotes': None, 'upvotes': None}
self._edited = False
self._owner_account_id = None

if parent is not None:
if not isinstance(parent, Post):
Expand Down Expand Up @@ -143,7 +144,8 @@ def _parse_api_post(self, response):
'owner': {
'display_name': '_user_name',
'link': '_user_url',
'reputation': '_owner_rep'
'reputation': '_owner_rep',
'account_id': '_owner_account_id'
},
'question_id': '_post_id',
'answer_id': '_post_id',
Expand Down Expand Up @@ -221,6 +223,9 @@ def post_id(self):
def post_score(self):
return int(self._post_score)

def get_account_id(self) -> Optional[int]:
return int(self._owner_account_id) if self._owner_account_id is not None else None

@property
def post_site(self):
if type(self._post_site) in [bytes, bytearray]:
Expand Down
2 changes: 1 addition & 1 deletion globalvars.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ class GlobalVars:
# accurately from the correct route. However, for scanning posts, we also want the question data associated
# with an answer.
se_api_question_answer_post_filter = \
"!scxINmJWAnjuckNJkP9YfbG196DrCj)DDXQjRKUGfp-QzZ3b1nRh1Qitw7)FdyW_)iLrL8TBxFYZ1"
"!*b8DH81UdVw)kQyIDQMTMc9jrRZh7*x*46g(fPTyFh)C2d3Fp9kSnnFDHyiG5L3cG39qg66pgP63xLa74Uq"
se_api_url_base = "https://api.stackexchange.com/2.4/"
se_api_default_params = {
'key': 'IAkbitmze4B8KpacUfLqkw((',
Expand Down
13 changes: 12 additions & 1 deletion spamhandling.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,19 @@ def sum_weight(reasons: list):
return s


def is_post_owner_net_blacklisted(post: Post) -> bool:
"""Check if the owner of a post is network-wide blacklisted"""
for acct_id, site in GlobalVars.blacklisted_users.keys():
if int(acct_id) == post.get_account_id() and site == "stackexchange.com":
return True
return False


# noinspection PyMissingTypeHints
def check_if_spam(post, dont_ignore_for=None):
test, why = findspam.FindSpam.test_post(post)
if datahandling.is_blacklisted_user(parsing.get_user_from_url(post.user_url)):
net_blacklisted: bool = is_post_owner_net_blacklisted(post)
if datahandling.is_blacklisted_user(parsing.get_user_from_url(post.user_url)) or net_blacklisted:
test.append("blacklisted user")
blacklisted_user_data = datahandling.get_blacklisted_user_data(parsing.get_user_from_url(post.user_url))
if len(blacklisted_user_data) > 1:
Expand All @@ -62,6 +71,8 @@ def check_if_spam(post, dont_ignore_for=None):
ms_url = datahandling.resolve_ms_link(rel_url) or to_metasmoke_link(rel_url)
why += "\nBlacklisted user - blacklisted for {} ({}) by {}".format(
blacklisted_post_url, ms_url, blacklisted_by)
if net_blacklisted:
why += "\nNetwork-wide blacklisted user"
CoconutMacaroon marked this conversation as resolved.
Show resolved Hide resolved
else:
why += "\n" + u"Blacklisted user - blacklisted by {}".format(blacklisted_by)
if test:
Expand Down
12 changes: 6 additions & 6 deletions test/test_chatcommands.py
Original file line number Diff line number Diff line change
Expand Up @@ -589,16 +589,16 @@ def test_blacklisted_users():

# Format: !!/*blu profileurl
assert chatcommands.isblu("https://stackoverflow.com/users/4622463/angussidney", original_msg=msg) == \
"User is not blacklisted (`4622463` on `stackoverflow.com`)."
"User is neither blacklisted on `stackoverflow.com` (`4622463`) nor network-wide"
assert chatcommands.addblu("https://stackoverflow.com/users/4622463/angussidney", original_msg=msg) == \
"User blacklisted (`4622463` on `stackoverflow.com`)."
# TODO: Edit command to check and not blacklist again, add test
assert chatcommands.isblu("https://stackoverflow.com/users/4622463/angussidney", original_msg=msg) == \
"User is blacklisted (`4622463` on `stackoverflow.com`)."
"User is blacklisted on `stackoverflow.com` (`4622463`), but not network-wide"
assert chatcommands.rmblu("https://stackoverflow.com/users/4622463/angussidney", original_msg=msg) == \
"The user has been removed from the user-blacklist (`4622463` on `stackoverflow.com`)."
assert chatcommands.isblu("https://stackoverflow.com/users/4622463/angussidney", original_msg=msg) == \
"User is not blacklisted (`4622463` on `stackoverflow.com`)."
"User is neither blacklisted on `stackoverflow.com` (`4622463`) nor network-wide"
assert chatcommands.rmblu("https://stackoverflow.com/users/4622463/angussidney", original_msg=msg) == \
"The user is not blacklisted. Perhaps they have already been removed from the blacklist. Please " \
"see: [Blacklists, watchlists, and the user-whitelist: User-blacklist and user-whitelist]" \
Expand All @@ -608,16 +608,16 @@ def test_blacklisted_users():

# Format: !!/*blu userid sitename
assert chatcommands.isblu("4622463 stackoverflow", original_msg=msg) == \
"User is not blacklisted (`4622463` on `stackoverflow.com`)."
"User is neither blacklisted on `stackoverflow.com` (`4622463`) nor network-wide"
assert chatcommands.addblu("4622463 stackoverflow", original_msg=msg) == \
"User blacklisted (`4622463` on `stackoverflow.com`)."
# TODO: Add test here as well
assert chatcommands.isblu("4622463 stackoverflow", original_msg=msg) == \
"User is blacklisted (`4622463` on `stackoverflow.com`)."
"User is blacklisted on `stackoverflow.com` (`4622463`), but not network-wide"
assert chatcommands.rmblu("4622463 stackoverflow", original_msg=msg) == \
"The user has been removed from the user-blacklist (`4622463` on `stackoverflow.com`)."
assert chatcommands.isblu("4622463 stackoverflow", original_msg=msg) == \
"User is not blacklisted (`4622463` on `stackoverflow.com`)."
"User is neither blacklisted on `stackoverflow.com` (`4622463`) nor network-wide"
assert chatcommands.rmblu("4622463 stackoverflow", original_msg=msg) == \
"The user is not blacklisted. Perhaps they have already been removed from the blacklist. Please " \
"see: [Blacklists, watchlists, and the user-whitelist: User-blacklist and user-whitelist]" \
Expand Down
Loading