Введение
Мысль написать свой мессенджер у меня возникла ещё этак в прошлом году, почти четыре месяца назад. Тогда ещё трава была зеленее и Telegram нормально функционировал, без замедления и финального блокирования. Из-за работы, других проектов и в конце-концов лени я откладывал написание мессенджера до лучших худших времён. И вот такие времена настали, телеграм был полностью заблокирован и без использования VPN до него уже нормально не достучаться. С полыхающей пятой точкой и мотивацией Вергилия я решил наконец-таки начать писать мессенджер, который в результате был создан за неделю (начал 19 числа, закончил 26, тобишь сегодня).

И в чём собственно суть / цель данного мессенджера? Здесь я преследовал несколько задач:
-
Было интересно написать +- нормально работающее приложение под андроид (по факту и под Linux / Windows) на библиотеке Fyne, не используя при этом AndroidStudio, Java, Kotlin и прочие нормальные (если AndroidStudio можно таковым назвать с его маниакальным аппетитом к оперативной памяти) инструменты мобильной разработки. Это был некий челлендж того, насколько Fyne сможет предоставить возможностей. Потом об этом я ещё напишу и вкратце опишу все нюансы и костыли с которыми столкнулся,
-
Было интересно написать мессенджер с E2E-шифрованием, который бы мог +- адекватно работать в групповых чатах. На тот момент времени мной уже был написан мессенджер на основе анонимной сети Hidden Lake. Хоть данный мессенджер и обладал E2E-шифрованием по умолчанию, но групповые чаты для него были роскошью (в основном из-за специфики QB-задачи),
-
Мессенджер должен быть простым в использовании, но при этом и не централизованным, в привычном смысле слова. Взгляд пал на архитектуру слабой централизации при которой любой сервер может быть заменяем, клиент может использовать несколько серверов, через клиентов будет проходить ретрансляция данных с одного сервера на другие (в рамках скоупа самого клиента – его каналы и сообщения). В результате этого, при блокировке одного сервера – вполне корректно продолжит функционировать другой.
Когда решил всё-таки написать мессенджер
За день конечно удалось написать только базу группового чатов по типу следующего. А все остальные 6 дней добавлял БД, сертификаты, фиксил код, добавлял поиск, редактирование канала, фиксил код и т.д.

Поиск похожих проектов на Fyne
Честно сказать, я в начале вообще не смотрел есть ли какие-то мессенджеры / чаты, которые бы выглядели как завершённые проекты с хорошо продуманным UI/UX. Возможно если бы я потратил некоторое время на поиск аналогов, я бы и не начал писать свой мессенджер. И нет! Вовсе не из-за того, что аналоги которые я недавно нашёл настолько хороши, что я впал бы в депрессию – напротив, их почти нет. И почему бы я не начал писать, ведь альтернатив особо нет? Да потому что этот факт свидетельствует о том, что написать что-то красивое на Fyne либо крайне сложно, либо невозможно с учётом достаточно долгого существования самого проекта.
Как пример, вот несколько проектов связанных с мессенджерами / чатами c официального сайта https://apps.fyne.io/all.html:
-
https://apps.fyne.io/apps/com.github.magdev2022.p2pChat.html
-
https://apps.fyne.io/apps/xyz.andy.fybro.html (это уже плюс-минус нормальное и наверное единственное)
Скрытый текст
Я кстати подал заявку на официальный сайт, чтобы внесли в список и этот мессенджер. Но думаю из-за названия и логотипа, шансы крайне малы
И с хабра:
-
https://habr.com/ru/articles/802815/ (здесь стоит сказать, что цель больше была показать сам процесс программирования, нежели результат с итоговым UI/UX)
-
https://habr.com/ru/articles/1013178/ (здесь клиент LLM, но и даже таковой был построен не по классическому интерактивному способу взаимодействия в виде чата)
Всё это я показываю не для того, чтобы кого-то оскорбить и сказать, что вот они плохо поработали над UI/UX. Это я демонстрирую только для того, чтобы показать, что Fyne крайне плохо может быть применён в создании чатов/мессенджеров. По мере работы с данной библиотекой я столкнулся с огромным количеством костылей просто потому, что Fyne не имеет возможности реализовать как-то иначе то или иное решение. Далее и более подробно я постараюсь показать что имею в виду.
Fyne – это просто боль
Этот блок можно пропустить, если интересны больше технические подробности со стороны безопасности или если интересен итоговый результат. Здесь будет в неком виде нытьё, в неком виде конструктивная критика
Я сейчас буду много критиковать Fyne, но всё равно стоит сказать, что данный проект, даже с учётом всех нижеописанных недостатков, мне понравился, в Fyne есть просто гигантские перспективы роста, особенно для таких людей как я, которым лень вникать в особенности мобильной разработки. Да и вообще я никакой не дизайнер и не фронтендер – мне это всё не нравится, и Fyne в таком случае является выходом из этой ситуации, тернистым, топорным, деревянным, но выходом.
Виджет Label и поле Selectable
И так, приступим. Первым нюансом с которым с столкнулся – это виджет Label. Самый обычный дефолтный виджет, в который можно вписать текст и радоваться, что тут может пойти не так? Виджет по умолчанию не поддерживает копирование текста. По умолчанию же, не проблема – просто ставим поле Selectable и радуемся, так ведь?

Нет, в идеальном мире это было бы отличное решение и при корректной кросс-компиляции не возникло бы никаких трудностей. Но кросс-компиляция в Fyne с особенностями. Суть заключается в том, что я хотел делать списки Label’ов – это был бы мой чат. Я хотел, чтобы текст можно было из чата копировать (и не по какой-либо кнопке как везде в сообществах Fyne советуют!), а просто при помощи образного Ctrl+C. При тестировании на Linux всё работало идеально, как и задумывалось, но стоило мне это портировать на Android – отключался скроллинг списков, потому что Fyne решил брать приоритет при нажатии не на скролл страницы, а на выделение текста в виджете из-за чего скролл работал просто неадекватно, вернее сказать не работал – он не мог пролестнуть и страницы. Это было первое моё изумление как такое можно было сделать с учётом того, что большинство имеющихся приложений вполне нормально разрешают копировать текст и скроллить страницу. Взять для примера тот же Ozon и копирование названия товара. В итоге, я сдался и решил, что потом как-нибудь починю, лимит истраченных нервов уже был превышен.
Метод Scrolled и поле OnScroll
Мне нужна была логика скроллинга чата и подгрузки уже ранее имеющейся истории, когда был проскроллен весь имеющийся батч/пакет сообщений. Это было необходимо для того, чтобы не загружать всю историю чата одномоментно в оперативную память. В общем стандартная функциональность для мессенджеров и чатов (хоть изначально я думал схалтурить и просто сделать так, чтобы подгружалось N-ое количество сообщений, тем самым не париться с этим). Поглядел, почитал в разных источниках – есть метод Scrolled – он считывает событие скроллинга. Потрясно думаю я, то что нужно – как только скроллинг дойдёт до позиции Y=0, следует подгружать историю. Так я и сделал, решение идеально завелось на Linux, но вы наверное уже чувствуете что что-то здесь …

И да, легендарная кросс-компиляция Fyne сделала так, что на Android функция Scrolled вообще не вызывается. Вообще ни при каких обстоятельствах, я даже ради этого поле с логгированием создал в мессенджере, чтобы проверять вызывается ли она вообще. Ответ: нет, не вызывается как бы я свой палец не натирал на экране ничего не двигалось. Почему не работало? Я не знаю и до сих пор. В общем начал искать решения проблемы и обнаружил, что существует также функция / поле OnScroll, которую можно зассетить в Scroll контейнер. Переместил туда логику и оно заработало ¯_(ツ)_/¯. И на том спасибо. В чём принципиальное различие этих двух функций я так и не понял.
Refresh обязателен или нет?
Эта проблема меня менее всего бесила, но глаза мазолила. Суть такая, что вызов Refresh на виджеты в Linux скорее всего необязателен, но в Android – это обязательно нужно вызывать иначе состояние того или иного объекта просто не будет изменяться пока не перезапустишь приложение. Почему нельзя было сделать также в Linux, чтобы была лучшая совместимость в моей любимой кросс-компиляции – хз.
Ввод большого текста в Entry приводит к лагу приложения
Issue с проблемой: https://github.com/fyne-io/fyne/issues/678#issuecomment-3879645905
С этой проблемой я столкнулся ещё когда писал клиента для анонимной сети Hidden Lake также с использованием Fyne. Тогда мне необходимо было в поле соединения с другом – вставить его публичный ключ. Ключ в Hidden Lake является постквантовым, что само по себе уже свидетельствует о бОльшей длине ключа, чем при использовании классических алгоритмов. Но к этому же ключ ещё и двойной – один для инкапсуляции ключей (ML-KEM-768), другой для подписания сообщений (ML-DSA-65). В итоге, размер ключа превышал 6KiB. Технически не так много, примерно 6000 символов, но у Fyne были другие планы. Как только вставлялся текст такого или бОльшего размера, поле ввода становилось чёрным и после этого окно начало тормозить. Всё связано из-за того, что поле Entry в Fyne анализирует изменение каждого символа и отдаёт событие на это. При этом альтернатив данному полю собственно и нет, а если и есть, то они работают по схожему принципу. В конечном итоге, мне пришлось делать вставку ключа с подменой отображения на статику PubKey{…}, а во внутреннем состоянии Entry сохранять содержимое. В мессенджере такого у меня ещё не было, но гарантированно будет, если кто-то решит вставить большой текст!
Отсутствие remote push-уведомлений
Предположим сценарий при котором ваше приложение закрыто и тут вам кто-то пишет. В стандартных приложениях вам просто высветится push-уведомление с конкретным сообщением или просто информацией о том, что кто-то написал – посмотри, пожалуйста. Вроде ничего необычного, должен быть запущен какой-то слушатель в фоновом режиме ОС, который бы перенаправлял уведомление на приложение. Но нюанс заключается здесь в том, что Fyne не поддерживает никакие виды Foreground / Background сервисов. У него есть только локальные push-уведомления, которые работают до тех пор, пока приложение активно и перестают после закрытия. С этой проблемой я ещё столкнулся, когда хотел имплементировать анонимную сеть Hidden Lake на андроид, чтобы процесс генерации и обработки трафика постоянно находился в фоновом режиме. Не удалось в общем…
Скрытый текст
Когда я совсем отчаялся и попросил альтернативные варианты у ИИ гугловского поисковика – он дал настолько гениальный ответ, близкий к безумию. Он посоветовал отправлять постоянно, раз в одну-две минуты локальные push-уведомления, чтобы процесс андроида считал приложение всегда активным. С одной стороны это конечно бы работало, но вот спам уведомлениями любого бы вывел из себя. Тем не менее идея забавная, я бы до такой фигни не додумался, хотя очень люблю костылить.
И снова Label, но уже с форматированием
Представьте такую ситуацию – вы хотите отправить в тексте ссылку на товар, на картинку, на видео – вообще без разницы. И вот вы отправляете, а ссылка – чистый текст, так ещё и вдобавок который нельзя скопировать (ノಠ益ಠ)ノ彡┻━┻. Тут я начал думать, ну наверное как-то всё же можно сделать форматирование текста так, чтобы вставить ссылку – ну можно же, так ведь? (дежавю?) Fyne отвечает – “а не сильно ли ты много хочешь, дружок?”. В Fyne есть отдельные виджеты на добавление ссылок, это здорово, но не то. Есть RichText, это здорово, говорит Fyne, но не хочешь ли ты такого отображения?

Спросите меня какого фига Fyne решил перенести ссылку на следующую строку? Мне этот мир уже настолько понятен, что я к этому отношусь с пониманием. А почему в итоге то строка перенеслась – я без понятия.
Но справедливости ради, есть ещё такой способ. О нём я узнал относительно недавно, поэтому в будущем постараюсь обновить мессенджер. А пока он едет на чистом Label.

Перекрытие виджета Button списком при вводе текста
Issue с проблемой: https://github.com/fyne-io/fyne/issues/5695#issuecomment-4125797414
Ситуация – у вас есть список сообщений, есть поле ввода текста и кнопка отправления этого сообщения. Всё просто и стандартно. Вы на андроиде, пишите текст и вот уже готовы его отправить. Нажимаете на кнопку отправления, клавиатура уходит вниз, кнопка не прожата. Думаете – ну наверное промазал, отправлю пока так сообщение. Нажимаете кнопку, сообщение отправлено. Повторяете процедуру – то же самое, в этот раз вы уверены, что точно не промахнулись по кнопке, но всё равно кнопка не прожалась. А всё дело в том, что почему-то так решилось, что приоретизация кнопки ниже списка со скроллингом, но при этом приоретизация Selectable поля в Label виджете выше списка со скроллингом ╥﹏╥!

Как показывает практика, такое неподобающее поведение Fyne случается довольно редко. Нашлось на этой планете только два человека: я и тот кто написал Issue. Везение оно такое, работает в обе стороны. В итоге, какое решение я предпринял? Просто удалил все кнопки, которые бы требовали ввода Input и перевёл само отправление на нажатие Enter / Submit.
Ну и на этом пожалуй всё. Можно конечно ещё говорить о том как я хотел сделать красивое отображение собеседующего с аватарками и прочими достопримечательностями. Но здесь скорее уже меня не хватило на саму реализацию. Может быть в будущем это и появится.
Модель угроз
После всего вышеописанного эпоса можно поговорить и о безопасности мессенджера. В первую очередь необходимо разобраться с какими криптографическими алгоритмами будем иметь дело.
-
Симметричный алгоритм – AES-256-GCM. Классика, которая реализует AEAD-режим. Фактически нам нужно лишь AE. Вектор инициализации IV у нас будет генерироваться случайный, чтобы уменьшить количество возможных состояний протокола. В этом есть и минус – появляется угроза парадокса дней рождения, из-за чего 96-битный вектор инициализации становится фактически 48-битным. При достижении 232 отправленных сообщений необходимо будет менять ключ шифрования (рекомендация NIST SP 800-38D).
-
Асимметричный алгоритм инкапсуляции – ML-KEM-768. Не классика, но к сожалению необходимость для будущего. При появлении достаточно мощных квантовых компьютеров можно будет взламывать все ранее сгенерированные шифртексты. С другой стороны, криптография – наука консервативная, и прошло достаточно мало времени и потрачено сил криптоаналитиков, чтобы можно было с бОльшей уверенностью говорить о его безопасности. Тем не менее мы играем в игру при которой либо нас сейчас начнут взламывать с N%-ой вероятностью (где 0 < N < 100), либо нас теоретически способны будут взломать в будущем со 100%-ой вероятностью. Лучше поставить ставку на прогресс.
-
Асимметричный алгоритм подписания – ML-DSA-65. Не классика, но также необходимость для будущего. Применимы все те же тезисы, что и к ML-KEM-768. Теоретический нюанс который здесь возникает – это то, что оба алгоритма являются ML (основанными на решётках). Если задача будет взломана, то будут взломаны оба алгоритма одномоментно.
-
Хеш-функция SHA-384. На первый взгляд странный выбор, т.к. в основном выбирают либо SHA-256, либо SHA-512. Но суть здесь заключается в том, что все алгоритмы подобраны под 128-битную защиту при существовании достаточно мощных квантовых компьютеров. Алгоритм Гровера теоретически позволяет более успешно производить атаку парадокса дней рождения не за 1/2, а за 1/3. Из этого также следует, что смена ключа шифрования для AES-256-GCM (если адаптироваться под квантовые компьютеры) должна быть ещё чаще чем 232 переданных сообщений, из-за малого вектора инициализации IV.
-
Код аутентификации HMAC(SHA-384). HMAC наследует безопасность применяемой хеш-функции. Если она безопасна, то настолько же безопасен HMAC.
Таким образом, выбранные криптографические алгоритмы, с определёнными выше оговорённостями, можно считать безопасными. Теперь же необходимо прочертить модель угроз самого мессенджера и за какую область безопасности он отвечает, а за что напротив – не берёт ответственности.
Мессенджер не является анонимным. Внешний наблюдатель может располагать информацией о взаимодействии клиентов с сервером и на основе этого делать дальнейшие предположения об отправлении и получении сообщений. Компрометация сервера позволит наблюдателю получить более точную и обширную информацию о пользователях, такую как: инициализация / подгрузка клиента, инициализация / подгрузка канала, отправление / получение сообщения, список клиентов, список участников канала, список зашифрованных сообщений в канале. Для анонимизации канала рекомендуется использовать анонимную сеть, а через мессенджер уже передавать симметричные / публичные ключи, если того требует процедура инициализации анонимизирующей системы.
Мессенджер не защищает от наблюдателя, у которого имеется доступ к устройству. Предполагается, что среда, в которой будет запущен процесс мессенджера, является надёжной и не допускает к себе сторонних лиц. Если чувствуется угроза, то следует удалить приложение и воспользоваться каким-либо механизмом очистки памяти. В крайнем случае следует физически ликвидировать устройство, а конкретно – модули памяти.
Мессенджер защищает передаваемые сообщения от чтения сторонними лицами посредством симметричного шифрования (AES-256-GCM). Ключ симметричного алгоритма генерируется на стороне создателя канала и передаётся в шифрованном виде на публичном ключе каждого участника канала (ML-KEM-768). Каждое передаваемое сообщение и инициализация канала подписывается приватным ключом создателя (ML-DSA-65).
Предполагается, что у клиентов корректно работают и функционируют криптографические алгоритмы, в частности КСГПСЧ, как наиболее чувствительная сущность. Предполагается также, что клиенты со временем должны будут менять каналы, чтобы стойкость симметричного ключа, сопоставимая примерно 232 переданных сообщений, не успевала расходоваться.
Криптографический протокол
При проектировании протокола одним из главных условий у меня было – это минимизировать количество возможных состояний. Одно из таковых я уже обсуждал – вектор инициализации IV, т.к. достаточно проблемно учитывать порядок сообщений в C2C (client-to-client) соединении. Также из протокола я убрал возможность добавлять или удалять пользователей из каналов. Данный механизм требует дополнительного описания и также приводит к возможному порождению состояний, когда у одного клиента старая информация о канале, а у другого – новая.
В протоколе также существует ещё дополнительный алгоритм, который не был указан в модели угроз – алгоритм подтверждения работы (Proof of Work). Данный механизм необходим для всех основных сетевых действий – инициализации клиента на сервере, инициализации канала, отправлении сообщений. Это сделано для того, чтобы уменьшить количество возможного спама, если таковой настанет. У каждой функции своя сложность подтверждения:
-
Инициализация клиента – сложность 30-бит. Это примерно 7-20 минут подтверждения публичного ключа, чтобы его далее установить на серверах и чтобы его могли подгружать другие клиенты,
-
Инициализация канала – сложность 26-бит. Это примерно 2-3 минуты подтверждения идентификатора канала, чтобы его далее установить на серверах и чтобы его могли подгружать участники этого канала,
-
Отправление сообщения – сложность 22-бит. Это примерно 2 секунды подтверждения хеша сообщения, чтобы его далее установить на серверах и чтобы его могли подгружать участники конкретного канала.
Инициализация клиента
(sk, pk) <- G() // генерация приватного / публичного ключей
p <- P(pk) // подтверждение публичного ключа доказательством работы
(p, pk) -> SERVER // регистрация публичного ключа на сервере
Подгрузка клиентов
H(req_pk) -> SERVER // запрос на получение публичного ключа по хешу
(p, pk) <- SERVER // получение ключа с доказательством работы
ok ?= Vp(pk, p) // проверка доказательства работы
H(req_pk) ?= H(pk) // проверка полученного публичного ключа
pk -> STORAGE // сохранение ключа
Инициализация канала
k <- G() // ключ канала
enc_n <- E(k,name) // зашифрованное название канала
PL = (H(my_pk), H(pk1), H(pk2), ..., H(pkN)) // хеши публичных ключей участников канала
EL = (E(my_pk, k), E(pk1, k), E(pk2, k), ..., E(pkN, k)) // зашифрованный ключ канала публичными ключами участников
id <- HMAC(k, (enc_n || PL || EL)) // идентификатор канала
s <- S(my_sk, id) // подпись канала
p <- P(id) // подтверждение доказательством работы
(id, enc_n, PL, EL, p, s) -> SERVER // отправление информации о канале серверу
Подгрузка каналов
(id, enc_n, PL, EL, p, s) <- SERVER // получение канала
ok ?= Vs(PL[0], id, s) // проверка подписи
ok ?= Vp(id, p) // проверка доказательства работы
index <- H(my_pk) ?in PL // проверка моего ключа в списке
k <- D(my_sk, EL[index]) // получение ключа канала
id ?= HMAC(k, (n || PL || EL)) // проверка идентификатора канала
n <- D(k, enc_n) // получение названия канала
(id, k, n, PL) -> STORAGE // сохранение канала
Отправление сообщения в канал
k <- STORAGE[id] // получение ключа канала
c <- E(k, m) // создание шифртекста
h <- HMAC(id, c) // хеш шифртекста
s <- S(my_pk, h) // подпись хеша
p <- P(h) // подтверждение хеша доказательством работы
(id, H(my_pk), c, s, p) -> SERVER // отправление сообщения на сервер
Получение сообщения из канала
(id, H(pk), c, s, p) <- SERVER // получение сообщения от сервера
ok ?= Vs(pk, c, s) // проверка подписи участника
ok ?= Vp(c, p) // проверка доказательства работы
(k, PL) <- STORAGE[id] // получение ключа и списка участников канала
H(pk) ?in PL // проверка принадлежности участника к каналу
m <- D(k, c) // расшифрование сообщения
m -> STORAGE // сохранение сообщения
Алгоритм использования
У мессенджера существуют свои тараканы, поэтому алгоритм запуска, особенно первичного, разнится с большинством других мессенджеров. Алгоритм первичного запуска следующий:
-
Как только запускается мессенджер – он начинает генерировать пару приватный / публичный ключ. Далее хеш публичного ключа подвергается доказательству работы со сложностью 30-бит. При этом этапе главное не закрывать приложение, пока в логах (на странице настроек) не будет надпись “client is locally initialized!”.
-
После успешной инициализации – нужно подключиться к серверам. Для этого необходимо обладать его TLS сертификатом. Я воспользовался уже арендованным сервером на vdsina и сертификат указал ниже в спойлере. Его необходимо добавить на странице настроек, где также располагаются логи. Если соединение горит зелёным – значит Ping прошёл успешно, в противном случае – нет подключения или какие-то ошибки соединения.
-
Для связи с участниками сети – необходимо либо создать собственный канал, в котором будет указан весь список участников, либо когда другой участник укажет вас в собственном канале. Для создания канала с пользователями – необходимо на странице “Add channel” добавить хеш публичного ключа одного из пользователей. Мой хеш я также приложу в описании, если кому-то будет интересно пообщаться. После добавления нужно назвать как-либо свой канал и подтвердить создание. Само создание будет также подвергаться PoW-алгоритму в фоновом режиме, но уже со сложностью 26-бит.
Скрытый текст
Сертификат сервера
-----BEGIN CERTIFICATE-----
MIIBUzCB+aADAgECAgEBMAoGCCqGSM49BAMCMA8xDTALBgNVBAoTBDk5OTkwHhcN
MjYwMzI1MjM1ODE2WhcNMzEwMzI1MjM1ODE2WjAPMQ0wCwYDVQQKEwQ5OTk5MFkw
EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBaWNuAYWwm81AZcW9XiocM3oa1uP7PbE
OZWvGYlLWYBNOm1iUcoAf+2s9JAobCjLYwt8jbPXovfPA1Q5ikVLNaNGMEQwDgYD
VR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAw
DwYDVR0RBAgwBocEXmdbUTAKBggqhkjOPQQDAgNJADBGAiEA2s7XAVOPX0/Tq2Iq
ABPiLdtG1qtckhqvAG58wTrBJ6ACIQCyVF06MIsJBk9cUXjGWmlkzx6D4hlJzHbq
BMqeLdChUQ==
-----END CERTIFICATE-----
Мой хеш
3c45f47ce55ef0c24a0303fe0e694b45bd2f0cf64973e0bed0eda084c48140fb6d1286c798e923bb1670ed92105bebea
Как это добро вообще выглядит
Вот так (на примере взаимодействия нескольких участников)
Скрытый текст
Заключение
Мессенджер был написан всего за неделю – я уверен, в нём найдётся ещё много неожиданных моментов, особенно с лучшей кросс-компиляцией Fyne. Мне даже страшно как этот мессенджер запустится на Windows, потому как я тестировал его только под Linux и Android (и то не уверен насколько Fyne будет успешно работать на других версиях андроида, с другим железом и прочими характеристиками). В любом случае, результат как по мне, получился достаточно приемлемый и опыт написания приложения полностью на Fyne был запоминающимся. Не рекомендовал бы его повторять, но запоминающийся это да…
Из близлежайших обновлений, которые я бы хотел сделать:
-
Перевод Label на RichText в отображении сообщений канала,
-
Выгрузка и загрузка LocalData (где хранится приватный ключ, чёрные списки каналов и клиентов, избранные каналы, соединения), чтобы иметь возможность один профиль переносить на другие устройства,
-
Добавить больше информационных логов, отвечающих за действие передачи / принятия клиентов, каналов, сообщений,
-
И конечно же отрефакторить этот код. За неделю было написано столько “красивого” кода, что ещё примерно неделю мне придётся его исправлять. Это похоже вот на следующий мем:

Ну и на этом пожалуй всё, с проектом более детально можно ознакомиться тут: https://github.com/number571/fuckoff-gov. Установить приложение можно либо из релизов, либо самостоятельно средствами Go. Весь исходный код проекта открыт, если будут появляться какие-то баги в приложении, то пишите в Issues, ну или на худой конец в комментариях – буду стараться фиксить.
Автор: Number571


