initial commit - email2post.py
This commit is contained in:
commit
6babc1b53b
4 changed files with 189 additions and 0 deletions
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -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.
|
7
README.md
Normal file
7
README.md
Normal file
|
@ -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.
|
||||||
|
|
159
email2post.py
Executable file
159
email2post.py
Executable file
|
@ -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)
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pnutpy
|
||||||
|
Mastodon.py
|
Loading…
Reference in a new issue