Add Activepieces integration for workflow automation
- Add Activepieces fork with SmoothSchedule custom piece - Create integrations app with Activepieces service layer - Add embed token endpoint for iframe integration - Create Automations page with embedded workflow builder - Add sidebar visibility fix for embed mode - Add list inactive customers endpoint to Public API - Include SmoothSchedule triggers: event created/updated/cancelled - Include SmoothSchedule actions: create/update/cancel events, list resources/services/customers 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": ["../../../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# pieces-bluesky
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Building
|
||||
|
||||
Run `nx build pieces-bluesky` to build the library.
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "@activepieces/piece-bluesky",
|
||||
"version": "0.0.8",
|
||||
"type": "commonjs",
|
||||
"main": "./src/index.js",
|
||||
"types": "./src/index.d.ts",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0",
|
||||
"@atproto/api": "0.16.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"name": "pieces-bluesky",
|
||||
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "packages/pieces/community/bluesky/src",
|
||||
"projectType": "library",
|
||||
"release": {
|
||||
"version": {
|
||||
"manifestRootsToUpdate": [
|
||||
"dist/{projectRoot}"
|
||||
],
|
||||
"currentVersionResolver": "git-tag",
|
||||
"fallbackCurrentVersionResolver": "disk"
|
||||
}
|
||||
},
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nx/js:tsc",
|
||||
"outputs": [
|
||||
"{options.outputPath}"
|
||||
],
|
||||
"options": {
|
||||
"outputPath": "dist/packages/pieces/community/bluesky",
|
||||
"tsConfig": "packages/pieces/community/bluesky/tsconfig.lib.json",
|
||||
"packageJson": "packages/pieces/community/bluesky/package.json",
|
||||
"main": "packages/pieces/community/bluesky/src/index.ts",
|
||||
"assets": [
|
||||
"packages/pieces/community/bluesky/*.md",
|
||||
{
|
||||
"input": "packages/pieces/community/bluesky/src/i18n",
|
||||
"output": "./src/i18n",
|
||||
"glob": "**/!(i18n.json)"
|
||||
}
|
||||
],
|
||||
"buildableProjectDepsInPackageJsonType": "dependencies",
|
||||
"updateBuildableProjectDepsInPackageJson": true
|
||||
},
|
||||
"dependsOn": [
|
||||
"^build",
|
||||
"prebuild"
|
||||
]
|
||||
},
|
||||
"nx-release-publish": {
|
||||
"options": {
|
||||
"packageRoot": "dist/{projectRoot}"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint",
|
||||
"outputs": [
|
||||
"{options.outputFile}"
|
||||
]
|
||||
},
|
||||
"prebuild": {
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"cwd": "packages/pieces/community/bluesky",
|
||||
"command": "bun install --no-save --silent"
|
||||
},
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"PDS Host": "PDS-Host",
|
||||
"Identifier": "Identifier",
|
||||
"Password": "Kennwort",
|
||||
"The Personal Data Server host. Leave empty for default Bluesky network (https://bsky.social)": "Der Personal Data Server Host. Leer lassen für das Standard Bluesky Netzwerk (https://bsky.social)",
|
||||
"Your Bluesky handle or email address": "Ihr Bluesky Handle oder Ihre E-Mail-Adresse",
|
||||
"Your Bluesky account password or app password": "Ihr Bluesky Konto-Passwort oder App-Passwort",
|
||||
"\nTo authenticate with Bluesky:\n\n1. **PDS Host**: The Personal Data Server host. Leave empty to use the default Bluesky network (https://bsky.social).\n2. **Identifier**: Your Bluesky handle (e.g., yourhandle.bsky.social) or email address.\n3. **Password**: Your Bluesky account password or app password.\n\nFor enhanced security, consider using an app password from your Bluesky account settings.\n": "\nZur Authentifizierung mit Bluesky:\n\n1. **PDS Host**: Der Personal Data Server Host. Leer lassen um das Standard Bluesky Netzwerk (https://bsky.social) zu verwenden.\n2. **Identifier**: Ihr Bluesky Handle (z. yourhandle.bsky.social) oder E-Mail-Adresse.\n3. **Passwort**: Ihr Bluesky Konto-Passwort oder App-Passwort.\n\nUm die Sicherheit zu verbessern, sollten Sie ein App-Passwort aus Ihren Bluesky-Kontoeinstellungen verwenden.\n",
|
||||
"Create Post": "Beitrag erstellen",
|
||||
"Like Post": "Like Beitrag",
|
||||
"Repost Post": "Beitrag wiederholen",
|
||||
"Find Post": "Post finden",
|
||||
"Find Thread": "Thread finden",
|
||||
"Create a new post on Bluesky": "Erstelle einen neuen Beitrag auf Bluesky",
|
||||
"Like a post on Bluesky": "Gefällt mir einen Beitrag auf Bluesky",
|
||||
"Share someone else's post to your timeline": "Teile jemandes Beitrag auf deiner Zeitleiste",
|
||||
"Get detailed information about a specific post": "Erhalte detaillierte Informationen zu einem bestimmten Beitrag",
|
||||
"Get a full conversation thread with replies": "Erhalte einen vollständigen Thread mit Antworten",
|
||||
"Post Type": "Beitragstyp",
|
||||
"Post Text": "Beitragstext",
|
||||
"Post Language": "Post-Sprache",
|
||||
"Image URLs": "Bild-URLs",
|
||||
"Image Descriptions": "Bildbeschreibungen",
|
||||
"Video URL": "Video-URL",
|
||||
"Video Description": "Videobeschreibung",
|
||||
"Video Captions": "Videounterlagen",
|
||||
"Link to Share": "Link zum Teilen",
|
||||
"Reply to Post": "Auf Beitrag antworten",
|
||||
"Thread Posts": "Thread-Beiträge",
|
||||
"Hashtags": "Hashtags",
|
||||
"Content Warnings": "Inhaltswarnungen",
|
||||
"Audience": "Zielgruppe",
|
||||
"Select Method": "Methode auswählen",
|
||||
"Select Post": "Beitrag auswählen",
|
||||
"Post URL": "Post-URL",
|
||||
"Thread Depth": "Thread-Tiefe",
|
||||
"Parent Height": "Übergeordnete Höhe",
|
||||
"Type of content you're sharing": "Art der Inhalte, die Sie teilen",
|
||||
"What do you want to post? (Max 300 characters)": "Was möchtest du posten? (max. 300 Zeichen)",
|
||||
"Language of your post": "Sprache Ihres Beitrags",
|
||||
"Add up to 4 images by URL": "Bis zu 4 Bilder nach URL hinzufügen",
|
||||
"Describe each image for accessibility": "Beschreiben Sie jedes Bild für Barrierefreiheit",
|
||||
"Link to video file (MP4, max 100MB)": "Link zur Videodatei (MP4, max 100MB)",
|
||||
"Describe the video for accessibility": "Beschreiben Sie das Video für Barrierefreiheit",
|
||||
"Caption file URLs (optional)": "Untertitel-Datei-URLs (optional)",
|
||||
"URL to share with your post": "URL zum Teilen mit Ihrem Beitrag",
|
||||
"URL of post to reply to": "URL des zu antwortenden Beitrags",
|
||||
"Create additional connected posts": "Erstelle weitere verbundene Beiträge",
|
||||
"Add hashtags (e.g., tech,bluesky)": "Hashtags hinzufügen (z. B. tech,bluesky)",
|
||||
"Add warnings for sensitive content": "Warnungen für vertrauliche Inhalte hinzufügen",
|
||||
"Who can see this post": "Wer kann diesen Beitrag sehen",
|
||||
"How to choose the post": "Wie man den Beitrag auswählt",
|
||||
"Choose from your recent timeline posts (only when \"From my timeline\" is selected above)": "Wählen Sie aus Ihren letzten Timeline-Beiträgen (nur wenn \"Aus meiner Timeline\" oben ausgewählt ist)",
|
||||
"Paste the Bluesky post URL": "Die Bluesky Post URL einfügen",
|
||||
"Paste the Bluesky post URL (e.g., https://bsky.app/profile/username.bsky.social/post/xxx)": "Füge die Bluesky Post-URL ein (z.B. https://bsky.app/profile/username.bsky.social/post/xxx)",
|
||||
"How many levels deep to retrieve replies": "Wie viele Level tief um Antworten zu erhalten",
|
||||
"How many parent posts to retrieve": "Wie viele übergeordnete Beiträge abrufen sollen",
|
||||
"Text Post": "Text-Beitrag",
|
||||
"Photo Post": "Foto-Beitrag",
|
||||
"Link Share": "Link teilen",
|
||||
"Reply": "Antwort",
|
||||
"Repost with Comment": "Reposten mit Kommentar",
|
||||
"English": "Englisch",
|
||||
"Spanish": "Spanisch",
|
||||
"French": "Französisch",
|
||||
"German": "Deutsch",
|
||||
"Italian": "Italienisch",
|
||||
"Portuguese": "Portugiesisch",
|
||||
"Japanese": "Japanisch",
|
||||
"Korean": "Koreanisch",
|
||||
"Chinese": "Chinesisch",
|
||||
"Russian": "Russisch",
|
||||
"Arabic": "Arabisch",
|
||||
"Hindi": "Hannah",
|
||||
"Dutch": "Niederländisch",
|
||||
"Swedish": "Schwedisch",
|
||||
"Other": "Andere",
|
||||
"Adult Content": "Erwachseneninhalt",
|
||||
"Graphic Content": "Grafikinhalt",
|
||||
"Sensitive Topic": "Sensitives Thema",
|
||||
"Violence": "Gewalt",
|
||||
"Spam/Promotional": "Spam/Werbung",
|
||||
"Everyone (Public)": "Alle (öffentlich)",
|
||||
"Followers only": "Nur Follower",
|
||||
"Private/Unlisted": "Privat/Nicht gelistet",
|
||||
"From my timeline": "Aus meiner Zeitleiste",
|
||||
"Enter URL manually": "URL manuell eingeben",
|
||||
"1 level": "1 Level",
|
||||
"2 levels": "2 Stufen",
|
||||
"3 levels": "3 Stufen",
|
||||
"5 levels": "5 Stufen",
|
||||
"10 levels": "10 Level",
|
||||
"20 levels": "20 Level",
|
||||
"50 levels": "50 Level",
|
||||
"100 levels (max)": "100 Level (max)",
|
||||
"No parents": "Keine Eltern",
|
||||
"1 parent": "1 Elternteil",
|
||||
"2 parents": "2 Eltern",
|
||||
"3 parents": "3 Eltern",
|
||||
"5 parents": "5 Eltern",
|
||||
"10 parents": "10 Eltern",
|
||||
"20 parents": "20 Eltern",
|
||||
"All parents (80 max)": "Alle Eltern (80 max)",
|
||||
"New Posts by Author": "Neue Beiträge vom Autor",
|
||||
"New Follower on Account": "Neuer Follower auf Konto",
|
||||
"New Timeline Posts": "Neue Timeline-Beiträge",
|
||||
"New Post (with Search Options)": "Neuer Beitrag (mit Suchoptionen)",
|
||||
"Triggers when a selected author creates a new post": "Löst aus, wenn ein ausgewählter Autor einen neuen Beitrag erstellt",
|
||||
"Triggers when someone new follows your Bluesky account": "Löst aus, wenn jemand neu deinem Bluesky-Konto folgt",
|
||||
"Triggers when new posts appear in your timeline": "Auslöser wenn neue Beiträge in der Zeitleiste erscheinen",
|
||||
"Triggers when posts match your search criteria": "Löst aus, wenn Beiträge Ihren Suchkriterien entsprechen",
|
||||
"How to select author?": "Wie wählt man den Autor aus?",
|
||||
"Select Author": "Autor auswählen",
|
||||
"Author Handle": "Autoren-Handle",
|
||||
"Include Replies": "Antworten einbeziehen",
|
||||
"Include Reposts": "Reposts einbeziehen",
|
||||
"Search Query": "Suchanfrage",
|
||||
"Language Filter": "Sprachfilter",
|
||||
"Filter by Images": "Nach Bildern filtern",
|
||||
"Filter by Videos": "Nach Videos filtern",
|
||||
"Sort Order": "Sortierung",
|
||||
"Choose how to select the author": "Wählen Sie, wie der Autor ausgewählt wird",
|
||||
"Choose from accounts you follow": "Wählen Sie aus Konten, denen Sie folgen",
|
||||
"Enter the Bluesky username (e.g., username.bsky.social)": "Geben Sie den Bluesky Benutzernamen ein (z. B. username.bsky.social)",
|
||||
"Include reply posts by this author": "Antwortbeiträge dieses Autors einbeziehen",
|
||||
"Include posts that this author reposted": "Einfügen von Beiträgen, die dieser Autor reposted",
|
||||
"Keywords, hashtags (#example), or mentions (@handle) to find": "Stichwörter, Hashtags (#Beispiel) oder Erwähnungen (@handle) zu finden",
|
||||
"Filter by language": "Nach Sprache filtern",
|
||||
"Only posts with/without images": "Nur Beiträge mit/ohne Bilder",
|
||||
"Only posts with/without videos": "Nur Beiträge mit/ohne Videos",
|
||||
"How to sort results": "Ergebnisse sortieren",
|
||||
"From my following list": "Von meiner folgenden Liste",
|
||||
"Enter handle manually": "Handle manuell eingeben",
|
||||
"Latest First": "Neueste zuerst",
|
||||
"Most Popular": "Beliebteste"
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"PDS Host": "Host PDS",
|
||||
"Identifier": "Identifier",
|
||||
"Password": "Contraseña",
|
||||
"The Personal Data Server host. Leave empty for default Bluesky network (https://bsky.social)": "El servidor de datos personales. Dejar vacío para la red Bluesky predeterminada (https://bsky.social)",
|
||||
"Your Bluesky handle or email address": "Su nombre de usuario o dirección de correo electrónico Bluesky",
|
||||
"Your Bluesky account password or app password": "Su contraseña de cuenta Bluesky o su contraseña de aplicación",
|
||||
"\nTo authenticate with Bluesky:\n\n1. **PDS Host**: The Personal Data Server host. Leave empty to use the default Bluesky network (https://bsky.social).\n2. **Identifier**: Your Bluesky handle (e.g., yourhandle.bsky.social) or email address.\n3. **Password**: Your Bluesky account password or app password.\n\nFor enhanced security, consider using an app password from your Bluesky account settings.\n": "\nTo authenticate with Bluesky:\n\n1. **PDS Host**: The Personal Data Server host. Leave empty to use the default Bluesky network (https://bsky.social).\n2. **Identifier**: Your Bluesky handle (e.g., yourhandle.bsky.social) or email address.\n3. **Password**: Your Bluesky account password or app password.\n\nFor enhanced security, consider using an app password from your Bluesky account settings.\n",
|
||||
"Create Post": "Crear publicación",
|
||||
"Like Post": "Mensaje Me Gusta",
|
||||
"Repost Post": "Repostar post",
|
||||
"Find Post": "Buscar post",
|
||||
"Find Thread": "Buscar hilo",
|
||||
"Create a new post on Bluesky": "Crear una nueva publicación en Bluesky",
|
||||
"Like a post on Bluesky": "Como un post en Bluesky",
|
||||
"Share someone else's post to your timeline": "Comparte la publicación de otra persona en tu línea de tiempo",
|
||||
"Get detailed information about a specific post": "Obtener información detallada sobre un post específico",
|
||||
"Get a full conversation thread with replies": "Obtén un hilo de conversación completo con respuestas",
|
||||
"Post Type": "Tipo de Post",
|
||||
"Post Text": "Publicar texto",
|
||||
"Post Language": "Idioma del post",
|
||||
"Image URLs": "URLs de imagen",
|
||||
"Image Descriptions": "Descripciones de imagen",
|
||||
"Video URL": "URL del vídeo",
|
||||
"Video Description": "Descripción del vídeo",
|
||||
"Video Captions": "Subtítulos de vídeo",
|
||||
"Link to Share": "Enlace para compartir",
|
||||
"Reply to Post": "Responder al mensaje",
|
||||
"Thread Posts": "Mensajes del hilo",
|
||||
"Hashtags": "Etiquetas",
|
||||
"Content Warnings": "Advertencias de contenido",
|
||||
"Audience": "Público",
|
||||
"Select Method": "Seleccionar método",
|
||||
"Select Post": "Seleccionar Post",
|
||||
"Post URL": "Publicar URL",
|
||||
"Thread Depth": "Profundidad del hilo",
|
||||
"Parent Height": "Altura padre",
|
||||
"Type of content you're sharing": "Tipo de contenido que estás compartiendo",
|
||||
"What do you want to post? (Max 300 characters)": "¿Qué quieres publicar? (máximo 300 caracteres)",
|
||||
"Language of your post": "Idioma de tu publicación",
|
||||
"Add up to 4 images by URL": "Añadir hasta 4 imágenes por URL",
|
||||
"Describe each image for accessibility": "Describa cada imagen para su accesibilidad",
|
||||
"Link to video file (MP4, max 100MB)": "Enlace al archivo de vídeo (MP4, max 100MB)",
|
||||
"Describe the video for accessibility": "Describa el vídeo para su accesibilidad",
|
||||
"Caption file URLs (optional)": "URL del archivo de subtítulos (opcional)",
|
||||
"URL to share with your post": "URL para compartir con tu publicación",
|
||||
"URL of post to reply to": "URL del mensaje al que responder",
|
||||
"Create additional connected posts": "Crear mensajes adicionales conectados",
|
||||
"Add hashtags (e.g., tech,bluesky)": "Añadir hashtags (por ej., tech,bluesky)",
|
||||
"Add warnings for sensitive content": "Añadir advertencias para contenido sensible",
|
||||
"Who can see this post": "Quién puede ver esta publicación",
|
||||
"How to choose the post": "Cómo elegir el post",
|
||||
"Choose from your recent timeline posts (only when \"From my timeline\" is selected above)": "Elija entre sus publicaciones recientes de la línea de tiempo (sólo cuando \"De mi línea de tiempo\" está seleccionado arriba)",
|
||||
"Paste the Bluesky post URL": "Pegar la URL del post Bluesky",
|
||||
"Paste the Bluesky post URL (e.g., https://bsky.app/profile/username.bsky.social/post/xxx)": "Pegar la URL del post Bluesky (por ejemplo, https://bsky.app/profile/username.bsky.social/post/xxx)",
|
||||
"How many levels deep to retrieve replies": "Cuántos niveles hay para recuperar respuestas",
|
||||
"How many parent posts to retrieve": "Cuántos mensajes padre recuperar",
|
||||
"Text Post": "Mensaje de texto",
|
||||
"Photo Post": "Post de foto",
|
||||
"Link Share": "Compartir Enlace",
|
||||
"Reply": "Responder",
|
||||
"Repost with Comment": "Republicar con comentario",
|
||||
"English": "Inglés",
|
||||
"Spanish": "Español",
|
||||
"French": "Francés",
|
||||
"German": "Alemán",
|
||||
"Italian": "Italiano",
|
||||
"Portuguese": "Portugués",
|
||||
"Japanese": "Japonés",
|
||||
"Korean": "Coreano",
|
||||
"Chinese": "Chino",
|
||||
"Russian": "Ruso",
|
||||
"Arabic": "Árabe",
|
||||
"Hindi": "Hindú",
|
||||
"Dutch": "Holandés",
|
||||
"Swedish": "Sueco",
|
||||
"Other": "Otro",
|
||||
"Adult Content": "Contenido adulto",
|
||||
"Graphic Content": "Contenido gráfico",
|
||||
"Sensitive Topic": "Tema sensible",
|
||||
"Violence": "Violencia",
|
||||
"Spam/Promotional": "Spam/Promocional",
|
||||
"Everyone (Public)": "Todo el mundo (público)",
|
||||
"Followers only": "Sólo seguidores",
|
||||
"Private/Unlisted": "Privado/No listado",
|
||||
"From my timeline": "Desde mi línea de tiempo",
|
||||
"Enter URL manually": "Introducir URL manualmente",
|
||||
"1 level": "1 nivel",
|
||||
"2 levels": "2 niveles",
|
||||
"3 levels": "3 niveles",
|
||||
"5 levels": "5 niveles",
|
||||
"10 levels": "10 niveles",
|
||||
"20 levels": "20 niveles",
|
||||
"50 levels": "50 niveles",
|
||||
"100 levels (max)": "100 niveles (máximo)",
|
||||
"No parents": "Sin padres",
|
||||
"1 parent": "1 padre",
|
||||
"2 parents": "2 padres",
|
||||
"3 parents": "3 padres",
|
||||
"5 parents": "5 padres",
|
||||
"10 parents": "10 padres",
|
||||
"20 parents": "20 padres",
|
||||
"All parents (80 max)": "Todos los padres (80 máx.)",
|
||||
"New Posts by Author": "Nuevos posts por autor",
|
||||
"New Follower on Account": "Nuevo seguidor en la cuenta",
|
||||
"New Timeline Posts": "Nuevos mensajes del cronograma",
|
||||
"New Post (with Search Options)": "Nuevo mensaje (con opciones de búsqueda)",
|
||||
"Triggers when a selected author creates a new post": "Dispara cuando un autor seleccionado crea una nueva publicación",
|
||||
"Triggers when someone new follows your Bluesky account": "Dispara cuando alguien nuevo sigue tu cuenta Bluesky",
|
||||
"Triggers when new posts appear in your timeline": "Dispara cuando los nuevos mensajes aparecen en tu línea de tiempo",
|
||||
"Triggers when posts match your search criteria": "Dispara cuando los mensajes coincidan con tus criterios de búsqueda",
|
||||
"How to select author?": "¿Cómo seleccionar el autor?",
|
||||
"Select Author": "Seleccionar autor",
|
||||
"Author Handle": "Manejo del autor",
|
||||
"Include Replies": "Incluye respuestas",
|
||||
"Include Reposts": "Incluye reposts",
|
||||
"Search Query": "Buscar consulta",
|
||||
"Language Filter": "Filtro de idioma",
|
||||
"Filter by Images": "Filtrar por imágenes",
|
||||
"Filter by Videos": "Filtrar por Videos",
|
||||
"Sort Order": "Ordenar",
|
||||
"Choose how to select the author": "Elija cómo seleccionar el autor",
|
||||
"Choose from accounts you follow": "Elige entre las cuentas que sigues",
|
||||
"Enter the Bluesky username (e.g., username.bsky.social)": "Introduzca el nombre de usuario de Bluesky (por ejemplo, username.bsky.social)",
|
||||
"Include reply posts by this author": "Incluye respuestas de este autor",
|
||||
"Include posts that this author reposted": "Incluye mensajes que este autor ha vuelto a publicar",
|
||||
"Keywords, hashtags (#example), or mentions (@handle) to find": "Palabras clave, hashtags (#ejemplo), o menciones (@handle) a encontrar",
|
||||
"Filter by language": "Filtrar por idioma",
|
||||
"Only posts with/without images": "Sólo publicaciones con/sin imágenes",
|
||||
"Only posts with/without videos": "Sólo publicaciones con/sin vídeos",
|
||||
"How to sort results": "Cómo ordenar los resultados",
|
||||
"From my following list": "De mi siguiente lista",
|
||||
"Enter handle manually": "Introducir handle manualmente",
|
||||
"Latest First": "Último primero",
|
||||
"Most Popular": "Más Popular"
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"PDS Host": "Hôte PDS",
|
||||
"Identifier": "Identifier",
|
||||
"Password": "Password",
|
||||
"The Personal Data Server host. Leave empty for default Bluesky network (https://bsky.social)": "L'hôte du serveur de données personnelles. Laissez vide pour le réseau Bluesky par défaut (https://bsky.social)",
|
||||
"Your Bluesky handle or email address": "Votre identifiant Bluesky ou votre adresse e-mail",
|
||||
"Your Bluesky account password or app password": "Mot de passe de votre compte Bluesky ou mot de passe de l'application",
|
||||
"\nTo authenticate with Bluesky:\n\n1. **PDS Host**: The Personal Data Server host. Leave empty to use the default Bluesky network (https://bsky.social).\n2. **Identifier**: Your Bluesky handle (e.g., yourhandle.bsky.social) or email address.\n3. **Password**: Your Bluesky account password or app password.\n\nFor enhanced security, consider using an app password from your Bluesky account settings.\n": "\nTo authenticate with Bluesky:\n\n1. **PDS Host**: The Personal Data Server host. Leave empty to use the default Bluesky network (https://bsky.social).\n2. **Identifier**: Your Bluesky handle (e.g., yourhandle.bsky.social) or email address.\n3. **Password**: Your Bluesky account password or app password.\n\nFor enhanced security, consider using an app password from your Bluesky account settings.\n",
|
||||
"Create Post": "Créer un message",
|
||||
"Like Post": "J'aime la publication",
|
||||
"Repost Post": "Republier le post",
|
||||
"Find Post": "Rechercher une publication",
|
||||
"Find Thread": "Trouver un fil de discussion",
|
||||
"Create a new post on Bluesky": "Créer un nouveau message sur Bluesky",
|
||||
"Like a post on Bluesky": "Aimer un message sur Bluesky",
|
||||
"Share someone else's post to your timeline": "Partagez le message de quelqu'un d'autre sur votre chronologie",
|
||||
"Get detailed information about a specific post": "Obtenir des informations détaillées sur un message spécifique",
|
||||
"Get a full conversation thread with replies": "Obtenir un fil de conversation complet avec des réponses",
|
||||
"Post Type": "Type de publication",
|
||||
"Post Text": "Poster le texte",
|
||||
"Post Language": "Langue de la publication",
|
||||
"Image URLs": "URL de l'image",
|
||||
"Image Descriptions": "Descriptions de l'image",
|
||||
"Video URL": "URL de la vidéo",
|
||||
"Video Description": "Description de la vidéo",
|
||||
"Video Captions": "Légendes vidéo",
|
||||
"Link to Share": "Lien pour partager",
|
||||
"Reply to Post": "Répondre au message",
|
||||
"Thread Posts": "Messages de discussion",
|
||||
"Hashtags": "Hashtags",
|
||||
"Content Warnings": "Avertissements de contenu",
|
||||
"Audience": "Audience",
|
||||
"Select Method": "Sélectionnez la méthode",
|
||||
"Select Post": "Sélectionner une publication",
|
||||
"Post URL": "URL de la publication",
|
||||
"Thread Depth": "Profondeur du fil",
|
||||
"Parent Height": "Hauteur du parent",
|
||||
"Type of content you're sharing": "Type de contenu que vous partagez",
|
||||
"What do you want to post? (Max 300 characters)": "Que voulez-vous poster ? (Max 300 caractères)",
|
||||
"Language of your post": "Langue de votre publication",
|
||||
"Add up to 4 images by URL": "Ajouter jusqu'à 4 images par URL",
|
||||
"Describe each image for accessibility": "Décrire chaque image pour l'accessibilité",
|
||||
"Link to video file (MP4, max 100MB)": "Lien vers un fichier vidéo (MP4, max 100MB)",
|
||||
"Describe the video for accessibility": "Décrire la vidéo pour l'accessibilité",
|
||||
"Caption file URLs (optional)": "URL du fichier de légende (facultatif)",
|
||||
"URL to share with your post": "URL à partager avec votre message",
|
||||
"URL of post to reply to": "URL du message à répondre à",
|
||||
"Create additional connected posts": "Créer des publications connectées supplémentaires",
|
||||
"Add hashtags (e.g., tech,bluesky)": "Ajouter des hashtags (par exemple, tech, bluesky)",
|
||||
"Add warnings for sensitive content": "Ajouter des avertissements pour le contenu sensible",
|
||||
"Who can see this post": "Qui peut voir ce message",
|
||||
"How to choose the post": "Comment choisir la publication",
|
||||
"Choose from your recent timeline posts (only when \"From my timeline\" is selected above)": "Choisissez parmi vos derniers messages de la chronologie (uniquement lorsque \"À partir de ma chronologie\" est sélectionné ci-dessus)",
|
||||
"Paste the Bluesky post URL": "Coller l'URL du message Bluesky",
|
||||
"Paste the Bluesky post URL (e.g., https://bsky.app/profile/username.bsky.social/post/xxx)": "Collez l'URL du message Bluesky (par exemple, https://bsky.app/profile/username.bsky.social/post/xxx)",
|
||||
"How many levels deep to retrieve replies": "Combien de niveaux de profondeur pour récupérer les réponses",
|
||||
"How many parent posts to retrieve": "Combien de messages parents récupérer",
|
||||
"Text Post": "Texte de la publication",
|
||||
"Photo Post": "Publication de photo",
|
||||
"Link Share": "Partager le lien",
|
||||
"Reply": "Répondre",
|
||||
"Repost with Comment": "Republier avec un commentaire",
|
||||
"English": "Anglais",
|
||||
"Spanish": "Espagnol",
|
||||
"French": "Français",
|
||||
"German": "Allemand",
|
||||
"Italian": "Italien",
|
||||
"Portuguese": "Portugais",
|
||||
"Japanese": "Japonais",
|
||||
"Korean": "Coréen",
|
||||
"Chinese": "Chinois",
|
||||
"Russian": "Russe",
|
||||
"Arabic": "Arabe",
|
||||
"Hindi": "Hindi",
|
||||
"Dutch": "Néerlandais",
|
||||
"Swedish": "Suédois",
|
||||
"Other": "Autres",
|
||||
"Adult Content": "Contenu adulte",
|
||||
"Graphic Content": "Contenu Graphique",
|
||||
"Sensitive Topic": "Sujet sensible",
|
||||
"Violence": "Violence",
|
||||
"Spam/Promotional": "Spam/Promotionnel",
|
||||
"Everyone (Public)": "Tout le monde (public)",
|
||||
"Followers only": "Abonnés seulement",
|
||||
"Private/Unlisted": "Privé/Non listé",
|
||||
"From my timeline": "Depuis ma chronologie",
|
||||
"Enter URL manually": "Entrez l'URL manuellement",
|
||||
"1 level": "1 niveau",
|
||||
"2 levels": "2 niveaux",
|
||||
"3 levels": "3 niveaux",
|
||||
"5 levels": "5 niveaux",
|
||||
"10 levels": "10 niveaux",
|
||||
"20 levels": "20 niveaux",
|
||||
"50 levels": "50 niveaux",
|
||||
"100 levels (max)": "100 niveaux (max)",
|
||||
"No parents": "Aucun parent",
|
||||
"1 parent": "1 parent",
|
||||
"2 parents": "2 parents",
|
||||
"3 parents": "3 parents",
|
||||
"5 parents": "5 parents",
|
||||
"10 parents": "10 parents",
|
||||
"20 parents": "20 parents",
|
||||
"All parents (80 max)": "Tous les parents (80 max)",
|
||||
"New Posts by Author": "Nouveaux messages par auteur",
|
||||
"New Follower on Account": "Nouveau Suiveur sur le Compte",
|
||||
"New Timeline Posts": "Nouvelles publications de la timeline",
|
||||
"New Post (with Search Options)": "Nouvelle publication (avec options de recherche)",
|
||||
"Triggers when a selected author creates a new post": "Déclenche lorsqu'un auteur sélectionné crée un nouveau message",
|
||||
"Triggers when someone new follows your Bluesky account": "Déclenche quand quelqu'un de nouveau suit votre compte Bluesky",
|
||||
"Triggers when new posts appear in your timeline": "Déclenche quand de nouveaux messages apparaissent dans votre chronologie",
|
||||
"Triggers when posts match your search criteria": "Déclenche lorsque les messages correspondent à vos critères de recherche",
|
||||
"How to select author?": "Comment sélectionner un auteur?",
|
||||
"Select Author": "Sélectionner l'auteur",
|
||||
"Author Handle": "Descripteur de l'auteur",
|
||||
"Include Replies": "Inclure les réponses",
|
||||
"Include Reposts": "Inclure les republications",
|
||||
"Search Query": "Requête de recherche",
|
||||
"Language Filter": "Filtre de langue",
|
||||
"Filter by Images": "Filtrer par Images",
|
||||
"Filter by Videos": "Filtrer par Vidéos",
|
||||
"Sort Order": "Ordre de tri",
|
||||
"Choose how to select the author": "Choisir comment sélectionner l'auteur",
|
||||
"Choose from accounts you follow": "Choisissez parmi les comptes que vous suivez",
|
||||
"Enter the Bluesky username (e.g., username.bsky.social)": "Entrez le nom d'utilisateur Bluesky (par exemple, username.bsky.social)",
|
||||
"Include reply posts by this author": "Inclure les messages de réponse de cet auteur",
|
||||
"Include posts that this author reposted": "Inclure les messages que cet auteur a publiés",
|
||||
"Keywords, hashtags (#example), or mentions (@handle) to find": "Mots-clés, hashtags (#exemple), ou mentions (@handle) pour trouver",
|
||||
"Filter by language": "Filtrer par langue",
|
||||
"Only posts with/without images": "Seulement les messages avec/sans images",
|
||||
"Only posts with/without videos": "Seulement les messages avec/sans vidéos",
|
||||
"How to sort results": "Comment trier les résultats",
|
||||
"From my following list": "De ma liste suivante",
|
||||
"Enter handle manually": "Saisir manuellement le gestionnaire",
|
||||
"Latest First": "Dernier en premier",
|
||||
"Most Popular": "Les plus populaires"
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"PDS Host": "PDS ホスト",
|
||||
"Identifier": "Identifier",
|
||||
"Password": "Password",
|
||||
"The Personal Data Server host. Leave empty for default Bluesky network (https://bsky.social)": "個人データサーバーのホスト。デフォルトのBlueskyネットワーク(https://bsky.social)の場合は空のままにしてください。",
|
||||
"Your Bluesky handle or email address": "Blueskyハンドルまたはメールアドレス",
|
||||
"Your Bluesky account password or app password": "Blueskyアカウントのパスワードまたはアプリのパスワード",
|
||||
"\nTo authenticate with Bluesky:\n\n1. **PDS Host**: The Personal Data Server host. Leave empty to use the default Bluesky network (https://bsky.social).\n2. **Identifier**: Your Bluesky handle (e.g., yourhandle.bsky.social) or email address.\n3. **Password**: Your Bluesky account password or app password.\n\nFor enhanced security, consider using an app password from your Bluesky account settings.\n": "\nTo authenticate with Bluesky:\n\n1. **PDS Host**: The Personal Data Server host. Leave empty to use the default Bluesky network (https://bsky.social).\n2. **Identifier**: Your Bluesky handle (e.g., yourhandle.bsky.social) or email address.\n3. **Password**: Your Bluesky account password or app password.\n\nFor enhanced security, consider using an app password from your Bluesky account settings.\n",
|
||||
"Create Post": "投稿を作成",
|
||||
"Like Post": "投稿にいいね!",
|
||||
"Repost Post": "Repost Post",
|
||||
"Find Post": "投稿を検索",
|
||||
"Find Thread": "スレッドを検索",
|
||||
"Create a new post on Bluesky": "Blueskyに新しい投稿を作成",
|
||||
"Like a post on Bluesky": "Blueskyの投稿のように",
|
||||
"Share someone else's post to your timeline": "自分のタイムラインに他の人の投稿をシェアする",
|
||||
"Get detailed information about a specific post": "特定の投稿に関する詳細情報を取得する",
|
||||
"Get a full conversation thread with replies": "返信付きの完全な会話スレッドを取得",
|
||||
"Post Type": "投稿の種類",
|
||||
"Post Text": "投稿テキスト",
|
||||
"Post Language": "投稿の言語",
|
||||
"Image URLs": "画像のURL",
|
||||
"Image Descriptions": "画像の説明",
|
||||
"Video URL": "動画の URL",
|
||||
"Video Description": "ビデオの説明",
|
||||
"Video Captions": "ビデオの図表番号",
|
||||
"Link to Share": "共有へのリンク",
|
||||
"Reply to Post": "投稿に返信",
|
||||
"Thread Posts": "スレッドの投稿",
|
||||
"Hashtags": "ハッシュタグ",
|
||||
"Content Warnings": "コンテンツ警告",
|
||||
"Audience": "オーディエンス:",
|
||||
"Select Method": "メソッドを選択",
|
||||
"Select Post": "投稿を選択",
|
||||
"Post URL": "投稿URL",
|
||||
"Thread Depth": "スレッドの深さ",
|
||||
"Parent Height": "親の高さ",
|
||||
"Type of content you're sharing": "共有しているコンテンツの種類",
|
||||
"What do you want to post? (Max 300 characters)": "何を投稿しますか?(最大300文字)",
|
||||
"Language of your post": "あなたの投稿の言語",
|
||||
"Add up to 4 images by URL": "URLで最大4枚の画像を追加",
|
||||
"Describe each image for accessibility": "アクセシビリティのためにそれぞれの画像を説明してください",
|
||||
"Link to video file (MP4, max 100MB)": "ビデオファイルへのリンク (MP4, max 100MB)",
|
||||
"Describe the video for accessibility": "アクセシビリティのためのビデオを説明してください",
|
||||
"Caption file URLs (optional)": "キャプションファイルのURL (任意)",
|
||||
"URL to share with your post": "投稿と共有するURL",
|
||||
"URL of post to reply to": "返信する投稿の URL",
|
||||
"Create additional connected posts": "追加の接続された投稿を作成",
|
||||
"Add hashtags (e.g., tech,bluesky)": "ハッシュタグを追加(例:tech,bluesky)",
|
||||
"Add warnings for sensitive content": "機密コンテンツに警告を追加する",
|
||||
"Who can see this post": "誰がこの投稿を閲覧できますか?",
|
||||
"How to choose the post": "投稿の選択方法",
|
||||
"Choose from your recent timeline posts (only when \"From my timeline\" is selected above)": "最近のタイムライン投稿から選択してください(上記「タイムラインから」が選択されている場合のみ)",
|
||||
"Paste the Bluesky post URL": "Blueskyの投稿URLを貼り付け",
|
||||
"Paste the Bluesky post URL (e.g., https://bsky.app/profile/username.bsky.social/post/xxx)": "Bluesky post URL (例: https://bsky.app/profile/username.bsky.social/post/xxx) を貼り付けてください",
|
||||
"How many levels deep to retrieve replies": "返信を取得する深さのレベル",
|
||||
"How many parent posts to retrieve": "取得する親ポストの数",
|
||||
"Text Post": "テキスト投稿",
|
||||
"Photo Post": "写真の投稿",
|
||||
"Link Share": "リンクの共有",
|
||||
"Reply": "返信",
|
||||
"Repost with Comment": "コメント付きのrepost",
|
||||
"English": "日本語",
|
||||
"Spanish": "スペイン語",
|
||||
"French": "フランス語",
|
||||
"German": "ドイツ語",
|
||||
"Italian": "イタリア語",
|
||||
"Portuguese": "ポルトガル語",
|
||||
"Japanese": "日本語",
|
||||
"Korean": "Korean",
|
||||
"Chinese": "中国語",
|
||||
"Russian": "ロシア語",
|
||||
"Arabic": "アラビア文字",
|
||||
"Hindi": "ヒンディー語",
|
||||
"Dutch": "オランダ語",
|
||||
"Swedish": "スウェーデン語",
|
||||
"Other": "その他",
|
||||
"Adult Content": "アダルトコンテンツ",
|
||||
"Graphic Content": "グラフィックコンテンツ",
|
||||
"Sensitive Topic": "敏感なトピック",
|
||||
"Violence": "暴力行為(暴力)",
|
||||
"Spam/Promotional": "スパム/プロモーション",
|
||||
"Everyone (Public)": "すべてのユーザー(公開)",
|
||||
"Followers only": "フォロワーのみ",
|
||||
"Private/Unlisted": "非公開/非公開にする",
|
||||
"From my timeline": "タイムラインから",
|
||||
"Enter URL manually": "URLを手動で入力",
|
||||
"1 level": "1 レベル",
|
||||
"2 levels": "2 レベル",
|
||||
"3 levels": "3レベル",
|
||||
"5 levels": "5レベル",
|
||||
"10 levels": "10レベル",
|
||||
"20 levels": "20レベル",
|
||||
"50 levels": "50レベル",
|
||||
"100 levels (max)": "100レベル(最大)",
|
||||
"No parents": "保護者なし",
|
||||
"1 parent": "1 つの親",
|
||||
"2 parents": "2人の親",
|
||||
"3 parents": "3人の親",
|
||||
"5 parents": "5人の親",
|
||||
"10 parents": "親10名",
|
||||
"20 parents": "20人の親",
|
||||
"All parents (80 max)": "すべての保護者(最大80名)",
|
||||
"New Posts by Author": "投稿者による新規投稿",
|
||||
"New Follower on Account": "アカウントの新しいフォロワー数",
|
||||
"New Timeline Posts": "新しいタイムライン投稿",
|
||||
"New Post (with Search Options)": "新規投稿 (検索オプション付き)",
|
||||
"Triggers when a selected author creates a new post": "選択した著者が新しい投稿を作成したときにトリガーします",
|
||||
"Triggers when someone new follows your Bluesky account": "新しい誰かがあなたのBlueskyアカウントをフォローしたときにトリガーします",
|
||||
"Triggers when new posts appear in your timeline": "タイムラインに新しい投稿が表示されたときにトリガーします",
|
||||
"Triggers when posts match your search criteria": "検索条件に一致する投稿をトリガーする",
|
||||
"How to select author?": "著者を選択する方法は?",
|
||||
"Select Author": "投稿者を選択",
|
||||
"Author Handle": "著者のハンドル:",
|
||||
"Include Replies": "返信を含める",
|
||||
"Include Reposts": "リポジトリを含める",
|
||||
"Search Query": "検索クエリ",
|
||||
"Language Filter": "言語フィルタ",
|
||||
"Filter by Images": "画像でフィルター",
|
||||
"Filter by Videos": "動画でフィルター",
|
||||
"Sort Order": "並び順",
|
||||
"Choose how to select the author": "作成者を選択する方法を選択",
|
||||
"Choose from accounts you follow": "フォロー中のアカウントから選択",
|
||||
"Enter the Bluesky username (e.g., username.bsky.social)": "Blueskyユーザー名を入力してください(例:username.bsky.social)",
|
||||
"Include reply posts by this author": "この著者による返信投稿を含める",
|
||||
"Include posts that this author reposted": "この投稿者が再投稿した投稿を含める",
|
||||
"Keywords, hashtags (#example), or mentions (@handle) to find": "キーワード、ハッシュタグ (#example)、メンション(@handle) で検索",
|
||||
"Filter by language": "言語でフィルター",
|
||||
"Only posts with/without images": "画像の有無の投稿のみ",
|
||||
"Only posts with/without videos": "動画の有無の投稿のみ",
|
||||
"How to sort results": "結果を並べ替える方法",
|
||||
"From my following list": "以下のリストから",
|
||||
"Enter handle manually": "手動でハンドルを入力",
|
||||
"Latest First": "最新の順",
|
||||
"Most Popular": "最も人気"
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"PDS Host": "PDS host",
|
||||
"Identifier": "Identifier",
|
||||
"Password": "Wachtwoord",
|
||||
"The Personal Data Server host. Leave empty for default Bluesky network (https://bsky.social)": "De persoonlijke gegevens server host. Laat leeg voor het standaard Bluesky netwerk (https://bsky.social)",
|
||||
"Your Bluesky handle or email address": "Jouw Bluesky handle of e-mailadres",
|
||||
"Your Bluesky account password or app password": "Uw Bluesky account wachtwoord of app-wachtwoord",
|
||||
"\nTo authenticate with Bluesky:\n\n1. **PDS Host**: The Personal Data Server host. Leave empty to use the default Bluesky network (https://bsky.social).\n2. **Identifier**: Your Bluesky handle (e.g., yourhandle.bsky.social) or email address.\n3. **Password**: Your Bluesky account password or app password.\n\nFor enhanced security, consider using an app password from your Bluesky account settings.\n": "\nOm te verifiëren met Bluesky:\n\n1. **PDS Host**: De persoonlijke gegevens server host. Laat leeg om gebruik te maken van het standaard Bluesky netwerk (https://bsky.social).\n2. **Identificeren**: Uw Bluesky handgreep (bijv. uwhandle.bsky.social) of e-mailadres.\n3. **Wachtwoord**: Uw Bluesky account wachtwoord of app-wachtwoord.\n\nVoor verbeterde beveiliging, overweeg om een app-wachtwoord te gebruiken vanuit je Bluesky accountinstellingen.\n",
|
||||
"Create Post": "Post aanmaken",
|
||||
"Like Post": "Vind bericht leuk",
|
||||
"Repost Post": "Bericht opnieuw plaatsen",
|
||||
"Find Post": "Zoek bericht",
|
||||
"Find Thread": "Discussie zoeken",
|
||||
"Create a new post on Bluesky": "Maak een nieuw bericht op Bluesky",
|
||||
"Like a post on Bluesky": "Als een bericht op Bluesky",
|
||||
"Share someone else's post to your timeline": "Deel het bericht van iemand anders op je tijdlijn",
|
||||
"Get detailed information about a specific post": "Krijg gedetailleerde informatie over een specifiek bericht",
|
||||
"Get a full conversation thread with replies": "Krijg een volledig gesprek met reacties",
|
||||
"Post Type": "Type post",
|
||||
"Post Text": "Plaats tekst",
|
||||
"Post Language": "Post taal",
|
||||
"Image URLs": "Afbeelding URLs",
|
||||
"Image Descriptions": "Afbeelding omschrijvingen",
|
||||
"Video URL": "Video URL",
|
||||
"Video Description": "Video beschrijving",
|
||||
"Video Captions": "Video bijschriften",
|
||||
"Link to Share": "Link naar delen",
|
||||
"Reply to Post": "Reageer op bericht",
|
||||
"Thread Posts": "Thread Berichten",
|
||||
"Hashtags": "Hashtags",
|
||||
"Content Warnings": "Waarschuwingen inhoud",
|
||||
"Audience": "Auditie",
|
||||
"Select Method": "Selecteer methode",
|
||||
"Select Post": "Selecteer bericht",
|
||||
"Post URL": "Post URL",
|
||||
"Thread Depth": "Thread diepte",
|
||||
"Parent Height": "Bovenliggende hoogte",
|
||||
"Type of content you're sharing": "Soort inhoud die u deelt",
|
||||
"What do you want to post? (Max 300 characters)": "Wat wil je doen? (Max 300 tekens)",
|
||||
"Language of your post": "Taal van uw bericht",
|
||||
"Add up to 4 images by URL": "Voeg tot 4 afbeeldingen via URL toe",
|
||||
"Describe each image for accessibility": "Beschrijf elke afbeelding voor toegankelijkheid",
|
||||
"Link to video file (MP4, max 100MB)": "Link naar videobestand (MP4, max 100MB)",
|
||||
"Describe the video for accessibility": "Beschrijf de video voor toegankelijkheid",
|
||||
"Caption file URLs (optional)": "URL's bijschrift (optioneel)",
|
||||
"URL to share with your post": "URL om te delen met uw bericht",
|
||||
"URL of post to reply to": "URL van bericht om te beantwoorden",
|
||||
"Create additional connected posts": "Extra verbonden berichten aanmaken",
|
||||
"Add hashtags (e.g., tech,bluesky)": "Voeg hashtags toe (bijv. tech,bluesky)",
|
||||
"Add warnings for sensitive content": "Waarschuwingen voor gevoelige inhoud toevoegen",
|
||||
"Who can see this post": "Wie kan dit bericht zien",
|
||||
"How to choose the post": "Hoe het bericht te kiezen",
|
||||
"Choose from your recent timeline posts (only when \"From my timeline\" is selected above)": "Kies uit je recente tijdlijnberichten (alleen wanneer \"Van mijn tijdlijn\" hierboven is geselecteerd)",
|
||||
"Paste the Bluesky post URL": "Plak de Bluesky post URL",
|
||||
"Paste the Bluesky post URL (e.g., https://bsky.app/profile/username.bsky.social/post/xxx)": "Plak de Bluesky post URL (bijv. https://bsky.app/profile/username.bsky.social/post/xxx)",
|
||||
"How many levels deep to retrieve replies": "Hoeveel levels diep om reacties op te halen",
|
||||
"How many parent posts to retrieve": "Hoeveel bovenliggende berichten op te halen",
|
||||
"Text Post": "Tekst bericht",
|
||||
"Photo Post": "Foto plaatsen",
|
||||
"Link Share": "Deel koppeling",
|
||||
"Reply": "Beantwoorden",
|
||||
"Repost with Comment": "Herpost met reactie",
|
||||
"English": "Nederlands",
|
||||
"Spanish": "Spaans",
|
||||
"French": "Frans",
|
||||
"German": "Duits",
|
||||
"Italian": "Italiaans",
|
||||
"Portuguese": "Portugees",
|
||||
"Japanese": "Afrikaans",
|
||||
"Korean": "Koreaans",
|
||||
"Chinese": "Chinees",
|
||||
"Russian": "Russisch",
|
||||
"Arabic": "Arabisch",
|
||||
"Hindi": "Hindoestani",
|
||||
"Dutch": "Nederlands",
|
||||
"Swedish": "Zweeds",
|
||||
"Other": "anders",
|
||||
"Adult Content": "Inhoud Volwassenen",
|
||||
"Graphic Content": "Grafische inhoud",
|
||||
"Sensitive Topic": "Gevoelig onderwerp",
|
||||
"Violence": "Geweld",
|
||||
"Spam/Promotional": "Spam/Promotionele",
|
||||
"Everyone (Public)": "Iedereen (Public)",
|
||||
"Followers only": "Alleen volgers",
|
||||
"Private/Unlisted": "Privé/niet-genoteerd",
|
||||
"From my timeline": "Van mijn tijdlijn",
|
||||
"Enter URL manually": "Voer handmatig een URL in",
|
||||
"1 level": "1 niveau",
|
||||
"2 levels": "2 niveaus",
|
||||
"3 levels": "3 niveaus",
|
||||
"5 levels": "5 niveaus",
|
||||
"10 levels": "10 niveaus",
|
||||
"20 levels": "20 niveaus",
|
||||
"50 levels": "50 levels",
|
||||
"100 levels (max)": "100 levels (max)",
|
||||
"No parents": "Geen ouders",
|
||||
"1 parent": "1 bovenliggende",
|
||||
"2 parents": "2 ouders",
|
||||
"3 parents": "3 ouders",
|
||||
"5 parents": "5 ouders",
|
||||
"10 parents": "10 ouders",
|
||||
"20 parents": "20 ouders",
|
||||
"All parents (80 max)": "Alle ouders (80 max)",
|
||||
"New Posts by Author": "Nieuwe posts door auteur",
|
||||
"New Follower on Account": "Nieuwe volger op account",
|
||||
"New Timeline Posts": "Nieuwe tijdlijn posts",
|
||||
"New Post (with Search Options)": "Nieuw bericht (met zoekopties)",
|
||||
"Triggers when a selected author creates a new post": "Triggert wanneer een geselecteerde auteur een nieuw bericht maakt",
|
||||
"Triggers when someone new follows your Bluesky account": "Triggert wanneer iemand nieuw jouw Bluesky account volgt",
|
||||
"Triggers when new posts appear in your timeline": "Triggert wanneer nieuwe berichten op je tijdlijn verschijnen",
|
||||
"Triggers when posts match your search criteria": "Activeert wanneer berichten voldoen aan uw zoekcriteria",
|
||||
"How to select author?": "Hoe selecteert u auteur?",
|
||||
"Select Author": "Selecteer auteur",
|
||||
"Author Handle": "Auteur Handel",
|
||||
"Include Replies": "Inclusief antwoorden",
|
||||
"Include Reposts": "Inclusief Reposts",
|
||||
"Search Query": "Zoek query",
|
||||
"Language Filter": "Taal filter",
|
||||
"Filter by Images": "Filter op afbeeldingen",
|
||||
"Filter by Videos": "Filter op video's",
|
||||
"Sort Order": "Sorteren bestelling",
|
||||
"Choose how to select the author": "Kies hoe u de auteur wilt selecteren",
|
||||
"Choose from accounts you follow": "Kies uit accounts die je volgt",
|
||||
"Enter the Bluesky username (e.g., username.bsky.social)": "Vul de Bluesky gebruikersnaam in (bijv. username.bsky.social)",
|
||||
"Include reply posts by this author": "Antwoordberichten van deze auteur toevoegen",
|
||||
"Include posts that this author reposted": "Voeg berichten toe die deze auteur opnieuw heeft geplaatst",
|
||||
"Keywords, hashtags (#example), or mentions (@handle) to find": "Trefwoorden, hashtags (#example), of noemt (@handle) om te vinden",
|
||||
"Filter by language": "Filter op taal",
|
||||
"Only posts with/without images": "posts alleen met/zonder afbeeldingen",
|
||||
"Only posts with/without videos": "Alleen berichten met/zonder video's",
|
||||
"How to sort results": "Hoe de resultaten te sorteren",
|
||||
"From my following list": "Van mijn volgende lijst",
|
||||
"Enter handle manually": "Handgreep handmatig invoeren",
|
||||
"Latest First": "Nieuwste eerst",
|
||||
"Most Popular": "Meest populair"
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"PDS Host": "Host PDS",
|
||||
"Identifier": "Identifier",
|
||||
"Password": "Senha",
|
||||
"The Personal Data Server host. Leave empty for default Bluesky network (https://bsky.social)": "O host de servidor de dados pessoais. Deixe em branco para a rede padrão (https://bsky.social)",
|
||||
"Your Bluesky handle or email address": "Seu identificador de usuário ou endereço de e-mail",
|
||||
"Your Bluesky account password or app password": "Sua senha de conta Bluesky ou senha do aplicativo",
|
||||
"\nTo authenticate with Bluesky:\n\n1. **PDS Host**: The Personal Data Server host. Leave empty to use the default Bluesky network (https://bsky.social).\n2. **Identifier**: Your Bluesky handle (e.g., yourhandle.bsky.social) or email address.\n3. **Password**: Your Bluesky account password or app password.\n\nFor enhanced security, consider using an app password from your Bluesky account settings.\n": "\nPara autenticar com Bluesky:\n\n1. **PDS Host**: O Host do banco do seu Servidor Pessoal. Deixe em branco para utilizar o endereço padrão Bluesky Network(https://bsky.social)\n2. **Identificador**: Seu domínio controlador Bluesky (ex. yourhandle.bsky.social) ou endereço de e-mail.\n3. **Password**: Senha da sua conta ou aplicativo Bluesky.\n\nPara maior segurança, considere utilizar uma senha do aplicativo gerador nas Configurações da sua conta Bluesky.",
|
||||
"Create Post": "Criar publicação",
|
||||
"Like Post": "Curtir Postagem",
|
||||
"Repost Post": "Postagem republicada",
|
||||
"Find Post": "Encontrar publicação",
|
||||
"Find Thread": "Encontrar tópico",
|
||||
"Create a new post on Bluesky": "Criar um novo post no Bluesky",
|
||||
"Like a post on Bluesky": "Curta um post no Bluesky",
|
||||
"Share someone else's post to your timeline": "Compartilhe a publicação de alguém na sua linha do tempo",
|
||||
"Get detailed information about a specific post": "Obter informações detalhadas sobre uma postagem específica",
|
||||
"Get a full conversation thread with replies": "Obter um tópico completo de conversa com respostas",
|
||||
"Post Type": "Tipo de publicação",
|
||||
"Post Text": "Texto da postagem",
|
||||
"Post Language": "Idioma da postagem",
|
||||
"Image URLs": "URLs da imagem",
|
||||
"Image Descriptions": "Descrições da imagem",
|
||||
"Video URL": "URL do vídeo",
|
||||
"Video Description": "Descrição do Vídeo",
|
||||
"Video Captions": "Legendas de Vídeo",
|
||||
"Link to Share": "Link para Compartilhar",
|
||||
"Reply to Post": "Responder à publicação",
|
||||
"Thread Posts": "Postagens do tópico",
|
||||
"Hashtags": "Palavras-chave",
|
||||
"Content Warnings": "Avisos de conteúdo",
|
||||
"Audience": "Público",
|
||||
"Select Method": "Selecione o Método",
|
||||
"Select Post": "Selecionar Postagem",
|
||||
"Post URL": "URL da postagem",
|
||||
"Thread Depth": "Profundidade do tópico",
|
||||
"Parent Height": "Altura Pai",
|
||||
"Type of content you're sharing": "Tipo de conteúdo que você está compartilhando",
|
||||
"What do you want to post? (Max 300 characters)": "O que você quer publicar? (Máximo de 300 caracteres)",
|
||||
"Language of your post": "Idioma da sua publicação",
|
||||
"Add up to 4 images by URL": "Adicione até 4 imagens por URL",
|
||||
"Describe each image for accessibility": "Descrever cada imagem para acessibilidade",
|
||||
"Link to video file (MP4, max 100MB)": "Link para o arquivo de vídeo (MP4, máx. 100MB)",
|
||||
"Describe the video for accessibility": "Descreva o vídeo para acessibilidade",
|
||||
"Caption file URLs (optional)": "URLs do arquivo de legenda (opcional)",
|
||||
"URL to share with your post": "URL para compartilhar com sua publicação",
|
||||
"URL of post to reply to": "URL da postagem para responder a",
|
||||
"Create additional connected posts": "Crie publicações adicionais conectadas",
|
||||
"Add hashtags (e.g., tech,bluesky)": "Adicionar hashtags (por exemplo, tecnologia, bluesky)",
|
||||
"Add warnings for sensitive content": "Adicionar avisos para conteúdo confidencial",
|
||||
"Who can see this post": "Quem pode ver esta publicação",
|
||||
"How to choose the post": "Como escolher o post",
|
||||
"Choose from your recent timeline posts (only when \"From my timeline\" is selected above)": "Escolha entre suas postagens recentes de linha do tempo (somente quando \"Da minha linha do tempo\" estiver selecionado acima)",
|
||||
"Paste the Bluesky post URL": "Cole a URL do post do Bluesky",
|
||||
"Paste the Bluesky post URL (e.g., https://bsky.app/profile/username.bsky.social/post/xxx)": "Cole a URL do post do Bluesky (por exemplo, https://bsky.app/profile/username.bsky.social/post/xxx)",
|
||||
"How many levels deep to retrieve replies": "Quantos níveis profundas para recuperar as respostas",
|
||||
"How many parent posts to retrieve": "Quantas publicações pai para recuperar",
|
||||
"Text Post": "Post de texto",
|
||||
"Photo Post": "Publicação de Fotos",
|
||||
"Link Share": "Link de Compartilhamento",
|
||||
"Reply": "Responder",
|
||||
"Repost with Comment": "Republicar com Comentário",
|
||||
"English": "Portuguese-Brazil",
|
||||
"Spanish": "espanhol",
|
||||
"French": "francês",
|
||||
"German": "alemão",
|
||||
"Italian": "italiano",
|
||||
"Portuguese": "Português",
|
||||
"Japanese": "japonês",
|
||||
"Korean": "coreano",
|
||||
"Chinese": "chinês",
|
||||
"Russian": "Russo",
|
||||
"Arabic": "Arábico",
|
||||
"Hindi": "hindi",
|
||||
"Dutch": "Neerlandês",
|
||||
"Swedish": "sueco",
|
||||
"Other": "Outros",
|
||||
"Adult Content": "Conteúdo adulto",
|
||||
"Graphic Content": "Conteúdo gráfico",
|
||||
"Sensitive Topic": "Tópico Sensível",
|
||||
"Violence": "Violência",
|
||||
"Spam/Promotional": "Spam/Promocional",
|
||||
"Everyone (Public)": "Todos (Publicados)",
|
||||
"Followers only": "Apenas seguidores",
|
||||
"Private/Unlisted": "Privado/Não listado",
|
||||
"From my timeline": "Da minha linha do tempo",
|
||||
"Enter URL manually": "Inserir URL manualmente",
|
||||
"1 level": "1 nível",
|
||||
"2 levels": "2 níveis",
|
||||
"3 levels": "3 níveis",
|
||||
"5 levels": "5 níveis",
|
||||
"10 levels": "10 níveis",
|
||||
"20 levels": "20 níveis",
|
||||
"50 levels": "50 níveis",
|
||||
"100 levels (max)": "100 níveis (máximo)",
|
||||
"No parents": "Sem pais",
|
||||
"1 parent": "1 pai",
|
||||
"2 parents": "2 pais",
|
||||
"3 parents": "3 pais",
|
||||
"5 parents": "5 pais",
|
||||
"10 parents": "10 pais",
|
||||
"20 parents": "20 pais",
|
||||
"All parents (80 max)": "Todos os pais (80 máx)",
|
||||
"New Posts by Author": "Novas publicações do autor",
|
||||
"New Follower on Account": "Novo seguidor em conta",
|
||||
"New Timeline Posts": "Novas Postagens na Linha Temporal",
|
||||
"New Post (with Search Options)": "Novo Post (com opções de pesquisa)",
|
||||
"Triggers when a selected author creates a new post": "Dispara quando um autor selecionado cria uma nova postagem",
|
||||
"Triggers when someone new follows your Bluesky account": "Dispara quando alguém novo segue a sua conta Bluesky",
|
||||
"Triggers when new posts appear in your timeline": "Dispara quando novas postagens aparecem em sua linha do tempo",
|
||||
"Triggers when posts match your search criteria": "Dispara quando as mensagens correspondem aos seus critérios de pesquisa",
|
||||
"How to select author?": "Como selecionar o autor?",
|
||||
"Select Author": "Selecionar Autor",
|
||||
"Author Handle": "Manipulador do Autor",
|
||||
"Include Replies": "Incluir respostas",
|
||||
"Include Reposts": "Incluir Reposts",
|
||||
"Search Query": "Consulta de Pesquisa",
|
||||
"Language Filter": "Filtro de Idioma",
|
||||
"Filter by Images": "Filtrar por imagens",
|
||||
"Filter by Videos": "Filtrar por vídeos",
|
||||
"Sort Order": "Ordem de classificação",
|
||||
"Choose how to select the author": "Escolha como selecionar o autor",
|
||||
"Choose from accounts you follow": "Escolha das contas que você segue",
|
||||
"Enter the Bluesky username (e.g., username.bsky.social)": "Digite o nome de usuário Bluesky (por exemplo, username.bsky.social)",
|
||||
"Include reply posts by this author": "Incluir postagens de resposta deste autor",
|
||||
"Include posts that this author reposted": "Incluir postagens que este autor repostou",
|
||||
"Keywords, hashtags (#example), or mentions (@handle) to find": "Palavras-chave, hashtags (#exemplo), ou menções (@handle) para encontrar",
|
||||
"Filter by language": "Filtrar por idioma",
|
||||
"Only posts with/without images": "Apenas postagens com/sem imagens",
|
||||
"Only posts with/without videos": "Apenas postagens com/sem vídeos",
|
||||
"How to sort results": "Como classificar os resultados",
|
||||
"From my following list": "Da minha lista seguinte",
|
||||
"Enter handle manually": "Inserir identificador manualmente",
|
||||
"Latest First": "Últimos Primeiro",
|
||||
"Most Popular": "Por Popularidade"
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
{
|
||||
"Bluesky": "Синески",
|
||||
"PDS Host": "Сервер PDS",
|
||||
"Identifier": "Identifier",
|
||||
"Password": "Password",
|
||||
"The Personal Data Server host. Leave empty for default Bluesky network (https://bsky.social)": "Хост сервера персональных данных. Оставьте пустым для стандартной сети Bluesky (https://bsky.social)",
|
||||
"Your Bluesky handle or email address": "Ваш Bluesky аккаунт или адрес электронной почты",
|
||||
"Your Bluesky account password or app password": "Ваш Bluesky пароль или пароль от приложения",
|
||||
"\nTo authenticate with Bluesky:\n\n1. **PDS Host**: The Personal Data Server host. Leave empty to use the default Bluesky network (https://bsky.social).\n2. **Identifier**: Your Bluesky handle (e.g., yourhandle.bsky.social) or email address.\n3. **Password**: Your Bluesky account password or app password.\n\nFor enhanced security, consider using an app password from your Bluesky account settings.\n": "\nДля аутентификации с помощью Bluesky:\n\n1. **PDS Host**: сервер персональных данных. Оставьте пустым, чтобы использовать сеть по умолчанию Bluesky (https://bsky.social).\n2. **Identifier**: Ваш ручной кодек (напр. yourhandle.bsky.social) или email адрес.\n3. **Пароль**: Ваш пароль или пароль от учетной записи синего пользователя или приложения.\n\nДля повышенной безопасности попробуйте использовать пароль приложения из настроек вашего Bluesky аккаунта.\n",
|
||||
"Create Post": "Создать запись",
|
||||
"Like Post": "Лайкнуть пост",
|
||||
"Repost Post": "Репост",
|
||||
"Find Post": "Найти пост",
|
||||
"Find Thread": "Найти тему",
|
||||
"Create a new post on Bluesky": "Создать новый пост на Bluesky",
|
||||
"Like a post on Bluesky": "Лайкнуть пост на синески",
|
||||
"Share someone else's post to your timeline": "Поделитесь чужим сообщением на вашу ленту времени",
|
||||
"Get detailed information about a specific post": "Получить подробную информацию об отдельном сообщении",
|
||||
"Get a full conversation thread with replies": "Получить полное обсуждение с ответами",
|
||||
"Post Type": "Тип записи",
|
||||
"Post Text": "Текст сообщения",
|
||||
"Post Language": "Язык записи",
|
||||
"Image URLs": "URL-адреса изображений",
|
||||
"Image Descriptions": "Описание изображения",
|
||||
"Video URL": "URL видео",
|
||||
"Video Description": "Описание видео",
|
||||
"Video Captions": "Подписи видео",
|
||||
"Link to Share": "Ссылка на публикацию",
|
||||
"Reply to Post": "Ответить на сообщение",
|
||||
"Thread Posts": "Посты в теме",
|
||||
"Hashtags": "Хэштеги",
|
||||
"Content Warnings": "Предупреждения содержимого",
|
||||
"Audience": "Аудитория",
|
||||
"Select Method": "Выберите метод",
|
||||
"Select Post": "Выбрать пост",
|
||||
"Post URL": "URL записи",
|
||||
"Thread Depth": "Глубина потока",
|
||||
"Parent Height": "Родительская высота",
|
||||
"Type of content you're sharing": "Тип контента, который вы передаете",
|
||||
"What do you want to post? (Max 300 characters)": "Что вы хотите отправить сообщение? (Макс. 300 символов)",
|
||||
"Language of your post": "Язык вашего сообщения",
|
||||
"Add up to 4 images by URL": "Добавить до 4 изображений по URL",
|
||||
"Describe each image for accessibility": "Опишите каждое изображение для доступности",
|
||||
"Link to video file (MP4, max 100MB)": "Ссылка на видео файл (MP4, максимум 100MB)",
|
||||
"Describe the video for accessibility": "Опишите видео для доступности",
|
||||
"Caption file URLs (optional)": "URL файла заголовка (необязательно)",
|
||||
"URL to share with your post": "URL-адрес для отправки с вашим сообщением",
|
||||
"URL of post to reply to": "URL сообщения для ответа на",
|
||||
"Create additional connected posts": "Создать дополнительные подключенные сообщения",
|
||||
"Add hashtags (e.g., tech,bluesky)": "Добавить хэштеги (например, тех,блюски)",
|
||||
"Add warnings for sensitive content": "Добавить предупреждения о конфиденциальном контенте",
|
||||
"Who can see this post": "Кто может видеть этот пост",
|
||||
"How to choose the post": "Как выбрать сообщение",
|
||||
"Choose from your recent timeline posts (only when \"From my timeline\" is selected above)": "Выбрать из последних сообщений (только когда выбрано \"С моего времени\" выше)",
|
||||
"Paste the Bluesky post URL": "Вставить Bluesky пост URL",
|
||||
"Paste the Bluesky post URL (e.g., https://bsky.app/profile/username.bsky.social/post/xxx)": "Вставьте URL записи Bluesky (например, https://bsky.app/profile/username.bsky.social/post/xxx)",
|
||||
"How many levels deep to retrieve replies": "Количество уровней для получения ответов",
|
||||
"How many parent posts to retrieve": "Количество родительских записей для получения",
|
||||
"Text Post": "Текстовое сообщение",
|
||||
"Photo Post": "Фото пост",
|
||||
"Link Share": "Ссылка поделиться",
|
||||
"Reply": "Ответ",
|
||||
"Repost with Comment": "Переписать с комментарием",
|
||||
"English": "Russian",
|
||||
"Spanish": "Испанский",
|
||||
"French": "Французский",
|
||||
"German": "Немецкий",
|
||||
"Italian": "Итальянский",
|
||||
"Portuguese": "Португальский",
|
||||
"Japanese": "Японский",
|
||||
"Korean": "Корейский",
|
||||
"Chinese": "Китайский",
|
||||
"Russian": "Русский",
|
||||
"Arabic": "Арабский",
|
||||
"Hindi": "Хинди",
|
||||
"Dutch": "Голландский",
|
||||
"Swedish": "Шведский",
|
||||
"Other": "Прочие вопросы",
|
||||
"Adult Content": "Взрослый контент",
|
||||
"Graphic Content": "Графический контент",
|
||||
"Sensitive Topic": "Чувствительная тема",
|
||||
"Violence": "Насилие",
|
||||
"Spam/Promotional": "Спам/Реклама",
|
||||
"Everyone (Public)": "Все (публичные)",
|
||||
"Followers only": "Только подписчики",
|
||||
"Private/Unlisted": "Приватная/Нет в списке",
|
||||
"From my timeline": "Из моего графика",
|
||||
"Enter URL manually": "Введите URL вручную",
|
||||
"1 level": "1 уровень",
|
||||
"2 levels": "2 уровня",
|
||||
"3 levels": "3 уровня",
|
||||
"5 levels": "5 уровней",
|
||||
"10 levels": "10 уровней",
|
||||
"20 levels": "20 уровней",
|
||||
"50 levels": "50 уровней",
|
||||
"100 levels (max)": "100 уровней (макс)",
|
||||
"No parents": "Нет родителей",
|
||||
"1 parent": "1 родитель",
|
||||
"2 parents": "2 родителя",
|
||||
"3 parents": "3 родителя",
|
||||
"5 parents": "5 родителей",
|
||||
"10 parents": "10 родителей",
|
||||
"20 parents": "20 родителей",
|
||||
"All parents (80 max)": "Все родители (80 макс)",
|
||||
"New Posts by Author": "Новые сообщения автора",
|
||||
"New Follower on Account": "Новый подписчик на аккаунте",
|
||||
"New Timeline Posts": "Новые сообщения Ленты",
|
||||
"New Post (with Search Options)": "Новый пост (с параметрами поиска)",
|
||||
"Triggers when a selected author creates a new post": "Триггеры, когда выбранный автор создает новое сообщение",
|
||||
"Triggers when someone new follows your Bluesky account": "Включает когда кто-то новый подписался на ваш Bluesky аккаунт",
|
||||
"Triggers when new posts appear in your timeline": "Триггеры при появлении новых сообщений на вашей шкале времени",
|
||||
"Triggers when posts match your search criteria": "Триггеры, когда сообщения соответствуют вашим критериям поиска",
|
||||
"How to select author?": "Как выбрать автора?",
|
||||
"Select Author": "Выбрать автора",
|
||||
"Author Handle": "Авторский обработчик",
|
||||
"Include Replies": "Включить ответы",
|
||||
"Include Reposts": "Включить репосты",
|
||||
"Search Query": "Поисковый запрос",
|
||||
"Language Filter": "Фильтр языков",
|
||||
"Filter by Images": "Фильтр по изображениям",
|
||||
"Filter by Videos": "Фильтр по видео",
|
||||
"Sort Order": "Порядок сортировки",
|
||||
"Choose how to select the author": "Выберите, как выбрать автора",
|
||||
"Choose from accounts you follow": "Выберите из аккаунтов, на которые вы подписаны",
|
||||
"Enter the Bluesky username (e.g., username.bsky.social)": "Введите имя пользователя Bluesky (например, username.bsky.social)",
|
||||
"Include reply posts by this author": "Включить сообщения об ответах от этого автора",
|
||||
"Include posts that this author reposted": "Включать сообщения, которые этот автор хранил",
|
||||
"Keywords, hashtags (#example), or mentions (@handle) to find": "Ключевые слова, хэштеги (#example) или упоминания (@handle), чтобы найти",
|
||||
"Filter by language": "Фильтр по языку",
|
||||
"Only posts with/without images": "Только записи с изображениями или без изображений",
|
||||
"Only posts with/without videos": "Только записи с видео или без видео",
|
||||
"How to sort results": "Как сортировать результаты",
|
||||
"From my following list": "Из моего следующего списка",
|
||||
"Enter handle manually": "Введите дескриптор вручную",
|
||||
"Latest First": "Сначала последние",
|
||||
"Most Popular": "Самые популярные"
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"PDS Host": "PDS Host",
|
||||
"Identifier": "Identifier",
|
||||
"Password": "Password",
|
||||
"The Personal Data Server host. Leave empty for default Bluesky network (https://bsky.social)": "The Personal Data Server host. Leave empty for default Bluesky network (https://bsky.social)",
|
||||
"Your Bluesky handle or email address": "Your Bluesky handle or email address",
|
||||
"Your Bluesky account password or app password": "Your Bluesky account password or app password",
|
||||
"\nTo authenticate with Bluesky:\n\n1. **PDS Host**: The Personal Data Server host. Leave empty to use the default Bluesky network (https://bsky.social).\n2. **Identifier**: Your Bluesky handle (e.g., yourhandle.bsky.social) or email address.\n3. **Password**: Your Bluesky account password or app password.\n\nFor enhanced security, consider using an app password from your Bluesky account settings.\n": "\nTo authenticate with Bluesky:\n\n1. **PDS Host**: The Personal Data Server host. Leave empty to use the default Bluesky network (https://bsky.social).\n2. **Identifier**: Your Bluesky handle (e.g., yourhandle.bsky.social) or email address.\n3. **Password**: Your Bluesky account password or app password.\n\nFor enhanced security, consider using an app password from your Bluesky account settings.\n",
|
||||
"Create Post": "Create Post",
|
||||
"Like Post": "Like Post",
|
||||
"Repost Post": "Repost Post",
|
||||
"Find Post": "Find Post",
|
||||
"Find Thread": "Find Thread",
|
||||
"Create a new post on Bluesky": "Create a new post on Bluesky",
|
||||
"Like a post on Bluesky": "Like a post on Bluesky",
|
||||
"Share someone else's post to your timeline": "Share someone else's post to your timeline",
|
||||
"Get detailed information about a specific post": "Get detailed information about a specific post",
|
||||
"Get a full conversation thread with replies": "Get a full conversation thread with replies",
|
||||
"Post Type": "Post Type",
|
||||
"Post Text": "Post Text",
|
||||
"Post Language": "Post Language",
|
||||
"Image URLs": "Image URLs",
|
||||
"Image Descriptions": "Image Descriptions",
|
||||
"Video URL": "Video URL",
|
||||
"Video Description": "Video Description",
|
||||
"Video Captions": "Video Captions",
|
||||
"Link to Share": "Link to Share",
|
||||
"Reply to Post": "Reply to Post",
|
||||
"Thread Posts": "Thread Posts",
|
||||
"Hashtags": "Hashtags",
|
||||
"Content Warnings": "Content Warnings",
|
||||
"Audience": "Audience",
|
||||
"Select Method": "Select Method",
|
||||
"Select Post": "Select Post",
|
||||
"Post URL": "Post URL",
|
||||
"Thread Depth": "Thread Depth",
|
||||
"Parent Height": "Parent Height",
|
||||
"Type of content you're sharing": "Type of content you're sharing",
|
||||
"What do you want to post? (Max 300 characters)": "What do you want to post? (Max 300 characters)",
|
||||
"Language of your post": "Language of your post",
|
||||
"Add up to 4 images by URL": "Add up to 4 images by URL",
|
||||
"Describe each image for accessibility": "Describe each image for accessibility",
|
||||
"Link to video file (MP4, max 100MB)": "Link to video file (MP4, max 100MB)",
|
||||
"Describe the video for accessibility": "Describe the video for accessibility",
|
||||
"Caption file URLs (optional)": "Caption file URLs (optional)",
|
||||
"URL to share with your post": "URL to share with your post",
|
||||
"URL of post to reply to": "URL of post to reply to",
|
||||
"Create additional connected posts": "Create additional connected posts",
|
||||
"Add hashtags (e.g., tech,bluesky)": "Add hashtags (e.g., tech,bluesky)",
|
||||
"Add warnings for sensitive content": "Add warnings for sensitive content",
|
||||
"Who can see this post": "Who can see this post",
|
||||
"How to choose the post": "How to choose the post",
|
||||
"Choose from your recent timeline posts (only when \"From my timeline\" is selected above)": "Choose from your recent timeline posts (only when \"From my timeline\" is selected above)",
|
||||
"Paste the Bluesky post URL": "Paste the Bluesky post URL",
|
||||
"Paste the Bluesky post URL (e.g., https://bsky.app/profile/username.bsky.social/post/xxx)": "Paste the Bluesky post URL (e.g., https://bsky.app/profile/username.bsky.social/post/xxx)",
|
||||
"How many levels deep to retrieve replies": "How many levels deep to retrieve replies",
|
||||
"How many parent posts to retrieve": "How many parent posts to retrieve",
|
||||
"Text Post": "Text Post",
|
||||
"Photo Post": "Photo Post",
|
||||
"Link Share": "Link Share",
|
||||
"Reply": "Reply",
|
||||
"Repost with Comment": "Repost with Comment",
|
||||
"English": "English",
|
||||
"Spanish": "Spanish",
|
||||
"French": "French",
|
||||
"German": "German",
|
||||
"Italian": "Italian",
|
||||
"Portuguese": "Portuguese",
|
||||
"Japanese": "Japanese",
|
||||
"Korean": "Korean",
|
||||
"Chinese": "Chinese",
|
||||
"Russian": "Russian",
|
||||
"Arabic": "Arabic",
|
||||
"Hindi": "Hindi",
|
||||
"Dutch": "Dutch",
|
||||
"Swedish": "Swedish",
|
||||
"Other": "Other",
|
||||
"Adult Content": "Adult Content",
|
||||
"Graphic Content": "Graphic Content",
|
||||
"Sensitive Topic": "Sensitive Topic",
|
||||
"Violence": "Violence",
|
||||
"Spam/Promotional": "Spam/Promotional",
|
||||
"Everyone (Public)": "Everyone (Public)",
|
||||
"Followers only": "Followers only",
|
||||
"Private/Unlisted": "Private/Unlisted",
|
||||
"From my timeline": "From my timeline",
|
||||
"Enter URL manually": "Enter URL manually",
|
||||
"1 level": "1 level",
|
||||
"2 levels": "2 levels",
|
||||
"3 levels": "3 levels",
|
||||
"5 levels": "5 levels",
|
||||
"10 levels": "10 levels",
|
||||
"20 levels": "20 levels",
|
||||
"50 levels": "50 levels",
|
||||
"100 levels (max)": "100 levels (max)",
|
||||
"No parents": "No parents",
|
||||
"1 parent": "1 parent",
|
||||
"2 parents": "2 parents",
|
||||
"3 parents": "3 parents",
|
||||
"5 parents": "5 parents",
|
||||
"10 parents": "10 parents",
|
||||
"20 parents": "20 parents",
|
||||
"All parents (80 max)": "All parents (80 max)",
|
||||
"New Posts by Author": "New Posts by Author",
|
||||
"New Follower on Account": "New Follower on Account",
|
||||
"New Timeline Posts": "New Timeline Posts",
|
||||
"New Post (with Search Options)": "New Post (with Search Options)",
|
||||
"Triggers when a selected author creates a new post": "Triggers when a selected author creates a new post",
|
||||
"Triggers when someone new follows your Bluesky account": "Triggers when someone new follows your Bluesky account",
|
||||
"Triggers when new posts appear in your timeline": "Triggers when new posts appear in your timeline",
|
||||
"Triggers when posts match your search criteria": "Triggers when posts match your search criteria",
|
||||
"How to select author?": "How to select author?",
|
||||
"Select Author": "Select Author",
|
||||
"Author Handle": "Author Handle",
|
||||
"Include Replies": "Include Replies",
|
||||
"Include Reposts": "Include Reposts",
|
||||
"Search Query": "Search Query",
|
||||
"Language Filter": "Language Filter",
|
||||
"Filter by Images": "Filter by Images",
|
||||
"Filter by Videos": "Filter by Videos",
|
||||
"Sort Order": "Sort Order",
|
||||
"Choose how to select the author": "Choose how to select the author",
|
||||
"Choose from accounts you follow": "Choose from accounts you follow",
|
||||
"Enter the Bluesky username (e.g., username.bsky.social)": "Enter the Bluesky username (e.g., username.bsky.social)",
|
||||
"Include reply posts by this author": "Include reply posts by this author",
|
||||
"Include posts that this author reposted": "Include posts that this author reposted",
|
||||
"Keywords, hashtags (#example), or mentions (@handle) to find": "Keywords, hashtags (#example), or mentions (@handle) to find",
|
||||
"Filter by language": "Filter by language",
|
||||
"Only posts with/without images": "Only posts with/without images",
|
||||
"Only posts with/without videos": "Only posts with/without videos",
|
||||
"How to sort results": "How to sort results",
|
||||
"From my following list": "From my following list",
|
||||
"Enter handle manually": "Enter handle manually",
|
||||
"Latest First": "Latest First",
|
||||
"Most Popular": "Most Popular"
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
{
|
||||
"Bluesky": "Bluesky",
|
||||
"PDS Host": "PDS Host",
|
||||
"Identifier": "Identifier",
|
||||
"Password": "Password",
|
||||
"The Personal Data Server host. Leave empty for default Bluesky network (https://bsky.social)": "The Personal Data Server host. Leave empty for default Bluesky network (https://bsky.social)",
|
||||
"Your Bluesky handle or email address": "Your Bluesky handle or email address",
|
||||
"Your Bluesky account password or app password": "Your Bluesky account password or app password",
|
||||
"\nTo authenticate with Bluesky:\n\n1. **PDS Host**: The Personal Data Server host. Leave empty to use the default Bluesky network (https://bsky.social).\n2. **Identifier**: Your Bluesky handle (e.g., yourhandle.bsky.social) or email address.\n3. **Password**: Your Bluesky account password or app password.\n\nFor enhanced security, consider using an app password from your Bluesky account settings.\n": "\nTo authenticate with Bluesky:\n\n1. **PDS Host**: The Personal Data Server host. Leave empty to use the default Bluesky network (https://bsky.social).\n2. **Identifier**: Your Bluesky handle (e.g., yourhandle.bsky.social) or email address.\n3. **Password**: Your Bluesky account password or app password.\n\nFor enhanced security, consider using an app password from your Bluesky account settings.\n",
|
||||
"Create Post": "Create Post",
|
||||
"Like Post": "Like Post",
|
||||
"Repost Post": "Repost Post",
|
||||
"Find Post": "Find Post",
|
||||
"Find Thread": "Find Thread",
|
||||
"Create a new post on Bluesky": "Create a new post on Bluesky",
|
||||
"Like a post on Bluesky": "Like a post on Bluesky",
|
||||
"Share someone else's post to your timeline": "Share someone else's post to your timeline",
|
||||
"Get detailed information about a specific post": "Get detailed information about a specific post",
|
||||
"Get a full conversation thread with replies": "Get a full conversation thread with replies",
|
||||
"Post Type": "Post Type",
|
||||
"Post Text": "Post Text",
|
||||
"Post Language": "Post Language",
|
||||
"Image URLs": "Image URLs",
|
||||
"Image Descriptions": "Image Descriptions",
|
||||
"Video URL": "Video URL",
|
||||
"Video Description": "Video Description",
|
||||
"Video Captions": "Video Captions",
|
||||
"Link to Share": "Link to Share",
|
||||
"Reply to Post": "Reply to Post",
|
||||
"Thread Posts": "Thread Posts",
|
||||
"Hashtags": "Hashtags",
|
||||
"Content Warnings": "Content Warnings",
|
||||
"Audience": "Audience",
|
||||
"Select Method": "Select Method",
|
||||
"Select Post": "Select Post",
|
||||
"Post URL": "Post URL",
|
||||
"Thread Depth": "Thread Depth",
|
||||
"Parent Height": "Parent Height",
|
||||
"Type of content you're sharing": "Type of content you're sharing",
|
||||
"What do you want to post? (Max 300 characters)": "What do you want to post? (Max 300 characters)",
|
||||
"Language of your post": "Language of your post",
|
||||
"Add up to 4 images by URL": "Add up to 4 images by URL",
|
||||
"Describe each image for accessibility": "Describe each image for accessibility",
|
||||
"Link to video file (MP4, max 100MB)": "Link to video file (MP4, max 100MB)",
|
||||
"Describe the video for accessibility": "Describe the video for accessibility",
|
||||
"Caption file URLs (optional)": "Caption file URLs (optional)",
|
||||
"URL to share with your post": "URL to share with your post",
|
||||
"URL of post to reply to": "URL of post to reply to",
|
||||
"Create additional connected posts": "Create additional connected posts",
|
||||
"Add hashtags (e.g., tech,bluesky)": "Add hashtags (e.g., tech,bluesky)",
|
||||
"Add warnings for sensitive content": "Add warnings for sensitive content",
|
||||
"Who can see this post": "Who can see this post",
|
||||
"How to choose the post": "How to choose the post",
|
||||
"Choose from your recent timeline posts (only when \"From my timeline\" is selected above)": "Choose from your recent timeline posts (only when \"From my timeline\" is selected above)",
|
||||
"Paste the Bluesky post URL": "Paste the Bluesky post URL",
|
||||
"Paste the Bluesky post URL (e.g., https://bsky.app/profile/username.bsky.social/post/xxx)": "Paste the Bluesky post URL (e.g., https://bsky.app/profile/username.bsky.social/post/xxx)",
|
||||
"How many levels deep to retrieve replies": "How many levels deep to retrieve replies",
|
||||
"How many parent posts to retrieve": "How many parent posts to retrieve",
|
||||
"Text Post": "Text Post",
|
||||
"Photo Post": "Photo Post",
|
||||
"Link Share": "Link Share",
|
||||
"Reply": "Reply",
|
||||
"Repost with Comment": "Repost with Comment",
|
||||
"English": "English",
|
||||
"Spanish": "Spanish",
|
||||
"French": "French",
|
||||
"German": "German",
|
||||
"Italian": "Italian",
|
||||
"Portuguese": "Portuguese",
|
||||
"Japanese": "Japanese",
|
||||
"Korean": "Korean",
|
||||
"Chinese": "Chinese",
|
||||
"Russian": "Russian",
|
||||
"Arabic": "Arabic",
|
||||
"Hindi": "Hindi",
|
||||
"Dutch": "Dutch",
|
||||
"Swedish": "Swedish",
|
||||
"Other": "Other",
|
||||
"Adult Content": "Adult Content",
|
||||
"Graphic Content": "Graphic Content",
|
||||
"Sensitive Topic": "Sensitive Topic",
|
||||
"Violence": "Violence",
|
||||
"Spam/Promotional": "Spam/Promotional",
|
||||
"Everyone (Public)": "Everyone (Public)",
|
||||
"Followers only": "Followers only",
|
||||
"Private/Unlisted": "Private/Unlisted",
|
||||
"From my timeline": "From my timeline",
|
||||
"Enter URL manually": "Enter URL manually",
|
||||
"1 level": "1 level",
|
||||
"2 levels": "2 levels",
|
||||
"3 levels": "3 levels",
|
||||
"5 levels": "5 levels",
|
||||
"10 levels": "10 levels",
|
||||
"20 levels": "20 levels",
|
||||
"50 levels": "50 levels",
|
||||
"100 levels (max)": "100 levels (max)",
|
||||
"No parents": "No parents",
|
||||
"1 parent": "1 parent",
|
||||
"2 parents": "2 parents",
|
||||
"3 parents": "3 parents",
|
||||
"5 parents": "5 parents",
|
||||
"10 parents": "10 parents",
|
||||
"20 parents": "20 parents",
|
||||
"All parents (80 max)": "All parents (80 max)",
|
||||
"New Posts by Author": "New Posts by Author",
|
||||
"New Follower on Account": "New Follower on Account",
|
||||
"New Timeline Posts": "New Timeline Posts",
|
||||
"New Post (with Search Options)": "New Post (with Search Options)",
|
||||
"Triggers when a selected author creates a new post": "Triggers when a selected author creates a new post",
|
||||
"Triggers when someone new follows your Bluesky account": "Triggers when someone new follows your Bluesky account",
|
||||
"Triggers when new posts appear in your timeline": "Triggers when new posts appear in your timeline",
|
||||
"Triggers when posts match your search criteria": "Triggers when posts match your search criteria",
|
||||
"How to select author?": "How to select author?",
|
||||
"Select Author": "Select Author",
|
||||
"Author Handle": "Author Handle",
|
||||
"Include Replies": "Include Replies",
|
||||
"Include Reposts": "Include Reposts",
|
||||
"Search Query": "Search Query",
|
||||
"Language Filter": "Language Filter",
|
||||
"Filter by Images": "Filter by Images",
|
||||
"Filter by Videos": "Filter by Videos",
|
||||
"Sort Order": "Sort Order",
|
||||
"Choose how to select the author": "Choose how to select the author",
|
||||
"Choose from accounts you follow": "Choose from accounts you follow",
|
||||
"Enter the Bluesky username (e.g., username.bsky.social)": "Enter the Bluesky username (e.g., username.bsky.social)",
|
||||
"Include reply posts by this author": "Include reply posts by this author",
|
||||
"Include posts that this author reposted": "Include posts that this author reposted",
|
||||
"Keywords, hashtags (#example), or mentions (@handle) to find": "Keywords, hashtags (#example), or mentions (@handle) to find",
|
||||
"Filter by language": "Filter by language",
|
||||
"Only posts with/without images": "Only posts with/without images",
|
||||
"Only posts with/without videos": "Only posts with/without videos",
|
||||
"How to sort results": "How to sort results",
|
||||
"From my following list": "From my following list",
|
||||
"Enter handle manually": "Enter handle manually",
|
||||
"Latest First": "Latest First",
|
||||
"Most Popular": "Most Popular"
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"PDS Host": "PDS Host",
|
||||
"Identifier": "Identifier",
|
||||
"Password": "密码",
|
||||
"The Personal Data Server host. Leave empty for default Bluesky network (https://bsky.social)": "The Personal Data Server host. Leave empty for default Bluesky network (https://bsky.social)",
|
||||
"Your Bluesky handle or email address": "Your Bluesky handle or email address",
|
||||
"Your Bluesky account password or app password": "Your Bluesky account password or app password",
|
||||
"\nTo authenticate with Bluesky:\n\n1. **PDS Host**: The Personal Data Server host. Leave empty to use the default Bluesky network (https://bsky.social).\n2. **Identifier**: Your Bluesky handle (e.g., yourhandle.bsky.social) or email address.\n3. **Password**: Your Bluesky account password or app password.\n\nFor enhanced security, consider using an app password from your Bluesky account settings.\n": "\nTo authenticate with Bluesky:\n\n1. **PDS Host**: The Personal Data Server host. Leave empty to use the default Bluesky network (https://bsky.social).\n2. **Identifier**: Your Bluesky handle (e.g., yourhandle.bsky.social) or email address.\n3. **Password**: Your Bluesky account password or app password.\n\nFor enhanced security, consider using an app password from your Bluesky account settings.\n",
|
||||
"Create Post": "Create Post",
|
||||
"Like Post": "Like Post",
|
||||
"Repost Post": "Repost Post",
|
||||
"Find Post": "Find Post",
|
||||
"Find Thread": "Find Thread",
|
||||
"Create a new post on Bluesky": "Create a new post on Bluesky",
|
||||
"Like a post on Bluesky": "Like a post on Bluesky",
|
||||
"Share someone else's post to your timeline": "Share someone else's post to your timeline",
|
||||
"Get detailed information about a specific post": "Get detailed information about a specific post",
|
||||
"Get a full conversation thread with replies": "Get a full conversation thread with replies",
|
||||
"Post Type": "Post Type",
|
||||
"Post Text": "Post Text",
|
||||
"Post Language": "Post Language",
|
||||
"Image URLs": "Image URLs",
|
||||
"Image Descriptions": "Image Descriptions",
|
||||
"Video URL": "Video URL",
|
||||
"Video Description": "Video Description",
|
||||
"Video Captions": "Video Captions",
|
||||
"Link to Share": "Link to Share",
|
||||
"Reply to Post": "Reply to Post",
|
||||
"Thread Posts": "Thread Posts",
|
||||
"Hashtags": "Hashtags",
|
||||
"Content Warnings": "Content Warnings",
|
||||
"Audience": "Audience",
|
||||
"Select Method": "Select Method",
|
||||
"Select Post": "Select Post",
|
||||
"Post URL": "Post URL",
|
||||
"Thread Depth": "Thread Depth",
|
||||
"Parent Height": "Parent Height",
|
||||
"Type of content you're sharing": "Type of content you're sharing",
|
||||
"What do you want to post? (Max 300 characters)": "What do you want to post? (Max 300 characters)",
|
||||
"Language of your post": "Language of your post",
|
||||
"Add up to 4 images by URL": "Add up to 4 images by URL",
|
||||
"Describe each image for accessibility": "Describe each image for accessibility",
|
||||
"Link to video file (MP4, max 100MB)": "Link to video file (MP4, max 100MB)",
|
||||
"Describe the video for accessibility": "Describe the video for accessibility",
|
||||
"Caption file URLs (optional)": "Caption file URLs (optional)",
|
||||
"URL to share with your post": "URL to share with your post",
|
||||
"URL of post to reply to": "URL of post to reply to",
|
||||
"Create additional connected posts": "Create additional connected posts",
|
||||
"Add hashtags (e.g., tech,bluesky)": "Add hashtags (e.g., tech,bluesky)",
|
||||
"Add warnings for sensitive content": "Add warnings for sensitive content",
|
||||
"Who can see this post": "Who can see this post",
|
||||
"How to choose the post": "How to choose the post",
|
||||
"Choose from your recent timeline posts (only when \"From my timeline\" is selected above)": "Choose from your recent timeline posts (only when \"From my timeline\" is selected above)",
|
||||
"Paste the Bluesky post URL": "Paste the Bluesky post URL",
|
||||
"Paste the Bluesky post URL (e.g., https://bsky.app/profile/username.bsky.social/post/xxx)": "Paste the Bluesky post URL (e.g., https://bsky.app/profile/username.bsky.social/post/xxx)",
|
||||
"How many levels deep to retrieve replies": "How many levels deep to retrieve replies",
|
||||
"How many parent posts to retrieve": "How many parent posts to retrieve",
|
||||
"Text Post": "Text Post",
|
||||
"Photo Post": "Photo Post",
|
||||
"Link Share": "Link Share",
|
||||
"Reply": "Reply",
|
||||
"Repost with Comment": "Repost with Comment",
|
||||
"English": "English",
|
||||
"Spanish": "Spanish",
|
||||
"French": "French",
|
||||
"German": "German",
|
||||
"Italian": "Italian",
|
||||
"Portuguese": "Portuguese",
|
||||
"Japanese": "Japanese",
|
||||
"Korean": "Korean",
|
||||
"Chinese": "Chinese",
|
||||
"Russian": "Russian",
|
||||
"Arabic": "Arabic",
|
||||
"Hindi": "Hindi",
|
||||
"Dutch": "Dutch",
|
||||
"Swedish": "Swedish",
|
||||
"Other": "Other",
|
||||
"Adult Content": "Adult Content",
|
||||
"Graphic Content": "Graphic Content",
|
||||
"Sensitive Topic": "Sensitive Topic",
|
||||
"Violence": "Violence",
|
||||
"Spam/Promotional": "Spam/Promotional",
|
||||
"Everyone (Public)": "Everyone (Public)",
|
||||
"Followers only": "Followers only",
|
||||
"Private/Unlisted": "Private/Unlisted",
|
||||
"From my timeline": "From my timeline",
|
||||
"Enter URL manually": "Enter URL manually",
|
||||
"1 level": "1 level",
|
||||
"2 levels": "2 levels",
|
||||
"3 levels": "3 levels",
|
||||
"5 levels": "5 levels",
|
||||
"10 levels": "10 levels",
|
||||
"20 levels": "20 levels",
|
||||
"50 levels": "50 levels",
|
||||
"100 levels (max)": "100 levels (max)",
|
||||
"No parents": "No parents",
|
||||
"1 parent": "1 parent",
|
||||
"2 parents": "2 parents",
|
||||
"3 parents": "3 parents",
|
||||
"5 parents": "5 parents",
|
||||
"10 parents": "10 parents",
|
||||
"20 parents": "20 parents",
|
||||
"All parents (80 max)": "All parents (80 max)",
|
||||
"New Posts by Author": "New Posts by Author",
|
||||
"New Follower on Account": "New Follower on Account",
|
||||
"New Timeline Posts": "New Timeline Posts",
|
||||
"New Post (with Search Options)": "New Post (with Search Options)",
|
||||
"Triggers when a selected author creates a new post": "Triggers when a selected author creates a new post",
|
||||
"Triggers when someone new follows your Bluesky account": "Triggers when someone new follows your Bluesky account",
|
||||
"Triggers when new posts appear in your timeline": "Triggers when new posts appear in your timeline",
|
||||
"Triggers when posts match your search criteria": "Triggers when posts match your search criteria",
|
||||
"How to select author?": "How to select author?",
|
||||
"Select Author": "Select Author",
|
||||
"Author Handle": "Author Handle",
|
||||
"Include Replies": "Include Replies",
|
||||
"Include Reposts": "Include Reposts",
|
||||
"Search Query": "Search Query",
|
||||
"Language Filter": "Language Filter",
|
||||
"Filter by Images": "Filter by Images",
|
||||
"Filter by Videos": "Filter by Videos",
|
||||
"Sort Order": "Sort Order",
|
||||
"Choose how to select the author": "Choose how to select the author",
|
||||
"Choose from accounts you follow": "Choose from accounts you follow",
|
||||
"Enter the Bluesky username (e.g., username.bsky.social)": "Enter the Bluesky username (e.g., username.bsky.social)",
|
||||
"Include reply posts by this author": "Include reply posts by this author",
|
||||
"Include posts that this author reposted": "Include posts that this author reposted",
|
||||
"Keywords, hashtags (#example), or mentions (@handle) to find": "Keywords, hashtags (#example), or mentions (@handle) to find",
|
||||
"Filter by language": "Filter by language",
|
||||
"Only posts with/without images": "Only posts with/without images",
|
||||
"Only posts with/without videos": "Only posts with/without videos",
|
||||
"How to sort results": "How to sort results",
|
||||
"From my following list": "From my following list",
|
||||
"Enter handle manually": "Enter handle manually",
|
||||
"Latest First": "Latest First",
|
||||
"Most Popular": "Most Popular"
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { createPiece, PieceAuth } from '@activepieces/pieces-framework';
|
||||
import { PieceCategory } from '@activepieces/shared';
|
||||
import { blueskyAuth } from './lib/common/auth';
|
||||
import { createPost } from './lib/actions/create-post';
|
||||
import { likePost } from './lib/actions/like-post';
|
||||
import { repostPost } from './lib/actions/repost';
|
||||
import { findPost } from './lib/actions/find-post';
|
||||
import { findThread } from './lib/actions/find-thread';
|
||||
import { newPostsByAuthor } from './lib/triggers/new-posts-by-author';
|
||||
import { newFollowerOnAccount } from './lib/triggers/new-follower-on-account';
|
||||
import { newTimelinePosts } from './lib/triggers/new-timeline-posts';
|
||||
import { newPost } from './lib/triggers/new-post';
|
||||
export { blueskyAuth } from './lib/common/auth';
|
||||
export { createBlueskyAgent } from './lib/common/client';
|
||||
|
||||
export const bluesky = createPiece({
|
||||
displayName: 'Bluesky',
|
||||
auth: blueskyAuth,
|
||||
minimumSupportedRelease: '0.36.1',
|
||||
logoUrl: 'https://cdn.activepieces.com/pieces/bluesky.png',
|
||||
authors: ['Sanket6652'],
|
||||
categories: [PieceCategory.COMMUNICATION],
|
||||
actions: [createPost, likePost, repostPost, findPost, findThread],
|
||||
triggers: [newPostsByAuthor, newFollowerOnAccount, newTimelinePosts, newPost],
|
||||
});
|
||||
@@ -0,0 +1,439 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { blueskyAuth } from '../common/auth';
|
||||
import { createBlueskyAgent } from '../common/client';
|
||||
import { RichText } from '@atproto/api';
|
||||
import {
|
||||
postTextProperty,
|
||||
postTypeDropdown,
|
||||
simpleLanguageDropdown,
|
||||
imageUrlsProperty,
|
||||
imageDescriptionsProperty,
|
||||
linkUrlProperty,
|
||||
replyToPostProperty,
|
||||
contentWarningDropdown,
|
||||
audienceDropdown
|
||||
} from '../common/props';
|
||||
|
||||
const videoUrlProperty = Property.ShortText({
|
||||
displayName: 'Video URL',
|
||||
description: 'Link to video file (MP4, max 100MB)',
|
||||
required: false,
|
||||
});
|
||||
|
||||
const videoAltTextProperty = Property.LongText({
|
||||
displayName: 'Video Description',
|
||||
description: 'Describe the video for accessibility',
|
||||
required: false,
|
||||
});
|
||||
|
||||
const videoCaptionsProperty = Property.Array({
|
||||
displayName: 'Video Captions',
|
||||
description: 'Caption file URLs (optional)',
|
||||
required: false,
|
||||
});
|
||||
|
||||
const threadContentProperty = Property.Array({
|
||||
displayName: 'Thread Posts',
|
||||
description: 'Create additional connected posts',
|
||||
required: false,
|
||||
});
|
||||
|
||||
const additionalHashtagsProperty = Property.ShortText({
|
||||
displayName: 'Hashtags',
|
||||
description: 'Add hashtags (e.g., tech,bluesky)',
|
||||
required: false,
|
||||
});
|
||||
|
||||
async function parsePostUrl(url: string, agent: any): Promise<string> {
|
||||
if (url.startsWith('at://')) {
|
||||
return url;
|
||||
}
|
||||
|
||||
const urlMatch = url.match(/https?:\/\/bsky\.app\/profile\/([^/]+)\/post\/([^/?]+)/);
|
||||
if (urlMatch) {
|
||||
const handle = urlMatch[1];
|
||||
const postId = urlMatch[2];
|
||||
|
||||
const didDoc = await agent.resolveHandle({ handle });
|
||||
return `at://${didDoc.data.did}/app.bsky.feed.post/${postId}`;
|
||||
}
|
||||
|
||||
throw new Error('Invalid post URL format. Please use a valid Bluesky post URL.');
|
||||
}
|
||||
|
||||
async function getReplyRefs(postUri: string, agent: any): Promise<any> {
|
||||
try {
|
||||
const postThread = await agent.getPostThread({ uri: postUri });
|
||||
const post = postThread.thread.post;
|
||||
|
||||
if (!post) {
|
||||
throw new Error('Post not found');
|
||||
}
|
||||
|
||||
const parentReply = post.record.reply;
|
||||
|
||||
return {
|
||||
root: parentReply?.root || { uri: post.uri, cid: post.cid },
|
||||
parent: { uri: post.uri, cid: post.cid },
|
||||
};
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to resolve reply: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function createEnhancedLinkEmbed(url: string, agent: any): Promise<any> {
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
'User-Agent': 'Mozilla/5.0 (compatible; Activepieces-Bot/1.0)',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
}
|
||||
|
||||
const html = await response.text();
|
||||
|
||||
const titleMatch = html.match(/<meta\s+property="og:title"\s+content="([^"]*)"[^>]*>/i) ||
|
||||
html.match(/<title[^>]*>([^<]*)<\/title>/i);
|
||||
const title = titleMatch ? titleMatch[1].trim() : url;
|
||||
|
||||
const descMatch = html.match(/<meta\s+property="og:description"\s+content="([^"]*)"[^>]*>/i) ||
|
||||
html.match(/<meta\s+name="description"\s+content="([^"]*)"[^>]*>/i);
|
||||
const description = descMatch ? descMatch[1].trim() : '';
|
||||
|
||||
const imageMatch = html.match(/<meta\s+property="og:image"\s+content="([^"]*)"[^>]*>/i);
|
||||
|
||||
const external: any = {
|
||||
uri: url,
|
||||
title: title.length > 0 ? title : url,
|
||||
description: description.length > 0 ? description : 'Shared link',
|
||||
};
|
||||
|
||||
if (imageMatch && imageMatch[1]) {
|
||||
try {
|
||||
let imageUrl = imageMatch[1];
|
||||
|
||||
if (imageUrl.startsWith('/')) {
|
||||
const urlObj = new URL(url);
|
||||
imageUrl = `${urlObj.protocol}//${urlObj.host}${imageUrl}`;
|
||||
} else if (!imageUrl.startsWith('http')) {
|
||||
imageUrl = new URL(imageUrl, url).href;
|
||||
}
|
||||
|
||||
const imageResponse = await fetch(imageUrl);
|
||||
if (imageResponse.ok) {
|
||||
const imageBlob = await imageResponse.blob();
|
||||
|
||||
if (imageBlob.size <= 1000000 && imageBlob.type.startsWith('image/')) {
|
||||
const thumbResponse = await agent.uploadBlob(imageBlob, {
|
||||
encoding: imageBlob.type,
|
||||
});
|
||||
external.thumb = thumbResponse.data.blob;
|
||||
}
|
||||
}
|
||||
} catch (thumbError) {
|
||||
console.warn('Failed to upload link thumbnail:', thumbError);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
$type: 'app.bsky.embed.external',
|
||||
external,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
$type: 'app.bsky.embed.external',
|
||||
external: {
|
||||
uri: url,
|
||||
title: url,
|
||||
description: 'Shared link',
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const createPost = createAction({
|
||||
auth: blueskyAuth,
|
||||
name: 'createPost',
|
||||
displayName: 'Create Post',
|
||||
description: 'Create a new post on Bluesky',
|
||||
props: {
|
||||
postType: postTypeDropdown,
|
||||
text: postTextProperty,
|
||||
language: simpleLanguageDropdown,
|
||||
imageUrls: imageUrlsProperty,
|
||||
imageDescriptions: imageDescriptionsProperty,
|
||||
videoUrl: videoUrlProperty,
|
||||
videoAltText: videoAltTextProperty,
|
||||
videoCaptions: videoCaptionsProperty,
|
||||
linkUrl: linkUrlProperty,
|
||||
replyToPost: replyToPostProperty,
|
||||
threadContent: threadContentProperty,
|
||||
additionalHashtags: additionalHashtagsProperty,
|
||||
contentWarnings: contentWarningDropdown,
|
||||
audience: audienceDropdown,
|
||||
},
|
||||
async run({ auth, propsValue }) {
|
||||
const {
|
||||
text,
|
||||
language,
|
||||
imageUrls,
|
||||
imageDescriptions,
|
||||
videoUrl,
|
||||
videoAltText,
|
||||
videoCaptions,
|
||||
linkUrl,
|
||||
replyToPost,
|
||||
threadContent,
|
||||
additionalHashtags,
|
||||
contentWarnings,
|
||||
audience
|
||||
} = propsValue;
|
||||
|
||||
try {
|
||||
const agent = await createBlueskyAgent(auth.props);
|
||||
|
||||
let processedText = text;
|
||||
if (additionalHashtags && additionalHashtags.trim()) {
|
||||
const hashtags = additionalHashtags
|
||||
.split(',')
|
||||
.map(tag => tag.trim().startsWith('#') ? tag.trim() : `#${tag.trim()}`)
|
||||
.filter(tag => tag.length > 1)
|
||||
.join(' ');
|
||||
|
||||
if (hashtags) {
|
||||
processedText += ` ${hashtags}`;
|
||||
}
|
||||
}
|
||||
|
||||
const richText = new RichText({ text: processedText });
|
||||
await richText.detectFacets(agent);
|
||||
|
||||
if (richText.length > 300) {
|
||||
throw new Error('Post text cannot exceed 300 characters');
|
||||
}
|
||||
|
||||
const postRecord: any = {
|
||||
text: richText.text,
|
||||
facets: richText.facets,
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
if (additionalHashtags && additionalHashtags.trim()) {
|
||||
const tagList = additionalHashtags
|
||||
.split(',')
|
||||
.map(tag => tag.trim().replace(/^#/, ''))
|
||||
.filter(tag => tag.length > 0);
|
||||
|
||||
if (tagList.length > 0) {
|
||||
postRecord.tags = tagList;
|
||||
}
|
||||
}
|
||||
|
||||
if (contentWarnings && contentWarnings.length > 0) {
|
||||
postRecord.labels = {
|
||||
$type: 'com.atproto.label.defs#selfLabels',
|
||||
values: contentWarnings.map((warning: string) => ({ val: warning })),
|
||||
};
|
||||
}
|
||||
|
||||
if (language && language !== 'other') {
|
||||
postRecord.langs = [language];
|
||||
}
|
||||
|
||||
if (replyToPost && replyToPost.trim()) {
|
||||
const postUri = await parsePostUrl(replyToPost, agent);
|
||||
postRecord.reply = await getReplyRefs(postUri, agent);
|
||||
}
|
||||
|
||||
if (videoUrl && videoUrl.trim()) {
|
||||
try {
|
||||
const videoResponse = await fetch(videoUrl);
|
||||
if (!videoResponse.ok) {
|
||||
throw new Error(`Failed to fetch video from ${videoUrl}`);
|
||||
}
|
||||
|
||||
const videoBlob = await videoResponse.blob();
|
||||
|
||||
if (!videoBlob.type.startsWith('video/')) {
|
||||
throw new Error('File must be a video format');
|
||||
}
|
||||
|
||||
if (videoBlob.size > 100000000) {
|
||||
throw new Error(`Video is too large (max 100MB), got ${Math.round(videoBlob.size / 1000000)}MB`);
|
||||
}
|
||||
|
||||
const videoBlobResponse = await agent.uploadBlob(videoBlob, {
|
||||
encoding: videoBlob.type,
|
||||
});
|
||||
|
||||
const captionObjects = [];
|
||||
if (videoCaptions && videoCaptions.length > 0) {
|
||||
for (let i = 0; i < videoCaptions.length; i++) {
|
||||
const captionUrl = videoCaptions[i] as string;
|
||||
try {
|
||||
const captionResponse = await fetch(captionUrl);
|
||||
if (!captionResponse.ok) continue;
|
||||
|
||||
const captionBlob = await captionResponse.blob();
|
||||
const captionBlobResponse = await agent.uploadBlob(captionBlob, {
|
||||
encoding: 'text/vtt',
|
||||
});
|
||||
|
||||
captionObjects.push({
|
||||
lang: 'en',
|
||||
file: captionBlobResponse.data.blob,
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn(`Failed to upload caption ${i + 1}:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const videoEmbed: any = {
|
||||
$type: 'app.bsky.embed.video',
|
||||
video: videoBlobResponse.data.blob,
|
||||
};
|
||||
|
||||
if (videoAltText && videoAltText.trim()) {
|
||||
videoEmbed.alt = videoAltText.trim();
|
||||
}
|
||||
|
||||
if (captionObjects.length > 0) {
|
||||
videoEmbed.captions = captionObjects;
|
||||
}
|
||||
|
||||
postRecord.embed = videoEmbed;
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to process video: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
}
|
||||
else if (imageUrls && imageUrls.length > 0) {
|
||||
if (imageUrls.length > 4) {
|
||||
throw new Error('Maximum of 4 images allowed per post');
|
||||
}
|
||||
|
||||
const images = [];
|
||||
for (let i = 0; i < imageUrls.length; i++) {
|
||||
const imageUrl = imageUrls[i] as string;
|
||||
const altText = (imageDescriptions?.[i] as string) || '';
|
||||
|
||||
try {
|
||||
const imageResponse = await fetch(imageUrl);
|
||||
if (!imageResponse.ok) {
|
||||
throw new Error(`Failed to fetch image from ${imageUrl}`);
|
||||
}
|
||||
|
||||
const imageBlob = await imageResponse.blob();
|
||||
|
||||
if (imageBlob.size > 1000000) {
|
||||
throw new Error(`Image at ${imageUrl} is too large (max 1MB)`);
|
||||
}
|
||||
|
||||
const blobResponse = await agent.uploadBlob(imageBlob, {
|
||||
encoding: imageBlob.type,
|
||||
});
|
||||
|
||||
images.push({
|
||||
alt: altText,
|
||||
image: blobResponse.data.blob,
|
||||
});
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to upload image ${i + 1}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
}
|
||||
|
||||
postRecord.embed = {
|
||||
$type: 'app.bsky.embed.images',
|
||||
images,
|
||||
};
|
||||
}
|
||||
|
||||
else if (linkUrl && linkUrl.trim()) {
|
||||
try {
|
||||
const linkEmbed = await createEnhancedLinkEmbed(linkUrl, agent);
|
||||
postRecord.embed = linkEmbed;
|
||||
} catch (error) {
|
||||
console.warn('Failed to fetch link metadata, using basic embed:', error);
|
||||
postRecord.embed = {
|
||||
$type: 'app.bsky.embed.external',
|
||||
external: {
|
||||
uri: linkUrl,
|
||||
title: linkUrl,
|
||||
description: 'Shared link',
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const mainPostResponse = await agent.post(postRecord);
|
||||
const createdPosts = [mainPostResponse];
|
||||
|
||||
if (threadContent && threadContent.length > 0) {
|
||||
let previousPost = mainPostResponse;
|
||||
|
||||
for (let i = 0; i < threadContent.length; i++) {
|
||||
const threadText = threadContent[i] as string;
|
||||
if (!threadText || !threadText.trim()) continue;
|
||||
|
||||
try {
|
||||
const threadRichText = new RichText({ text: threadText });
|
||||
await threadRichText.detectFacets(agent);
|
||||
|
||||
if (threadRichText.length > 300) {
|
||||
console.warn(`Thread post ${i + 1} exceeds 300 characters, skipping`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const threadPostRecord: any = {
|
||||
text: threadRichText.text,
|
||||
facets: threadRichText.facets,
|
||||
createdAt: new Date().toISOString(),
|
||||
reply: {
|
||||
root: { uri: mainPostResponse.uri, cid: mainPostResponse.cid },
|
||||
parent: { uri: previousPost.uri, cid: previousPost.cid },
|
||||
},
|
||||
};
|
||||
|
||||
if (language && language !== 'other') {
|
||||
threadPostRecord.langs = [language];
|
||||
}
|
||||
|
||||
if (contentWarnings && contentWarnings.length > 0) {
|
||||
threadPostRecord.labels = {
|
||||
$type: 'com.atproto.label.defs#selfLabels',
|
||||
values: contentWarnings.map((warning: string) => ({ val: warning })),
|
||||
};
|
||||
}
|
||||
|
||||
const threadResponse = await agent.post(threadPostRecord);
|
||||
createdPosts.push(threadResponse);
|
||||
previousPost = threadResponse;
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
} catch (threadError) {
|
||||
console.warn(`Failed to create thread post ${i + 1}:`, threadError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
mainPost: {
|
||||
uri: mainPostResponse.uri,
|
||||
cid: mainPostResponse.cid,
|
||||
},
|
||||
threadPosts: createdPosts.slice(1).map(post => ({
|
||||
uri: post.uri,
|
||||
cid: post.cid,
|
||||
})),
|
||||
totalPosts: createdPosts.length,
|
||||
record: postRecord,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to create post: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,71 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { blueskyAuth } from '../common/auth';
|
||||
import { createBlueskyAgent } from '../common/client';
|
||||
import { postUrlProperty, extractPostInfoFromUrl } from '../common/props';
|
||||
|
||||
export const findPost = createAction({
|
||||
auth: blueskyAuth,
|
||||
name: 'findPost',
|
||||
displayName: 'Find Post',
|
||||
description: 'Get detailed information about a specific post',
|
||||
props: {
|
||||
postUrl: postUrlProperty,
|
||||
},
|
||||
async run({ auth, propsValue }) {
|
||||
const { postUrl } = propsValue;
|
||||
|
||||
try {
|
||||
const agent = await createBlueskyAgent(auth.props);
|
||||
|
||||
const postInfo = extractPostInfoFromUrl(postUrl);
|
||||
let atUri = postInfo.uri;
|
||||
|
||||
if (!atUri && postInfo.handle && postInfo.postId) {
|
||||
const didDoc = await agent.resolveHandle({ handle: postInfo.handle });
|
||||
|
||||
if (didDoc.data?.did) {
|
||||
atUri = `at://${didDoc.data.did}/app.bsky.feed.post/${postInfo.postId}`;
|
||||
} else {
|
||||
throw new Error(`Could not resolve handle: ${postInfo.handle}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!atUri) {
|
||||
throw new Error('Could not parse the post URL. Please make sure you are using a valid Bluesky post URL like: https://bsky.app/profile/username.bsky.social/post/xxx');
|
||||
}
|
||||
|
||||
if (!atUri.startsWith('at://')) {
|
||||
throw new Error('Invalid URI format. Must be an AT-URI starting with "at://" or a valid Bluesky URL');
|
||||
}
|
||||
|
||||
const response = await agent.getPosts({ uris: [atUri] });
|
||||
|
||||
if (!response.data?.posts || response.data.posts.length === 0) {
|
||||
throw new Error('Post not found or not accessible');
|
||||
}
|
||||
|
||||
const post = response.data.posts[0];
|
||||
|
||||
return {
|
||||
success: true,
|
||||
uri: atUri,
|
||||
cid: post.cid,
|
||||
record: post.record,
|
||||
author: post.author,
|
||||
indexedAt: post.indexedAt,
|
||||
replyCount: post.replyCount || 0,
|
||||
repostCount: post.repostCount || 0,
|
||||
likeCount: post.likeCount || 0,
|
||||
quoteCount: post.quoteCount || 0,
|
||||
embed: post.embed,
|
||||
labels: post.labels,
|
||||
threadgate: post.threadgate,
|
||||
viewer: post.viewer,
|
||||
retrievedAt: new Date().toISOString(),
|
||||
};
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
||||
throw new Error(`Failed to find post: ${errorMessage}`);
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,116 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { blueskyAuth } from '../common/auth';
|
||||
import { createBlueskyAgent } from '../common/client';
|
||||
import { postUrlProperty, threadDepthDropdown, parentHeightDropdown, extractPostInfoFromUrl } from '../common/props';
|
||||
|
||||
export const findThread = createAction({
|
||||
auth: blueskyAuth,
|
||||
name: 'findThread',
|
||||
displayName: 'Find Thread',
|
||||
description: 'Get a full conversation thread with replies',
|
||||
props: {
|
||||
postUrl: postUrlProperty,
|
||||
depth: threadDepthDropdown,
|
||||
parentHeight: parentHeightDropdown,
|
||||
},
|
||||
async run({ auth, propsValue }) {
|
||||
const { postUrl, depth = 6, parentHeight = 80 } = propsValue;
|
||||
|
||||
try {
|
||||
const agent = await createBlueskyAgent(auth.props);
|
||||
|
||||
const postInfo = extractPostInfoFromUrl(postUrl);
|
||||
let atUri = postInfo.uri;
|
||||
|
||||
if (!atUri && postInfo.handle && postInfo.postId) {
|
||||
const didDoc = await agent.resolveHandle({ handle: postInfo.handle });
|
||||
|
||||
if (didDoc.data?.did) {
|
||||
atUri = `at://${didDoc.data.did}/app.bsky.feed.post/${postInfo.postId}`;
|
||||
} else {
|
||||
throw new Error(`Could not resolve handle: ${postInfo.handle}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!atUri) {
|
||||
throw new Error('Could not parse the post URL. Please make sure you are using a valid Bluesky post URL like: https://bsky.app/profile/username.bsky.social/post/xxx');
|
||||
}
|
||||
|
||||
if (!atUri.startsWith('at://')) {
|
||||
throw new Error('Invalid URI format. Must be an AT-URI starting with "at://" or a valid Bluesky URL');
|
||||
}
|
||||
|
||||
const depthNum = parseInt(depth.toString(), 10);
|
||||
const parentHeightNum = parseInt(parentHeight.toString(), 10);
|
||||
|
||||
if (isNaN(depthNum) || depthNum < 0 || depthNum > 1000) {
|
||||
throw new Error('Depth must be a number between 0 and 1000');
|
||||
}
|
||||
if (isNaN(parentHeightNum) || parentHeightNum < 0 || parentHeightNum > 1000) {
|
||||
throw new Error('Parent height must be a number between 0 and 1000');
|
||||
}
|
||||
|
||||
const response = await agent.getPostThread({
|
||||
uri: atUri,
|
||||
depth: depthNum,
|
||||
parentHeight: parentHeightNum,
|
||||
});
|
||||
|
||||
const stats = {
|
||||
totalPosts: 0,
|
||||
parentPosts: 0,
|
||||
replyPosts: 0,
|
||||
notFoundPosts: 0,
|
||||
blockedPosts: 0,
|
||||
};
|
||||
|
||||
const countPosts = (threadPost: any): void => {
|
||||
if (!threadPost) return;
|
||||
|
||||
if (threadPost.$type === 'app.bsky.feed.defs#notFoundPost') {
|
||||
stats.notFoundPosts++;
|
||||
} else if (threadPost.$type === 'app.bsky.feed.defs#blockedPost') {
|
||||
stats.blockedPosts++;
|
||||
} else if (threadPost.post) {
|
||||
stats.totalPosts++;
|
||||
}
|
||||
|
||||
if (threadPost.parent) {
|
||||
stats.parentPosts++;
|
||||
countPosts(threadPost.parent);
|
||||
}
|
||||
|
||||
if (threadPost.replies && Array.isArray(threadPost.replies)) {
|
||||
stats.replyPosts += threadPost.replies.length;
|
||||
threadPost.replies.forEach((reply: any) => countPosts(reply));
|
||||
}
|
||||
};
|
||||
|
||||
if (response.data.thread) {
|
||||
countPosts(response.data.thread);
|
||||
if (
|
||||
response.data.thread.$type === 'app.bsky.feed.defs#threadViewPost' &&
|
||||
'post' in response.data.thread &&
|
||||
response.data.thread.post
|
||||
) {
|
||||
stats.totalPosts = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
thread: response.data.thread,
|
||||
requestedUri: atUri,
|
||||
parameters: {
|
||||
depth: depthNum,
|
||||
parentHeight: parentHeightNum,
|
||||
},
|
||||
statistics: stats,
|
||||
retrievedAt: new Date().toISOString(),
|
||||
};
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
||||
throw new Error(`Failed to find thread: ${errorMessage}`);
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,123 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { blueskyAuth, BlueSkyAuthType } from '../common/auth';
|
||||
import { createBlueskyAgent } from '../common/client';
|
||||
import { parseBlueskyUrl } from '../common/props';
|
||||
|
||||
|
||||
export const likePost = createAction({
|
||||
auth: blueskyAuth,
|
||||
name: 'likePost',
|
||||
displayName: 'Like Post',
|
||||
description: 'Like a post on Bluesky',
|
||||
props: {
|
||||
selectionMethod: Property.StaticDropdown({
|
||||
displayName: 'Select Method',
|
||||
description: 'How to choose the post',
|
||||
required: true,
|
||||
defaultValue: 'timeline',
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'From my timeline', value: 'timeline' },
|
||||
{ label: 'Enter URL manually', value: 'manual' },
|
||||
],
|
||||
},
|
||||
}),
|
||||
|
||||
postSelection: Property.Dropdown({
|
||||
auth: blueskyAuth,
|
||||
displayName: 'Select Post',
|
||||
description: 'Choose from your recent timeline posts (only when "From my timeline" is selected above)',
|
||||
required: false,
|
||||
refreshers: ['auth'],
|
||||
options: async ({ auth }) => {
|
||||
try {
|
||||
if (!auth) return { options: [] };
|
||||
const agent = await createBlueskyAgent(auth.props);
|
||||
const timeline = await agent.getTimeline({ limit: 50 });
|
||||
|
||||
return {
|
||||
options: timeline.data.feed.map(item => ({
|
||||
label: `@${item.post.author.handle}: ${item.post.record['text'] ? String(item.post.record['text']).substring(0, 80) : 'Media post'}${String(item.post.record['text'] || '').length > 80 ? '...' : ''} (${new Date(item.post.indexedAt).toLocaleDateString()})`,
|
||||
value: item.post.uri
|
||||
}))
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
options: [{ label: 'Error loading posts', value: '' }]
|
||||
};
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
postUrl: Property.ShortText({
|
||||
displayName: 'Post URL',
|
||||
description: 'Paste the Bluesky post URL',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
async run({ auth, propsValue }) {
|
||||
const { selectionMethod, postSelection, postUrl } = propsValue;
|
||||
|
||||
let postUri: string;
|
||||
|
||||
if (selectionMethod === 'timeline') {
|
||||
if (!postSelection) {
|
||||
throw new Error('Please select a post from your timeline dropdown');
|
||||
}
|
||||
postUri = postSelection as string;
|
||||
} else if (selectionMethod === 'manual') {
|
||||
if (!postUrl || !postUrl.trim()) {
|
||||
throw new Error('Post URL is required when using manual entry method');
|
||||
}
|
||||
|
||||
try {
|
||||
const agent = await createBlueskyAgent(auth.props);
|
||||
postUri = await parseBlueskyUrl(postUrl.trim(), agent);
|
||||
} catch (error) {
|
||||
throw new Error(`Invalid post URL: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
} else {
|
||||
throw new Error('Please select a post selection method');
|
||||
}
|
||||
|
||||
try {
|
||||
const agent = await createBlueskyAgent(auth.props);
|
||||
|
||||
const postsResponse = await agent.getPosts({ uris: [postUri] });
|
||||
|
||||
if (!postsResponse.data.posts || postsResponse.data.posts.length === 0) {
|
||||
throw new Error('Post not found');
|
||||
}
|
||||
|
||||
const post = postsResponse.data.posts[0];
|
||||
|
||||
const response = await agent.like(postUri, post.cid);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
likeUri: response.uri,
|
||||
likeCid: response.cid,
|
||||
postUri: postUri,
|
||||
postCid: post.cid,
|
||||
postAuthor: post.author.handle,
|
||||
postText: post.record['text'] ? String(post.record['text']).substring(0, 100) + (String(post.record['text']).length > 100 ? '...' : '') : 'No text available',
|
||||
selectionMethod: selectionMethod,
|
||||
likedAt: new Date().toISOString(),
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
if (error.message.includes('Post not found')) {
|
||||
throw new Error('Post not found. Please check the URL and try again.');
|
||||
}
|
||||
if (error.message.includes('Invalid post URL format')) {
|
||||
throw new Error('Invalid post URL format. Please use a valid Bluesky post URL or AT-URI.');
|
||||
}
|
||||
if (error.message.includes('Authentication')) {
|
||||
throw new Error('Authentication failed. Please check your credentials.');
|
||||
}
|
||||
throw new Error(`Failed to like post: ${error.message}`);
|
||||
}
|
||||
throw new Error('Failed to like post: Unknown error occurred');
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,127 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { blueskyAuth, BlueSkyAuthType } from '../common/auth';
|
||||
import { createBlueskyAgent } from '../common/client';
|
||||
import { parseBlueskyUrl } from '../common/props';
|
||||
|
||||
|
||||
|
||||
export const repostPost = createAction({
|
||||
auth: blueskyAuth,
|
||||
name: 'repostPost',
|
||||
displayName: 'Repost Post',
|
||||
description: 'Share someone else\'s post to your timeline',
|
||||
props: {
|
||||
selectionMethod: Property.StaticDropdown({
|
||||
displayName: 'Select Method',
|
||||
description: 'How to choose the post',
|
||||
required: true,
|
||||
defaultValue: 'timeline',
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'From my timeline', value: 'timeline' },
|
||||
{ label: 'Enter URL manually', value: 'manual' },
|
||||
],
|
||||
},
|
||||
}),
|
||||
|
||||
postSelection: Property.Dropdown({
|
||||
auth: blueskyAuth,
|
||||
displayName: 'Select Post',
|
||||
description: 'Choose from your recent timeline posts (only when "From my timeline" is selected above)',
|
||||
required: false,
|
||||
refreshers: ['auth'],
|
||||
options: async ({ auth }) => {
|
||||
try {
|
||||
if (!auth) return { options: [] };
|
||||
const agent = await createBlueskyAgent(auth.props);
|
||||
const timeline = await agent.getTimeline({ limit: 50 });
|
||||
|
||||
return {
|
||||
options: timeline.data.feed.map(item => ({
|
||||
label: `@${item.post.author.handle}: ${item.post.record['text'] ? String(item.post.record['text']).substring(0, 80) : 'Media post'}${String(item.post.record['text'] || '').length > 80 ? '...' : ''} (${new Date(item.post.indexedAt).toLocaleDateString()})`,
|
||||
value: item.post.uri
|
||||
}))
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
options: [{ label: 'Error loading posts', value: '' }]
|
||||
};
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
postUrl: Property.ShortText({
|
||||
displayName: 'Post URL',
|
||||
description: 'Paste the Bluesky post URL',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
async run({ auth, propsValue }) {
|
||||
const { selectionMethod, postSelection, postUrl } = propsValue;
|
||||
|
||||
let postUri: string;
|
||||
|
||||
if (selectionMethod === 'timeline') {
|
||||
if (!postSelection) {
|
||||
throw new Error('Please select a post from your timeline dropdown');
|
||||
}
|
||||
postUri = postSelection as string;
|
||||
} else if (selectionMethod === 'manual') {
|
||||
if (!postUrl || !postUrl.trim()) {
|
||||
throw new Error('Post URL is required when using manual entry method');
|
||||
}
|
||||
|
||||
try {
|
||||
const agent = await createBlueskyAgent(auth.props);
|
||||
postUri = await parseBlueskyUrl(postUrl.trim(), agent);
|
||||
} catch (error) {
|
||||
throw new Error(`Invalid post URL: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
} else {
|
||||
throw new Error('Please select a post selection method');
|
||||
}
|
||||
|
||||
try {
|
||||
const agent = await createBlueskyAgent(auth.props);
|
||||
|
||||
const postsResponse = await agent.getPosts({ uris: [postUri] });
|
||||
|
||||
if (!postsResponse.data.posts || postsResponse.data.posts.length === 0) {
|
||||
throw new Error('Post not found');
|
||||
}
|
||||
|
||||
const post = postsResponse.data.posts[0];
|
||||
|
||||
const response = await agent.repost(postUri, post.cid);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
repostUri: response.uri,
|
||||
repostCid: response.cid,
|
||||
originalPost: {
|
||||
uri: postUri,
|
||||
cid: post.cid,
|
||||
author: post.author.handle,
|
||||
text: post.record['text'] ? String(post.record['text']).substring(0, 100) + (String(post.record['text']).length > 100 ? '...' : '') : 'No text available',
|
||||
createdAt: post.record['createdAt'] || post.indexedAt,
|
||||
},
|
||||
selectionMethod: selectionMethod,
|
||||
repostedAt: new Date().toISOString(),
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
if (error.message.includes('Post not found')) {
|
||||
throw new Error('Post not found. Please check the URL and try again.');
|
||||
}
|
||||
if (error.message.includes('Invalid post URL format')) {
|
||||
throw new Error('Invalid post URL format. Please use a valid Bluesky post URL or AT-URI.');
|
||||
}
|
||||
if (error.message.includes('Authentication')) {
|
||||
throw new Error('Authentication failed. Please check your credentials.');
|
||||
}
|
||||
throw new Error(`Failed to repost: ${error.message}`);
|
||||
}
|
||||
throw new Error('Failed to repost: Unknown error occurred');
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,74 @@
|
||||
import { PieceAuth, Property } from '@activepieces/pieces-framework';
|
||||
import { AtpAgent } from '@atproto/api';
|
||||
|
||||
export interface BlueSkyAuthType {
|
||||
pdsHost?: string;
|
||||
identifier: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
const description = `
|
||||
To authenticate with Bluesky:
|
||||
|
||||
1. **PDS Host**: The Personal Data Server host. Leave empty to use the default Bluesky network (https://bsky.social).
|
||||
2. **Identifier**: Your Bluesky handle (e.g., yourhandle.bsky.social) or email address.
|
||||
3. **Password**: Your Bluesky account password or app password.
|
||||
|
||||
For enhanced security, consider using an app password from your Bluesky account settings.
|
||||
`;
|
||||
|
||||
export const blueskyAuth = PieceAuth.CustomAuth({
|
||||
description: description,
|
||||
required: true,
|
||||
props: {
|
||||
pdsHost: Property.ShortText({
|
||||
displayName: 'PDS Host',
|
||||
description: 'The Personal Data Server host. Leave empty for default Bluesky network (https://bsky.social)',
|
||||
required: false,
|
||||
defaultValue: 'https://bsky.social',
|
||||
}),
|
||||
identifier: Property.ShortText({
|
||||
displayName: 'Identifier',
|
||||
description: 'Your Bluesky handle or email address',
|
||||
required: true,
|
||||
}),
|
||||
password: PieceAuth.SecretText({
|
||||
displayName: 'Password',
|
||||
description: 'Your Bluesky account password or app password',
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
validate: async ({ auth }) => {
|
||||
try {
|
||||
const agent = new AtpAgent({
|
||||
service: auth.pdsHost || 'https://bsky.social',
|
||||
});
|
||||
|
||||
await agent.login({
|
||||
identifier: auth.identifier,
|
||||
password: auth.password,
|
||||
});
|
||||
|
||||
return {
|
||||
valid: true,
|
||||
};
|
||||
} catch (error: any) {
|
||||
if (error.message?.includes('Invalid identifier or password')) {
|
||||
return {
|
||||
valid: false,
|
||||
error: 'Invalid credentials. Please check your identifier and password.',
|
||||
};
|
||||
} else if (error.message?.includes('Invalid request')) {
|
||||
return {
|
||||
valid: false,
|
||||
error: 'Invalid request. Please check your identifier format.',
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
valid: false,
|
||||
error: `Authentication failed: ${error.message || 'Unknown error'}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
import { AtpAgent } from '@atproto/api';
|
||||
import { BlueSkyAuthType } from './auth';
|
||||
|
||||
const agentCache = new Map<string, { agent: AtpAgent; expires: number }>();
|
||||
|
||||
export async function createBlueskyAgent(auth: BlueSkyAuthType): Promise<AtpAgent> {
|
||||
const cacheKey = `${auth.identifier}:${auth.pdsHost || 'https://bsky.social'}`;
|
||||
const cached = agentCache.get(cacheKey);
|
||||
|
||||
if (cached && Date.now() < cached.expires) {
|
||||
return cached.agent;
|
||||
}
|
||||
|
||||
try {
|
||||
const agent = new AtpAgent({
|
||||
service: auth.pdsHost || 'https://bsky.social',
|
||||
});
|
||||
|
||||
await agent.login({
|
||||
identifier: auth.identifier,
|
||||
password: auth.password,
|
||||
});
|
||||
|
||||
agentCache.set(cacheKey, {
|
||||
agent,
|
||||
expires: Date.now() + 50 * 60 * 1000,
|
||||
});
|
||||
|
||||
return agent;
|
||||
} catch (error: any) {
|
||||
agentCache.delete(cacheKey);
|
||||
throw new Error(`Failed to create Bluesky agent: ${error.message || 'Unknown error'}`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getCurrentSession(auth: BlueSkyAuthType) {
|
||||
const agent = await createBlueskyAgent(auth);
|
||||
return {
|
||||
did: agent.session?.did,
|
||||
handle: agent.session?.handle,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,392 @@
|
||||
import { Property } from '@activepieces/pieces-framework';
|
||||
import { blueskyAuth } from './auth';
|
||||
|
||||
export const languageOptions = [
|
||||
{ label: 'English', value: 'en' },
|
||||
{ label: 'Spanish', value: 'es' },
|
||||
{ label: 'French', value: 'fr' },
|
||||
{ label: 'German', value: 'de' },
|
||||
{ label: 'Italian', value: 'it' },
|
||||
{ label: 'Portuguese', value: 'pt' },
|
||||
{ label: 'Russian', value: 'ru' },
|
||||
{ label: 'Japanese', value: 'ja' },
|
||||
{ label: 'Korean', value: 'ko' },
|
||||
{ label: 'Chinese (Simplified)', value: 'zh' },
|
||||
{ label: 'Chinese (Traditional)', value: 'zh-TW' },
|
||||
{ label: 'Arabic', value: 'ar' },
|
||||
{ label: 'Hindi', value: 'hi' },
|
||||
{ label: 'Dutch', value: 'nl' },
|
||||
{ label: 'Swedish', value: 'sv' },
|
||||
{ label: 'Norwegian', value: 'no' },
|
||||
{ label: 'Danish', value: 'da' },
|
||||
{ label: 'Finnish', value: 'fi' },
|
||||
{ label: 'Polish', value: 'pl' },
|
||||
{ label: 'Czech', value: 'cs' },
|
||||
{ label: 'Hungarian', value: 'hu' },
|
||||
{ label: 'Romanian', value: 'ro' },
|
||||
{ label: 'Greek', value: 'el' },
|
||||
{ label: 'Turkish', value: 'tr' },
|
||||
{ label: 'Hebrew', value: 'he' },
|
||||
{ label: 'Thai', value: 'th' },
|
||||
{ label: 'Vietnamese', value: 'vi' },
|
||||
{ label: 'Indonesian', value: 'id' },
|
||||
{ label: 'Malay', value: 'ms' },
|
||||
{ label: 'Filipino', value: 'fil' },
|
||||
];
|
||||
|
||||
|
||||
export const languageDropdown = Property.StaticDropdown({
|
||||
displayName: 'Language',
|
||||
description: 'Select the language for the post',
|
||||
required: false,
|
||||
options: {
|
||||
options: languageOptions,
|
||||
},
|
||||
});
|
||||
|
||||
export const multiLanguageDropdown = Property.StaticMultiSelectDropdown({
|
||||
displayName: 'Languages',
|
||||
description: 'Select one or more languages for the post',
|
||||
required: false,
|
||||
options: {
|
||||
options: languageOptions,
|
||||
},
|
||||
});
|
||||
|
||||
export const threadDepthDropdown = Property.StaticDropdown({
|
||||
displayName: 'Thread Depth',
|
||||
description: 'How many levels deep to retrieve replies',
|
||||
required: false,
|
||||
defaultValue: '10',
|
||||
options: {
|
||||
options: [
|
||||
{ label: '1 level', value: '1' },
|
||||
{ label: '2 levels', value: '2' },
|
||||
{ label: '3 levels', value: '3' },
|
||||
{ label: '5 levels', value: '5' },
|
||||
{ label: '10 levels', value: '10' },
|
||||
{ label: '20 levels', value: '20' },
|
||||
{ label: '50 levels', value: '50' },
|
||||
{ label: '100 levels (max)', value: '100' },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
export const parentHeightDropdown = Property.StaticDropdown({
|
||||
displayName: 'Parent Height',
|
||||
description: 'How many parent posts to retrieve',
|
||||
required: false,
|
||||
defaultValue: '3',
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'No parents', value: '0' },
|
||||
{ label: '1 parent', value: '1' },
|
||||
{ label: '2 parents', value: '2' },
|
||||
{ label: '3 parents', value: '3' },
|
||||
{ label: '5 parents', value: '5' },
|
||||
{ label: '10 parents', value: '10' },
|
||||
{ label: '20 parents', value: '20' },
|
||||
{ label: 'All parents (80 max)', value: '80' },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
export const visibilityDropdown = Property.StaticDropdown({
|
||||
displayName: 'Visibility',
|
||||
description: 'Post visibility setting',
|
||||
required: false,
|
||||
defaultValue: 'public',
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'Public', value: 'public' },
|
||||
{ label: 'Unlisted', value: 'unlisted' },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
export const moderationLabelDropdown = Property.StaticMultiSelectDropdown({
|
||||
displayName: 'Content Labels',
|
||||
description: 'Apply content moderation labels to the post',
|
||||
required: false,
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'Adult Content', value: 'adult' },
|
||||
{ label: 'Graphic Media', value: 'graphic-media' },
|
||||
{ label: 'Nudity', value: 'nudity' },
|
||||
{ label: 'Sexual', value: 'sexual' },
|
||||
{ label: 'Violence', value: 'violence' },
|
||||
{ label: 'Self Harm', value: 'self-harm' },
|
||||
{ label: 'Spam', value: 'spam' },
|
||||
{ label: 'Impersonation', value: 'impersonation' },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
export const postUrlProperty = Property.ShortText({
|
||||
displayName: 'Post URL',
|
||||
description: 'Paste the Bluesky post URL (e.g., https://bsky.app/profile/username.bsky.social/post/xxx)',
|
||||
required: true,
|
||||
});
|
||||
|
||||
export const userHandleProperty = Property.ShortText({
|
||||
displayName: 'User Handle',
|
||||
description: 'Bluesky username (e.g., username.bsky.social)',
|
||||
required: true,
|
||||
});
|
||||
|
||||
export const authorSelectionProperty = Property.Dropdown({
|
||||
displayName: 'Select Author',
|
||||
description: 'Choose from accounts you follow',
|
||||
required: false,
|
||||
auth: blueskyAuth,
|
||||
refreshers: ['auth'],
|
||||
options: async ({ auth }) => {
|
||||
try {
|
||||
const { createBlueskyAgent } = await import('./client');
|
||||
if (!auth) return { options: [] };
|
||||
const agent = await createBlueskyAgent(auth.props);
|
||||
const session = agent.session;
|
||||
|
||||
if (!session?.did) {
|
||||
return { options: [{ label: 'Please authenticate first', value: '' }] };
|
||||
}
|
||||
|
||||
const followingResponse = await agent.getFollows({
|
||||
actor: session.did,
|
||||
limit: 100
|
||||
});
|
||||
|
||||
const options = followingResponse.data.follows.map((follow: any) => ({
|
||||
label: `${follow.displayName || follow.handle} (@${follow.handle})`,
|
||||
value: follow.handle
|
||||
}));
|
||||
|
||||
options.unshift({ label: 'Enter handle manually', value: 'manual' });
|
||||
|
||||
return { options };
|
||||
} catch (error) {
|
||||
return {
|
||||
options: [{ label: 'Error loading following list', value: '' }]
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export const postSearchProperty = Property.ShortText({
|
||||
displayName: 'Search Posts',
|
||||
description: 'Search for posts by keywords, hashtags, or content',
|
||||
required: false,
|
||||
});
|
||||
|
||||
export const postTextProperty = Property.LongText({
|
||||
displayName: 'Post Text',
|
||||
description: 'What do you want to post? (Max 300 characters)',
|
||||
required: true,
|
||||
});
|
||||
|
||||
export const imageUrlsProperty = Property.Array({
|
||||
displayName: 'Image URLs',
|
||||
description: 'Add up to 4 images by URL',
|
||||
required: false,
|
||||
});
|
||||
|
||||
export const imageDescriptionsProperty = Property.Array({
|
||||
displayName: 'Image Descriptions',
|
||||
description: 'Describe each image for accessibility',
|
||||
required: false,
|
||||
});
|
||||
|
||||
export const linkUrlProperty = Property.ShortText({
|
||||
displayName: 'Link to Share',
|
||||
description: 'URL to share with your post',
|
||||
required: false,
|
||||
});
|
||||
|
||||
export const replyToPostProperty = Property.ShortText({
|
||||
displayName: 'Reply to Post',
|
||||
description: 'URL of post to reply to',
|
||||
required: false,
|
||||
});
|
||||
|
||||
export const postTypeDropdown = Property.StaticDropdown({
|
||||
displayName: 'Post Type',
|
||||
description: 'Type of content you\'re sharing',
|
||||
required: false,
|
||||
defaultValue: 'text',
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'Text Post', value: 'text' },
|
||||
{ label: 'Photo Post', value: 'photo' },
|
||||
{ label: 'Link Share', value: 'link' },
|
||||
{ label: 'Reply', value: 'reply' },
|
||||
{ label: 'Repost with Comment', value: 'quote' },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
export const simpleLanguageDropdown = Property.StaticDropdown({
|
||||
displayName: 'Post Language',
|
||||
description: 'Language of your post',
|
||||
required: false,
|
||||
defaultValue: 'en',
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'English', value: 'en' },
|
||||
{ label: 'Spanish', value: 'es' },
|
||||
{ label: 'French', value: 'fr' },
|
||||
{ label: 'German', value: 'de' },
|
||||
{ label: 'Italian', value: 'it' },
|
||||
{ label: 'Portuguese', value: 'pt' },
|
||||
{ label: 'Japanese', value: 'ja' },
|
||||
{ label: 'Korean', value: 'ko' },
|
||||
{ label: 'Chinese', value: 'zh' },
|
||||
{ label: 'Russian', value: 'ru' },
|
||||
{ label: 'Arabic', value: 'ar' },
|
||||
{ label: 'Hindi', value: 'hi' },
|
||||
{ label: 'Dutch', value: 'nl' },
|
||||
{ label: 'Swedish', value: 'sv' },
|
||||
{ label: 'Other', value: 'other' },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
export const replyDepthDropdown = Property.StaticDropdown({
|
||||
displayName: 'How many replies to show?',
|
||||
description: 'Choose how deep to go into the conversation',
|
||||
required: false,
|
||||
defaultValue: '10',
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'Just direct replies (1 level)', value: '1' },
|
||||
{ label: 'Short conversation (3 levels)', value: '3' },
|
||||
{ label: 'Full conversation (10 levels)', value: '10' },
|
||||
{ label: 'Entire thread (50+ levels)', value: '50' },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
export const parentPostsDropdown = Property.StaticDropdown({
|
||||
displayName: 'Show conversation context?',
|
||||
description: 'Include previous posts in the thread for context',
|
||||
required: false,
|
||||
defaultValue: '3',
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'No context - just this post', value: '0' },
|
||||
{ label: 'Recent context (3 previous posts)', value: '3' },
|
||||
{ label: 'Full context (10 previous posts)', value: '10' },
|
||||
{ label: 'Complete thread history', value: '80' },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
export const contentWarningDropdown = Property.StaticMultiSelectDropdown({
|
||||
displayName: 'Content Warnings',
|
||||
description: 'Add warnings for sensitive content',
|
||||
required: false,
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'Adult Content', value: 'adult' },
|
||||
{ label: 'Graphic Content', value: 'graphic-media' },
|
||||
{ label: 'Sensitive Topic', value: 'sensitive' },
|
||||
{ label: 'Violence', value: 'violence' },
|
||||
{ label: 'Spam/Promotional', value: 'spam' },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
export const audienceDropdown = Property.StaticDropdown({
|
||||
displayName: 'Audience',
|
||||
description: 'Who can see this post',
|
||||
required: false,
|
||||
defaultValue: 'public',
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'Everyone (Public)', value: 'public' },
|
||||
{ label: 'Followers only', value: 'followers' },
|
||||
{ label: 'Private/Unlisted', value: 'unlisted' },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
export function extractPostInfoFromUrl(url: string): { uri?: string; handle?: string; postId?: string } {
|
||||
if (url.startsWith('at://')) {
|
||||
return { uri: url };
|
||||
}
|
||||
|
||||
const urlMatch = url.match(/https?:\/\/bsky\.app\/profile\/([^/]+)\/post\/([^/?]+)/);
|
||||
if (urlMatch) {
|
||||
const handle = urlMatch[1];
|
||||
const postId = urlMatch[2];
|
||||
|
||||
return {
|
||||
handle: handle,
|
||||
postId: postId,
|
||||
};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
export function normalizeHandle(handle: string): string {
|
||||
let cleanHandle = handle.replace(/^@/, '');
|
||||
|
||||
if (!cleanHandle.includes('.')) {
|
||||
cleanHandle += '.bsky.social';
|
||||
}
|
||||
|
||||
return cleanHandle;
|
||||
}
|
||||
|
||||
export function createSimpleExternalLink(url: string): { uri: string; title: string; description?: string } {
|
||||
return {
|
||||
uri: url,
|
||||
title: url,
|
||||
description: 'Shared link'
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export const postUriProperty = Property.ShortText({
|
||||
displayName: 'Post URI (Advanced)',
|
||||
description: 'Technical AT-URI format (at://did:plc:xxx/app.bsky.feed.post/xxx) - use Post URL field instead',
|
||||
required: false,
|
||||
});
|
||||
|
||||
export const externalLinkProperty = Property.Object({
|
||||
displayName: 'External Link (Advanced)',
|
||||
description: 'Manual link object - use "Link to Share" field instead',
|
||||
required: false,
|
||||
});
|
||||
|
||||
export const replyToProperty = Property.Object({
|
||||
displayName: 'Reply To (Advanced)',
|
||||
description: 'Manual reply object - use "Reply to Post" field instead',
|
||||
required: false,
|
||||
});
|
||||
|
||||
|
||||
|
||||
export async function parseBlueskyUrl(url: string, agent: any): Promise<string> {
|
||||
if (url.startsWith('at://')) {
|
||||
if (!url.match(/^at:\/\/did:plc:[a-z0-9]+\/app\.bsky\.feed\.post\/[a-z0-9]+$/)) {
|
||||
throw new Error('Invalid AT-URI format');
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
const urlMatch = url.match(/https?:\/\/bsky\.app\/profile\/([^/]+)\/post\/([^/?]+)/);
|
||||
if (urlMatch) {
|
||||
const handle = urlMatch[1];
|
||||
const postId = urlMatch[2];
|
||||
|
||||
if (!handle.includes('.') && !handle.includes('@')) {
|
||||
throw new Error('Invalid handle format in URL');
|
||||
}
|
||||
|
||||
const didDoc = await agent.resolveHandle({ handle: handle.replace('@', '') });
|
||||
return `at://${didDoc.data.did}/app.bsky.feed.post/${postId}`;
|
||||
}
|
||||
|
||||
throw new Error('Invalid post URL format. Please use a valid Bluesky post URL or AT-URI.');
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
import { createTrigger, TriggerStrategy, PiecePropValueSchema, AppConnectionValueForAuthProperty } from '@activepieces/pieces-framework';
|
||||
import { DedupeStrategy, Polling, pollingHelper } from '@activepieces/pieces-common';
|
||||
import { blueskyAuth } from '../common/auth';
|
||||
import { createBlueskyAgent } from '../common/client';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const polling: Polling<AppConnectionValueForAuthProperty<typeof blueskyAuth>, Record<string, never>> = {
|
||||
strategy: DedupeStrategy.TIMEBASED,
|
||||
items: async ({ auth, lastFetchEpochMS }) => {
|
||||
try {
|
||||
const agent = await createBlueskyAgent(auth.props);
|
||||
|
||||
const session = agent.session;
|
||||
if (!session?.did) {
|
||||
throw new Error('Could not get user DID from session');
|
||||
}
|
||||
|
||||
const userDid = session.did;
|
||||
|
||||
const response = await agent.getFollowers({
|
||||
actor: userDid,
|
||||
limit: 100
|
||||
});
|
||||
|
||||
if (!response.data?.followers || !Array.isArray(response.data.followers)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const currentTime = Date.now();
|
||||
const cutoffTime = lastFetchEpochMS || 0;
|
||||
|
||||
return response.data.followers
|
||||
.filter((follower: any) => {
|
||||
const profileTime = follower.indexedAt ? dayjs(follower.indexedAt).valueOf() : currentTime;
|
||||
return profileTime > cutoffTime - (24 * 60 * 60 * 1000);
|
||||
})
|
||||
.map((follower: any) => ({
|
||||
epochMilliSeconds: follower.indexedAt ? dayjs(follower.indexedAt).valueOf() : currentTime,
|
||||
data: {
|
||||
did: follower.did,
|
||||
handle: follower.handle,
|
||||
displayName: follower.displayName || follower.handle,
|
||||
description: follower.description || '',
|
||||
avatar: follower.avatar || '',
|
||||
banner: follower.banner || '',
|
||||
followersCount: follower.followersCount || 0,
|
||||
followsCount: follower.followsCount || 0,
|
||||
postsCount: follower.postsCount || 0,
|
||||
indexedAt: follower.indexedAt || new Date().toISOString(),
|
||||
viewer: follower.viewer || {},
|
||||
labels: follower.labels || [],
|
||||
createdAt: follower.createdAt || null
|
||||
}
|
||||
}))
|
||||
.sort((a: any, b: any) => b.epochMilliSeconds - a.epochMilliSeconds);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error fetching followers:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const newFollowerOnAccount = createTrigger({
|
||||
auth: blueskyAuth,
|
||||
name: 'newFollowerOnAccount',
|
||||
displayName: 'New Follower on Account',
|
||||
description: 'Triggers when someone new follows your Bluesky account',
|
||||
props: {},
|
||||
sampleData: {
|
||||
did: 'did:plc:example123',
|
||||
handle: 'newfollower.bsky.social',
|
||||
displayName: 'New Follower',
|
||||
description: 'A new user who just followed your account',
|
||||
avatar: 'https://cdn.bsky.app/img/avatar/plain/did:plc:example123/example@jpeg',
|
||||
banner: 'https://cdn.bsky.app/img/banner/plain/did:plc:example123/example@jpeg',
|
||||
followersCount: 42,
|
||||
followsCount: 156,
|
||||
postsCount: 78,
|
||||
indexedAt: '2024-01-01T12:00:00.000Z',
|
||||
viewer: {
|
||||
muted: false,
|
||||
blockedBy: false,
|
||||
following: 'at://did:plc:example123/app.bsky.graph.follow/example456',
|
||||
followedBy: 'at://did:plc:example456/app.bsky.graph.follow/example789'
|
||||
},
|
||||
labels: [],
|
||||
createdAt: '2023-06-01T12:00:00.000Z'
|
||||
},
|
||||
type: TriggerStrategy.POLLING,
|
||||
|
||||
async test(context) {
|
||||
return await pollingHelper.test(polling, context);
|
||||
},
|
||||
|
||||
async onEnable(context) {
|
||||
const { store, auth, propsValue } = context;
|
||||
await pollingHelper.onEnable(polling, { store, auth, propsValue });
|
||||
},
|
||||
|
||||
async onDisable(context) {
|
||||
const { store, auth, propsValue } = context;
|
||||
await pollingHelper.onDisable(polling, { store, auth, propsValue });
|
||||
},
|
||||
|
||||
async run(context) {
|
||||
return await pollingHelper.poll(polling, context);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,222 @@
|
||||
import { createTrigger, TriggerStrategy, PiecePropValueSchema, Property, AppConnectionValueForAuthProperty } from '@activepieces/pieces-framework';
|
||||
import { DedupeStrategy, Polling, pollingHelper } from '@activepieces/pieces-common';
|
||||
import { blueskyAuth } from '../common/auth';
|
||||
import { createBlueskyAgent } from '../common/client';
|
||||
import { simpleLanguageDropdown } from '../common/props';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const polling: Polling<AppConnectionValueForAuthProperty<typeof blueskyAuth>, {
|
||||
searchQuery: string;
|
||||
searchLanguage?: string;
|
||||
includeImages?: boolean;
|
||||
includeVideos?: boolean;
|
||||
sortBy?: string;
|
||||
}> = {
|
||||
strategy: DedupeStrategy.TIMEBASED,
|
||||
items: async ({ auth, propsValue, lastFetchEpochMS }) => {
|
||||
const { searchQuery, searchLanguage, includeImages, includeVideos, sortBy } = propsValue;
|
||||
|
||||
try {
|
||||
if (!searchQuery || searchQuery.trim().length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const agent = await createBlueskyAgent(auth.props);
|
||||
|
||||
const searchParams: any = {
|
||||
q: searchQuery.trim(),
|
||||
limit: 50,
|
||||
sort: sortBy || 'latest'
|
||||
};
|
||||
|
||||
if (searchLanguage && searchLanguage !== 'other') {
|
||||
searchParams.lang = searchLanguage;
|
||||
}
|
||||
|
||||
const response = await agent.api.app.bsky.feed.searchPosts(searchParams);
|
||||
|
||||
if (!response.data?.posts || !Array.isArray(response.data.posts)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const cutoffTime = lastFetchEpochMS || 0;
|
||||
|
||||
return response.data.posts
|
||||
.filter((post: any) => {
|
||||
if (!post.indexedAt) return false;
|
||||
|
||||
const postTime = dayjs(post.indexedAt).valueOf();
|
||||
if (postTime <= cutoffTime) return false;
|
||||
|
||||
if (includeImages === false && post.embed?.images) return false;
|
||||
if (includeVideos === false && post.embed?.video) return false;
|
||||
if (includeImages === true && !post.embed?.images) return false;
|
||||
if (includeVideos === true && !post.embed?.video) return false;
|
||||
|
||||
return true;
|
||||
})
|
||||
.map((post: any) => ({
|
||||
epochMilliSeconds: dayjs(post.indexedAt).valueOf(),
|
||||
data: {
|
||||
uri: post.uri,
|
||||
cid: post.cid,
|
||||
author: post.author,
|
||||
record: post.record,
|
||||
indexedAt: post.indexedAt,
|
||||
|
||||
replyCount: post.replyCount || 0,
|
||||
repostCount: post.repostCount || 0,
|
||||
likeCount: post.likeCount || 0,
|
||||
quoteCount: post.quoteCount || 0,
|
||||
|
||||
labels: post.labels || [],
|
||||
viewer: post.viewer || {},
|
||||
embed: post.embed || null,
|
||||
|
||||
searchContext: {
|
||||
query: searchQuery,
|
||||
language: searchLanguage || null,
|
||||
matchedTerms: extractMatchedTerms(post.record?.text || '', searchQuery),
|
||||
hasImages: !!(post.embed?.images),
|
||||
hasVideo: !!(post.embed?.video),
|
||||
hasExternalLink: !!(post.embed?.external)
|
||||
}
|
||||
}
|
||||
}))
|
||||
.sort((a: any, b: any) => b.epochMilliSeconds - a.epochMilliSeconds);
|
||||
|
||||
} catch (error) {
|
||||
console.warn('Failed to search posts:', error instanceof Error ? error.message : 'Unknown error');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function extractMatchedTerms(text: string, query: string): string[] {
|
||||
if (!text || !query) return [];
|
||||
|
||||
const cleanQuery = query.toLowerCase()
|
||||
.replace(/["']/g, '')
|
||||
.replace(/\s+(or|and)\s+/gi, ' ');
|
||||
|
||||
const terms = cleanQuery.split(/\s+/).filter(term => term.length > 0 && !['or', 'and'].includes(term.toLowerCase()));
|
||||
const matchedTerms: string[] = [];
|
||||
const lowerText = text.toLowerCase();
|
||||
|
||||
terms.forEach(term => {
|
||||
if (term.startsWith('#') || term.startsWith('@')) {
|
||||
if (lowerText.includes(term)) {
|
||||
matchedTerms.push(term);
|
||||
}
|
||||
} else if (lowerText.includes(term)) {
|
||||
matchedTerms.push(term);
|
||||
}
|
||||
});
|
||||
|
||||
return [...new Set(matchedTerms)];
|
||||
}
|
||||
|
||||
export const newPost = createTrigger({
|
||||
auth: blueskyAuth,
|
||||
name: 'newPost',
|
||||
displayName: 'New Post (with Search Options)',
|
||||
description: 'Triggers when posts match your search criteria',
|
||||
props: {
|
||||
searchQuery: Property.ShortText({
|
||||
displayName: 'Search Query',
|
||||
description: 'Keywords, hashtags (#example), or mentions (@handle) to find',
|
||||
required: true
|
||||
}),
|
||||
searchLanguage: {
|
||||
...simpleLanguageDropdown,
|
||||
displayName: 'Language Filter',
|
||||
description: 'Filter by language',
|
||||
required: false
|
||||
},
|
||||
includeImages: Property.Checkbox({
|
||||
displayName: 'Filter by Images',
|
||||
description: 'Only posts with/without images',
|
||||
required: false,
|
||||
defaultValue: undefined
|
||||
}),
|
||||
includeVideos: Property.Checkbox({
|
||||
displayName: 'Filter by Videos',
|
||||
description: 'Only posts with/without videos',
|
||||
required: false,
|
||||
defaultValue: undefined
|
||||
}),
|
||||
sortBy: Property.StaticDropdown({
|
||||
displayName: 'Sort Order',
|
||||
description: 'How to sort results',
|
||||
required: false,
|
||||
defaultValue: 'latest',
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'Latest First', value: 'latest' },
|
||||
{ label: 'Most Popular', value: 'top' }
|
||||
]
|
||||
}
|
||||
})
|
||||
},
|
||||
sampleData: {
|
||||
uri: 'at://did:plc:example123/app.bsky.feed.post/example456',
|
||||
cid: 'bafyreib2rxk3vcfbqij7y6kzgy4knknc7ff4t5jn2m5fbn6jdl7czfqyqe',
|
||||
author: {
|
||||
did: 'did:plc:example123',
|
||||
handle: 'searchauthor.bsky.social',
|
||||
displayName: 'Search Result Author',
|
||||
avatar: 'https://cdn.bsky.app/img/avatar/plain/did:plc:example123/example@jpeg',
|
||||
viewer: {
|
||||
muted: false,
|
||||
blockedBy: false
|
||||
}
|
||||
},
|
||||
record: {
|
||||
$type: 'app.bsky.feed.post',
|
||||
createdAt: '2024-01-01T12:00:00.000Z',
|
||||
text: 'This post matches your search criteria! #automation #activepieces',
|
||||
langs: ['en']
|
||||
},
|
||||
indexedAt: '2024-01-01T12:00:00.000Z',
|
||||
|
||||
replyCount: 2,
|
||||
repostCount: 5,
|
||||
likeCount: 12,
|
||||
quoteCount: 1,
|
||||
|
||||
labels: [],
|
||||
viewer: {
|
||||
repost: null,
|
||||
like: null
|
||||
},
|
||||
embed: null,
|
||||
|
||||
searchContext: {
|
||||
query: 'automation',
|
||||
language: 'en',
|
||||
matchedTerms: ['automation'],
|
||||
hasImages: false,
|
||||
hasVideo: false,
|
||||
hasExternalLink: false
|
||||
}
|
||||
},
|
||||
type: TriggerStrategy.POLLING,
|
||||
|
||||
async test(context) {
|
||||
return await pollingHelper.test(polling, context);
|
||||
},
|
||||
|
||||
async onEnable(context) {
|
||||
const { store, auth, propsValue } = context;
|
||||
await pollingHelper.onEnable(polling, { store, auth, propsValue });
|
||||
},
|
||||
|
||||
async onDisable(context) {
|
||||
const { store, auth, propsValue } = context;
|
||||
await pollingHelper.onDisable(polling, { store, auth, propsValue });
|
||||
},
|
||||
|
||||
async run(context) {
|
||||
return await pollingHelper.poll(polling, context);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,249 @@
|
||||
import { createTrigger, TriggerStrategy, PiecePropValueSchema, Property, AppConnectionValueForAuthProperty } from '@activepieces/pieces-framework';
|
||||
import { DedupeStrategy, Polling, pollingHelper } from '@activepieces/pieces-common';
|
||||
import { blueskyAuth, BlueSkyAuthType } from '../common/auth';
|
||||
import { createBlueskyAgent } from '../common/client';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const polling: Polling<AppConnectionValueForAuthProperty<typeof blueskyAuth>, {
|
||||
authorSelection: string;
|
||||
authorFromFollowing?: string;
|
||||
authorHandle?: string;
|
||||
includeReplies?: boolean;
|
||||
includeReposts?: boolean;
|
||||
}> = {
|
||||
strategy: DedupeStrategy.TIMEBASED,
|
||||
items: async ({ auth, propsValue, lastFetchEpochMS }) => {
|
||||
const { authorSelection, authorFromFollowing, authorHandle, includeReplies = false, includeReposts = false } = propsValue;
|
||||
|
||||
let selectedAuthorHandle: string;
|
||||
if (authorSelection === 'following') {
|
||||
if (!authorFromFollowing) {
|
||||
return [];
|
||||
}
|
||||
selectedAuthorHandle = authorFromFollowing;
|
||||
} else if (authorSelection === 'manual') {
|
||||
if (!authorHandle) {
|
||||
return [];
|
||||
}
|
||||
selectedAuthorHandle = authorHandle;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
if (!selectedAuthorHandle || selectedAuthorHandle.trim().length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const agent = await createBlueskyAgent(auth.props);
|
||||
|
||||
const normalizedHandle = selectedAuthorHandle.replace('@', '').trim();
|
||||
|
||||
const response = await agent.getAuthorFeed({
|
||||
actor: normalizedHandle,
|
||||
limit: 50,
|
||||
filter: 'posts_with_replies'
|
||||
});
|
||||
|
||||
if (!response.data?.feed || !Array.isArray(response.data.feed)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const cutoffTime = lastFetchEpochMS || 0;
|
||||
|
||||
return response.data.feed
|
||||
.filter((feedItem: any) => {
|
||||
const post = feedItem.post;
|
||||
if (!post || !post.indexedAt) return false;
|
||||
|
||||
const postTime = dayjs(post.indexedAt).valueOf();
|
||||
if (postTime <= cutoffTime) return false;
|
||||
|
||||
const isReply = post.record?.reply !== undefined;
|
||||
const isRepost = feedItem.reason?.$type === 'app.bsky.feed.defs#reasonRepost';
|
||||
|
||||
if (isReply && !includeReplies) return false;
|
||||
if (isRepost && !includeReposts) return false;
|
||||
|
||||
return post.author.handle === normalizedHandle || post.author.did === normalizedHandle;
|
||||
})
|
||||
.map((feedItem: any) => {
|
||||
const post = feedItem.post;
|
||||
return {
|
||||
epochMilliSeconds: dayjs(post.indexedAt).valueOf(),
|
||||
data: {
|
||||
uri: post.uri,
|
||||
cid: post.cid,
|
||||
author: post.author,
|
||||
record: post.record,
|
||||
indexedAt: post.indexedAt,
|
||||
|
||||
replyCount: post.replyCount || 0,
|
||||
repostCount: post.repostCount || 0,
|
||||
likeCount: post.likeCount || 0,
|
||||
quoteCount: post.quoteCount || 0,
|
||||
|
||||
labels: post.labels || [],
|
||||
viewer: post.viewer || {},
|
||||
embed: post.embed || null,
|
||||
|
||||
postContext: {
|
||||
authorHandle: normalizedHandle,
|
||||
isReply: post.record?.reply !== undefined,
|
||||
isRepost: feedItem.reason?.$type === 'app.bsky.feed.defs#reasonRepost',
|
||||
replyTo: post.record?.reply?.parent?.uri || null,
|
||||
hasImages: !!(post.embed?.images),
|
||||
hasVideo: !!(post.embed?.video),
|
||||
hasExternalLink: !!(post.embed?.external)
|
||||
}
|
||||
}
|
||||
};
|
||||
})
|
||||
.sort((a: any, b: any) => b.epochMilliSeconds - a.epochMilliSeconds);
|
||||
|
||||
} catch (error) {
|
||||
console.warn('Failed to fetch author posts:', error instanceof Error ? error.message : 'Unknown error');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const newPostsByAuthor = createTrigger({
|
||||
auth: blueskyAuth,
|
||||
name: 'newPostsByAuthor',
|
||||
displayName: 'New Posts by Author',
|
||||
description: 'Triggers when a selected author creates a new post',
|
||||
props: {
|
||||
authorSelection: Property.StaticDropdown({
|
||||
displayName: 'How to select author?',
|
||||
description: 'Choose how to select the author',
|
||||
required: true,
|
||||
defaultValue: 'following',
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'From my following list', value: 'following' },
|
||||
{ label: 'Enter handle manually', value: 'manual' },
|
||||
],
|
||||
},
|
||||
}),
|
||||
|
||||
authorFromFollowing: Property.Dropdown({
|
||||
auth: blueskyAuth,
|
||||
displayName: 'Select Author',
|
||||
description: 'Choose from accounts you follow',
|
||||
required: false,
|
||||
refreshers: ['auth'],
|
||||
options: async ({ auth }) => {
|
||||
try {
|
||||
if (!auth) return { options: [] };
|
||||
const agent = await createBlueskyAgent(auth.props);
|
||||
const session = agent.session;
|
||||
|
||||
if (!session?.did) {
|
||||
return { options: [{ label: 'Please authenticate first', value: '' }] };
|
||||
}
|
||||
|
||||
const followingResponse = await agent.getFollows({
|
||||
actor: session.did,
|
||||
limit: 100
|
||||
});
|
||||
|
||||
return {
|
||||
options: followingResponse.data.follows.map(follow => ({
|
||||
label: `${follow.displayName || follow.handle} (@${follow.handle})`,
|
||||
value: follow.handle
|
||||
}))
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
options: [{ label: 'Error loading following list', value: '' }]
|
||||
};
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
authorHandle: Property.ShortText({
|
||||
displayName: 'Author Handle',
|
||||
description: 'Enter the Bluesky username (e.g., username.bsky.social)',
|
||||
required: false
|
||||
}),
|
||||
includeReplies: Property.Checkbox({
|
||||
displayName: 'Include Replies',
|
||||
description: 'Include reply posts by this author',
|
||||
required: false,
|
||||
defaultValue: false
|
||||
}),
|
||||
includeReposts: Property.Checkbox({
|
||||
displayName: 'Include Reposts',
|
||||
description: 'Include posts that this author reposted',
|
||||
required: false,
|
||||
defaultValue: false
|
||||
})
|
||||
},
|
||||
sampleData: {
|
||||
uri: 'at://did:plc:example123/app.bsky.feed.post/example456',
|
||||
cid: 'bafyreib2rxk3vcfbqij7y6kzgy4knknc7ff4t5jn2m5fbn6jdl7czfqyqe',
|
||||
author: {
|
||||
did: 'did:plc:example123',
|
||||
handle: 'author.bsky.social',
|
||||
displayName: 'Example Author',
|
||||
avatar: 'https://cdn.bsky.app/img/avatar/plain/did:plc:example123/example@jpeg',
|
||||
followersCount: 1500,
|
||||
followsCount: 300,
|
||||
postsCount: 250,
|
||||
viewer: {
|
||||
muted: false,
|
||||
blockedBy: false,
|
||||
following: 'at://following-record-uri'
|
||||
}
|
||||
},
|
||||
record: {
|
||||
$type: 'app.bsky.feed.post',
|
||||
createdAt: '2024-01-01T12:00:00.000Z',
|
||||
text: 'Just posted something new! Excited to share this with everyone.',
|
||||
langs: ['en']
|
||||
},
|
||||
indexedAt: '2024-01-01T12:00:00.000Z',
|
||||
|
||||
replyCount: 3,
|
||||
repostCount: 8,
|
||||
likeCount: 25,
|
||||
quoteCount: 2,
|
||||
|
||||
labels: [],
|
||||
viewer: {
|
||||
repost: null,
|
||||
like: null
|
||||
},
|
||||
embed: null,
|
||||
|
||||
postContext: {
|
||||
authorHandle: 'author.bsky.social',
|
||||
isReply: false,
|
||||
isRepost: false,
|
||||
replyTo: null,
|
||||
hasImages: false,
|
||||
hasVideo: false,
|
||||
hasExternalLink: false
|
||||
}
|
||||
},
|
||||
type: TriggerStrategy.POLLING,
|
||||
|
||||
async test(context) {
|
||||
return await pollingHelper.test(polling, context);
|
||||
},
|
||||
|
||||
async onEnable(context) {
|
||||
const { store, auth, propsValue } = context;
|
||||
await pollingHelper.onEnable(polling, { store, auth, propsValue });
|
||||
},
|
||||
|
||||
async onDisable(context) {
|
||||
const { store, auth, propsValue } = context;
|
||||
await pollingHelper.onDisable(polling, { store, auth, propsValue });
|
||||
},
|
||||
|
||||
async run(context) {
|
||||
return await pollingHelper.poll(polling, context);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,138 @@
|
||||
import { createTrigger, TriggerStrategy, PiecePropValueSchema, AppConnectionValueForAuthProperty } from '@activepieces/pieces-framework';
|
||||
import { DedupeStrategy, Polling, pollingHelper } from '@activepieces/pieces-common';
|
||||
import { blueskyAuth } from '../common/auth';
|
||||
import { createBlueskyAgent } from '../common/client';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const polling: Polling<AppConnectionValueForAuthProperty<typeof blueskyAuth>, Record<string, never>> = {
|
||||
strategy: DedupeStrategy.TIMEBASED,
|
||||
items: async ({ auth, lastFetchEpochMS }) => {
|
||||
try {
|
||||
const agent = await createBlueskyAgent(auth.props);
|
||||
|
||||
const response = await agent.getTimeline({
|
||||
limit: 50
|
||||
});
|
||||
|
||||
if (!response.data?.feed || !Array.isArray(response.data.feed)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const cutoffTime = lastFetchEpochMS || 0;
|
||||
|
||||
return response.data.feed
|
||||
.filter((item: any) => {
|
||||
if (!item.post || !item.post.indexedAt) return false;
|
||||
|
||||
const postTime = dayjs(item.post.indexedAt).valueOf();
|
||||
return postTime > cutoffTime;
|
||||
})
|
||||
.map((item: any) => ({
|
||||
epochMilliSeconds: dayjs(item.post.indexedAt).valueOf(),
|
||||
data: {
|
||||
uri: item.post.uri,
|
||||
cid: item.post.cid,
|
||||
author: item.post.author,
|
||||
record: item.post.record,
|
||||
indexedAt: item.post.indexedAt,
|
||||
|
||||
replyCount: item.post.replyCount || 0,
|
||||
repostCount: item.post.repostCount || 0,
|
||||
likeCount: item.post.likeCount || 0,
|
||||
quoteCount: item.post.quoteCount || 0,
|
||||
|
||||
labels: item.post.labels || [],
|
||||
viewer: item.post.viewer || {},
|
||||
embed: item.post.embed || null,
|
||||
|
||||
reason: item.reason || null,
|
||||
reply: item.reply || null,
|
||||
|
||||
feedContext: {
|
||||
isRepost: !!item.reason,
|
||||
repostBy: item.reason?.by || null,
|
||||
isReply: !!item.reply,
|
||||
replyToPost: item.reply?.parent || null,
|
||||
replyToRoot: item.reply?.root || null
|
||||
}
|
||||
}
|
||||
}))
|
||||
.sort((a: any, b: any) => b.epochMilliSeconds - a.epochMilliSeconds);
|
||||
|
||||
} catch (error) {
|
||||
console.warn('Failed to fetch timeline posts:', error instanceof Error ? error.message : 'Unknown error');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const newTimelinePosts = createTrigger({
|
||||
auth: blueskyAuth,
|
||||
name: 'newTimelinePosts',
|
||||
displayName: 'New Timeline Posts',
|
||||
description: 'Triggers when new posts appear in your timeline',
|
||||
props: {},
|
||||
sampleData: {
|
||||
uri: 'at://did:plc:example123/app.bsky.feed.post/example456',
|
||||
cid: 'bafyreib2rxk3vcfbqij7y6kzgy4knknc7ff4t5jn2m5fbn6jdl7czfqyqe',
|
||||
author: {
|
||||
did: 'did:plc:example123',
|
||||
handle: 'author.bsky.social',
|
||||
displayName: 'Example Author',
|
||||
avatar: 'https://cdn.bsky.app/img/avatar/plain/did:plc:example123/example@jpeg',
|
||||
viewer: {
|
||||
muted: false,
|
||||
blockedBy: false,
|
||||
following: 'at://did:plc:user456/app.bsky.graph.follow/example789'
|
||||
}
|
||||
},
|
||||
record: {
|
||||
$type: 'app.bsky.feed.post',
|
||||
createdAt: '2024-01-01T12:00:00.000Z',
|
||||
text: 'This is a new post in your timeline!',
|
||||
langs: ['en']
|
||||
},
|
||||
indexedAt: '2024-01-01T12:00:00.000Z',
|
||||
|
||||
replyCount: 3,
|
||||
repostCount: 8,
|
||||
likeCount: 15,
|
||||
quoteCount: 2,
|
||||
|
||||
labels: [],
|
||||
viewer: {
|
||||
repost: null,
|
||||
like: null
|
||||
},
|
||||
embed: null,
|
||||
|
||||
reason: null,
|
||||
reply: null,
|
||||
feedContext: {
|
||||
isRepost: false,
|
||||
repostBy: null,
|
||||
isReply: false,
|
||||
replyToPost: null,
|
||||
replyToRoot: null
|
||||
}
|
||||
},
|
||||
type: TriggerStrategy.POLLING,
|
||||
|
||||
async test(context) {
|
||||
return await pollingHelper.test(polling, context);
|
||||
},
|
||||
|
||||
async onEnable(context) {
|
||||
const { store, auth, propsValue } = context;
|
||||
await pollingHelper.onEnable(polling, { store, auth, propsValue });
|
||||
},
|
||||
|
||||
async onDisable(context) {
|
||||
const { store, auth, propsValue } = context;
|
||||
await pollingHelper.onDisable(polling, { store, auth, propsValue });
|
||||
},
|
||||
|
||||
async run(context) {
|
||||
return await pollingHelper.poll(polling, context);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"extends": "../../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"importHelpers": true,
|
||||
"noImplicitOverride": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noPropertyAccessFromIndexSignature": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../../dist/out-tsc",
|
||||
"declaration": true,
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
Reference in New Issue
Block a user