Как стать автором
Обновить

Как реализовать игнор заблокированных пользователей в группах Telegram?

Мессенджеры *Open source *C++ *Социальные сети и сообщества
image alt

UPD: благодаря помощи Tkachov эта статья всё-таки превратилась из просьбы о помощи в работающую инструкцию. В комментариях он дал работающий код для создания игнора в десктопном клиенте Телеграм. Я на всякий случай расположу объяснение в конце статьи. Далее статья продолжается в том же виде, в каком была написана.
UPD: Сделал «сайт» для распространения готового приложения sites.google.com/view/ignoram

* * *

Я хотел бы попросить помощи у сообщества, потому что все собственные возможности я полностью исчерпал.

Можно было бы задать вопрос на сервисе Q&A (он же бывший Тостер) — только я это уже сделал, и пользы вышло не очень много.

Тем не менее, просто задавать вопрос под видом статьи мне кажется не очень корректным. Поэтому я попытаюсь добавить в текст предысторию всего этого процесса и рассказать, какие шаги уже предпринял.

Если повезёт, то кто-нибудь в комментариях подскажет решение. И тогда нашедшие эту статью в поисковиках тоже смогут эту проблему решить.

Проблема и предыстория


Как хорошо знают все пользователи Telegram, в групповых чатах этого мессенджера не существует функции игнора пользователя. Вы можете заблокировать неприятного вам пользователя, но это всего лишь запрещает ему писать вам личные сообщения. В групповом чате — а нынче групповые чаты, кажется, вмещают до 10 тысяч пользователей — вы просто обязаны видеть всё, что скинут прочие участники группы.

В «Чёрном Зеркале» даже была серия про это.


Продолжайте смотреть! Продолжайте смотреть! Продолжайте смотреть!

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

Вот первый запрос этой функции от 2015 года. В этой теме можно найти ссылки на последующие темы, люди постоянно просили эту функцию в последующие годы, но каждое обращение оказывалось закрытым с довольно глупым ответом «Это не проблема клиента, а проблема API».

Даже не программисту понятно, что данные показывают пользователю с помощью клиента. И даже если API передаёт вам все сообщения с сервера — нет никакой проблемы спрятать часть из них на стороне клиента.

Но вот уже пять лет, каждый год, а иногда по нескольку раз в год, люди добираются до Гитхаба с просьбой ввести игнор в группах — и каждый раз получают отказ и закрытие темы. Больше полугода назад, видимо, утомившись от постоянных требований, разработчики вывесили ни к чему не обязывающую отговорку: мол, да-да, мы всё сделаем, когда-нибудь в далёком будущем.


Я тебя услышал! Я просто это озвучил, пока не готов тебе ответить, скажем так, я понял твою позицию. Давай, на созвоне, не теряйся!

Польза от этой отговорки ровно одна — теперь на все жалобы администраторы tdesktop на Гитхабе отсылают к этому сообщению. Дескать — вот смотрите, они всё знают, они всё сделают, извините, ветка закрыта. За полгода в телеграме произошло множество трудоёмких изменений, например в чаты добавили анимированные стикеры и КрАсИвЕнЬкИе шевелящиеся формы для опросов. Но никакого игнора даже близко не появилось. Думаю, не появится и через год.

При этом ситуация выглядит даже немного зловеще — есть ощущение, что какими-то специальными инструкциями для официальных форков вводить эту функцию запрещается. У Телеграма есть не меньше десятка широко известных форков (вроде Unigram) и под сотню малоизвестных, но ни в одном из них эта функция не реализована. Я писал в поддержку самых популярных форков, но не получил никакого ответа.

При этом управлять со стороны клиента сообщениями заблокированных пользователей можно. Это подтверждается существованием Telegreat. Это форк Телеграма, в котором была заявлена опция игнора заблокированных пользователей.

Увы, этот игнор ненастоящий. Всё, что делает опция «Игнорировать заблокированных пользователей» — раскрашивает текст сообщений заблокированных пользователей в серый цвет. Картинки, видео, музыка или стикеры от заблокированных пользователей видны по-прежнему; кроме того серый цвет на тёмных темах отлично читается.

В чат к разработчику, где я сидел какое-то время, регулярно приходят люди и просят сделать «настоящий игнор». Раньше он просто отказывался; в последние же полгода и вовсе забросил свой проект, потому что у него «нет времени». Что ж, это обычное дело для бесплатных открытых проектов.

Я упоминаю об этом проекте по двум причинам.

  • Во-первых, он рекламируется именно как «клиент с функцией игнора» и его советуют люди, которые читают только описание, не зная, как на самом деле там этот «игнор» реализован. Как было сказано выше, функция эта не работает.
  • Во-вторых, он является живым подтверждением того, что в клиенте Телеграма можно определить сообщение заблокированного пользователя и изменить его. То есть на самом деле игнор на стороне клиента возможен.

Однако никто этого не делает.

Лучше всего передаёт мои чувства комментарий в похожей теме на Реддите четырёхлетней давности:
I'm shocked you can't do it.

Seriously?

If I just don't want to read anything some user writes I should be able to do so… it's like, one of the very basic features of every communication protocols EVER!

You could do it in the 90s with emails and newsgroups!

Причём всюду, где идёт речь о причинах отсутствия игнора, повторяются одни и те же вещи:
— Вы будете видеть не все сообщения и из-за этого не будете понимать что происходит в чате.
— Вы будете дублировать сообщения.
— Нарушится функциональность системы «ответов»

И, опять же, я использую цитату, просто чтобы показать вам — это не мои личные мысли, это наша общая позиция, людей, которые хотят ввести эту функцию:
I can assure you 100.00% of people asking for this feature, are fully aware of the side effects it has, and could not care less about those.

Yes just not show it at all. Yes including if it's a reply. Yes if the admin needs to see everything to moderate, then they don't have the luxury of using ignore. And most certainly we can compare TG groups to IRC, because they are both a mechanism for accomplishing the same thing, namely multi-user communication.
Вот просто пример из жизни, касающийся дублирования — я сижу в нескольких телеграм-чатах и в течение дня обязательно несколько пользователей бросают в чат одну и ту же новость / смешной скриншот / свежий популярный мем. Несмотря на то, что до них это уже было. Несмотря на то, что никакого игнора не существует — они всё равно просто не читают историю чата и дублируют сообщения. Про то, как люди не умеют пользоваться поиском на форумах, и многократно повторяют одинаковые вопросы — я даже не буду упоминать, наверняка большинство читателей с этим сталкивались. Опять же, безо всякого игнора эта проблема уже существует. И игнор её не усугубит.

Так почему же на самом деле эта функция не существует?

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

Версия маркетинговая немного проще.

Когда в 1995 году Халед Мардам-Бей создал mIRC — он написал программу для людей. В смысле, для самостоятельных людей, которые знают, что они хотят, выбирают то, что считают правильным, и несут ответственность за последствия своих действий. С одной стороны это вызывало проблемы, например, со скриптовыми уязвимостями — запускаешь строку с командами и всё, ты взломан. С другой стороны — это был мессенджер, которым пользовались взрослые и ответственные люди. И они знали, как существовать в чате с игнором, где часть сообщений просто не видна.

Когда в 2013 году Паша Дуров сделал Telegram — он сделал программу «для домохозяек». Для тупого массового пользователя, которому нужен добрый опекун. Отсюда вся эта регистрация по номеру телефона, рассылка сообщений всем по телефонной книге, флажки и запреты со всех сторон. Ведь они «нажмут что-нибудь и всё исчезнет!». А потом будут писать жалобы в поддержку! Отсюда и такие глупые оправдания — «ведь если вы включите игнор, то вы не увидите сообщений других людей». Когда делаешь массовый продукт — ты вынужден ограничивать любую функциональность. Продукт для дурака должен иметь защиту от дурака.

Тут появляется коронный аргумент, который всегда приводят в конце: «не нравится — так не пользуйся». Я не буду объяснять, что с ним не так, это этический вопрос формата «почему нельзя есть людей» — кто его задаёт, тому бессмысленно объяснять.

Но в случае с Телеграмом этот аргумент немножко видоизменяют: «не нравится — так сделай как тебе нравится, это же open source».

Попытка решения


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

В этом смысле меня бы не беспокоили ни возможные «тормоза» от скрытия сообщений, ни «проблема потери контекста в беседе», ни проблема «как же админить чат если не видишь части сообщений». Это всё меня не интересовало.

Мне казалось очевидным, что достаточно добавить строку вида:
if message.author.isBlocked == true then message.value= 'The message from blocked user'
Извините за псевдокод, но мне кажется, что идею он передаёт верно. Я просто хотел отловить любое сообщение на самом раннем этапе, когда оно только приходит с сервера, и заменять его на строку о блокировке. Таким образом решалась бы проблема и с картинками / стикерами / гифками — они бы тоже заменялись на такое сообщение и их не было бы видно. И даже если бы кто-то отвечал на такое сообщение или цитировал его — всё равно оно бы заменялось.

Но с чего начать?

Поспрашивав у знакомых программистов, я выяснил, что есть два пути. Первый — это использовать родной клиент Телеграм, и второй — использовать сторонний клиент, который написан с помощью более новой библиотеки TDLib. Я отправился на упомянутый в начале статьи сервис вопросов и задал там вопрос. Как видите, очереди из отвечающих не выстроилось. Единственная ниточка вела к мессенджеру Котатограм.

Я пообщался с создателем этого мессенджера, и он дал мне два полезных совета.
Во-первых, он сказал, что сообщения в нативном клиенте выводятся обычным способом, а не генерируются (я, впрочем, и так это подозревал на примере Telegreat) — поэтому можно в качестве исходника использовать родной клиент.
Во-вторых, он рассказал, где искать сам вывод сообщений — в блоке history_message.

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

Как я действовал дальше? Инструкция по сборке существует на Гитхабе, но она не отражает всех этапов работы. Я их тут опишу, на случай, если в статью придёт кто-то ещё, с программированием не особенно знакомый, но желающий сделать для себя такой же клиент с игнором:

  1. Я решил всё делать на виртуальной машине. Для этого я установил себе Oracle VM Virtual Box и скачал с официального сайта Microsoft их официальную виртуальную машину с предустановленными Windows 10 и Visual Studio 2019
  2. На втором этапе нужно расширить предустановленный Visual Studio. Как оказалось, по умолчанию в него не входит C++, поэтому нужно запустить Visual Studio Installer из главного меню. Нажать там кнопочку Modify и выбрать секцию с C++, он её докачает и установит. Ну и на всякий случай я ещё скачал и установил QT отсюда.
  3. Кроме того нужно установить Git c git-scm.com
  4. Теперь можно начать выполнять инструкцию с Гитхаба. Но будьте внимательны!

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

Первая остановка происходит после строки:

gyp --no-circular-check breakpad_client.gyp --format=ninja

Вторая — после четвёртой строки с конца, длинной инструкции конфигурирования QT:

configure -prefix "%LibrariesPath%\Qt-5.12.5" ....... 

Как мне подсказали знакомые, это связано с тем, что в ходе выполнения этих команд запускаются другие bat-файлы, в которых уже прописан прямой выход из процесса. В результате дальше нужно либо просто копировать и вставлять команды, либо разбить инструкцию на 3 bat файла (но в этом случае нужно не забыть, что команды следует продолжать выполнять из той же директории, в которой процесс остановился!).

Получение собственных api_id тоже описано на Гитхабе.

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

Помощь зала (и найденное решение)


И вот здесь наконец-то появляется моя просьба к хабрасообществу. Как читатели уже догадались, я не знаю, что и как менять. Мне кажется, что я нашёл место, где выводится сообщение, но я не уверен, потому что я же не знаю C++. Не говоря уж о том, что (возможно) правильно было бы что-то менять не в history_message, а где-нибудь в history_item.


«Что-то на эльфийском, не могу прочесть»

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

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

UPD: Как я упомянул в начале статьи, в комментариях появился работающий вариант кода. Привожу его и прямо в статье.

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

В файле History_Message.cpp мы находим функцию как раз в том самом месте, что на скриншоте выше:

HistoryMessage::HistoryMessage(
	not_null<History*> history,
	const MTPDmessage &data,
	MTPDmessage_ClientFlags clientFlags)


И внутри заменяем оригинальный текст:

	if (const auto media = data.vmedia()) {
		setMedia(*media);
	}
	setText({
		TextUtilities::Clean(qs(data.vmessage())),
		Api::EntitiesFromMTP(data.ventities().value_or_empty())
	});


На расширенный:


	//UserId from = data.vfrom_id().value_or_empty();
        // Это почему-то перестало работать, поэтому пришлось добавить другую строку
        UserId from = data.vfrom_id() ? peerFromMTP(*data.vfrom_id()) : PeerId(0);
	PeerData* pd = from ? history->owner().user(from) : history->peer;
	if (pd->isUser() && pd->asUser()->isBlocked()) {
		setText({
			TextUtilities::Clean(qs("The message from blocked user")),
			Api::EntitiesFromMTP(data.ventities().value_or_empty())
			});
	}
	else {
		// оригинальные строки 442-448:
		if (const auto media = data.vmedia()) {
			setMedia(*media);
		}
		setText({
			TextUtilities::Clean(qs(data.vmessage())),
			Api::EntitiesFromMTP(data.ventities().value_or_empty())
			});
	}


Если использовать только этот код — старые сообщения заблокированных пользователей будут видны, но новые уже нет.

Поэтому вторая часть решения, упомянутая в этом комментарии

В файл history_view_message.cpp добавляется следующий код:

PeerData* pd = item->from();
if (pd->isUser() && pd->asUser()->isBlocked()) return;


UPD: в новой версии компилятор ругается на пустой return, поэтому я добавил вывод QSize(0,0) В другом случае можно наоборот убрать его.

PeerData* pd = item->from();
if (pd->isUser() && pd->asUser()->isBlocked()) return QSize(0,0);


Сразу после объявления переменной item в функциях
QSize Message::performCountOptimalSize()
и
void Message::draw(
Painter &p,
QRect clip,
TextSelection selection,
crl::time ms)

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

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

Для желающих добавил на Дропбокс скомпилированное приложение.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Считаете ли вы полезной фукнцию игнора конкретных пользователей в групповых чатах?
84.16% Да 170
15.84% Нет 32
Проголосовали 202 пользователя. Воздержался 31 пользователь.
Теги:
Хабы:
Всего голосов 23: ↑15 и ↓8 +7
Просмотры 34K
Комментарии Комментарии 38