File size: 3,881 Bytes
b1f1d99 246cab2 b1f1d99 246cab2 b1f1d99 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | from typing import Any, Optional
from smolagents.tools import Tool
import os
import upload_post
class UploadPostTextTool(Tool):
name = "upload_post_text"
description = "Publish (or schedule) a text-only post on social networks through Upload-Post: X, LinkedIn, Facebook, Threads, Bluesky, Discord, Telegram, Google Business, Slack, Mastodon and more. Returns a dict with the per-platform results, or a job_id when the post is scheduled or queued."
inputs = {'text': {'type': 'string', 'description': 'The text of the post.'}, 'platforms': {'type': 'array', 'description': "Platforms to publish to, e.g. ['x', 'linkedin']. Allowed: linkedin, x, facebook, threads, bluesky, discord, telegram, google_business, slack, mastodon, nostr, lemmy, devto, hashnode, wordpress, whop, listmonk."}, 'profile': {'type': 'string', 'description': 'Upload-Post profile (username) to publish from. Omit to use the default profile. Use upload_post_profiles to list profiles.', 'nullable': True}, 'link_url': {'type': 'string', 'description': 'URL shown as a link preview card on LinkedIn, Bluesky and Facebook.', 'nullable': True}, 'scheduled_date': {'type': 'string', 'description': "ISO-8601 date/time to schedule the post instead of publishing now, e.g. '2026-12-31T18:00:00Z'. Must be in the future (max 365 days).", 'nullable': True}, 'timezone': {'type': 'string', 'description': "IANA timezone (e.g. 'Europe/Madrid') for scheduled_date. Defaults to UTC.", 'nullable': True}, 'add_to_queue': {'type': 'boolean', 'description': "Put the post in the profile's next free queue slot. Not combinable with scheduled_date.", 'nullable': True}, 'first_comment': {'type': 'string', 'description': 'First comment posted right after publishing (X, LinkedIn, Facebook, Threads, Bluesky).', 'nullable': True}}
output_type = "object"
def __init__(self, api_key=None, profile=None, **kwargs):
super().__init__()
import os
self.api_key = api_key or os.environ.get("UPLOAD_POST_API_KEY")
self.profile = profile or os.environ.get("UPLOAD_POST_PROFILE")
self.client = None
def setup(self):
from upload_post import UploadPostClient
if not self.api_key:
raise ValueError(
"Missing Upload-Post API key: pass api_key=... or set UPLOAD_POST_API_KEY."
)
self.client = UploadPostClient(self.api_key)
self.is_initialized = True
def forward(
self,
text: str,
platforms: list,
profile: Optional[str] = None,
link_url: Optional[str] = None,
scheduled_date: Optional[str] = None,
timezone: Optional[str] = None,
add_to_queue: Optional[bool] = None,
first_comment: Optional[str] = None,
) -> Any:
if not self.is_initialized:
self.setup()
user = profile or self.profile
if not user:
raise ValueError(
"No Upload-Post profile: pass profile=... or set UPLOAD_POST_PROFILE. "
"Call upload_post_profiles to list the available profiles."
)
if isinstance(platforms, str):
platforms = [p.strip() for p in platforms.split(",") if p.strip()]
if not platforms:
raise ValueError("At least one platform is required.")
if scheduled_date and add_to_queue:
raise ValueError("scheduled_date and add_to_queue cannot be used together.")
options = {
"link_url": link_url,
"scheduled_date": scheduled_date,
"timezone": timezone,
"add_to_queue": add_to_queue,
"first_comment": first_comment,
}
options = {k: v for k, v in options.items() if v is not None and v != ""}
return self.client.upload_text(
title=text, user=user, platforms=list(platforms), **options
)
|