2017-04-24 16:25:34 +02:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
2019-01-02 10:49:49 +01:00
|
|
|
import os
|
2017-04-24 16:25:34 +02:00
|
|
|
import re
|
2017-12-29 14:26:40 +01:00
|
|
|
import socket
|
2019-08-22 17:10:37 +02:00
|
|
|
import subprocess
|
|
|
|
import tempfile
|
2018-01-21 16:39:40 +01:00
|
|
|
import unicodedata
|
2019-02-13 13:38:37 +01:00
|
|
|
import warnings
|
2017-04-24 16:25:34 +02:00
|
|
|
|
|
|
|
from bs4 import BeautifulSoup
|
|
|
|
|
2017-12-30 16:30:35 +01:00
|
|
|
from toot.exceptions import ConsoleError
|
|
|
|
|
2017-04-24 16:25:34 +02:00
|
|
|
|
2019-01-24 11:18:28 +01:00
|
|
|
def str_bool(b):
|
|
|
|
"""Convert boolean to string, in the way expected by the API."""
|
|
|
|
return "true" if b else "false"
|
|
|
|
|
|
|
|
|
2017-04-24 16:25:34 +02:00
|
|
|
def get_text(html):
|
|
|
|
"""Converts html to text, strips all tags."""
|
2019-02-13 13:38:37 +01:00
|
|
|
|
|
|
|
# Ignore warnings made by BeautifulSoup, if passed something that looks like
|
|
|
|
# a file (e.g. a dot which matches current dict), it will warn that the file
|
|
|
|
# should be opened instead of passing a filename.
|
|
|
|
with warnings.catch_warnings():
|
|
|
|
warnings.simplefilter("ignore")
|
|
|
|
text = BeautifulSoup(html.replace(''', "'"), "html.parser").get_text()
|
2018-01-21 16:39:40 +01:00
|
|
|
|
|
|
|
return unicodedata.normalize('NFKC', text)
|
2017-04-24 16:25:34 +02:00
|
|
|
|
|
|
|
|
|
|
|
def parse_html(html):
|
|
|
|
"""Attempt to convert html to plain text while keeping line breaks.
|
|
|
|
Returns a list of paragraphs, each being a list of lines.
|
|
|
|
"""
|
|
|
|
paragraphs = re.split("</?p[^>]*>", html)
|
|
|
|
|
|
|
|
# Convert <br>s to line breaks and remove empty paragraphs
|
|
|
|
paragraphs = [re.split("<br */?>", p) for p in paragraphs if p]
|
|
|
|
|
|
|
|
# Convert each line in each paragraph to plain text:
|
|
|
|
return [[get_text(l) for l in p] for p in paragraphs]
|
|
|
|
|
|
|
|
|
|
|
|
def format_content(content):
|
|
|
|
"""Given a Status contents in HTML, converts it into lines of plain text.
|
|
|
|
|
|
|
|
Returns a generator yielding lines of content.
|
|
|
|
"""
|
|
|
|
|
|
|
|
paragraphs = parse_html(content)
|
|
|
|
|
|
|
|
first = True
|
|
|
|
|
|
|
|
for paragraph in paragraphs:
|
|
|
|
if not first:
|
|
|
|
yield ""
|
|
|
|
|
|
|
|
for line in paragraph:
|
|
|
|
yield line
|
|
|
|
|
|
|
|
first = False
|
2017-12-29 14:26:40 +01:00
|
|
|
|
|
|
|
|
|
|
|
def domain_exists(name):
|
|
|
|
try:
|
|
|
|
socket.gethostbyname(name)
|
|
|
|
return True
|
|
|
|
except OSError:
|
|
|
|
return False
|
2017-12-30 16:30:35 +01:00
|
|
|
|
|
|
|
|
|
|
|
def assert_domain_exists(domain):
|
|
|
|
if not domain_exists(domain):
|
|
|
|
raise ConsoleError("Domain {} not found".format(domain))
|
2018-01-04 12:36:14 +01:00
|
|
|
|
|
|
|
|
2019-01-02 10:49:49 +01:00
|
|
|
EOF_KEY = "Ctrl-Z" if os.name == 'nt' else "Ctrl-D"
|
|
|
|
|
|
|
|
|
|
|
|
def multiline_input():
|
|
|
|
"""Lets user input multiple lines of text, terminated by EOF."""
|
|
|
|
lines = []
|
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
lines.append(input())
|
|
|
|
except EOFError:
|
|
|
|
break
|
|
|
|
|
|
|
|
return "\n".join(lines).strip()
|
2019-08-22 17:10:37 +02:00
|
|
|
|
|
|
|
|
2022-11-12 07:29:08 +01:00
|
|
|
EDITOR_DIVIDER = "------------------------ >8 ------------------------"
|
|
|
|
|
|
|
|
EDITOR_INPUT_INSTRUCTIONS = f"""
|
|
|
|
{EDITOR_DIVIDER}
|
|
|
|
Do not modify or remove the line above.
|
|
|
|
Enter your toot above it.
|
|
|
|
Everything below it will be ignored.
|
2019-08-22 17:10:37 +02:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
def editor_input(editor, initial_text):
|
|
|
|
"""Lets user input text using an editor."""
|
|
|
|
initial_text = (initial_text or "") + EDITOR_INPUT_INSTRUCTIONS
|
|
|
|
|
2022-11-11 14:19:09 +01:00
|
|
|
with tempfile.NamedTemporaryFile(suffix='.toot') as f:
|
2019-08-22 17:10:37 +02:00
|
|
|
f.write(initial_text.encode())
|
|
|
|
f.flush()
|
|
|
|
|
|
|
|
subprocess.run([editor, f.name])
|
|
|
|
|
|
|
|
f.seek(0)
|
|
|
|
text = f.read().decode()
|
|
|
|
|
2022-11-12 07:29:08 +01:00
|
|
|
return text.split(EDITOR_DIVIDER)[0].strip()
|