Add direct message
This commit is contained in:
parent
259cb95b43
commit
2f8ef1b524
20 changed files with 1066 additions and 195 deletions
|
@ -1,42 +1,72 @@
|
|||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import ListView
|
||||
from django.http import HttpResponse, JsonResponse, HttpResponseBadRequest
|
||||
from django.http import HttpResponse, JsonResponse, HttpResponseBadRequest, HttpResponsePermanentRedirect, HttpResponseRedirect
|
||||
from django.core.paginator import Paginator
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.shortcuts import render
|
||||
from django.forms.models import model_to_dict
|
||||
from django.db.models import Case, BooleanField
|
||||
from django.db.models import Case, BooleanField, When, Q, Subquery, OuterRef, Exists, Count, IntegerField
|
||||
from django.db.models.functions import Coalesce
|
||||
from django.utils import timezone
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.urls import reverse
|
||||
|
||||
import datetime
|
||||
|
||||
from judge import event_poster as event
|
||||
from judge.jinja2.gravatar import gravatar
|
||||
from .models import Message, Profile
|
||||
from judge.models import Friend
|
||||
|
||||
from chat_box.models import Message, Profile, Room, UserRoom, Ignore
|
||||
from chat_box.utils import encrypt_url, decrypt_url
|
||||
|
||||
import json
|
||||
|
||||
|
||||
|
||||
class ChatView(ListView):
|
||||
context_object_name = 'message'
|
||||
template_name = 'chat/chat.html'
|
||||
title = _('Chat Box')
|
||||
paginate_by = 50
|
||||
messages = Message.objects.filter(hidden=False)
|
||||
paginator = Paginator(messages, paginate_by)
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.room_id = None
|
||||
self.room = None
|
||||
self.paginate_by = 50
|
||||
self.messages = None
|
||||
self.paginator = None
|
||||
|
||||
def get_queryset(self):
|
||||
return self.messages
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
request_room = kwargs['room_id']
|
||||
page = request.GET.get('page')
|
||||
|
||||
if request_room:
|
||||
try:
|
||||
self.room = Room.objects.get(id=request_room)
|
||||
if not can_access_room(request, self.room):
|
||||
return HttpResponseBadRequest()
|
||||
except Room.DoesNotExist:
|
||||
return HttpResponseBadRequest()
|
||||
else:
|
||||
request_room = None
|
||||
|
||||
if request_room != self.room_id or not self.messages:
|
||||
self.room_id = request_room
|
||||
self.messages = Message.objects.filter(hidden=False, room=self.room_id)
|
||||
self.paginator = Paginator(self.messages, self.paginate_by)
|
||||
|
||||
if page == None:
|
||||
update_last_seen(request, **kwargs)
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
cur_page = self.paginator.get_page(page)
|
||||
|
||||
return render(request, 'chat/message_list.html', {
|
||||
'object_list': cur_page.object_list,
|
||||
'num_pages': self.paginator.num_pages
|
||||
})
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
@ -45,7 +75,22 @@ class ChatView(ListView):
|
|||
context['title'] = self.title
|
||||
context['last_msg'] = event.last()
|
||||
context['status_sections'] = get_status_context(self.request)
|
||||
context['today'] = timezone.now().strftime("%d-%m-%Y")
|
||||
context['room'] = self.room_id
|
||||
context['unread_count_lobby'] = get_unread_count(None, self.request.profile)
|
||||
if self.room:
|
||||
users_room = [self.room.user_one, self.room.user_two]
|
||||
users_room.remove(self.request.profile)
|
||||
context['other_user'] = users_room[0]
|
||||
context['other_online'] = get_user_online_status(context['other_user'])
|
||||
context['is_ignored'] = Ignore.is_ignored(self.request.profile, context['other_user'])
|
||||
else:
|
||||
context['online_count'] = get_online_count()
|
||||
context['message_template'] = {
|
||||
'author': self.request.profile,
|
||||
'id': '$id',
|
||||
'time': timezone.now(),
|
||||
'body': '$body'
|
||||
}
|
||||
return context
|
||||
|
||||
|
||||
|
@ -73,21 +118,46 @@ def delete_message(request):
|
|||
@login_required
|
||||
def post_message(request):
|
||||
ret = {'msg': 'posted'}
|
||||
if request.method != 'POST':
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
if request.method == 'GET':
|
||||
return JsonResponse(ret)
|
||||
room = None
|
||||
if request.POST['room']:
|
||||
room = Room.objects.get(id=request.POST['room'])
|
||||
|
||||
if not can_access_room(request, room) or request.profile.mute:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
new_message = Message(author=request.profile,
|
||||
body=request.POST['body'])
|
||||
body=request.POST['body'],
|
||||
room=room)
|
||||
new_message.save()
|
||||
|
||||
event.post('chat', {
|
||||
'type': 'new_message',
|
||||
'message': new_message.id,
|
||||
})
|
||||
|
||||
if not room:
|
||||
event.post('chat_lobby', {
|
||||
'type': 'lobby',
|
||||
'author_id': request.profile.id,
|
||||
'message': new_message.id,
|
||||
'room': 'None',
|
||||
'tmp_id': request.POST.get('tmp_id')
|
||||
})
|
||||
else:
|
||||
for user in room.users():
|
||||
event.post('chat_' + str(user.id), {
|
||||
'type': 'private',
|
||||
'author_id': request.profile.id,
|
||||
'message': new_message.id,
|
||||
'room': room.id,
|
||||
'tmp_id': request.POST.get('tmp_id')
|
||||
})
|
||||
|
||||
return JsonResponse(ret)
|
||||
|
||||
|
||||
def can_access_room(request, room):
|
||||
return not room or room.user_one == request.profile or room.user_two == request.profile
|
||||
|
||||
|
||||
@login_required
|
||||
def chat_message_ajax(request):
|
||||
if request.method != 'GET':
|
||||
|
@ -100,6 +170,9 @@ def chat_message_ajax(request):
|
|||
|
||||
try:
|
||||
message = Message.objects.filter(hidden=False).get(id=message_id)
|
||||
room = message.room
|
||||
if room and not room.contain(request.profile):
|
||||
return HttpResponse('Unauthorized', status=401)
|
||||
except Message.DoesNotExist:
|
||||
return HttpResponseBadRequest()
|
||||
return render(request, 'chat/message.html', {
|
||||
|
@ -107,53 +180,156 @@ def chat_message_ajax(request):
|
|||
})
|
||||
|
||||
|
||||
def get_user_online_status():
|
||||
last_five_minutes = timezone.now()-timezone.timedelta(minutes=5)
|
||||
return Profile.objects \
|
||||
.filter(display_rank='user',
|
||||
last_access__gte = last_five_minutes)\
|
||||
.annotate(is_online=Case(default=True,output_field=BooleanField()))\
|
||||
.order_by('-rating')
|
||||
@login_required
|
||||
def update_last_seen(request, **kwargs):
|
||||
if 'room_id' in kwargs:
|
||||
room_id = kwargs['room_id']
|
||||
elif request.method == 'GET':
|
||||
room_id = request.GET.get('room')
|
||||
elif request.method == 'POST':
|
||||
room_id = request.POST.get('room')
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
try:
|
||||
profile = request.profile
|
||||
room = None
|
||||
if room_id:
|
||||
room = Room.objects.get(id=int(room_id))
|
||||
except Room.DoesNotExist:
|
||||
return HttpResponseBadRequest()
|
||||
except Exception as e:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
if room and not room.contain(profile):
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
user_room, _ = UserRoom.objects.get_or_create(user=profile, room=room)
|
||||
user_room.last_seen = timezone.now()
|
||||
user_room.save()
|
||||
|
||||
return JsonResponse({'msg': 'updated'})
|
||||
|
||||
|
||||
def get_admin_online_status():
|
||||
all_admin = Profile.objects.filter(display_rank='admin')
|
||||
last_five_minutes = timezone.now()-timezone.timedelta(minutes=5)
|
||||
def get_online_count():
|
||||
last_two_minutes = timezone.now()-timezone.timedelta(minutes=2)
|
||||
return Profile.objects.filter(last_access__gte=last_two_minutes).count()
|
||||
|
||||
|
||||
def get_user_online_status(user):
|
||||
time_diff = timezone.now() - user.last_access
|
||||
is_online = time_diff <= timezone.timedelta(minutes=2)
|
||||
return is_online
|
||||
|
||||
|
||||
def user_online_status_ajax(request):
|
||||
if request.method != 'GET':
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
user_id = request.GET.get('user')
|
||||
|
||||
if user_id:
|
||||
try:
|
||||
user_id = int(user_id)
|
||||
user = Profile.objects.get(id=user_id)
|
||||
except Exception as e:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
is_online = get_user_online_status(user)
|
||||
return render(request, 'chat/user_online_status.html', {
|
||||
'other_user': user,
|
||||
'other_online': is_online,
|
||||
'is_ignored': Ignore.is_ignored(request.profile, user)
|
||||
})
|
||||
else:
|
||||
return render(request, 'chat/user_online_status.html', {
|
||||
'online_count': get_online_count(),
|
||||
})
|
||||
|
||||
|
||||
def get_online_status(request_user, queryset, rooms=None):
|
||||
if not queryset:
|
||||
return None
|
||||
last_two_minutes = timezone.now()-timezone.timedelta(minutes=2)
|
||||
ret = []
|
||||
|
||||
for admin in all_admin:
|
||||
if rooms:
|
||||
unread_count = get_unread_count(rooms, request_user)
|
||||
count = {}
|
||||
for i in unread_count:
|
||||
count[i['other_user']] = i['unread_count']
|
||||
|
||||
for user in queryset:
|
||||
is_online = False
|
||||
if (admin.last_access >= last_five_minutes):
|
||||
if (user.last_access >= last_two_minutes):
|
||||
is_online = True
|
||||
ret.append({'user': admin, 'is_online': is_online})
|
||||
|
||||
user_dict = {'user': user, 'is_online': is_online}
|
||||
if rooms and user.id in count:
|
||||
user_dict['unread_count'] = count[user.id]
|
||||
user_dict['url'] = encrypt_url(request_user.id, user.id)
|
||||
ret.append(user_dict)
|
||||
return ret
|
||||
|
||||
|
||||
def get_status_context(request):
|
||||
friend_list = request.profile.get_friends()
|
||||
all_user_status = get_user_online_status()
|
||||
friend_status = []
|
||||
user_status = []
|
||||
def get_status_context(request, include_ignored=False):
|
||||
if include_ignored:
|
||||
ignored_users = Profile.objects.none()
|
||||
queryset = Profile.objects
|
||||
else:
|
||||
ignored_users = Ignore.get_ignored_users(request.profile)
|
||||
queryset = Profile.objects.exclude(id__in=ignored_users)
|
||||
|
||||
for user in all_user_status:
|
||||
if user.username in friend_list:
|
||||
friend_status.append(user)
|
||||
else:
|
||||
user_status.append(user)
|
||||
last_two_minutes = timezone.now()-timezone.timedelta(minutes=2)
|
||||
recent_profile = Room.objects.filter(
|
||||
Q(user_one=request.profile) | Q(user_two=request.profile)
|
||||
).annotate(
|
||||
last_msg_time=Subquery(
|
||||
Message.objects.filter(room=OuterRef('pk')).values('time')[:1]
|
||||
),
|
||||
other_user=Case(
|
||||
When(user_one=request.profile, then='user_two'),
|
||||
default='user_one',
|
||||
)
|
||||
).filter(last_msg_time__isnull=False)\
|
||||
.exclude(other_user__in=ignored_users)\
|
||||
.order_by('-last_msg_time').values('other_user', 'id')[:20]
|
||||
|
||||
recent_profile_id = [str(i['other_user']) for i in recent_profile]
|
||||
joined_id = ','.join(recent_profile_id)
|
||||
recent_rooms = [int(i['id']) for i in recent_profile]
|
||||
recent_list = None
|
||||
if joined_id:
|
||||
recent_list = Profile.objects.raw(
|
||||
f'SELECT * from judge_profile where id in ({joined_id}) order by field(id,{joined_id})')
|
||||
|
||||
friend_list = Friend.get_friend_profiles(request.profile).exclude(id__in=recent_profile_id)\
|
||||
.exclude(id__in=ignored_users)\
|
||||
.order_by('-last_access')
|
||||
admin_list = queryset.filter(display_rank='admin')\
|
||||
.exclude(id__in=friend_list).exclude(id__in=recent_profile_id)
|
||||
all_user_status = queryset\
|
||||
.filter(display_rank='user',
|
||||
last_access__gte = last_two_minutes)\
|
||||
.annotate(is_online=Case(default=True,output_field=BooleanField()))\
|
||||
.order_by('-rating').exclude(id__in=friend_list).exclude(id__in=admin_list)\
|
||||
.exclude(id__in=recent_profile_id)[:30]
|
||||
|
||||
return [
|
||||
{
|
||||
'title': 'Admins',
|
||||
'user_list': get_admin_online_status(),
|
||||
'title': 'Recent',
|
||||
'user_list': get_online_status(request.profile, recent_list, recent_rooms),
|
||||
},
|
||||
{
|
||||
'title': 'Following',
|
||||
'user_list': friend_status,
|
||||
'user_list': get_online_status(request.profile, friend_list),
|
||||
},
|
||||
{
|
||||
'title': 'Users',
|
||||
'user_list': user_status,
|
||||
'title': 'Admins',
|
||||
'user_list': get_online_status(request.profile, admin_list),
|
||||
},
|
||||
{
|
||||
'title': 'Other',
|
||||
'user_list': get_online_status(request.profile, all_user_status),
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -162,4 +338,80 @@ def get_status_context(request):
|
|||
def online_status_ajax(request):
|
||||
return render(request, 'chat/online_status.html', {
|
||||
'status_sections': get_status_context(request),
|
||||
})
|
||||
'unread_count_lobby': get_unread_count(None, request.profile),
|
||||
})
|
||||
|
||||
|
||||
@login_required
|
||||
def get_room(user_one, user_two):
|
||||
if user_one.id > user_two.id:
|
||||
user_one, user_two = user_two, user_one
|
||||
room, created = Room.objects.get_or_create(user_one=user_one, user_two=user_two)
|
||||
return room
|
||||
|
||||
|
||||
@login_required
|
||||
def get_or_create_room(request):
|
||||
decrypted_other_id = request.GET.get('other')
|
||||
request_id, other_id = decrypt_url(decrypted_other_id)
|
||||
|
||||
if not other_id or not request_id or request_id != request.profile.id:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
try:
|
||||
other_user = Profile.objects.get(id=int(other_id))
|
||||
except Exception:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
user = request.profile
|
||||
|
||||
if not other_user or not user:
|
||||
return HttpResponseBadRequest()
|
||||
# TODO: each user can only create <= 300 rooms
|
||||
room = get_room(other_user, user)
|
||||
return JsonResponse({'room': room.id, 'other_user_id': other_user.id})
|
||||
|
||||
|
||||
def get_unread_count(rooms, user):
|
||||
if rooms:
|
||||
mess = Message.objects.filter(room=OuterRef('room'),
|
||||
time__gte=OuterRef('last_seen'))\
|
||||
.exclude(author=user)\
|
||||
.order_by().values('room')\
|
||||
.annotate(unread_count=Count('pk')).values('unread_count')
|
||||
|
||||
return UserRoom.objects\
|
||||
.filter(user=user, room__in=rooms)\
|
||||
.annotate(
|
||||
unread_count=Coalesce(Subquery(mess, output_field=IntegerField()), 0),
|
||||
other_user=Case(
|
||||
When(room__user_one=user, then='room__user_two'),
|
||||
default='room__user_one',
|
||||
)
|
||||
).filter(unread_count__gte=1).values('other_user', 'unread_count')
|
||||
else: # lobby
|
||||
mess = Message.objects.filter(room__isnull=True,
|
||||
time__gte=OuterRef('last_seen'))\
|
||||
.exclude(author=user)\
|
||||
.annotate(unread_count=Count('pk')).values('unread_count')
|
||||
|
||||
return UserRoom.objects\
|
||||
.filter(user=user, room__isnull=True)\
|
||||
.annotate(
|
||||
unread_count=Coalesce(Subquery(mess, output_field=IntegerField()), 0),
|
||||
).values_list('unread_count', flat=True)[0]
|
||||
|
||||
|
||||
@login_required
|
||||
def toggle_ignore(request, **kwargs):
|
||||
user_id = kwargs['user_id']
|
||||
if not user_id:
|
||||
return HttpResponseBadRequest()
|
||||
try:
|
||||
other_user = Profile.objects.get(id=user_id)
|
||||
except:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
Ignore.toggle_ignore(request.profile, other_user)
|
||||
next_url = request.GET.get('next', '/')
|
||||
return HttpResponseRedirect(next_url)
|
Loading…
Add table
Add a link
Reference in a new issue