You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
289 lines
17 KiB
289 lines
17 KiB
/* |
|
* chat_core — API каналов, сообщений, профиля и настроек одного узла. |
|
* chat_service_start/stop управляют всем чат-сервисом поверх работающего ядра. |
|
* Данные хранятся в общей SQLite; события для интерфейса доставляет chat_event. |
|
* |
|
* Все DB-операции и сетевые функции выполняются в uasync-потоке. |
|
* Все функции per-instance: первым аргументом struct UTUN_INSTANCE*. |
|
* GUI вызывает через gui_bridge_post_uasync() trampoline-функции, |
|
* чей arg-структура несёт inst. |
|
*/ |
|
|
|
#ifndef CHAT_CORE_H |
|
#define CHAT_CORE_H |
|
|
|
#include <stdint.h> |
|
#include <stddef.h> |
|
#include <sqlite3.h> |
|
|
|
struct sqlite3; |
|
struct UTUN_INSTANCE; |
|
|
|
/* ── Жизненный цикл ── */ |
|
|
|
/* Запустить чат поверх core_start; UTUN не требуется. Повтор допустим; 0/-1. */ |
|
int chat_service_start(struct UTUN_INSTANCE* inst); |
|
/* Остановить чат, сохранив ядро, UTUN и БД. Повтор допустим; вызывать вне callbacks чата. |
|
* Отменяет доставку и ждёт native media workers; длительная задача может задержать stop. */ |
|
void chat_service_stop(struct UTUN_INSTANCE* inst); |
|
|
|
/* Внутренняя часть запуска: открыть/подключить БД и загрузить каналы. 0/-1. |
|
* Для запуска всего чат-сервиса использовать chat_service_start(). */ |
|
int chat_core_init(struct UTUN_INSTANCE* inst, const char* db_path); |
|
/* Освободить chat_core после остановки остальных частей чата; общая БД остаётся у ядра. */ |
|
void chat_core_destroy(struct UTUN_INSTANCE* inst); |
|
|
|
/* Backfill: регистрирует уже скачанные медиафайлы (лежат на диске, но отсутствуют |
|
* в media_files) — вызывается после загрузки каналов/сообщений при старте. */ |
|
void chat_media_backfill(struct UTUN_INSTANCE* inst); |
|
|
|
/* Докачка недостающего медиа за последние storage_backfill_days дней. Проходит по |
|
* медиасообщениям каналов и запускает загрузку для тех, чей файл отсутствует на диске. */ |
|
void chat_media_autodownload_backfill(struct UTUN_INSTANCE* inst); |
|
|
|
/* Регистрирует триггер: после первого 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); |
|
|
|
/* Заимствованная БД текущего chat_core; NULL до init/после destroy. */ |
|
struct sqlite3* chat_core_get_db(struct UTUN_INSTANCE* inst); |
|
/* 1 — chat_core инициализирован, 0 — нет; не проверяет доступность сети. */ |
|
int chat_core_is_initialized(struct UTUN_INSTANCE* inst); |
|
|
|
/* ── Отправка сообщения (GUI → uasync) ── */ |
|
|
|
struct chat_msg_submit { |
|
struct UTUN_INSTANCE* inst; |
|
char channel_id[64]; /* десятичный ID канала */ |
|
char content_type[32]; /* тип содержимого сообщения */ |
|
char media_src[1024]; /* исходный файл вложения */ |
|
char media_dest[1024]; /* путь подготовленного локального файла */ |
|
uint8_t media_copy; /* 1 = скопировать исходный файл при регистрации */ |
|
uint8_t media_video; /* 1 = подготовить как видео (пробинг+транскод) */ |
|
int duration_ms; /* видео: длительность (0 = неизвестно) */ |
|
int width; /* видео: ширина */ |
|
int height; /* видео: высота */ |
|
uint8_t* data; /* содержимое сообщения, заимствовано обычным submit */ |
|
uint32_t data_len; /* размер data в байтах */ |
|
uint64_t timestamp; /* входное время; отправка назначает новый timestamp через db_sync */ |
|
uint64_t reply_to_ts; /* 0 = не ответ */ |
|
uint64_t reply_to_node_id; /* node_id автора исходного сообщения */ |
|
}; |
|
|
|
/* Подписать, сохранить и разослать сообщение. req/data остаются у вызывающего. */ |
|
void chat_core_submit_message(struct UTUN_INSTANCE* inst, struct chat_msg_submit* req); |
|
|
|
/* Отправка медиа-сообщения (вложение): src->dest (copy) или видео (transcode). */ |
|
void chat_core_submit_media_message(struct UTUN_INSTANCE* inst, struct chat_msg_submit* req); |
|
|
|
/* ── DB-операции (оставлены для интроспекции) ── */ |
|
|
|
/* Число локальных сообщений; 0 также при отсутствии БД или ошибке запроса. */ |
|
uint32_t chat_core_count(struct UTUN_INSTANCE* inst, const char* ch_id); |
|
/* Хеш записи по позиции с нуля в порядке (timestamp, node_id); hash_out — 32 байта. |
|
* 0 = запрос выполнен (нет записи — нулевой хеш), -1 = ошибка. */ |
|
int chat_core_chain_hash_at(struct UTUN_INSTANCE* inst, const char* ch_id, uint32_t pos, uint8_t* hash_out); |
|
/* Список ID: count:u16, затем [длина:u8, строка]; buf_size >= 2. 0/-1. |
|
* Списки ограничены буфером; out_len — записанные байты, числа в порядке байтов хоста. */ |
|
int chat_core_list_channels(struct UTUN_INSTANCE* inst, uint8_t* buf, size_t buf_size, size_t* out_len); |
|
/* Список участников: count:u16, затем node_id:u64; buf_size >= 2. 0/-1. */ |
|
int chat_core_list_peers(struct UTUN_INSTANCE* inst, const char* ch_id, uint8_t* buf, size_t buf_size, size_t* out_len); |
|
/* Ключи X25519/Ed25519 (по 32 байта) и список адресов из БД; buf_size >= 65. |
|
* out_len — размер результата; 0 = прочитано, -1 = нет узла или ошибка. */ |
|
int chat_core_load_nodeinfo(struct UTUN_INSTANCE* inst, uint64_t node_id, uint8_t* buf, size_t buf_size, size_t* out_len); |
|
|
|
/* ── Создание канала (GUI → uasync) ── */ |
|
|
|
struct chat_channel_create { |
|
struct UTUN_INSTANCE* inst; |
|
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]; |
|
}; |
|
|
|
struct chat_create_auto_arg { struct UTUN_INSTANCE* inst; char name[128]; }; |
|
|
|
/* Подготовить локальные таблицы, группу и синхронизацию канала. Членства на других узлах не даёт. */ |
|
void chat_core_ensure_channel_ready(struct UTUN_INSTANCE* inst, const char* ch_id); |
|
/* Создать канал по готовым ключам и метаданным req; req остаётся у вызывающего. */ |
|
void chat_core_create_channel(struct UTUN_INSTANCE* inst, struct chat_channel_create* req); |
|
void chat_core_create_channel_trampoline(void* arg); |
|
/* Сгенерировать ключи и создать канал, добавив себя владельцем. */ |
|
void chat_core_create_channel_auto(struct UTUN_INSTANCE* inst, const char* name); |
|
void chat_core_create_channel_auto_trampoline(void* arg); |
|
|
|
/* ── Локальное удаление канала (GUI → uasync) ── */ |
|
|
|
struct chat_delete_channel_req { |
|
struct UTUN_INSTANCE* inst; |
|
char ch_id[64]; |
|
}; |
|
|
|
/* Удалить канал с этого устройства: отмена медиа-загрузок, удаление группы |
|
* (соединения/синк), db_sync инстанса, таблиц msg_ и peers_, медиафайлов и |
|
* merkle-хешей. Работает в uasync-потоке. Другие узлы канал не теряют. */ |
|
void chat_core_delete_channel(struct UTUN_INSTANCE* inst, const char* ch_id); |
|
void chat_core_delete_channel_trampoline(void* arg); |
|
|
|
/* ── Утилиты ── */ |
|
|
|
/* Задать локальный ID в контексте чата; ключи и идентичность ядра не меняет. */ |
|
void chat_core_set_my_node_id(struct UTUN_INSTANCE* inst, uint64_t node_id); |
|
/* Сохранить имя узла и обновить собственные member-записи каналов. */ |
|
void chat_core_update_my_name(struct UTUN_INSTANCE* inst, const char* name); |
|
|
|
struct update_my_name_arg { struct UTUN_INSTANCE* inst; char name[128]; }; |
|
void chat_core_update_my_name_trampoline(void* arg); |
|
/* Обновить адреса topo_node и собственные member-записи каналов. */ |
|
void chat_core_sync_my_addresses(struct UTUN_INSTANCE* inst); |
|
/* Обновить своё userinfo во всех каналах: увеличить update_ts, подписать и запустить sync. |
|
* Адреса обновляются отдельно через topo_node; подпись member-блока их не содержит. */ |
|
void chat_core_update_my_member(struct UTUN_INSTANCE* inst); |
|
|
|
/* Сохранение key-value в ui_state (вызывается из uasync-потока) */ |
|
void chat_core_save_ui_state(struct UTUN_INSTANCE* inst, const char* key, const char* value); |
|
|
|
/* Per-channel «play sound on new message» (ui_state: ch_<id>_sound_on_message, default 1=on). |
|
* set — сохраняет и рассылает пирам через chat_sync (троттлинг db_sync на сервере). */ |
|
int chat_core_get_sound_on_message(struct UTUN_INSTANCE* inst, const char* ch_id); |
|
void chat_core_set_sound_on_message(struct UTUN_INSTANCE* inst, const char* ch_id, int on); |
|
|
|
struct chat_sound_arg { struct UTUN_INSTANCE* inst; char ch_id[64]; int on; }; |
|
void chat_core_set_sound_on_message_trampoline(void* arg); |
|
|
|
struct save_ui_state_arg { struct UTUN_INSTANCE* inst; char data[1024]; }; |
|
void chat_core_save_ui_state_trampoline(void* arg); |
|
|
|
/* Установка chat-настройки (name=value) через chat_setting API (GUI → uasync) */ |
|
void chat_core_set_setting(struct UTUN_INSTANCE* inst, const char* name, const char* value); |
|
|
|
struct chat_setting_arg { struct UTUN_INSTANCE* inst; char data[256]; }; |
|
void chat_core_set_setting_trampoline(void* arg); |
|
|
|
/* Запуск подключения к узлам выбранного канала (GUI → uasync) */ |
|
void chat_core_connect_channel(struct UTUN_INSTANCE* inst, const char* ch_id); |
|
void chat_core_connect_channel_trampoline(void* arg); |
|
|
|
/* Однократная попытка подключения к конкретному узлу канала (GUI → uasync) */ |
|
void chat_core_connect_node(struct UTUN_INSTANCE* inst, const char* ch_id, uint64_t node_id); |
|
void chat_core_connect_node_trampoline(void* arg); |
|
|
|
struct chat_connect_node_req { |
|
struct UTUN_INSTANCE* inst; |
|
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 { |
|
struct UTUN_INSTANCE* inst; |
|
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(struct UTUN_INSTANCE* inst, 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(struct UTUN_INSTANCE* inst, 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 { |
|
struct UTUN_INSTANCE* inst; |
|
char channel_id[64]; |
|
uint64_t ts; |
|
uint64_t author_node_id; |
|
}; |
|
|
|
/* Сбор статуса (NTP + connections) и отправка в GUI. arg = struct UTUN_INSTANCE* (inst) */ |
|
void chat_core_collect_status_trampoline(void* arg); |
|
|
|
/* Сбор списка ETCP-соединений и отправка в GUI (binary packet). arg = inst */ |
|
void chat_core_collect_conn_list_trampoline(void* arg); |
|
|
|
struct chat_node_arg { struct UTUN_INSTANCE* inst; uint64_t node_id; }; |
|
|
|
/* Сбор полных метрик одного соединения (arg = u_malloc struct chat_node_arg) */ |
|
void chat_core_collect_conn_metrics_trampoline(void* arg); |
|
|
|
/* Сбор live-снапшота для панели деталей участника (arg = u_malloc struct chat_node_arg) */ |
|
void chat_core_collect_member_detail_trampoline(void* arg); |
|
|
|
/* ── Headless CLI API ── */ |
|
|
|
/* Список каналов с метаданными в JSON: [{id,name,owner_id,owner_name,peers,msgs,online}] */ |
|
int chat_core_get_channels_json(struct UTUN_INSTANCE* inst, char* buf, size_t buf_size, size_t* out_len); |
|
|
|
/* Сообщения канала в JSON: [{id,ts,author_id,author_name,content_type,data,local_attrs}]. |
|
* От новых к старым; count<=0 — до 100 записей, offset — сколько пропустить. */ |
|
int chat_core_get_messages_json(struct UTUN_INSTANCE* inst, const char* ch_id, int count, int offset, |
|
char* buf, size_t buf_size, size_t* out_len); |
|
|
|
/* Мемберы в JSON: [{node_id,name,online,connected,adm_tags,x25519,ed25519,addrs}]. |
|
* connected означает наличие транспорта в реестре, а не его UP или готовность группы. */ |
|
int chat_core_get_members_json(struct UTUN_INSTANCE* inst, const char* ch_id, char* buf, size_t buf_size, size_t* out_len); |
|
|
|
/* Имя узла из таблицы nodes */ |
|
int chat_core_get_node_name(struct UTUN_INSTANCE* inst, 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 |
|
|
|
struct chat_member_tags; |
|
|
|
/* Загрузить текущие adm_tags мембера в память. Возвращает NULL если мембера нет. */ |
|
struct chat_member_tags* chat_member_tags_load(struct UTUN_INSTANCE* inst, 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 UTUN_INSTANCE* inst, 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 { |
|
struct UTUN_INSTANCE* inst; |
|
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(struct UTUN_INSTANCE* inst, const char* ch_id, uint64_t target_node_id); |
|
void chat_core_transfer_admin_trampoline(void* arg); |
|
|
|
struct chat_transfer_admin_req { |
|
struct UTUN_INSTANCE* inst; |
|
char ch_id[64]; |
|
uint64_t target_node_id; |
|
}; |
|
|
|
#endif /* CHAT_CORE_H */
|
|
|