@memberjunction/actions-bizapps-social
Version:
Social Media Actions for MemberJunction - Twitter, LinkedIn, Facebook, Instagram, TikTok, YouTube, HootSuite, Buffer
293 lines • 11.8 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.LinkedInSearchPostsAction = void 0;
const global_1 = require("@memberjunction/global");
const linkedin_base_action_1 = require("../linkedin-base.action");
const core_1 = require("@memberjunction/core");
const actions_1 = require("@memberjunction/actions");
/**
* Action to search for historical posts on LinkedIn
*/
let LinkedInSearchPostsAction = class LinkedInSearchPostsAction extends linkedin_base_action_1.LinkedInBaseAction {
/**
* Search for posts on LinkedIn with various filters
*/
async InternalRunAction(params) {
const { Params, ContextUser } = params;
try {
// Initialize OAuth
const companyIntegrationId = this.getParamValue(Params, 'CompanyIntegrationID');
if (!await this.initializeOAuth(companyIntegrationId)) {
throw new Error('Failed to initialize OAuth connection');
}
// Extract parameters
const query = this.getParamValue(Params, 'Query');
const hashtags = this.getParamValue(Params, 'Hashtags');
const startDate = this.getParamValue(Params, 'StartDate');
const endDate = this.getParamValue(Params, 'EndDate');
const authorType = this.getParamValue(Params, 'AuthorType') || 'all'; // 'personal', 'organization', 'all'
const organizationId = this.getParamValue(Params, 'OrganizationID');
const limit = this.getParamValue(Params, 'Limit') || 100;
const offset = this.getParamValue(Params, 'Offset') || 0;
const includeAnalytics = this.getParamValue(Params, 'IncludeAnalytics') === true;
// Build search parameters
const searchParams = {
query: query,
hashtags: hashtags,
startDate: startDate ? new Date(startDate) : undefined,
endDate: endDate ? new Date(endDate) : undefined,
limit: limit,
offset: offset
};
// Perform the search
const posts = await this.searchPostsImplementation(searchParams, authorType, organizationId, includeAnalytics);
(0, core_1.LogStatus)(`Found ${posts.length} posts matching search criteria`);
// Update output parameters
const outputParams = [...Params];
const postsParam = outputParams.find(p => p.Name === 'Posts');
if (postsParam)
postsParam.Value = posts;
const totalCountParam = outputParams.find(p => p.Name === 'TotalCount');
if (totalCountParam)
totalCountParam.Value = posts.length;
return {
Success: true,
ResultCode: 'SUCCESS',
Message: `Successfully found ${posts.length} posts`,
Params: outputParams
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
return {
Success: false,
ResultCode: 'ERROR',
Message: `Failed to search posts: ${errorMessage}`,
Params
};
}
}
/**
* Implementation of post search
*/
async searchPostsImplementation(params, authorType, organizationId, includeAnalytics = false) {
const allPosts = [];
const authorsToSearch = [];
// Determine which authors to search
if (authorType === 'personal' || authorType === 'all') {
const userUrn = await this.getCurrentUserUrn();
authorsToSearch.push(userUrn);
}
if (authorType === 'organization' || authorType === 'all') {
if (organizationId) {
authorsToSearch.push(`urn:li:organization:${organizationId}`);
}
else {
// Get all admin organizations
const orgs = await this.getAdminOrganizations();
authorsToSearch.push(...orgs.map(org => org.urn));
}
}
// Search posts for each author
for (const authorUrn of authorsToSearch) {
(0, core_1.LogStatus)(`Searching posts for author: ${authorUrn}`);
// Get all posts for the author (LinkedIn doesn't support direct content search)
const shares = await this.getAllSharesForAuthor(authorUrn, params.limit || 100);
// Filter posts based on search criteria
for (const share of shares) {
if (this.matchesSearchCriteria(share, params)) {
const post = this.normalizePost(share);
// Optionally fetch analytics
if (includeAnalytics && authorUrn.includes('organization')) {
try {
const analytics = await this.getPostAnalyticsForSearch(share.id, authorUrn);
if (analytics) {
post.analytics = this.normalizeAnalytics(analytics);
}
}
catch (error) {
(0, core_1.LogError)(`Failed to get analytics for post ${share.id}: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
allPosts.push(post);
}
}
}
// Sort by date (newest first)
allPosts.sort((a, b) => b.publishedAt.getTime() - a.publishedAt.getTime());
// Apply offset and limit
const startIndex = params.offset || 0;
const endIndex = startIndex + (params.limit || 100);
return allPosts.slice(startIndex, endIndex);
}
/**
* Get all shares for an author with pagination
*/
async getAllSharesForAuthor(authorUrn, maxResults) {
const allShares = [];
let start = 0;
const count = 50; // LinkedIn's typical page size
while (allShares.length < maxResults) {
try {
const shares = await this.getShares(authorUrn, count, start);
if (shares.length === 0) {
break; // No more results
}
allShares.push(...shares);
start += count;
// Check if we've hit the API limit
if (shares.length < count) {
break; // Last page
}
}
catch (error) {
(0, core_1.LogError)(`Error fetching shares at offset ${start}: ${error instanceof Error ? error.message : 'Unknown error'}`);
break;
}
}
return allShares.slice(0, maxResults);
}
/**
* Check if a share matches the search criteria
*/
matchesSearchCriteria(share, params) {
const content = share.specificContent?.['com.linkedin.ugc.ShareContent']?.shareCommentary?.text || '';
const publishedDate = new Date(share.firstPublishedAt || share.created.time);
// Check date range
if (params.startDate && publishedDate < params.startDate) {
return false;
}
if (params.endDate && publishedDate > params.endDate) {
return false;
}
// Check query (case-insensitive)
if (params.query) {
const query = params.query.toLowerCase();
if (!content.toLowerCase().includes(query)) {
return false;
}
}
// Check hashtags
if (params.hashtags && params.hashtags.length > 0) {
const contentLower = content.toLowerCase();
const hasAllHashtags = params.hashtags.every(hashtag => {
const normalizedHashtag = hashtag.toLowerCase().startsWith('#')
? hashtag.toLowerCase()
: `#${hashtag.toLowerCase()}`;
return contentLower.includes(normalizedHashtag);
});
if (!hasAllHashtags) {
return false;
}
}
return true;
}
/**
* Get analytics for a post during search
*/
async getPostAnalyticsForSearch(shareId, organizationUrn) {
try {
const response = await this.axiosInstance.get('/organizationalEntityShareStatistics', {
params: {
q: 'organizationalEntity',
organizationalEntity: organizationUrn,
shares: `List(${shareId})`
}
});
if (response.data.elements && response.data.elements.length > 0) {
return response.data.elements[0];
}
return null;
}
catch (error) {
// Silently fail for individual analytics requests during search
return null;
}
}
/**
* Override base searchPosts method
*/
async searchPosts(params) {
return this.searchPostsImplementation(params, 'all');
}
/**
* Define the parameters this action expects
*/
get Params() {
return [
...this.commonSocialParams,
{
Name: 'Query',
Type: 'Input',
Value: null
},
{
Name: 'Hashtags',
Type: 'Input',
Value: null // Array of hashtags
},
{
Name: 'StartDate',
Type: 'Input',
Value: null
},
{
Name: 'EndDate',
Type: 'Input',
Value: null
},
{
Name: 'AuthorType',
Type: 'Input',
Value: 'all' // 'personal', 'organization', 'all'
},
{
Name: 'OrganizationID',
Type: 'Input',
Value: null
},
{
Name: 'Limit',
Type: 'Input',
Value: 100
},
{
Name: 'Offset',
Type: 'Input',
Value: 0
},
{
Name: 'IncludeAnalytics',
Type: 'Input',
Value: false
},
{
Name: 'Posts',
Type: 'Output',
Value: null
},
{
Name: 'TotalCount',
Type: 'Output',
Value: null
}
];
}
/**
* Get action description
*/
get Description() {
return 'Searches for historical LinkedIn posts with support for content search, hashtags, date ranges, and author filtering. Retrieves posts from personal profiles and organization pages.';
}
};
exports.LinkedInSearchPostsAction = LinkedInSearchPostsAction;
exports.LinkedInSearchPostsAction = LinkedInSearchPostsAction = __decorate([
(0, global_1.RegisterClass)(actions_1.BaseAction, 'LinkedInSearchPostsAction')
], LinkedInSearchPostsAction);
//# sourceMappingURL=search-posts.action.js.map