Skip to content

Commit

Permalink
Merge pull request #27 from usegalaxy-eu/better-previews
Browse files Browse the repository at this point in the history
Improve preview generation, matrix mentions and image support
  • Loading branch information
arash77 authored Jun 5, 2024
2 parents fc4ac20 + 841dc3e commit 883012d
Show file tree
Hide file tree
Showing 10 changed files with 304 additions and 171 deletions.
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

0 comments on commit 883012d

Please sign in to comment.