Неизвестный ответ — это никогда не отсутствие
getChatMember может завершиться неудачей: Telegram недоступен, бот потерял
права администратора в канале, API ограничивает частоту запросов. Ничто из
этого не означает, что клиент вышел.
Неудавшаяся проверка записывает unknown — состояние, отличное от absent, и
оно ничего не блокирует. Если бы эти два состояния слились в одно, людей
блокировали бы из-за сбоя, и это тот режим отказа, который есть у данного
механизма и которого нет больше нигде в продукте.
Клиент, у которого вообще нет записи о членстве, находится в том же положении:
никто не установил, что он отсутствует, поэтому ничто не ограничивается.
Два требования — два обещания
Они разделены, потому что это разные обещания. Заблокировать может только
требование при активации; ограничение покупки ни у кого ничего не отбирает.
Требование при покупке применяется на обеих поверхностях по одному правилу. Бот
проверяет его, когда клиент нажимает Оплатить, и спрашивает Telegram вживую,
если кэшированному ответу больше пяти минут. Веб-оформление проверяет его, когда
клиент нажимает Подтвердить, но у процесса API нет собственного транспорта
Telegram, поэтому он читает только подписки, записанные воркером: ответ старше
двадцати четырёх часов или его отсутствие — это unknown, а unknown никогда не
блокирует. Клиенту, которому веб отказал, показываются каналы с кнопкой
подписки для каждого; как только он подписался, следующий проход воркера — или
«Я подписался» в боте — записывает новое состояние, и подтверждение проходит.
Клиент без идентичности Telegram не может состоять в Telegram-канале, и его об
этом не просят.
О выходе предупреждают до того, как он что-то стоит
Последовательность такова: compliant → warned → suspended, и она управляется льготным периодом, который настраивает оператор. Первая проверка, обнаружившая отсутствие клиента, предупреждает его и запускает отсчёт. До истечения отсчёта не происходит ничего. Предупреждение срабатывает один раз за эпизод, а не при каждой проверке, потому что ежедневное напоминание подписаться на канал — это то, как клиент учится игнорировать бота. Три дня выбраны значением по умолчанию потому, что этот срок покрывает выходные, когда человек с наибольшей вероятностью разбирает список своих каналов и с наименьшей вероятностью замечает последствия. Нулевой льготный период — законная конфигурация, и он блокирует сразу, но предупреждение всё равно отправляется, потому что клиент, заблокированный без предупреждения, — это клиент, который не может понять почему.Что получает клиент и что происходит на самом деле
Каждая смена состояния отправляет в чат клиента одно сообщение. Предупреждение перечисляет каналы, на которые он всё ещё не подписан, с кнопкой для каждого, и называет момент, когда доступ прекратится, если не подписаться раньше. Сообщение о приостановке говорит, что доступ выключен и что повторная подписка вернёт его при следующей проверке. Сообщение о восстановлении говорит, что доступ вернулся. Проверка, которая ничего не изменила, ничего не отправляет. Приостановка и восстановление не пишутся в Remnawave напрямую. Воркер ставит fulfillment-операциюdisable для каждого entitlement, чей оплаченный период
ещё не закончился, и enable для каждого, который Remnawave считает
отключённым, когда клиент возвращается, — под ключом идемпотентности, в котором
записаны entitlement и момент решения. Поэтому клиент, отключённый за выход из
канала, несёт ту же историю операций, что и отключённый оператором вручную, а
оператор, читающий журнал fulfillment, видит корреляцию channel-gate.
Entitlement, приостановленный оператором, не затрагивается: он выключен по
решению, которое повторная подписка отменять не должна. Состояние принуждения
записывается до попытки применить последствие, так что последствие, которое не
удалось поставить в очередь, повторяется следующим проходом, а не теряется.
Повторная подписка восстанавливает доступ автоматически
Клиенту, устранившему проблему, не должно приходиться просить кого-то отменить последствия. Следующая проверка, обнаружившая его членство, восстанавливает доступ независимо от того, был ли он предупреждён или уже заблокирован.left_at проставляется в момент, когда отсутствие замечено впервые, и
очищается при возвращении, поэтому льготный отсчёт ведётся от момента, когда
клиент действительно вышел, а не от момента, когда это заметил проход воркера.
Исключения
Правило является маркетинговым механизмом, и всегда есть те, к кому оно применяться не должно: платящий клиент, который не может подписаться по своим причинам, кто-то, кому оператор пообещал, тестовый аккаунт. Исключение фиксирует, кто его выдал и почему, потому что исключение, которое никто не может объяснить, неотличимо от ошибки. Оно может иметь срок действия. Исключение для заблокированного клиента восстанавливает его немедленно — правило больше не применяется, а значит, не должно применяться и его следствие. Отзыв исключения возвращает клиента в состояниеcompliant, а не сразу в
suspended: решает следующая проверка, и она предупреждает первой, как и для
всех остальных. Блокировка при отзыве отобрала бы доступ без всякого
уведомления.
Кеширование и ограничение частоты запросов
Ответы о членстве кешируются на настраиваемое окно, по умолчанию шесть часов.getChatMember ограничен по частоте запросов у третьей стороны, и повторный
опрос на каждом экране сделал бы бота медленнее для всех ради соблюдения
маркетингового правила.
Воркер перепроверяет ограниченными партиями, поэтому крупная установка
перепроверяется за много проходов, а не одним всплеском, из-за которого бота
начнут троттлить.