Как найти что-то из своего во ВКонтакте и немного grep-магии
Когда я собирал свои смешнявки для галереи, взбрело мне в голову посмотреть, что там есть в ВК — вдруг что-то смешное я сделал и отослал через него? Если смотреть все вручную, то так можно и кукухой поехать, поэтому я решил поехать кукухой автоматизированно.
Disclaimer: Длительное чтение историй своих переписок 10-летней давности может привести к необратимым повреждениям психики и к потере кучи времени. Повторять описанное стоит с максимальными мерами предосторожности.
Кому не интересно нудное описание, что есть и чего нет в архиве — листайте до заголовка Ищем информацию
Скачиваем архив с данными
С этим все почти просто. Заходим по ссылке, запрашиваем архив. Есть нюанс: для скачивания понадобится привязка к мобильному устройству и/или к мобильному телефону, так что если вы до сих пор злостный анон, ВК захочет вас послать. Не забудьте в настройках поставить все галочки — по умолчанию они не все установлены.
Мой архив был готов через пару часов после запроса.
Смотрим, что внутри
Первое, что бросается в глаза — очень маленький размер архива, всего 13 мегабайт. Я в ВК 14 лет и в моей жизни был период, когда “вся жизнь там”. Очень сомнительно звучит, что “это самый полный архив”.
Внутри лежит поганый HTML. С одной стороны — удобно для обывателя, но с другой — я ожидал выгрузку в машиночитаемом формате. Те же операторы связи закинут список ваших звонков в CSV. Или телега, которая дает выбор — либо красивую HTML-ку, либо JSON (правда, для индивидуальных чатов — только HTML). Ну ладно, это не самое страшное.
Вишенкой к формату идет кодировка — конечно же, это всеми любимая CP1251. Ну, хоть не KOI8-R, и на том спасибо.
Малый размер объясняется тем, что внутри только текстовая информация. Картинок нет, только ссылки на них. При этом телега честно скачает, если ее попрость, а тут такой опции нет. Получается, что как бэкап этот архив использовать не получится. Истории тоже особо нет, только та информация, которую и без этого архива можно посмотреть на сайте. С некоторыми оговорками — см. далее.
Что в итоге есть?
Информация о профиле
Которую и так видно на главной странице. Всякие вузы, интересы, даты рождения, отношения к алкоголю и т.п.
Список друзей
Для каждого друга покажут: текущее имя или DELETED, ссылку на страницу и дату добавления в друзья (текстом). При этом примерно до июня 2009 года текст даты будет “очень давно”.
Подарки
Картинка, ссылка на профиль подарившего, дата.
Подписки
Тупо список пабликов с ссылкой на них. Иногда с пометкой “Администратор” или “Владелец”.
Черный список
В формате списка друзей, только вместо “очень давно” — “Дата добавления неизвестна”. И DELETED может быть не только ссылкой, но и просто текстом. Спасибо, ВК, теперь я знаю, что у меня в черном списке есть
DELETED
Дата добавления неизвестна
Сообщения
Разбиты по диалогам. Только текстовая информация. Даже картинки указаны голой ссылой, а не в <img>
теге. Даты, разумеется, только текстом. Часть контента просто заменяется текстом, например, сообщение может содержать только “пересланное сообщение” без ссылки — и решай ребус, что же ты там такого переслал. Особенно если это чат с DELETED. Похожая ситуация с “1 прикреплённое сообщение” и “Запись на стене”.
Вы, 17 мая 2018 в 16:39:27
2 прикреплённых сообщения
Возникают также сомнения, что история сообщений действительно вся: скорее всего только то, что и так через интерфейс можно посмотреть. Но проверять уже желания нет. “Этот вопрос оставлен как упражение читателю”, как говорится.
Стена
Примерно в том же формате, что и сообщения, но еще есть ссылка на оригинал. Репосты раскрываются, а не показываются дурацкосй строкой вроде “Запись на стене”. Однако “Аудиозапись” и “Граффити” — без ссылки. Посты от других людей на стене тоже есть. И даже есть ссылка на комментарии к записи, которые в этом же архиве.
Лайки
5 категорий: Фото, Видео, Стена, Заметки, Товары. В каждой — список ссылок. Тут хотя бы логично, хотя насколько я знаю, даты лайков ВК тоже знает.
Фотографии
Альбомы из профиля и вдобавок фотографии со страницы, со стены, сохраненные и из “фото со мной”. Для альбомов есть даты сохранения и обновления. Фотки в таком же формате, как и стена, со ссылкой на комменты. Но зато сама фотка вставлена <img>
тегом.
Видео
Загруженные и добавленные. Как фотки (есть превьюшка), есть еще длительность и количество просмотров. Но прямой ссылки на скачивание нет.
Музыка
Список композиций с именем и длительность, без ссылок.
Документы
Список с датой загрузки, названием файла и ссылкой на него в документах. Никаких превью и прямых ссылок на скачивание.
Товары
К сожалению, я не настолько успешен, чтобы иметь свой бизнес в ВК, поэтому у меня тут пусто.
Истории
Aka “сториз”. Не умею таких делать, и тут у меня тоже пусто.
Приложения
Список текущих приложений с названием, ссылкой, правами доступа в формате “кучей через запятую” и датой в формате “Дата последнего запуска неизвестна”.
Закладки
Нет, не те. Страницы, записи, видео, товары, статьи, ссылки, подкасты. Везде “Данных нет”.
История изменений
Для имени, почты и телефонов. У меня там пусто, но вот немного печалит, что мне суют всяких DELETED, когда ВК все это про них знает. Привязки к телефонам по классике: “Привязан номер телефона 1234547”. Ишь чего, машиночитаемый формат захотел!
Платежи
Все, чего у меня нет: карты, история платежей, голоса, голоса внутри приложений, подписки, история переводов.
Реклама
Это уже поинтереснее: всего этого не посмотришь на сайте. Ретаргетинг:
Вы попали в 3271 группу ретаргетинга
Кабинет пользователя
Что бы это ни значило.
Интересы
Бизнес
Пользовательский интерес
ИТ (компьютеры и софт)
Пользовательский интерес
Образование
Пользовательский интерес
iPad
Системный сегмент
Firefox
Системный сегмент
Windows 10
Системный сегмент
Android (бюджетные)
Системный сегмент
Друзья именинников — всех, 3 дня
Системный сегмент
Друзья именинников — всех, 7 днeй
Системный сегмент
Друзья именинников — женщин, 3 дня
Сторонний сегмент
Друзья именинников — женщин, 7 дней
Сторонний сегмент
Образование
Пользовательский интерес
Наука и техника
Пользовательский интерес
Общество
Пользовательский интерес
Интересы к “Windows 10” и “Android (бюджетные)” даже натолкнули на мысль, что меня ломанули. Но в целом нормальный список, понимаешь, почему тебе суют рекламу IT-конференций.
В “Кабинетах” и “Часто посещаемых местах” закономерно пусто, но про последние не очень ясно, что они делают в рекламе.
Прочее
“Обращения в Поддержку”: только в новую, в старую — нет. Блокировки: когда блокировали вход в страницу и почему. “Сеансы авторизации”: только недавние, с именем, User-Agent и IP. “Импортированные контакты”: из почты, телефона, Twitter, FB, G+ и ОК — там у меня пусто. “Верификация”: для тех, у кого галочка рядом с именем.
Комментарии
Одной большой кучей. Текст, ссылка на оригинал и дата. Комментарии могут быть:
- к публикациям на вашей стене.
- к публикациям на чужих стенах, в т.ч. в пабликах. Причем даже если владелец стены их скрыл.
- комментариями от имени сообщества, если писал от его имени.
- ВНЕЗАПНО публикациями в пабликах — когда пишешь на стену паблика от своего имени. Почему ВК считает это комментарием — непонятно.
Приятный бонус заключается в том, что можно прочитать комментарии к постам, доступа к которым уже нет.
Немного удручает, что нет никакой информации о том, что комментируешь, только ссылка. Особенно прикольно, когда есть id стены, профиль удален, из текста непонятно, кому ты это писал — и решай ребус, что это за чел. Иногда помогает гугление (причем гугл может не помнить, а яндекс выдаст результат, но, опять же, не для всех).
Чего в архиве точно нет
Если вы надеялись, что в архиве будет суперполное досье на вас, то у меня для вас печальные новости: ВК явно хранит данных о вас гораздо больше, чем присылает в архиве. Я приведу список того, чего точно нет, но я наверняка о чем-то забыл. А чего-то вообще не знаю.
Обсуждений в группах
Имеются в виду те, которые отдельно от стены.
Постов от имени сообщества
Если вы админ сообщества и на стену публиковали записи от имени сообщества, то их не будет. Не важно, подписывались ли вы своим именем или нет. Забавно с учетом того, что комментарии от имени сообщества в архиве есть.
Постов на чужих стенах
С учетом того, что записи на стенах пабликов есть — очень странно.
Некоторых комментариев
Например, к фотографиям в чужих альбомах. Комментариев, отправленных через виджет ВК на сторонних сайтах. Возможно, еще каких-то.
Обращений в старую поддержку и баг-репортов
Отчетливо помню, как парочку багов заводил. Баг-репорты, видимо, сгинули вместе со старым баг-трекером, как и обращения в ТП. Второе может быть объяснено тем, что обращение осуществлялось через обсуждения в специальном паблике.
Мнений
Олды помнят, как говорится. Когда-то давно были “мнения”, где человеку можно было анонимно написать что-нибудь. Прикольно, правда? Ну это хотя бы можно понять: сейчас такого уже нет, сервак отключили, данные удалили и все это забыли.
Закладок людей
Да, в ВК можно добавлять людей в закладки. У меня там 25 человек, которые не в друзьях. И этого в архиве нет, хотя другие закладки есть.
Немного про безопасность
Все ссылки на фото (даже которые в личке грузили!) являются публично доступными. СЕСУРИТИ. Документы хоть закрыты — но за это ВК уже нагибали. Так что если вы слали нюдсы в личных сообщениях, то не такие уж они и приватные. Хотя вряд ли это вас смутит, если вы шлете дикпики через интернет.
Ищем информацию
Текст
Наконец-то можно приступить к поиску иголки в стоге сена. Начнем с поиска текста. Самый очевидный вариант — это grep
, но если вы попробуете что-то поискать, то вас будет ждать облом. Потому что кодировка не та. Чтобы это исправить, поможет iconv
, который умеет конвертировать данные между разными кодировками:
grep -Rnia "$(iconv -t CP1251 <<<'привет')" . | iconv -f CP1251
Не забудьте флаг a
, который говорит grep
, что мы работаем с бинарным форматом, иначе он будет спамить текстом Binary file matches
.
Однако и с такой командой есть проблема: несмотря на флаг -i
, который нам говорит игнорировать регистр, grep
ничего не знает о нашей кодировке и не знает, что ‘п’ и ‘П’ — это одна буква. Поэтому спереди надо дописать LC_CTYPE=ru_RU.CP1251
.
Стоит также учесть, что всякие знаки препинания в html записаны мнемониками или кодом, поэтому писать их в шаблоне поиска не стоит.
Комментарии мне было удобно читать по обсуждениям. Чтобы выдрать все обсуждения, в которых поучаствовали, выполняем
grep -Raioh 'https://vk.com/wall[^?]*' ../Archive/comments | sort | uniq
Довольно много комментариев будут на вашей стене, лучше отфильтровать по своему id. Повторюсь, что есть скрытые комментарии. Судя по всему, они появились следующим образом: пользователь закрыл комменты к своей стене. По крайней мере, API ВК пишет Access to post comments denied
.
Картинки
С поиском по картинкам сами знаете, как обстоят дела, поэтому придется их смотреть своими глазами. Но для этого их надо сначала скачать. Если вы дата сатанист, то все равно в хозяйстве пригодится — какую-нибудь очередную генерилку треша написать, например. У меня в личке было всего полторы тысячи картинок.
Качать будем из сообщений, т.к. на стене — только ссылки на посты. Но если нужны картинки со стены — они есть в альбомах. Насчет комментариев — не знаю, не нашел у себя ни одного комментария с картинкой.
Включаем grep
-магию:
grep -RiohPa "attachment__link' href='\K.*?(?=')" ../Archive/messages | grep 'userapi' | xargs -P 10 -n 1 curl -sO
Что тут происходит? Мы занимаемся запрещенными темными искусствами — парсим HTML с помощью регулярных выражений :) И не простых, а перловых (-P
), да еще с positive look-ahead ((?=)
) и zero-width look-behind (\K
). Флаг -o
говорит нам о том, что нас интересует только совпадение, т.е. сама ссылка, которая находится после href
в одинарных кавычках. Подробнее про это можно почитать на Unix StackExchange.
Потом выбираем ссылки только на картинки, которые хранятся на серверах ВК. А дальше с помощью xargs
в 10 потоков скачиваем все через curl
. Подробнее про связку xargs
с curl
. Клево, но будут потенциальные проблемы, если что-то не получится загрузить. Тогда было бы мудрее записать все в файл, убрав дубликаты (у меня штук 30 было), и потом сверять список скачанного и список из файла.
Если нужны картинки из фоток, то можно немного поменять первую команду:
grep -RiohPa "img src=\"\K.*?(?=\")" ../Archive/photos/
Ссылки
Пришлось добавить вариантов завершения ссылки, т.к. она может быть текстом. А еще в HTML от ВК где-то двойные кавычки используются, а где-то одинарные.
grep -Raioh 'https\?://[^"<'\'']*' ../Archive | grep -v -e 'vk.com/wall' -e 'www.w3.org' -e 'vk.com/id' | sort | uniq > all_links.txt
Второй grep
отсеивает лишние результаты: ссылки на стены и профили ВК, строку с ссылкой на стандарт из шапки. Дальше можно анализировать этот список как душе угодно: читать все подряд, искать grep
ом интересное, удалить к чертям.
Можно посмотреть топ доменов ваших ссылок:
cat all_links.txt | grep -ioP '://\K[^/]*' | sort | uniq -c | sort -n
Но предварительно лучше разобраться с поддоменами, чтобы были более четкие данные. Я особо не парился и писал ручками обрезку домена третьего уровня через sed
:
cat all_links.txt | grep -ioP '://\K[^/]*' | sed 's/.*.userapi.com/userapi.com/g' | sed 's/.*.joyreactor.cc/joyreactor.cc/g' | sort | uniq -c | sort -n
Заключение
Я на самом деле хотел только картинки посмотреть. А потом одно посмотрел, другое посмотрел, да и затянуло. Разумеется, в обычных условия я не позволи бы себе столько времени потратить на такую чушь — не повторяйте мою ошибку :). Но, признаюсь, чтение некоторых старых переписок навеяло… воспоминания. Эмоции. Пищу для размышлений и рефлексии. Так что, может быть, эти часы копания в прошлом были потрачены не совсем зря.
UPD
Вторым ответом техподдержка ВК признала, что есть недочеты и передала разработчикам.
ВНЕЗАПНО публикациями в пабликах
как выяснилось, такое происходит не всегда, почему — не знаю. Просмотрел часть своих старых групп на 1,5 землекопа - моих постов со их стен в архиве нет.
Впечатления от CSS, или как я галерею со смешнявками делал
Disclaimer: Я не фронтендер. И вообще не особо разбираюсь во всем этом. Наверняка специалисты закидают меня субстанциями за то, что сделал что-то криво и вообще “вы не разобрались”. Все впечатления сильно субъективные и зависят от моего круга общения.
Предыстория
Какие-то потуги в сайтостроении у меня были еще в школе, в далеких 2000-х. Статичные сайтики, табличная верстка (sic!), инлайн-стили. Картинка на заднем фоне была тогда вау-эффектом, взаимодействие с пользователем — на грани фантастики, javascript был очень дозированно, jQuery не то что не устарел — его тогда не существовало, а отсупы go <br> <br> <br> <br>.
Сайты получались соответствующие: максимум сделать домашку на 1 страничку или расшарить какую-нибудь решалку. Или по работе мелочь какую-нибудь сделать. Несмотря на это, в голове у меня укрепилось, что HTML — это все ж таки язык разметки, хочешь сделать красивее — используй стили, хочешь взаимодействия с сервером или чего-то нестандартного — пиши скрипт или переделывай все на PHP.
Потом появился jQuery, и ты не мог считаться настоящим фронтенд-разработчиком, если его не знал и не использовал. Все больше и больше вещей делались скриптами. А там и оглянуться не успел, как везде ангуляры, реакты, вуе, бабели, боуеры, вебпаки, ад, черти, безумие и шутки про 1000 новых фреймворков в секунду.
И вот месяц с хвостиком назад пришла в голову мысль сделать еще один свой сайтик, чтобы посты забекапить. Ну и всякие “умные” мысли вроде этой куда-то выкидывать. Об этом я уже коротенечко писал: настоящий веб, разметка отдельно, стили отображения — отдельно.
CSS
Когда хотел немного поменять разметку стандартной темы — особо и вариантов не было, но благо базовые знания были и разбирался со всем быстро. Потом захотелось немного красивостей навроде плашек с тегами — нашел пару примеров, на их основе делал: немного псевдоэлементов c font-awesome — и тоже получилось. Приятным сюрпризом оказалась легкая возможность менять стили для печати страницы, но тут уже не все так гладко: когда сегодня рефакторил стили, выяснил, что есть еще нерешенные проблемы.
Потом случилось знаменательное для меня событие: мою смешнявку админ профунктора не просто принял, но еще и репостнул себе в канал. Смешнявка не высший сорт конечно, но тогда я не знал, как справиться с этой славой и твердо решил сохранить свой около-прогерский “ориджинал контент” на сайте, даже про это картинку сделал:
На волне эйфории от создания статического сайта я решил, что буду делать все на чистом CSS. Ну, а что — игры на чистом CSS делают, вот даже есть бродилка от первого лица. Позже выяснилось, что бродилка в себе до фига javascript содержит и ни фига она не pure css, но на хабре была в районе 2014-2015 года была тема про игры на чистом CSS, и там, насколько помню, все было честно. В любом случае на момент старта у меня в голове была картинка, что CSS очень мощный и с ним можно сделать почти все:
Первое, что заметно — очень много материалов. Хорошие доки на Mozilla CDN, если там не понятно — гугл выплюнет 300 тыщ блогпостов, где все написано как для даунов. Становится понятно, откуда так много “вайти в айти” в этой области, хотя скорее тут положительная обратная связь и другие факторы тоже играют. Тем не менее, порог вхождения ощутимо низкий.
Однако это касается простых вещей. Почти все материалы, которые мне попадались — “практические”: сделай так и все. Нет глубокого объяснения, почему это работает так, а не иначе. Нет фундамента, цельной картины, разъяснения внутренних механизмов.
При этом в самом CSS очень много нюансов. Эти нюансы подскажет браузер, но иногда доходит до абсурда: все готово, осталось чуть-чуть поправить, и тут тебе надо поменять несколько свойств у родителя и детей. Или вообще все выбросить и переписать на flexbox, потому что “там проще”. Например, много свойств меняют свое значение или не выполняются, если ты значение display
поменяешь.
Этими нюансами CSS мне напоминает химию. Ты не можешь оследить все взаимодействия на уровне атомов, поэтому тебе очень много вещей надо просто запомнить. Даже хороший химик не всегда тебе может объяснить, почему реакция происходит именно так — он просто знает, что она так проходит, есть экспериментальные данные. И он это это запомнил, как и особенности кучи других реакций.
Но CSS ведь не наука о сложной природе, а абстракция, созданная человеком. Однако тут получается, что людям проще и быстрее попробовать, чем реально изучить фундаментально. Как в машинном обучении:
Видимо, как следствие, встречается довольно много “трюков”. Некоторые из них хорошие — показывают возможности, которые неочевидны. Некторые откровенно вредны — используют всякие извороты, чтобы сделать, что тебе “нужно” — по сути, являются костылями.
Тем не менее, я получил большое удовлетворение, когда в итоге у меня получилось сделать галерею на чистом CSS с ленивой подгрузкой изображений. Результаты можно посмотреть тут, а исходники — в developer-tools или тут. Но не могу сказать, что я на 100% уверен, что делает каждая строчка CSS, и что будет, если ее убрать. Особенно с учетом того, что некоторые завязаны друг на друга и меняют свое поведение.
Но были и печальные моменты. Знаете, что было самое сложное? Сделать резиновый квадрат. Да-да, можно делать игры, можно сделать галерею на чистом CSS, будучи нубом как я, но резиновый квадрат это уже сложно. Хотя, по идее, именно ради этого CSS и должен был развиваться, разве нет? Я задал вопрос на StackOverflow, спросил у нескольких фронтендеров — никто не знает, как это решить. Есть конечно нюансы, но это хороший пример того, что в CSS не все в порядке. Подробнее можете прочитать в вопросе. Особенно меня позабавил чувак, который предложил jQuery для решения этой проблемы (“верните мой 2007”, как говорится).
UPD: в 2021 году появился aspect-ratio
, и я в итоге пофиксил это.
Знатно у меня пригорело от того, что простые вещи делаются сложнее “сложных”. У меня реально существенно больше времени ушло на верстку превьюшек (хотя казалось бы, чего там сложного — накидал квадратиков, да картинки в них), чем на элементы управления (стрелки, переход на следующую картинку и т.п.). Я думал, все эти смешнявки про то, как тяжело добиться нужного форматирования в CSS — это приколы из серии “ты забыл точку с запятой и компилятор тебе выдал 1000 ошибок, ну ты и нуб”, а это оказалось суровой реальностью.
Итого
А итог получается смешанный. С одной стороны — на CSS можно сделать много всего, но сложно. Или нельзя, и надо добавить чуть-чуть JS. Или генерации на сервере. А потом удивляемся, почему страницы там медленно грузятся и статичные read-only страницы не отображаются нормально или крашат браузер. Реальная история: с моего старого планшета почти невозможно читать какие-нибудь фэндом-вики на *.fandom.com — постоянные вылеты при скролле. Кто-то скажет “купи новый планшет”, но черт побери, там сложность контента как на википедии — простыня текста да пара картинок. С учетом всего этого многие выбирают простой путь и генерят все фреймворками на JS. И вообще, какой смысл от стилей, если все равно сервак нужен? Можно тогда джаваскриптом получать viewport размеры и на сервере по пикселям все генерить? И не удивлюсь, что где-то так и есть.
Как убить на мелкий скрипт кучу времени или история одного пулл-реквеста
Этой заметкой я хочу еще раз показать, что оценка времени на задачу — это нетривиальная проблема. Даже элементарные задачи по написанию 15-строчного скрипта могут растягиваться на несколько часов.
Понадобилось мне дублировать информацию из markdown-заметок в телеграм-канал. Казалось быть, что тут рассусоливать — Ctrl+C
и Ctrl+V
в помощь. Однако выяснился маленький нюанс: markdown в телеге не совсем полноценный и ссылки в таком формате [text](http://example.com)
клиент не поддерживает. Ладно, подумал я, попробуем что-то с этим сделать.
Пытаюсь победить врукопашную
Руками править ссылки, особенно когда их несколько — довольно муторно. Особенно с учетом бага декстопного клиента, из-за которого невозможно редактировать отложенные сообщения. С планшета можно отредачить, но это еще напряжнее.
Может, получится копированием из браузера или текстового редактора? Снова облом и еще один баг в телеге: при использовании буфера обмена форматирование сообщения теряется. Я заинтересовался, почему же так произошло.
В этом нам поможет утилита xclip
, которая позволяет инспектировать содержимое буфера обмена. Для сообщения телеги она показывает такие доступные форматы:
text/plain
UTF8_STRING
STRING
TEXT
application/x-td-field-text
application/x-td-field-tags
Сравните это с теми, что предоставляет Firefox:
text/html
text/_moz_htmlcontext
text/_moz_htmlinfo
UTF8_STRING
COMPOUND_TEXT
TEXT
STRING
text/plain;charset=utf-8
text/plain
text/x-moz-url-priv
или какой-нибудь Libre Office Writer:
application/x-openoffice-embed-source-xml;windows_formatname="Star Embed Source (XML)"
text/rtf
text/richtext
text/html
text/plain;charset=utf-16
application/x-openoffice-objectdescriptor-xml;windows_formatname="Star Object Descriptor (XML)";classname="8BC6B165-B1B2-4EDD-aa47-dae2ee689dd6";typename="LibreOffice 6.0 Text Document";viewaspect="1";width="16999";height="2995";posx="0";posy="0"
text/plain;charset=utf-8
UTF8_STRING
STRING
application/x-libreoffice-internal-id-5387
В общем, судя по всему, проблема достаточно глубокая. С учетом того, что я уже наткнулся на три бага декстопного клиента телеги, пора бы уже рассмотреть альтернативные решения. Конечно, можно попробовать сделать пулл-реквест, чтобы пофиксить один из двух багов, но что-то я посмотрел на исходники, вспомнил насколько я хорош с Qt да и с современными плюсам и подумал, что нет, как-нибудь в другой раз. Отложенные 5 минут я к этому моменту уже давно исчерпал, но уже интересно было из принципа хоть как-то облегчить себе задачу копипаста.
В любой непонятной ситуации пиши скрипт
Да-да, я не очень хороший человек, который все пытается решить программированием. Тем не менее, немножко погуглив, я выяснил, что боты все ж таки могут в нормальном маркдауне посылать сообщения. И может даже люди. Но это не точно.
Ок, нам нужен скрипт, который прочитает файлик и отправить его сообщением в телегу. Какие библиотеки для этого есть? На PyPI 31 страница с результатами по запросу “telegram”. Выбрал Telethon — на хабре статьи с ним есть, и вообще, стильно, модно, молодежно, асинхронно, чистый питон, MTProto и так далее.
Накидаем простенький скрипт, чтобы проверить работу клиента и получить информацию о себе. Не забываем о том, что я живу в стране, которая заботится о моей безопасности, поэтому к телеграму я могу подключиться только через прокси.
from telethon.sync import TelegramClient
import socks
api_id = 11111
api_hash = '...'
proxy=(socks.SOCKS4, '127.0.0.1', 9050)
with TelegramClient('anon', api_id, api_hash, proxy=proxy, timeout=60) as client:
print(client.get_me().stringify())
Запускаем скрипт… и он не работает из-за того, что не может подключиться к телеге. Конечно же, я не HACKERMAN, чтобы он с первого раза заработал. Наверно, проблема с прокси.
We need to do deeper
Ок, проверим, работает ли Tor, который исполняет роль прокси:
$ curl -XGET httpbin.org/ip --socks5 localhost:9050
Получаем ответ с адресом, который не мой — все ок. Значит, проблема в том, как работает прокси в Telethon. Какую библиотеку он для этого использует? PySocks. Отлично, с этой библиотекой работал давным-давно. Там есть простенький скрипт, которым в том числе можно проверить работу прокси. Запускаем и получаем…
Traceback (most recent call last):
File "test.py", line 110, in <module>
print("HTTP: " + opener.open("http://httpbin.org/ip").read().decode())
File "/usr/lib/python3.6/urllib/request.py", line 526, in open
response = self._open(req, data)
File "/usr/lib/python3.6/urllib/request.py", line 544, in _open
'_open', req)
File "/usr/lib/python3.6/urllib/request.py", line 504, in _call_chain
result = func(*args)
File "test.py", line 94, in http_open
return self.do_open(build, req)
File "/usr/lib/python3.6/urllib/request.py", line 1318, in do_open
encode_chunked=req.has_header('Transfer-encoding'))
File "/usr/lib/python3.6/http/client.py", line 1254, in request
self._send_request(method, url, body, headers, encode_chunked)
File "/usr/lib/python3.6/http/client.py", line 1300, in _send_request
self.endheaders(body, encode_chunked=encode_chunked)
File "/usr/lib/python3.6/http/client.py", line 1249, in endheaders
self._send_output(message_body, encode_chunked=encode_chunked)
File "/usr/lib/python3.6/http/client.py", line 1036, in _send_output
self.send(msg)
File "/usr/lib/python3.6/http/client.py", line 974, in send
self.connect()
File "test.py", line 55, in connect
((socket.IPPROTO_TCP, socket.TCP_NODELAY, 1),))
File "/usr/lib/python3/dist-packages/socks.py", line 200, in create_connection
proxy_username, proxy_password)
File "/usr/lib/python3/dist-packages/socks.py", line 322, in set_proxy
username.encode() if username else None,
AttributeError: 'int' object has no attribute 'encode'
Ах ты ж… Какого черта? Я ведь сам туда хэндлер для https добавлял, должно работать! Что могло там поменяться за… 6 лет? Какой я старый :(
15-20 минут на клонирование репы и принт-деббагинг приводят к ответу, что проблема в неправильном порядке аргументов — в основной библиотеке поменяли аргументы, а во вспомогательном скрипте забыли поправить. Хорошо хоть пулл-реквеcт на фикс этой проблемы есть, и скрипт оттуда работает. Мораль проста: если список аргументов длинный, лучше использовать именованные параметры.
Ок, с этим разобрались, значит, проблема не в прокси. В чем же дело? И тут мне в голову приходит гениальнейшая идея про то, что может стоит включить дебаг-логи в Telethon? И одновременно осознание, какой же я даун, если не додумался до этого раньше. Пробуем с дебаг-логами:
DEBUG:telethon.network.mtprotosender:Connection attempt 2...
WARNING:telethon.network.mtprotosender:Attempt 2 at connecting failed: ProxyConnectionError: Error connecting to SOCKS4 proxy 127.0.0.1:9050: [Errno 115] Operation now in progress
О чем же говорит нам эта ошибка? По номеру находим код EINPROGRESS
, а про него читаем уже маны ядра по функции connect. Если общими словами, то проблема заключается в том, что у нас неблокирующая операция (в нашем случае установка соединения) не успела выполнится мгновенно и намекает нам, что она все еще выполняется. Похоже на правду: Tor не очень быстр. Не ожидал конечно, что ради 10-строчного скрипта на питоне, который в наши времена любой школотрон пишет за 5 минут с закрытыми глазами, мне придется опуститься до уровня системых вызовов и вспоминать особенности работы с сокетами…
После того, как я понял, в чем проблема, выяснилось, что она на самом деле уже известна, правда сформулирована по-другому. И внезапно автор этого тикета в репозитории — создатель библиотеки Telethon. Есть еще баг с похожими симптомами в самом Telethon и подозрительно похожий на мою ситуацию тикет в PySocks.
Пора уже делать что-то полезное
Дальше стоит уже более обозримая задача: пофиксить это хоть как-нибудь. Лезть в дебри работы с сокетами — уж очень специфичное занятие, тем более, что реагирует мейнтенер не очень быстро. Тот же баг от автора Telethon висит уже больше года без ответа и привета. Опытный читатель здесь может сказать: бросай ты уже эту библиотеку, возьми другую. И, наверно, будет прав. Но мы не ищем легких путей: попробуем сделать что-нибудь с самим Telethon.
Клонируем репу, немного принт-дебажим как питекантропы, которые не умеют пользоваться отладчиком, и наконец наш фикс готов: просто добавляем таймаут перед установкой соединения. Ведь по сути вся асинхронность заключается в том, что таймаут нулевой.
- s.setblocking(False)
+ s.settimeout(timeout)
Грязно, некрасиво, неасинхронно, но работает. Получаю большое удовольствие от того, какой я молодец и смог пофиксить проблему, и создаю PR.
Возвращаемся назад
Вы ведь еще же помните, что я всего лишь хотел в телегу сообщения в маркдауне отсылать, да? Накидал я в итоге по-быренькому этот скрипт, заодно пару вещей с форматированием исправил, но к делу это не относится. Вроде все хорошо, скрипт работает, казалось бы, задача решена. Но тут что-то во мне ёкнуло и я задумался: не хочу я быть как трейдеры в телеге, которые делают так:
Как нормально передать параметры api_key
и api_hash
, которые нельзя отозвать/поменять, но можно заабузить? Посмотрел, как это делают в примерах Telethon — через переменные окружения. Неплохо, но где хранить тогда эти переменные окружения? В конфиге, чтобы подгружать их? А что если кто-то получит доступ к конфигу?
Тут я немного начал параноить (видимо сказывается профдеформация) и начал думать в сторону всяких Vault, шифрованных разделов, аппаратных ключей, начал смотреть про их уязвимости… Решил даже спросить совета у хорошего знакомого, который лучше разбирается в ИБ, чем я. Рассказал ему о проблеме. И тут он спрашивает про мой скрипт:
А зачем ему энтерпрайз левел секурити?)
…
… подумал я и впихнул чтение через keyring. Не plain-text, ничего разворачивать не надо, даже ставить не надо. Для моих целей действительно сойдет.
Заключение
Я надеюсь, что мне удалось на примере еще раз показать, что оценка времени на работу — непростое занятие, что для программиста иметь кругозор — хорошо, и что надо знать, когда стоит остановиться. Скрипт разросся до 50 строк, из которых полезны только 10, остальное — парсинг параметров, импорты да отступы. А пулл-реквест в итоге приняли на следующий день после небольшой дискуссии.
Надеюсь, что статья вас хотя бы позабавила, а если вы узнали что-то новое, то вообще шикарно. Спасибо за внимание!
Опрос: оправдана ли трата нескольких часов на этот скрипт?
- Да
- Зависит, от ситуации, но скорее да
- Зависит, от ситуации, но скорее нет
- Точно нет
Программист — существо социальное
Работа в команде
Каким бы крутым специалистом ни был кандидат, если у него мерзкий или, используя современные термины, «токсичный» характер — в команде он не приживется. В «благоразумных» компаниях возьмут человека с уровнем знаний пониже, но который впишется в команду и будет полноценно вкладываться в общее дело, а «противного гуру», скорее всего, оставят за бортом, чтобы лодку не раскачивал. Обмен знаниями, коллаборация, эмерджентное развитие без общения очень тяжелы и дороги. Техническим навыкам научить проще, чем социальным.
Взаимодействие с другими отделами
Частая причина “войн” отделов — непонимание. И это замкнутый круг: из-за непонимания возникают проблемы, ответственность сваливают друг на друга, и оборот вражды делает ещё один круг. Чтобы разорвать порочный круг, надо просто сесть и поговорить, наладить общение (даже если ненавидите этих уродов людей), понять, какие у них трудности и в чем их проблемы. “Возлюби ближнего своего”. Можно разработать сотни регламентов, но если вас на том конце будут ненавидеть, то и качество будет “на отвали” (итальянская забастовка).
Развитие себя и своей команды
Обратная связь, метод «360 градусов», встречи 1:1, да даже корпоративы — это все про общение, налаживание контактов и социальные взаимодействия. И хоть в каком-то виде они в организации должны быть, иначе сохранение существующего строя становится целью компании, а люди превращаются в винтики внутри системы: без обратной связи и реагирования на неё все покатится в бездну, как в каких-нибудь сильно бюрократизированных организациях, где система — прежде всего.
Один из наилучших способов оценить свою работу — обратная связь от коллег, как внутри компании (от команды, лида/руководителя), так и вне её (общение с другими специалистами в отрасли). Однако для объективной оценки надо выстраивать доверительные отношения, иначе получится как в социальных сетях/конференциях — красивая картинка, а на самом деле та ещё дичь. Без обратной связи команда не понимает правильности сделанных шагов и выбранного направления, а, значит, и эффективности проделанной работы, что неминуемо приведет к спаду мотивации.
Прокачка навыков тимлида
Обратите внимание на тимлидские конференции. Про что там доклады? Про общение, налаживание связей, софт-скиллы, онбординг, мотивацию, выгорание и не только. Встречаются выступления про процессы, «залётные» доклады про декомпозицию задач и планирование, но самые интересные всегда про людей. Характер людей менять тяжело, их понять-то не всегда просто, а без этого никак. По этому поводу, кроме докладов с конференций (например, TeamleadConf), не лишним будет прочитать книги Дж. Ханка Рейнвотера «Как пасти котов. Наставление для программистов, руководящих другими программистами» и Марка Гоулстона «Как разговаривать с мудаками. Что делать с неадекватными и невыносимыми людьми в вашей жизни». Первая довольно легкая, вторая более фундаментальна, но не забывайте про разумный скепсис.
Лучшие практики и с чем их едят
Стоит понимать, что общие принципы фундаментальны и особо не зависят не то что от языка, но даже и от парадигмы программирования. На этих принципах уже строятся более конкретные вещи. И по мере того, как программист становится матерым, он из этих фундаментальных принципов может выводить уже прикладные решения. Поэтому всякие трюки “специально для Java” или “специально для Python”, конечно, полезны тем, что дают вам знание на уровне “а что, так можно было?” или “о, так это так работает”, но лучше все-таки более системно подходить к этому вопросу. Языки меняются, «кишки» — тоже дело переходящее, фундаментальные принципы — редко.
Понимать практики — значит, знать когда они не работают. Практики — они сферические и в вакууме, выжимка сути, а у реального кода среди прочего есть окружение, контекст, уже сложившиеся подходы, сроки и квалификация разработчиков. К ним надо относиться со здоровым скепсисом, понимать, зачем они и про что, а не бросаться применять.
Через призму такого зрения возникнет вопрос: а как писать хорошо на новом языке программирования и понять его философию? Самый простой ответ — посмотреть существующий код из плюс-минус доверенных источников, желательно, разумеется, от создателей языка. Но большинство мейнстримовых языков сейчас мультипарадигменные и довольно мощные, поэтому в одном языке можно написать одни и те же вещи очень по-разному, и, опять же, на это могут сильно влиять окружение проекта, его цели и т. п., например, код на Kotlin для Android, для Enterprise-бэкэнда, для IOS и для фронта могут выглядеть настолько по-разному, что как будто написаны на разных языках. И “лучшая практика” для IOS-кода может быть тем еще костылем в мире энтерпрайза и наоборот. Поэтому надо затачивать практики под свое окружение и проверять их, а не делать из них культ карго.
Стоит еще вопрос задать — а зачем вам практики нужны? Что конкретно плохо в текущем коде? Как бы вы это решили в хорошо знакомом языке? Можно ли так же решить в этом? Да, возможно, неидиоматично получится, но почему это вас должно волновать? Код решает конкретную проблему, к нему есть требования по качеству. Идиоматичность — приятно, но совсем не главное. Узкие вещи по языку лучше изучать планомерно и глубоко, а не на основе коротышей с поверхностными трюками.