Dnishe/blueprints/chat.py
2026-06-08 21:56:14 +00:00

175 lines
6.4 KiB
Python

"""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/<int:server_id>/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/<int:user_id>', 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