UNPKG

n8n-nodes-bluesky

Version:

⚠️ PROOF OF CONCEPT - Bluesky nodes for n8n (unmaintained, use at own risk)

243 lines (242 loc) 10.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BlueskySearch = void 0; class BlueskySearch { constructor() { this.description = { displayName: 'Bluesky Search', name: 'blueskySearch', group: ['transform'], version: 1, description: 'Search Bluesky posts using the AT Protocol', defaults: { name: 'Bluesky Search' }, icon: 'file:bluesky.svg', credentials: [ { name: 'blueskyApi', required: true }, ], inputs: ['main'], outputs: ['main'], properties: [ { displayName: 'Query', name: 'query', type: 'string', default: '', placeholder: 'search terms', description: 'Search query (supports Lucene syntax)', required: true, }, { displayName: 'Limit', name: 'limit', type: 'number', typeOptions: { minValue: 1, maxValue: 100 }, default: 25, description: 'Maximum number of results to return', }, { displayName: 'Sort', name: 'sort', type: 'options', options: [ { name: 'Latest', value: 'latest' }, { name: 'Top', value: 'top' }, ], default: 'latest', description: 'Sort order for results', }, { displayName: 'Additional Filters', name: 'additionalFilters', type: 'collection', placeholder: 'Add Filter', default: {}, options: [ { displayName: 'Author', name: 'author', type: 'string', default: '', placeholder: 'handle.bsky.social', description: 'Filter to posts by specific author', }, { displayName: 'Domain', name: 'domain', type: 'string', default: '', placeholder: 'example.com', description: 'Filter to posts linking to domain', }, { displayName: 'Language', name: 'lang', type: 'string', default: '', placeholder: 'en', description: 'Filter by language code', }, { displayName: 'Since', name: 'since', type: 'string', default: '', placeholder: '2025-01-01T00:00:00Z', description: 'Posts after this datetime (ISO 8601)', }, { displayName: 'Until', name: 'until', type: 'string', default: '', placeholder: '2025-12-31T23:59:59Z', description: 'Posts before this datetime (ISO 8601)', }, { displayName: 'Mentions', name: 'mentions', type: 'string', default: '', placeholder: 'handle.bsky.social', description: 'Filter by mentioned account', }, { displayName: 'Tags', name: 'tag', type: 'string', default: '', placeholder: 'hashtag1,hashtag2', description: 'Comma-separated hashtags (without #)', }, ], }, { displayName: 'Return Format', name: 'returnFormat', type: 'options', options: [ { name: 'Individual Posts', value: 'items' }, { name: 'Summary', value: 'summary' }, ], default: 'items', description: 'How to format the output', }, ], }; } async execute() { var _a, _b, _c, _d, _e, _f, _g; const returnData = []; // Get credentials const creds = await this.getCredentials('blueskyApi'); const identifier = String(creds.identifier); const password = String(creds.password); const host = creds.host || 'https://bsky.social'; // Get node parameters const query = this.getNodeParameter('query', 0); const limit = this.getNodeParameter('limit', 0); const sort = this.getNodeParameter('sort', 0); const additionalFilters = this.getNodeParameter('additionalFilters', 0); const returnFormat = this.getNodeParameter('returnFormat', 0); if (!query.trim()) { throw new Error('Search query is required.'); } // 1) Create session (Bearer token) const session = await this.helpers.httpRequest({ method: 'POST', url: `${host}/xrpc/com.atproto.server.createSession`, body: { identifier, password }, json: true, }); const token = session === null || session === void 0 ? void 0 : session.accessJwt; if (!token) throw new Error('Failed to obtain Bluesky access token'); // 2) Build query params for searchPosts const queryParams = { q: query, limit, sort, }; // Add optional filters if (additionalFilters.author) queryParams.author = additionalFilters.author; if (additionalFilters.domain) queryParams.domain = additionalFilters.domain; if (additionalFilters.lang) queryParams.lang = additionalFilters.lang; if (additionalFilters.since) queryParams.since = additionalFilters.since; if (additionalFilters.until) queryParams.until = additionalFilters.until; if (additionalFilters.mentions) queryParams.mentions = additionalFilters.mentions; if (additionalFilters.tag) { // Convert comma-separated tags to array queryParams.tag = additionalFilters.tag.split(',').map((t) => t.trim()); } // 3) Call searchPosts const res = await this.helpers.httpRequest({ method: 'GET', url: `${host}/xrpc/app.bsky.feed.searchPosts`, qs: queryParams, headers: { Authorization: `Bearer ${token}` }, json: true, }); const posts = (res === null || res === void 0 ? void 0 : res.posts) || []; const hitsTotal = (res === null || res === void 0 ? void 0 : res.hitsTotal) || posts.length; const cursor = (res === null || res === void 0 ? void 0 : res.cursor) || null; // 4) Format output if (returnFormat === 'summary') { // Return summary returnData.push({ json: { query, hits_total: hitsTotal, returned: posts.length, cursor, posts: posts.map((p) => { var _a, _b, _c; return ({ uri: p.uri, author: ((_a = p.author) === null || _a === void 0 ? void 0 : _a.handle) || 'unknown', text: ((_b = p.record) === null || _b === void 0 ? void 0 : _b.text) || '', indexedAt: p.indexedAt, createdAt: ((_c = p.record) === null || _c === void 0 ? void 0 : _c.createdAt) || '', }); }), }, }); } else { // Return individual posts as items for (const p of posts) { const postId = p.uri.split('/').pop(); const handle = ((_a = p.author) === null || _a === void 0 ? void 0 : _a.handle) || 'unknown'; const webLink = `https://bsky.app/profile/${handle}/post/${postId}`; returnData.push({ json: { uri: p.uri, cid: p.cid, author: { did: (_b = p.author) === null || _b === void 0 ? void 0 : _b.did, handle: (_c = p.author) === null || _c === void 0 ? void 0 : _c.handle, displayName: (_d = p.author) === null || _d === void 0 ? void 0 : _d.displayName, avatar: (_e = p.author) === null || _e === void 0 ? void 0 : _e.avatar, }, text: ((_f = p.record) === null || _f === void 0 ? void 0 : _f.text) || '', createdAt: (_g = p.record) === null || _g === void 0 ? void 0 : _g.createdAt, indexedAt: p.indexedAt, replyCount: p.replyCount || 0, repostCount: p.repostCount || 0, likeCount: p.likeCount || 0, quoteCount: p.quoteCount || 0, webLink, rawPost: p, }, }); } } return [returnData]; } } exports.BlueskySearch = BlueskySearch;