Skip to main content
Glama

mcp-google-sheets

index.ts6.73 kB
import { ApFile, Property } from '@activepieces/pieces-framework'; import { HttpMethod, httpClient, AuthenticationType, } from '@activepieces/pieces-common'; import FormData from 'form-data'; export const santizeText = (text: string) => { // LinkedIn Posts API has a list of characters that need to be escaped since it's type is "LittleText" // https://learn.microsoft.com/en-us/linkedin/marketing/community-management/shares/posts-api?view=li-lms-2023-11&tabs=http // https://learn.microsoft.com/en-us/linkedin/marketing/community-management/shares/little-text-format?view=li-lms-2023-11 // eslint-disable-next-line no-useless-escape return text.replace(/[\(*\)\[\]\{\}<>@|~_]/gm, (x: string) => '\\' + x); }; export const linkedinCommon = { baseUrl: 'https://api.linkedin.com', linkedinHeaders: { 'X-Restli-Protocol-Version': '2.0.0', 'LinkedIn-Version': '202411', }, text: Property.LongText({ displayName: 'Text', required: true, }), imageUrl: Property.File({ displayName: 'Image', required: false, }), link: Property.ShortText({ displayName: 'Content - URL', required: false, }), linkTitle: Property.ShortText({ displayName: 'Content - Title', required: false, }), linkDescription: Property.ShortText({ displayName: 'Content - Description', required: false, }), visibility: Property.Dropdown({ displayName: 'Visibility', refreshers: [], required: true, options: async () => { return { options: [ { label: 'Public', value: 'PUBLIC', }, { label: 'Connections Only', value: 'CONNECTIONS', }, ], }; }, }), company: Property.Dropdown({ displayName: 'Company Page', required: true, refreshers: [], options: async ({ auth }) => { if (!auth) { return { disabled: true, placeholder: 'Connect your account', options: [], }; } const authProp = auth as { access_token: string }; const companies: any = await linkedinCommon.getCompanies( authProp.access_token ); const options = []; for (const company in companies) { options.push({ label: companies[company].localizedName, value: companies[company].id, }); } return { options: options, }; }, }), getCompanies: async (accessToken: string) => { const companies = ( await httpClient.sendRequest({ url: `${linkedinCommon.baseUrl}/v2/organizationalEntityAcls`, method: HttpMethod.GET, authentication: { type: AuthenticationType.BEARER_TOKEN, token: accessToken, }, queryParams: { q: 'roleAssignee', }, }) ).body; const companyIds = companies.elements.map( (company: { organizationalTarget: string }) => { return company.organizationalTarget.substr( company.organizationalTarget.lastIndexOf(':') + 1 ); } ); const response = await fetch(`${linkedinCommon.baseUrl}/rest/organizations?ids=List(${companyIds.join(',')})`, { method: 'GET', headers: { ...linkedinCommon.linkedinHeaders, 'Authorization': `Bearer ${accessToken}` } }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const companySearch = await response.json(); return companySearch.results; }, generatePostRequestBody: (data: { urn: string; text: any; link?: string | undefined; linkTitle?: string | undefined; linkDescription?: string | undefined; visibility: string; image?: Image | undefined; }) => { const requestObject: Post = { author: `urn:li:${data.urn}`, lifecycleState: 'PUBLISHED', commentary: data.text, distribution: { feedDistribution: 'MAIN_FEED', }, visibility: data.visibility, isReshareDisabledByAuthor: false, }; if (data.link) { requestObject.content = { article: { source: data.link, title: data.linkTitle, description: data.linkDescription, thumbnail: data.image?.value.image, }, }; } else if (data.image) { requestObject.content = { media: { id: data.image.value.image, }, }; } return requestObject; }, uploadImage: async ( accessToken: string, urn: string, image: ApFile ): Promise<Image> => { const uploadData = ( await httpClient.sendRequest({ method: HttpMethod.POST, url: `${linkedinCommon.baseUrl}/v2/images`, authentication: { type: AuthenticationType.BEARER_TOKEN, token: accessToken, }, queryParams: { action: 'initializeUpload', }, body: { initializeUploadRequest: { owner: `urn:li:${urn}`, }, }, }) ).body as Image; const uploadFormData = new FormData(); const { filename, base64 } = image; uploadFormData.append('file', Buffer.from(base64, 'base64'), filename); await httpClient.sendRequest({ url: uploadData.value.uploadUrl, method: HttpMethod.POST, body: uploadFormData, authentication: { type: AuthenticationType.BEARER_TOKEN, token: accessToken, }, headers: { 'Content-Type': 'multipart/form-data', }, }); return uploadData; }, }; export interface UgcPost { author: string; lifecycleState: string; specificContent: { 'com.linkedin.ugc.ShareContent': { shareCommentary: { text?: string; }; shareMediaCategory: string; media?: [ { status: string; description?: { text: string; }; originalUrl?: string; thumbnail?: string; title?: { text: string; }; } ]; }; }; visibility: { 'com.linkedin.ugc.MemberNetworkVisibility': string; }; } export interface Post { author: string; commentary: string; lifecycleState: string; visibility: string; distribution: { feedDistribution: string; }; content?: { article?: { source: string; thumbnail?: string | undefined; title?: string | undefined; description?: string | undefined; }; media?: { id: string; }; }; isReshareDisabledByAuthor: boolean; } export interface Image { value: { uploadUrlExpiresAt: number; uploadUrl: string; image: string; }; }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/activepieces/activepieces'

If you have feedback or need assistance with the MCP directory API, please join our Discord server