March 8, 2020

Превращаем аватарку в Telegram в часы

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

Подготовка

Для начала предлагаю создать отдельную папку для проекта, так как файлов будет несколько. И сразу же создадим три пустых файла со следующими названиями: main.py, config.py и utils.py. В первом будет основный скрипт, который будет всем управлять, во втором сохраним константы, а в третьем напишем всякие полезные утилиты, которые могли бы загромождать код в основном модуле.

Далее там же создадим папку resources, в которую поместим шрифт, скачанный с https://www.fonts-online.ru/font/DS-Digital. Переименуем шрифт на ds-digit.ttf, чтобы дальше ничего не путать.

А сейчас переходим в терминал/консоль и устанавливаем необходимые пакеты.

pip install telethon Pillow pytz

В случае, если у вас MacOS или Linux, то используйте pip3.

Получение api_id и api_hash

Идем на официальный сайт Telegram на https://my.telegram.org/. Там вас попросят ввести номер телефона и код подтверждения, который придет — делаем. Затем переходим в раздел API Development Tools. Думаю, там всё будет интуитивно понятно.

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

Теперь открываем ранее созданный модуль config.py и вставляем туда только что полученные данные в нужные переменные.

api_id = 123456
api_hash = 'qwerty123uiop456asdf789'
session_name = 'session'

Обратите внимание на то, что api_id — числовое значение, а api_hash — строка. Также мы добавили еще одну переменную session, которая будет определять имя файла, в котором будет хранится сессия.

Генерация текущего времени

Переходим в utils.py и начинаем писать код. Первым делом подключаем необходимые модули.

from PIL import Image, ImageFont, ImageDraw
from datetime import datetime
from pytz import timezone

Первый из них нужен для генерации картинок, вторые два — для работы со временем.

Далее напишем функцию, которая возвращает текущее время в формате "%H:%M".

def get_current_time():
    return datetime.now(timezone('Europe/Moscow')).strftime('%H:%M')

Важно отметить, что в данном случае я запрашиваю московское время, то есть часовой пояс 'Europe/Moscow'. В целом, можете посмотреть все доступные варианты по ссылке https://stackoverflow.com/questions/13866926/is-there-a-list-of-pytz-timezones и выбрать более удобный для вас.

Генерация картинки

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

Сначала объявим саму функцию и создадим сущность картинки.

def generate_image(text):
    image = Image.new('RGB', (500, 500), color='black')
    W, H = image.size
    draw = ImageDraw.Draw(image)

Обратите внимание, что мы создали переменные W и H, которые отвечают за ширину и высоту картинки — нам это сейчас понадобится. Итак, пустая картинка есть, далее вставляем текст текст.

    font = ImageFont.truetype(font='resources/ds-digit.ttf', size=212)
    wt, ht = draw.textsize(text, font=font)
    draw.text(((W - wt) / 2, (H - ht) / 2 ), text, font=font, fill='#157381')

В первой строчке создаем сущность ранее сохраненного шрифта, во второй получаем размер текста и в конце с помощью небольших математических вычислений рисуем текст ровно посередине. В метод draw.text() по порядку передаем координаты, где надо вставить текст, переданное в нашу функцию строковое значание, полученный шрифт и цвет (в моем случае #157381).

И в конце просто сохраняем результат.

    image.save('time_image.jpg')

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

current_time = get_current_time()
generate_image(current_time)

Если всё прошло без ошибок, то у вас появится картинка в папке с проектом.

Думаю, те две строчки можно удалить, они не понадобятся.

Примерно такой код должен получится.

from PIL import Image, ImageFont, ImageDraw
from datetime import datetime
from pytz import timezone


def get_current_time():
    return datetime.now(timezone('Europe/Moscow')).strftime('%H:%M')


def generate_image(text):
    image = Image.new('RGB', (500, 500), color='black')
    W, H = image.size
    draw = ImageDraw.Draw(image)

    font = ImageFont.truetype(font='resources/ds-digit.ttf', size=212)
    wt, ht = draw.textsize(text, font=font)
    draw.text(((W - wt) / 2, (H - ht) / 2 ), text, font=font, fill='#157381')

    image.save('time_image.jpg') 

Основной цикл

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

Для начала подключим модули. С компонентами telethon разбермся чуть позже. Из utils подключаем написанные нами функции. А также импортируем конфиг и модуль time, в котором будем использовать таймер.

from telethon.sync import TelegramClient
from telethon.tl.functions.photos import UploadProfilePhotoRequest, DeletePhotosRequest
from utils import get_current_time, generate_image
import config

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

def main():
    pass

if __name__ == '__main__':
    main()

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

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

previous_time = ''

Затем с помощью контекстного менеджера создаем сущность клиента, через которого будем отправлять все запросы. В аргументы передаем имя сессии, api_id и api_hash, которые ранее сохранили в конфиге.

with TelegramClient(config.session_name, config.api_id, config.api_hash) as client:

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

while True:
    if not previous_time == get_current_time():

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

current_time = get_current_time()
previous_time = current_time
generate_image(current_time)

С помощью клиента загружаем только что сгенерированную картинку в переменную.

image = client.upload_file('time_image.jpg')

А теперь самое интересное. С помощью ранее подключенных инструментов удаляем текущую аватарку.

client(DeletePhotosRequest(client.get_profile_photos('me')))

И ставим новую, только что сделанную.

client(UploadProfilePhotoRequest(image))

Результат вы видите сами. Весь код модуля main.py получился следующим.

from telethon.sync import TelegramClient
from telethon.tl.functions.photos import UploadProfilePhotoRequest, DeletePhotosRequest
from utils import *
import config
import time


def main():
    previous_time = ''
    with TelegramClient(config.session_name, config.api_id, config.api_hash) as client:
        while True:
            if not previous_time == get_current_time():
                current_time = get_current_time()
                previous_time = current_time
                generate_image(current_time)
                image = client.upload_file('time_image.jpg')
                client(DeletePhotosRequest(client.get_profile_photos('me')))
                client(UploadProfilePhotoRequest(image))


if __name__ == '__main__':
    main() 

Для запуска введите в терминале команду

python3 main.py

Заключение

По-моему, получился достаточно классный проект. А весь исходный код доступен на моем гитхабе: https://github.com/adreex/TimeChanger (буду рад, если поставите звездочку).

Если получу много обратной связи в комментариях здесь, либо прямо в личные сообщения (t.me/a1f20), то выпущу вторую часть, в которой реализуем прогресс дня в описании профиля, а также загрузим всё это на бесплатный хостинг.


Материал подготовлен образовательной организацией Python Academy.