### Run Galaxy Social CLI for Preview and Publishing (Bash)
Source: https://context7.com/usegalaxy-eu/galaxy-social/llms.txt
This Bash script demonstrates how to use the Galaxy Social command-line interface for previewing and publishing posts. It shows commands for previewing single posts or all posts in a folder, publishing posts using environment variables for credentials, and specifying a custom output file.
```bash
# Preview a single post (dry-run)
python lib/galaxy_social.py --files posts/2025/announcement.md --preview
# Preview all posts in a folder
python lib/galaxy_social.py --folder posts/2025 --preview
# Publish posts (requires environment variables for credentials)
export MASTODON_ACCESS_TOKEN="your_token"
export BLUESKY_PASSWORD="your_password"
export LINKEDIN_ACCESS_TOKEN="your_token"
python lib/galaxy_social.py --files posts/2025/announcement.md
# Specify custom output file for tracking
python lib/galaxy_social.py --files posts/2025/announcement.md --json-out custom_processed.json
```
--------------------------------
### Load and Render Media Options from YAML (JavaScript)
Source: https://github.com/usegalaxy-eu/galaxy-social/blob/main/docs/index.html
Fetches plugin configurations from a remote YAML file, parses it using jsyaml, and dynamically generates UI elements (checkboxes, input fields) for each enabled plugin. It includes logic for handling plugin-specific input formats and integrates with the Tagify library for enhanced input fields. Error handling is included for fetch requests and image loading.
```javascript
try {
const res = await fetch(
"https://raw.githubusercontent.com/usegalaxy-eu/galaxy-social/main/plugins.yml"
);
const data = jsyaml.load(await res.text());
const container = document.getElementById("media-options");
const plugins = data.plugins
.filter((p) => p.enabled && p.name !== "markdown")
.sort((a, b) => a.name.localeCompare(b.name));
plugins.forEach((plugin) => {
const name = plugin.name.toLowerCase();
pluginMax[name] = plugin.config?.max_content_length;
const wrap = document.createElement("div");
wrap.className = "space-y-2";
const cb = document.createElement("input");
cb.type = "checkbox";
cb.id = `cb-${name}`;
cb.value = name;
cb.className = "media-checkbox form-checkbox";
const lbl = document.createElement("label");
lbl.htmlFor = cb.id;
lbl.className = "flex items-center space-x-2";
lbl.appendChild(cb);
const fullName = plugin.name.toLowerCase();
const base = fullName.split("-")[0];
const icon = document.createElement("img");
icon.src = `https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/${base}.svg`;
icon.alt = `${base} logo`;
icon.className = "w-5 h-5 inline-block";
icon.onerror = () => {
icon.onerror = null;
icon.src = `https://cdn.simpleicons.org/${base}`;
};
lbl.appendChild(icon);
const span = document.createElement("span");
span.className = "font-semibold";
span.textContent = name;
lbl.appendChild(span);
if (plugin.config?.account_url) {
const link = document.createElement("a");
link.href = plugin.config.account_url;
link.target = "_blank";
link.className = "text-blue-500 text-sm";
link.innerHTML = "🔗";
lbl.appendChild(link);
}
wrap.appendChild(lbl);
const tagContainer = document.createElement("div");
tagContainer.className = "space-y-2 hidden";
if (!name.includes("slack")) {
const mIn = document.createElement("input");
mIn.id = `mention-${name}`;
mIn.placeholder = "Mentions";
mIn.className = "w-full p-2 border rounded";
tagContainer.appendChild(mIn);
const mentionHint = document.createElement("div");
mentionHint.className = "text-xs text-gray-500 italic transition-all duration-300";
let format = null;
let validator = null;
if (name.includes("bluesky")) {
format = "username.server (e.g. galaxyproject.bsky.social)";
validator = (tag) => /^[^@:\s]+\.[^@:\s]+$/.test(tag.value);
} else if (name.includes("mastodon")) {
format = "username@server (e.g. galaxyproject@mstdn.science)";
validator = (tag) => /^[^@:\s]+@[^@:\s]+$/.test(tag.value);
} else if (name.includes("matrix")) {
format = "username:server (e.g. bgruening:matrix.org)";
validator = (tag) => /^[^@:\s]+:[^@:\s]+$/.test(tag.value);
} else if (name.includes("linkedin")) {
format = "the part after /in/ in the organization’s LinkedIn URL (e.g. galaxy-project)";
}
if (format) {
mentionHint.textContent = `Mentions are added without @ symbol. Format: ${format}`;
tagContainer.appendChild(mentionHint);
}
const tagifyInstance = new Tagify(mIn, {
delimiters: ",| ",
trim: false,
pattern: /\S+/,
transformTag: (tagData) => {
tagData.value = tagData.value.replace(/^@/, "");
},
validate: (tag) => {
const isValid = validator ? validator(tag) : true;
if (!isValid) {
mentionHint.classList.add("text-red-500");
} else {
mentionHint.classList.remove("text-red-500");
}
return isValid;
},
});
tagifyInstance.on("input", (e) => {
if (!e.detail.value) {
mentionHint.classList.remove("text-red-500");
}
});
tagInstances[`mention-${name}`] = tagifyInstance;
tagifyInstance.on("change", updateAll);
}
if (!name.includes("slack") && !name.includes("matrix")) {
const hIn = document.createElement("input");
hIn.id = `hashtag-${name}`;
hIn.placeholder = "Hashtags";
hIn.className = "w-full p-2 border rounded";
tagContainer.appendChild(hIn);
const hashtagHint = document.createElement("div");
hashtagHint.className = "text-xs text-gray-500 italic";
hashtagHint.textContent = "Hashtags are added without # symbol.";
tagContainer.appendChild(hashtagHint);
tagInstances[`hashtag-${name}`] = new Tagify(hIn, {
delimiters: ",| ",
trim: false,
pattern: /^[A-Za-z0-9_]+$/,
transformTag: (tagData) => {
tagData.value = tagData.value.replace(/^#/, "");
},
});
tagInstances[`hashtag-${name}`].on("change", updateAll);
}
wrap.appendChild(tagContainer);
container.appendChild(wrap);
cb.addEventListener("change", () => {
tagContainer.classList.toggle("hidden", !cb.checked);
updateAll();
});
});
} catch (e) {
console.error(e);
showError("Failed to load media options");
}
```
--------------------------------
### Mastodon Plugin: Format and Create Posts (Python)
Source: https://context7.com/usegalaxy-eu/galaxy-social/llms.txt
Illustrates the usage of the `mastodon_client` from the Mastodon plugin. It shows how to format content with mentions, hashtags, and images, and then create a post on the Mastodon platform.
```python
from lib.plugins.mastodon import mastodon_client
# Initialize client
client = mastodon_client(
base_url="https://mstdn.science",
access_token="your_access_token",
max_content_length=500
)
# Format content for posting
content = "Galaxy 25.1 is here! Check out https://gxy.io/r/v25.1"
mentions = ["galaxyfreiburg@bawu.social"]
hashtags = ["UseGalaxy", "GalaxyProject"]
images = [{"url": "https://example.com/image.png", "alt_text": "Release banner"}]
formatted_content, preview, warnings = client.format_content(
content=content,
mentions=mentions,
hashtags=hashtags,
images=images
)
# formatted_content contains: {"body": "...", "images": [...], "chunks": [...]}
# Create the post (returns success status and URL)
success, post_url = client.create_post(formatted_content)
# success: True
# post_url: "https://mstdn.science/@galaxyproject/123456789"
```
--------------------------------
### Format Content for Custom Platform (Python)
Source: https://context7.com/usegalaxy-eu/galaxy-social/llms.txt
The `format_content` method in `custom_client` prepares text, mentions, and hashtags for posting to a custom platform. It handles basic formatting and prepares for potential chunking based on `max_content_length`. Dependencies include the `custom_client` class itself.
```python
class custom_client:
def __init__(self, **kwargs):
self.api_key = kwargs.get("api_key")
self.max_content_length = kwargs.get("max_content_length", 280)
def format_content(self, content, mentions, hashtags, images, **kwargs):
"""Format content for the platform. Returns (formatted_content, preview, warnings)."""
formatted_mentions = " ".join([f"@{m}" for m in mentions])
formatted_hashtags = " ".join([f"#{h}" for h in hashtags])
full_content = f"{content}\n{formatted_mentions}\n{formatted_hashtags}"
formatted_content = {
"body": full_content,
"images": images,
"chunks": [full_content] # Split into chunks if exceeds max_content_length
}
preview = full_content
warnings = ""
return formatted_content, preview, warnings
```
--------------------------------
### Create Post for Custom Platform (Python)
Source: https://context7.com/usegalaxy-eu/galaxy-social/llms.txt
The `create_post` method in `custom_client` handles the actual publishing of content to the custom platform. It includes basic error handling for the API call. The success status and the URL of the created post are returned. This method relies on the `api_key` configured in the client.
```python
class custom_client:
# ... (previous methods)
def create_post(self, content, **kwargs):
"""Publish content. Returns (success: bool, url: str|None)."""
# Implementation for posting to the platform
try:
# API call to create post
post_url = "https://custom-platform.com/post/123"
return True, post_url
except Exception as e:
print(f"Error: {e}")
return False, None
```
--------------------------------
### Post to Matrix with Galaxy Social Plugin (Python)
Source: https://context7.com/usegalaxy-eu/galaxy-social/llms.txt
This Python code snippet shows how to use the Matrix plugin to post messages to Matrix rooms. It details client initialization with a base URL, access token, and room ID, followed by formatting content with mentions and hashtags, and creating a post. The plugin supports markdown formatting and user mention resolution.
```python
from lib.plugins.matrix import matrix_client
# Initialize client
client = matrix_client(
base_url="https://matrix.org",
access_token="your_matrix_access_token",
room_id="!ArjKhGljVCmzqxhauY:matrix.org"
)
# Format content with Matrix mentions
content = "Galaxy 25.1 is now available with exciting new features!"
mentions = ["user:matrix.org"] # Matrix user IDs
hashtags = ["galaxy", "bioinformatics"]
images = [{"url": "https://example.com/banner.png", "alt_text": "Banner"}]
formatted_content, preview, warnings = client.format_content(
content=content,
mentions=mentions,
hashtags=hashtags,
images=images
)
# Create post in room
success, event_url = client.create_post(formatted_content)
# success: True
# event_url: "https://matrix.to/#/!ArjKhGljVCmzqxhauY:matrix.org/$event_id"
```
--------------------------------
### Post to Bluesky with Galaxy Social Plugin (Python)
Source: https://context7.com/usegalaxy-eu/galaxy-social/llms.txt
This Python code snippet demonstrates how to use the Bluesky plugin to post content to Bluesky. It includes initialization of the client with credentials, formatting content with mentions, hashtags, and images, and creating a post. The plugin handles automatic facet detection and image compression.
```python
from lib.plugins.bluesky import bluesky_client
# Initialize client
client = bluesky_client(
base_url="https://bsky.social",
username="galaxyproject.bsky.social",
password="your_app_password",
max_content_length=300
)
# Format content with mentions and hashtags
content = "Big news! Galaxy 25.1 released https://gxy.io/r/v25.1"
mentions = ["galaxyproject.bsky.social"]
hashtags = ["UseGalaxy"]
images = [{"url": "https://example.com/banner.jpg", "alt_text": "Banner"}]
formatted_content, preview, warnings = client.format_content(
content=content,
mentions=mentions,
hashtags=hashtags,
images=images
)
# Create threaded post
success, post_url = client.create_post(formatted_content)
# success: True
# post_url: "https://bsky.app/profile/galaxyproject.bsky.social/post/abc123"
```
--------------------------------
### Create a Custom Social Media Plugin (Python)
Source: https://context7.com/usegalaxy-eu/galaxy-social/llms.txt
This Python code snippet outlines the structure for creating a custom social media plugin. To add support for a new platform, you need to create a plugin class that implements the `format_content` and `create_post` methods. The new plugin should then be registered in the `plugins.yml` configuration file.
```python
# Example structure for a custom plugin
# class CustomSocialMediaClient:
# def __init__(self, ...):
# pass
#
# def format_content(self, content, mentions, hashtags, images):
# # Format content for the custom platform
# formatted_content = ...
# return formatted_content, preview, warnings
#
# def create_post(self, formatted_content):
# # Post the formatted content to the custom platform
# success, post_url = ...
# return success, post_url
```
--------------------------------
### Integrate Galaxy Social with GitHub Actions (Python)
Source: https://context7.com/usegalaxy-eu/galaxy-social/llms.txt
This Python code snippet shows how to use the `github_run.py` module for automated posting via GitHub Actions. It covers initializing the GitHub run with a PR number, retrieving markdown files from a PR, commenting with previews, initializing tracking branches, committing processed files, and creating retry PRs for failed posts.
```python
from github_run import github_run
# Initialize with PR number
gh = github_run(
pr_number=123,
processed_files_path="processed_files.json",
processed_files_branch="processed_files"
)
# Get markdown files from PR
files_to_process = gh.get_files()
# Returns: ["posts/2025/announcement.md", ...]
# Post a comment with preview
gh.comment("Preview generated successfully!", preview=True)
# Initialize tracking branch if needed
gh.initialize_processed_files_branch()
# Commit processed files after posting
processed_files = gh.commit_processed_files()
# Returns: {"posts/2025/announcement.md": {"mastodon-galaxyproject": True, ...}}
# Create retry PR for failed posts
not_posted = {"posts/2025/announcement.md": ["linkedin-galaxyproject"]}
gh.new_pr(not_posted)
```
--------------------------------
### Manual Post Creation Link
Source: https://github.com/usegalaxy-eu/galaxy-social/blob/main/README.md
Offers a link to manually create a new social media post. This link includes pre-filled parameters for media, mentions, and hashtags across multiple platforms, suggesting a template-based approach.
```html
```
--------------------------------
### Process Markdown Files with Galaxy Social Core API (Python)
Source: https://context7.com/usegalaxy-eu/galaxy-social/llms.txt
Demonstrates how to use the `galaxy_social` class to process single or multiple markdown files for social media posting. It supports preview mode and saving processed file information.
```python
from lib.galaxy_social import galaxy_social
# Initialize with preview mode (dry-run)
gs = galaxy_social(preview=True)
# Process a single markdown file
processed_files = {}
processed_files, message = gs.process_markdown_file(
file_path="posts/2025/2025-01-07-announcement.md",
processed_files=processed_files
)
print(message)
# Output: "👋 Hello! I'm your friendly social media assistant. Below are the previews of this post..."
# Process multiple files and save results
files_to_process = [
"posts/2025/announcement.md",
"posts/2025/release-notes.md"
]
message = gs.process_files(files_to_process, "processed_files.json")
print(message)
```
--------------------------------
### CSS Styling for Galaxy Social Post Creator Preview
Source: https://github.com/usegalaxy-eu/galaxy-social/blob/main/docs/index.html
This CSS code defines the styling for the preview pane of the Galaxy Social Post Creator. It sets properties for padding, font, colors, and margins for various HTML elements like headings, paragraphs, lists, blockquotes, code, links, and images, ensuring a consistent and readable preview.
```css
.editor-preview, .editor-preview-side {
padding: 1rem;
font-family: inherit;
line-height: 1.6;
background: white;
color: black;
overflow-wrap: break-word;
}
.editor-preview h1, .editor-preview-side h1 {
font-size: 2em;
font-weight: bold;
margin: 1rem 0;
}
.editor-preview h2, .editor-preview-side h2 {
font-size: 1.5em;
font-weight: bold;
margin: 1rem 0;
}
.editor-preview h3, .editor-preview-side h3 {
font-size: 1.25em;
font-weight: bold;
margin: 1rem 0;
}
.editor-preview h4, .editor-preview-side h4 {
font-size: 1.125em;
font-weight: bold;
margin: 1rem 0;
}
.editor-preview h5, .editor-preview-side h5 {
font-size: 1em;
font-weight: bold;
margin: 1rem 0;
}
.editor-preview h6, .editor-preview-side h6 {
font-size: 0.875em;
font-weight: bold;
margin: 1rem 0;
}
.editor-preview p, .editor-preview-side p {
margin: 1rem 0;
line-height: 1.6;
}
.editor-preview ul, .editor-preview-side ul {
list-style-type: disc;
padding-left: 2rem;
}
.editor-preview ol, .editor-preview-side ol {
list-style-type: decimal;
padding-left: 2rem;
}
.editor-preview blockquote, .editor-preview-side blockquote {
border-left: 4px solid #ccc;
padding-left: 1rem;
color: #555;
font-style: italic;
}
.editor-preview code, .editor-preview-side code {
background-color: #f3f4f6;
padding: 0.2em 0.4em;
border-radius: 4px;
font-family: monospace;
}
.editor-preview a, .editor-preview-side a {
color: #2563eb;
text-decoration: underline;
word-break: break-all;
transition: color 0.2s;
}
.editor-preview a:hover, .editor-preview-side a:hover {
color: #1d4ed8;
text-decoration: underline;
}
.editor-preview img, .editor-preview-side img {
max-width: 35%;
height: auto;
display: block;
margin: 1rem 0;
border-radius: 0.5rem;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}
```
--------------------------------
### Configure Custom Platform Plugin (YAML)
Source: https://context7.com/usegalaxy-eu/galaxy-social/llms.txt
This YAML configuration snippet shows how to enable and configure the `custom-account` plugin within Galaxy Social. It specifies the class to use, enables the plugin, and provides configuration details such as the API key and maximum content length, often sourced from environment variables.
```yaml
# Add to plugins.yml
plugins:
- name: custom-account
class: custom_platform.custom_client
enabled: true
config:
api_key: $CUSTOM_PLATFORM_API_KEY
max_content_length: 280
```
--------------------------------
### Galaxy Social Post Template (YAML)
Source: https://github.com/usegalaxy-eu/galaxy-social/blob/main/README.md
This is the standard template for creating a new post on Galaxy Social. It uses YAML for metadata like media targets, mentions, and hashtags, followed by the main content. Ensure all required fields are present and correctly formatted. Markdown is supported for content but may not render on all platforms.
```yaml
---
media:
- mastodon-eu-freiburg
- matrix-eu-announce
- mastodon-galaxyproject
- bluesky-galaxyproject
- linkedin-galaxyproject
mentions:
mastodon-eu-freiburg:
- galaxyproject@mstdn.science
mastodon-galaxyproject:
- galaxyfreiburg@bawü.social
bluesky-galaxyproject:
- galaxyproject.bsky.social
hashtags:
mastodon-eu-freiburg:
- UseGalaxy
- GalaxyProject
- UniFreiburg
- EOSC
- EuroScienceGateway
mastodon-galaxyproject:
- UseGalaxy
bluesky-galaxyproject:
- UseGalaxy
- GalaxyProject
- UniFreiburg
- EOSC
- EuroScienceGateway
linkedin-galaxyproject:
- UseGalaxy
- GalaxyProject
- UniFreiburg
- EOSC
- EuroScienceGateway
---
Your text content goes here. (Markdown syntax will not pass to Bluesky, Mastodon, and Linkedin!)
For images just drag and drop them here. they will look like this:

```
--------------------------------
### Emoji Picker Integration for EasyMDE Editor
Source: https://github.com/usegalaxy-eu/galaxy-social/blob/main/docs/index.html
This JavaScript code manages an emoji picker for the EasyMDE markdown editor. It handles opening, closing, and selecting emojis, inserting them into the editor's current cursor position. It also includes event listeners for clicks and key presses on the page to manage the emoji picker's visibility. Dependencies include an EasyMDE editor instance and an HTML element for the emoji picker.
```javascript
const emojiPicker = document.getElementById("emoji-picker");
let isEmojiOpen = false;
let currentEditor = null;
let emojiBtn = null;
function handleEmojiSelect(e) {
currentEditor.codemirror.replaceSelection(e.detail.unicode);
closeEmojiPicker();
}
function closeEmojiPicker() {
if (!isEmojiOpen) return;
emojiPicker.style.display = "none";
emojiPicker.removeEventListener("emoji-click", handleEmojiSelect);
isEmojiOpen = false;
}
function onPageClick(e) {
if (
isEmojiOpen &&
!emojiPicker.contains(e.target) &&
!emojiBtn.contains(e.target)
) {
closeEmojiPicker();
}
}
function onPageKey(e) {
if (isEmojiOpen && e.key === "Escape") {
closeEmojiPicker();
}
}
document.addEventListener("click", onPageClick);
document.addEventListener("keydown", onPageKey);
window.addEventListener("DOMContentLoaded", async () => {
easyMDE = new EasyMDE({
element: document.getElementById("post-content"),
spellChecker: false,
toolbar: [
"bold",
"italic",
"heading",
"|",
"quote",
"unordered-list",
"ordered-list",
"|",
"link",
"image",
"table",
"horizontal-rule",
{
name: "emoji",
action: function (editor) {
currentEditor = editor;
if (!emojiBtn) {
emojiBtn = Array.from(
document.querySelectorAll(".editor-toolbar button")
).find((b) => b.getAttribute("title") === "Insert Emoji");
if (!emojiBtn) return;
}
if (isEmojiOpen) {
return closeEmojiPicker();
}
const rect = emojiBtn.getBoundingClientRect();
emojiPicker.style.left = `${rect.left + window.scrollX}px`;
emojiPicker.style.top = `${rect.bottom + window.scrollY}px`;
emojiPicker.style.display = "block";
isEmojiOpen = true;
emojiPicker.addEventListener("emoji-click", handleEmojiSelect, {
once: true,
});
},
className: "fa fa-smile-o",
title: "Insert Emoji",
},
],
});
});
```
--------------------------------
### Markdown Generation and Validation for Social Media Posts
Source: https://github.com/usegalaxy-eu/galaxy-social/blob/main/docs/index.html
This JavaScript function `updateAll` validates user input for a social media post, including title, content, and selected platforms. It generates metadata (media, mentions, hashtags) and constructs the final markdown content. It also calculates potential post splits based on character limits for different platforms. Dependencies include an EasyMDE editor instance and pre-defined `pluginMax` and `tagInstances` objects.
```javascript
function updateAll() {
clearError();
const title = document.getElementById("post-title").value.trim();
const content = easyMDE.value().trim();
const selected = Array.from( document.querySelectorAll("input.media-checkbox:checked") ).map((cb) => cb.value);
// Validation
if (!title) {
hideOutput();
return showError("Title is required.");
}
if (selected.length === 0) {
hideOutput();
return showError("Select at least one media platform.");
}
if (!content) {
hideOutput();
return showError("Content cannot be empty.");
}
// split warnings under content
const warnDiv = document.getElementById("warnings");
const warnings = [];
selected.forEach((name) => {
const maxLen = pluginMax[name];
if (maxLen && content.length > maxLen) {
const parts = Math.ceil(content.length / maxLen);
warnings.push(
`⚠️ Will split into ${parts} posts on ${name} (max ${maxLen} chars each)`
);
}
});
if (warnings.length > 0) {
warnDiv.innerHTML = warnings.map((w) => `