Pull to refresh

Современное программирование: взгляд после 25 лет перерыва или как я писал бота для Телеграм

Reading time 7 min
Views 29K
Задача кажется простой, когда ничего про нее не знаешь и когда решил.

В один “прекрасный” день, я понял, что мне не интересно сидеть на многочисленных форумах, а хочется создать свой канал и делиться “мудростью”.

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

Прочитал первый попавшийся в инете самоучитель на 149 страниц. Я знаком с Pascal, FoxPro, Interbase и даже (ха-ха 3 раза) 20 лет назад продавал свои программы, а потом как-то не сложилось, ушел в торговлю. Работа программистом мне сильно помогла в постановке задач для кодеров кстати. Но вернемся к Python, кажется, ничего сложного, ведь и на BASIC программировать можно было и это не мой институтский диплом с программно-аппаратным комплексом генератора поверки МИГа на Assembler. Одна проблема, всё это было давно, так что возвращаясь к заголовку — кажется что просто, потому что пока ничего не знаю про задачу, но попробовать стоит.

Я решил, что удобнее всего делать проект со смартфона на Андроид, ибо он всегда под рукой. Итак ставим:

  1. Pydroid 3 — IDE for Python 3 Собственно Питон для Андроид.
  2. @BotFather, В Телеграм устанавливаем отца всех ботов — он понадобится чтобы получить идентификатор вашего бота и сделать основные настройки.
  3. Rebootr Приложение для запуска проекта на heroku.
  4. GIT Приложение для онлайн-хостинга репозиториев.
  5. Windscribe Без VPN никуда?
  6. Termux Эмулятор терминала и среды Linux.

И что в сухом остатке? Программировать на смартфоне можно в теории, но невозможно на практике. Максимум проверить код, исправить ошибку, исследовать работоспособность.

Такое длинное вступление, а что я хотел получить от своего бота. Сначала мне показалось достаточным добавлять в конце сообщения Хэштег. Потом пришла мысль “накрыть” его ссылкой на мой канал, так при репосте будет дополнительная завлекуха на канал источник. Потом, добавил автоматизацию ссылки на источник, откуда я беру сообщение. Далее сделаю лирическое отступление. Реклама, конечно двигатель прогресса. Но иногда ее количество зашкаливает. Телеграм был выбран мной в том числе и из-за того, что тут есть возможность бороться с последней. Я уважаю читателей своего канала и поэтому интересные новости, найденные на просторах инета чищу от рекламы и отправляю в канал. В то же время я уважаю авторов и практически всегда даю ссылку на источник в виде “Читать далее...”. По причине нелюбви рекламы я скачиваю контент с Ютуба и выкладываю его в своем канале в виде видеофайла. В один определенный момент бот, который качал видео сошел с ума и стал присылать мне рекламу каждый час. Так в моем боте появилась возможность скачивать видео с ютуба. Недавно кстати познакомился с автором этого бота, он был сильно удивлен, т.к. по его словам отправляет рекламу “всего” 20 раз в месяц. Тоже самое произошло и с ботом, который делает водяной знак — он был отправлен в топку, а у меня появилась возможность делать водяной знак.

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

  1. Мне действительно помогли статьи на Хабре. Поэтому не буду переписывать как и что установить. Тут все есть. Кстати, я обращался в личку к авторам и мне ни разу не отказали в помощи.
  2. github Сервис онлайн-хостинга репозиториев, обладающий всеми функциями распределенного контроля версий и функциональностью управления исходным кодом. Букварь
  3. heroku — облачная PaaS-платформа, поддерживающая ряд языков программирования. Очень быстро от него отказался.
  4. pyTelegramBotAPI — Одна из основных библиотек при написании бота для Телеграм.
  5. Учебник по написанию ботов
  6. Python 3 для начинающих
  7. Боты: информация для разработчиков
  8. Справочник по HTML
  9. Без VPN никуда?

Второе лирическое отступление или война план покажет. Когда я начал писать бота, первым делом посмотрел чужие коды. Если нет каментов понять можно с трудом:

Земля тряслась — как наши груди,
Смешались в кучу кони, люди,
И залпы тысячи орудий
Слились в протяжный вой…

Лермонтов писал о коде. В куче лежат функции, декораторы. Нет красоты кода, о ресурсах никто не заботится. Хотя красоту скорее всего может увидеть извращенец мазохист. До меня очень быстро дошел смысл фразы знакомого программиста “Посмотри прогу, может быть разберешься”. Самый главный взрыв мозга у меня был, когда я наконец то осознал, что код событийный, а не последовательный. Это другой уровень.

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

file_info = bot.get_file(message.photo[-1].file_id)

А что документация? Идем к первоисточнику

image

image

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

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

font = ImageFont.truetype("Pillow/Tests/fonts/FreeMono.ttf", width//20)
pos = (width//4, height - height//10)
text = skanal
drawing.text(pos, text, fill=black, font=font)
pos = (1 + width // 4, 1 + height - height // 10)
drawing.text(pos, text, fill=black, font=font)
pos = (2 + width // 4, 2 + height - height // 10)
drawing.text(pos, text, fill=black, font=font)

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

Еще один непостижимый для меня момент произошел с картинкой после обработки с помощью библиотеки PIL (сразу после водяного знака). Отправляю ее в свой бот:

 with open(photo_path, 'rb') as fi:
    bot.send_photo(message.chat.id, fi)

Все замечательно, картинка нравится. Затем мне требуется добавить к картинке комментарий и посмотреть, а красиво ли все смотрится вместе? Пишем:

bot.send_photo(message.chat.id, message.photo[-1].file_id, caption='Какая красота')

В бот почему то уходит первоначальная, необработанная картинка. Хорошо, попробуем обмануть: getupdates.offset -1 толку никакого, Телеграм уверен, что это одно и тоже фото. Хорошо, делаем так:

 with open(photo_path, 'rb') as fi:
    info = bot.send_photo(message.chat.id, fi)

Переписываем:

bot.send_photo(message.chat.id, info.photo[-1].file_id, caption='Какая красота')

Результат тот же — выводится первоначальная картинка. И только замена message в первом аргументе на info дала желаемый результат.

Также тут приведу кусок интересного кода начального уровня загрузки с ютуб (NB: без проверки на ошибки):

elif message.entities:             # Работа со ссылками pkanal = 6
        for item in message.entities:
           if item.type == "url" and message.text.find('  ') == -1:
               if 'youtube.com' in message.text or 'youtu.be' in message.text:           #  Загружаем с Ютуб
                   ydl_opts = {'outtmpl': '/tmp/f.mp3', 'preferredcodec': 'mp3', 'max_filesize': 60000000}
                   link_of_the_video = message.text
                   with youtube_dl.YoutubeDL(ydl_opts) as ydl:
                       ydl.download([link_of_the_video])
                   bot.delete_message(message.chat.id, message.message_id)

                   if os.path.exists('/tmp/f.mp3'):  # файл есть
                       video = open('/tmp/f.mp3', 'rb')
                       bot.send_video(message.chat.id, video)
                       os.remove('/tmp/f.mp3')
                       pkanal = 6
                   else:       # файла нет
                       bot.send_message(message.chat.id, 'Слишком большой файл', parse_mode='html', disable_web_page_preview=True)

Для меня было камнем преткновения, что entities это массив массивов и надо “пробежаться” по всему массиву чтобы бот понял, что работаем со ссылкой. Еще выяснилось, что пользователи отправляют ссылку в бот как “Поделиться” из Ютуб поэтому в примере прописано еще и “youtu.be”. Как сразу отправить в Телеграм файл я не придумал, поэтому мы его сохраняем, отправляем и затем удаляем. Во время тестирования мне сразу указали на то, что люди начнут качать гигантские файлы — позднее пришлось ввести ограничение.

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

if message.from_user.id in [adm_obj.user.id for adm_obj in bot.get_chat_administrators(chat_id)]:

Тут получаем от канала список Администраторов и смотрим является ли автор сообщения тоже Администратором.

Пара слов о том, как пришлось воевать с роскомнадзором. Поскольку доступ к API заблокирован, для разработки бота локально нужно как-то пропустить трафик через наших доблестных защитников. Сделать это можно двумя путями — через VPN или через proxy. Самым простым и быстрым и “нормальным” способом можно считать ssh тоннель: устанавливаем соединение между клиентом и proxy сервером, получаем на локальном хосте порт куда можно посылать трафик с нашей стороны, а выйдет он с другой стороны (уже где-то в Германии). Для удобства можно добавить некое подобие автоматизации этого соединения — скрипт и ярлык на рабочем столе, которым его запустим, при необходимости. Под “нормальным” способом здесь я понимаю ситуацию, где входную и выходную точку контролируем мы сами — слева наш ноутбук, справа VPS в Германии. по середине трафик могут перехватывать сколько угодно, он шифрованный, а на выходе мы оказываемся в юрисдикции другого государства, и законы РФ к нему не применимы. Как плюс мы получаем дополнительно гарантию того что proxy сервер не будет изменен или выключен, пока мы не сделаем этого самостоятельно. Т.е. в отличие от всяких дядиных VPN у нас появляется спокойствие и уверенность в завтрашнем дне.

Скрипт

/home/user/proxy.sh

Код скрипта

#!/bin/bash
ssh -f -D 1080 user@12.34.56.78 sleep 72000

После того как у нас соединение с сервером установлено, а порт открыт, нам нужно как-то направить туда трафик. Идя по пути наименьшего сопротивления и чтобы не думать о том как настраивать proxy в IDE\docker\python можно сделать одну настройку на всех, такой настройкой будет выступать proxychains. Если запустить любой софт с помощью этой утилиты, то она перенаправит трафик через цепочку proxy которые прописаны в конфиге.

/etc/proxychains.conf

В нашем случае это одна цепочка и написать ее не составляет никакого труда.

socks5 127.0.0.1 1080

и еще один костыль, который был предпринят, чтобы только не настраивать VPN (сарказм) — это способ запуска питоновского приложения из PyCharm. В простом случае чтобы запустить через proxychains приложение достаточно написать proxychains app.py и всё. Но в IDE обязательно требуется указать интерпретатор. Обойти это просто — создаем новую «run configuration», выбираем shell script. и заполняем поля

image

После чего запуск с точки зрения IDE приобретает такой вид: proxychains python3 app.py — собственно это нам и надо. Таким образом, когда мы хотим заняться проектом все что нужно сделать — запустить скрипт на рабочем столе, а потом для запуска бота нажать кнопку “play" в ide. Всю остальную магию сделают proxychains и ssh.

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

Если вы уже запустили мой бот, то увидели, что есть хэштег #Реклама. Как же так? — спросите вы. А тут я пошел на поводу у конечных пользователей — многие выкладывают рекламу в своих каналах и быстро привыкнув к оформлению сообщений с помощью бота, попросили добавить. Рекламу можно не любить, бороться с ней, но это суровые и необходимые для пользователей реалии.

Проект некоммерческий, поэтому думаю можно назвать сам бот @SGK_espace_bot.

А тут видео как пользоваться
Буду благодарен за любую конструктивную критику.
Tags:
Hubs:
+28
Comments 39
Comments Comments 39

Articles