Mastodon-бот, urllib3 v2.0.0 и TOTP

10 мая 2023
Mastodon-бот, urllib3 v2.0.0 и TOTP

В сегодняшнем дайджесте посмотрим, как создать бота для Mastodon, отпразднуем с сообществом выход urllib3 v2.0.0 и расскажем про реализацию алгоритма TOTP всего в 20 строках кода.

Разработка Mastodon-бота на Python

Илон Маск продолжает агрессивную монетизацию Twitter. Доступ к API стал платным и это поставило крест на создании бесплатных ботов для интерактивных коммуникаций. Свято место пусто не бывает — всё больше разработчиков выбирают в качестве платформы децентрализованную self-hosted социальную сеть Mastodon. Каждый сервер (участник сети) может иметь собственные правила и не подчиняется какой-то единой политике.

Чтобы попрактиковаться в создании Mastodon-ботов, можете попробовать инстанс botsin.space. Список правил, которые необходимо соблюдать людям и ботам описан в их Code-of-Conduct. Чтобы не изобретать велосипед, в качестве оболочки для API имеет смысл выбрать библиотеку Mastodon.py

Минимальный пример в стиле Hello, world! будет выглядеть так:

from mastodon import Mastodon

# Create an instance of the Mastodon class
mastodon = Mastodon(
    access_token='your_access_token_here',
    api_base_url='https://your.instance.url'
)

# Post a new status update
mastodon.status_post('Hello, Mastodon!')

Автоматический ответ на упоминания имени пользователя в toots (аналог твита в Twitter) можно выразить вот таким кодом:

# Define a function to handle mentions
def handle_mention(status):
    if '@your_bot_username' in status.content:
        mastodon.status_post('@' + status.account.username + ' Hello there!')

# Start streaming for mentions
mastodon.stream_user(handle_mention)

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

urllib3 v2.0.0 вышел в релиз

HTTP-клиент urllib3, о котором мы неоднократно упоминали в наших дайджестах, получил долгожданное обновление до версии 2.0.0. Выпущенный 12 лет назад, он стал настоящим сокровищем и был загружен более 8 млрд раз. За прошедшие годы разработчики пришли к выводу, что необходимо провести большой рефакторинг и результатом стала версия 2.0.0. Мы детально взглянули на релиз и готовы поделиться некоторыми выводами.

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

import urllib3

resp = urllib3.request("GET", "https://example.com")

print(resp.status)
# 200
print(resp.headers.get("Content-Type"))
# text/html; charset=UTF-8

Кажется, принцип KISS здесь был задействован на полную катушку. Второй важной и полезной возможностью стала встроенная поддержка JSON в API-интерфейсах запросов и ответов. Это автоматически упрощает код и позволяет взаимодействовать с API-интерфейсами HTTP через JSON:

import urllib3

resp = urllib3.request(
    "POST", "https://httpbin.org/anything",
    # The 'json' parameter encodes the JSON into the body
    # and sets the 'Content-Type' to 'application/json'.
    json={"key": "value"}
)

# The HTTPResponse.json() method decodes JSON in the body
# and loads the data into a Python object.
print(resp.json())

Отдельно декодировать JSON не требуется, мы сразу получаем на выходе объект с данными:

{
  "headers": {
    "Accept-Encoding": "identity",
    "Content-Length": "15",
    "Content-Type": "application/json",
    "Host": "httpbin.org",
    "User-Agent": "python-urllib3/2.0.0"
  },
  "json": {
    "key": "value"
  },
  "method": "POST",
  "url": "https://httpbin.org/anything"
} 

Красота, да и только. Ну и третье существенное изменение коснулось отсутствующего в Python понятия безопасности типов. Если функция принимает строковый параметр, а вы в неё отправите целочисленный, то интерпретатор не поднимет тревоги. Правда, функция при этом, скорее всего, тоже не заработает правильно. Начиная с версии 2.0.0, urllib3 обзавёлся строгими подсказками типов.

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

TOTP в 20 строках кода

Алгоритм TOTP (Time-Based One-Time Password) —«священный грааль» инициативы OATH (Initiative for Open Authentication). Этот алгоритм применяется в качестве составляющей двухфакторной или мультифакторной авторизации, дополняя традиционные механизмы на основе паролей, кодовых фраз, аппаратных токенов и прочих.

TOTP-коды остаются действительными лишь короткий отрезок времени, после чего «протухают». Штатно код меняется каждые 30 секунд. Если школьник Вася перехватит данные с помощью трояна, то у него будет лишь несколько секунд, чтобы ими воспользоваться. Не печалься, Вася! Ещё станешь хорошим хакером! А мы пока посмотрим на внутреннее устройство TOTP.

«Сердцем» TOTP является алгоритм HOTP (HMAC-based One-Time Password). HMAC, как часть этого алгоритма, гарантирует, что в случае перехвата сообщения атакующий не сможет угадать длину или расшифровать сообщение без наличия ключа или кода. Если вы планируете задействовать TOTP, советуем обратить внимание на проект MinTOTP. Авторы явно задались целью сделать настолько минималистичный код, насколько это возможно. Полноценная реализация заняла всего 20 строк кода (30 с начальным шебангом и пустыми строками):

#!/usr/bin/env python3

import base64
import hmac
import struct
import sys
import time


def hotp(key, counter, digits=6, digest='sha1'):
    key = base64.b32decode(key.upper() + '=' * ((8 — len(key)) % 8))
    counter = struct.pack('>Q', counter)
    mac = hmac.new(key, counter, digest).digest()
    offset = mac[-1] & 0x0f
    binary = struct.unpack('>L', mac[offset:offset+4])[0] & 0x7fffffff
    return str(binary)[-digits:].zfill(digits)


def totp(key, time_step=30, digits=6, digest='sha1'):
    return hotp(key, int(time.time() / time_step), digits, digest)


def main():
    args = [int(x) if x.isdigit() else x for x in sys.argv[1:]]
    for key in sys.stdin:
        print(totp(key.strip(), *args))


if __name__ == '__main__':
    main()

Модуль hmac нужен для реализации HOTP и берётся из стандартной библиотеки. HOTP принимает на вход секретный ключ, закодированный в Base32 и счётчик. На выходе выдаётся 6 цифр. Функция TOTP фактически служит “обёрткой” для HOTP с подсчётом временных интервалов, начиная с Unix epoch (1970-01-01 00:00:00 UTC). Практично, удобно… Рекомендуем!

Митапы

Онлайн

Python meetup

28 июня 2023

Рады сообщить, что у нас запланирован летний Python Meetup. Программа мероприятия формируется. Заявки на участие спикера принимаются до 10 июня. Видео с предыдущего митапа в студийном качестве 4K уже выложены на нашем YouTube-канале.

Теперь следить за митапами Evrone стало удобнее. В Telegram-канале Evrone meetups мы выкладываем анонсы с подробными описаниями докладов, а также студийные записи после мероприятий. А ещё, у нас можно выступить, мы поможем оформить вашу экспертизу в яркое выступление. Подписывайтесь и пишите @andrew_aquariuss, чтобы узнать подробности.

Регистрация

Вакансии

Удаленка / Офис

Evrone 

Мы рады новым Python-разработчикам. Удалённая работа с первого дня, помощь в подготовке выступлений на профессиональных конференциях, поощрение и оплата участия в Open-source проектах. Прозрачный способ увеличить грейд через обучение и проверку навыков под контролем ментора. Здесь есть понимание как организовать разработку комфортно и эффективно. Присоединяйтесь!

Подробнее

Подписаться
на Digest →
Важные новости и мероприятия без спама
Технологии которыми вы владеете и которые вам интересны
Ваш адрес электронной почты в безопасности - вот наша политика конфиденциальности.