From 6babc1b53b9cdee11700358c520d0217d2402015 Mon Sep 17 00:00:00 2001 From: Morgan McMillian Date: Sat, 15 Aug 2020 16:37:53 -0700 Subject: [PATCH] initial commit - email2post.py --- LICENSE | 21 +++++++ README.md | 7 +++ email2post.py | 159 +++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 2 + 4 files changed, 189 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100755 email2post.py create mode 100644 requirements.txt diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9427995 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Morgan McMillian + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..54790fb --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# blarp - a collection of my social posting bits + +## email2post.py + +Parse email from STDIN and create a post on one or more social network. +Currently supports mastodon and pnut.io. + diff --git a/email2post.py b/email2post.py new file mode 100755 index 0000000..e6300dc --- /dev/null +++ b/email2post.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 +import sys +import os +import email +import email.header +import textwrap +import logging +import argparse +import configparser + +import pnutpy + +from mastodon import Mastodon + +logger = logging.getLogger() + +def parse(data, pnut, mstdn): + body = "" + attachments = [] + + for part in data.walk(): + disposition = part.get_content_disposition() + contentType = part.get_content_type() + + if contentType == "multipart/mixed": + 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 pnut: + post_pnut(data['Subject'], body, attachments) + + if mstdn: + post_mastodon(data['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) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..9e8aedf --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +pnutpy +Mastodon.py