Telegram-бот на Python для создания задач в MS Outlook и заметок в Evernote

    В MS Outlook есть прекрасный раздел задач. Это удобно и быстро. Можно с утра (или с вечера) накидать себе задачек на день и постепенно расщёлкивать их. Вторым уровнем группируем задачи по матрице Эйзенхауера или используем любой другой удобный вам инструмент тайм-менеджмента. Одна проблема с этими задачами: для их создания надо заходить в Outlook, щёлкать, писать, нажимать Enter, двигать в нужную группу. Это всё долго и не всегда удобно. Но есть в Outlook также правила, позволяющие письма содержащие определённые слова в теме автоматически делать задачами. Ещё быстрее и проще получится, если такие письма отправляются прямо из Telegram. О простейших ботах помогающих создавать задачи в MS Outlook и заметки в Evernote без необходимости сложных интеграций.

    Бот: Telegram + Python + Docker

    Строго говоря ботов два. Пока не получилось совместить весь функционал в одном боте так, чтобы это было удобно как переслать/накидать сообщение и нажать на кнопку "Send". Немного о стеке. Python был выбран за простоту кода и доступность для менеджеров проектов без глубоких познаний в программировании. Docker — удобно упаковать бота в контейнер и запускать в нём, без необходимости думать о версиях Linux на хостинге, настройках сервисов и так далее. В качестве хостера использовался DigitalOcean, в котором Docker-контейнеры разворачиваются легко и просто.

    Конечно же использовался не чистый Telegram API, а удобный telebot. Для отправки писем вполне подошла стандартная библиотека smtplib. Для сокращения текста используется textwrap. А формированием сущности сообщения занимается тоже стандартная email.mime:

    import os
    import telebot
    import smtplib
    import textwrap
    from email.mime.text import MIMEText

    Пароли в контейнер кладём аккуратно. Чтобы не хранить их открытым текстом в исходнике, используем переменные окружения:

    #Configuration
    CFG_TOKEN = os.environ.get('CFG_TOKEN') #TELEGRAM_BOT_TOKEN
    CFG_SMTP_LOGIN = os.environ.get('CFG_SMTP_LOGIN') #'%YOUR_SMTP_LOGIN_ON_YANDEX%'
    CFG_SMTP_PASS = os.environ.get('CFG_SMTP_PASS') #'%YOUR_SMTP_PASS_ON_YANDEX%'
    CFG_SMTP_FROM = os.environ.get('CFG_SMTP_FROM') #'%FROM_EMAIL_ADDRESS%'
    CFG_SMTP_TO = os.environ.get('CFG_SMTP_TO') #'%TO_EMAIL_ADDRESS%'

    При запуске Docker-контейнера берём переменные откуда нам удобно, например из файла .env или переменной окружения на хостовой машине.

    Бот частный. Он не предназначен для раздачи доступов всем подряд. Поэтому в самом начале проводим проверку отправителя. Мне было лень писать обработчик значений с разными типами, поэтому я написал обработчик исключения:

    try:
        CFG_OWNER_ID = int(os.environ.get('CFG_OWNER_ID')) #YOUR_USER_ID_IN_TELEGRAM
    except:
        CFG_OWNER_ID = os.environ.get('CFG_OWNER_ID')

    Собственно письмо формируем по следующему алгоритму. Если текста сообщения менее или равна 40 символам, то помещаем его целиком в тему письма, а в теле пишем стандартный текст. Если более - помещаем первые 40 символов, а остаток кладём в тело письма. Это точно позволяет обойти какие-то правила антиспам-фильтра Яндекса, который использовался для рассылки сообщений.

    msg = MIMEText('')
    if len(txt) > 40:
    	txt = textwrap.shorten(txt, width=40, placeholder="...") #Лишнее обрезаем тремя точками
      msg = MIMEText(message.text)
    else:
    	msg = MIMEText("Задача из Telegram")

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

    if "forward_sender_name" in message.json:
    	txt = f'[{message.json["forward_sender_name"]}] {txt}'
    elif "forward_from" in message.json:
      txt = f'[{message.json["forward_from"]["first_name"]}] {txt}'

    Метим наше сообщение в теме как "[TASK]", чтобы MS Outlook мог его отличить от обычных писем и обработать правилами:

    msg['Subject'] = f'[TASK] {txt}'

    Далее стандартно отправляем наше письмо, не забывая вернуть сообщение с успехом или ошибкой при отправке:

    try:
    	server = smtplib.SMTP_SSL('smtp.yandex.ru:465')
      server.login(CFG_SMTP_LOGIN, CFG_SMTP_PASS)
      server.sendmail(CFG_SMTP_FROM, CFG_SMTP_TO, msg.as_string()) 
      bot.send_message(message.from_user.id, "Задача отправлена в Outlook")
      server.quit()
    except Exception as err:
      bot.send_message(message.from_user.id, f"При отправке сообщения произошла ошибка: {str(err)}")
    

    Полный код бота доступен здесь: https://github.com/nsuvorov83/sendtask-bot

    Настройка MS Outlook

    Правила MS Outlook настраиваются просто:

    А как отправлять заметки в Evernote?

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

    Ключевой момент здесь - обработка типов сообщений и вложений. Определяем функции-обработчики для различных типов сообщений Telegram:

    def _is_photo(message):
            return message.photo
    
    def _is_document(message):
            return message.document
    
    def _is_text(message):
        return message.text
    
    def _is_caption(message):
        return message.caption
      
    #А вот как определяется сам тип сообщения. Если тип некорректный, то вернётся False
    if _is_photo(message):
    	#Photo handler
      cached_files = getPhotoCached(message)
      #Insert into e-mail
      for f in cached_files:
      	with open(f, 'rb') as file:
        msg.attach(MIMEImage(file.read()))

    Пересылаемые фотографии и документы обязательно надо закешировать прежде чем их можно будет вложить в E-mail. Суть функции одна, поэтому привожу её только для фото:

    def cachFile(file_info):
        dt = datetime.datetime.now()
        timestamp = dt.timestamp()
        downloaded_file = bot.download_file(file_info.file_path)
        ext = os.path.split(file_info.file_path)[1].split('.')[1]
        cached_file_name = str(timestamp) + '.' + ext
        src = tempfile.gettempdir() + os.path.sep + cached_file_name
        with open(src, 'wb') as new_file:
            new_file.write(downloaded_file)
        return src
      
    def getPhotoCached(message):
        cached_files = []
        ff = message.photo[-1]
        file_info = bot.get_file(ff.file_id)
        asrc = cachFile(file_info)
        cached_files.append(asrc)
        return cached_files
      
    if _is_photo(message):
            #Photo handler
            cached_files = getPhotoCached(message)
            #Attach into e-mail
            for f in cached_files:
                with open(f, 'rb') as file:
                    msg.attach(MIMEImage(file.read()))

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

    if len(txt) > 30:
    	txt_subject = textwrap.shorten(txt, width=30, placeholder="...")
    elif len(txt) == 0:
      #Стандартный заголовой для заметок без текста включает "Из Telegram", текущую дату и время
      now = datetime.datetime.now()
    	txt_subject = 'Из Telegram ' + str(now)
    else:
    	txt_subject = txt
    
    msg['Subject'] = f'{txt_subject} #{d.year} #из_telegram'

    Процесс отправки e-mail стандартный и описан выше.

    Полный код бота найдёте здесь

    Комментарии 2

      +1
      В вашем материале очень наглядно в очередной раз доказывается, что при всем многообразии мессенжеров и прочих crm главным интструментом работыостается email.

      С чем и не поспоришь.

        0

        Из tg в outlook это правильно и удобно, потому что ms Outlook или thunderbird не только мессенджеры, но и БД c возможностями поиска, классификации, экспорта, и генерации отчётов в Эксель и все такое. Если б реализовать это на на чистом ecmascript 5 то можно было б обойтись бесплатным хостингом веб приложений Гугла, имею ввиду app script для google sheets. Нюанс, из-за ограничений JS5 общаться с ТГ нужно без библиотек.

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое