Pull to refresh

Comments 35

Я не погромист, но считаю себя умеющим писать ТЗ. С помощью антигравити создал рабочий проект по передачи пушей с одного устройства на другое. Суть проекта в том, что если у человека много телефонов, собирать пуши с них на основной, где, например, часы привязаны. Или как вариант прямо на часы.

Разумеется, нужно было обходиться без всякого кривого gms, который и работает не понятно как, и не везде есть, а на часах и нет. Поэтому было выбрано решение использовать отдельный сервер (оно написало его на go) и mttq протокол. Приложения успешно объединяются в группу, есть фильтры и прочее. Сама передача пушей работает отлично даже при проблемах с gms. В качестве идентификатора группы устройств используются guid, регистрация не нужна.

Что думаете о таком концепте?

Концепт рабочий, и вы, по сути, независимо пришли к той же архитектуре, что и UnifiedPush: приложение получает у дистрибьютора адрес, сервер шлёт туда, дистрибьютор будит устройство. Разница в транспорте (у вас MQTT, у нас HTTP плюс WebSocket) и в том, что у вас адресом служит GUID группы, а у нас топик.

MQTT тут выбран удачнее, чем кажется на первый взгляд. У него дешёвый keepalive и нативный fan-out на нескольких подписчиков, а «раздать одно уведомление на телефон, планшет и часы» это ровно pub/sub. Мы остались на WebSocket только потому, что переиспользовали готовый ntfy, а если бы писали с нуля именно под несколько устройств одного человека, MQTT был бы честным кандидатом.

Три вещи, о которых стоит подумать, если проект выйдет за пределы личного пользования.

Первое. GUID у вас одновременно и адрес, и пароль: кто его узнал, тот и читает ваши уведомления, и подсовывает свои. У нас та же модель для топика, и это осознанный размен, но содержимое до сервера уже зашифровано, сервер видит шифротекст. У вас, если я правильно понял, текст уведомления идёт через сервер как есть, и это единственное место, где стоит навести порядок в первую очередь.

Второе. Дедупликация. Когда на одну группу подписаны телефон и часы, полезно, чтобы прочтение на одном гасило уведомление на другом, иначе часы будут вибрировать тем, что вы уже посмотрели.

Третье. Doze и вендорские «оптимизаторы» убивают долгоживущее соединение с большим удовольствием, и MQTT тут не защищён ничем по сравнению с WebSocket. Держите соединение foreground-сервисом и смотрите на интервал keepalive: слишком короткий ест батарею, слишком длинный даёт минуты задержки после выхода из сна.

Встречный вопрос, самому интересно: как вы снимаете уведомления с исходных телефонов, через NotificationListenerService? И что делаете с пачками, когда мессенджер присылает десяток уведомлений подряд, схлопываете или льёте как есть?

  1. приложение построено по принципу минимальной проблемности. никакой регистрации, почты, паролей, которые никто не запоминает, ставит одинаковыые и забывает, достаточно длинная случайная строка лучше 90% паролей обычного пользователя и дает быстрый старт (отдельно ниже). планирую добавить поддержку ssl между сервером и приложениями, этого достаточно, так как сервер можно использовать свой

  2. существующая модель осуществляет связь на основе принципа основного устройства, то есть главный телефон (мастер), на котором висят часы, или, собственно, сами часы, получает уведомления с группы. Так что отмечать прочитанность на другом устройстве вроде как не обязательно. Из плюшек есть история, поиск по истории, выбор приложений для захвата, выбор ключевых слов (черный и белый список). конкретно под часы оптимизированное приложение еще не собирал, планируется в нем функция второго мастера (когда на часах интернет через телефон, мастер-телефон, когда через esim-часы) что б меньше разряжать часы

  3. приложение выбрасывает и сервисное уведомление а так вносится в список защищенных (тестилось на ху/хо конкретно), проблем с выгрузкой нет, возможно в фоне что-то ходит, в явном виде это я не описывал

схлопывания нет, наоборот пришлось доработать так, что б не было сообщений вида "whatsapp 2 новых сообщения". используется стандартное разрешение на захват приложений, как приложение часов

теперь про принцип быстрого старта:

  1. на мастере указывается адрес сервера или используется дефолтный и нажимается кнопка "создать", это приводит к созданию группы и guid, на экране отображается qr содержащий сервер и guid

  2. клиент просто сканирует qr

  3. в настройках выбираются параметры захвата (приложения, ключевые слова)

голову не сношаем, почты не требуем, данные не собираем. сервер на go жрет 60мб только, работает без зависимостей. приложение на flutter, 80мб, не так много, как сейчас обычно, но есть желание заставить переписать на pure java/kotlin что б на часах не жрало

Как вы помните мы делаем мессенджер.

Нет, мы не помним. Нас тут десятки тысяч и все не следят за вашими статьями. А вам трудно дать в каждой статье хотя бы ссылку на сайт или репо вашего мессенджера? Или вам не нужны клиенты? Я понимаю политику “кому надо, тот сам всё разроет и сам разберётся”, но нужна ли вам именно эта политика?

Кстати, зашёл на репо. В главной папке нет readme. Почему?

При установке на Андроид, на одном из начальных экранов написано: работает через Bluetooth и Wi-Fi direct. А через стандартный инет работает? Непонятно.

По всем трём пунктам справедливо, спасибо. По порядку.

Про «как вы помните»: вы правы, так писать нельзя, читатель видит нас впервые. Поправили начало статьи, ссылка на сайт и на репозитории теперь в первом абзаце, а не в футере.

Про README: полез проверять и нашёл кое-что похуже. В репозиториях клиентов README есть, но у Android он был написан задолго до текущего состояния и сообщал, что это pre-alpha, в Play не выкладывается и кросс-платформенная совместимость ещё допиливается. Всё это давно неправда. Устаревший README хуже отсутствующего, потому что он отвечает на вопрос, и отвечает неверно. Переписали: что это, где скачать, что где лежит в коде, как собрать. Заодно нашли публичный репозиторий со спекой, у которого README действительно не было, добавили. Если вы заходили в какой-то третий репозиторий, скажите в какой, посмотрим и его.

Про Bluetooth и Wi-Fi Direct: это целиком наш косяк в тексте, а не ваше невнимание. Обычная переписка идёт через интернет, как в любом мессенджере. Bluetooth и Wi-Fi Direct это отдельный режим (Радиочат) на случай, когда сети нет совсем: устройства рядом находят друг друга и передают сообщения напрямую. Слайд онбординга был написан так, что читался как описание всего продукта. Текст переписали, в следующей сборке он начинается со слов о том, что обычная переписка идёт через интернет.

Спасибо за оперативный ответ и даже фиксы.

Про readme, имелась ввиду папка выше Андроида, то есть, главная папка проекта. Там нет readme.

Что пофиксили и обновили описания, это хорошо.

Точно, и это оказалось хуже, чем я сначала подумал. У организации действительно не было страницы: GitHub рисует её из специального репозитория .github, а его у нас просто не существовало. Человек приходил на организацию и видел список из восьми репозиториев без единого слова о том, что это и с чего начинать.

Сделали: github.com/rcq-messenger теперь открывается описанием проекта, таблицей «хочу сделать это, идти сюда» по всем репозиториям, ссылками на спеку, приватность и условия.

Спасибо, что дожали, сами бы мы на это ещё долго не посмотрели: когда живёшь внутри проекта, точка входа для постороннего человека это последнее, что замечаешь. Приносим изменения и еще раз благодарим за внимательность.

Рад, что помог. Может, возьмёте меня на ставку уже?)

С радостью! Но мы живем на донатах :'(

Комннты читаются как нейрослоп =(

Они и есть нейрослоп(

Я понимаю желание оптимизировать и автоматизировать все вокруг, но как же кровоточат глаза...

Сначала подумал, что мб просто через сетку прогоняют для вычитывания или какие-то куски скопипастили, но почитал остальные ответы и к сожалению да, все полимеры потеряны =(

Как же знакомо все) Да, firebase - единственная возможность получить пуш, если приложение смахнуто. Тоже прошли через все эти костыли и остановились на таком варианте: по умолчанию сообщение дублируется на почту пользователя если не прочитано в течении 10 минут. клиенты обычно в фоне норм работают и доставка у них более стабильна

Но с «firebase единственная возможность, если приложение смахнуто» я поспорю, тут важная разница, на которую мы сами не сразу посмотрели внимательно.

Смахивание из недавних и force stop это разные вещи. Force stop убивает всё, включая доставку через FCM: система перестаёт будить приложение, пока пользователь сам его не откроет. А обычное смахивание из списка недавних на стоковом Android не убивает foreground-сервис, и постоянное соединение переживает его спокойно. Именно так живёт ntfy, и именно так теперь живём мы: свой сокет в foreground-сервисе, ценой постоянного уведомления в шторке. Firebase выигрывает не в том, что он один умеет пережить смахивание, а в том, что при нём вашему приложению вообще не надо держать соединение и тратить батарею. Отдельная история это вендорские «оптимизаторы», вот они убивают и то, и другое, и с ними не воюет никто.

Дублирование на почту через 10 минут это честное инженерное решение, и оно закрывает ровно ту дыру, которая остаётся: сигнал не дошёл, но информация всё равно доехала. Нам этот путь закрыт по построению, у нас нет почты пользователя, её отсутствие это и есть продукт. Плюс для сквозного шифрования второй канал в открытом виде это шаг назад: содержимое уходит из системы к почтовому провайдеру.

Мы решили ту же задачу иначе: пуш-сервер держит сигнал в кэше 12 часов, а клиент при переподключении просит всё, что пришло после последнего доставленного, и догоняет пропущенное. Уведомление в этом случае приходит поздно, но не теряется. По сути та же гарантия, что у вас, только вторым каналом служит не почта, а само переподключение.

Интересно, как люди реагируют на дубли. Со стороны кажется, что это ровно та функция, которую часть пользователей попросит выключить, а часть не заметит вовсе. У вас есть переключатель?

Именно поэтому предыдущий пункт про туннель не косметика.

Во-первых, непонятно, что за туннель из предыдущего пункта.

Во-вторых, от этого фрагмента и всей статьи ужасно сквозит текстом, написанным ИИ.

Ответы автора в комментариях тоже сквозят ИИ. Так что есть сомнения в замыслах авторов "проекта".

Замыслы удобнее проверять не по стилю комментариев, а по тому, что можно открыть и посмотреть:

  • клиенты под AGPL-3.0, сервер тоже открыт и поднимается у себя, аккаунт не обязан жить у нас;

  • протокол выложен отдельным документом, а не «доверьтесь нам»;

  • при регистрации не спрашивается ни телефон, ни почта, ни имя. Это проверяется за минуту установкой;

  • ни рекламы, ни аналитических SDK в сборке, проект живёт на донатах.

Если конкретное утверждение из статьи вызывает сомнение, назовите его, и я покажу коммит, строчку в чужих исходниках или живой ответ сервера. В таком формате спор имеет смысл, в формате «текст звучит не так» его нет.

По первому пункту вы правы, это дыра в тексте. Слово «туннель» я использую так, будто читатель уже знает, о чём речь, а он не знает. Дописал абзац: внутри приложения живёт sing-box, он поднимает локальный прокси и уводит трафик через наши релеи по VLESS + Reality или Hysteria2, когда прямое соединение до сервера не проходит. Отдельного VPN ставить не надо, системного профиля не появляется. Спасибо, поправил.

По второму. Да, тексты мы пишем с ИИ и скрывать это не вижу смысла. Часть команды по-русски не пишет вообще, материал собирается на английском и переводится, отсюда ровный и слегка стерильный тон, который вы и почувствовали. Инструмент при этом остаётся инструментом: он не делает утверждение верным и не делает его ложным. Спорить сегодня о том, пользоваться им или нет, мне кажется примерно так же содержательно, как спорить о подсветке синтаксиса.

Поэтому единственный честный ответ на «пахнет ИИ» это «проверьте». В статье почти всё проверяемо без нас:

  • оба фрагмента, на которых держится весь разбор, лежат в исходниках ntfy: limitRequestsWithTopic в server/server_middleware.go и проверка на 507 в handlePublishInternal. Откройте и убедитесь сами, что лимит списывается с подписчика, а не с отправителя;

  • обязательность заголовка TTL это RFC 8030, раздел 5.2, читается за минуту;

  • код дистрибьютора, о котором речь, лежит в push/embedded в github.com/rcq-messenger/rcq-android, там же вся история коммитов с датами;

  • пуш-сервер, о котором речь, отвечает прямо сейчас: curl https://push.rcq.app/v1/health;

  • в описании релиза v0.75 лежат sha256 всех APK, те же файлы отдаёт rcq.app, хеши обязаны совпасть. Если не совпадут, это будет гораздо более интересная новость, чем стиль статьи.

Чего проверить нельзя, скажу прямо: цифры отказов взяты из журнала нашего прода, и тут остаётся верить на слово. Всё остальное открыто.

Горшочек-chatgpt не вари. Хватит отвечать через ИИ. Не поленитесь ответить хуману ручками.

Предлагаете использовать четки вместо калькулятора? Печально.

Вас просят уместно использовать инструменты.

Если от генерированного текста воротит, значит инструмент задачу коммуникации выполняет плохо. Либо потому что вы им не умеете пользоваться (а это так, потому что правильным промптом можно добиться человеческого стиля), либо потому что инструмент не дорос (и это то-же так, рано или поздно неестественность пофиксят).

Уже объясняли в других ветках, что большая часть команды не владеет языком, а также это экономит уйму времени, как в статье так и в комментариях все написано по делу, вопрос эстетики - это оффтоп.

Предлагаю использовать мозг...тем, кому это дано.

На вопрос про туннель вы ответили и поправили -- спасибо. Хотя я бы ожидал лучшей вычитки статьи перед публикацией.

А дальше вы отвечаете на вопрос, который я не задавал. При чем тут "код дистрибьютора" и прочие предложения идти и что-то проверять?

Статья интересная и техничная (это прекрасно), но читать противно (это ужасно). И вам об этом несколько разных участников сказали. Поэтому лучше принимайте сигналы и делайте выводы, а не пишите длинные водянистые ответы о том, что вас не спрашивали.

Если звучит резко, то так уж получается, пишу для робота же 😀

Вот так, здравая идея о едином дистрибьюторе, который держит всего один сокет и экономит батарею разбивается об реальность, где каждое приложение сам себе дистрибьютор и держит свой собственный сокет. Зачем эта прослойка в виде UnifiedPush теперь? Вы пытались связаться с ntfy чтоб решить найденные проблемы?

У меня сложилось впечатление что ntfy писали чисто с целью саботажа альтернатив гугла.
Неочевидное поведение, костыли, скрытые особенности - то что мы любим.
А ведь глобальная задача все еще крайне проста - обычный подписчик / паблишер, единственное существенное требование - экономия данных и энергоэффективность в фоне.

То есть, вместо того, чтобы запилить свой более подходящий для ваших клиента и сервера протокол, вы взяли unifiedpush, который переусложнил архитектуру и, скорее всего, раздул размер apk?

Мы и написали своё, вы точно понимаете что описывается в статье? Мы поставили пометку сложности, если что. Дистрибьютор наш, пуш-сервер наш, сокет наш. UnifiedPush это не реализация, а интерфейс между приложением и тем, кто ему доставляет: четыре системных бродкаста, REGISTERUNREGISTERNEW_ENDPOINTMESSAGE. Свой «более подходящий протокол» был бы ровно тем же foreground-сервисом с тем же сокетом, только несовместимым ни с чем.

Пользователь, у которого уже стоит ntfy или NextPush со своим сервером, остаётся на нём. Мы не обязаны быть единственной точкой, через которую ходят чужие уведомления. Для мессенджера, который продаёт «не доверяйте нам на слово», это не украшение, а свойство.

Сервер остаётся тупым: он делает POST на URL и всё. У нас федерация, сервер открыт и его поднимают у себя. Такому острову не нужно поднимать ещё и пуш-сервер, он может слать в любой UnifiedPush-эндпоинт, какой выберет его владелец.

И самое практичное: в июне это позволило отгрузить пуши без единой строчки пуш-инфраструктуры на нашей стороне, а в июле заменить транспорт целиком, не тронув ни одной строки в коде уведомлений. Именно это в статье и описано: тот же MESSAGE-бродкаст, та же библиотека, тот же обработчик, новый транспорт под ними.

Про размер APK, цифры из сборки v0.75, arm64 около 105 МБ:

libsignal (сквозное шифрование) 70,6 МБ
sing-box для обхода блокировок 41,1 МБ
WebRTC для звонков 11,0 МБ
SQLCipher 5,5 МБ
UnifiedPush connector 165 КБ

Connector это 0,16% сборки. Честный минус назову сам: он тянет tink (3,2 МБ до шринкера), потому что умеет расшифровывать WebPush-полезную нагрузку. Откажись мы от совместимости с WebPush-дистрибьюторами, эту зависимость можно выбросить. Вот это единственная реальная цена вопроса, и она пара мегабайт на фоне ста двадцати, которые занимают шифрование, обход блокировок и звонки.

Так что архитектуру он не переусложнил, он зафиксировал границу, по которой мы потом безболезненно поменяли всё, что за ней.

Про ИИ отвечу коротко, я это уже писал в соседней ветке.
Да, тексты пишутся с ИИ, часть команды по-русски не пишет вообще. Это не тайна и не оправдание, это способ производства (эх, четки и калькуляторы). Проверять имеет смысл не то, кто держал клавиатуру, а сходятся ли факты: два фрагмента из исходников ntfy, RFC 8030, наш код в push/embedded, живой curl https://push.rcq.app/v1/health и sha256 сборок в описании релиза.

Конечно, я понимаю. И я 15 лет был мобильным разработчиком.

Я о том, что можно было сделать своё, а вы увидели молоток, и всё остальное для вас стало гвоздями.

я не мобильный разработчик и, вероятно, не в курсе полностью, но мне казалось, что невозможно сделать надёжный push без сервисов Гугла.

Все остальные решения будут показывать перманентное уведомление о фоновом процессе. Даже если проверять по таймеру, то примерно то же самое.

Есть ещё какие-то способы?

Все остальные решения будут показывать перманентное уведомление о фоновом процессе.

Да, и это не должно считаться чем-то неприличным.

Это стратегия Гугла по загону всех коммуникаций в его каналы, чтобы иметь доступ ко всем данным. Не ведитесь на это.

Держим Foreground Service, подключение по сокету к серверу, и получаем пуши.

Держим Foreground Service, подключение по сокету к серверу, и получаем пуши.

Заранее прошу прощения, если вопрос тупой – с низкоуровневой инфраструктурой передачи пушей никогда не разбирался. Спрашиваю из любопытства.

Вот у меня сейчас есть аппа, которая тоже держит FGS – но, никакого уведомления на шторке она не показывает. По простой причине: у неё нет пермиссии не пуши – мне не надо, меня локейшн интересует, FGS только ради того, чтобы он не терялся в бэкграунде. А у вас так сделать нельзя? Если вы работаете в обход FCM, пермиссия на нотифы вообще требуется?

Ребята работают за донаты, зачем такие претензии. Пошли по естественному пути - сначала использовали доступные инструменты (протоколы и реализации unified push), когда там нашлись косяки минимальными усилиями починили для себя. Я думаю если бы написали свое решение были бы коменты "ох уж эти велосипедо строители, нет чтобы взять готовое решение".

И, кстати, статья более, чем полностью, написана Опусом. Да ещё и ответы на комменты так же. Вы совсем с дуба упали?

приложение, о котором узнаёшь, только когда сам его открыл, никому не нужно

Холодильник, который просто хранит продукты, а не гоняется за тобой по квартире в попытках запихнуть тебе в жопу бутылку кефира — никому не нужен.

Господи, сделай так, чтобы я никогда не обнищал духом и баблом настолько, чтобы мне пришлось включить уведомления на телефоне.

Sign up to leave a comment.

Articles