"""Chat routes (messages, channels, servers, DMs).""" import json from flask import Blueprint, g, jsonify, request, current_app from database import get_db from decorators import token_required, get_user_badge chat_bp = Blueprint('chat', __name__, url_prefix='/api') def get_socketio(): """Get SocketIO instance from current app context.""" return current_app.extensions.get('socketio') @chat_bp.route('/messages', methods=['GET']) @token_required def get_messages(): """Get messages from a channel or global chat.""" channel_id = request.args.get('channel_id', type=int) db = get_db(current_app) base_query = ''' SELECT m.id, m.sender_id, u.username, u.avatar_url, u.pronouns, m.content, m.timestamp, m.channel_id, m.receiver_id, m.is_global, m.reply_to FROM messages m JOIN users u ON m.sender_id = u.id ''' if channel_id: query = base_query + ' WHERE m.channel_id = ? ORDER BY m.timestamp DESC LIMIT 50' messages = db.execute(query, (channel_id,)).fetchall() else: query = base_query + ' WHERE m.is_global = 1 ORDER BY m.timestamp DESC LIMIT 50' messages = db.execute(query).fetchall() result = [] for m in messages: msg_dict = dict(m) msg_dict['badge'] = get_user_badge(m['username']) result.append(msg_dict) return jsonify([dict(m) for m in reversed(result)]), 200 @chat_bp.route('/messages', methods=['POST']) @token_required def post_message(): """Post a message to global chat, channel, or DM.""" content = None receiver_id = None channel_id = None reply_to = None file_info = None if request.content_type and request.content_type.startswith('multipart/form-data'): content = request.form.get('content') receiver_id = request.form.get('receiver_id', type=int) channel_id = request.form.get('channel_id', type=int) reply_to = request.form.get('reply_to', type=int) if 'file' in request.files and request.files['file'].filename: try: from blueprints.files import process_file_upload file_info = process_file_upload(request.files['file'], current_app) except ValueError: return jsonify({'error': "You can't upload big files, if you need more files, contact dev and buy your own disk space on server"}), 400 else: data = request.json or {} content = data.get('content') receiver_id = data.get('receiver_id') channel_id = data.get('channel_id') reply_to = data.get('reply_to') if file_info: content = json.dumps({'file': file_info, 'text': content or ''}) if not content: return jsonify({'error': 'Content required'}), 400 db = get_db(current_app) is_global = 1 if (receiver_id is None and not channel_id) else 0 cur = db.execute( 'INSERT INTO messages (sender_id, receiver_id, channel_id, content, is_global, reply_to) VALUES (?, ?, ?, ?, ?, ?)', (g.user['id'], receiver_id, channel_id, content, is_global, reply_to) ) db.commit() message_id = cur.lastrowid message = db.execute(''' SELECT m.id, m.sender_id, u.username, u.avatar_url, u.pronouns, m.content, m.timestamp, m.channel_id, m.receiver_id, m.is_global, m.reply_to FROM messages m JOIN users u ON m.sender_id = u.id WHERE m.id = ? ''', (message_id,)).fetchone() message_dict = dict(message) message_dict['badge'] = get_user_badge(message['username']) socketio = get_socketio() if socketio: socketio.emit('new_message', message_dict) return jsonify({'id': message_id, 'status': 'sent'}), 201 @chat_bp.route('/servers', methods=['GET']) @token_required def get_servers(): """Get all servers and their channels.""" db = get_db(current_app) servers = db.execute('SELECT id, name FROM servers').fetchall() result = [] for s in servers: channels = db.execute('SELECT id, name FROM channels WHERE server_id = ?', (s['id'],)).fetchall() result.append({ 'id': s['id'], 'name': s['name'], 'channels': [{'id': c['id'], 'name': c['name']} for c in channels] }) return jsonify(result), 200 @chat_bp.route('/servers', methods=['POST']) @token_required def create_server(): """Create a new server.""" data = request.json name = data.get('name') if not name: return jsonify({'error': 'Server name required'}), 400 db = get_db(current_app) try: cur = db.execute('INSERT INTO servers (name) VALUES (?)', (name,)) server_id = cur.lastrowid db.execute('INSERT INTO channels (server_id, name) VALUES (?, ?)', (server_id, 'general')) db.commit() return jsonify({'id': server_id, 'name': name}), 201 except Exception: return jsonify({'error': 'Server already exists'}), 409 @chat_bp.route('/servers//channels', methods=['POST']) @token_required def create_channel(server_id): """Create a new channel in a server.""" data = request.json name = data.get('name') if not name: return jsonify({'error': 'Channel name required'}), 400 db = get_db(current_app) cur = db.execute('INSERT INTO channels (server_id, name) VALUES (?, ?)', (server_id, name)) db.commit() return jsonify({'id': cur.lastrowid, 'name': name}), 201 @chat_bp.route('/dm/', methods=['GET']) @token_required def get_dm(user_id): """Get direct messages with a specific user.""" since = request.args.get('since') db = get_db(current_app) query = ''' SELECT m.id, m.sender_id, u.username, u.avatar_url, u.pronouns, m.content, m.timestamp, m.channel_id, m.receiver_id, m.is_global, m.reply_to FROM messages m JOIN users u ON m.sender_id = u.id WHERE m.is_global = 0 AND ( (m.sender_id = ? AND m.receiver_id = ?) OR (m.sender_id = ? AND m.receiver_id = ?) ) ''' params = [g.user['id'], user_id, user_id, g.user['id']] if since: query += ' AND m.timestamp > ?' params.append(since) query += ' ORDER BY m.timestamp DESC LIMIT 50' messages = db.execute(query, tuple(params)).fetchall() result = [dict(m) for m in messages] for m in result: m['badge'] = get_user_badge(m['username']) return jsonify(list(reversed(result))), 200