/* * chat_core.h — центральный API чата в потоке uasync * * Все DB-операции и сетевые функции выполняются в uasync-потоке. * GUI вызывает chat_core_submit_message() через gui_bridge_post_uasync(). */ #ifndef CHAT_CORE_H #define CHAT_CORE_H #include #include #include struct sqlite3; struct UTUN_INSTANCE; /* ── Жизненный цикл ── */ int chat_core_init(struct UTUN_INSTANCE* inst, const char* db_path); void chat_core_destroy(struct UTUN_INSTANCE* inst); /* Backfill: регистрирует уже скачанные медиафайлы (лежат на диске, но отсутствуют * в media_files) — вызывается после загрузки каналов/сообщений при старте. */ void chat_media_backfill(void); /* Докачка недостающего медиа за последние storage_backfill_days дней. Проходит по * медиасообщениям каналов и запускает загрузку для тех, чей файл отсутствует на диске. */ void chat_media_autodownload_backfill(void); /* Регистрирует триггер: после первого SYNC_DONE (+ дебаунс 2с) анонсирует локальные * блоки суперузлам и запускает chat_media_autodownload_backfill. Один раз за процесс. */ void chat_media_startup_backfill_init(struct UTUN_INSTANCE* inst); void chat_media_startup_backfill_destroy(struct UTUN_INSTANCE* inst); struct sqlite3* chat_core_get_db(void); struct UTUN_INSTANCE* chat_core_get_inst(void); int chat_core_is_initialized(void); /* ── Отправка сообщения (GUI → uasync) ── */ struct chat_msg_submit { char channel_id[64]; char content_type[32]; char media_src[1024]; char media_dest[1024]; uint8_t media_copy; uint8_t media_video; /* 1 = подготовить как видео (пробинг+транскод) */ int duration_ms; /* видео: длительность (0 = неизвестно) */ int width; /* видео: ширина */ int height; /* видео: высота */ uint8_t* data; uint32_t data_len; uint64_t timestamp; uint64_t reply_to_ts; /* 0 = не ответ */ uint64_t reply_to_node_id; /* node_id автора исходного сообщения */ }; void chat_core_submit_message(struct chat_msg_submit* req); /* Отправка медиа-сообщения (вложение): src->dest (copy) или видео (transcode). */ void chat_core_submit_media_message(struct chat_msg_submit* req); /* ── DB-операции (оставлены для интроспекции) ── */ uint32_t chat_core_count(const char* ch_id); int chat_core_chain_hash_at(const char* ch_id, uint32_t pos, uint8_t* hash_out); int chat_core_list_channels(uint8_t* buf, size_t buf_size, size_t* out_len); int chat_core_list_peers(const char* ch_id, uint8_t* buf, size_t buf_size, size_t* out_len); int chat_core_load_nodeinfo(uint64_t node_id, uint8_t* buf, size_t buf_size, size_t* out_len); /* ── Создание канала (GUI → uasync) ── */ struct chat_channel_create { char channel_id[64]; char name[128]; uint64_t owner_node_id; uint8_t x25519_pubkey[32]; uint8_t x25519_privkey[32]; uint8_t ed25519_pubkey[32]; uint8_t ed25519_privkey[32]; uint8_t signature[64]; }; void chat_core_ensure_channel_ready(const char* ch_id); void chat_core_create_channel(struct chat_channel_create* req); void chat_core_create_channel_trampoline(void* arg); void chat_core_create_channel_auto(const char* name); void chat_core_create_channel_auto_trampoline(void* arg); /* ── Локальное удаление канала (GUI → uasync) ── */ struct chat_delete_channel_req { char ch_id[64]; }; /* Удалить канал с этого устройства: отмена медиа-загрузок, удаление группы * (соединения/синк), db_sync инстанса, таблиц msg_ и peers_, медиафайлов и * merkle-хешей. Работает в uasync-потоке. Другие узлы канал не теряют. */ void chat_core_delete_channel(const char* ch_id); void chat_core_delete_channel_trampoline(void* arg); /* ── Утилиты ── */ void chat_core_set_my_node_id(uint64_t node_id); void chat_core_update_my_name(const char* name); void chat_core_update_my_name_trampoline(void* arg); void chat_core_sync_my_addresses(void); /* Единое обновление своего мембера: адреса + bump update_ts + подпись (с адресами) + синк */ void chat_core_update_my_member(void); /* Сохранение key-value в ui_state (вызывается из uasync-потока) */ void chat_core_save_ui_state(const char* key, const char* value); void chat_core_save_ui_state_trampoline(void* arg); /* Установка chat-настройки (name=value) через chat_setting API (GUI → uasync) */ void chat_core_set_setting(const char* name, const char* value); void chat_core_set_setting_trampoline(void* arg); /* Запуск подключения к узлам выбранного канала (GUI → uasync) */ void chat_core_connect_channel(const char* ch_id); void chat_core_connect_channel_trampoline(void* arg); /* Однократная попытка подключения к конкретному узлу канала (GUI → uasync) */ void chat_core_connect_node(const char* ch_id, uint64_t node_id); void chat_core_connect_node_trampoline(void* arg); struct chat_connect_node_req { char ch_id[64]; uint64_t node_id; }; /* Трамплин для gui_bridge_post_uasync (GUI → uasync) */ void chat_core_submit_trampoline(void* arg); /* Трамплин для ручного запуска загрузки медиа (JNI → uasync) */ struct attachment_dl_req { char channel_id[64]; int64_t msg_id; }; void chat_core_attachment_download_trampoline(void* arg); /* Обновление local_attrs для записи (вызывается из uasync-потока) */ int chat_core_update_local_attrs(const char* ch_id, uint64_t ts, const uint8_t* author_sig, const char* local_attrs_json); /* Пометить голосовое сообщение как проигранное (добавляет "pl":1 в local_attrs) */ void chat_core_mark_voice_played(const char* channel_id, uint64_t ts, uint64_t author_node_id); void chat_core_mark_voice_played_trampoline(void* arg); struct voice_played_req { char channel_id[64]; uint64_t ts; uint64_t author_node_id; }; /* Сбор статуса (NTP + connections) и отправка в GUI */ void chat_core_collect_status_trampoline(void* arg); /* Сбор списка ETCP-соединений и отправка в GUI (binary packet) */ void chat_core_collect_conn_list_trampoline(void* arg); /* Сбор полных метрик одного соединения (arg = u_malloc uint64_t peer_node_id) */ void chat_core_collect_conn_metrics_trampoline(void* arg); /* Сбор live-снапшота для панели деталей участника (arg = u_malloc uint64_t node_id) */ void chat_core_collect_member_detail_trampoline(void* arg); /* ── Headless CLI API ── */ /** Вариант для сборки без UTUN_INSTANCE (использует sqlite3* и node_id) */ void chat_core_set_ctx_for_headless(sqlite3* db, struct UTUN_INSTANCE* inst, uint64_t my_node_id); /** Список каналов с метаданными в JSON: [{id,name,owner_id,owner_name,peers,msgs,online}] */ int chat_core_get_channels_json(char* buf, size_t buf_size, size_t* out_len); /** Сообщения канала в JSON: [{id,ts,author_id,author_name,content_type,data}] * count=0 → все, offset=0 → сначала */ int chat_core_get_messages_json(const char* ch_id, int count, int offset, char* buf, size_t buf_size, size_t* out_len); /** Мемберы канала с полным состоянием в JSON: * [{node_id,name,online,connected,x25519,ed25519,addrs:[{ip,port,proto,rtt}]}] */ int chat_core_get_members_json(const char* ch_id, char* buf, size_t buf_size, size_t* out_len); /** Имя узла из таблицы nodes */ int chat_core_get_node_name(uint64_t node_id, char* out, size_t sz); /* ── Редактирование adm_tags (владелец канала) ── */ #define CHAT_MEMBER_TAGS_MAX_KEYS 16 #define CHAT_MEMBER_TAGS_KEY_SZ 64 #define CHAT_MEMBER_TAGS_VAL_SZ 256 struct chat_member_tags; /** Загрузить текущие adm_tags мембера в память. Возвращает NULL если мембера нет. */ struct chat_member_tags* chat_member_tags_load(const char* ch_id, uint64_t node_id); /** Установить ключ. val=NULL → удалить ключ. ver — особый, автоинкремент при commit. */ int chat_member_tags_set(struct chat_member_tags* t, const char* key, const char* val); /** Получить значение ключа. Возвращает NULL если ключ не найден. */ const char* chat_member_tags_get(struct chat_member_tags* t, const char* key); /** Заменить все теги данными из JSON-строки (плоский, строковые значения). */ int chat_member_tags_replace(struct chat_member_tags* t, const char* json); /** Сохранить: сериализовать в JSON, инкрементировать ver, подписать канальным Ed25519, * записать через member_sync_put() → синхронизируется всем. */ int chat_member_tags_commit(struct chat_member_tags* t); /** Освободить без сохранения. */ void chat_member_tags_free(struct chat_member_tags* t); /** Трамплин для gui_bridge: arg = struct chat_member_tags_req* (доверенная память). */ struct chat_member_tags_req { char ch_id[64]; uint64_t node_id; char json[512]; }; void chat_member_tags_commit_trampoline(void* arg); /* ── Передача прав админа (владелец → другому узлу) ── */ /** Передать оба канальных privkey выбранному узлу (encrypted+signed через etcp_router). */ void chat_core_transfer_admin(const char* ch_id, uint64_t target_node_id); void chat_core_transfer_admin_trampoline(void* arg); struct chat_transfer_admin_req { char ch_id[64]; uint64_t target_node_id; }; #endif /* CHAT_CORE_H */