OSINT в Telegram



    Протокол Telegram известен своей доступностью и открытостью. У него есть множество публичных реализаций: tdlib/td, rubenlagus/TelegramApi, vysheng/tg, LonamiWebs/Telethon и другие. Однако, даже имея в распоряжении столь богатый инструментарий и объемную документацию (https://core.telegram.org/api), решить прикладную задачу, собрав из многообразия методов API нужную цепочку – не так-то просто. Сможет, например, “неподготовленный ум“ сходу догадаться, как решить прикладную задачу а-ля “поиск по номеру в Telegram“? — Скорее всего, придется потратить какое-то время на изучение API.


    Официальный клиент Telegram содержит в себе массу API-цепочек, реализующих определенные пользовательские сценарии. Если подумать, взаимодействие на основе сценариев — наиболее удобный и предпочтительный способ, поэтому мы решили пойти по пути упрощения взаимодействия с Telegram на основе реализации библиотеки сценариев. Так как наша деятельность тесно связана с направлением OSINT, то в первую очередь мы решили реализовать ряд OSINT-сценариев, применимых в сети Telegram, о которых и хотим рассказать в этой статье.


    Для решения задач OSINT мы еще давно начали работу над собственным клиентом для сети Telegram, который в последствии трансформировался в расширяемую библиотеку сценариев — telegram-osint-lib.


    Почему пришлось делать собственный клиент?


    Мы разрабатываем программные системы для сбора данных из открытых источников. Об одном из наших сервисов — Nuga — мы уже как-то рассказывали ранее.


    Здесь и далее под “клиентом” подразумевается не графическое приложение, а “бот” (не путать с внутренним функционалом Telegram), управляемый из консоли, который решает определенную прикладную задачу в рамках сети Telegram с помощью предоставляемого API.


    На момент первичной реализации клиента (~конец 2018 года) сторонние библиотеки-клиенты Telegram, которые мы рассматривали, обновлялись нерегулярно и не соответствовали требованиям из документации по протоколу, которая обновлялась с большой задержкой (полгода и более), как и официальные клиенты.


    Часто в новоиспеченных версиях официальных клиентов можно было наблюдать кардинальные отличия от официальной документации, что сеяло зерна конспирологических сомнений. Например, API Layer 105 (выпущенный с огромным скачком от последней версии 23) наконец-то рассказал нам об опциональных полях и всех недостающих типах данных, устранив тем самым все подозрения на гипотетические закладки. Сейчас с этим стало лучше, документация обновлена, хотя некоторые детали все равно приходится определять экспериментально.


    Сценарийная направленность


    Занимаясь достаточно долгое время развитием собственного клиента, клиент оброс множеством оберток над группами API-вызовов. Стало понятно, что как простой набор API вызовов к Telegram он нас не особо интересует — в пределах сети Telegram гораздо интереснее выполнять комплексные операции, задействующие сразу множество API-вызовов. Таким образом был осуществлен переход от API-направленности к сценарийной направленности клиента.


    Сценарий в telegram-osint-lib – это “черный ящик”, реализованный в виде последовательности API-вызовов, позволяющей достичь определенной конечной цели (output) на основе входных параметров (input). Аргументы представляют из себя понятные сущности окружающего мира (например, ключевое слово для поиска). В результате использования аргументов внутри черного ящика на выходе получается запрошенная информация (например, сообщения с указанным ключевым словом). Вся рутина по взаимодействию с API Telegram при этом инкапсулирована в реализации сценария. Сценарии могут сочетаться друг с другом, формируя более комплексные сценарии.


    Концепция сценария была разработана в процессе решения задач внутри компании, однако, схожие понятия встречаются много где, например, в литературе по анализу требований — Scenario-based modeling and its applications — т.е идея “scenario-based“ сама по себе, конечно же, не нова.


    Детали реализации


    Библиотека telegram-osint-lib реализована по асинхронной модели и рассчитана на поддержку нескольких одновременных соединений (пример). Изначально при реализации мы следовали следующим принципам:


    • fail fast: при работе с проприетарным протоколом (пусть и имеющим более-менее открытую документацию) необходимо реагировать на изменения оперативно

    • conformity: библиотечная реализация максимально соответствует существующим клиентам и учитывает ограничения, заложенные в протокол

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


    На этапе дизайна архитектуры библиотеки, были выделены следующие уровни абстракции (от низкого к высокому):


    1. Уровень Telegram API (TL nodes);

    2. Уровень клиента, содержащего набор нужных методов API для выполнения класса операций

    3. Уровень сценария, комбинируемого с соседями по уровню

    4. Уровень интерфейса пользователя (скрипт, вызывающий сценарии)


    Так как клиент реализован по асинхронной модели, каждый вызов возвращает результат не напрямую, а через callback. Такой подход к реализации позволяет обрабатывать асинхронные ответы (что предусмотрено протоколом Telegram) и держать в одном потоке множество соединений.


    Поддержка мультиверсионности схемы данных


    Как известно, Telegram протокол описан в виде схемы на языке TL. Однако, на той же странице, мы можем получить схему и в формате JSON (которая на практике оказывается более применимой). В схеме два основных блока: constructors и methods. Первый описывает структуры данных, которые принимает на вход или возвращает Telegram сервер, а второй — описание методов: спецификацию типов входных параметров и тип ответа.


    Несмотря на то, что сейчас протокол на сайте регулярно обновляется (в процессе написания статьи вышла новая версия TL-Schema 108, а за ней и 109), наблюдать за дельтой изменений между версиями по прежнему необходимо, для чего используется небольшой сниппет, принимающий на вход два json файла и выдающий на выходе конструкторы/методы, которые есть во втором файле, но нет в первом. На текущий момент в официальном API описано более 1100 дескрипторов (конструкторов/методов). Пользуясь такой json схемой нетрудно составить или декодировать любое сообщение.


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


    OSINT на примере некоторых сценариев


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


    Пойдем от простого к сложному и попробуем разобрать некоторые из существующих сценариев на примере реальных кейсов. Для более удобного взаимодействия со сценариями библиотека telegram-osint-lib была завернута в Docker:


    docker build -t telegram-osint-lib . 
    docker run -d -t --name tg-osint-lib telegram-osint-lib

    Преамбула: Генерируем бота-клиента


    Перед тем как запустить содержимое Docker-контейнера и получить о пользователе первые данные, нам понадобится “зайти” в сеть Telegram. Для входа в сеть библиотека использует ботов (о которых уже было упомянуто выше), от имени которых производятся все действия.


    Для генерации бота нам потребуется сценарий регистрации, реализующий связку auth.sendCodeauth.signInauth.signUp:


    docker exec -i tg-osint-lib php examples/registration.php
    Number: 790612***31
    SMS code: 123123

    На выходе получаем бота, готового к задачам OpenSource Intelligence:


    AuthKey: 790612***31:aabbccdd...

    Этот ключ(AuthKey) будет использоваться во всех дальнейших примерах следующим образом:


    docker exec --env BOT=... -i tg-osint-lib php ...

    Ниже в примерах, для краткости, бот будет указываться так: --env BOT=...


    Собираем сведения о владельце номера


    Начать мы решили с простого сценария — поиска пользователя Telegram по номеру телефона. Данный сценарий довольно очевидный и доступный для рядовых пользователей, однако его массовое применение несколько затруднительно “из коробки“.


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


    1. Ник (который многие пользователей могут использовать и в других местах)

    2. Фото

    3. Общие чаты

    4. Имя/Фамилия

    5. “О себе”

    6. Последнее время пребывания в Telegram

    7. Предпочтительный язык


    Этой информации уже достаточно для составления начального профиля владельца номера. Мы будем использовать сценарий из telegram-osint-lib, который будет получать на входе список телефонных номеров, пробегать по ним и запрашивать для каждого номера соответствующий аккаунт на сервере Telegram. Если аккаунт есть, мы получаем его данные. Если же аккаунт не найден, сохраняем номер телефона в отдельный список, которые будет обрабатываться в дальнейшем вручную.


    Запускаем сценарий на группе номеров:


    docker exec --env BOT=... -i tg-osint-lib php examples/parseNumbers.php 7985****294,7985****977,7986****777,7986****252,7988****417,7999****169,7999****869,7999****053,7999****364,7999****916,7999****475,7999****959,7985****025,7985****343,7989****207,7916****668,7926****802 > numbersInfo.txt

    Всю основную работу в недрах библиотеки выполняет метод InfoClient::getInfoByPhone(), использующий связку API вызовов import_contacts->get_user_full->delete_contacts->get_user_full. А обработка результатов происходит в функции обратного вызова, которой в параметрах передаётся модель с данными пользователя. Если у пользователя установлено фото профиля, изображение будет загружено в папку со скриптом примера, а в поле Photo будет указано имя файла.




    Следим за присутствием пользователя


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


    Составив несколько таких карт по разным пользователям, мы сможем попробовать установить корреляцию между их заходами с целью определить возможность активного общения между ними.


    Запускаем уже другой сценарий на группе номеров и наблюдаем за ними некоторое время:


    docker exec --env BOT=... -i tg-osint-lib php examples/monitorNumbers.php 97155******9,...,798*****777 presence_map.txt

    На выходе сценария мы получим ASCII-карту (где “+“ — состояние online в секунду времени) взаимоприсутствия интересующих пользователей в сети. Из карты видно, что вероятность активного общения между пользователями 2,4,5,9 более вероятна, чем между всеми остальными:




    Откуда HackerNews черпает свои новости?


    В Telegram популярны каналы новостного типа — рассылающие анонсы новостей, статей и прочих тематических постов. Но многие ли обращают внимание, откуда популярные каналы черпают информацию?


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


    В библиотеке реализовано два похожих метода для работы с сообщениями групп: InfoClient::getChannelLinks() и InfoClient::getChannelMessages(). Работают они практически одинаково, за исключением того, что первый метод фильтрует сообщения и отбирает только те, в которых были размещены ссылки.


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


    docker exec --env BOT=... -i tg-osint-lib php examples/parseChannelLinks.php https://t.me/HNews "2019-12-01 00:00:00"

    Спустя некоторое время сценарий соберет статистику по доменам и на выходе мы увидим примерно следующие результаты:




    Итого, получили источники по убыванию частотности:


    1. habr.com (45%)

    2. xakep.ru (44%)

    3. threatpost.com (11%)

    4. остальное (<1%)


    Теперь тот, кто подписан на HackerNews, Xakep.ru и Habrahabr может задуматься, а не подписан ли он на что-то лишнее?


    Самый большой болтун


    Кроме анализа информационных источников группы, немалый интерес может представлять выборка самых активных участников тематической группы. Не секрет, что 20% участников группы производят 80% активности этой группы. Получив такую информацию, можно определить наиболее приоритетных для группы людей и наладить контакт именно с ними.


    Определять топ самых активных участников проще всего на промежутке времени или по числу последних сообщений (например, по последним 1000 сообщений). Собрать информацию о частоте сообщений участников группы нам поможет метод API messages.getHistory, используемый сценарием с другим целевым предназначением (сбор сообщений в группе), но кастомизируемый внешними инструментами. Запускаем очередной сценарий на одной из популярных групп с пост-фильтрацией средствами командной строки:


    docker exec --env BOT=... -i tg-osint-lib php parseGroupMessages.php https://t.me/vityapelevin -- 1570207168 1580207168 --info \
      head -n 2000 | \
      ggrep -oP 'from [a-zA-Z0-9_]+ at' | \
      sort | uniq -c | sort -r -n -k1 | awk '{print $1 " " $3 }' | \
      head -n10

    Что на выходе позволяет получить самых больших болтунов группы:


    355 289336351
    237 710806664
    226 Yuliya04
    216 735896305
    187 Retrovertigodor
    187 971662085
    175 Mahmud_Abas
    141 VwVwVoid
    94 nikol_pelevina
    85 kotenok_gaff

    Общие интересы


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


    Ядром сценария, определяющего “карту интересов” пользователя, будет API-метод get_common_chats, возвращающий список чатов, которые являются общими для бота и пользователя из наших контактов.
    Итоговый алгоритм определения интересов пользователя будет следующим:


    1. Добавить пользователя к себе в контакты

    2. Подписать бота на множество популярных чатов, используя join_channel

    3. Получить информацию об общих с пользователем чатах, с помощью get_common_chats

    4. Сопоставить общие чаты с классами интересов


    Для исполнения этого алгоритма на конкретном номере будет использоваться очередной сценарий, использующий внутри себя другой, уже задействованный ранее, сценарий поиска информации по номеру телефона:


    docker exec --env BOT=... -i tg-osint-lib php examples/commonChats.php 7926****802

    Изучая исходник примененного сценария можно обратить внимание на то, как происходит комбинирование простых сценариев в более сложные:


    public function getCommonChats(?callable $callback = null)
    {
        $client = new UserContactsScenario([$this->phone], function (UserInfoModel $user) use ($callback) {
            $this->infoClient->getCommonChats($user->id, $user->accessHash, 100, 0, function (AnonymousMessage $message) use ($callback) {
                if (!Chats::isIt($message)) return;
                $updates = new Chats($message);
    
                foreach ($updates->getChats() as $chat) {
                    $this->commonChats[] = strtolower($chat->username);
                }
                ...
            });
        });
        $client->startActions(false);
    }
    

    По полученным общим чатам “вычисляются” классы интересов, к которым они относятся, подсчитывается количество общих чатов в каждой из категорий, и на выходе сценария получаем список интересов пользователя, упорядоченный по количеству чатов, а значит можем сделать вывод, какие из интересов наиболее предпочтительны пользователем:




    Извлечение сообщений пользователя из публичных чатов и каналов


    Продолжаем дальше искать новые источники открытой информации в Telegram. Какую информацию о пользователе мы смогли извлечь на данный момент:


    1. Номер → Профиль пользователя

    2. Профиль пользователя → Общие каналы с пользователем


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


       3.  Общие каналы с пользователем → Сообщения пользователя в канале


    Для этого нам поможет сценарий извлечения сообщений группы. В его основе лежит API-метод messages.getHistory. Для примера, выгрузим последние сообщения уже известного нам пользователя a_averyanova_m:


    docker exec --env BOT=... -i tg-osint-lib php parseGroupMessages.php https://t.me/phuketrusa a_averyanova_m --info | head -n10 
    30.01.2020 13:26:17 | parseGroupMessages.php: starting group resolver for username: phuketrusa
    30.01.2020 13:26:18 | TelegramOSINT\Scenario\GroupMessagesScenario: resolved user a_averyanova_m to 272425703
    30.01.2020 13:26:19 | TelegramOSINT\Scenario\GroupMessagesScenario: got message 'Учиться мопед водить ?))))  \\ Ну приеду , проверю информацию )' from a_averyanova_m at 2020-01-30 12:25:48
    30.01.2020 13:26:19 | TelegramOSINT\Scenario\GroupMessagesScenario: loading more messages, starting with 26451
    30.01.2020 13:26:20 | TelegramOSINT\Scenario\GroupMessagesScenario: loading more messages, starting with 26332
    30.01.2020 13:26:21 | TelegramOSINT\Scenario\GroupMessagesScenario: loading more messages, starting with 26219
    30.01.2020 13:26:22 | TelegramOSINT\Scenario\GroupMessagesScenario: got message 'Я так поняла , тут вариантов не много, или ты катаешься без прав (наши тут с кат.авто не прокатят) и лишаешься страховки (если вдруг что)+платить штраф 500-1000бат, или идёшь и получаешь права в Тае, по времени это пару дней (когда как) и по деньгам явно дешевле, чем на лапу давать) мы по прилету , будем делать их на месте' from a_averyanova_m at 2020-01-29 14:38:40
    30.01.2020 13:26:22 | TelegramOSINT\Scenario\GroupMessagesScenario: loading more messages, starting with 26099
    30.01.2020 13:26:22 | TelegramOSINT\Scenario\GroupMessagesScenario: got message 'Спасибо' from a_averyanova_m at 2020-01-29 10:55:06
    30.01.2020 13:26:22 | TelegramOSINT\Scenario\GroupMessagesScenario: got message 'Добрый день , еду на 2 месяца в Тай, кто какую страховку делал , при условии того, что буду ездить на байке без открытой категории. \\ И ещё , слышала, что можно открыть категорию ( получить их права) на месте , кто этим занимался, платить каждый раз по 1000бат тоже не самый классный вариант' from a_averyanova_m at 2020-01-29 10:09:10

    Помимо возможностей пост-фильтрации, есть возможность выборки по диапазону дат. Пример фильтра (~с 04.10.2019 до 27.01.2020):


    docker exec --env BOT=... -i tg-osint-lib php parseGroupMessages.php https://t.me/vityapelevin -- 1570207168 1580207168 --info | grep сновидения
    
    28.01.2020 10:45:22 | TelegramOSINT\Scenario\GroupMessagesScenario: got message 'Да, там про всякие сверхвозможности, в искусстве сновидения вроде' from 735896305 at 2020-01-27 21:02:01

    На этом этапе проведем небольшую ретроспективу собранных данных по одному из исходных номеров:


    1. Первым шагом в разделе “Собираем сведения о владельце номера“ мы сопоставили профиль пользователя a_averyanova_m с реальным человеком, владеющим номером 7926****802

    2. Далее в разделе “Общие интересы“ определили возможные интересы человека на основе анализа общих групп (путешествия)

    3. И, наконец, в этом разделе извлекли публичную переписку в одной из обнаруженных общих групп с ботом


    Вместе вся эта информация позволяет сформировать достаточно полное представление об исходном владельце номера и даже о его планах на будущее.


    Гео-разведка


    Использование гео-позиции открывает интересные возможности для сбора информации, однако сама по себе гео-разведка является одним из тех направлений OSINT, разрушительный эффект от которых очевиден разработчикам по умолчанию и который они всяческими методами пытаются снижать. Разработчики Telegram здесь не стали исключением и так же постарались минимизировать возможные утечки, связанные с гео-локацией. В итоге весь потенциал направления сузился до нескольких API-методов: geochats.getLocated и contacts.getLocated, но и их в некоторых случаях может хватить для извлечения дополнительной информации о пользователе — например, где он чаще всего появляется в городе?


    Завершающий сценарий, который мы рассмотрим в этой статье, позволяет определять потенциальные “места обитания” пользователя на определенной локации на основе поля гео-точек. В основе сценария будет лежать API-метод contacts.getLocated, который возвращает гео-чаты и контакты, находящиеся в определенном радиусе (эмпирическая оценка ~1 километр) от заданной гео-точки. Метод возвращает структуру Updates, благодаря которой мы можем реализовать мониторинг изменений в отслеживаемой группе пользователей.


    Запускаем сценарий на поле из двух точек для конкретного пользователя:





    docker exec --env BOT=... -i tg-osint-lib php geoSearch.php 55.753930,37.615714,55.756390,37.661931 b00k1ng 30 --info
    ...
    29.01.2020 16:00:06 | TelegramOSINT\Scenario\GeoSearchScenario: found group 'Эдвард юил' near (55.753930, 37.615714)
    29.01.2020 16:00:06 | TelegramOSINT\Scenario\GroupMembersScenario: searching chat 1404414249 participants for b00k1ng
    29.01.2020 16:00:06 | TelegramOSINT\Scenario\GeoSearchScenario: found group 'Френдовская' near (55.753930, 37.615714)
    29.01.2020 16:00:06 | TelegramOSINT\Scenario\GroupMembersScenario: searching chat 1404180655 participants for b00k1ng
    29.01.2020 16:00:06 | TelegramOSINT\Scenario\GroupMembersScenario: chat 1211826903 contains user 883904218 with username b00k1ng

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


    Внутри сценарий, также как и некоторые другие, из рассмотренных выше, организован по принципу композируемости: сам сценарий GeoSearchScenario, по сути, выполняет один запрос, а дальнейшая работа по проверке участников делегируется сценарию GroupMembersScenario:


    $groupHandler = function (GeoChannelModel $model) use (&$generator, &$finders, $username) {
        $membersFinder = new GroupMembersScenario(
            $model->getGroupId(),
            null,
            $generator,
            100,
            $username
        );
    
        $membersFinder->startActions(false);
        $finders[] = $membersFinder;
    };
    
    $search = new GeoSearchScenario($points, $groupHandler, $generator, $limit);
    $search->startActions();

    Заключение


    В этой статье на примере библиотеки сценариев telegram-osint-lib мы разобрали ряд сценариев OSINT-направленности в сети Telegram. Как можно было заметить, разобранные “утечки“ OSINT являются издержками исходных бизнес-требований к функционалу, а потому не могут быть устранены легко. Наверное, это одна из тех причин, по которой направление разведки по открытым источникам будет существовать постоянно в том или ином виде — это что-то вроде шума в электронных цепях, к которому все уже давно привыкли: ослабить эффект возможно, но устранить полностью экономически невыгодно.


    Мы шли от более простых сценариев к более сложным, встраивая и комбинируя сценарии друг с другом. Структура библиотеки позволяет легко комбинировать как методы, так и сценарии между собой в нужную цепочку вызовов, получая на выходе необходимый сценарий. А асинхронный механизм работы уменьшает зависимость различных методов друг от друга и позволяет реализовать несколько одновременных соединений, позволяя масштабировать сценарии.


    Мы рассмотрели ряд примеров, работающих автономно, без какого-либо “состояния” (State), однако добавление к сценариям состояния (до чего у нас пока не дошли руки), позволит вытворять еще более интересные штуки в сфере OSINT. Например обнаруженная в августе 2018 года “уязвимость“, позволяющая определять номер телефона по никнейму пользователя была не уязвимостью, а ничем иным, как OSINT-сценарием с наличием состояния: кто-то начал массово искать пользователей по номеру телефона, собирая базу данных (State) вида “пользователь->номер“, исходя из структуры которой определение телефона по никнейму являлось тривиальной операцией.


    В завершение хотелось бы отметить, что Telegram API непрерывно развивается, открывая с каждым API Layer все новые возможности не только для OSINT-исследователя, поэтому любой желающий может присоединиться к разработке библиотеки сценариев и пополнять её новыми сценариями, скрывающими хитросплетения API-вызовов и решающими любые прикладные задачи в сети Telegram.

    Postuf
    Компания

    Похожие публикации

    Комментарии 3

      +1
      По поводу @HNews, там раньше было множество источников типа securitylab, cnews и еще парочка менее популярных ресурсов, но из-за слишком одинаковых новостей и (или) низкого качества их всех повыпиливали :)
        +1
        А вот Фейсбук в отличие от Телеграмма после скандала с Cambridge Analytics запретил искать пользователей по номеру их телефона…
          0
          Спасибо за статью.
          Поясните, пожалуйста, самую интересную часть, связанную с геолокацией.
          определять потенциальные “места обитания” пользователя на определенной локации на основе поля гео-точек. В основе сценария будет лежать API-метод contacts.getLocated, который возвращает гео-чаты и контакты, находящиеся в определенном радиусе (эмпирическая оценка ~1 километр) от заданной гео-точки.


          Пользователи шарят в телеграм свою геопозицию? Если да, то в каких случаях?
          Геочаты привязаны к каким-либо координатам? Можете привести пример?

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

          Самое читаемое