""" Chat web interface routes Provides HTML templates and endpoints for web-based chat """ from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify from flask_login import login_required, current_user from sqlalchemy import desc, and_ from datetime import datetime, timedelta from app.extensions import db from app.models import ChatRoom, ChatMessage, ChatParticipant, Post # Create blueprint chat = Blueprint('chat', __name__) @chat.route('/') @login_required def index(): """Chat main page with room list and rules""" # Get user's recent chat rooms user_rooms = db.session.query(ChatRoom).join(ChatParticipant).filter( and_( ChatParticipant.user_id == current_user.id, ChatRoom.is_active == True ) ).order_by(desc(ChatRoom.last_activity)).limit(10).all() # Get public rooms that are active public_rooms = ChatRoom.query.filter( ChatRoom.is_active == True, ChatRoom.is_private == False ).order_by(desc(ChatRoom.last_activity)).limit(10).all() return render_template('chat/index.html', user_rooms=user_rooms, public_rooms=public_rooms) @chat.route('/room/') @login_required def room(room_id): """Chat room interface""" room = ChatRoom.query.get_or_404(room_id) # Check access if room.is_private: participant = ChatParticipant.query.filter_by( room_id=room_id, user_id=current_user.id ).first() if not participant: flash('You do not have access to this chat room.', 'error') return redirect(url_for('chat.index')) # Get or create participant record participant = ChatParticipant.query.filter_by( room_id=room_id, user_id=current_user.id ).first() if not participant and not room.is_private: # Auto-join public rooms participant = ChatParticipant( room_id=room_id, user_id=current_user.id, role='member' ) db.session.add(participant) db.session.commit() # Get recent messages messages = ChatMessage.query.filter_by( room_id=room_id, is_deleted=False ).order_by(ChatMessage.created_at).limit(50).all() # Get participants participants = ChatParticipant.query.filter_by(room_id=room_id).all() return render_template('chat/room.html', room=room, messages=messages, participants=participants, current_participant=participant) @chat.route('/create') @login_required def create_room_form(): """Show create room form""" # Get available posts for post discussions recent_posts = Post.query.filter_by(published=True).order_by( desc(Post.created_at) ).limit(20).all() # Check if a specific post was requested pre_selected_post = request.args.get('post_id') if pre_selected_post: try: pre_selected_post = int(pre_selected_post) except ValueError: pre_selected_post = None return render_template('chat/create_room.html', posts=recent_posts, pre_selected_post=pre_selected_post) @chat.route('/create', methods=['POST']) @login_required def create_room(): """Create a new chat room""" room_name = request.form.get('room_name') description = request.form.get('description', '') room_type = request.form.get('room_type', 'general') is_private = bool(request.form.get('is_private')) related_post_id = request.form.get('related_post_id') if not room_name: flash('Room name is required.', 'error') return redirect(url_for('chat.create_room_form')) # Convert to integer if post ID is provided if related_post_id: try: related_post_id = int(related_post_id) # Verify the post exists related_post = Post.query.get(related_post_id) if not related_post: flash('Selected post does not exist.', 'error') return redirect(url_for('chat.create_room_form')) # If post is selected, set room type to post_discussion room_type = 'post_discussion' except ValueError: related_post_id = None else: related_post_id = None # If no post selected, ensure it's general discussion if room_type == 'post_discussion': room_type = 'general' # Check if room name already exists existing_room = ChatRoom.query.filter_by(name=room_name).first() if existing_room: flash('A room with that name already exists.', 'error') return redirect(url_for('chat.create_room_form')) try: # Create the room room = ChatRoom( name=room_name, description=description, room_type=room_type, is_private=is_private, is_active=True, created_by_id=current_user.id, related_post_id=related_post_id ) db.session.add(room) db.session.flush() # Get the room ID # Add creator as participant with admin role participant = ChatParticipant( room_id=room.id, user_id=current_user.id, role='admin', joined_at=datetime.utcnow() ) db.session.add(participant) # Add welcome message if related_post_id: welcome_content = f"Welcome to the discussion for '{related_post.title}'! This room was created by {current_user.nickname} to discuss this post." else: welcome_content = f"Welcome to {room_name}! This room was created by {current_user.nickname}." welcome_message = ChatMessage( content=welcome_content, room_id=room.id, sender_id=current_user.id, is_system_message=True ) db.session.add(welcome_message) # Update room activity room.last_activity = datetime.utcnow() room.message_count = 1 db.session.commit() if related_post_id: flash(f'Chat room "{room_name}" created successfully and linked to the post!', 'success') else: flash(f'Chat room "{room_name}" created successfully!', 'success') return redirect(url_for('chat.room', room_id=room.id)) except Exception as e: db.session.rollback() flash(f'Error creating room: {str(e)}', 'error') return redirect(url_for('chat.create_room_form')) @chat.route('/support') @login_required def support(): """Admin support page""" # Get user's recent support tickets (rooms they created for support) recent_tickets = ChatRoom.query.filter( ChatRoom.room_type == 'admin_support', ChatRoom.created_by_id == current_user.id ).order_by(desc(ChatRoom.created_at)).limit(5).all() return render_template('chat/support.html', recent_tickets=recent_tickets) @chat.route('/embed/') @login_required def embed_post_chat(post_id): """Embedded chat widget for post pages""" post = Post.query.get_or_404(post_id) # Find existing discussion room discussion_room = ChatRoom.query.filter( and_( ChatRoom.room_type == 'post_discussion', ChatRoom.related_post_id == post_id, ChatRoom.is_active == True ) ).first() return render_template('chat/embed.html', post=post, discussion_room=discussion_room) @chat.route('/post-discussions') @login_required def post_discussions(): """View all chat rooms related to posts""" page = request.args.get('page', 1, type=int) # Get all rooms that are linked to posts post_rooms = ChatRoom.query.filter( ChatRoom.related_post_id.isnot(None), ChatRoom.is_active == True ).join(Post).order_by(ChatRoom.last_activity.desc()).paginate( page=page, per_page=20, error_out=False ) # Get statistics total_post_discussions = ChatRoom.query.filter( ChatRoom.related_post_id.isnot(None), ChatRoom.is_active == True ).count() active_discussions = ChatRoom.query.filter( ChatRoom.related_post_id.isnot(None), ChatRoom.is_active == True, ChatRoom.last_activity >= datetime.utcnow() - timedelta(days=7) ).count() return render_template('chat/post_discussions.html', rooms=post_rooms, total_discussions=total_post_discussions, active_discussions=active_discussions) @chat.route('/post//discussions') @login_required def post_specific_discussions(post_id): """View all chat rooms for a specific post""" post = Post.query.get_or_404(post_id) # Get all rooms for this specific post rooms = ChatRoom.query.filter( ChatRoom.related_post_id == post_id, ChatRoom.is_active == True ).order_by(ChatRoom.last_activity.desc()).all() return render_template('chat/post_specific_discussions.html', post=post, rooms=rooms)