abdullahalioo's picture
Upload 8 files
81923e0 verified
from flask import Flask, request, jsonify, send_from_directory
from flask_socketio import SocketIO, emit, join_room, leave_room
import json
import os
import random
import secrets
from datetime import datetime
from filelock import FileLock
from functools import wraps
app = Flask(__name__, static_folder='static', template_folder='templates')
app.config['SECRET_KEY'] = os.environ.get('SESSION_SECRET', secrets.token_hex(32))
socketio = SocketIO(app, cors_allowed_origins="*", async_mode='eventlet')
DATA_DIR = 'data'
USERS_FILE = os.path.join(DATA_DIR, 'users.json')
MESSAGES_FILE = os.path.join(DATA_DIR, 'messages.json')
USERS_LOCK = FileLock(f"{USERS_FILE}.lock")
MESSAGES_LOCK = FileLock(f"{MESSAGES_FILE}.lock")
active_users = {}
def init_data_files():
os.makedirs(DATA_DIR, exist_ok=True)
if not os.path.exists(USERS_FILE):
with USERS_LOCK:
with open(USERS_FILE, 'w') as f:
json.dump({}, f, indent=2)
if not os.path.exists(MESSAGES_FILE):
with MESSAGES_LOCK:
with open(MESSAGES_FILE, 'w') as f:
json.dump([], f, indent=2)
def read_json(file_path, lock):
with lock:
with open(file_path, 'r') as f:
return json.load(f)
def write_json(file_path, data, lock):
with lock:
with open(file_path, 'w') as f:
json.dump(data, f, indent=2)
def generate_user_id():
users = read_json(USERS_FILE, USERS_LOCK)
while True:
user_id = str(random.randint(10000000, 99999999))
if user_id not in users:
return user_id
def generate_token():
return secrets.token_urlsafe(32)
def generate_message_id():
return secrets.token_urlsafe(16)
def get_user_by_token(token):
users = read_json(USERS_FILE, USERS_LOCK)
for user_id, user_data in users.items():
if user_data.get('token') == token:
return user_id, user_data
return None, None
@app.route('/')
def index():
return send_from_directory('templates', 'index.html')
@app.route('/api/register', methods=['POST'])
def register():
data = request.json
name = data.get('name', '').strip()
email = data.get('email', '').strip()
if not name or not email:
return jsonify({'error': 'Name and email are required'}), 400
users = read_json(USERS_FILE, USERS_LOCK)
for user_data in users.values():
if user_data.get('email') == email:
return jsonify({'error': 'Email already registered'}), 400
user_id = generate_user_id()
token = generate_token()
users[user_id] = {
'name': name,
'email': email,
'token': token,
'contacts': [],
'created_at': datetime.now().isoformat()
}
write_json(USERS_FILE, users, USERS_LOCK)
return jsonify({
'user_id': user_id,
'token': token,
'name': name,
'email': email
})
@app.route('/api/login', methods=['POST'])
def login():
data = request.json
user_id = data.get('user_id', '').strip()
token = data.get('token', '').strip()
if not user_id or not token:
return jsonify({'error': 'User ID and token are required'}), 400
users = read_json(USERS_FILE, USERS_LOCK)
if user_id not in users:
return jsonify({'error': 'Invalid user ID'}), 401
user_data = users[user_id]
if user_data.get('token') != token:
return jsonify({'error': 'Invalid token'}), 401
return jsonify({
'user_id': user_id,
'name': user_data['name'],
'email': user_data['email'],
'contacts': user_data.get('contacts', [])
})
@app.route('/api/contacts/add', methods=['POST'])
def add_contact():
data = request.json
token = data.get('token', '')
contact_id = data.get('contact_id', '').strip()
user_id, user_data = get_user_by_token(token)
if not user_id:
return jsonify({'error': 'Unauthorized'}), 401
if not contact_id:
return jsonify({'error': 'Contact ID is required'}), 400
users = read_json(USERS_FILE, USERS_LOCK)
if contact_id not in users:
return jsonify({'error': 'User not found'}), 404
if contact_id == user_id:
return jsonify({'error': 'Cannot add yourself as contact'}), 400
if contact_id in user_data.get('contacts', []):
return jsonify({'error': 'Contact already added'}), 400
users[user_id]['contacts'] = users[user_id].get('contacts', []) + [contact_id]
write_json(USERS_FILE, users, USERS_LOCK)
contact_data = users[contact_id]
return jsonify({
'contact_id': contact_id,
'name': contact_data['name'],
'email': contact_data['email']
})
@app.route('/api/contacts', methods=['POST'])
def get_contacts():
data = request.json
token = data.get('token', '')
user_id, user_data = get_user_by_token(token)
if not user_id:
return jsonify({'error': 'Unauthorized'}), 401
users = read_json(USERS_FILE, USERS_LOCK)
contacts = []
for contact_id in user_data.get('contacts', []):
if contact_id in users:
contact = users[contact_id]
contacts.append({
'user_id': contact_id,
'name': contact['name'],
'email': contact['email'],
'online': contact_id in active_users
})
return jsonify({'contacts': contacts})
@app.route('/api/messages', methods=['POST'])
def get_messages():
data = request.json
token = data.get('token', '')
contact_id = data.get('contact_id', '')
user_id, user_data = get_user_by_token(token)
if not user_id:
return jsonify({'error': 'Unauthorized'}), 401
all_messages = read_json(MESSAGES_FILE, MESSAGES_LOCK)
conversation_messages = [
msg for msg in all_messages
if (msg['from'] == user_id and msg['to'] == contact_id) or
(msg['from'] == contact_id and msg['to'] == user_id)
]
conversation_messages.sort(key=lambda x: x['timestamp'])
return jsonify({'messages': conversation_messages})
@socketio.on('connect')
def handle_connect():
print(f'Client connected: {request.sid}')
@socketio.on('authenticate')
def handle_authenticate(data):
token = data.get('token', '')
user_id, user_data = get_user_by_token(token)
if user_id:
active_users[user_id] = request.sid
join_room(user_id)
socketio.emit('user_status', {
'user_id': user_id,
'online': True
})
emit('authenticated', {'success': True, 'user_id': user_id})
print(f'User {user_id} ({user_data["name"]}) authenticated')
else:
emit('authenticated', {'success': False, 'error': 'Invalid token'})
@socketio.on('disconnect')
def handle_disconnect(reason=None):
user_id = None
for uid, sid in active_users.items():
if sid == request.sid:
user_id = uid
break
if user_id:
del active_users[user_id]
socketio.emit('user_status', {
'user_id': user_id,
'online': False
})
print(f'User {user_id} disconnected')
@socketio.on('send_message')
def handle_send_message(data):
token = data.get('token', '')
to_user_id = data.get('to', '')
message_text = data.get('message', '')
user_id, user_data = get_user_by_token(token)
if not user_id:
emit('error', {'message': 'Unauthorized'})
return
message_id = generate_message_id()
timestamp = datetime.now().isoformat()
message = {
'message_id': message_id,
'from': user_id,
'to': to_user_id,
'message': message_text,
'timestamp': timestamp,
'status': 'sent'
}
messages = read_json(MESSAGES_FILE, MESSAGES_LOCK)
messages.append(message)
write_json(MESSAGES_FILE, messages, MESSAGES_LOCK)
emit('message_sent', message)
if to_user_id in active_users:
message['status'] = 'delivered'
messages = read_json(MESSAGES_FILE, MESSAGES_LOCK)
for msg in messages:
if msg['message_id'] == message_id:
msg['status'] = 'delivered'
break
write_json(MESSAGES_FILE, messages, MESSAGES_LOCK)
socketio.emit('new_message', message, room=to_user_id)
emit('message_status_update', {
'message_id': message_id,
'status': 'delivered'
})
@socketio.on('message_delivered')
def handle_message_delivered(data):
message_id = data.get('message_id', '')
messages = read_json(MESSAGES_FILE, MESSAGES_LOCK)
for msg in messages:
if msg['message_id'] == message_id and msg['status'] == 'sent':
msg['status'] = 'delivered'
break
write_json(MESSAGES_FILE, messages, MESSAGES_LOCK)
for msg in messages:
if msg['message_id'] == message_id:
from_user_id = msg['from']
if from_user_id in active_users:
socketio.emit('message_status_update', {
'message_id': message_id,
'status': 'delivered'
}, room=from_user_id)
break
@socketio.on('message_read')
def handle_message_read(data):
message_id = data.get('message_id', '')
messages = read_json(MESSAGES_FILE, MESSAGES_LOCK)
for msg in messages:
if msg['message_id'] == message_id:
msg['status'] = 'read'
from_user_id = msg['from']
break
write_json(MESSAGES_FILE, messages, MESSAGES_LOCK)
if from_user_id in active_users:
socketio.emit('message_status_update', {
'message_id': message_id,
'status': 'read'
}, room=from_user_id)
@socketio.on('mark_conversation_read')
def handle_mark_conversation_read(data):
token = data.get('token', '')
contact_id = data.get('contact_id', '')
user_id, user_data = get_user_by_token(token)
if not user_id:
return
messages = read_json(MESSAGES_FILE, MESSAGES_LOCK)
updated_message_ids = []
for msg in messages:
if msg['from'] == contact_id and msg['to'] == user_id and msg['status'] != 'read':
msg['status'] = 'read'
updated_message_ids.append(msg['message_id'])
write_json(MESSAGES_FILE, messages, MESSAGES_LOCK)
if contact_id in active_users:
for msg_id in updated_message_ids:
socketio.emit('message_status_update', {
'message_id': msg_id,
'status': 'read'
}, room=contact_id)
@socketio.on('typing')
def handle_typing(data):
token = data.get('token', '')
to_user_id = data.get('to', '')
is_typing = data.get('typing', False)
user_id, user_data = get_user_by_token(token)
if not user_id:
return
if to_user_id in active_users:
socketio.emit('user_typing', {
'user_id': user_id,
'typing': is_typing
}, room=to_user_id)
# Hugging Face Spaces compatibility
if __name__ == '__main__':
init_data_files()
print("WhatsApp-like server starting on Hugging Face...")
print("Access the app at https://your-username-your-app-name.hf.space")
socketio.run(app, host='0.0.0.0', port=7860, debug=False)