fixes
Some checks failed
CI / Check (pull_request) Has been cancelled
CI / Format (pull_request) Has been cancelled
CI / Clippy (pull_request) Has been cancelled
CI / Build (macos-latest) (pull_request) Has been cancelled
CI / Build (ubuntu-latest) (pull_request) Has been cancelled
CI / Build (windows-latest) (pull_request) Has been cancelled
Some checks failed
CI / Check (pull_request) Has been cancelled
CI / Format (pull_request) Has been cancelled
CI / Clippy (pull_request) Has been cancelled
CI / Build (macos-latest) (pull_request) Has been cancelled
CI / Build (ubuntu-latest) (pull_request) Has been cancelled
CI / Build (windows-latest) (pull_request) Has been cancelled
This commit is contained in:
154
src/main.rs
154
src/main.rs
@@ -1,3 +1,4 @@
|
||||
mod accounts;
|
||||
mod app;
|
||||
mod audio;
|
||||
mod config;
|
||||
@@ -31,6 +32,21 @@ use input::{handle_auth_input, handle_main_input};
|
||||
use tdlib::AuthState;
|
||||
use utils::{disable_tdlib_logs, with_timeout_ignore};
|
||||
|
||||
/// Parses `--account <name>` from CLI arguments.
|
||||
fn parse_account_arg() -> Option<String> {
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
let mut i = 1;
|
||||
while i < args.len() {
|
||||
if args[i] == "--account" {
|
||||
if i + 1 < args.len() {
|
||||
return Some(args[i + 1].clone());
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), io::Error> {
|
||||
// Загружаем переменные окружения из .env
|
||||
@@ -48,6 +64,24 @@ async fn main() -> Result<(), io::Error> {
|
||||
// Загружаем конфигурацию (создаёт дефолтный если отсутствует)
|
||||
let config = config::Config::load();
|
||||
|
||||
// Загружаем/создаём accounts.toml + миграция legacy ./tdlib_data/
|
||||
let accounts_config = accounts::load_or_create();
|
||||
|
||||
// Резолвим аккаунт из CLI или default
|
||||
let account_arg = parse_account_arg();
|
||||
let (account_name, db_path) =
|
||||
accounts::resolve_account(&accounts_config, account_arg.as_deref())
|
||||
.unwrap_or_else(|e| {
|
||||
eprintln!("Error: {}", e);
|
||||
std::process::exit(1);
|
||||
});
|
||||
|
||||
// Создаём директорию аккаунта если её нет
|
||||
let db_path = accounts::ensure_account_dir(
|
||||
account_arg.as_deref().unwrap_or(&accounts_config.default_account),
|
||||
)
|
||||
.unwrap_or(db_path);
|
||||
|
||||
// Отключаем логи TDLib ДО создания клиента
|
||||
disable_tdlib_logs();
|
||||
|
||||
@@ -66,18 +100,20 @@ async fn main() -> Result<(), io::Error> {
|
||||
panic_hook(info);
|
||||
}));
|
||||
|
||||
// Create app state
|
||||
let mut app = App::new(config);
|
||||
// Create app state with account-specific db_path
|
||||
let mut app = App::new(config, db_path);
|
||||
app.current_account_name = account_name;
|
||||
|
||||
// Запускаем инициализацию TDLib в фоне (только для реального клиента)
|
||||
let client_id = app.td_client.client_id();
|
||||
let api_id = app.td_client.api_id;
|
||||
let api_hash = app.td_client.api_hash.clone();
|
||||
let db_path_str = app.td_client.db_path.to_string_lossy().to_string();
|
||||
|
||||
tokio::spawn(async move {
|
||||
let _ = tdlib_rs::functions::set_tdlib_parameters(
|
||||
false, // use_test_dc
|
||||
"tdlib_data".to_string(), // database_directory
|
||||
db_path_str, // database_directory
|
||||
"".to_string(), // files_directory
|
||||
"".to_string(), // database_encryption_key
|
||||
true, // use_file_database
|
||||
@@ -233,12 +269,23 @@ async fn run_app<B: ratatui::backend::Backend, T: tdlib::TdClientTrait>(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match app.screen {
|
||||
AppScreen::Loading => {
|
||||
// В состоянии загрузки игнорируем ввод
|
||||
// Ctrl+A opens account switcher from any screen
|
||||
if key.code == KeyCode::Char('a')
|
||||
&& key.modifiers.contains(KeyModifiers::CONTROL)
|
||||
&& app.account_switcher.is_none()
|
||||
{
|
||||
app.open_account_switcher();
|
||||
} else if app.account_switcher.is_some() {
|
||||
// Route to main input handler when account switcher is open
|
||||
handle_main_input(app, key).await;
|
||||
} else {
|
||||
match app.screen {
|
||||
AppScreen::Loading => {
|
||||
// В состоянии загрузки игнорируем ввод
|
||||
}
|
||||
AppScreen::Auth => handle_auth_input(app, key.code).await,
|
||||
AppScreen::Main => handle_main_input(app, key).await,
|
||||
}
|
||||
AppScreen::Auth => handle_auth_input(app, key.code).await,
|
||||
AppScreen::Main => handle_main_input(app, key).await,
|
||||
}
|
||||
|
||||
// Любой ввод требует перерисовки
|
||||
@@ -251,6 +298,97 @@ async fn run_app<B: ratatui::backend::Backend, T: tdlib::TdClientTrait>(
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Process pending chat initialization (reply info, pinned, photos)
|
||||
if let Some(chat_id) = app.pending_chat_init.take() {
|
||||
// Загружаем недостающие reply info (игнорируем ошибки)
|
||||
with_timeout_ignore(
|
||||
Duration::from_secs(5),
|
||||
app.td_client.fetch_missing_reply_info(),
|
||||
)
|
||||
.await;
|
||||
|
||||
// Загружаем последнее закреплённое сообщение (игнорируем ошибки)
|
||||
with_timeout_ignore(
|
||||
Duration::from_secs(2),
|
||||
app.td_client.load_current_pinned_message(chat_id),
|
||||
)
|
||||
.await;
|
||||
|
||||
// Авто-загрузка фото (последние 30 сообщений)
|
||||
#[cfg(feature = "images")]
|
||||
{
|
||||
use crate::tdlib::PhotoDownloadState;
|
||||
|
||||
if app.config().images.auto_download_images && app.config().images.show_images {
|
||||
let photo_file_ids: Vec<i32> = app
|
||||
.td_client
|
||||
.current_chat_messages()
|
||||
.iter()
|
||||
.rev()
|
||||
.take(30)
|
||||
.filter_map(|msg| {
|
||||
msg.photo_info().and_then(|p| {
|
||||
matches!(p.download_state, PhotoDownloadState::NotDownloaded)
|
||||
.then_some(p.file_id)
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
for file_id in &photo_file_ids {
|
||||
if let Ok(Ok(path)) = tokio::time::timeout(
|
||||
Duration::from_secs(5),
|
||||
app.td_client.download_file(*file_id),
|
||||
)
|
||||
.await
|
||||
{
|
||||
for msg in app.td_client.current_chat_messages_mut() {
|
||||
if let Some(photo) = msg.photo_info_mut() {
|
||||
if photo.file_id == *file_id {
|
||||
photo.download_state =
|
||||
PhotoDownloadState::Downloaded(path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app.needs_redraw = true;
|
||||
}
|
||||
|
||||
// Check pending account switch
|
||||
if let Some((account_name, new_db_path)) = app.pending_account_switch.take() {
|
||||
// 1. Stop playback
|
||||
app.stop_playback();
|
||||
|
||||
// 2. Recreate client (closes old, creates new, inits TDLib params)
|
||||
if let Err(e) = app.td_client.recreate_client(new_db_path).await {
|
||||
app.error_message = Some(format!("Ошибка переключения: {}", e));
|
||||
continue;
|
||||
}
|
||||
|
||||
// 3. Reset app state
|
||||
app.current_account_name = account_name;
|
||||
app.screen = AppScreen::Loading;
|
||||
app.chats.clear();
|
||||
app.selected_chat_id = None;
|
||||
app.chat_state = Default::default();
|
||||
app.input_mode = Default::default();
|
||||
app.status_message = Some("Переключение аккаунта...".to_string());
|
||||
app.error_message = None;
|
||||
app.is_searching = false;
|
||||
app.search_query.clear();
|
||||
app.message_input.clear();
|
||||
app.cursor_position = 0;
|
||||
app.message_scroll_offset = 0;
|
||||
app.pending_chat_init = None;
|
||||
app.account_switcher = None;
|
||||
|
||||
app.needs_redraw = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user