2 Commits

Author SHA1 Message Date
cd5e6e1f6b fix(update): prevent repeated check crash and bump to 2.2.2
All checks were successful
Desktop CI / tests (push) Successful in 15s
Desktop Release / release (push) Successful in 3m30s
2026-02-15 23:59:57 +03:00
4b3347a069 fix(update,security): add release notes in updater and bump to 2.2.1
All checks were successful
Desktop CI / tests (push) Successful in 13s
Desktop Release / release (push) Successful in 3m30s
2026-02-15 23:51:44 +03:00
4 changed files with 50 additions and 16 deletions

View File

@@ -1 +1 @@
APP_VERSION = "2.2.0"
APP_VERSION = "2.2.2"

46
main.py
View File

@@ -2,7 +2,6 @@
import os
import shutil
import sys
import threading
import time
from PySide6.QtCore import QProcess
@@ -172,6 +171,7 @@ class VkChatManager(QMainWindow):
self.update_channel = UPDATE_CHANNEL_DEFAULT
self.update_checker = None
self.update_thread = None
self._update_in_progress = False
self._update_check_silent = False
self._bulk_worker_thread = None
self._bulk_worker = None
@@ -463,10 +463,11 @@ class VkChatManager(QMainWindow):
self._log_event("update_channel", f"update_channel={self.update_channel}")
def check_for_updates(self, silent_no_updates=False):
if self.update_thread and self.update_thread.is_alive():
if self._update_in_progress:
return
self._update_check_silent = silent_no_updates
self._update_in_progress = True
self._set_update_action_state(True)
channel_label = "бета" if self.update_channel == "beta" else "релизы"
self.status_label.setText(f"Статус: проверка обновлений ({channel_label})...")
@@ -477,15 +478,21 @@ class VkChatManager(QMainWindow):
request_timeout=UPDATE_REQUEST_TIMEOUT,
channel=self.update_channel,
)
self.update_thread = QThread(self)
self.update_checker.moveToThread(self.update_thread)
self.update_thread.started.connect(self.update_checker.run)
self.update_checker.check_finished.connect(self._on_update_check_finished)
self.update_checker.check_failed.connect(self._on_update_check_failed)
self.update_thread = threading.Thread(target=self.update_checker.run, daemon=True)
self.update_checker.check_finished.connect(self.update_thread.quit)
self.update_checker.check_failed.connect(self.update_thread.quit)
self.update_checker.check_finished.connect(self.update_checker.deleteLater)
self.update_checker.check_failed.connect(self.update_checker.deleteLater)
self.update_thread.finished.connect(self._on_update_thread_finished)
self.update_thread.finished.connect(self.update_thread.deleteLater)
self.update_thread.start()
def _on_update_check_finished(self, result):
self._set_update_action_state(False)
self.update_checker = None
self.update_thread = None
if result.get("has_update"):
latest_version = result.get("latest_version") or result.get("latest_tag") or "unknown"
@@ -499,6 +506,14 @@ class VkChatManager(QMainWindow):
f"Доступная версия: {latest_version}\n\n"
"Открыть страницу загрузки?"
)
release_notes = (result.get("release_notes") or "").strip()
if release_notes:
preview_lines = [line.strip() for line in release_notes.splitlines() if line.strip()]
preview_text = "\n".join(preview_lines[:8])
if len(preview_lines) > 8:
preview_text += "\n..."
message_box.setInformativeText(f"Что нового:\n{preview_text}")
message_box.setDetailedText(release_notes)
update_now_button = message_box.addButton("Обновить сейчас", QMessageBox.ButtonRole.AcceptRole)
download_button = message_box.addButton("Скачать", QMessageBox.ButtonRole.AcceptRole)
setup_button = None
@@ -538,8 +553,6 @@ class VkChatManager(QMainWindow):
def _on_update_check_failed(self, error_text):
self._set_update_action_state(False)
self.update_checker = None
self.update_thread = None
self._log_event("update_check_failed", error_text, level="WARN")
if not self.update_repository_url:
self.status_label.setText("Статус: обновления не настроены (URL репозитория не задан).")
@@ -556,6 +569,11 @@ class VkChatManager(QMainWindow):
if not self._update_check_silent:
QMessageBox.warning(self, "Проверка обновлений", error_text)
def _on_update_thread_finished(self):
self._update_in_progress = False
self.update_checker = None
self.update_thread = None
def setup_token_timer(self):
self.token_countdown_timer = QTimer(self)
self.token_countdown_timer.timeout.connect(self.update_token_timer_display)
@@ -975,12 +993,26 @@ class VkChatManager(QMainWindow):
self.token = token
# Сохраняем и получаем корректный expiration_time (0 или будущее время)
try:
self.token_expiration_time = token_store_save_token(
self.token,
TOKEN_FILE,
APP_DATA_DIR,
expires_in=expires_in,
)
except Exception as e:
try:
expires_value = int(expires_in)
except (TypeError, ValueError):
expires_value = 0
self.token_expiration_time = (time.time() + expires_value) if expires_value > 0 else 0
self._log_event("token_store_save", str(e), level="WARN")
QMessageBox.warning(
self,
"Предупреждение",
"Не удалось безопасно сохранить токен на диске. "
"Текущая сессия активна, но после перезапуска потребуется повторная авторизация.",
)
self.token_input.setText(self.token[:50] + "...")
self.status_label.setText("Статус: авторизован")

View File

@@ -91,8 +91,8 @@ def save_token(token, token_file, app_data_dir, expires_in=0):
try:
stored_token = _encrypt_token(token)
encrypted = True
except Exception:
pass
except Exception as exc:
raise RuntimeError("Failed to securely store token with DPAPI.") from exc
data = {
"token": stored_token,

View File

@@ -102,6 +102,7 @@ def _extract_release_payload(release_data, repository_url, current_version):
latest_tag = release_data.get("tag_name") or release_data.get("name") or ""
latest_version = latest_tag.lstrip("vV").strip()
html_url = release_data.get("html_url") or releases_url
release_notes = (release_data.get("body") or "").strip()
assets = release_data.get("assets") or []
download_url = ""
download_name = ""
@@ -147,6 +148,7 @@ def _extract_release_payload(release_data, repository_url, current_version):
"current_version": current_version,
"latest_tag": latest_tag,
"release_url": html_url,
"release_notes": release_notes,
"download_url": download_url,
"download_name": download_name,
"installer_url": installer_url,