#!/usr/bin/env python3 import sys import os import email import email.header import textwrap import logging import argparse import configparser import time import pnutpy from mastodon import Mastodon logger = logging.getLogger() def parse(data, pnut, mstdn): body = "" attachments = [] msg = [] for part in data.walk(): disposition = part.get_content_disposition() contentType = part.get_content_type() logger.debug(disposition) logger.debug(contentType) if contentType in ["multipart/mixed", "multipart/alternative"]: continue if disposition == "inline": body = part.get_payload(decode=True).decode("utf-8") elif disposition == "attachment": attachment = part.get_payload(decode=True) filename = part.get_filename() attachments.append({ 'type': contentType, 'filename': filename, 'file': attachment }) else: logger.debug("unknown disposition") logger.debug(disposition) if contentType == "text/plain": body = part.get_payload(decode=True).decode("utf-8") else: logger.error("can't get the message, bailing out") return parsebody = body.split('\n') for line in parsebody: if line == "-- ": break msg.append(line) body = '\n'.join(msg) deltac = data.get('Chat-Version') if deltac is None: subject = data['Subject'] else: logger.debug(f"Chat-Version: {deltac}") subject = "" logger.debug(subject) logger.debug(body) logger.debug(len(attachments)) if pnut: post_pnut(subject, body, attachments) if mstdn: post_mastodon(subject, body, attachments) def post_pnut(title, text, attachments): post = {'text': text, 'raw': []} mtext = textwrap.wrap(text, 254) if len(mtext) > 1: longpost = { 'title': title, 'body': text, 'tstamp': time.time() * 1000 } pretext = textwrap.wrap(text, 100) post['text'] = pretext[0] post['text'] += "... - https://longpo.st/p/{object_id} - #longpost" post['raw'].append({ 'type': "nl.chimpnut.blog.post", 'value': longpost }) for attachment in attachments: try: file_, meta = pnutpy.api.create_file( files={'content': attachment['file']}, data={ 'name': attachment['filename'], 'type': 'dev.thrrgilag.blarp', 'is_public': 'true' } ) value = { '+io.pnut.core.file': { 'file_id': file_.id, 'file_token': file_.file_token, 'format': 'oembed' } } post['raw'].append({ 'type': "io.pnut.core.oembed", 'value': value }) except Exception: logger.error("problem handling an attachment") logger.exception("pnut attachment") return try: response, meta = pnutpy.api.create_post(data=post) logger.debug(response) except Exception: logger.error("problem posting to pnut") logger.exception("create_post") def post_mastodon(title, text, attachments): media_ids = [] for attachment in attachments: file_ = mastodon.media_post(attachment['file'], attachment['type']) media_ids.append(file_['id']) response = mastodon.status_post(text, media_ids=media_ids) logger.debug(response) if __name__ == "__main__": if "XDG_CONFIG_HOME" in os.environ: config_home = os.path.expanduser(os.environ["XDG_CONFIG_HOME"]) config_file = os.path.join(config_home, "email2post.conf") else: config_home = os.path.expanduser("~") config_file = os.path.join(config_home, ".email2post.conf") a_parser = argparse.ArgumentParser() a_parser.add_argument( '-d', '--debug', action='store_true', dest='debug', help="debug" ) a_parser.add_argument( '-c', '--config', dest='config_file', ) a_parser.add_argument( '-p', '--pnut', action='store_true', dest='pnut', help="post to pnut.io" ) a_parser.add_argument( '-m', '--mastodon', action='store_true', dest='mstdn', help="post to mastodon" ) args = a_parser.parse_args() if args.debug: logging.basicConfig(level=logging.DEBUG) if args.config_file: config_file = args.config_file config = configparser.ConfigParser() config.read(config_file) pnutpy.api.add_authorization_token(config['pnut']['token']) mastodon = Mastodon( client_id=config['mastodon']['client_id'], client_secret=config['mastodon']['client_secret'], access_token=config['mastodon']['access_token'], api_base_url=config['mastodon']['url'] ) data = email.message_from_file(sys.stdin) parse(data, args.pnut, args.mstdn)