import os
import uuid
import json
from datetime import datetime
from flask import Flask, request, jsonify, send_file
from flask_socketio import SocketIO, emit
import base64
import io
app = Flask(__name__)
app.config['SECRET_KEY'] = 'messenger-secret-key'
app.config['DEBUG'] = False
# Настройки для production среды
socketio = SocketIO(
app,
cors_allowed_origins="*",
ping_timeout=60,
ping_interval=25,
logger=True,
engineio_logger=False,
async_mode='eventlet',
max_http_buffer_size=100 * 1024 * 1024 # 100MB для больших файлов
)
# Хранилища данных
users = {}
messages = []
voice_messages = {}
active_calls = {}
HTML_TEMPLATE = '''
Мобильный Мессенджер
Подключение...
Запись голосового сообщения...
Нажмите для остановки
Входящий звонок
...
'''
@app.route('/')
def index():
return HTML_TEMPLATE
@socketio.on('connect')
def handle_connect():
print(f'Client connected: {request.sid}')
emit('user_list_update', {
'count': len(users),
'users': [user_data['username'] for user_data in users.values()]
})
@socketio.on('disconnect')
def handle_disconnect():
print(f'Client disconnected: {request.sid}')
# Find and remove the disconnected user
disconnected_user = None
for user_id, user_data in list(users.items()):
if user_data.get('sid') == request.sid:
disconnected_user = user_data['username']
# End any active calls
for call_id, call_data in list(active_calls.items()):
if call_data['from_user'] == disconnected_user or call_data['to_user'] == disconnected_user:
try:
emit('call_ended', room=call_data['to_sid'])
except:
pass
try:
emit('call_ended', room=call_data['from_sid'])
except:
pass
del active_calls[call_id]
del users[user_id]
break
if disconnected_user:
emit('user_list_update', {
'count': len(users),
'users': [user_data['username'] for user_data in users.values()]
}, broadcast=True)
@socketio.on('register')
def handle_register(username):
user_id = str(uuid.uuid4())
users[user_id] = {
'username': username,
'sid': request.sid,
'joined': datetime.now().isoformat()
}
emit('registration_success', {
'user_id': user_id,
'username': username
})
emit('user_list_update', {
'count': len(users),
'users': [user_data['username'] for user_data in users.values()]
}, broadcast=True)
@socketio.on('message')
def handle_message(data):
message = {
'id': str(uuid.uuid4()),
'sender': data['sender'],
'text': data['text'],
'timestamp': datetime.now().isoformat()
}
messages.append(message)
emit('new_message', message, broadcast=True)
@socketio.on('voice_message')
def handle_voice_message(data):
try:
audio_data = base64.b64decode(data['audio_data'])
filename = f"voice_{uuid.uuid4()}.webm"
with open(filename, 'wb') as f:
f.write(audio_data)
voice_data = {
'filename': filename,
'sender': data['sender'],
'duration': data.get('duration', 0),
'timestamp': datetime.now().isoformat()
}
voice_messages[filename] = voice_data
emit('new_voice_message', voice_data, broadcast=True)
except Exception as e:
print(f"Error handling voice message: {e}")
@socketio.on('call_request')
def handle_call_request(data):
target_username = data.get('to_user')
target_sid = None
for user_data in users.values():
if user_data['username'] == target_username:
target_sid = user_data['sid']
break
if target_sid:
active_calls[data['call_id']] = {
'from_user': data['from_user'],
'to_user': target_username,
'from_sid': request.sid,
'to_sid': target_sid,
'is_video': data.get('isVideo', False)
}
emit('incoming_call', {
'call_id': data['call_id'],
'from_user': data['from_user'],
'isVideo': data.get('isVideo', False)
}, room=target_sid)
@socketio.on('call_answer')
def handle_call_answer(data):
call_id = data.get('call_id')
if call_id in active_calls:
call_data = active_calls[call_id]
if data['answer']:
emit('call_answered', {
'answer': True,
'isVideo': call_data['is_video']
}, room=call_data['from_sid'])
else:
emit('call_answered', {
'answer': False
}, room=call_data['from_sid'])
del active_calls[call_id]
@socketio.on('call_end')
def handle_call_end(data):
call_id = data.get('call_id')
if call_id in active_calls:
call_data = active_calls[call_id]
try:
emit('call_ended', room=call_data['to_sid'])
except:
pass
try:
emit('call_ended', room=call_data['from_sid'])
except:
pass
del active_calls[call_id]
@socketio.on('webrtc_offer')
def handle_webrtc_offer(data):
target_username = data.get('to_user')
target_sid = None
for user_data in users.values():
if user_data['username'] == target_username:
target_sid = user_data['sid']
break
if target_sid:
emit('webrtc_offer', {
'offer': data['offer'],
'from_user': data.get('from_user'),
'call_id': data.get('call_id')
}, room=target_sid)
@socketio.on('webrtc_answer')
def handle_webrtc_answer(data):
target_username = data.get('to_user')
target_sid = None
for user_data in users.values():
if user_data['username'] == target_username:
target_sid = user_data['sid']
break
if target_sid:
emit('webrtc_answer', {
'answer': data['answer'],
'from_user': data.get('from_user')
}, room=target_sid)
@socketio.on('webrtc_ice_candidate')
def handle_webrtc_ice_candidate(data):
target_username = data.get('to_user')
target_sid = None
for user_data in users.values():
if user_data['username'] == target_username:
target_sid = user_data['sid']
break
if target_sid:
emit('webrtc_ice_candidate', {
'candidate': data['candidate'],
'from_user': data.get('from_user')
}, room=target_sid)
@app.route('/voice/')
def get_voice_message(filename):
if filename in voice_messages:
return send_file(filename, mimetype='audio/webm')
return 'File not found', 404
def cleanup_temp_files():
"""Очистка временных файлов"""
import glob
for filename in glob.glob("voice_*.webm"):
try:
os.remove(filename)
except:
pass
if __name__ == '__main__':
try:
print("Запуск мобильного мессенджера...")
print("Порт: 7860")
socketio.run(
app,
host='0.0.0.0',
port=7860,
debug=False,
allow_unsafe_werkzeug=True
)
finally:
cleanup_temp_files()