Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve preview generation, matrix mentions and image support #27

Merged
merged 4 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Get changed files in posts folder
id: get_changed_files
Expand All @@ -30,9 +30,9 @@ jobs:

- name: Set up Python
if: steps.get_changed_files.outputs.any_changed == 'true'
uses: actions/setup-python@v3
uses: actions/setup-python@v5
with:
python-version: 3.9
python-version: '3.12'

- name: Install dependencies
if: steps.get_changed_files.outputs.any_changed == 'true'
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/publish_content.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Get changed files in posts folder
id: get_changed_files
Expand All @@ -31,9 +31,9 @@ jobs:

- name: Set up Python
if: steps.get_changed_files.outputs.any_changed == 'true'
uses: actions/setup-python@v3
uses: actions/setup-python@v5
with:
python-version: 3.9
python-version: '3.12'

- name: Install dependencies
if: steps.get_changed_files.outputs.any_changed == 'true'
Expand Down
16 changes: 8 additions & 8 deletions github_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ def comment(self, comment_text):
url = (
f"https://api.github.com/repos/{self.repo}/issues/{self.pr_number}/comments"
)
data = {"body": str(comment_text)}
response = requests.post(url, headers=headers, json=data)
if response.status_code == 201:
return True
else:
raise Exception(
f"Failed to create github comment!, {response.json().get('message')}"
)
for comment_body in comment_text.split("\n\n---\n"):
data = {"body": str(comment_body)}
response = requests.post(url, headers=headers, json=data)
if response.status_code != 201:
raise Exception(
f"Failed to create github comment!, {response.json().get('message')}"
)
return True

def get_files(self):
url = f"https://api.github.com/repos/{self.repo}/pulls/{self.pr_number}/files"
Expand Down
45 changes: 26 additions & 19 deletions lib/galaxy_social.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ def __init__(self, preview: bool, json_out: str):

self.plugins = {}
for plugin in self.plugins_config["plugins"]:
if preview and plugin["name"].lower() != "markdown":
continue

if plugin["enabled"]:
module_name, class_name = plugin["class"].rsplit(".", 1)
try:
Expand Down Expand Up @@ -112,35 +109,45 @@ def parse_markdown_file(self, file_path):

def process_markdown_file(self, file_path, processed_files):
content, metadata = self.parse_markdown_file(file_path)
if self.preview:
formatting_results = {}
for media in metadata["media"]:
try:
_, _, message = self.plugins["markdown"].create_post(
formatting_results[media] = self.plugins[media].format_content(
content=content,
mentions=[],
hashtags=[],
mentions=metadata.get("mentions", {}).get(media, []),
hashtags=metadata.get("hashtags", {}).get(media, []),
images=metadata.get("images", []),
media=metadata["media"],
preview=True,
file_path=file_path,
)
return processed_files, message
except Exception as e:
raise Exception(f"Failed to create preview for {file_path}.\n{e}")
raise Exception(f"Failed to format post for {file_path}.\n{e}")
if self.preview:
message = f'Hi, I\'m your friendly social media assistant. In the following, you will see a preview of this post "{file_path}"'
for media in metadata["media"]:
formatted_content, preview, warning = formatting_results[media]
message += f"\n\n## {media}\n\n"
message += preview
if warning:
message += f"\nWARNING: {warning}"
return processed_files, message.strip()

stats = {}
url = {}
if file_path in processed_files:
stats = processed_files[file_path]
for media in metadata["media"]:
if file_path in processed_files and media in processed_files[file_path]:
if stats.get(media):
print("Skipping previous post to", media)
continue
mentions = metadata.get("mentions", {}).get(media, [])
hashtags = metadata.get("hashtags", {}).get(media, [])
images = metadata.get("images", [])
formatted_content, _, _ = formatting_results[media]
stats[media], url[media] = self.plugins[media].create_post(
content, mentions, hashtags, images, file_path=file_path
formatted_content, file_path=file_path
)
url_text = "\n".join(
[f"[{media}]({link})" for media, link in url.items() if link]
[
f"- [{media}]({link})" if link else f"- {media}"
for media, link in url.items()
if stats[media]
]
)
message = f"Posted to:\n\n{url_text}" if url_text else "No posts created."

Expand All @@ -150,7 +157,7 @@ def process_markdown_file(self, file_path, processed_files):

def process_files(self, files_to_process):
processed_files = {}
messages = "---\n"
messages = ""
processed_files_path = self.json_out
if os.path.exists(processed_files_path):
with open(processed_files_path, "r") as file:
Expand Down
66 changes: 51 additions & 15 deletions lib/plugins/bluesky.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,52 @@ def handle_url_card(
)
return embed_external

def create_post(
self, content, mentions, hashtags, images, **kwargs
) -> Tuple[bool, Optional[str]]:
def wrap_text_with_index(self, content):
if len(content) <= self.max_content_length:
return [content]
urls = re.findall(r"https?://\S+", content)
placeholder_content = re.sub(
r"https?://\S+", lambda m: "~" * len(m.group()), content
)
wrapped_lines = textwrap.wrap(
placeholder_content, self.max_content_length - 8, replace_whitespace=False
)
final_lines = []
url_index = 0
for i, line in enumerate(wrapped_lines, 1):
while "~~~~~~~~~~" in line and url_index < len(urls):
placeholder = "~" * len(urls[url_index])
line = line.replace(placeholder, urls[url_index], 1)
url_index += 1
final_lines.append(f"{line} ({i}/{len(wrapped_lines)})")
return final_lines

def format_content(self, content, mentions, hashtags, images, **kwargs):
mentions = " ".join([f"@{v}" for v in mentions])
hashtags = " ".join([f"#{v}" for v in hashtags])
if len(images) > 4:
warnings = f"A maximum of four images, not {len(images)}, can be included in a single bluesky post."
images = images[:4]
else:
warnings = ""

chunks = self.wrap_text_with_index(f"{content}\n\n{mentions}\n{hashtags}")

formatted_content = {
"body": "\n\n".join(chunks),
"images": images,
"chunks": chunks,
}
preview = formatted_content["body"]
images_preview = "\n".join(
[f'![{image.get("alt_text", "")}]({image["url"]})' for image in images]
)
preview += "\n\n" + images_preview
return formatted_content, preview, warnings

def create_post(self, content, **kwargs) -> Tuple[bool, Optional[str]]:
embed_images = []
for image in images[:4]:
for image in content["images"][:4]:
response = requests.get(image["url"])
if response.status_code == 200 and response.headers.get(
"Content-Type", ""
Expand All @@ -172,17 +213,11 @@ def create_post(
else None
)

status = []
reply_to = None
mentions = " ".join([f"@{v}" for v in mentions])
hashtags = " ".join([f"#{v}" for v in hashtags])
for text in textwrap.wrap(
content + "\n" + mentions + "\n" + hashtags,
self.max_content_length,
replace_whitespace=False,
):

for text in content["chunks"]:
facets, last_url = self.parse_facets(text)
if not images or reply_to:
if not content["images"] or reply_to:
embed = self.handle_url_card(cast(str, last_url))

post = self.blueskysocial.send_post(
Expand All @@ -192,13 +227,14 @@ def create_post(
for _ in range(5):
data = self.blueskysocial.get_posts([post.uri]).posts
if data:
status.append(data[0].record.text == text)
break
else:
return False, None

if reply_to is None:
link = f"https://bsky.app/profile/{self.blueskysocial.me.handle}/post/{post.uri.split('/')[-1]}"
root = atproto.models.create_strong_ref(post)
parent = atproto.models.create_strong_ref(post)
reply_to = atproto.models.AppBskyFeedPost.ReplyRef(parent=parent, root=root)

return all(status), link
return True, link
46 changes: 15 additions & 31 deletions lib/plugins/markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,42 +10,26 @@ def __init__(self, **kwargs):
else os.path.join(os.getcwd(), kwargs["save_path"])
)

def create_post(self, content, mentions, hashtags, images, **kwargs):
def format_content(self, content, mentions, hashtags, images, **kwargs):
_images = "\n".join(
[f'![{image.get("alt_text", "")}]({image["url"]})' for image in images]
)
mentions = " ".join([f"@{v}" for v in mentions])
hashtags = " ".join([f"#{v}" for v in hashtags])
warnings = ""
formatted_content = "\n\n".join([content, mentions, hashtags, _images])
preview = formatted_content
return formatted_content, preview, warnings

def create_post(self, formatted_content, **kwargs):
try:
_images = (
"\n"
+ "\n".join(
[
f'![{image.get("alt_text", "")}]({image["url"]})'
for image in images
]
)
if images
else ""
)
mentions = "\n" + " ".join([f"@{v}" for v in mentions]) if mentions else ""
hashtags = "\n" + " ".join([f"#{v}" for v in hashtags]) if hashtags else ""
text = f"{content}{mentions}{hashtags}{_images}"
if self.save_path:
os.makedirs(self.save_path, exist_ok=True)
prefix = kwargs.get("file_path", "").replace(".md", "")
file_name = (
f"{self.save_path}/{prefix.replace('/', '-')}_{time.strftime('%Y%m%d-%H%M%S')}.md"
)
file_name = f"{self.save_path}/{prefix.replace('/', '-')}_{time.strftime('%Y%m%d-%H%M%S')}.md"
with open(file_name, "w") as f:
f.write(text)
if kwargs.get("preview"):
social_media = ", ".join(kwargs.get("media", []))
pre_comment_text = ""
if len(images) > 4 and (
"mastodon" in social_media or "bluesky" in social_media
):
pre_comment_text = f"Please note that Mastodon and Bluesky only support up to 4 images in a single post. The first 4 images will be included in the post, and the rest will be ignored.\n"
comment_text = f"{pre_comment_text}This is a preview from {prefix.split('/')[-1]} that will be posted to {social_media}:\n\n{text}"
return True, None, comment_text
f.write(formatted_content)
return True, None
except Exception as e:
if kwargs.get("preview", False):
print(e)
return False, None, e
print(e)
return False, None
Loading
Loading