from flask import Flask, render_template_string, request, redirect, url_for, session, flash, jsonify from flask_caching import Cache import json import os import logging import threading import time from datetime import datetime, timedelta from huggingface_hub import HfApi, hf_hub_download from werkzeug.utils import secure_filename import random app = Flask(__name__) app.secret_key = os.getenv("FLASK_SECRET_KEY", "supersecretkey") DATA_FILE = 'data_adusis.json' REPO_ID = "Eluza133/A12d12s12" HF_TOKEN_WRITE = os.getenv("HF_TOKEN") HF_TOKEN_READ = os.getenv("HF_TOKEN_READ") or HF_TOKEN_WRITE cache = Cache(app, config={'CACHE_TYPE': 'simple'}) logging.basicConfig(level=logging.INFO) @cache.memoize(timeout=300) def load_data(): try: download_db_from_hf() with open(DATA_FILE, 'r', encoding='utf-8') as file: data = json.load(file) if not isinstance(data, dict): logging.warning("Данные не в формате dict, инициализация пустой базы") return {'posts': [], 'users': {}, 'general_chat': [], 'private_chats': {}} data.setdefault('posts', []) data.setdefault('users', {}) data.setdefault('general_chat', []) data.setdefault('private_chats', {}) for user in data['users']: data['users'][user].setdefault('last_chat_visit', '1970-01-01 00:00:00') data['users'][user].setdefault('last_private_visit', '1970-01-01 00:00:00') data['users'][user].setdefault('last_seen', '1970-01-01 00:00:00') logging.info("Данные успешно загружены") return data except Exception as e: logging.error(f"Ошибка при загрузке данных: {e}") return {'posts': [], 'users': {}, 'general_chat': [], 'private_chats': {}} def save_data(data): try: with open(DATA_FILE, 'w', encoding='utf-8') as file: json.dump(data, file, ensure_ascii=False, indent=4) upload_db_to_hf() cache.clear() logging.info("Данные сохранены и загружены на HF") except Exception as e: logging.error(f"Ошибка при сохранении данных: {e}") raise def upload_db_to_hf(): try: api = HfApi() api.upload_file( path_or_fileobj=DATA_FILE, path_in_repo=DATA_FILE, repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN_WRITE, commit_message=f"Бэкап {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" ) logging.info("База данных загружена на Hugging Face") except Exception as e: logging.error(f"Ошибка при загрузке базы данных: {e}") def download_db_from_hf(): try: hf_hub_download( repo_id=REPO_ID, filename=DATA_FILE, repo_type="dataset", token=HF_TOKEN_READ, local_dir=".", local_dir_use_symlinks=False ) logging.info("База данных скачана с Hugging Face") except Exception as e: logging.error(f"Ошибка при скачивании базы данных: {e}") if not os.path.exists(DATA_FILE): with open(DATA_FILE, 'w', encoding='utf-8') as f: json.dump({'posts': [], 'users': {}, 'general_chat': [], 'private_chats': {}}, f) def periodic_backup(): while True: upload_db_to_hf() time.sleep(1800) def get_unread_count(data, username): if username not in data['users']: return 0 last_visit = datetime.strptime(data['users'][username]['last_chat_visit'], '%Y-%m-%d %H:%M:%S') return sum(1 for msg in data['general_chat'] if datetime.strptime(msg['time'], '%Y-%m-%d %H:%M:%S') > last_visit) def get_private_unread_count(data, username): if username not in data['users']: return 0 last_visit = datetime.strptime(data['users'][username]['last_private_visit'], '%Y-%m-%d %H:%M:%S') unread = 0 for chat_key, messages in data['private_chats'].items(): users = chat_key.split('_') if username in users: unread += sum(1 for msg in messages if datetime.strptime(msg['time'], '%Y-%m-%d %H:%M:%S') > last_visit and msg['sender'] != username) return unread def is_user_online(data, username): if username not in data['users']: return False last_seen = datetime.strptime(data['users'][username]['last_seen'], '%Y-%m-%d %H:%M:%S') return (datetime.now() - last_seen).total_seconds() < 300 def update_last_seen(data, username): if username in data['users']: data['users'][username]['last_seen'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') save_data(data) BASE_STYLE = ''' :root { --primary: #ff4d6d; --secondary: #00ddeb; --accent: #8b5cf6; --background-light: #f7f9fc; --background-dark: #1e1b2e; --card-bg: rgba(255, 255, 255, 0.97); --card-bg-dark: rgba(40, 35, 60, 0.97); --text-light: #2a1e5a; --text-dark: #e8e1ff; --shadow: 0 12px 40px rgba(0, 0, 0, 0.15); --glass-bg: rgba(255, 255, 255, 0.2); --transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); --online: #34c759; --offline: #ff3b30; } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Inter', sans-serif; background: var(--background-light); color: var(--text-light); line-height: 1.7; overflow-x: hidden; } body.dark { background: var(--background-dark); color: var(--text-dark); } .sidebar { position: fixed; top: 0; left: 0; width: 300px; height: 100%; background: var(--glass-bg); backdrop-filter: blur(15px); padding: 30px; box-shadow: var(--shadow); z-index: 1000; transition: transform var(--transition); } .sidebar.hidden { transform: translateX(-100%); } .sidebar-header { display: flex; align-items: center; gap: 15px; margin-bottom: 40px; } .nav-brand { font-size: 2em; font-weight: 900; background: linear-gradient(135deg, var(--primary), var(--accent)); -webkit-background-clip: text; color: transparent; } .logo { width: 45px; height: 45px; border-radius: 16px; box-shadow: var(--shadow); transition: transform var(--transition); } .logo:hover { transform: scale(1.1); } .nav-links { display: flex; flex-direction: column; gap: 15px; } .nav-link { display: flex; align-items: center; gap: 15px; padding: 15px 25px; background: var(--card-bg); color: var(--text-light); text-decoration: none; border-radius: 16px; font-size: 1.1em; font-weight: 600; transition: var(--transition); position: relative; } body.dark .nav-link { background: var(--card-bg-dark); color: var(--text-dark); } .nav-link:hover { transform: translateX(8px); background: var(--primary); color: white; box-shadow: 0 8px 25px rgba(255, 77, 109, 0.5); } .nav-link .badge { position: absolute; right: 15px; background: var(--secondary); color: white; padding: 5px 12px; border-radius: 14px; font-size: 0.85em; font-weight: 700; } .logout-btn { background: var(--secondary); color: white; } .logout-btn:hover { background: #00b8c5; } .menu-btn { display: none; font-size: 30px; background: var(--glass-bg); border: none; color: var(--primary); cursor: pointer; position: fixed; top: 20px; left: 20px; z-index: 1001; padding: 12px; border-radius: 50%; box-shadow: var(--shadow); transition: var(--transition); } .menu-btn:hover { background: var(--primary); color: white; } .container { margin: 20px auto 20px 320px; max-width: 1200px; padding: 30px; transition: var(--transition); } .btn { padding: 15px 30px; background: var(--primary); color: white; border: none; border-radius: 16px; cursor: pointer; font-size: 1.1em; font-weight: 600; transition: var(--transition); display: inline-flex; align-items: center; gap: 10px; box-shadow: var(--shadow); } .btn:hover { transform: scale(1.05); background: #e6415f; box-shadow: 0 10px 30px rgba(255, 77, 109, 0.6); } input, textarea, select { width: 100%; padding: 15px; margin: 12px 0; border: none; border-radius: 16px; background: var(--glass-bg); color: var(--text-light); font-size: 1.1em; transition: var(--transition); box-shadow: inset 0 3px 10px rgba(0, 0, 0, 0.1); } body.dark input, body.dark textarea, body.dark select { color: var(--text-dark); } input:focus, textarea:focus, select:focus { outline: none; background: rgba(255, 255, 255, 0.25); box-shadow: 0 0 0 4px var(--primary); } .modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.9); z-index: 2000; justify-content: center; align-items: center; transition: opacity var(--transition); } .modal img, .modal video { max-width: 95%; max-height: 95%; object-fit: contain; border-radius: 20px; box-shadow: var(--shadow); animation: zoomIn 0.4s ease; } .theme-toggle { position: fixed; top: 20px; right: 20px; background: var(--glass-bg); border: none; padding: 12px; border-radius: 50%; cursor: pointer; font-size: 26px; box-shadow: var(--shadow); transition: var(--transition); } .theme-toggle:hover { transform: rotate(90deg); background: var(--accent); color: white; } .status-dot { width: 12px; height: 12px; border-radius: 50%; display: inline-block; margin-left: 8px; } .online { background: var(--online); } .offline { background: var(--offline); } @keyframes zoomIn { from { opacity: 0; transform: scale(0.85); } to { opacity: 1; transform: scale(1); } } @media (max-width: 900px) { .sidebar { width: 100%; max-width: 300px; transform: translateX(-100%); } .sidebar.active { transform: translateX(0); } .menu-btn { display: block; } .container { margin: 80px 20px 20px 20px; max-width: calc(100% - 40px); } .theme-toggle { top: 80px; } } @media (max-width: 480px) { .nav-brand { font-size: 1.6em; } .nav-link { font-size: 1em; padding: 12px 20px; } .btn { padding: 12px 20px; font-size: 1em; } .logo { width: 40px; height: 40px; } } ''' NAV_HTML = ''' ''' @app.route('/register', methods=['GET', 'POST']) def register(): if request.method == 'POST': username = request.form.get('username') password = request.form.get('password') data = load_data() if username in data['users']: flash('Пользователь уже существует!') return redirect(url_for('register')) data['users'][username] = { 'password': password, 'bio': '', 'link': '', 'avatar': None, 'last_chat_visit': '1970-01-01 00:00:00', 'last_private_visit': '1970-01-01 00:00:00', 'last_seen': datetime.now().strftime('%Y-%m-%d %H:%M:%S') } save_data(data) flash('Регистрация успешна! Пожалуйста, войдите.') return redirect(url_for('login')) is_authenticated = 'username' in session username = session.get('username', None) data = load_data() unread_count = get_unread_count(data, username) if is_authenticated else 0 private_unread_count = get_private_unread_count(data, username) if is_authenticated else 0 user_count = len(data['users']) is_online = is_user_online(data, username) if is_authenticated else False html = ''' Register - Content Hub ''' + NAV_HTML + '''

Register

{% with messages = get_flashed_messages() %} {% if messages %} {% for message in messages %}
{{ message }}
{% endfor %} {% endif %} {% endwith %}
''' return render_template_string(html, is_authenticated=is_authenticated, username=username, unread_count=unread_count, user_count=user_count, private_unread_count=private_unread_count, is_online=is_online) @app.route('/login', methods=['GET', 'POST']) def login(): data = load_data() if request.method == 'POST': username = request.form.get('username') password = request.form.get('password') if username in data['users'] and data['users'][username]['password'] == password: session['username'] = username session.permanent = True update_last_seen(data, username) return redirect(url_for('feed')) flash('Неверное имя пользователя или пароль!') return redirect(url_for('login')) is_authenticated = 'username' in session username = session.get('username', None) if is_authenticated: update_last_seen(data, username) unread_count = get_unread_count(data, username) if is_authenticated else 0 private_unread_count = get_private_unread_count(data, username) if is_authenticated else 0 user_count = len(data['users']) is_online = is_user_online(data, username) if is_authenticated else False html = ''' Login - Content Hub ''' + NAV_HTML + '''

Login

{% with messages = get_flashed_messages() %} {% if messages %} {% for message in messages %}
{{ message }}
{% endfor %} {% endif %} {% endwith %}
''' return render_template_string(html, is_authenticated=is_authenticated, username=username, unread_count=unread_count, user_count=user_count, private_unread_count=private_unread_count, is_online=is_online) @app.route('/logout') def logout(): data = load_data() username = session.get('username', None) if username: update_last_seen(data, username) session.pop('username', None) return redirect(url_for('feed')) @app.route('/toggle_like/', methods=['POST']) def toggle_like(post_id): if 'username' not in session: return jsonify({'error': 'Not authenticated'}), 401 data = load_data() username = session['username'] post = next((p for p in data['posts'] if p['id'] == post_id), None) if not post: return jsonify({'error': 'Post not found'}), 404 likes = post.get('likes', []) if username in likes: post['likes'] = [u for u in likes if u != username] liked = False else: post['likes'] = likes + [username] liked = True save_data(data) return jsonify({'liked': liked, 'likes_count': len(post['likes'])}) @app.route('/increment_view/', methods=['POST']) def increment_view(post_id): data = load_data() post = next((p for p in data['posts'] if p['id'] == post_id), None) if post: post['views'] = post.get('views', 0) + 1 save_data(data) return jsonify({'views': post['views']}) return jsonify({'error': 'Post not found'}), 404 @app.route('/', methods=['GET', 'POST']) def feed(): data = load_data() username = session.get('username', None) if username: update_last_seen(data, username) posts = sorted(data.get('posts', []), key=lambda x: datetime.strptime(x['upload_date'], '%Y-%m-%d %H:%M:%S'), reverse=True) is_authenticated = 'username' in session unread_count = get_unread_count(data, username) if is_authenticated else 0 private_unread_count = get_private_unread_count(data, username) if is_authenticated else 0 user_count = len(data['users']) is_online = is_user_online(data, username) if is_authenticated else False if request.method == 'POST' and is_authenticated: post_id = request.form.get('post_id') post = next((p for p in data['posts'] if p['id'] == post_id), None) if not post: return "Пост не найден", 404 if 'comment' in request.form: comment_text = request.form.get('comment') if comment_text: post['comments'] = post.get('comments', []) + [{ 'user': username, 'text': comment_text, 'date': datetime.now().strftime('%Y-%m-%d %H:%M:%S') }] save_data(data) return redirect(url_for('feed')) html = ''' Adusis - QoS, BBC, BNWO HUB ''' + NAV_HTML + '''
{% for post in posts %}
{% if post['type'] == 'video' %} {% else %} {{ post['title'] }} {% endif %}

{{ post['title'] }}

{{ post['description']|truncate(150) }}

By: {{ post['uploader'] }}

{{ post['likes']|length }} {{ post['comments']|length }} {{ post['views'] }}
{% for comment in post.get('comments', []) %}
{{ comment['user'] }}: {{ comment['text'] }}
{% endfor %} {% if is_authenticated %}
{% endif %}
{% endfor %}
''' return render_template_string(html, posts=posts, is_authenticated=is_authenticated, username=username, repo_id=REPO_ID, unread_count=unread_count, user_count=user_count, private_unread_count=private_unread_count, is_online=is_online, is_user_online=lambda u: is_user_online(data, u)) @app.route('/post/', methods=['GET', 'POST']) def post_page(post_id): data = load_data() username = session.get('username', None) if username: update_last_seen(data, username) post = next((p for p in data['posts'] if p['id'] == post_id), None) if not post: return "Пост не найден", 404 is_authenticated = 'username' in session unread_count = get_unread_count(data, username) if is_authenticated else 0 private_unread_count = get_private_unread_count(data, username) if is_authenticated else 0 user_count = len(data['users']) is_online = is_user_online(data, username) if is_authenticated else False post['views'] = post.get('views', 0) + 1 save_data(data) if request.method == 'POST' and is_authenticated: if 'comment' in request.form: comment_text = request.form.get('comment') if comment_text: post['comments'] = post.get('comments', []) + [{'user': username, 'text': comment_text, 'date': datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] save_data(data) html = ''' {{ post['title'] }} - Content Hub ''' + NAV_HTML + '''

{{ post['title'] }}

{% if post['type'] == 'video' %} {% else %} {{ post['title'] }} {% endif %}

{{ post['description'] }}

By: {{ post['uploader'] }} | {{ post['upload_date'] }}

Views: {{ post['views'] }} | Likes: {{ post['likes']|length }}

{% if is_authenticated %}
{% endif %}

Comments

{% for comment in post.get('comments', []) %}

{{ comment['user'] }} ({{ comment['date'] }}): {{ comment['text'] }}

{% endfor %} Back to Feed {% if not is_authenticated %}

Login to like and comment.

{% endif %}
''' return render_template_string(html, post=post, repo_id=REPO_ID, is_authenticated=is_authenticated, username=username, unread_count=unread_count, user_count=user_count, private_unread_count=private_unread_count, is_online=is_online, is_user_online=lambda u: is_user_online(data, u)) @app.route('/profile', methods=['GET', 'POST']) def profile(): if 'username' not in session: flash('Войдите, чтобы просмотреть свой профиль!') return redirect(url_for('login')) data = load_data() username = session['username'] update_last_seen(data, username) user_posts = sorted([p for p in data['posts'] if p['uploader'] == username], key=lambda x: datetime.strptime(x['upload_date'], '%Y-%m-%d %H:%M:%S'), reverse=True) is_authenticated = 'username' in session unread_count = get_unread_count(data, username) private_unread_count = get_private_unread_count(data, username) user_count = len(data['users']) is_online = is_user_online(data, username) total_views = sum(post.get('views', 0) for post in user_posts) total_likes = sum(len(post.get('likes', [])) for post in user_posts) user_data = data['users'].get(username, {}) bio = user_data.get('bio', '') link = user_data.get('link', '') avatar = user_data.get('avatar', None) last_seen = user_data.get('last_seen', 'Never') if request.method == 'POST': if 'delete_post' in request.form: post_id = request.form.get('post_id') if post_id: data['posts'] = [p for p in data['posts'] if p['id'] != post_id or p['uploader'] != username] save_data(data) return redirect(url_for('profile')) elif 'update_profile' in request.form: bio = request.form.get('bio', '') link = request.form.get('link', '') avatar_file = request.files.get('avatar') if avatar_file and avatar_file.filename: filename = secure_filename(avatar_file.filename) temp_path = os.path.join('uploads', filename) os.makedirs('uploads', exist_ok=True) avatar_file.save(temp_path) api = HfApi() avatar_path = f"avatars/{username}/{filename}" api.upload_file( path_or_fileobj=temp_path, path_in_repo=avatar_path, repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN_WRITE, commit_message=f"Загружен аватар для {username}" ) data['users'][username]['avatar'] = avatar_path if os.path.exists(temp_path): os.remove(temp_path) data['users'][username]['bio'] = bio data['users'][username]['link'] = link save_data(data) return redirect(url_for('profile')) html = ''' Profile - {{ username }} - Content Hub ''' + NAV_HTML + '''
{% if avatar %} Avatar {% else %}
{% endif %}

{{ username }}

{{ bio }}

{% if link %}

{{ link }}

{% endif %}

Total Views: {{ total_views }} | Total Likes: {{ total_likes }}

Last Seen: {{ last_seen }}

Posts

{% for post in user_posts %}
{% if post['type'] == 'video' %} {% else %} {{ post['title'] }} {% endif %}

{{ post['title'] }}

{{ post['description']|truncate(100) }}

{{ post['upload_date'] }} | Views: {{ post['views'] }} | Likes: {{ post['likes']|length }}

{% endfor %} {% if not user_posts %}

This user hasn't uploaded any posts yet.

{% endif %}
''' return render_template_string(html, username=username, user_posts=user_posts, bio=bio, link=link, avatar=avatar, total_views=total_views, total_likes=total_likes, last_seen=last_seen, is_authenticated=is_authenticated, repo_id=REPO_ID, unread_count=unread_count, user_count=user_count, private_unread_count=private_unread_count, is_online=is_online) @app.route('/user/') def user_profile(username): data = load_data() current_user = session.get('username', None) if current_user: update_last_seen(data, current_user) if username not in data['users']: return "Пользователь не найден", 404 user_posts = sorted([p for p in data['posts'] if p['uploader'] == username], key=lambda x: datetime.strptime(x['upload_date'], '%Y-%m-%d %H:%M:%S'), reverse=True) is_authenticated = 'username' in session unread_count = get_unread_count(data, current_user) if is_authenticated else 0 private_unread_count = get_private_unread_count(data, current_user) if is_authenticated else 0 user_count = len(data['users']) is_online = is_user_online(data, current_user) if is_authenticated else False total_views = sum(post.get('views', 0) for post in user_posts) total_likes = sum(len(post.get('likes', [])) for post in user_posts) user_data = data['users'].get(username, {}) bio = user_data.get('bio', '') link = user_data.get('link', '') avatar = user_data.get('avatar', None) last_seen = user_data.get('last_seen', 'Never') is_user_online_status = is_user_online(data, username) html = ''' {{ username }}'s Profile - Content Hub ''' + NAV_HTML + '''
{% if avatar %} Avatar {% else %}
{% endif %}

{{ username }}

{{ bio }}

{% if link %}

{{ link }}

{% endif %}

Total Views: {{ total_views }} | Total Likes: {{ total_likes }}

Last Seen: {{ last_seen }}

{% if is_authenticated and username != current_user %} Message {% endif %}

Posts

{% for post in user_posts %}
{% if post['type'] == 'video' %} {% else %} {{ post['title'] }} {% endif %}

{{ post['title'] }}

{{ post['description']|truncate(100) }}

{{ post['upload_date'] }} | Views: {{ post['views'] }} | Likes: {{ post['likes']|length }}

{% endfor %} {% if not user_posts %}

This user hasn't uploaded any posts yet.

{% endif %}
''' return render_template_string(html, username=username, current_user=current_user, user_posts=user_posts, bio=bio, link=link, avatar=avatar, total_views=total_views, total_likes=total_likes, last_seen=last_seen, is_authenticated=is_authenticated, repo_id=REPO_ID, unread_count=unread_count, user_count=user_count, private_unread_count=private_unread_count, is_online=is_online, is_user_online_status=is_user_online_status) @app.route('/upload', methods=['GET', 'POST']) def upload(): if 'username' not in session: flash('Войдите, чтобы загрузить контент!') return redirect(url_for('login')) data = load_data() username = session['username'] update_last_seen(data, username) is_authenticated = 'username' in session unread_count = get_unread_count(data, username) private_unread_count = get_private_unread_count(data, username) user_count = len(data['users']) is_online = is_user_online(data, username) if request.method == 'POST': title = request.form.get('title') description = request.form.get('description') file = request.files.get('file') if file and title: filename = secure_filename(file.filename) file_type = 'video' if filename.endswith(('.mp4', '.mov', '.avi')) else 'image' temp_path = os.path.join('uploads', filename) os.makedirs('uploads', exist_ok=True) file.save(temp_path) api = HfApi() file_path_in_repo = f"{file_type}s/{username}_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{filename}" api.upload_file( path_or_fileobj=temp_path, path_in_repo=file_path_in_repo, repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN_WRITE, commit_message=f"Загружен {file_type} от {username}" ) post_id = ''.join(random.choices('abcdefghijklmnopqrstuvwxyz0123456789', k=10)) data['posts'].append({ 'id': post_id, 'title': title, 'description': description, 'uploader': username, 'filename': file_path_in_repo, 'type': file_type, 'upload_date': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'views': 0, 'likes': [], 'comments': [] }) save_data(data) if os.path.exists(temp_path): os.remove(temp_path) flash('Контент успешно загружен!') return redirect(url_for('profile')) html = ''' Upload Content - Content Hub ''' + NAV_HTML + '''

Upload Content

{% with messages = get_flashed_messages() %} {% if messages %} {% for message in messages %}
{{ message }}
{% endfor %} {% endif %} {% endwith %}
''' return render_template_string(html, is_authenticated=is_authenticated, username=username, unread_count=unread_count, user_count=user_count, private_unread_count=private_unread_count, is_online=is_online) @app.route('/chat', methods=['GET', 'POST']) def chat(): data = load_data() username = session.get('username', None) if username: update_last_seen(data, username) data['users'][username]['last_chat_visit'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') save_data(data) is_authenticated = 'username' in session chat_messages = sorted(data['general_chat'], key=lambda x: datetime.strptime(x['time'], '%Y-%m-%d %H:%M:%S')) unread_count = get_unread_count(data, username) if is_authenticated else 0 private_unread_count = get_private_unread_count(data, username) if is_authenticated else 0 user_count = len(data['users']) is_online = is_user_online(data, username) if is_authenticated else False if request.method == 'POST' and is_authenticated: message = request.form.get('message') file = request.files.get('file') post_id = request.form.get('post_id') if message or file or post_id: msg = { 'sender': username, 'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S') } if message: msg['text'] = message if post_id: msg['post_id'] = post_id if file and file.filename: filename = secure_filename(file.filename) temp_path = os.path.join('uploads', filename) os.makedirs('uploads', exist_ok=True) file.save(temp_path) file_type = 'video' if filename.endswith(('.mp4', '.mov', '.avi')) else 'image' api = HfApi() file_path = f"chat_files/{username}_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{filename}" api.upload_file( path_or_fileobj=temp_path, path_in_repo=file_path, repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN_WRITE, commit_message=f"Загружен файл чата от {username}" ) msg['file'] = file_path msg['file_type'] = file_type if os.path.exists(temp_path): os.remove(temp_path) data['general_chat'].append(msg) save_data(data) return redirect(url_for('chat')) html = ''' General Chat - Content Hub ''' + NAV_HTML + '''

General Chat

{% for msg in chat_messages %}
{% if 'file' in msg %} {% if msg['file_type'] == 'video' %} {% else %} Chat file {% endif %} {% endif %}

{{ msg['sender'] }} ({{ msg['time'] }})

{% if 'text' in msg %}

{{ msg['text'] }}

{% endif %} {% if 'post_id' in msg %}

Shared post: View Post

{% endif %}
{% endfor %}
{% if is_authenticated %}
{% else %}

Login to send messages.

{% endif %}
''' return render_template_string(html, chat_messages=chat_messages, is_authenticated=is_authenticated, username=username, repo_id=REPO_ID, unread_count=unread_count, user_count=user_count, private_unread_count=private_unread_count, is_online=is_online, is_user_online=lambda u: is_user_online(data, u)) @app.route('/users') def users(): data = load_data() username = session.get('username', None) if username: update_last_seen(data, username) is_authenticated = 'username' in session unread_count = get_unread_count(data, username) if is_authenticated else 0 private_unread_count = get_private_unread_count(data, username) if is_authenticated else 0 user_count = len(data['users']) is_online = is_user_online(data, username) if is_authenticated else False users_list = sorted(data['users'].keys()) html = ''' Users - Content Hub ''' + NAV_HTML + '''

Users ({{ user_count }})

{% for user in users_list %}
{{ user }}

Last seen: {{ data['users'][user]['last_seen'] }}

{% if is_authenticated and user != username %} Message {% endif %}
{% endfor %}
''' return render_template_string(html, users_list=users_list, data=data, is_authenticated=is_authenticated, username=username, unread_count=unread_count, user_count=user_count, private_unread_count=private_unread_count, is_online=is_online, is_user_online=lambda u: is_user_online(data, u)) @app.route('/messages', methods=['GET', 'POST']) def messages(): if 'username' not in session: flash('Войдите, чтобы просмотреть сообщения!') return redirect(url_for('login')) data = load_data() username = session['username'] update_last_seen(data, username) data['users'][username]['last_private_visit'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') save_data(data) is_authenticated = 'username' in session unread_count = get_unread_count(data, username) private_unread_count = get_private_unread_count(data, username) user_count = len(data['users']) is_online = is_user_online(data, username) private_chats = {k: v for k, v in data['private_chats'].items() if username in k.split('_')} if request.method == 'POST': recipient = request.form.get('recipient') message = request.form.get('message') file = request.files.get('file') if recipient and (message or file): if recipient not in data['users']: flash('Получатель не найден!') return redirect(url_for('messages')) chat_key = '_'.join(sorted([username, recipient])) if chat_key not in data['private_chats']: data['private_chats'][chat_key] = [] msg = { 'sender': username, 'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S') } if message: msg['text'] = message if file and file.filename: filename = secure_filename(file.filename) temp_path = os.path.join('uploads', filename) os.makedirs('uploads', exist_ok=True) file.save(temp_path) file_type = 'video' if filename.endswith(('.mp4', '.mov', '.avi')) else 'image' api = HfApi() file_path = f"private_files/{chat_key}_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{filename}" api.upload_file( path_or_fileobj=temp_path, path_in_repo=file_path, repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN_WRITE, commit_message=f"Загружен файл приватного чата между {username} и {recipient}" ) msg['file'] = file_path msg['file_type'] = file_type if os.path.exists(temp_path): os.remove(temp_path) data['private_chats'][chat_key].append(msg) save_data(data) return redirect(url_for('messages') + f'#chat_{recipient}') html = ''' Private Messages - Content Hub ''' + NAV_HTML + '''

Private Messages

{% for chat_key, messages in private_chats.items() %} {% set recipient = chat_key.split('_')|reject('eq', username)|first %} {% endfor %}
{% for user in data['users'].keys() %} {% if user != username %}
{% if private_chats %} {% set active_chat = request.args.get('chat') or (private_chats.keys()|list|first).split('_')|reject('eq', username)|first %} {% set chat_key = '_'.join(sorted([username, active_chat])) %} {% set messages = private_chats.get(chat_key, []) %}

Chat with {{ active_chat }}

{% for msg in messages %}
{% if 'file' in msg %} {% if msg['file_type'] == 'video' %} {% else %} Chat file {% endif %} {% endif %}

{{ msg['sender'] }} ({{ msg['time'] }})

{% if 'text' in msg %}

{{ msg['text'] }}

{% endif %}
{% endfor %}
{% else %}

No private chats yet. Start one!

{% endif %}
''' return render_template_string(html, private_chats=private_chats, data=data, is_authenticated=is_authenticated, username=username, unread_count=unread_count, user_count=user_count, private_unread_count=private_unread_count, is_online=is_online, is_user_online=lambda u: is_user_online(data, u)) if __name__ == '__main__': threading.Thread(target=periodic_backup, daemon=True).start() app.run(debug=True, host='0.0.0.0', port=7860)