diff --git a/app/app/urls.py b/app/app/urls.py index 337aeacefc9..e07e5043449 100644 --- a/app/app/urls.py +++ b/app/app/urls.py @@ -186,6 +186,7 @@ url(r'^settings/privacy/?', marketing.views.privacy_settings, name='privacy_settings'), url(r'^settings/matching/?', marketing.views.matching_settings, name='matching_settings'), url(r'^settings/feedback/?', marketing.views.feedback_settings, name='feedback_settings'), + url(r'^settings/slack/?', marketing.views.slack_settings, name='slack_settings'), url(r'^settings/(.*)?', marketing.views.email_settings, name='feedback_settings'), # marketing views diff --git a/app/dashboard/helpers.py b/app/dashboard/helpers.py index a771b8bbeda..1efe0ae847d 100644 --- a/app/dashboard/helpers.py +++ b/app/dashboard/helpers.py @@ -33,6 +33,7 @@ from dashboard.models import Bounty, BountyFulfillment, BountySyncRequest, UserAction from dashboard.notifications import ( maybe_market_to_email, maybe_market_to_github, maybe_market_to_slack, maybe_market_to_twitter, + maybe_market_to_user_slack, ) from economy.utils import convert_amount from github.utils import _AUTH @@ -556,6 +557,7 @@ def process_bounty_changes(old_bounty, new_bounty): print("============ posting ==============") did_post_to_twitter = maybe_market_to_twitter(new_bounty, event_name) did_post_to_slack = maybe_market_to_slack(new_bounty, event_name) + did_post_to_user_slack = maybe_market_to_user_slack(new_bounty, event_name) did_post_to_github = maybe_market_to_github(new_bounty, event_name, profile_pairs) did_post_to_email = maybe_market_to_email(new_bounty, event_name) print("============ done posting ==============") @@ -566,6 +568,7 @@ def process_bounty_changes(old_bounty, new_bounty): 'did_post_to_email': did_post_to_email, 'did_post_to_github': did_post_to_github, 'did_post_to_slack': did_post_to_slack, + 'did_post_to_user_slack': did_post_to_user_slack, 'did_post_to_twitter': did_post_to_twitter, } diff --git a/app/dashboard/notifications.py b/app/dashboard/notifications.py index 5950af64ac7..aa1e30af67e 100644 --- a/app/dashboard/notifications.py +++ b/app/dashboard/notifications.py @@ -171,6 +171,21 @@ def maybe_market_to_slack(bounty, event_name): if bounty.network != settings.ENABLE_NOTIFICATIONS_ON_NETWORK: return False + msg = build_message_for_slack(bounty, event_name) + if not msg: + return False + + try: + channel = 'notif-gitcoin' + sc = SlackClient(settings.SLACK_TOKEN) + sc.api_call("chat.postMessage", channel=channel, text=msg) + except Exception as e: + print(e) + return False + return True + + +def build_message_for_slack(bounty, event_name): conv_details = "" usdt_details = "" try: @@ -178,20 +193,61 @@ def maybe_market_to_slack(bounty, event_name): usdt_details = f"({bounty.value_in_usdt_now} USD {conv_details} " except Exception: pass # no USD conversion rate + except Exception as e: + print(e) + return False + title = bounty.title if bounty.title else bounty.github_url msg = f"{event_name.replace('bounty', 'funded_issue')} worth {round(bounty.get_natural_value(), 4)} {bounty.token_name} " \ f"{usdt_details}" \ f"{bounty.token_name}: {title} \n\n{bounty.get_absolute_url()}" + return msg + + +def maybe_market_to_user_slack(bounty, event_name): + """Send a Slack message to the user's slack channel for the specified Bounty. + + Args: + bounty (dashboard.models.Bounty): The Bounty to be marketed. + event_name (str): The name of the event. + Returns: + bool: Whether or not the Slack notification was sent successfully. + + """ + from marketing.models import EmailSubscriber + if bounty.get_natural_value() < 0.0001: + return False + if bounty.network != settings.ENABLE_NOTIFICATIONS_ON_NETWORK: + return False + + msg = build_message_for_slack(bounty, event_name) + if not msg: + return False + + url = bounty.github_url + uri = parse(url).path + uri_array = uri.split('/') + sent = False try: - channel = 'notif-gitcoin' - sc = SlackClient(settings.SLACK_TOKEN) - sc.api_call("chat.postMessage", channel=channel, text=msg) + username = uri_array[1] + repo = uri_array[2] + subscribers = EmailSubscriber.objects.filter(profile__handle=username, repos__contains=[repo]) + subscribers = subscribers & EmailSubscriber.objects.exclude(slack_token='', slack_channel='') + for subscriber in subscribers: + try: + sc = SlackClient(subscriber.slack_token) + sc.api_call("chat.postMessage", channel=subscriber.slack_channel, text=msg) + sent = True + except Exception as e: + print(e) + except IndexError: + return False except Exception as e: print(e) return False - return True + return sent def maybe_market_tip_to_email(tip, emails): """Send an email for the specified Tip. diff --git a/app/dashboard/views.py b/app/dashboard/views.py index 6914cd54f13..bfcf00d191b 100644 --- a/app/dashboard/views.py +++ b/app/dashboard/views.py @@ -41,7 +41,7 @@ ) from dashboard.notifications import ( maybe_market_tip_to_email, maybe_market_tip_to_github, maybe_market_tip_to_slack, maybe_market_to_slack, - maybe_market_to_twitter, + maybe_market_to_twitter, maybe_market_to_user_slack, ) from dashboard.utils import get_bounty, get_bounty_id, has_tx_mined, web3_process_bounty from gas.utils import conf_time_spread, eth_usd_conv_rate, recommend_min_gas_price_to_confirm_in_time @@ -114,6 +114,7 @@ def create_new_interest_helper(bounty, user): bounty.interested.add(interest) record_user_action(user, 'start_work', interest) maybe_market_to_slack(bounty, 'start_work') + maybe_market_to_user_slack(bounty, 'start_work') maybe_market_to_twitter(bounty, 'start_work') return interest @@ -230,6 +231,7 @@ def remove_interest(request, bounty_id): bounty.interested.remove(interest) interest.delete() maybe_market_to_slack(bounty, 'stop_work') + maybe_market_to_user_slack(bounty, 'stop_work') maybe_market_to_twitter(bounty, 'stop_work') except Interest.DoesNotExist: return JsonResponse({ @@ -279,6 +281,7 @@ def uninterested(request, bounty_id, profile_id): interest = Interest.objects.get(profile_id=profile_id, bounty=bounty) bounty.interested.remove(interest) maybe_market_to_slack(bounty, 'stop_work') + maybe_market_to_user_slack(bounty, 'stop_work') interest.delete() except Interest.DoesNotExist: return JsonResponse({ diff --git a/app/marketing/migrations/0022_auto_20180419_1517.py b/app/marketing/migrations/0022_auto_20180419_1517.py new file mode 100644 index 00000000000..601b722d801 --- /dev/null +++ b/app/marketing/migrations/0022_auto_20180419_1517.py @@ -0,0 +1,30 @@ +# Generated by Django 2.0.4 on 2018-04-19 15:17 + +import django.contrib.postgres.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('marketing', '0021_emailsubscriber_profile'), + ] + + operations = [ + migrations.AddField( + model_name='emailsubscriber', + name='repos', + field=django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=200), blank=True, default=[], size=None), + ), + migrations.AddField( + model_name='emailsubscriber', + name='slack_channel', + field=models.CharField(default='', max_length=255), + ), + migrations.AddField( + model_name='emailsubscriber', + name='slack_token', + field=models.CharField(default='', max_length=255), + ), + ] diff --git a/app/marketing/models.py b/app/marketing/models.py index fd11cd516f1..803e788042a 100644 --- a/app/marketing/models.py +++ b/app/marketing/models.py @@ -37,6 +37,9 @@ class EmailSubscriber(SuperModel): priv = models.CharField(max_length=30, default='') github = models.CharField(max_length=255, default='') keywords = ArrayField(models.CharField(max_length=200), blank=True, default=[]) + repos = ArrayField(models.CharField(max_length=200), blank=True, default=[]) + slack_token = models.CharField(max_length=255, default='') + slack_channel = models.CharField(max_length=255, default='') profile = models.ForeignKey( 'dashboard.Profile', on_delete=models.CASCADE, diff --git a/app/marketing/views.py b/app/marketing/views.py index 8208c839138..7380bc4cb4a 100644 --- a/app/marketing/views.py +++ b/app/marketing/views.py @@ -417,6 +417,10 @@ def funnel(request): 'body': 'Feedback', 'href': '/settings/feedback', }, + { + 'body': 'Slack', + 'href': '/settings/slack', + }, ] @@ -615,6 +619,44 @@ def email_settings(request, key): return TemplateResponse(request, 'settings/email.html', context) +def slack_settings(request): + + # setup + profile, es, user, is_logged_in = settings_helper_get_auth(request) + if not es: + login_redirect = redirect('/login/github?next=' + request.get_full_path()) + return login_redirect + + msg = '' + + if request.POST and request.POST.get('submit'): + token = request.POST.get('token', '') + repos = request.POST.get('repos').split(',') + channel = request.POST.get('channel', '') + es.slack_token = token + es.repos = repos + es.slack_channel = channel + ip = get_ip(request) + if not es.metadata.get('ip', False): + es.metadata['ip'] = [ip] + else: + es.metadata['ip'].append(ip) + es.save() + msg = "Updated your preferences. " + + context = { + 'repos': ",".join(es.repos), + 'is_logged_in': is_logged_in, + 'nav': 'internal', + 'active': '/settings/slack', + 'title': _('Slack Settings'), + 'navs': settings_navs, + 'es': es, + 'msg': msg, + } + return TemplateResponse(request, 'settings/slack.html', context) + + def _leaderboard(request): return leaderboard(request, '') diff --git a/app/retail/templates/settings/slack.html b/app/retail/templates/settings/slack.html new file mode 100644 index 00000000000..87fb58daf4d --- /dev/null +++ b/app/retail/templates/settings/slack.html @@ -0,0 +1,23 @@ +{% extends 'settings/settings.html' %} +{% load i18n static %} + +{% block settings_content %} +
+ +{% endblock %} \ No newline at end of file