Skip to content

Commit

Permalink
Merge pull request #5569 from gitcoinco/chat/driver-1.2
Browse files Browse the repository at this point in the history
Chat 1.2 - General Availability
  • Loading branch information
thelostone-mc authored Jan 9, 2020
2 parents dd38451 + 3e01223 commit 7e82ea0
Show file tree
Hide file tree
Showing 48 changed files with 782 additions and 57 deletions.
20 changes: 20 additions & 0 deletions app/app/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,30 @@ def preprocess(request):
callback = request.GET.get('cb')
handle_marketing_callback(callback, request)

chat_unread_messages = False

if profile and profile.chat_id:
try:
from chat.tasks import get_driver
chat_driver = get_driver()

chat_unreads_request = chat_driver.teams.get_team_unreads_for_user(
profile.chat_id
)

if 'message' not in chat_unreads_request:
for teams in chat_unreads_request:
if teams['msg_count'] > 0 or teams['mention_count'] > 0:
chat_unread_messages = True
break
except Exception as e:
logger.error(str(e))

context = {
'STATIC_URL': settings.STATIC_URL,
'MEDIA_URL': settings.MEDIA_URL,
'num_slack': num_slack,
'chat_unread_messages' : chat_unread_messages,
'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,
Expand Down
10 changes: 8 additions & 2 deletions app/app/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
'health_check.contrib.s3boto3_storage',
'app',
'avatar',
'chat',
'retail',
'rest_framework',
'marketing',
Expand Down Expand Up @@ -468,6 +469,8 @@
CELERY_TASK_SERIALIZER = 'json'
# http://docs.celeryproject.org/en/latest/userguide/configuration.html#std:setting-result_serializer
CELERY_RESULT_SERIALIZER = 'json'
# http://docs.celeryproject.org/en/latest/userguide/configuration.html#std:setting-result_backend
CELERY_RESULT_BACKEND = env('CELERY_RESULT_BACKEND', default=CACHEOPS_REDIS)

DJANGO_REDIS_IGNORE_EXCEPTIONS = env.bool('REDIS_IGNORE_EXCEPTIONS', default=True)
DJANGO_REDIS_LOG_IGNORED_EXCEPTIONS = env.bool('REDIS_LOG_IGNORED_EXCEPTIONS', default=True)
Expand Down Expand Up @@ -542,8 +545,11 @@
GITHUB_APP_NAME = env('GITHUB_APP_NAME', default='gitcoin-local')

# Chat
CHAT_URL = env('CHAT_DRIVER_USER', default='') # location of where mattermost is hosted

CHAT_PORT = env('CHAT_PORT', default=8065) # port of where mattermost is hosted
CHAT_URL = env('CHAT_URL', default='http://localhost') # location of where mattermost is hosted
CHAT_DRIVER_TOKEN = env('CHAT_DRIVER_TOKEN', default='') # driver token
GITCOIN_HACK_CHAT_TEAM_ID = env('GITCOIN_HACK_CHAT_TEAM_ID', default='')
GITCOIN_CHAT_TEAM_ID = env('GITCOIN_CHAT_TEAM_ID', default='')
# Social Auth
LOGIN_URL = 'gh_login'
LOGOUT_URL = 'logout'
Expand Down
5 changes: 3 additions & 2 deletions app/app/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,9 @@
url(r'^api/v0.1/choose_persona/', dashboard.views.choose_persona, name='choose_persona'),

# chat
url(r'^chat/', chat.views.embed, name='chat'),
url(r'^chat', chat.views.embed, name='chat2'),
url(r'^chat/web', chat.views.embed, name='web_chat'),
url(r'^chat/web/', chat.views.embed, name='web_chat2'),
re_path(r'^chat/?', chat.views.chat, name='chat'),
# Health check endpoint
re_path(r'^health/', include('health_check.urls')),
re_path(r'^lbcheck/?', healthcheck.views.lbcheck, name='lbcheck'),
Expand Down
43 changes: 43 additions & 0 deletions app/assets/v2/css/chat.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
.banner {
background-color: #0d023b;
background-size: cover;
background-position: 0px 0px;
background-repeat: no-repeat;
position: relative;
color: #fff;
padding-top: 5rem;
padding-bottom: 5rem;
}

.sub-headline {
line-height: 48px;
}

.online {
color: #25e899;
}

.dot {
background-color: #25e899;
display: none;
width: 9px;
height: 9px;
border-radius: 50%;
position: absolute;
top: 0.5em;
right: 0.9em;
}

.open-app {
background-color: #0D001A;
}

img.feature {
border-radius: 80px;
}

@media (max-width: 1024px) {
.chat-img {
display: none;
}
}
Binary file added app/assets/v2/images/chat/android.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/assets/v2/images/chat/apple.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/assets/v2/images/chat/chat.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/assets/v2/images/chat/desktop.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/assets/v2/images/chat/feature_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/assets/v2/images/chat/feature_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/assets/v2/images/chat/feature_3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/assets/v2/images/chat/mobile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/assets/v2/images/chat/web.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/assets/v2/images/chat/windows.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions app/assets/v2/js/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -406,3 +406,7 @@ const gitcoinUpdates = (force) => {
if (document.contxt.github_handle) {
gitcoinUpdates();
}

if (document.contxt.chat_unread_messages) {
$('#chat-notification-dot').addClass('notification__dot__active');
}
1 change: 0 additions & 1 deletion app/chat/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
default_app_config = 'inbox.apps.NotificationConfig'
21 changes: 21 additions & 0 deletions app/chat/apps.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
# -*- coding: utf-8 -*-
"""Define the Dashboard application configuration.
Copyright (C) 2018 Gitcoin Core
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from __future__ import unicode_literals

from django.apps import AppConfig


Expand Down
Empty file added app/chat/management/__init__.py
Empty file.
Empty file.
79 changes: 79 additions & 0 deletions app/chat/management/commands/sync_users_to_chat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
'''
Copyright (C) 2018 Gitcoin Core
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
from django.conf import settings
from django.core.management.base import BaseCommand
from dashboard.models import Profile
from chat.tasks import create_user
import logging
from celery import group

logger = logging.getLogger(__name__)

from marketing.utils import should_suppress_notification_email


class Command(BaseCommand):
help = "create users to Gitcoin chat, creates the user if it doesn't exist"

def handle(self, *args, **options):
try:

profiles = Profile.objects.filter(user__is_active=True, chat__id__exact='').prefetch_related('user')

tasks = []

for profile in profiles:
# if profile.chat_id is None:
tasks.append(create_user.si(options={
"email": profile.user.email,
"username": profile.handle,
"first_name": profile.user.first_name,
"last_name": profile.user.last_name,
"nickname": profile.handle,
"auth_data": f'{profile.user.id}',
"auth_service": "gitcoin",
"locale": "en",
"props": {},
"notify_props": {
"email": "false",
"push": "mention",
"desktop": "all",
"desktop_sound": "true",
"mention_keys": f'{profile.handle}, @{profile.handle}',
"channel": "true",
"first_name": "false"
},
}, params={
"tid": settings.GITCOIN_HACK_CHAT_TEAM_ID
}))
job = group(tasks)

result = job.apply_async()
for result_req in result.get():
if 'message' not in result_req:
if 'username' in result_req and 'id' in result_req:
profile = Profile.objects.get(handle=result_req['username'])
if profile is not None:
profile.chat_id = result_req['id']
profile.save()

except ConnectionError as exec:
print(str(exec))
self.retry(30)
except Exception as e:
logger.error(str(e))
131 changes: 131 additions & 0 deletions app/chat/tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
from app.redis_service import RedisService
from celery import app, group
from celery.utils.log import get_task_logger
from dashboard.models import Profile
from django.conf import settings

from mattermostdriver import Driver

logger = get_task_logger(__name__)

redis = RedisService().redis

# Lock timeout of 2 minutes (just in the case that the application hangs to avoid a redis deadlock)
LOCK_TIMEOUT = 60 * 2

chat_driver = Driver({
'url': settings.CHAT_URL,
'port': settings.CHAT_PORT,
'token': settings.CHAT_DRIVER_TOKEN
})


def get_driver():
chat_driver.login()
return chat_driver


@app.shared_task(bind=True, max_retries=3)
def create_channel(self, options, retry: bool = True) -> None:
"""
:param options:
:param retry:
:return:
"""
with redis.lock("tasks:create_channel:%s" % options['channel_name'], timeout=LOCK_TIMEOUT):

try:
chat_driver.login()
new_channel = chat_driver.channels.create_channel(options={
'team_id': options['team_id'],
'name': options['channel_name'],
'display_name': options['channel_display_name'],
'type': 'O'
})

return new_channel
except ConnectionError as exc:
logger.info(str(exc))
logger.info("Retrying connection")
self.retry(30)
except Exception as e:
print("we got an exception when creating a channel")
logger.error(str(e))


@app.shared_task(bind=True, max_retries=3)
def add_to_channel(self, options, retry: bool = True) -> None:
"""
:param options:
:param retry:
:return:
"""
with redis.lock("tasks:add_to_channel:%s" % options['channel_id'], timeout=LOCK_TIMEOUT):
chat_driver.login()
try:
for x in options['profiles']:
if x is not None:
response = chat_driver.channels.add_user(options['channel_id'], options={
'user_id': x
})
except ConnectionError as exc:
logger.info(str(exc))
logger.info("Retrying connection")
self.retry(30)
except Exception as e:
logger.error(str(e))


@app.shared_task(bind=True, max_retries=1)
def create_user(self, options, params, retry: bool = True):
with redis.lock("tasks:create_user:%s" % options['username'], timeout=LOCK_TIMEOUT):
try:
chat_driver.login()
create_user_response = chat_driver.users.create_user(
options=options,
params=params
)
return create_user_response
except ConnectionError as exc:
logger.info(str(exc))
logger.info("Retrying connection")
self.retry(30)
except Exception as e:
logger.error(str(e))
return None


@app.shared_task(bind=True, max_retries=3)
def update_user(self, query_opts, update_opts, retry: bool = True) -> None:
"""
:param self:
:param query_opts:
:param update_opts:
:param retry:
:return: None
"""

if update_opts is None:
return

with redis.lock("tasks:update_user:%s" % query_opts['handle'], timeout=LOCK_TIMEOUT):

try:

if query_opts['chat_id'] is None:
chat_user = chat_driver.users.get_user_by_username(query_opts['handle'])
if 'message' not in chat_user:
chat_id = chat_user['id']
else:
chat_id = query_opts['chat_id']

user_profile = Profile.objects.filter(handle=query_opts['handle'])
user_profile.chat_id = chat_id
user_profile.save()
chat_driver.users.update_user(chat_id, options=update_opts)
except ConnectionError as exc:
logger.info(str(exc))
logger.info("Retrying connection")
self.retry(30)
except Exception as e:
logger.error(str(e))
Loading

0 comments on commit 7e82ea0

Please sign in to comment.