diff --git a/app/app/context.py b/app/app/context.py index b99a21b908d..8465cb8ad43 100644 --- a/app/app/context.py +++ b/app/app/context.py @@ -131,7 +131,9 @@ def preprocess(request): 'github_handle': request.user.username if user_is_authenticated else False, 'email': request.user.email if user_is_authenticated else False, 'name': request.user.get_full_name() if user_is_authenticated else False, - 'last_chat_status': request.user.profile.last_chat_status if (hasattr(request.user, 'profile') and user_is_authenticated) else False, + 'last_chat_status': + request.user.profile.last_chat_status if + (hasattr(request.user, 'profile') and user_is_authenticated) else False, 'raven_js_version': settings.RAVEN_JS_VERSION, 'raven_js_dsn': settings.SENTRY_JS_DSN, 'release': settings.RELEASE, diff --git a/app/app/utils.py b/app/app/utils.py index 98343e20f37..aa568dd2e84 100644 --- a/app/app/utils.py +++ b/app/app/utils.py @@ -1,6 +1,8 @@ import email +import functools import imaplib import logging +import multiprocessing.pool import os import re import time @@ -490,3 +492,22 @@ def get_profiles_from_text(text): username_pattern = re.compile(r'@(\S+)') mentioned_usernames = re.findall(username_pattern, text) return Profile.objects.filter(handle__in=mentioned_usernames).distinct() + + +def timeout(max_timeout): + """Timeout decorator, parameter in seconds.""" + + def timeout_decorator(item): + """Wrap the original function.""" + + @functools.wraps(item) + def func_wrapper(*args, **kwargs): + """Closure for function.""" + pool = multiprocessing.pool.ThreadPool(processes=1) + async_result = pool.apply_async(item, args, kwargs) + # raises a TimeoutError if execution exceeds max_timeout + return async_result.get(max_timeout) + + return func_wrapper + + return timeout_decorator diff --git a/app/dashboard/notifications.py b/app/dashboard/notifications.py index aaee402716e..227b3ad4fff 100644 --- a/app/dashboard/notifications.py +++ b/app/dashboard/notifications.py @@ -31,6 +31,7 @@ import requests import twitter +from app.utils import timeout from economy.utils import convert_token_to_usdt from git.utils import delete_issue_comment, org_name, patch_issue_comment, post_issue_comment, repo_name from marketing.mails import featured_funded_bounty, send_mail, setup_lang, tip_email @@ -255,7 +256,7 @@ def build_message_for_integration(bounty, event_name): f"\n{bounty.get_absolute_url()}" return msg - +@timeout(2) def maybe_market_to_user_slack(bounty, event_name): """Send a Slack message to the user's slack channel for the specified Bounty. diff --git a/app/dashboard/views.py b/app/dashboard/views.py index 7c1a078a5d3..4d626dd4497 100644 --- a/app/dashboard/views.py +++ b/app/dashboard/views.py @@ -1776,7 +1776,10 @@ def helper_handle_approvals(request, bounty): maybe_market_to_github(bounty, 'work_started', profile_pairs=bounty.profile_pairs) maybe_market_to_slack(bounty, 'worker_approved') - maybe_market_to_user_slack(bounty, 'worker_approved') + try: + maybe_market_to_user_slack(bounty, 'worker_approved') + except TimeoutError: + pass record_bounty_activity(bounty, request.user, 'worker_approved', interest) else: start_work_rejected(interest, bounty) @@ -1786,7 +1789,10 @@ def helper_handle_approvals(request, bounty): interest.delete() maybe_market_to_slack(bounty, 'worker_rejected') - maybe_market_to_user_slack(bounty, 'worker_rejected') + try: + maybe_market_to_user_slack(bounty, 'worker_rejected') + except TimeoutError: + pass messages.success(request, _(f'{worker} has been {mutate_worker_action_past_tense}')) else: