332 lines
7.8 KiB
Python
332 lines
7.8 KiB
Python
"""
|
|
|
|
Post Utilities
|
|
|
|
"""
|
|
|
|
import json
|
|
from typing import Dict
|
|
|
|
__all__ = ["Post"]
|
|
|
|
from substack.exceptions import SectionNotExistsException
|
|
|
|
|
|
class Post:
|
|
"""
|
|
|
|
Post utility class
|
|
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
title: str,
|
|
subtitle: str,
|
|
user_id,
|
|
audience: str = None,
|
|
write_comment_permissions: str = None,
|
|
):
|
|
"""
|
|
|
|
Args:
|
|
title:
|
|
subtitle:
|
|
user_id:
|
|
audience: possible values: everyone, only_paid, founding, only_free
|
|
write_comment_permissions: none, only_paid, everyone (this field is a mess)
|
|
"""
|
|
self.draft_title = title
|
|
self.draft_subtitle = subtitle
|
|
self.draft_body = {"type": "doc", "content": []}
|
|
self.draft_bylines = [{"id": int(user_id), "is_guest": False}]
|
|
self.audience = audience if audience is not None else "everyone"
|
|
self.draft_section_id = None
|
|
self.section_chosen = True
|
|
|
|
# TODO better understand the possible values and combinations with audience
|
|
if write_comment_permissions is not None:
|
|
self.write_comment_permissions = write_comment_permissions
|
|
else:
|
|
self.write_comment_permissions = self.audience
|
|
|
|
def set_section(self, name: str, sections: list):
|
|
"""
|
|
|
|
Args:
|
|
name:
|
|
sections:
|
|
|
|
Returns:
|
|
|
|
"""
|
|
section = [s for s in sections if s.get("name") == name]
|
|
if len(section) != 1:
|
|
raise SectionNotExistsException(name)
|
|
section = section[0]
|
|
self.draft_section_id = section.get("id")
|
|
|
|
def add(self, item: Dict):
|
|
"""
|
|
|
|
Add item to draft body.
|
|
|
|
Args:
|
|
item:
|
|
|
|
Returns:
|
|
|
|
"""
|
|
|
|
self.draft_body["content"] = self.draft_body.get("content", []) + [
|
|
{"type": item.get("type")}
|
|
]
|
|
content = item.get("content")
|
|
if item.get("type") == "captionedImage":
|
|
self.captioned_image(**item)
|
|
elif item.get("type") == "embeddedPublication":
|
|
self.draft_body["content"][-1]["attrs"] = item.get("url")
|
|
elif item.get("type") == "youtube2":
|
|
self.youtube(item.get("src"))
|
|
elif item.get("type") == "subscribeWidget":
|
|
self.subscribe_with_caption(item.get("message"))
|
|
else:
|
|
if content is not None:
|
|
self.add_complex_text(content)
|
|
|
|
if item.get("type") == "heading":
|
|
self.attrs(item.get("level", 1))
|
|
|
|
marks = item.get("marks")
|
|
if marks is not None:
|
|
self.marks(marks)
|
|
|
|
return self
|
|
|
|
def paragraph(self, content=None):
|
|
"""
|
|
|
|
Args:
|
|
content:
|
|
|
|
Returns:
|
|
|
|
"""
|
|
item = {"type": "paragraph"}
|
|
if content is not None:
|
|
item["content"] = content
|
|
return self.add(item)
|
|
|
|
def heading(self, content=None, level: int = 1):
|
|
"""
|
|
|
|
Args:
|
|
content:
|
|
level:
|
|
|
|
Returns:
|
|
|
|
"""
|
|
|
|
item = {"type": "heading"}
|
|
if content is not None:
|
|
item["content"] = content
|
|
item["level"] = level
|
|
return self.add(item)
|
|
|
|
def horizontal_rule(self):
|
|
"""
|
|
|
|
Returns:
|
|
|
|
"""
|
|
return self.add({"type": "horizontal_rule"})
|
|
|
|
def attrs(self, level):
|
|
"""
|
|
|
|
Args:
|
|
level:
|
|
|
|
Returns:
|
|
|
|
"""
|
|
content_attrs = self.draft_body["content"][-1].get("attrs", {})
|
|
content_attrs.update({"level": level})
|
|
self.draft_body["content"][-1]["attrs"] = content_attrs
|
|
return self
|
|
|
|
def captioned_image(
|
|
self,
|
|
src: str,
|
|
fullscreen: bool = False,
|
|
imageSize: str = "normal",
|
|
height: int = 819,
|
|
width: int = 1456,
|
|
resizeWidth: int = 728,
|
|
bytes: str = None,
|
|
alt: str = None,
|
|
title: str = None,
|
|
type: str = None,
|
|
href: str = None,
|
|
belowTheFold: bool = False,
|
|
internalRedirect: str = None,
|
|
):
|
|
"""
|
|
|
|
Add image to body.
|
|
|
|
Args:
|
|
bytes:
|
|
alt:
|
|
title:
|
|
type:
|
|
href:
|
|
belowTheFold:
|
|
internalRedirect:
|
|
src:
|
|
fullscreen:
|
|
imageSize:
|
|
height:
|
|
width:
|
|
resizeWidth:
|
|
"""
|
|
|
|
content = self.draft_body["content"][-1].get("content", [])
|
|
content += [
|
|
{
|
|
"type": "image2",
|
|
"attrs": {
|
|
"src": src,
|
|
"fullscreen": fullscreen,
|
|
"imageSize": imageSize,
|
|
"height": height,
|
|
"width": width,
|
|
"resizeWidth": resizeWidth,
|
|
"bytes": bytes,
|
|
"alt": alt,
|
|
"title": title,
|
|
"type": type,
|
|
"href": href,
|
|
"belowTheFold": belowTheFold,
|
|
"internalRedirect": internalRedirect,
|
|
},
|
|
}
|
|
]
|
|
self.draft_body["content"][-1]["content"] = content
|
|
return self
|
|
|
|
def text(self, value: str):
|
|
"""
|
|
|
|
Add text to the last paragraph.
|
|
|
|
Args:
|
|
value: Text to add to paragraph.
|
|
|
|
Returns:
|
|
|
|
"""
|
|
content = self.draft_body["content"][-1].get("content", [])
|
|
content += [{"type": "text", "text": value}]
|
|
self.draft_body["content"][-1]["content"] = content
|
|
return self
|
|
|
|
def add_complex_text(self, text):
|
|
"""
|
|
|
|
Args:
|
|
text:
|
|
"""
|
|
if isinstance(text, str):
|
|
self.text(text)
|
|
else:
|
|
for chunk in text:
|
|
if chunk:
|
|
self.text(chunk.get("content")).marks(chunk.get("marks", []))
|
|
|
|
def marks(self, marks):
|
|
"""
|
|
|
|
Args:
|
|
marks:
|
|
|
|
Returns:
|
|
|
|
"""
|
|
content = self.draft_body["content"][-1].get("content", [])[-1]
|
|
content_marks = content.get("marks", [])
|
|
for mark in marks:
|
|
new_mark = {"type": mark.get("type")}
|
|
if mark.get("type") == "link":
|
|
href = mark.get("href")
|
|
new_mark.update({"attrs": {"href": href}})
|
|
content_marks.append(new_mark)
|
|
content["marks"] = content_marks
|
|
return self
|
|
|
|
def remove_last_paragraph(self):
|
|
"""Remove last paragraph"""
|
|
del self.draft_body.get("content")[-1]
|
|
|
|
def get_draft(self):
|
|
"""
|
|
|
|
Returns:
|
|
|
|
"""
|
|
out = vars(self)
|
|
out["draft_body"] = json.dumps(out["draft_body"])
|
|
return out
|
|
|
|
def subscribe_with_caption(self, message: str = None):
|
|
"""
|
|
|
|
Add subscribe widget with caption
|
|
|
|
Args:
|
|
message:
|
|
|
|
Returns:
|
|
|
|
"""
|
|
|
|
if message is None:
|
|
message = """Thanks for reading this newsletter!
|
|
Subscribe for free to receive new posts and support my work."""
|
|
|
|
subscribe = self.draft_body["content"][-1]
|
|
subscribe["attrs"] = {
|
|
"url": "%%checkout_url%%",
|
|
"text": "Subscribe",
|
|
"language": "en",
|
|
}
|
|
subscribe["content"] = [
|
|
{
|
|
"type": "ctaCaption",
|
|
"content": [
|
|
{
|
|
"type": "text",
|
|
"text": message,
|
|
}
|
|
],
|
|
}
|
|
]
|
|
return self
|
|
|
|
def youtube(self, value: str):
|
|
"""
|
|
|
|
Add youtube video to post.
|
|
|
|
Args:
|
|
value: youtube url
|
|
|
|
Returns:
|
|
|
|
"""
|
|
content_attrs = self.draft_body["content"][-1].get("attrs", {})
|
|
content_attrs.update({"videoId": value})
|
|
self.draft_body["content"][-1]["attrs"] = content_attrs
|
|
return self
|