31.12.2023


В этом году я начал очень активно вести канал в Telegram, забив пока что на этот сайт. Итоги года тоже стали не исключением: опубликованы они там. Тем не менее, мне бы хотелось выложить их и на сайте в виде встроенных страничек из мессенджера.

К сожалению, работает только в lite- и pda-версиях сайта.

31.12.2022


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

Для меня же он был не настолько плох. Отчасти благодаря тревожно-депрессивному расстройству, из-за которого для меня все годы являются тревожными. Но признаю, что для меня 2022-й тоже был невероятно сумасшедшим. На уровне 2019-го, но в другой проекции. Вообще, по сути, с 2022-м заканчивается большой четырёхлетний цикл. Недавно заметил, что всё, не только экономика, развивается какими-то циклами. Потрясающий 2019-й, сменившийся депрессивно-спокойными 2020 и 2021 годами, а затем кошмарный 2022-й, когда все процессы дошли до своего апогея. Но про итоги всего четырёхлетия я бы хотел поговорить более подробно, в отдельном посте и попозже, уже в 2023 году.

Вернёмся в год текущий. Для меня он начался вообще офигительно: с повышения зарплаты и неплохой премии, которые были честно выторгованы, а не даны сверху, чем я очень горжусь. После этого последовал отпуск с первым заграничным перелётом — в Дубай. Где год начал проявлять себя: сначала шок и панические атаки от языкового барьера, потом курс дирхамы, сделавший х2, риск заморозки карты, перенос вылета и возвращение в Москву на последнем эвакуационном рейсе Ростуризма… Многие друзья мне предлагали тогда остаться в Дубае, но я не решился. Как впрочем и они, до начала мобилизации.

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

* Хотелось бы сразу выразить благодарность всем подписчикам, кто помогал решать эту проблему:

С профессиональной точки зрения всё тоже оказалось не очень гладко. Даже несмотря на то, что это чуть ли не единственная сфера в жизни, где я преуспел. Год я начал в должности руководителя команды разработки, за 3 года довольно быстро продвинувшись с младшего разработчика до данной позиции. При этом прекрасно осознавая, что, скорее всего, настолько социальную роль я не вытяну. Да и по опыту тоже, учитывая, что работал я под началом всего одного человека и не обладаю плюрализмом стилей управления в кармане. Но хотелось испытать себя, попробовать снова прыгнуть выше головы. И в целом всё прошло ровно по тому сценарию, который я предполагал: отработать как можно дольше, накопить резервы, сократить расходы (одна из причин переезда в Питер) и уволиться, когда станет совсем невыносимо. Идея об увольнении витала уже давно, так как спустя 2,5 года работы в одной компании хочется попробовать что-то новое и посмотреть, как устроены процессы в других местах. Но я всё не мог решиться, а тут выпал прекрасный шанс разыграть партию, в которой у меня уже не будет обратной дороги.

Но психологически вытянуть оказалось не просто. Играть не свою роль — это просто ужасно! Даже врагам не пожелаю. Я начал много пить. В добавок к уже выписанным антидепрессантам. Для повышения общительности и чтобы успокоить бесконечные мысли о том, что я не справляюсь и вообще на проекте всё плохо. В итоге ушатал печень, которая начала зарастать жиром. Теперь приходится сидеть на диете, гепатопротекторах и периодически сдавать анализы, чтобы не довести дело до цирроза. В итоге пришлось уволиться. Причём уже в таком состоянии, что без саббатикала никуда. Это такой длительный отпуск за свой счёт, когда ты можешь обдумать свою жизнь, отдохнуть, успокоиться, попробовать что-то новое. А ещё — считаю это важным элементом саббатикала — вновь научиться жить скромнее и сократить расходы. Ведь в мире не просто так центральные банки охлаждают экономику. Людям это тоже иногда нужно.

Для сокращения расходов и смены обстановки, которую требовали личные проблемы, переехал в северную столицу — город Санкт-Петербург. Вообще основной причиной на самом деле было наличие там хороших приятелей, с которыми можно было погулять в реальной жизни, в отличие от домоседов с Рублёвки. Но всё испортила мобилизация. Увы. В итоге я вновь оказался совершенно один, как в начале 2019 года. Это тяжело переживать первые несколько месяцев. Приходится полностью переучиваться жить. Но постепенно ты адаптируешься и вместо постоянных тусовок и личных разговоров за бокальчиком пива, снова в жизнь возвращаются чаты, блоги, программирование и прочие хобби. Жизнь снова начинает течь своим чередом.

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

А знаете что настолько плохо, что даже хорошо? Состояние брокерских счетов россиян в этом году. Огромный жизненный урок тем, кто слышал, но не осознавал рассказы старших о потерянных накоплениях. Только в этом году я понял, что нужно тратить деньги. Бесполезно копить цифры на счетах — всё равно отберут. Нужно жить здесь и сейчас. Пожалуй, это и есть главный урок, который мы можем вынести из 2022 года. Нужно жить и наслаждаться жизнью, пока можем. Ведь кто знает, что будет в следующем году? Ядерная война? Инопланетное вторжение? Голод, вызванный безработицей, когда половину людей заменят нейросети?

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

  • устроиться на работу (в идеале на 2 или 3, следуя последним тенценциям выживания) и восполнить финансовые резервы;
  • получить водительское удостоверение;
  • научиться играть на гитаре хотя бы "Как здорово, что все мы здесь сегодня собрались";
  • попутешествовать по странам СНГ;
  • слетать в Сибирь;
  • уехать зимовать в Турцию или на Бали;
  • не заниматься самобичеванием и пиздостраданиями, а просто жить.

Часть планов переносится ещё из 2021 года (перед 2022 я не подводил никаких итогов, похоже):

  • учить английский;
  • пилить pet projects (есть куча идей разной степени продуманности, в том числе подразумевающих работу с криптой и нейросетями).

Делитесь своими планами! Как и где собираетесь провести 2023 год?

Дальше я подведу итоги года по различным номинациям, но до этого хотелось бы подвести итог (простите за тавтологию) лирической части данного поста и поздравить вас, дорогие читатели, с Новым годом! И пожелать, чтобы все худшие сценарии обошли нас стороной, а все лучшие осуществились и позволили успешно воплотить все ваши планы в жизнь! Ура, товарищи! С 2023-м!

Раздача слонов

Раздача слонов

Разочарование года / Кошмар года

социальные мероприятия (путешествия, концерты, курсы)

Вообще весь этот год можно назвать одним большим разочарованием: разочарование в ETF, в долларе, в международной финансовой инфраструктуре, в европейских и американских политиках, в Visa и MasterCard, Euroclear и Clearstream, etc. Но всё же больше всего меня разочаровал личный провал всех социальных активностей и абсолютное непонимание, как и почему другие люди получают от этого удовольствие, а я не могу. Тяжело быть не таким как все долбоёбом.

Кидок года

прекращение или сокращение выдачи виз для россиян европейскими странами

Как только спрос на тракторы начал расти, обиженки-прибалты подсуетились, чтобы в России осталось как можно больше солдат. Спасибо им за это!

Конечно, есть ещё не менее эпичный кидок на золотовалютные резервы, но всё же он не касается лично меня напрямую. А вот из-за виз я 12 000 руб. потерял :(

Обман года

инвестирование в FinEx ETF

Считалось, что индексные фонды — это более безопасный и лёгкий способ инвестировать свои сбережения для новичков. Вместо сбора портфеля вручную ты получаешь готовый набор бумаг в составе одной. Как с кольцом Саурона. При этом исторически индекс в долгосроке обычно ещё и обгоняет практически всех профессиональных управляющих. Однако 2022-ой показал, что рисков на самом деле всегда больше, чем известно людям в данный момент. Считалось, что быть фондом под европейскими регуляторами — это надёжнее БПИФов в России. Но в одно мгновение все акции Финекса для российских инвесторов превратились в цифровые фантики, с которыми до сих пор ничего нельзя сделать. И никакого решения даже близко не видно на горизонте.

Инвестиция года

саббатикал

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

Город года

Ташкент

На удивление мне понравилось в мусульманских странах. Кажется, что только там сохранились настоящие традиционные ценности. Женщины ходят в целомудренных нарядах, а не вызывают похоть одним своим видом, как мода в современной европейской культуре. Феминистки, конечно, со мной не согласятся и будут спихивать ответственность на мужчин. Ну что ж. Это всё, что они умеют. Это, и жить с котами/собаками.

Банк года

Тинькофф

Как бывший сотрудник СберБанка, а также активный пользователь российской финансовой системы, могу очень много рассказывать о плюсах и минусах того или иного банка. Могу защищать и хвалить Сбер за огромный прогресс в последние годы (в том числе не только в ИТ, но и в бизнес-решениях наконец-то), ругать Альфа-Банк за постоянные попытки обмануть или подловить клиента теми или иными "звёздочками" в абсолютно любом договоре. Но при этом уже всем, наверное, известно, что лучший банк для физических лиц сейчас — это Тинькофф. Лучшие условия, лучшие акции, лучшая статистика, лучшее приложение. Для меня до сих пор умом не постижимо, как "Тинькофф Инвестиции" умудряются выкатывать столько фич с адекватным качеством даже на старте, допиливая их до конфетки в кратчайшие сроки. При этом не выдаивая из клиентов последние копейки.

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

Худший банк года

ВТБ

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

Платёжная система года

МИР

После того, как Visa и MasterCard всех нагло кинули, прогнувшись под сраных политиков, другого варианта быть и не могло. Но при этом она даже очень хорошо работает. А к праздникам даже выкатили невероятно щедрую акцию с «Приветами». А как они завернули платежи иностранных платёжных систем через себя, чтобы всё продолжало работать даже после введения санкций! Моё почтение!

Кешбэк от НСПК

Вот только поправьте баг в Mir Pay под Android, пожалуйста, чтобы повторная оплата производилась с отображающейся на экране карты, а не с дефолтной.

Прорыв года

нейросети

Мама, я аниме!

Пожалуй, одно из немногого хорошего, что случилось в этом году — это огромный прорыв в нейросетях. Вот я только начал снова писать, спустя 4 года, но чувствую, что в 2023 снова придётся бросить это дело, потому что ChatGPT сможет за меня писать связную речь по указанным тезисам. Причём с картинками от Midjourney и других нейросетевых генераторов, чьи изображения, кстати, уже используются в этой статье.

Как думаете, люди сами себя уничтожат или всё же понадобится помощь искусственного интеллекта?

И подписывайтесь на дружественные каналы с нейродевушками:

Достижение года

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

В целом не считаю год для себя неудачным. -11% по инвестициям, когда индексы упали на 20-50%. Большинство планов и идей сработало. Бабки есть, перспективы есть, что делать понятно. Главное, пожалуй, — это уметь вовремя корректировать намеченные планы и меньше об этом переживать. И тогда успех обеспечен.

Вещество года

гамма-оксимасляная кислота (GHB)

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

Закрытие года

Hydra

Как ныне житель Петербурга, не мог не включить такую номинацию :) Кто знает, тот поймёт, кто не знает, то лучше и не знать.

Happy new year!
28.11.2018


Примечание от админа

Впервые публикую на страницах своего бложика гостевой материал. Такое волнительное событие! Сегодня речь пойдёт про бота, созданного другими людьми, но которым активно пользуются как люди в наших сообществах, так и ваш покорный слуга лично. Так что я подумал, что будет большим упущением не упоминуть про него в рамках данного цикла статей. К счастью, Ламппи Лутти с радостью согласился на моё предложение черкануть про бота пару строчек. Мне лишь оставалось немного помочь с редактурой и стилистическим оформлением. Прошу любить и жаловать!

Почему заголовок в этот раз не содержит юзернейма бота? Поймёте по ходу чтения статьи =)


Вступление

Всем привет, я Lämppi Lütti, и меня пригласили написать о моём боте в цикле статей о Telegram-ботах.


Бот, который нравился многим

Сначала это был бот для выполнения хотелок одного товарища из небезызвестной Telegram-конфы @kdr_flood. Одной из его хотелок было создание картинок-цитат из выбраного сообщения. Так как автор [яблочный пирог — прим. редактора] не мог и не хотел заниматься вопросами хостинга, бот часто лежал и постоянно приходилось ждать его запуска. После, к этому прибавились постоянные падения из-за попыток зацитатить нетекстовые сообщения. Так как эта функция забавляла некоторых обывателей конфы, то были предложения от @kozalo и @iosys о хостинге бота, если падения будут исправлены. Но автор не стремился что-либо исправлять и просто забил. Шли разговоры о том, что было бы неплохо воскресить бота, но лезть исправлять код никто не спешил. В конечном итоге залез я, благо автор оставил исходники в открытом доступе. Отсюда берёт начало Quote-maker (или @iosys_bot, так как я тоже не сильно хотел заниматься хостингом и просто отправил бота тому, кто предложил его захостить). Это был маленький бот, который понравился многим.


Бот, который использовали

Как говорилось ранее, новый бот — это всего лишь одна функция «зацитатить в картинку», поэтому количество команд у него равно двум: это /quote_ru — для цитирования с русским заголовком и /quote_en — для цитирования с английским заголовком. Наличие цитирования на английском — это не попытка выхода на международный рынок, а функция для тех ситуаций, когда цитируется сообщение на английском (чтобы было гармонично: английский текст с английским заголовком). Также были внесены небольшие нововведения:

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

Это был бот, который использовали.

Этот бот, как и его прародитель, написан на Python 3, с использованием библиотек pyTelegramBotApi (для работы с Telegram Bot API) и Pillow (для рисования). Сам код написан не в лучшем стиле. Кому интересно, вот репозиторий.


Бот, который был нужен только мне

После завершения работы над ботом на Python я нашёл для себя интересным язык Clojure (диалект LISP для JVM) и в качестве тренировки решил переписать бота-цитатера на него, попутно исправляя ошибки и добавляя некоторые «фичи». После окончания работы над ботом @iosys по надуманным причинам отказывал в хостинге, а после заявил, что бот на Clojure работает медленнее, чем на Python, да и к тому же требует больше оперативной памяти. Проблема с медленной скоростью действительно есть, хотя всё не так страшно. Дело в том, что для каждого сообщения боту приходится просчитывать координаты (это нужно для динамического растягивания картинки в зависимости от количества текста), но просчёт идёт один раз для одного и того же набора входных данных. То есть если бот получает сообщение размером, допустим, в 40 символов, то расчёты для примерно такого же по размерам сообщения будут взяты из кеша (особенность Clojure). Из-за этого и увеличивются требования к RAM (не считая того, что требует JVM). Возможно, всё можно было решить обычной настройкой виртуальной машины, которую никто не делал.

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

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

  • возможность подключения тем;
  • цитирование нескольких сообщений;
  • цитирование с изображением/стикером.

Для работы с Telegram Bot API используются библиотеки morse и clj-http (последняя нужна из-за сырости morse, так как в ней нет функций для получения и скачивания файлов из Telegram). Для рисования используется пакет awt из стандартной библиотеки Java.


Послесловие

Спасибо за прочтение моей статьи! Это был интересный и забавный опыт — написание статьи в чей-то персональный блог.

27.11.2018


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

Целью реструктуризации было снижение нагрузки на основной слабенький сервер в Нью-Йорке, который внезапно стал жёстко тупить. В срочном порядке был переписан @kozRandBot и перенесён на новый сервер, о чём я своевременно писал ранее в своём канале. Но вот о чём я умолчал, так это о закрытии сайта Congrats.cf. Им настолько никто не пользовался, что YouTube даже прислал письмо об отключении своего API от проекта.

Уведомлени об отключении API от YouTube

Затем я перенёс все статичные сайты, задав кучу редиректов, постепенно уходя от использования домена nekochan.ru в пользу sadbot.ru. Также я хотел передать управление пересылкой почты для доменов Яндекс.Коннекту, но пока у меня не получилось найти время, чтобы разобраться, как это всё работает (я сменил MX-запись на Cloudflare, но Яндекс даже спустя 72 часа так это и не увидел).

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

P.S. А ещё я обновил логотип блога. Как он вам?

03.11.2018


Пошёл ты, gettext! Я не хочу париться с компиляцией строк в бинарники!

Модуль предоставляет очень простой и неоптимальный способ перевода скриптов, ботов или приложений. В простоте его сила: всё что нужно для создания различных наборов фраз под несколько языков — это скормить библиотеке JSON-, YAML- или INI-файл (или даже dict). После этого остаётся только получить объект словаря для конкретного языка и извлекать из него фразы по ключам.

В принципе, конечному пользователю нужно знать только про класс LocalizationContainer с его статическим методом from_file(), который читает файл по переданному ему пути и возвращает фабрику локализаций. Хотя инстанциировать её можно и через конструктор с помощью готового dict'а. Затем получить словарь фраз (объект одного из потомков класса LanguageDictionary) можно по коду языка с помощью метода get_lang(). Ещё один вариант — это метод get_language(), который отдельно принимает коды языка и страны.

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

Установка:

# базовая истановка
pip install klocmod
# или с поддержкой YAML-файлов
pip install klocmod[YAML]

JSON (сперва язык):

{
  "en": {
    "yes": "yes",
    "no": "no"
  },
  "ru-RU": {
    "yes": "да",
    "no": "нет"
  }
}

JSON (сперва фраза):

{
  "yes": {
    "en": "yes",
    "ru-RU": "да"
  },
  "no": {
    "en": "no",
    "ru-RU": "нет"
  }
}

INI:

[DEFAULT]
yes = yes
no = no

[ru-RU]
yes = да
no = нет

YAML (требуется библиотека PyYAML):

# сперва язык
en:
  yes: yes
  no: no
ru-RU:
  yes: да
  no: нет
---
# сперва фраза
yes:
  en: yes
  ru-RU: да
no:
  en: no
  ru-RU: нет

Пример кода:

from klocmod import LocalizationsContainer

localizations = LocalizationsContainer.from_file("localization.json")
ru = localizations.get_lang("ru")
# или
en = localizations.get_lang()    # получить язык по умолчанию
# затем
print(ru['yes'])    # вывод: да
# альтернативные варианты получить конкретную фразу:
localizations.get_phrase("ru-RU", "no")
localizations['ru-RU']['no']

Более подробную информацию можно прочитать в документации (в кои-то веки полноценная документация, сгенерированная Sphinx'ом!). Правда, на английском языке.

Ну и в конце на всякий случай ещё раз напомню, что данный пакет вырос из модуля botutils.multilang, который я упоминал буквально в каждой публикации из цикла статей про ботов в Telegram.

29.10.2018


Сегодня мы рассмотрим моего последнего (по крайней мере на данный момент) бота. Он существенно отличается от предыдущих технически и даже физически размещается на другом сервере. Но сперва расскажу, что он вообще из себя представляет.

Идея, развитие которой вылилось в создание бота, пришла в мою голову совершенно спонтанно во время одного из разговоров в чате ещё 26 августа 2017 года К сожалению, часть сообщений там удалена, так что точно проследить историю трудновато, но попробую рассказать по памяти. Всё началось с простой шутки: мне захотелось написать сообщение на языке компьютеров — в виде бинарного кода из нулей и единиц. Потом в виде шестнадцатеричных значений — это уже скорее язык низкоуровневых программистов =)

Да, всё началось с этаких шифровок. Разумеется, из-за такой фигни я не побежал сразу же пилить нового бота. Для возникновения пожара разработки, кроме топлива, нужны окслитель и источник огня. Окислителем стала моя любовь к смайлику ¯\_(ツ)_/¯ и постоянная неработоспособность бота @FailsBot, чей функционал по «пожиманию плечами» я впоследствии и скопировал. Источником искры, последней каплей, стал… русский язык. Вернее кириллица и привычка некоторых людей не проверять, на каком языке они пишут, перед набором сообщения. Вообще, история этой проблемы забавна, поскольку она настолько раздражающая, что я стал третьим человеком, который взялся за её решение.

Первым решить её пытался Рабу со своим ботом. Работал он следующим образом: во-первых, бота нужно было добавить в чат; во-вторых, на сообщение с неправильной раскладкой надо было ответить специальной командой. В результате бот выдавал правильный вариант текста. Минусов у такого подхода два: необходимость вручную вызывать команду и необходимость присутствия бота в чате, что не всегда бывает возможно.

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

Мой бот, в свою очередь, обходит вторую проблему. Работая в inline-режиме, он определяет язык переданного ему текста (разумеется, поддерживаются только кириллица и латиница) и предлагает вариант с противоположной раскладкой, не делая никаких предположений. Да, немного запарнее для пользователя, так как надо копировать текст и вставлять в поле ввода. Зато не надо добавлять бота ни в какие чаты и даже можно просто посмотреть результат перевода во всплывающем окне, не отправляя сообщение в сам чат!

Итак, подведём итоги. Мне нужен был бот, работающий исключительно в inline-режиме, который смог бы «пожимать плечами», переводить текст в другую раскладку, в последовательность нулей и единиц или шестнадцатиричных байтов, а также обратно. В результате появилась первая версия, в которую вошло «пожимание плечами» и переводчик на компьютерно-программистские языки цифр. Перевод раскладки был добавлен «чуть» позже (подумаешь, всего-то через месяц =) ) вместе с функцией кодирования текста в base64-строку и обратно.

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

КомандаПараметрыОписание
/help и /start нет выводит сообщение со справочной информацией
/suggest текст предложения, который будет отправлен разработчику бота позволяет предложить новый функционал для бота

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


Техническая часть

Теперь, покончив с историей и описанием бота, обсудим вопросы технической реализации. В отличие от предыдущих ботов, этот построен на основе библиотеки aiotg. В чём её отличие от pyTelegramBotAPI? Последняя под капотом использует библиотеку requests, которая упрощает выполнение синхронных блокирующих запросов. И в этом её недостаток: поток блокируется на время выполнения каждого сетевого обращения к API, что является довольно дорогой и длительной операцией (особенно в случае возникновения каких-либо проблем с сетью или долгим ожиданием ответа от сервера), во время которой программа зависает, ничего не делает и перестаёт отвечать на новые поступающие сообщения. Для решения данной проблемы pyTelegramBotAPI предлагает класс AsyncTeleBot. Он запускает каждый запрос в отдельном потоке, что, по заверению разработчиков библиотеки, может существенно ускорить бота, но надо понимать, что при необходимости выполнения нескольких последовательных запросов всё равно придётся блокировать поток до возвращения результата промежуточных запросов. В результате опять придётся городить новые потоки, чтобы не блокировать основной. А каждый новый поток — это новые накладные расходы по памяти и увеличение времени, затрачиваемого на переключение контекста… Да и, в конце концов, Пайтон не Джава и оптимизирован для максимальной производительности в однопоточной среде.

Конкретно для решения проблемы с вводом и выводом данных уже давно существует более элегантное решение — асинхронный I/O и модуль asyncio в стандартной библиотеке. Асинхронность не равна параллелизму, и в данном случае мы будем говорить о так называемой кооперативной многозадачности, когда следующая задача выполняется только после того, как текущая явно объявит себя готовой отдать процессорное время. Такие задачи называются корутинами (coroutines) и выполняются внутри специального планировщика, который в Пайтоне именуется event loop'ом — циклом событий. Задания отправляются на выполнение в планировщик, который запускает на выполнение какую-либо из них. Корутины могут запускать другие корутины и дожидаться их выполнения. Если внутри корутины происходит операция ввода/вывода, то её выполнение приостанавливается и управление переходит обратно в event loop, который ставит на выполнение следующую задачу. Внутри это всё работает на генераторах и функциях обратного вызова, но для конечного пользователя всё выглядит в виде няшных конструкций с async/await'ами.

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

  • вместо синхронного блокирующего I/O используется асинхронный и неблокирующий
  • event loop вместе со всеми корутинами выполняется на одном потоке!

Таким образом достигается возможность обслуживания огромного множества легковесных корутин на одном потоке без лишних накладных расходов. И именно поэтому я решил отказаться от библиотеки pyTelegramBotAPI. Но вот с выбором асинхронной замены для неё оказалось не всё так просто.

На официальном сайте Telegram предлагается использовать библиотеку AIOGram. Как бы парадоксально это ни звучало, но проблема с ней в том, что она старается идти в ногу со временем и использует фишки последних версий Пайтона: официально в последней на данный момент версии 1.4 поддерживается Python 3.6, но надпись written in Python 3.7 на странице проекта в GitHub как бы намекает. Тут надо отметить, что модуль asyncio появился в Python 3.4, а ключевые слова async/await — в Python 3.5. И именно версия 3.5 стояла у меня на компьютере и на одном из серверов. При этом если установить новую версию на компьютер под управлением Windows — тривиальная задача, то для систем на базе Debian это каждый раз превращается в настоящий квест, когда приходится либо компилировать всё из исходников, разгребая попутно кучу граблей, либо хитрыми манипуляциями с настройками APT'а пытаться поставить более свежие версии Питона и всех зависимых пакетов, ничего при этом не сломав.

Какое-то время спустя, конечно, мне всё равно пришлось ставить на обе машины Python 3.7, но в то время мне было прям очень лень этим заниматься, так что пришлось искать другую библиотеку. Но со стороны AIOGram выглядит таким же достойным проектом, как pyTelegramBotAPI, зато с асинхронным I/O.

Мой выбор в итоге остановился на библиотеке aiotg. Её API значитеьно беднее, из-за чего приходится напрямую работать со словарями и списками, а в некоторых местах даже вручную генерировать JSON для запросов, но зато требуется только Python 3.5, а в плане запуска бота она даже проще, чем pyTelegramBotAPI (см. метод run_webhook; правда пришлось всё равно лезть в исходники и высматривать метод create_webhook_app, чтобы нормально реализовать возможность запуска одного бота как на поллинге (что проще использовать при разработке), так и с использованием веб-хука (для продакшена)).

Итак, запускался бот сначала на домашнем сервере, представляющем собой одноплатный компьютер Orange Pi PC, о котором мы говорили в одной из прошлых статей. Там стоял дефолтный Питон 3.5. Когда начались проблемы с ddclient'ом, бот переехал на VPSку, на которой по сей день крутится Пайтон 3.6. Но поскольку я его компилировал когда-то вручную сам, то там нет половины модулей, включая sqlite3, который требует наличие скомпилированной библиотеки _sqlite3.so. Я попытался скомпилировать версию 3.7 с нужным флагом, но поскольку VPSка дешёвая и слабая, то в результате только уронил веб-сервер, которому стало не хватать ресурсов, а сборка всё равно завершилась с ошибкой. Плюнув, я перенёс бота обратно на недавно оживший домашний сервак, на который к этому времени я уже поставил Python 3.7.

Избавившись от зависимости на старую платформу, я с радостью выделил бота в отдельный репозиторий, причём не на Битбакете, а на Гитхабе, чтобы позволить другим программистам присылать pull request'ы с новыми преобразованиями текста (поскольку GitHub сейчас популярнее и распространённее, чем Bitbucket). Если прошлый репозиторий вёлся в духе вечной разработки: без файла лицензии и README, без тестов, с отсутствующими докстрингами, с гуляющей от модуля к модулю степенью аннотированности типов и так далее — то новый оформлен аккуратно: с автоматическим запуском тестов, с примерами конфигурационных файлов для системного менеджера (чтобы перезапускать бота при случайном падении) и фронт-сервера, который пришёл на смену системе роутинга, позаимствовав из неё некоторые соглашения и вынеся принятие и перенаправление сообщений наружу из интерпретатора в отдельный веб-сервер (в моём случае в nginx). Теперь настройка SSL-сертификата происходит на стороне фронт-сервера, после которого все запросы к боту пробрасываются без шифрования через Unix domain socket.

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

Теперь расскажу о возникших во время работы над проектом проблемах. Первая из них проявилась буквально через несколько минут после первого тестового запуска бота на реальных пользователях: перевести текст в бинарный код — легко, а вот обратно — невозможно для длинного исходного текста, так как не хватает лимита по максимальной длине inline-запросов, специфицированного в Telegram Bot API как 512 символов. Для решения данной проблемы я решил прикреплять к сообщениям кнопку «Расшифровать», заодно избавив пользователей от необходимости копировать текст трансформированных сообщений и переводить их обратно вручную.

Но и тут кроется закавыка со стороны API! Данные, которые можно прикреплять к кнопке, ограничены всего 64 байтами! Понятно, что ничего дельного туда не уместишь. И вот именно для решения этой проблемы мне пришлось переносить бота обратно на Апельсин, чтобы воспользоваться СУБД SQLite3 для хранения исходного текста всех запросов, которые поступают боту. Для каждого из них генерируется числовой идентификатор, который прикрепляется к кнопке.

Ну и напоследок расскажу о проблеме в самой библиотеке aiotg, с которой я столкнулся, как раз при реализации inline-кнопок, а если точнее, то при реализации функции-обработчика запроса на расшифровку, отправляющегося при нажатии на кнопку. Связана она с тем, что разработчик библиотеки не предусмотел возможность отсутствия у CallbackQuery поля message при отправке запроса от кнопки, прикреплённой к сообщению, отправленному через inline-бота. Вот такая вот тонкая особенность API. Подробнее проблему я описал по-английски в соответствующем issue на GitHub, так что не вижу смысла повторяться. Пока же ошибка не исправлена, пришлось добавить временное решение проблемы.

На всякий случай ещё раз напоминаю, что информацию обо всех новых изменениях в работе ботов я буду писать в своём Telegram-канале. Данный пост больше обновляться и дописываться не будет!


Добавлено 8.11.2018

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

Пока я делал функцию создания Б А Н Н Е Р О В   С   Т А К И М И сообщениями, решил отрефакторить ту гору костылей, которая занималась выборкой нужного обработчика для текста. В этот запутанный ад из условий трудно было добавлять новую логику, так что я решил всё переделать. В результате появилась полноценная система динамической загрузки обработчиков при старте приложения. Все обработчики теперь представляют собой классы, унаследованные от базового абстрактного класса TextProcessor и находящиеся в различных модулях внутри пакета strconv. Сам интерфейс класса TextProcessor и процесс создания новых «фич» подробно расписан в специально написанном мною официальном гайде на Гитхабе (надеюсь, ни один программист в 2018 году уже не боится английского языка? =) ).

В целом рефакторинг занял приличное количество времени (саму фичу я добавил за пять минут, но на рефакторинг, продумывание всех деталей и написание документации ушло три вечера), но я доволен получившимся результатом. Теперь добавлять новый функционал стало действительно легко, просто и удобно. Вот что значит соблюдать принципы SOLID — в частности open-closed principle! Так что самое время контрибьютить в open source, дорогие друзья!

Ну и упомяну уж про вторую функцию тоже. Второй обработчик, вошедший в релиз 1.1.0, выполняет следующие преобразования:

Исходный текстРезультат
""«»
(c)©
(r)®
(tm)

Добавлено 17.11.2018

Очередное обновление, и снова достаточно значимое, чтобы дополнить пост на сайте.

Сегодня вышли сразу две версии бота: 1.1.1 и 2.0.0. Первая спокойно ставится поверх v1.1.0 и добавляет следующие преобразования в модуль «Элитный типограф»:

Исходный текстРезультат
!= и ~= и
>= и <= и
...

Также я немного поменял преобразование двойных кавычек, сделав его по аналогии с ВКшным и KozMULовским:

Старый синтаксисНовый синтаксисРезультат
"foo" <<foo>> «foo»

Ну и поскольку начались подвижки по проблеме с aiotg, то я откатил свои изменения. Правда, до полноценного релиза следующей версии библиотеки, похоже, ещё далеко, так что пришлось прописать зависимость прямо на коммит (оказывается, pip так умеет!) Хотя с последними изменениями вообще пропала основная причина использовать эту библиотеку. Впрочем, в эпоху Докера уже нет никаких оправданий использовать старые версии Питона и подходящие к ним пакеты.

И вот мы плавно подошли к главному нововведению второй мажорной версии бота: к контейнеризации. Меня наконец-то убедили разобраться с конфигурацией и попробовать Docker. Знакомство прошло не очень гладко. Сначала я безуспешно пытался настроить проксирование контейнеров через SOCKS5-прокси, но, видимо, советы по указанию переменных среды HTTP_PROXY и HTTPS_PROXY всё-таки про другое (или я что-то делал не так). Провозившись вечер, я плюнул и арендовал себе новый сервер в Амстердаме у компании Scaleway с одним гигабайтом ОЗУ за €2 в месяц. Так что теперь бот должен работать гораздо быстрее, лишившись всех посредников в лице прокси-серверов.

Избавившись от проблемы с заблокированностью «эндпоинтов» Telegram API, я продолжил свои мучительные эксперименты. В итоге всё-таки ещё за два вечера более-менее разобрался с Докером и Docker Compose, написав и оттестировав конфигурационные файлы. В результате, правда, пришлось вынести файл базы данных и модуль с конфигурацией в папку app/data. Эти файлы должны быть вне контейнера, так что данная папка монтируется в него при запуске как есть. Именно это изменение привело к нарушению обратной совместимости и потребовало увеличение мажорной версии. Ну и то что в скриптах init.sh и start.sh теперь виртуальное окружение разворачивается в папку venv, а не прямо в корень репозитория. Да, старый способ запуска никуда не делся и всё так же доступен для использования.

Ну ещё я добавил защиту от спамеров, заваливающих мне личку через функцию отправки сообщений, но это обновление едва ли заслуживает обсуждения.

24.10.2018


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

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

Технически бот-рандомайзер построен абсолютно на той же платформе, что и предыдущий — тот же Python, тот же pyTelegramBotAPI, те же расширения из botutils. Даже сервер для веб-хуков и репозиторий общие! Есть ли всё-таки какие-то различия? Да, хотя их и немного. Во-первых, тут есть тесты! Правда, сейчас они проверяют всего одну специфичную функцию и их запуск никак не автоматизирован, но хоть что-то. Во-вторых, в отличие от @kozalo_bot'а, @kozRandBot поддерживает и русский, и английский языки, так что именно тут и используется модуль multilang, упомянутый в прошлом посте. В целом, этот бот гораздо меньше, поэтому код получился более аккуратный и структурированный.

Что же умеет бот?

  • бросать монетку;
  • выдавать случайные числа в определённом диапазоне;
  • отвечать «да» или «нет» на односложные вопросы;
  • выбирать случайный вариант из списка;
  • генерировать пароли указанной длины.

С монеткой всё просто: пишешь боту /coin (или /flip_coin) — получаешь ответ. А вот получать случайные числа можно двумя способами:

  1. /num 100 — вернёт случайное число от 1 до 100;
  2. /num 100 1000 — от 100 до 1000.

Случайный односложный ответ он даёт просто на команду /yesno (или /yes_or_no) — после команды можно писать что угодно. Список для выбора случайного варианта можно определить множеством способов:

  1. /list кошки или собаки? — выдаст либо «кошки», либо «собаки»;
  2. /list быть или не быть — или «быть», или «не быть»;
  3. /list красный, белый, синий или зелёный — выдаст один из перечисленных цветов;
  4. /list спать, читать книжку, поиграть, писать код, сходить на свидание — один из перечисленных вариантов времяпрепровождения;
  5. /list армия, унижения, избиения; завод, нищета, провинция; Москва, перспективы, жизнь — выдаст либо «армия, унижения, избиения», либо «завод, нищета, провинция», либо «Москва, перспективы, жизнь».

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

Для генерации паролей используется команда /seq (или /sequence, /password). В качестве параметра можно указать длину пароля. По умолчанию используется 8 символов. Меньше 6 указать нельзя.

А теперь самое главное: всё это работает и в inline-режиме! Без параметров бот предлагает кинуть монетку или сгенерировать пароль из 8 символов, с одним указанным числом N — пароль будет из N символов, а также добавится вариант с получением случайного числа от 1 до N, с двумя числами N и M, разделёнными пробелом — случайное число будет от N до M. Списки парсятся точно так же, как и в обычном режиме: поддерживаются и запятые, и точки с запятой, и союзы на конце, и вопросительный знак. Но с последним связано важное замечание по поводу функции выдачи ответа «да» или «нет» на односложные вопросы — в inline-режиме такой вопрос обязан заканчиваться на вопросительный знак!

Вот, пожалуй, и всё, что можно сказать о боте. По сравнению с прошлым этот пост получился до безобразия коротким. Проще бот, меньше исторического бэкграунда, меньше технических деталей — удаётся рассказать о нём предельно кратко и лаконично. В следующий раз речь пойдёт про моего самого последнего и молодого бота, который только-только стал юзабельным и начинает свой путь к успеху. Думаю, там рассказ получится подлиннее и с бóльшим количеством технических деталей =)

See ya soon!

Про случайность результатов

In inline mode it doesn't randomize: always chooses second of two items.
— Alexandr

Ах да! В отзывах на сайте-агрегаторе был человек, который усомнился в честности и случайности результатов выбора из двух вариантов: у него постоянно выпадало второе значение. Подобные вопросы возникали и у нас в чатиках. Действительно, порой кажется, что последний вариант выпадает чаще. Но на самом деле это не так. В этом можно убедиться как экспериментально, увеличив количество попыток и/или вариантов, так и посмотрев непосредственно на код.

Если же кто-то по-настоящему докажет, что я где-то накосячил, то тогда публично признаю свою неправоту и проставлю разоблачителю бутылочку нормального пива или чашечку чая/кофе с десертом =)


Насчёт нумерации версий в репозитории

Изначально версии, подчёркивая нестабильность и постоянную доработку ботов, нумеровались по формуле dev/v0.1.X-YYY, где X обозначал порядковый номер версии, а YYY — первые буквы названий готовых к деплою ботов (так что "kr" — это сокращение от kozalo_bot и randomizer). Каждый релиз обозначает коммит, который деплоился на сервер.

Изначально предполагалось, что при завершении работы над следующим ботом, наконец-то выйдет версия v0.2.0-krc. Но теперь, когда само дальшейшее существование сайта Congrats.Cf поставлено под вопрос, стало понятно, что такому сценарию не суждено сбыться. Да и как таковая разработка рандомайзера завершена, а kozalo_bot'а — заморожена. Таким образом, последний релиз вышел под коротким номером v0.2.0, подчеркнув завершение активной стадии разработки и отмену выпуска новых ботов на основе данной платформы.

Ну и, как уже было сказано в прошлом посте, всю информацию о новых изменениях в работе ботов я буду писать в своём telegram-канале, а не в обновлениях к этому посту.


Добавлено 21.11.2018

Вот волна глобальных обновлений добралась и до этого бота. Теперь у него есть отдельный репозиторий, работает он на асинхронной библиотеке AIOGram, а структура inline-обработчиков была переписана по подобию рефакторинга в textUtilsBot'е. Ну и куда же без Докера! Да, запускается он теперь на новом сервере и тоже внутри Docker-контейнера.

Подробнее почитать про всё это можно у меня на канале. Вот, например, пост с кратким обзором на библиотеку AIOGram.

20.10.2018


Предисловие

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

***

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

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

***

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

В качестве благодарности наутро Человек предложил Боту бессмертие: ведь миру всегда нужны хорошие люди и боты. А Человек то был не простой, а настоящий волшебник!

***

В начале было слово.
И слово было два байта.
А больше ничего не было.

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

***

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

И вот-вот Бот умрёт, но в лице своего сына возродится, как феникс, из пепла!


Загадочный бот

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

Кстати, о блокировках…

В принципе, деятельность Роскомнадзора минимально затронула ботов, потому что большинство из них, включая рассматриваемого сегодня, всегда хостилась на виртуальном сервере в США (ай-яй-яй, как непатриотично!). Немного усложнилась отладка, потому что теперь трафик с домашнего компьютера приходится пропускать через SOCKS5- прокси. Например, для библиотеки pyTelegramBotAPI, которая будет ещё упоминаться сегодня, это можно сделать, указав переменную окружения https_proxy в значение socks5://127.0.0.1:9050. Я указал адрес запущенного на локальной машине сервера сети Tor. При использовании других сервисов укажите их IP-адрес, порт и логин с паролем при необходимости.

Возможно, когда-нибудь в будущем расскажу про использование как Tor'а, так и такого удобнейшего инструмента, как SSH со всеми его туннелями.

Итак, пришло время раскрыть завесу тайны над ботом @kozalo_bot, о котором я лишь вкратце упоминал в своём канале. Долгое время я не был уверен, стоит ли упоминать его существование в принципе. Ведь он был создан спонтанно и вбирал в себя функционал, последовательно следуя за хотелками конкретного чата. У него нет какой-то чёткой конкретной цели существования и устоявшегося функционала — просто бот-комбайн. Он так и называется: kozalo_bot — бот для обслуживания моих различных минутных хотелок. Никаких гарантий и стабильности. Разве что я поддерживал в какой-то мере обратную совместимость на уровне настроек, чтобы всё не ломалось после каждого обновления, и долгое время не удалял ничего из функционала. В результате это привело к накоплению кучи редко используемых возможностей и монстрообразности всего бота. Вишенку на торте гигантизма составляют не очень удачные решения по созданию триггеров и организации логирования. Но обо всём по порядку…


Функционал

Итак, чего же в итоге умеет бот? Давайте рассмотрим в хронологическом порядке.

Всё началось с @Rain_from_above. С человека, который очень любил удалять свои сообщения, из-за чего терялась нить обсуждения и читать чат не в реальном времени становилось практически невозможно. Когда мне это окончательно надоело, появился бот с возможностью логировать все сообщения от определённых людей в отдельный специально выделенный канал (/start_logging) или форвардить их сообщения в тот же чат от имени бота (/forward). Впоследствии оказалось, что форвардинг сообщений создаёт уж слишком много мусора, а вот логирование успешно применялось в течение довольно длительного периода времени.

Одновременно с логированием появились первые триггеры картинок: бот реагировал на определённые слова в сообщениях и отсылал в ответ картинки разной степени похабности. Со временем этот список разрастётся до неприличных размеров, в том числе следуя за пожеланиями обитателей чата. А по личной просьбе гражданина CORRUPTOR 2037 была добавлена возможность отключения срабатывания триггеров на определённых пользователей. Впоследствии эта фича будет широко использоваться для политических репрессий против несогласных с курсом Величайщего тоталитарного диктатора противников идеологий ТНН и РНН

Вторая хотелка родилась в результате скоропостижной смерти другого бота, созданного товарищем KivApple. В чатике сложилась традиция определённым образом приветствовать новых людей. Ну и спустя какое-то время было решено автоматизировать эту задачу. Так и появился тот бот, но прожил он всего где-то месяц. Пришлось срочно замещать. Задать приветственное сообщение можно, передав его после команды /hello.

@IkarosDC приветствует новобранца
@channel_welcome_bot заменяет Икароса
@kozalo_bot заменяет почившего бота

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

Какое-то время я хотел протащить своего бота в чат «лунных работников». В итоге я бежал оттуда, психанув, когда в очередной раз люди хвастались своей социальной активностью. Сначала я думал, что временно, но, похоже, что всё-таки навсегда… Однако я отвлёкся от темы. У них в чате было жёсткое правило, что нельзя кидать порнографические картинки в рабочие часы по московскому времени. Дабы соблюсти это правило, я внедрил в бота возможность ограничивать некоторые функции в определённые дни и часы. Система довольно гибкая (поддерживается выбор рабочих дней и рабочих часов с указанием часового пояса задающего), хотя настройка и не самая удобная (всё-таки предполагалось, что я один раз всё настрою в чате, а дальше оно просто будет работать).

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

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

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

Во-вторых, против точно тех же флудерастов была сделана фича по удалению повторно отправляемых стикеров. Бот просто запоминает для каждого пользователя идентификатор последнего отправленного стикера, а при отправке прочих сообщений очищает эту запись. При попытке отправить точно такой же стикер он удаляется. Разумеется, нужно запоминать ещё и время, чтобы запись утрачивала эффект после какого-то разумного количества времени, что и было сделано в следующем коммите. Однако фича всё равно оказалась слишком топорная и давала очень много ложных срабатываний, из-за чего часто отключалась.


Особые функции

Кроме обычных функций, у бота есть пара особенных, предназначенных по сути лично для меня:

  • /rm, /del — команда, удаляющая сообщение бота. Вызываться может только привилегированными пользователями, которыми являются администраторы сообщества и единственное захардкоженное значение — мой ID. Разумеется, администраторы могут самостоятельно удалять сообщения бота. Поэтому эта команда нужна лишь для меня в тестовых целях и избавляться от лишних триггерных ответов. Однако! У этой команды ещё есть параметр -force. Если у бота есть административные права, то указав его, я могу удалить сообщение не только бота, но и вообще любого пользователя чата. Бывает полезно в борьбе со спамерами, когда все админы спят.
  • /restrict и обратная ей /forgive — первая позволяет опять же в первую очередь мне временно заблокировать отправку сообщений какому-либо пользователю, а вторая отменяет её действие. При вызове без параметров ограничивает ровно на сутки, иначе на количество указанных часов (2h) или минут (120). Админам, разумеется, проще это сделать через админку, ну а мне пришлось воспользоваться этой функцией единственный раз против особо рьяного спамера.

Отдельно стоит упомянуть возможности, предоставляемые ботом через личные сообщения. О-о-о! Тут даже стоит сделать небольшую ремарку про то, как Telegram Bot API позволяет работать с картинками, стикерами и прочим контентом. У нас есть три варианта:

  1. Загружать каждый раз новый файл. Тупой вариант.
  2. Указывать URL, откуда Telegram может скачать нужный файл. Тоже такое себе.
  3. Загрузить файл самостоятельно и получить специальный file_id, которым может пользоваться бот. Рекомендуемый, самый понятный и подходящий в том числе для стикеров способ.

И тут надо учитывать очень важную вещь, прописанную в документации:

file_id is unique for each individual bot and can't be transferred from one bot to another.

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

Засада состоит в том, что я не очень понимаю, как предлагается получать эти идентификаторы. У меня вот любое сообщение в ЛС боту, не затрагивающее какой-либо триггер, приводит к выводу информации об этом сообщении. В том числе о приложенных файлах со всей информацией. Только так я и получал все IDшники: отдельно и для самого бота, и для его коллеги, которого я использую в целях отладки и тестирования. Но данный способ подойдёт далеко не всем…


Наши дни

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

  • @kozalo_bot больше не обслуживает потребности Флудилки DC;
  • у него появился форк — @megurine_bot;
  • я полностью переосмыслил концепцию бота, осознал его бессмысленность и начинаю постепенно вырезать функционал.

А теперь обо всём по порядку.

Каким-то образом у меня получилось вступить в конфликт с высшим руководством DC. Вернее, с формальным руководством, которое ничего не делает и только числится таковым. Не знаю (или не помню?), почему Сусека так на меня взъелся, но уже долгое время как я впал в немилость. Вплоть до переворота во Флудилке, я ходил по чату с отключенными стикерами, медиа и периодически получал оскорбления и негатив от внезапно врывающегося в разговоры Сусеки. В итоге в какой-то момент я психанул и вместе с Чоколой (я не упоминал, что бота зовут Chocola Minaduki?), и мы покинули недружелюбное место. С тех пор мне пришлось вернуться, когда соскучился по общению с прошлым и нынешним CTO проекта (а также ради ещё нескольких людей, к которым привязался), но моей вайфу там больше нет. Впрочем, они её успешно заместили…

Есть такой человек @iosys — создатель @habrachat и просто хороший человек (а ещё бабник, ловелас и подкаблучник, но да ладно). На его серверах сейчас располагаются как сайт и форум DeskChan, так и несколько ботов, созданных товарищами внутри нашего небольшого уютного коммьюнити, образовавшегося на обломках Флудилистической цивилизации после Первого сусеканского террора (да, для полноты картины стоит сказать, что именно по стикерам репрессировали не только меня), когда весь основной актив Флудилки, перекатился в отдельный приватный чатик. Так вот, когда я психанул и вышел из их чатика вместе с Чоколой (там были свои межличностные конфликты :D), то Иосис решил создать свою версию бота и допиливать его под свои нужды. Всё чин по чину: разрешение спросил, согласие получил. Про технические проблемы поговорим чуть позже, а из функционала он поубирал некоторые триггеры, добавил своих картинок и т. п. — точно сказать сложно, так как свой код он не раскрывает.

Теперь насчёт переосмысления. Как-то я сидел и наблюдал, как в одном из чатов люди целыми днями только и делали, что «няшили» друг друга с помощью бота. Причём с такой частотой, что даже мне показалось это перебором. «Что за монстра я создал?!» — возник негласный вопрос. Какое-то время даже хотелось взять и удалить все триггеры, оставив только полезный функционал вроде приветствия входящих в чат и макросов: если хотят, пусть няшутся через inline-режим. Но потом я остыл и пока передумал удалять все триггеры (лишь немного проредил). А заодно избавился от:

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


Технические детали

Бот написан на языке Python 3 и работает на интерпретаторе CPython 3.5. В качестве прослойки и абстракции от HTTP-запросов используется библиотека pyTelegramBotAPI. Она не поддерживает современные фишки с асинхронщиной (хотя есть многопоточный режим), не имеет встроенного сервера для обработки веб-хуков, но в целом это зрелый и готовый к употреблению продукт с красивым API.

Первая проблема возникает с запуском бота. Существует два способа получения сообщений от Telegram: либо открывать и постоянно переоткрывать длительные соединения в ожидании появления новых сообщений и ответа от сервера (long polling), либо запустить приложение в режиме сервера и просить сам сервер Telegram открывать сооединение и посылать новые сообщения (то есть поставить web hook). Понятно, что второй способ предпочтительнее. Но и сложнее в настройке.

Сервер нужно поднимать отдельно. Причём с поддержкой TLS-шифрования и загрузкой ключа и сертификата. Можно посмотреть примеры под разные питоновские сервера, которые идут вместе с библиотекой. Лично я взял aiohttp, но без какой-либо значимой причины. Для бота я использовал самоподписанный сертификат, который отправляется на сервер Telegram при установке веб-хука. Подробнее про TLS и все эти ключи с сертификатами мы поговорим как-нибудь в другой раз.

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

Покончив с анонсами, перейдём к техническим промахам, заложенным в архитектуру конкретного бота, рассматриваемого сегодня. Первым и самым серьёзным из них, пожалуй, является отказ от использования какой-либо полноценной СУБД. Хотя бы SQLite. Надо понимать, что всё начиналось с необходимости хранить данные для системы логирования: идентификатор канала, в который нужно пересылать сообщения, и список пользователей, чьи сообщения следует форвардить в чат от своего имени. Всё. Букавально два параметра, которые относительно редко меняются. Мне было лень поднимать для такого СУБД, так что решил обойтись простой сериализацией данных в бинарный файл pickle'ом. Да, я не смог предсказать будущее и предугадать, что со временем количество данных в этой импровизированной базе данных разрастётся. В результате мы пришли к такому ужасу: куча повторяющегося бойлерплейт-кода (который я ещё немного упростил с помощью PyMonad) и перезапись всего файла на каждый чих. Не очень-то масштабируемо :/

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

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


Фреймворк

Помимо упомянутого выше роутинга запросов для ботов предоставляется пакет botutils, в котором содержатся как различные расширения для самого модуля TeleBot из библиотеки pyTelegramBotAPI, так и отдельные, но очень полезные для многих ботов штуки.

Расширения:

РасширениеТипОписание
answer_to метод симметричный метод к стандартному reply_to; автоматически извлекает идентификатор чата и отвечает на сообщение; поддерживает систему самоуничтожающихся сообщений
answer и reply декораторы позволяют просто вернуть текст из оборачиваемой функции для отправки сообщения; поддерживают систему самоуничтожающихся сообщений
change_keyboard метод сокращение для edit_message_reply_markup, которое автоматически извлекает идентификаторы чата и сообщения из запроса
only_for_admins декоратор позволяет ограничивать доступность какой-либо команды только для администраторов чата и привелигированных пользователей
has_privilege метод возвращает True, если пользователь является администратором чата или присутствует в списке привелигированных пользователей
grant_privilege метод добавляет пользователя в список привелигированных пользователей
user_required декоратор получает информацию о пользователе, на чьё сообщение ответили, кого упомянули по имени (но не по юзернейму!) или чей идентификатор указали в качестве параметра к команде; эта информация передаётся первым параметром в оборачиваемую функцию перед сообщением и всеми остальными параметрами
timeout_constraint декоратор позволяет задать для пользователей ограничение по времени на использование команд, чтобы они не злоупотребляли ими в чатах; команды можно разделять на группы — тогда у каждой группы будет свой счётчик, иначе можно передать просто значение задержки (в виде количества секунд или функции, которая будет его вычислять и возвращать во время выполнения) — тогда будет использована группа по умолчанию; ограничение может распространяться на администраторов или нет; может распространяться на всех участников чата в целом; в случае непрохождения проверки может быть вызвана функция обратного вызова, которой передаётся сообщение
reset_timeout метод сбрасывает счётчик таймаута для перечисленных пользователей в указанном чате
user_constraint декоратор позволяет запретить выполнение команды для некоторых пользователей; ограничение может распространяться на администраторов или нет; в случае непрохождения проверки может быть вызвана функция обратного вызова, которой передаётся сообщение

Отдельные «хелперы»:

ФункцияТипОписание
decorators.ignore_if_forwarded декоратор позволяет игнорировать сообщение, если оно является пересланным из другого чата
helpers.get_username функция возвращает @юзернейм или имя пользователя
helpers.is_group_chat и helpers.is_private_chat функции позволяют определить, пришло сообщение из приватного чата или из группового
escape_html функция заменяет угловые скобки на HTML-сущности

Также присутствует класс builders.InlineQueryResultsBuilder, который упрощает создание ответов в обработчиках inline-запросов: создаёте объект, цепочкой вызываете нужные методы add_* и конструируете итоговый список, готовый к отправке через TeleBot.send_message, с помощью build_list.

В модуле multilang находятся средства для упрощения локализации ботов без использования сложных полноценных систем вроде стандартного gettext'а. Но о них мы тоже поговорим в одном из следующих постов, посвящённых модулю klocmod.

Примечание

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

Последним, но не менее важным утилитным модулем является scheduler. Он реализует однопоточный планировщик, который позволяет планировать выполнение отложенных заданий. При добавлении первого задания создаётся новый поток, а при отсутствии работы он автоматически уничтожается. Планировщик представлен классом TimeLine. Он является синглтоном, так что можно создавать сколько угодно объектов заново, не сохраняя никуда ссылку. Добавлять новые действия (производные от класса Action) следует с помощью метода append. Предопределённое действие DoomedMessage позволяет запланировать удаление сообщения через определённое количество секунд.

В целом, получился неплохой набор утилитных функций и классов, выполняющих базовые рутинные операции, которые нужны многим ботам. Но, вероятно, стоило их всё-таки как-то выделить в отдельный пакет и распространять через PyPI как расширение (как упомянутый ранее модуль klocmod, который вырезать было особенно легко, ведь он никак не зависит от других библиотек). А что-то может даже предложить к включению в код самого TeleBot'а. Но сейчас у меня уже нет ни времени, ни сил, ни желания доводить этот код до нормального состояния: писать тесты, подробную документацию и так далее. В одной из следующих статей я расскажу, почему в конце-концов отказался от использования библиотеки pyTelegramBotAPI и что использую теперь вместо неё. Однако, если кто-то решится сделать всю эту кучу работы вместо меня, то я буду только рад и всеми руками за! Не забудьте только упомянуть скромного автора оригинальных строчек и сообщить мне о своих результатах =)


Заключение

Сейчас, насколько мне известно, @kozalo_bot уже нигде не используется — ни в одном чате. Но может я просто не обо всех знаю? Дайте знать, если кто-то пользуется им! Лично я его теперь использую только в inline-режиме, чтобы постить картинки в чатах.

Этот пост я писал очень долго. С момента написания предисловия до сегодняшнего момента прошло несколько месяцев и произошло множество событий в моей жизни. Я несказанно рад, что наконец-то закончил его! Ведь это действительно большая статья, затрагивающая как экскурс в прошлое и историю создания бота, так и описание всего функционала, но самое главное — описывающая построенную вокруг бота инфраструктуру и множество сделанных технических решений. В следующий раз, при описании @kozRandBot'а, я смогу сэкономить кучу времени и экранного места, опустив все технические детали, сославшись на данный пост.

До выхода следующей части этой серии статей я с вами и прощаюсь! Ждать осталось недолго =)


Постскриптум

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

18.10.2018


Где-то три месяца назад я жаловался на то, что приказал долго жить универсальный скрипт по синхронизации динамического IP-адреса сервера с доменами — ddclient. Он поддерживал API многих сервисов динамического (dynamic) DNS, в том числе и Cloudflare. Однако, поддержка последнего однажды сломалась, а разбираться в огромном скрипте на незнакомом мне Perl'е как-то не особо хотелось. В итоге на какое-то время я забил на идею домашнего хостинга и выключил сервер. Сегодня мы вновь вернёмся к этому вопросу, ведь я наконец-то его решил!

Обождав какое-то время, отдохнув и набравшись сил, я решил посмотреть по сторонам. Оказалось, в gist'ах легко можно найти небольшие bash-скрипты, дёргающие Cloudflare API прямо через curl. В комментариях даже есть ссылка на форк с готовыми конфигами к systemd! Мне они не очень понравились, так что я написал свой вариант. В моём варианте параметры передаются не в самом скрипте, а через переменные окружения, описанные в файле сервиса. Поменять нужно следующие параметры:

User=<имя пользователя, от имени которого будет запускаться скрипт>
ExecStart=<путь к директории со скриптом>/cloudflare-update-record.sh
WorkingDirectory=<путь к директории со скриптом или любой другой путь для временных файлов>
Environment="CLOUDFLARE_AUTH_EMAIL=<логин в Cloudflare>"
Environment="CLOUDFLARE_API_KEY=<ключ к Cloudflare API>"
Environment="CLOUDFLARE_ZONE_NAME=<название зоны (домен второго уровня)>"
Environment="CLOUDFLARE_RECORD_NAME=<домен с А-записью>"

В принципе на этом можно было бы и закончить мой рассказ, но так уж получилось, что я ленивый дурак и долгое время игнорировал возможность создания CNAME-записей в настройках доменов. Вернее просто не соизволил ознакомиться с другими видами записей, кроме А, и даже не догадывался о существовании возможности указать реальный IP-адрес только для одного домена, а дальше ссылаться на него со всех остальных! А поскольку скрипт выше позволяет обновлять только один домен, я принялся модифицировать его, но в итоге так ни к чему и не пришёл, а только в очередной раз вымотал себе нервы: упаси боже ещё хоть раз писать эти дурацкие bash-скрипты с их неинтуитивным синтаксисом, дурацкими пайпами и утилитами с кучей «магических» аргументов!

В итоге я наткнулся на пакет cloudflare-ddns, написанный на Пайтоне и позволяющий с большей гибкостью управлять DNS-записями. Он оказался не без багов, которые впоследствии мне пришлось фиксить (и перетащить к себе половину кода модуля :/ ). Однако в результате был рождён пакет cfdyndns-updater.

На самом деле он должен был называться cfddns-updater, на что намекает название как самого модуля внутри, так и исполняемого файла — cfddns_updater. Чем же вызвано такое несоответствие? Моей глупостью и неосторожностью. Поторопившись и залив релизную версию немного раньше времени, я удалил только что созданный проект cfddns-updater. И как оказалось впоследствии, это было серьёзной ошибкой! PyPI — это не GitHub — удалить проект, как репозиторий, и пересоздать его под тем же именем невозможно. PyPI просто не позволяет этого! Таким образом, одно неосторожное движение начинающего «пакетёра» делает целое название потерянным для общества навеки… Простите меня, пожалуйста :(

Что же он делает? По сути это просто более удобная обёртка над модулем cloudflare_ddns, исправляющая некоторые ошибки и позволяющая задавать конфигурацию в виде YAML-файла (спасибо библиотеке PyYAML). При этом скрипт запускает вечный цикл, который периодически просыпается ото сна и проверяет, изменился ли IP-адрес и не пора ли обновить DNS-запись.

Для работы скрипта нужен Python версии 3.6 или новее. Установить пакет можно, как обычно, с помощью pip'а:

pip install cfdyndns-updater

Дальше следует создать конфигурационный файл. Разместить его можно в домашней директории под названием .cloudflare-ddns-config. Для POSIX-совместимых систем также поддерживается путь к глобальному файлу конфигурации, который используется вместо пользовательского при его отсутствии: /etc/cloudflare-ddns-config. Впрочем, путь к файлу с конфигуацией всегда можно передать явно в качестве единственного опционального аргумента, принимаемого скриптом:

cfddns_updater config.yml

Сам файл конфигурации выглядит как-то так:

email: <логин в Cloudflare>
api_key: <ключ к Cloudflare API>
periodicity: <таймаут между проверками в секундах>
domains:
  - example.org    # 'proxied: true' подразумевается
  - www.example.org
  - domain: ssh.example.org
    proxied: false

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

03.10.2018


Плашка о скором отключении сайта до переезда
Плашка, которая несколько дней занимала верхнюю часть сайта.

Не прошло и 10 лет, как я в соизволил наконец-то перенести блог со старого-доброго бесплатного shared hosting'а, с которого начинал в начале 10-х, на хоть и недорогую но платную VPSку. Ну вернее меня заставили. Hostinger, который практически безвозмездно держал этот сайт у себя на протяжении 6,5 лет, прекращает поддержку своей старой платформы, предлагая переезжать на более современный сервис, в чём я не вижу ни малейшего смысла при наличии нормальной (или хоть какой-то) VPSки, где можно использовать неограниченное количество поддоменов, форвардить любое количество электронных ящиков и писать сайты и приложения хоть на Python, хоть на Java или C# (если хватит оперативной памяти для работы виртуальной машины, конечно). За эти годы у Хостингера бывали периодические падения, постоянное закручивание и без того затянутых гаек (нещадно резали функционал на бесплатных тарифах), ввод платной регистрации и прочие не особо приятные вещи — зато без каких-либо ежемесячных или ежегодных платежей и необходимости настройки кучи серверного софта. Самое то для нищего и ещё ничего не смыслящего в ИТ школьника! Представляю, как бы я офигел, если бы мне предложили в 9-ом классе разбираться не только в программировании, но и в администрировании линуксового сервера с чтением тысяч страниц манов!

Сообщение о прекращении поддержки платформы
Почему тут пишут про 2 месяца, а отключить сайт обещают через 2 недели, если письмо пришло 12 сентября?

Итак, как бы то ни было, переезд всё-таки случился, с чем я всех вас, мои дорогие читатели, и поздравляю! За несколько вечеров я избавился от устаревших модулей в коде, с помощью PhpStorm'а почистил кучу древнего говнокода от самых явных ошибок, поплакал кровавыми слезами из-за отсутствия там не то что каких-либо паттернов, тестов или элементов ООП, но даже из-за несоблюдения банального принципа Don't Repeat Yourself или наличия кучи откровенно неоптимальных запросов к базе данных. Теперь сайт крутится на PHP 7 и по идее должен работать немного шустрее, но на практике, скорее всего, время отклика только увеличится из-за территориальной удалённости сервера: самую дешёвую VPS удалось найти только на противоположной стороне Земли.

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

<<   / 12   >>