fixed messaging!

This commit is contained in:
2025-05-28 14:06:36 +02:00
parent 2a1b6f8a22
commit 082924a3ba
11 changed files with 315 additions and 273 deletions

View File

@@ -1,13 +1,11 @@
from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify, send_file
from flask_login import login_required, current_user
from flask_socketio import emit, join_room, leave_room
from models import db, Conversation, User, Message, MessageAttachment
from forms import ConversationForm
from routes.auth import require_password_change
import os
from werkzeug.utils import secure_filename
from datetime import datetime
from extensions import socketio
conversations_bp = Blueprint('conversations', __name__, url_prefix='/conversations')
@@ -239,30 +237,45 @@ def delete_conversation(conversation_id):
flash('Conversation has been deleted successfully.', 'success')
return redirect(url_for('conversations.conversations'))
@socketio.on('join_conversation')
@conversations_bp.route('/<int:conversation_id>/messages', methods=['GET'])
@login_required
def on_join(data):
conversation_id = data.get('conversation_id')
@require_password_change
def get_messages(conversation_id):
conversation = Conversation.query.get_or_404(conversation_id)
# Check if user is a member
if not current_user.is_admin and current_user not in conversation.members:
return
return jsonify({'error': 'Unauthorized'}), 403
# Join the room
join_room(f'conversation_{conversation_id}')
@socketio.on('leave_conversation')
@login_required
def on_leave(data):
conversation_id = data.get('conversation_id')
leave_room(f'conversation_{conversation_id}')
@socketio.on('heartbeat')
@login_required
def on_heartbeat(data):
# Just acknowledge the heartbeat to keep the connection alive
return {'status': 'ok'}
# Get the last message ID from the request
last_message_id = request.args.get('last_message_id', type=int)
# Query for new messages
query = Message.query.filter_by(conversation_id=conversation_id)
if last_message_id:
query = query.filter(Message.id > last_message_id)
messages = query.order_by(Message.created_at.asc()).all()
# Format messages for response
message_data = [{
'id': message.id,
'content': message.content,
'created_at': message.created_at.strftime('%b %d, %Y %H:%M'),
'sender_id': str(message.user_id),
'sender_name': f"{message.user.username} {message.user.last_name}",
'sender_avatar': url_for('profile_pic', filename=message.user.profile_picture) if message.user.profile_picture else url_for('static', filename='default-avatar.png'),
'attachments': [{
'name': attachment.name,
'size': attachment.size,
'url': url_for('conversations.download_attachment', message_id=message.id, attachment_index=index)
} for index, attachment in enumerate(message.attachments)]
} for message in messages]
return jsonify({
'success': True,
'messages': message_data
})
@conversations_bp.route('/<int:conversation_id>/send_message', methods=['POST'])
@login_required
@@ -272,59 +285,46 @@ def send_message(conversation_id):
# Check if user is a member
if not current_user.is_admin and current_user not in conversation.members:
return jsonify({'success': False, 'error': 'You do not have access to this conversation.'}), 403
return jsonify({'error': 'Unauthorized'}), 403
message_content = request.form.get('message', '').strip()
file_count = int(request.form.get('file_count', 0))
if not message_content and file_count == 0:
return jsonify({'success': False, 'error': 'Message or file is required.'}), 400
return jsonify({'error': 'Message cannot be empty'}), 400
# Create new message
# Create the message
message = Message(
content=message_content,
conversation_id=conversation_id,
user_id=current_user.id
user_id=current_user.id,
conversation_id=conversation_id
)
# Create conversation-specific directory
conversation_dir = os.path.join(UPLOAD_FOLDER, str(conversation_id))
os.makedirs(conversation_dir, exist_ok=True)
db.session.add(message)
db.session.flush() # Get the message ID
# Handle file attachments
attachments = []
for i in range(file_count):
file = request.files.get(f'file_{i}')
if file and file.filename:
if not allowed_file(file.filename):
return jsonify({'success': False, 'error': f'File type not allowed: {file.filename}'}), 400
if file.content_length and file.content_length > MAX_FILE_SIZE:
return jsonify({'success': False, 'error': f'File size exceeds limit: {file.filename}'}), 400
# Generate unique filename
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
filename = secure_filename(file.filename)
unique_filename = f"{timestamp}_{filename}"
file_path = os.path.join(conversation_dir, unique_filename)
# Save file
file.save(file_path)
# Create attachment record
attachment = MessageAttachment(
name=filename,
path=file_path,
type=get_file_extension(filename),
size=os.path.getsize(file_path)
)
message.attachments.append(attachment)
attachments.append(attachment)
file_key = f'file_{i}'
if file_key in request.files:
file = request.files[file_key]
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file_path = os.path.join(UPLOAD_FOLDER, filename)
file.save(file_path)
attachment = MessageAttachment(
message_id=message.id,
name=filename,
path=file_path,
size=os.path.getsize(file_path)
)
db.session.add(attachment)
attachments.append(attachment)
db.session.add(message)
db.session.commit()
# Prepare message data for WebSocket
# Prepare message data for response
message_data = {
'id': message.id,
'content': message.content,
@@ -339,10 +339,6 @@ def send_message(conversation_id):
} for index, attachment in enumerate(attachments)]
}
# Emit the message to all users in the conversation room
socketio.emit('new_message', message_data, room=f'conversation_{conversation_id}')
# Return response with message data
return jsonify({
'success': True,
'message': message_data
@@ -369,43 +365,4 @@ def download_attachment(message_id, attachment_index):
)
except (IndexError, Exception) as e:
flash('File not found.', 'error')
return redirect(url_for('conversations.conversation', conversation_id=conversation.id))
@conversations_bp.route('/<int:conversation_id>/messages')
@login_required
@require_password_change
def get_messages(conversation_id):
conversation = Conversation.query.get_or_404(conversation_id)
# Check if user is a member
if not current_user.is_admin and current_user not in conversation.members:
return jsonify({'success': False, 'error': 'You do not have access to this conversation.'}), 403
# Get the last message ID from the request
last_message_id = request.args.get('last_message_id', type=int)
# Query for new messages
query = Message.query.filter_by(conversation_id=conversation_id)
if last_message_id:
query = query.filter(Message.id > last_message_id)
messages = query.order_by(Message.created_at.asc()).all()
# Format messages for response
formatted_messages = []
for message in messages:
formatted_messages.append({
'id': message.id,
'content': message.content,
'created_at': message.created_at.strftime('%b %d, %Y %H:%M'),
'sender_id': str(message.user.id),
'sender_name': f"{message.user.username} {message.user.last_name}",
'sender_avatar': url_for('profile_pic', filename=message.user.profile_picture) if message.user.profile_picture else url_for('static', filename='default-avatar.png'),
'attachments': [{
'name': attachment.name,
'size': attachment.size,
'url': url_for('conversations.download_attachment', message_id=message.id, attachment_index=index)
} for index, attachment in enumerate(message.attachments)]
})
return jsonify({'success': True, 'messages': formatted_messages})
return redirect(url_for('conversations.conversation', conversation_id=conversation.id))