from django.shortcuts import render, redirect, get_object_or_404 from django.contrib.auth import authenticate, login, logout from django.contrib.auth.decorators import login_required from django.contrib.auth.models import User from django.contrib import messages from django.http import JsonResponse from django.views.decorators.http import require_POST from chat.models import UserProfile, Channel, Message, DirectMessage from chat.forms import RegisterForm, ProfileForm, ChannelForm, MessageFileForm from channels.layers import get_channel_layer from asgiref.sync import async_to_sync def _broadcast_message(group_name, message_type, msg, request_user): channel_layer = get_channel_layer() async_to_sync(channel_layer.group_send)( group_name, { 'type': message_type, 'message': msg.content, 'sender': request_user.username, 'sender_id': request_user.id, 'message_id': msg.id, 'timestamp': msg.timestamp.strftime('%H:%M'), 'avatar': request_user.profile.avatar.url if request_user.profile.avatar else None, 'role': request_user.profile.role, 'file_url': msg.file.url if msg.file else None, 'file_name': msg.file.name if msg.file else None, } ) def register_view(request): form = RegisterForm(request.POST or None) if request.method == 'POST' and form.is_valid(): user = User.objects.create_user( username=form.cleaned_data['username'], email=form.cleaned_data['email'], password=form.cleaned_data['password'], ) UserProfile.objects.create(user=user) login(request, user) return redirect('home') return render(request, 'chat/register.html', {'form': form}) def login_view(request): error = None if request.method == 'POST': user = authenticate(request, username=request.POST.get('username'), password=request.POST.get('password')) if user: login(request, user) return redirect('home') error = 'Invalid credentials.' return render(request, 'chat/login.html', {'error': error}) def logout_view(request): logout(request) return redirect('login') @login_required def home_view(request): channels = Channel.objects.all().prefetch_related('members') users = User.objects.exclude(id=request.user.id).select_related('profile') return render(request, 'chat/home.html', {'channels': channels, 'users': users}) @login_required def channel_view(request, channel_id): channel = get_object_or_404(Channel, id=channel_id) # Join if not member if request.user not in channel.members.all(): channel.members.add(request.user) # File upload via POST if request.method == 'POST': f = request.FILES.get('file') if f: msg = Message.objects.create( channel=channel, sender=request.user, content=request.POST.get('content', ''), file=f, ) _broadcast_message(f'channel_{channel.id}', 'chat_message', msg, request.user) return redirect('channel', channel_id=channel_id) messages_qs = channel.messages.filter(deleted=False).select_related('sender', 'sender__profile') return render(request, 'chat/channel.html', { 'channel': channel, 'messages': messages_qs, 'all_channels': Channel.objects.all(), }) @login_required def create_channel_view(request): profile = request.user.profile if not profile.is_moderator(): messages.error(request, 'No permission.') return redirect('home') form = ChannelForm(request.POST or None) if form.is_valid(): ch = form.save(commit=False) ch.created_by = request.user ch.save() ch.members.add(request.user) return redirect('channel', channel_id=ch.id) return render(request, 'chat/create_channel.html', {'form': form}) @login_required def delete_channel_view(request, channel_id): channel = get_object_or_404(Channel, id=channel_id) if request.user.profile.is_admin(): channel.delete() return redirect('home') @login_required def dm_view(request, username): other = get_object_or_404(User, username=username) if request.method == 'POST': f = request.FILES.get('file') if f: msg = DirectMessage.objects.create( sender=request.user, receiver=other, content=request.POST.get('content', ''), file=f, ) ids = sorted([request.user.id, other.id]) _broadcast_message(f'dm_{ids[0]}_{ids[1]}', 'dm_message', msg, request.user) return redirect('dm', username=username) dms = DirectMessage.objects.filter( sender__in=[request.user, other], receiver__in=[request.user, other], ).select_related('sender', 'sender__profile').order_by('timestamp') return render(request, 'chat/dm.html', { 'other': other, 'messages': dms, 'all_users': User.objects.exclude(id=request.user.id).select_related('profile'), }) @login_required def profile_view(request): profile = request.user.profile form = ProfileForm(request.POST or None, request.FILES or None, instance=profile) if form.is_valid(): form.save() messages.success(request, 'Profile updated.') return redirect('profile') return render(request, 'chat/profile.html', {'form': form}) def _handle_admin_action(request, action, target, role): if action == 'set_role' and role: target.profile.role = role target.profile.save() messages.success(request, f'Role updated for {target.username}.') elif action == 'block': request.user.profile.blocked_users.add(target) messages.success(request, f'{target.username} blocked.') elif action == 'unblock': request.user.profile.blocked_users.remove(target) messages.success(request, f'{target.username} unblocked.') @login_required def admin_panel_view(request): if not request.user.profile.is_admin(): messages.error(request, 'No permission.') return redirect('home') users = User.objects.all().select_related('profile').exclude(id=request.user.id) if request.method == 'POST': user_id = request.POST.get('user_id') action = request.POST.get('action') role = request.POST.get('role') target = get_object_or_404(User, id=user_id) _handle_admin_action(request, action, target, role) return redirect('admin_panel') blocked_ids = list(request.user.profile.blocked_users.values_list('id', flat=True)) return render(request, 'chat/admin_panel.html', { 'users': users, 'blocked_ids': blocked_ids, }) @login_required def delete_message_view(request, message_id): msg = get_object_or_404(Message, id=message_id) if request.user.profile.is_moderator() or msg.sender == request.user: msg.deleted = True msg.save() return redirect('channel', channel_id=msg.channel_id) @login_required def block_user_view(request, user_id): if request.user.profile.is_moderator(): target = get_object_or_404(User, id=user_id) request.user.profile.blocked_users.add(target) return redirect('home') def handler404(request, exception): return render(request, 'chat/404.html', status=404) def handler500(request): return render(request, 'chat/500.html', status=500) import urllib.request import urllib.error import json from django.conf import settings @login_required def voice_credentials_view(request): account_id = getattr(settings, 'CLOUDFLARE_ACCOUNT_ID', '') api_token = getattr(settings, 'CLOUDFLARE_API_TOKEN', '') turn_key_id = getattr(settings, 'CLOUDFLARE_TURN_KEY_ID', '') if not all([account_id, api_token, turn_key_id]): return JsonResponse({ "iceServers": [ {"urls": ["stun:stun.cloudflare.com:3478", "stun:stun.cloudflare.com:53"]} ] }) url = f"https://rtc.live.cloudflare.com/v1/turn/keys/{turn_key_id}/credentials/generate-ice-servers" headers = { "Authorization": f"Bearer {api_token}", "Content-Type": "application/json" } body = json.dumps({"ttl": 86400}).encode("utf-8") req = urllib.request.Request(url, data=body, headers=headers, method="POST") try: with urllib.request.urlopen(req, timeout=5) as response: res_data = json.loads(response.read().decode()) return JsonResponse(res_data) except urllib.error.URLError as e: return JsonResponse({ "error": str(e), "iceServers": [ {"urls": ["stun:stun.cloudflare.com:3478", "stun:stun.cloudflare.com:53"]} ] })