mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-09 15:07:55 -05:00
feat(tools): added smtp, sendgrid, mailgun, linkedin, fixed permissions in context menu (#2133)
* feat(tools): added twilio sendgrid integration * feat(tools): added smtp, sendgrid, mailgun, fixed permissions in context menu * added top level mocks for sporadically failing tests * incr type safety
This commit is contained in:
@@ -1464,11 +1464,17 @@ export function DiscordIcon(props: SVGProps<SVGSVGElement>) {
|
||||
|
||||
export function LinkedInIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='4 4 42 42' width='1em' height='1em'>
|
||||
<path
|
||||
fill='currentColor'
|
||||
d='M41,4H9C6.24,4,4,6.24,4,9v32c0,2.76,2.24,5,5,5h32c2.76,0,5-2.24,5-5V9C46,6.24,43.76,4,41,4z M17,20v19h-6V20H17z M11,14.47c0-1.4,1.2-2.47,3-2.47s2.93,1.07,3,2.47c0,1.4-1.12,2.53-3,2.53C12.2,17,11,15.87,11,14.47z M39,39h-6c0,0,0-9.26,0-10 c0-2-1-4-3.5-4.04h-0.08C27,24.96,26,27.02,26,29c0,0.91,0,10,0,10h-6V20h6v2.56c0,0,1.93-2.56,5.81-2.56 c3.97,0,7.19,2.73,7.19,8.26V39z'
|
||||
/>
|
||||
<svg {...props} height='72' viewBox='0 0 72 72' width='72' xmlns='http://www.w3.org/2000/svg'>
|
||||
<g fill='none' fillRule='evenodd'>
|
||||
<path
|
||||
d='M8,72 L64,72 C68.418278,72 72,68.418278 72,64 L72,8 C72,3.581722 68.418278,-8.11624501e-16 64,0 L8,0 C3.581722,8.11624501e-16 -5.41083001e-16,3.581722 0,8 L0,64 C5.41083001e-16,68.418278 3.581722,72 8,72 Z'
|
||||
fill='#0072B1'
|
||||
/>
|
||||
<path
|
||||
d='M62,62 L51.315625,62 L51.315625,43.8021149 C51.315625,38.8127542 49.4197917,36.0245323 45.4707031,36.0245323 C41.1746094,36.0245323 38.9300781,38.9261103 38.9300781,43.8021149 L38.9300781,62 L28.6333333,62 L28.6333333,27.3333333 L38.9300781,27.3333333 L38.9300781,32.0029283 C38.9300781,32.0029283 42.0260417,26.2742151 49.3825521,26.2742151 C56.7356771,26.2742151 62,30.7644705 62,40.051212 L62,62 Z M16.349349,22.7940133 C12.8420573,22.7940133 10,19.9296567 10,16.3970067 C10,12.8643566 12.8420573,10 16.349349,10 C19.8566406,10 22.6970052,12.8643566 22.6970052,16.3970067 C22.6970052,19.9296567 19.8566406,22.7940133 16.349349,22.7940133 Z M11.0325521,62 L21.769401,62 L21.769401,27.3333333 L11.0325521,27.3333333 L11.0325521,62 Z'
|
||||
fill='#FFF'
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -4344,3 +4350,81 @@ export function PylonIcon(props: SVGProps<SVGSVGElement>) {
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function SendgridIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
width='800px'
|
||||
height='800px'
|
||||
viewBox='0 0 256 256'
|
||||
version='1.1'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
xmlnsXlink='http://www.w3.org/1999/xlink'
|
||||
preserveAspectRatio='xMidYMid'
|
||||
>
|
||||
<g>
|
||||
<path
|
||||
d='M256.000405,0 L256.000405,170.666936 L170.666936,170.666936 L170.666936,255.996382 L0.00201096905,255.996382 L0.002,170.666 L0,170.666936 L0,85.3314569 L85.3334681,85.3314569 L85.3334681,0 L256.000405,0 Z'
|
||||
fill='#9DD6E3'
|
||||
/>
|
||||
<polygon
|
||||
fill='#3F72AB'
|
||||
points='0.00201096905 255.996382 85.3354791 255.996382 85.3354791 170.662915 0.00201096905 170.662915'
|
||||
/>
|
||||
<polygon
|
||||
fill='#00A9D1'
|
||||
points='170.666936 170.666936 256.000405 170.666936 256.000405 85.3314569 170.666936 85.3314569'
|
||||
/>
|
||||
<polygon
|
||||
fill='#00A9D1'
|
||||
points='85.3334681 85.3334679 170.666936 85.3334679 170.666936 0 85.3334681 0'
|
||||
/>
|
||||
<polygon
|
||||
fill='#2191C4'
|
||||
points='85.3334681 170.664925 170.666936 170.664925 170.666936 85.3314569 85.3334681 85.3314569'
|
||||
/>
|
||||
<polygon
|
||||
fill='#3F72AB'
|
||||
points='170.666936 85.3334679 256.000405 85.3334679 256.000405 0 170.666936 0'
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function MailgunIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
fill='currentColor'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
xmlSpace='preserve'
|
||||
viewBox='0 0 512 512'
|
||||
>
|
||||
<path d='M256.5 159.5c-53.5 0-97 43.5-97 97s43.5 97 97 97 97-43.5 97-97-43.5-97-97-97m-151.1 97c0-83.4 67.7-151.1 151.1-151.1s151.1 67.7 151.1 151.1c0 5.8-.5 11-1 16.3-1 14.7 9.4 25.7 24.1 25.7 24.7 0 27.3-32 27.3-42.5 0-111.7-90.2-202-202-202S54 144.3 54 256s90.2 202 202 202c59.3 0 112.3-25.7 149.5-66.1l41.4 34.6C400.3 479 332.1 512 256 512 114.4 512 0 397.1 0 256 0 114.4 114.9 0 256 0c141.6 0 256 114.9 256 256 0 56.7-27.3 102.8-81.3 102.8-24.1 0-38.3-11-46.7-23.1-26.8 43-74 71.3-128.5 71.3-82.4.6-150.1-67.1-150.1-150.5m151.1-44.6c24.7 0 44.6 19.9 44.6 44.1 0 24.7-19.9 44.6-44.6 44.6s-44.6-19.9-44.6-44.6c.6-24.1 20-44.1 44.6-44.1' />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function SmtpIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
width='30'
|
||||
height='24'
|
||||
viewBox='0 0 30 24'
|
||||
fill='none'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
>
|
||||
<path
|
||||
d='M2.35742 5.83288L11.7674 12.1071C13.0656 12.9712 13.7141 13.404 14.4151 13.5725C15.0352 13.7208 15.681 13.7208 16.2998 13.5725C17.0008 13.404 17.6492 12.9712 18.9475 12.1071L28.3574 5.83288M8.82844 21.7219H21.8864C24.1513 21.7219 25.2837 21.7219 26.1492 21.2811C26.9097 20.8931 27.5278 20.2744 27.9152 19.5137C28.3574 18.6482 28.3574 17.5158 28.3574 15.2509V7.97102C28.3574 5.70616 28.3574 4.57373 27.9166 3.70823C27.5288 2.94727 26.9102 2.32858 26.1492 1.94084C25.2837 1.5 24.1513 1.5 21.8864 1.5H8.82844C6.56358 1.5 5.43115 1.5 4.56566 1.94084C3.80519 2.32881 3.187 2.94747 2.79961 3.70823C2.35742 4.57373 2.35742 5.70616 2.35742 7.97102V15.2509C2.35742 17.5158 2.35742 18.6482 2.79826 19.5137C3.186 20.2747 3.80469 20.8933 4.56566 21.2811C5.43115 21.7219 6.56358 21.7219 8.82844 21.7219Z'
|
||||
stroke='currentColor'
|
||||
strokeWidth='2.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<circle cx='24' cy='6' r='4' fill='currentColor' stroke='none' />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -37,8 +37,10 @@ import {
|
||||
JinaAIIcon,
|
||||
JiraIcon,
|
||||
LinearIcon,
|
||||
LinkedInIcon,
|
||||
LinkupIcon,
|
||||
MailchimpIcon,
|
||||
MailgunIcon,
|
||||
Mem0Icon,
|
||||
MicrosoftExcelIcon,
|
||||
MicrosoftOneDriveIcon,
|
||||
@@ -65,9 +67,11 @@ import {
|
||||
ResendIcon,
|
||||
S3Icon,
|
||||
SalesforceIcon,
|
||||
SendgridIcon,
|
||||
SentryIcon,
|
||||
SerperIcon,
|
||||
SlackIcon,
|
||||
SmtpIcon,
|
||||
STTIcon,
|
||||
StagehandIcon,
|
||||
StripeIcon,
|
||||
@@ -117,10 +121,12 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
stripe: StripeIcon,
|
||||
stagehand_agent: StagehandIcon,
|
||||
stagehand: StagehandIcon,
|
||||
smtp: SmtpIcon,
|
||||
slack: SlackIcon,
|
||||
sharepoint: MicrosoftSharepointIcon,
|
||||
serper: SerperIcon,
|
||||
sentry: SentryIcon,
|
||||
sendgrid: SendgridIcon,
|
||||
salesforce: SalesforceIcon,
|
||||
s3: S3Icon,
|
||||
resend: ResendIcon,
|
||||
@@ -146,8 +152,10 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
microsoft_excel: MicrosoftExcelIcon,
|
||||
memory: BrainIcon,
|
||||
mem0: Mem0Icon,
|
||||
mailgun: MailgunIcon,
|
||||
mailchimp: MailchimpIcon,
|
||||
linkup: LinkupIcon,
|
||||
linkedin: LinkedInIcon,
|
||||
linear: LinearIcon,
|
||||
knowledge: PackageSearchIcon,
|
||||
jira: JiraIcon,
|
||||
|
||||
88
apps/docs/content/docs/en/tools/linkedin.mdx
Normal file
88
apps/docs/content/docs/en/tools/linkedin.mdx
Normal file
@@ -0,0 +1,88 @@
|
||||
---
|
||||
title: LinkedIn
|
||||
description: Share posts and manage your LinkedIn presence
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="linkedin"
|
||||
color="#0072B1"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[LinkedIn](https://www.linkedin.com) is the world’s largest professional networking platform, empowering users to build their careers, connect with their network, and share professional content. LinkedIn is widely used by professionals across industries for personal branding, recruiting, job search, and business development.
|
||||
|
||||
With LinkedIn, you can easily share posts to your personal feed to engage with your network, and access information about your profile to highlight your professional achievements. Automated integration with Sim allows you to leverage LinkedIn functionality programmatically—enabling agents and workflows to post updates, report on your professional presence, and keep your feed active without manual effort.
|
||||
|
||||
Key LinkedIn features available through this integration include:
|
||||
|
||||
- **Share Posts:** Automatically publish professional updates, articles, or announcements to your LinkedIn personal feed.
|
||||
- **Profile Information:** Retrieve detailed information about your LinkedIn profile to monitor or use in downstream tasks within your workflows.
|
||||
|
||||
These capabilities make it easy to keep your LinkedIn network engaged and to extend your professional reach efficiently as part of your AI or workflow automation strategy.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate LinkedIn into workflows. Share posts to your personal feed and access your LinkedIn profile information.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `linkedin_share_post`
|
||||
|
||||
Share a post to your personal LinkedIn feed
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `text` | string | Yes | The text content of your LinkedIn post |
|
||||
| `visibility` | string | No | Who can see this post: "PUBLIC" or "CONNECTIONS" \(default: "PUBLIC"\) |
|
||||
| `request` | string | No | No description |
|
||||
| `output` | string | No | No description |
|
||||
| `output` | string | No | No description |
|
||||
| `specificContent` | string | No | No description |
|
||||
| `shareCommentary` | string | No | No description |
|
||||
| `visibility` | string | No | No description |
|
||||
| `headers` | string | No | No description |
|
||||
| `output` | string | No | No description |
|
||||
| `output` | string | No | No description |
|
||||
| `output` | string | No | No description |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `postId` | string | Created post ID |
|
||||
| `profile` | json | LinkedIn profile information |
|
||||
| `error` | string | Error message if operation failed |
|
||||
|
||||
### `linkedin_get_profile`
|
||||
|
||||
Retrieve your LinkedIn profile information
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `postId` | string | Created post ID |
|
||||
| `profile` | json | LinkedIn profile information |
|
||||
| `error` | string | Error message if operation failed |
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Category: `tools`
|
||||
- Type: `linkedin`
|
||||
221
apps/docs/content/docs/en/tools/mailgun.mdx
Normal file
221
apps/docs/content/docs/en/tools/mailgun.mdx
Normal file
@@ -0,0 +1,221 @@
|
||||
---
|
||||
title: Mailgun
|
||||
description: Send emails and manage mailing lists with Mailgun
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="mailgun"
|
||||
color="#F06248"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Mailgun](https://www.mailgun.com) is a powerful email delivery service designed for developers and businesses to send, receive, and track emails effortlessly. Mailgun enables you to leverage robust APIs for reliable transactional and marketing email, flexible mailing list management, and advanced event tracking.
|
||||
|
||||
With Mailgun's comprehensive feature set, you can automate key email operations and closely monitor deliverability and recipient engagement. This makes it an ideal solution for workflow automation where communications, notifications, and campaign mails are core parts of your processes.
|
||||
|
||||
Key features of Mailgun include:
|
||||
|
||||
- **Transactional Email Sending:** Deliver high-volume emails such as account notifications, receipts, alerts, and password resets.
|
||||
- **Rich Email Content:** Send both plain text and HTML emails, and use tags for categorizing and tracking your messages.
|
||||
- **Mailing List Management:** Create, update, and manage mailing lists and members to send grouped communications efficiently.
|
||||
- **Domain Information:** Retrieve details about your sending domains to monitor configuration and health.
|
||||
- **Event Tracking:** Analyze email deliverability and engagement with detailed event data on sent messages.
|
||||
- **Message Retrieval:** Access stored messages for compliance, analysis, or troubleshooting needs.
|
||||
|
||||
By integrating Mailgun into Sim, your agents are empowered to programmatically send emails, manage email lists, access domain information, and monitor real-time events as part of automated workflows. This allows for intelligent, data-driven engagement with your users directly from your AI-powered processes.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Mailgun into your workflow. Send transactional emails, manage mailing lists and members, view domain information, and track email events. Supports text and HTML emails, tags for tracking, and comprehensive list management.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `mailgun_send_message`
|
||||
|
||||
Send an email using Mailgun API
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Mailgun API key |
|
||||
| `domain` | string | Yes | Mailgun domain \(e.g., mg.example.com\) |
|
||||
| `from` | string | Yes | Sender email address |
|
||||
| `to` | string | Yes | Recipient email address \(comma-separated for multiple\) |
|
||||
| `subject` | string | Yes | Email subject |
|
||||
| `text` | string | No | Plain text body of the email |
|
||||
| `html` | string | No | HTML body of the email |
|
||||
| `cc` | string | No | CC email address \(comma-separated for multiple\) |
|
||||
| `bcc` | string | No | BCC email address \(comma-separated for multiple\) |
|
||||
| `tags` | string | No | Tags for the email \(comma-separated\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the message was sent successfully |
|
||||
| `id` | string | Message ID |
|
||||
| `message` | string | Response message from Mailgun |
|
||||
|
||||
### `mailgun_get_message`
|
||||
|
||||
Retrieve a stored message by its key
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Mailgun API key |
|
||||
| `domain` | string | Yes | Mailgun domain |
|
||||
| `messageKey` | string | Yes | Message storage key |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the request was successful |
|
||||
| `recipients` | string | Message recipients |
|
||||
| `from` | string | Sender email |
|
||||
| `subject` | string | Message subject |
|
||||
| `bodyPlain` | string | Plain text body |
|
||||
| `strippedText` | string | Stripped text |
|
||||
| `strippedSignature` | string | Stripped signature |
|
||||
| `bodyHtml` | string | HTML body |
|
||||
| `strippedHtml` | string | Stripped HTML |
|
||||
| `attachmentCount` | number | Number of attachments |
|
||||
| `timestamp` | number | Message timestamp |
|
||||
| `messageHeaders` | json | Message headers |
|
||||
| `contentIdMap` | json | Content ID map |
|
||||
|
||||
### `mailgun_list_messages`
|
||||
|
||||
List events (logs) for messages sent through Mailgun
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Mailgun API key |
|
||||
| `domain` | string | Yes | Mailgun domain |
|
||||
| `event` | string | No | Filter by event type \(accepted, delivered, failed, opened, clicked, etc.\) |
|
||||
| `limit` | number | No | Maximum number of events to return \(default: 100\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the request was successful |
|
||||
| `items` | json | Array of event items |
|
||||
| `paging` | json | Paging information |
|
||||
|
||||
### `mailgun_create_mailing_list`
|
||||
|
||||
Create a new mailing list
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Mailgun API key |
|
||||
| `address` | string | Yes | Mailing list address \(e.g., list@example.com\) |
|
||||
| `name` | string | No | Mailing list name |
|
||||
| `description` | string | No | Mailing list description |
|
||||
| `accessLevel` | string | No | Access level: readonly, members, or everyone |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the list was created successfully |
|
||||
| `message` | string | Response message |
|
||||
| `list` | json | Created mailing list details |
|
||||
|
||||
### `mailgun_get_mailing_list`
|
||||
|
||||
Get details of a mailing list
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Mailgun API key |
|
||||
| `address` | string | Yes | Mailing list address |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the request was successful |
|
||||
| `list` | json | Mailing list details |
|
||||
|
||||
### `mailgun_add_list_member`
|
||||
|
||||
Add a member to a mailing list
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Mailgun API key |
|
||||
| `listAddress` | string | Yes | Mailing list address |
|
||||
| `address` | string | Yes | Member email address |
|
||||
| `name` | string | No | Member name |
|
||||
| `vars` | string | No | JSON string of custom variables |
|
||||
| `subscribed` | boolean | No | Whether the member is subscribed |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the member was added successfully |
|
||||
| `message` | string | Response message |
|
||||
| `member` | json | Added member details |
|
||||
|
||||
### `mailgun_list_domains`
|
||||
|
||||
List all domains for your Mailgun account
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Mailgun API key |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the request was successful |
|
||||
| `totalCount` | number | Total number of domains |
|
||||
| `items` | json | Array of domain objects |
|
||||
|
||||
### `mailgun_get_domain`
|
||||
|
||||
Get details of a specific domain
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Mailgun API key |
|
||||
| `domain` | string | Yes | Domain name |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the request was successful |
|
||||
| `domain` | json | Domain details |
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Category: `tools`
|
||||
- Type: `mailgun`
|
||||
@@ -33,8 +33,10 @@
|
||||
"jira",
|
||||
"knowledge",
|
||||
"linear",
|
||||
"linkedin",
|
||||
"linkup",
|
||||
"mailchimp",
|
||||
"mailgun",
|
||||
"mem0",
|
||||
"memory",
|
||||
"microsoft_excel",
|
||||
@@ -60,10 +62,12 @@
|
||||
"resend",
|
||||
"s3",
|
||||
"salesforce",
|
||||
"sendgrid",
|
||||
"sentry",
|
||||
"serper",
|
||||
"sharepoint",
|
||||
"slack",
|
||||
"smtp",
|
||||
"stagehand",
|
||||
"stagehand_agent",
|
||||
"stripe",
|
||||
|
||||
396
apps/docs/content/docs/en/tools/sendgrid.mdx
Normal file
396
apps/docs/content/docs/en/tools/sendgrid.mdx
Normal file
@@ -0,0 +1,396 @@
|
||||
---
|
||||
title: SendGrid
|
||||
description: Send emails and manage contacts, lists, and templates with SendGrid
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="sendgrid"
|
||||
color="#1A82E2"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[SendGrid](https://sendgrid.com) is a leading cloud-based email delivery platform trusted by developers and businesses to send reliable transactional and marketing emails at scale. With its robust APIs and powerful tools, SendGrid enables you to manage all aspects of your email communication, from sending notifications and receipts to managing complex marketing campaigns.
|
||||
|
||||
SendGrid empowers users with a full suite of email operations, allowing you to automate critical email workflows and closely manage contact lists, templates, and recipient engagement. Its seamless integration with Sim enables agents and workflows to deliver targeted messages, maintain dynamic contact and recipient lists, trigger personalized emails through templates, and track the results in real time.
|
||||
|
||||
Key features of SendGrid include:
|
||||
|
||||
- **Transactional Email:** Send automated and high-volume transactional emails (like notifications, receipts, and password resets).
|
||||
- **Dynamic Templates:** Use rich HTML or text templates with dynamic data for highly personalized communication at scale.
|
||||
- **Contact Management:** Add and update marketing contacts, manage recipient lists, and target segments for campaigns.
|
||||
- **Attachments Support:** Include one or more file attachments in your emails.
|
||||
- **Comprehensive API Coverage:** Programmatically manage emails, contacts, lists, templates, suppression groups, and more.
|
||||
|
||||
By connecting SendGrid with Sim, your agents can:
|
||||
|
||||
- Send both simple and advanced (templated or multi-recipient) emails as part of any workflow.
|
||||
- Manage and segment contacts and lists automatically.
|
||||
- Leverage templates for consistency and dynamic personalization.
|
||||
- Track and respond to email engagement within your automated processes.
|
||||
|
||||
This integration allows you to automate all critical communication flows, ensure messages reach the right audience, and maintain control over your organization’s email strategy, directly from Sim workflows.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate SendGrid into your workflow. Send transactional emails, manage marketing contacts and lists, and work with email templates. Supports dynamic templates, attachments, and comprehensive contact management.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `sendgrid_send_mail`
|
||||
|
||||
Send an email using SendGrid API
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | SendGrid API key |
|
||||
| `from` | string | Yes | Sender email address \(must be verified in SendGrid\) |
|
||||
| `fromName` | string | No | Sender name |
|
||||
| `to` | string | Yes | Recipient email address |
|
||||
| `toName` | string | No | Recipient name |
|
||||
| `subject` | string | No | Email subject \(required unless using a template with pre-defined subject\) |
|
||||
| `content` | string | No | Email body content \(required unless using a template with pre-defined content\) |
|
||||
| `contentType` | string | No | Content type \(text/plain or text/html\) |
|
||||
| `cc` | string | No | CC email address |
|
||||
| `bcc` | string | No | BCC email address |
|
||||
| `replyTo` | string | No | Reply-to email address |
|
||||
| `replyToName` | string | No | Reply-to name |
|
||||
| `attachments` | file[] | No | Files to attach to the email |
|
||||
| `templateId` | string | No | SendGrid template ID to use |
|
||||
| `dynamicTemplateData` | json | No | JSON object of dynamic template data |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the email was sent successfully |
|
||||
| `messageId` | string | SendGrid message ID |
|
||||
| `to` | string | Recipient email address |
|
||||
| `subject` | string | Email subject |
|
||||
|
||||
### `sendgrid_add_contact`
|
||||
|
||||
Add a new contact to SendGrid
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | SendGrid API key |
|
||||
| `email` | string | Yes | Contact email address |
|
||||
| `firstName` | string | No | Contact first name |
|
||||
| `lastName` | string | No | Contact last name |
|
||||
| `customFields` | json | No | JSON object of custom field key-value pairs \(use field IDs like e1_T, e2_N, e3_D, not field names\) |
|
||||
| `listIds` | string | No | Comma-separated list IDs to add the contact to |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `jobId` | string | Job ID for tracking the async contact creation |
|
||||
| `email` | string | Contact email address |
|
||||
| `firstName` | string | Contact first name |
|
||||
| `lastName` | string | Contact last name |
|
||||
| `message` | string | Status message |
|
||||
|
||||
### `sendgrid_get_contact`
|
||||
|
||||
Get a specific contact by ID from SendGrid
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | SendGrid API key |
|
||||
| `contactId` | string | Yes | Contact ID |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Contact ID |
|
||||
| `email` | string | Contact email address |
|
||||
| `firstName` | string | Contact first name |
|
||||
| `lastName` | string | Contact last name |
|
||||
| `createdAt` | string | Creation timestamp |
|
||||
| `updatedAt` | string | Last update timestamp |
|
||||
| `listIds` | json | Array of list IDs the contact belongs to |
|
||||
| `customFields` | json | Custom field values |
|
||||
|
||||
### `sendgrid_search_contacts`
|
||||
|
||||
Search for contacts in SendGrid using a query
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | SendGrid API key |
|
||||
| `query` | string | Yes | Search query \(e.g., \"email LIKE '%example.com%' AND CONTAINS\(list_ids, 'list-id'\)\"\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `contacts` | json | Array of matching contacts |
|
||||
| `contactCount` | number | Total number of contacts found |
|
||||
|
||||
### `sendgrid_delete_contacts`
|
||||
|
||||
Delete one or more contacts from SendGrid
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | SendGrid API key |
|
||||
| `contactIds` | string | Yes | Comma-separated contact IDs to delete |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `jobId` | string | Job ID for the deletion request |
|
||||
|
||||
### `sendgrid_create_list`
|
||||
|
||||
Create a new contact list in SendGrid
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | SendGrid API key |
|
||||
| `name` | string | Yes | List name |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | List ID |
|
||||
| `name` | string | List name |
|
||||
| `contactCount` | number | Number of contacts in the list |
|
||||
|
||||
### `sendgrid_get_list`
|
||||
|
||||
Get a specific list by ID from SendGrid
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | SendGrid API key |
|
||||
| `listId` | string | Yes | List ID |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | List ID |
|
||||
| `name` | string | List name |
|
||||
| `contactCount` | number | Number of contacts in the list |
|
||||
|
||||
### `sendgrid_list_all_lists`
|
||||
|
||||
Get all contact lists from SendGrid
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | SendGrid API key |
|
||||
| `pageSize` | number | No | Number of lists to return per page \(default: 100\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `lists` | json | Array of lists |
|
||||
|
||||
### `sendgrid_delete_list`
|
||||
|
||||
Delete a contact list from SendGrid
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | SendGrid API key |
|
||||
| `listId` | string | Yes | List ID to delete |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Success message |
|
||||
|
||||
### `sendgrid_add_contacts_to_list`
|
||||
|
||||
Add or update contacts and assign them to a list in SendGrid (uses PUT /v3/marketing/contacts)
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | SendGrid API key |
|
||||
| `listId` | string | Yes | List ID to add contacts to |
|
||||
| `contacts` | json | Yes | JSON array of contact objects. Each contact must have at least: email \(or phone_number_id/external_id/anonymous_id\). Example: \[\{"email": "user@example.com", "first_name": "John"\}\] |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `jobId` | string | Job ID for tracking the async operation |
|
||||
| `message` | string | Status message |
|
||||
|
||||
### `sendgrid_remove_contacts_from_list`
|
||||
|
||||
Remove contacts from a specific list in SendGrid
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | SendGrid API key |
|
||||
| `listId` | string | Yes | List ID |
|
||||
| `contactIds` | string | Yes | Comma-separated contact IDs to remove from the list |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `jobId` | string | Job ID for the request |
|
||||
|
||||
### `sendgrid_create_template`
|
||||
|
||||
Create a new email template in SendGrid
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | SendGrid API key |
|
||||
| `name` | string | Yes | Template name |
|
||||
| `generation` | string | No | Template generation type \(legacy or dynamic, default: dynamic\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Template ID |
|
||||
| `name` | string | Template name |
|
||||
| `generation` | string | Template generation |
|
||||
| `updatedAt` | string | Last update timestamp |
|
||||
| `versions` | json | Array of template versions |
|
||||
|
||||
### `sendgrid_get_template`
|
||||
|
||||
Get a specific template by ID from SendGrid
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | SendGrid API key |
|
||||
| `templateId` | string | Yes | Template ID |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Template ID |
|
||||
| `name` | string | Template name |
|
||||
| `generation` | string | Template generation |
|
||||
| `updatedAt` | string | Last update timestamp |
|
||||
| `versions` | json | Array of template versions |
|
||||
|
||||
### `sendgrid_list_templates`
|
||||
|
||||
Get all email templates from SendGrid
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | SendGrid API key |
|
||||
| `generations` | string | No | Filter by generation \(legacy, dynamic, or both\) |
|
||||
| `pageSize` | number | No | Number of templates to return per page \(default: 20\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `templates` | json | Array of templates |
|
||||
|
||||
### `sendgrid_delete_template`
|
||||
|
||||
Delete an email template from SendGrid
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | SendGrid API key |
|
||||
| `templateId` | string | Yes | Template ID to delete |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `messageId` | string | Email message ID \(send_mail\) |
|
||||
| `id` | string | Resource ID |
|
||||
| `jobId` | string | Job ID for async operations |
|
||||
| `email` | string | Email address |
|
||||
| `firstName` | string | First name |
|
||||
| `lastName` | string | Last name |
|
||||
| `contacts` | json | Array of contacts |
|
||||
| `contactCount` | number | Number of contacts |
|
||||
| `lists` | json | Array of lists |
|
||||
| `templates` | json | Array of templates |
|
||||
| `message` | string | Status or success message |
|
||||
| `name` | string | Resource name |
|
||||
| `generation` | string | Template generation |
|
||||
|
||||
### `sendgrid_create_template_version`
|
||||
|
||||
Create a new version of an email template in SendGrid
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | SendGrid API key |
|
||||
| `templateId` | string | Yes | Template ID |
|
||||
| `name` | string | Yes | Version name |
|
||||
| `subject` | string | Yes | Email subject line |
|
||||
| `htmlContent` | string | No | HTML content of the template |
|
||||
| `plainContent` | string | No | Plain text content of the template |
|
||||
| `active` | boolean | No | Whether this version is active \(default: true\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Version ID |
|
||||
| `templateId` | string | Template ID |
|
||||
| `name` | string | Version name |
|
||||
| `subject` | string | Email subject |
|
||||
| `active` | boolean | Whether this version is active |
|
||||
| `htmlContent` | string | HTML content |
|
||||
| `plainContent` | string | Plain text content |
|
||||
| `updatedAt` | string | Last update timestamp |
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Category: `tools`
|
||||
- Type: `sendgrid`
|
||||
78
apps/docs/content/docs/en/tools/smtp.mdx
Normal file
78
apps/docs/content/docs/en/tools/smtp.mdx
Normal file
@@ -0,0 +1,78 @@
|
||||
---
|
||||
title: SMTP
|
||||
description: Send emails via any SMTP mail server
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="smtp"
|
||||
color="#4A5568"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[SMTP (Simple Mail Transfer Protocol)](https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol) is the foundational standard for email transmission across the Internet. By connecting to any SMTP-compatible server—such as Gmail, Outlook, or your organization's own mail infrastructure—you can send emails programmatically and automate your outbound communications.
|
||||
|
||||
SMTP integration allows you to fully customize email sending through direct server connectivity, supporting both basic and advanced email use cases. With SMTP, you can control every aspect of message delivery, recipient management, and content formatting, making it suitable for transactional notifications, bulk mailings, and any automated workflow requiring robust outbound email delivery.
|
||||
|
||||
**Key features available via SMTP integration include:**
|
||||
|
||||
- **Universal Email Delivery:** Send emails using any SMTP server by configuring standard server connection parameters.
|
||||
- **Customizable Sender and Recipients:** Specify sender address, display name, primary recipients, as well as CC and BCC fields.
|
||||
- **Rich Content Support:** Send plain text or richly formatted HTML emails according to your requirements.
|
||||
- **Attachments:** Include multiple files as attachments in outgoing emails.
|
||||
- **Flexible Security:** Connect using TLS, SSL, or standard (unencrypted) protocols as supported by your SMTP provider.
|
||||
- **Advanced Headers:** Set reply-to headers and other advanced email options to cater for complex mailflows and user interactions.
|
||||
|
||||
By integrating SMTP with Sim, agents and workflows can programmatically send emails as part of any automated process—ranging from sending notifications and confirmations, to automating external communications, reporting, and document delivery. This offers a highly flexible, provider-agnostic approach to managing email directly within your AI-driven processes.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Send emails using any SMTP server (Gmail, Outlook, custom servers, etc.). Configure SMTP connection settings and send emails with full control over content, recipients, and attachments.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `smtp_send_mail`
|
||||
|
||||
Send emails via SMTP server
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `smtpHost` | string | Yes | SMTP server hostname \(e.g., smtp.gmail.com\) |
|
||||
| `smtpPort` | number | Yes | SMTP server port \(587 for TLS, 465 for SSL\) |
|
||||
| `smtpUsername` | string | Yes | SMTP authentication username |
|
||||
| `smtpPassword` | string | Yes | SMTP authentication password |
|
||||
| `smtpSecure` | string | Yes | Security protocol \(TLS, SSL, or None\) |
|
||||
| `from` | string | Yes | Sender email address |
|
||||
| `to` | string | Yes | Recipient email address |
|
||||
| `subject` | string | Yes | Email subject |
|
||||
| `body` | string | Yes | Email body content |
|
||||
| `contentType` | string | No | Content type \(text or html\) |
|
||||
| `fromName` | string | No | Display name for sender |
|
||||
| `cc` | string | No | CC recipients \(comma-separated\) |
|
||||
| `bcc` | string | No | BCC recipients \(comma-separated\) |
|
||||
| `replyTo` | string | No | Reply-to email address |
|
||||
| `attachments` | file[] | No | Files to attach to the email |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the email was sent successfully |
|
||||
| `messageId` | string | Message ID from SMTP server |
|
||||
| `to` | string | Recipient email address |
|
||||
| `subject` | string | Email subject |
|
||||
| `error` | string | Error message if sending failed |
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Category: `tools`
|
||||
- Type: `smtp`
|
||||
@@ -6,6 +6,23 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { createMockRequest } from '@/app/api/__test-utils__/utils'
|
||||
|
||||
const createMockStream = () => {
|
||||
return new ReadableStream({
|
||||
start(controller) {
|
||||
controller.enqueue(
|
||||
new TextEncoder().encode('data: {"blockId":"agent-1","chunk":"Hello"}\n\n')
|
||||
)
|
||||
controller.enqueue(
|
||||
new TextEncoder().encode('data: {"blockId":"agent-1","chunk":" world"}\n\n')
|
||||
)
|
||||
controller.enqueue(
|
||||
new TextEncoder().encode('data: {"event":"final","data":{"success":true}}\n\n')
|
||||
)
|
||||
controller.close()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
vi.mock('@/lib/execution/preprocessing', () => ({
|
||||
preprocessExecution: vi.fn().mockResolvedValue({
|
||||
success: true,
|
||||
@@ -36,28 +53,37 @@ vi.mock('@/lib/logs/execution/logging-session', () => ({
|
||||
})),
|
||||
}))
|
||||
|
||||
describe('Chat Identifier API Route', () => {
|
||||
const createMockStream = () => {
|
||||
return new ReadableStream({
|
||||
start(controller) {
|
||||
controller.enqueue(
|
||||
new TextEncoder().encode('data: {"blockId":"agent-1","chunk":"Hello"}\n\n')
|
||||
)
|
||||
controller.enqueue(
|
||||
new TextEncoder().encode('data: {"blockId":"agent-1","chunk":" world"}\n\n')
|
||||
)
|
||||
controller.enqueue(
|
||||
new TextEncoder().encode('data: {"event":"final","data":{"success":true}}\n\n')
|
||||
)
|
||||
controller.close()
|
||||
},
|
||||
})
|
||||
}
|
||||
vi.mock('@/lib/workflows/streaming', () => ({
|
||||
createStreamingResponse: vi.fn().mockImplementation(async () => createMockStream()),
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/utils', () => ({
|
||||
SSE_HEADERS: {
|
||||
'Content-Type': 'text/event-stream',
|
||||
'Cache-Control': 'no-cache',
|
||||
Connection: 'keep-alive',
|
||||
'X-Accel-Buffering': 'no',
|
||||
},
|
||||
generateRequestId: vi.fn().mockReturnValue('test-request-id'),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/api/workflows/[id]/execute/route', () => ({
|
||||
createFilteredResult: vi.fn().mockImplementation((result: any) => ({
|
||||
...result,
|
||||
logs: undefined,
|
||||
metadata: result.metadata
|
||||
? {
|
||||
...result.metadata,
|
||||
workflowConnections: undefined,
|
||||
}
|
||||
: undefined,
|
||||
})),
|
||||
}))
|
||||
|
||||
describe('Chat Identifier API Route', () => {
|
||||
const mockAddCorsHeaders = vi.fn().mockImplementation((response) => response)
|
||||
const mockValidateChatAuth = vi.fn().mockResolvedValue({ authorized: true })
|
||||
const mockSetChatAuthCookie = vi.fn()
|
||||
const mockCreateStreamingResponse = vi.fn().mockResolvedValue(createMockStream())
|
||||
|
||||
const mockChatResult = [
|
||||
{
|
||||
@@ -104,16 +130,6 @@ describe('Chat Identifier API Route', () => {
|
||||
validateAuthToken: vi.fn().mockReturnValue(true),
|
||||
}))
|
||||
|
||||
vi.doMock('@/lib/workflows/streaming', () => ({
|
||||
createStreamingResponse: mockCreateStreamingResponse,
|
||||
SSE_HEADERS: {
|
||||
'Content-Type': 'text/event-stream',
|
||||
'Cache-Control': 'no-cache',
|
||||
Connection: 'keep-alive',
|
||||
'X-Accel-Buffering': 'no',
|
||||
},
|
||||
}))
|
||||
|
||||
vi.doMock('@/lib/logs/console/logger', () => ({
|
||||
createLogger: vi.fn().mockReturnValue({
|
||||
debug: vi.fn(),
|
||||
@@ -372,6 +388,7 @@ describe('Chat Identifier API Route', () => {
|
||||
const params = Promise.resolve({ identifier: 'test-chat' })
|
||||
|
||||
const { POST } = await import('@/app/api/chat/[identifier]/route')
|
||||
const { createStreamingResponse } = await import('@/lib/workflows/streaming')
|
||||
|
||||
const response = await POST(req, { params })
|
||||
|
||||
@@ -380,7 +397,7 @@ describe('Chat Identifier API Route', () => {
|
||||
expect(response.headers.get('Cache-Control')).toBe('no-cache')
|
||||
expect(response.headers.get('Connection')).toBe('keep-alive')
|
||||
|
||||
expect(mockCreateStreamingResponse).toHaveBeenCalledWith(
|
||||
expect(createStreamingResponse).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
workflow: expect.objectContaining({
|
||||
id: 'workflow-id',
|
||||
@@ -396,7 +413,7 @@ describe('Chat Identifier API Route', () => {
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
}, 10000)
|
||||
|
||||
it('should handle streaming response body correctly', async () => {
|
||||
const req = createMockRequest('POST', { input: 'Hello world' })
|
||||
@@ -423,8 +440,9 @@ describe('Chat Identifier API Route', () => {
|
||||
})
|
||||
|
||||
it('should handle workflow execution errors gracefully', async () => {
|
||||
const originalStreamingResponse = mockCreateStreamingResponse.getMockImplementation()
|
||||
mockCreateStreamingResponse.mockImplementationOnce(async () => {
|
||||
const { createStreamingResponse } = await import('@/lib/workflows/streaming')
|
||||
const originalStreamingResponse = vi.mocked(createStreamingResponse).getMockImplementation()
|
||||
vi.mocked(createStreamingResponse).mockImplementationOnce(async () => {
|
||||
throw new Error('Execution failed')
|
||||
})
|
||||
|
||||
@@ -442,7 +460,7 @@ describe('Chat Identifier API Route', () => {
|
||||
expect(data).toHaveProperty('message', 'Execution failed')
|
||||
|
||||
if (originalStreamingResponse) {
|
||||
mockCreateStreamingResponse.mockImplementation(originalStreamingResponse)
|
||||
vi.mocked(createStreamingResponse).mockImplementation(originalStreamingResponse)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -474,10 +492,11 @@ describe('Chat Identifier API Route', () => {
|
||||
const params = Promise.resolve({ identifier: 'test-chat' })
|
||||
|
||||
const { POST } = await import('@/app/api/chat/[identifier]/route')
|
||||
const { createStreamingResponse } = await import('@/lib/workflows/streaming')
|
||||
|
||||
await POST(req, { params })
|
||||
|
||||
expect(mockCreateStreamingResponse).toHaveBeenCalledWith(
|
||||
expect(createStreamingResponse).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
input: expect.objectContaining({
|
||||
input: 'Hello world',
|
||||
@@ -492,10 +511,11 @@ describe('Chat Identifier API Route', () => {
|
||||
const params = Promise.resolve({ identifier: 'test-chat' })
|
||||
|
||||
const { POST } = await import('@/app/api/chat/[identifier]/route')
|
||||
const { createStreamingResponse } = await import('@/lib/workflows/streaming')
|
||||
|
||||
await POST(req, { params })
|
||||
|
||||
expect(mockCreateStreamingResponse).toHaveBeenCalledWith(
|
||||
expect(createStreamingResponse).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
input: expect.objectContaining({
|
||||
input: 'Hello world',
|
||||
|
||||
227
apps/sim/app/api/tools/smtp/send/route.ts
Normal file
227
apps/sim/app/api/tools/smtp/send/route.ts
Normal file
@@ -0,0 +1,227 @@
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import nodemailer from 'nodemailer'
|
||||
import { z } from 'zod'
|
||||
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
|
||||
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
||||
import { generateRequestId } from '@/lib/utils'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
const logger = createLogger('SmtpSendAPI')
|
||||
|
||||
const SmtpSendSchema = z.object({
|
||||
smtpHost: z.string().min(1, 'SMTP host is required'),
|
||||
smtpPort: z.number().min(1).max(65535, 'Port must be between 1 and 65535'),
|
||||
smtpUsername: z.string().min(1, 'SMTP username is required'),
|
||||
smtpPassword: z.string().min(1, 'SMTP password is required'),
|
||||
smtpSecure: z.enum(['TLS', 'SSL', 'None']),
|
||||
|
||||
from: z.string().email('Invalid from email address').min(1, 'From address is required'),
|
||||
to: z.string().min(1, 'To email is required'),
|
||||
subject: z.string().min(1, 'Subject is required'),
|
||||
body: z.string().min(1, 'Email body is required'),
|
||||
contentType: z.enum(['text', 'html']).optional().nullable(),
|
||||
|
||||
fromName: z.string().optional().nullable(),
|
||||
cc: z.string().optional().nullable(),
|
||||
bcc: z.string().optional().nullable(),
|
||||
replyTo: z.string().optional().nullable(),
|
||||
attachments: z.array(z.any()).optional().nullable(),
|
||||
})
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const requestId = generateRequestId()
|
||||
|
||||
try {
|
||||
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||
|
||||
if (!authResult.success) {
|
||||
logger.warn(`[${requestId}] Unauthorized SMTP send attempt: ${authResult.error}`)
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: authResult.error || 'Authentication required',
|
||||
},
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] Authenticated SMTP request via ${authResult.authType}`, {
|
||||
userId: authResult.userId,
|
||||
})
|
||||
|
||||
const body = await request.json()
|
||||
const validatedData = SmtpSendSchema.parse(body)
|
||||
|
||||
logger.info(`[${requestId}] Sending email via SMTP`, {
|
||||
host: validatedData.smtpHost,
|
||||
port: validatedData.smtpPort,
|
||||
to: validatedData.to,
|
||||
subject: validatedData.subject,
|
||||
secure: validatedData.smtpSecure,
|
||||
})
|
||||
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: validatedData.smtpHost,
|
||||
port: validatedData.smtpPort,
|
||||
secure: validatedData.smtpSecure === 'SSL',
|
||||
auth: {
|
||||
user: validatedData.smtpUsername,
|
||||
pass: validatedData.smtpPassword,
|
||||
},
|
||||
tls:
|
||||
validatedData.smtpSecure === 'None'
|
||||
? {
|
||||
rejectUnauthorized: false,
|
||||
}
|
||||
: {
|
||||
rejectUnauthorized: true,
|
||||
},
|
||||
})
|
||||
|
||||
const contentType = validatedData.contentType || 'text'
|
||||
const fromAddress = validatedData.fromName
|
||||
? `"${validatedData.fromName}" <${validatedData.from}>`
|
||||
: validatedData.from
|
||||
|
||||
const mailOptions: nodemailer.SendMailOptions = {
|
||||
from: fromAddress,
|
||||
to: validatedData.to,
|
||||
subject: validatedData.subject,
|
||||
[contentType === 'html' ? 'html' : 'text']: validatedData.body,
|
||||
}
|
||||
|
||||
if (validatedData.cc) {
|
||||
mailOptions.cc = validatedData.cc
|
||||
}
|
||||
if (validatedData.bcc) {
|
||||
mailOptions.bcc = validatedData.bcc
|
||||
}
|
||||
if (validatedData.replyTo) {
|
||||
mailOptions.replyTo = validatedData.replyTo
|
||||
}
|
||||
|
||||
if (validatedData.attachments && validatedData.attachments.length > 0) {
|
||||
const rawAttachments = validatedData.attachments
|
||||
logger.info(`[${requestId}] Processing ${rawAttachments.length} attachment(s)`)
|
||||
|
||||
const attachments = processFilesToUserFiles(rawAttachments, requestId, logger)
|
||||
|
||||
if (attachments.length > 0) {
|
||||
const totalSize = attachments.reduce((sum, file) => sum + file.size, 0)
|
||||
const maxSize = 25 * 1024 * 1024
|
||||
|
||||
if (totalSize > maxSize) {
|
||||
const sizeMB = (totalSize / (1024 * 1024)).toFixed(2)
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: `Total attachment size (${sizeMB}MB) exceeds SMTP limit of 25MB`,
|
||||
},
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const attachmentBuffers = await Promise.all(
|
||||
attachments.map(async (file) => {
|
||||
try {
|
||||
logger.info(
|
||||
`[${requestId}] Downloading attachment: ${file.name} (${file.size} bytes)`
|
||||
)
|
||||
|
||||
const buffer = await downloadFileFromStorage(file, requestId, logger)
|
||||
|
||||
return {
|
||||
filename: file.name,
|
||||
content: buffer,
|
||||
contentType: file.type || 'application/octet-stream',
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`[${requestId}] Failed to download attachment ${file.name}:`, error)
|
||||
throw new Error(
|
||||
`Failed to download attachment "${file.name}": ${error instanceof Error ? error.message : 'Unknown error'}`
|
||||
)
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
logger.info(`[${requestId}] Processed ${attachmentBuffers.length} attachment(s)`)
|
||||
mailOptions.attachments = attachmentBuffers
|
||||
}
|
||||
}
|
||||
|
||||
const result = await transporter.sendMail(mailOptions)
|
||||
|
||||
logger.info(`[${requestId}] Email sent successfully via SMTP`, {
|
||||
messageId: result.messageId,
|
||||
to: validatedData.to,
|
||||
})
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
messageId: result.messageId,
|
||||
to: validatedData.to,
|
||||
subject: validatedData.subject,
|
||||
})
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof z.ZodError) {
|
||||
logger.warn(`[${requestId}] Invalid request data`, { errors: error.errors })
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'Invalid request data',
|
||||
details: error.errors,
|
||||
},
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// Type guard for error objects with code property
|
||||
const isNodeError = (err: unknown): err is NodeJS.ErrnoException => {
|
||||
return err instanceof Error && 'code' in err
|
||||
}
|
||||
|
||||
let errorMessage = 'Failed to send email via SMTP'
|
||||
|
||||
if (isNodeError(error)) {
|
||||
if (error.code === 'EAUTH') {
|
||||
errorMessage = 'SMTP authentication failed - check username and password'
|
||||
} else if (error.code === 'ECONNECTION' || error.code === 'ECONNREFUSED') {
|
||||
errorMessage = 'Could not connect to SMTP server - check host and port'
|
||||
} else if (error.code === 'ECONNRESET') {
|
||||
errorMessage = 'Connection was reset by SMTP server'
|
||||
} else if (error.code === 'ETIMEDOUT') {
|
||||
errorMessage = 'SMTP server connection timeout'
|
||||
}
|
||||
}
|
||||
|
||||
// Check for SMTP response codes
|
||||
const hasResponseCode = (err: unknown): err is { responseCode: number } => {
|
||||
return typeof err === 'object' && err !== null && 'responseCode' in err
|
||||
}
|
||||
|
||||
if (hasResponseCode(error)) {
|
||||
if (error.responseCode >= 500) {
|
||||
errorMessage = 'SMTP server error - please try again later'
|
||||
} else if (error.responseCode >= 400) {
|
||||
errorMessage = 'Email rejected by SMTP server - check recipient addresses'
|
||||
}
|
||||
}
|
||||
|
||||
logger.error(`[${requestId}] Error sending email via SMTP:`, {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
code: isNodeError(error) ? error.code : undefined,
|
||||
responseCode: hasResponseCode(error) ? error.responseCode : undefined,
|
||||
})
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: errorMessage,
|
||||
},
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -231,6 +231,7 @@ const SCOPE_DESCRIPTIONS: Record<string, string> = {
|
||||
'projects:full': 'Full access to manage your Pipedrive projects',
|
||||
'webhooks:read': 'Read your Pipedrive webhooks',
|
||||
'webhooks:full': 'Full access to manage your Pipedrive webhooks',
|
||||
w_member_social: 'Access your LinkedIn profile',
|
||||
}
|
||||
|
||||
function getScopeDescription(scope: string): string {
|
||||
|
||||
@@ -411,7 +411,7 @@ export function Panel() {
|
||||
</PopoverItem> */}
|
||||
<PopoverItem
|
||||
onClick={handleExportJson}
|
||||
disabled={isExporting || !currentWorkflow}
|
||||
disabled={!userPermissions.canEdit || isExporting || !currentWorkflow}
|
||||
>
|
||||
<Braces className='h-3 w-3' />
|
||||
<span>Export workflow</span>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useCallback, useState } from 'react'
|
||||
import clsx from 'clsx'
|
||||
import { ChevronRight, Folder, FolderOpen } from 'lucide-react'
|
||||
import { useParams, useRouter } from 'next/navigation'
|
||||
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
|
||||
import { ContextMenu } from '@/app/workspace/[workspaceId]/w/components/sidebar/components-new/workflow-list/components/context-menu/context-menu'
|
||||
import { DeleteModal } from '@/app/workspace/[workspaceId]/w/components/sidebar/components-new/workflow-list/components/delete-modal/delete-modal'
|
||||
import {
|
||||
@@ -40,6 +41,7 @@ export function FolderItem({ folder, level, hoverHandlers }: FolderItemProps) {
|
||||
const workspaceId = params.workspaceId as string
|
||||
const updateFolderMutation = useUpdateFolder()
|
||||
const createWorkflowMutation = useCreateWorkflow()
|
||||
const userPermissions = useUserPermissionsContext()
|
||||
|
||||
// Delete modal state
|
||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false)
|
||||
@@ -260,6 +262,9 @@ export function FolderItem({ folder, level, hoverHandlers }: FolderItemProps) {
|
||||
onDuplicate={handleDuplicateFolder}
|
||||
onDelete={() => setIsDeleteModalOpen(true)}
|
||||
showCreate={true}
|
||||
disableRename={!userPermissions.canEdit}
|
||||
disableDuplicate={!userPermissions.canEdit}
|
||||
disableDelete={!userPermissions.canEdit}
|
||||
/>
|
||||
|
||||
{/* Delete Modal */}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useCallback, useRef, useState } from 'react'
|
||||
import clsx from 'clsx'
|
||||
import Link from 'next/link'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
|
||||
import { ContextMenu } from '@/app/workspace/[workspaceId]/w/components/sidebar/components-new/workflow-list/components/context-menu/context-menu'
|
||||
import { DeleteModal } from '@/app/workspace/[workspaceId]/w/components/sidebar/components-new/workflow-list/components/delete-modal/delete-modal'
|
||||
import { Avatars } from '@/app/workspace/[workspaceId]/w/components/sidebar/components-new/workflow-list/components/workflow-item/avatars/avatars'
|
||||
@@ -40,6 +41,7 @@ export function WorkflowItem({ workflow, active, level, onWorkflowClick }: Workf
|
||||
const workspaceId = params.workspaceId as string
|
||||
const { selectedWorkflows } = useFolderStore()
|
||||
const { updateWorkflow, workflows } = useWorkflowRegistry()
|
||||
const userPermissions = useUserPermissionsContext()
|
||||
const isSelected = selectedWorkflows.has(workflow.id)
|
||||
|
||||
// Delete modal state
|
||||
@@ -309,6 +311,10 @@ export function WorkflowItem({ workflow, active, level, onWorkflowClick }: Workf
|
||||
showRename={selectedWorkflows.size <= 1}
|
||||
showDuplicate={true}
|
||||
showExport={true}
|
||||
disableRename={!userPermissions.canEdit}
|
||||
disableDuplicate={!userPermissions.canEdit}
|
||||
disableExport={!userPermissions.canEdit}
|
||||
disableDelete={!userPermissions.canEdit}
|
||||
/>
|
||||
|
||||
{/* Delete Confirmation Modal */}
|
||||
|
||||
112
apps/sim/blocks/blocks/linkedin.ts
Normal file
112
apps/sim/blocks/blocks/linkedin.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { LinkedInIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import type { LinkedInResponse } from '@/tools/linkedin/types'
|
||||
|
||||
export const LinkedInBlock: BlockConfig<LinkedInResponse> = {
|
||||
type: 'linkedin',
|
||||
name: 'LinkedIn',
|
||||
description: 'Share posts and manage your LinkedIn presence',
|
||||
authMode: AuthMode.OAuth,
|
||||
longDescription:
|
||||
'Integrate LinkedIn into workflows. Share posts to your personal feed and access your LinkedIn profile information.',
|
||||
docsLink: 'https://docs.sim.ai/tools/linkedin',
|
||||
category: 'tools',
|
||||
bgColor: '#0072B1',
|
||||
icon: LinkedInIcon,
|
||||
subBlocks: [
|
||||
// Operation selection
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Share Post', id: 'share_post' },
|
||||
{ label: 'Get Profile', id: 'get_profile' },
|
||||
],
|
||||
value: () => 'share_post',
|
||||
},
|
||||
|
||||
// LinkedIn OAuth Authentication
|
||||
{
|
||||
id: 'credential',
|
||||
title: 'LinkedIn Account',
|
||||
type: 'oauth-input',
|
||||
provider: 'linkedin',
|
||||
serviceId: 'linkedin',
|
||||
requiredScopes: ['profile', 'openid', 'email', 'w_member_social'],
|
||||
placeholder: 'Select LinkedIn account',
|
||||
required: true,
|
||||
},
|
||||
|
||||
// Share Post specific fields
|
||||
{
|
||||
id: 'text',
|
||||
title: 'Post Text',
|
||||
type: 'long-input',
|
||||
placeholder: 'What do you want to share on LinkedIn?',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'share_post',
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'visibility',
|
||||
title: 'Visibility',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Public', id: 'PUBLIC' },
|
||||
{ label: 'Connections Only', id: 'CONNECTIONS' },
|
||||
],
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'share_post',
|
||||
},
|
||||
value: () => 'PUBLIC',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: ['linkedin_share_post', 'linkedin_get_profile'],
|
||||
config: {
|
||||
tool: (inputs) => {
|
||||
const operation = inputs.operation || 'share_post'
|
||||
|
||||
if (operation === 'get_profile') {
|
||||
return 'linkedin_get_profile'
|
||||
}
|
||||
|
||||
return 'linkedin_share_post'
|
||||
},
|
||||
params: (inputs) => {
|
||||
const operation = inputs.operation || 'share_post'
|
||||
const { credential, ...rest } = inputs
|
||||
|
||||
if (operation === 'get_profile') {
|
||||
return {
|
||||
accessToken: credential,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
text: rest.text,
|
||||
visibility: rest.visibility || 'PUBLIC',
|
||||
accessToken: credential,
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'Operation to perform' },
|
||||
credential: { type: 'string', description: 'LinkedIn access token' },
|
||||
text: { type: 'string', description: 'Post text content' },
|
||||
visibility: { type: 'string', description: 'Post visibility (PUBLIC or CONNECTIONS)' },
|
||||
},
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
postId: { type: 'string', description: 'Created post ID' },
|
||||
profile: { type: 'json', description: 'LinkedIn profile information' },
|
||||
error: { type: 'string', description: 'Error message if operation failed' },
|
||||
},
|
||||
}
|
||||
293
apps/sim/blocks/blocks/mailgun.ts
Normal file
293
apps/sim/blocks/blocks/mailgun.ts
Normal file
@@ -0,0 +1,293 @@
|
||||
import { MailgunIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { SendMessageResult } from '@/tools/mailgun/types'
|
||||
|
||||
export const MailgunBlock: BlockConfig<SendMessageResult> = {
|
||||
type: 'mailgun',
|
||||
name: 'Mailgun',
|
||||
description: 'Send emails and manage mailing lists with Mailgun',
|
||||
longDescription:
|
||||
'Integrate Mailgun into your workflow. Send transactional emails, manage mailing lists and members, view domain information, and track email events. Supports text and HTML emails, tags for tracking, and comprehensive list management.',
|
||||
docsLink: 'https://docs.sim.ai/tools/mailgun',
|
||||
category: 'tools',
|
||||
bgColor: '#F06248',
|
||||
icon: MailgunIcon,
|
||||
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
// Message Operations
|
||||
{ label: 'Send Message', id: 'send_message' },
|
||||
{ label: 'Get Message', id: 'get_message' },
|
||||
{ label: 'List Messages', id: 'list_messages' },
|
||||
// Mailing List Operations
|
||||
{ label: 'Create Mailing List', id: 'create_mailing_list' },
|
||||
{ label: 'Get Mailing List', id: 'get_mailing_list' },
|
||||
{ label: 'Add List Member', id: 'add_list_member' },
|
||||
// Domain Operations
|
||||
{ label: 'List Domains', id: 'list_domains' },
|
||||
{ label: 'Get Domain', id: 'get_domain' },
|
||||
],
|
||||
value: () => 'send_message',
|
||||
},
|
||||
{
|
||||
id: 'apiKey',
|
||||
title: 'Mailgun API Key',
|
||||
type: 'short-input',
|
||||
password: true,
|
||||
placeholder: 'Enter your Mailgun API key',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'domain',
|
||||
title: 'Domain',
|
||||
type: 'short-input',
|
||||
placeholder: 'mg.example.com',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['send_message', 'get_message', 'list_messages', 'get_domain'],
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
// Send Message fields
|
||||
{
|
||||
id: 'from',
|
||||
title: 'From Email',
|
||||
type: 'short-input',
|
||||
placeholder: 'sender@example.com',
|
||||
condition: { field: 'operation', value: 'send_message' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'to',
|
||||
title: 'To Email',
|
||||
type: 'short-input',
|
||||
placeholder: 'recipient@example.com',
|
||||
condition: { field: 'operation', value: 'send_message' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'subject',
|
||||
title: 'Subject',
|
||||
type: 'short-input',
|
||||
placeholder: 'Email subject',
|
||||
condition: { field: 'operation', value: 'send_message' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'text',
|
||||
title: 'Text Body',
|
||||
type: 'long-input',
|
||||
placeholder: 'Plain text email body',
|
||||
condition: { field: 'operation', value: 'send_message' },
|
||||
},
|
||||
{
|
||||
id: 'html',
|
||||
title: 'HTML Body',
|
||||
type: 'code',
|
||||
placeholder: '<html><body>HTML email body</body></html>',
|
||||
condition: { field: 'operation', value: 'send_message' },
|
||||
},
|
||||
{
|
||||
id: 'cc',
|
||||
title: 'CC',
|
||||
type: 'short-input',
|
||||
placeholder: 'cc@example.com',
|
||||
condition: { field: 'operation', value: 'send_message' },
|
||||
},
|
||||
{
|
||||
id: 'bcc',
|
||||
title: 'BCC',
|
||||
type: 'short-input',
|
||||
placeholder: 'bcc@example.com',
|
||||
condition: { field: 'operation', value: 'send_message' },
|
||||
},
|
||||
{
|
||||
id: 'tags',
|
||||
title: 'Tags',
|
||||
type: 'short-input',
|
||||
placeholder: 'tag1, tag2',
|
||||
condition: { field: 'operation', value: 'send_message' },
|
||||
},
|
||||
// Get Message fields
|
||||
{
|
||||
id: 'messageKey',
|
||||
title: 'Message Key',
|
||||
type: 'short-input',
|
||||
placeholder: 'Message storage key',
|
||||
condition: { field: 'operation', value: 'get_message' },
|
||||
required: true,
|
||||
},
|
||||
// List Messages fields
|
||||
{
|
||||
id: 'event',
|
||||
title: 'Event Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'All Events', id: '' },
|
||||
{ label: 'Accepted', id: 'accepted' },
|
||||
{ label: 'Delivered', id: 'delivered' },
|
||||
{ label: 'Failed', id: 'failed' },
|
||||
{ label: 'Opened', id: 'opened' },
|
||||
{ label: 'Clicked', id: 'clicked' },
|
||||
{ label: 'Unsubscribed', id: 'unsubscribed' },
|
||||
{ label: 'Complained', id: 'complained' },
|
||||
{ label: 'Stored', id: 'stored' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'list_messages' },
|
||||
},
|
||||
{
|
||||
id: 'limit',
|
||||
title: 'Limit',
|
||||
type: 'short-input',
|
||||
placeholder: '100',
|
||||
condition: { field: 'operation', value: 'list_messages' },
|
||||
},
|
||||
// Create Mailing List fields
|
||||
{
|
||||
id: 'address',
|
||||
title: 'List Address',
|
||||
type: 'short-input',
|
||||
placeholder: 'list@example.com',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['create_mailing_list', 'get_mailing_list', 'add_list_member'],
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'name',
|
||||
title: 'List Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'My Mailing List',
|
||||
condition: { field: 'operation', value: 'create_mailing_list' },
|
||||
},
|
||||
{
|
||||
id: 'description',
|
||||
title: 'Description',
|
||||
type: 'long-input',
|
||||
placeholder: 'Description of the mailing list',
|
||||
condition: { field: 'operation', value: 'create_mailing_list' },
|
||||
},
|
||||
{
|
||||
id: 'accessLevel',
|
||||
title: 'Access Level',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Read Only', id: 'readonly' },
|
||||
{ label: 'Members', id: 'members' },
|
||||
{ label: 'Everyone', id: 'everyone' },
|
||||
],
|
||||
value: () => 'readonly',
|
||||
condition: { field: 'operation', value: 'create_mailing_list' },
|
||||
},
|
||||
// Add List Member fields (reuse address from above for listAddress)
|
||||
{
|
||||
id: 'memberAddress',
|
||||
title: 'Member Email',
|
||||
type: 'short-input',
|
||||
placeholder: 'member@example.com',
|
||||
condition: { field: 'operation', value: 'add_list_member' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'memberName',
|
||||
title: 'Member Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'John Doe',
|
||||
condition: { field: 'operation', value: 'add_list_member' },
|
||||
},
|
||||
{
|
||||
id: 'vars',
|
||||
title: 'Custom Variables',
|
||||
type: 'code',
|
||||
placeholder: '{"key": "value"}',
|
||||
condition: { field: 'operation', value: 'add_list_member' },
|
||||
},
|
||||
{
|
||||
id: 'subscribed',
|
||||
title: 'Subscribed',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Yes', id: 'true' },
|
||||
{ label: 'No', id: 'false' },
|
||||
],
|
||||
value: () => 'true',
|
||||
condition: { field: 'operation', value: 'add_list_member' },
|
||||
},
|
||||
],
|
||||
|
||||
tools: {
|
||||
access: [
|
||||
'mailgun_send_message',
|
||||
'mailgun_get_message',
|
||||
'mailgun_list_messages',
|
||||
'mailgun_create_mailing_list',
|
||||
'mailgun_get_mailing_list',
|
||||
'mailgun_add_list_member',
|
||||
'mailgun_list_domains',
|
||||
'mailgun_get_domain',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => `mailgun_${params.operation}`,
|
||||
params: (params) => {
|
||||
const { operation, memberAddress, memberName, ...rest } = params
|
||||
|
||||
// Handle special field mappings for add_list_member
|
||||
if (operation === 'add_list_member') {
|
||||
return {
|
||||
...rest,
|
||||
listAddress: params.address,
|
||||
address: memberAddress,
|
||||
name: memberName,
|
||||
subscribed: params.subscribed === 'true',
|
||||
}
|
||||
}
|
||||
|
||||
return rest
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'Operation to perform' },
|
||||
apiKey: { type: 'string', description: 'Mailgun API key' },
|
||||
domain: { type: 'string', description: 'Mailgun domain' },
|
||||
// Message inputs
|
||||
from: { type: 'string', description: 'Sender email address' },
|
||||
to: { type: 'string', description: 'Recipient email address' },
|
||||
subject: { type: 'string', description: 'Email subject' },
|
||||
text: { type: 'string', description: 'Plain text body' },
|
||||
html: { type: 'string', description: 'HTML body' },
|
||||
cc: { type: 'string', description: 'CC email address' },
|
||||
bcc: { type: 'string', description: 'BCC email address' },
|
||||
tags: { type: 'string', description: 'Tags for the email' },
|
||||
messageKey: { type: 'string', description: 'Message storage key' },
|
||||
event: { type: 'string', description: 'Event type filter' },
|
||||
limit: { type: 'number', description: 'Number of events to return' },
|
||||
// Mailing list inputs
|
||||
address: { type: 'string', description: 'Mailing list address' },
|
||||
name: { type: 'string', description: 'List or member name' },
|
||||
description: { type: 'string', description: 'List description' },
|
||||
accessLevel: { type: 'string', description: 'List access level' },
|
||||
memberAddress: { type: 'string', description: 'Member email address' },
|
||||
memberName: { type: 'string', description: 'Member name' },
|
||||
vars: { type: 'string', description: 'Custom variables JSON' },
|
||||
subscribed: { type: 'string', description: 'Member subscription status' },
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
id: { type: 'string', description: 'Message ID' },
|
||||
message: { type: 'string', description: 'Response message' },
|
||||
items: { type: 'json', description: 'Array of items (messages, domains)' },
|
||||
list: { type: 'json', description: 'Mailing list details' },
|
||||
member: { type: 'json', description: 'Member details' },
|
||||
domain: { type: 'json', description: 'Domain details' },
|
||||
totalCount: { type: 'number', description: 'Total count of items' },
|
||||
},
|
||||
}
|
||||
478
apps/sim/blocks/blocks/sendgrid.ts
Normal file
478
apps/sim/blocks/blocks/sendgrid.ts
Normal file
@@ -0,0 +1,478 @@
|
||||
import { SendgridIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { SendMailResult } from '@/tools/sendgrid/types'
|
||||
|
||||
export const SendGridBlock: BlockConfig<SendMailResult> = {
|
||||
type: 'sendgrid',
|
||||
name: 'SendGrid',
|
||||
description: 'Send emails and manage contacts, lists, and templates with SendGrid',
|
||||
longDescription:
|
||||
'Integrate SendGrid into your workflow. Send transactional emails, manage marketing contacts and lists, and work with email templates. Supports dynamic templates, attachments, and comprehensive contact management.',
|
||||
docsLink: 'https://docs.sim.ai/tools/sendgrid',
|
||||
category: 'tools',
|
||||
bgColor: '#1A82E2',
|
||||
icon: SendgridIcon,
|
||||
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
// Mail Operations
|
||||
{ label: 'Send Mail', id: 'send_mail' },
|
||||
// Contact Operations
|
||||
{ label: 'Add Contact', id: 'add_contact' },
|
||||
{ label: 'Get Contact', id: 'get_contact' },
|
||||
{ label: 'Search Contacts', id: 'search_contacts' },
|
||||
{ label: 'Delete Contacts', id: 'delete_contacts' },
|
||||
// List Operations
|
||||
{ label: 'Create List', id: 'create_list' },
|
||||
{ label: 'Get List', id: 'get_list' },
|
||||
{ label: 'List All Lists', id: 'list_all_lists' },
|
||||
{ label: 'Delete List', id: 'delete_list' },
|
||||
{ label: 'Add Contacts to List', id: 'add_contacts_to_list' },
|
||||
{ label: 'Remove Contacts from List', id: 'remove_contacts_from_list' },
|
||||
// Template Operations
|
||||
{ label: 'Create Template', id: 'create_template' },
|
||||
{ label: 'Get Template', id: 'get_template' },
|
||||
{ label: 'List Templates', id: 'list_templates' },
|
||||
{ label: 'Delete Template', id: 'delete_template' },
|
||||
{ label: 'Create Template Version', id: 'create_template_version' },
|
||||
],
|
||||
value: () => 'send_mail',
|
||||
},
|
||||
{
|
||||
id: 'apiKey',
|
||||
title: 'SendGrid API Key',
|
||||
type: 'short-input',
|
||||
password: true,
|
||||
placeholder: 'Enter your SendGrid API key',
|
||||
required: true,
|
||||
},
|
||||
// Send Mail fields
|
||||
{
|
||||
id: 'from',
|
||||
title: 'From Email',
|
||||
type: 'short-input',
|
||||
placeholder: 'sender@yourdomain.com',
|
||||
condition: { field: 'operation', value: 'send_mail' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'fromName',
|
||||
title: 'From Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Sender Name',
|
||||
condition: { field: 'operation', value: 'send_mail' },
|
||||
},
|
||||
{
|
||||
id: 'to',
|
||||
title: 'To Email',
|
||||
type: 'short-input',
|
||||
placeholder: 'recipient@example.com',
|
||||
condition: { field: 'operation', value: 'send_mail' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'toName',
|
||||
title: 'To Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Recipient Name',
|
||||
condition: { field: 'operation', value: 'send_mail' },
|
||||
},
|
||||
{
|
||||
id: 'mailSubject',
|
||||
title: 'Subject',
|
||||
type: 'short-input',
|
||||
placeholder: 'Email subject (required unless using template)',
|
||||
condition: { field: 'operation', value: 'send_mail' },
|
||||
},
|
||||
{
|
||||
id: 'content',
|
||||
title: 'Content',
|
||||
type: 'long-input',
|
||||
placeholder: 'Email body content (required unless using template)',
|
||||
condition: { field: 'operation', value: 'send_mail' },
|
||||
},
|
||||
{
|
||||
id: 'contentType',
|
||||
title: 'Content Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Plain Text', id: 'text/plain' },
|
||||
{ label: 'HTML', id: 'text/html' },
|
||||
],
|
||||
value: () => 'text/plain',
|
||||
condition: { field: 'operation', value: 'send_mail' },
|
||||
},
|
||||
{
|
||||
id: 'cc',
|
||||
title: 'CC',
|
||||
type: 'short-input',
|
||||
placeholder: 'cc@example.com',
|
||||
condition: { field: 'operation', value: 'send_mail' },
|
||||
},
|
||||
{
|
||||
id: 'bcc',
|
||||
title: 'BCC',
|
||||
type: 'short-input',
|
||||
placeholder: 'bcc@example.com',
|
||||
condition: { field: 'operation', value: 'send_mail' },
|
||||
},
|
||||
{
|
||||
id: 'replyTo',
|
||||
title: 'Reply To',
|
||||
type: 'short-input',
|
||||
placeholder: 'replyto@example.com',
|
||||
condition: { field: 'operation', value: 'send_mail' },
|
||||
},
|
||||
{
|
||||
id: 'replyToName',
|
||||
title: 'Reply To Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Reply To Name',
|
||||
condition: { field: 'operation', value: 'send_mail' },
|
||||
},
|
||||
{
|
||||
id: 'mailTemplateId',
|
||||
title: 'Template ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'SendGrid template ID',
|
||||
condition: { field: 'operation', value: 'send_mail' },
|
||||
},
|
||||
{
|
||||
id: 'dynamicTemplateData',
|
||||
title: 'Dynamic Template Data',
|
||||
type: 'code',
|
||||
placeholder: '{"name": "John", "order_id": "12345"}',
|
||||
condition: { field: 'operation', value: 'send_mail' },
|
||||
},
|
||||
// File upload (basic mode)
|
||||
{
|
||||
id: 'attachmentFiles',
|
||||
title: 'Attachments',
|
||||
type: 'file-upload',
|
||||
canonicalParamId: 'attachments',
|
||||
placeholder: 'Upload files to attach',
|
||||
condition: { field: 'operation', value: 'send_mail' },
|
||||
mode: 'basic',
|
||||
multiple: true,
|
||||
required: false,
|
||||
},
|
||||
// Variable reference (advanced mode)
|
||||
{
|
||||
id: 'attachments',
|
||||
title: 'Attachments',
|
||||
type: 'short-input',
|
||||
canonicalParamId: 'attachments',
|
||||
placeholder: 'Reference files from previous blocks',
|
||||
condition: { field: 'operation', value: 'send_mail' },
|
||||
mode: 'advanced',
|
||||
required: false,
|
||||
},
|
||||
// Contact fields
|
||||
{
|
||||
id: 'email',
|
||||
title: 'Email',
|
||||
type: 'short-input',
|
||||
placeholder: 'contact@example.com',
|
||||
condition: { field: 'operation', value: ['add_contact'] },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'firstName',
|
||||
title: 'First Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'John',
|
||||
condition: { field: 'operation', value: ['add_contact'] },
|
||||
},
|
||||
{
|
||||
id: 'lastName',
|
||||
title: 'Last Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Doe',
|
||||
condition: { field: 'operation', value: ['add_contact'] },
|
||||
},
|
||||
{
|
||||
id: 'customFields',
|
||||
title: 'Custom Fields',
|
||||
type: 'code',
|
||||
placeholder: '{"custom_field_1": "value1"}',
|
||||
condition: { field: 'operation', value: ['add_contact'] },
|
||||
},
|
||||
{
|
||||
id: 'contactListIds',
|
||||
title: 'List IDs',
|
||||
type: 'short-input',
|
||||
placeholder: 'Comma-separated list IDs',
|
||||
condition: { field: 'operation', value: ['add_contact'] },
|
||||
},
|
||||
{
|
||||
id: 'contactId',
|
||||
title: 'Contact ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Contact ID',
|
||||
condition: { field: 'operation', value: ['get_contact'] },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'query',
|
||||
title: 'Search Query',
|
||||
type: 'long-input',
|
||||
placeholder: "email LIKE '%example.com%'",
|
||||
condition: { field: 'operation', value: ['search_contacts'] },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'contactIds',
|
||||
title: 'Contact IDs',
|
||||
type: 'short-input',
|
||||
placeholder: 'Comma-separated contact IDs',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['delete_contacts', 'remove_contacts_from_list'],
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'contacts',
|
||||
title: 'Contacts (JSON Array)',
|
||||
type: 'code',
|
||||
placeholder: '[{"email": "user@example.com", "first_name": "John"}]',
|
||||
condition: { field: 'operation', value: 'add_contacts_to_list' },
|
||||
required: true,
|
||||
},
|
||||
// List fields
|
||||
{
|
||||
id: 'listName',
|
||||
title: 'List Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'List name',
|
||||
condition: { field: 'operation', value: ['create_list'] },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'listId',
|
||||
title: 'List ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'List ID',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['get_list', 'delete_list', 'add_contacts_to_list', 'remove_contacts_from_list'],
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'listPageSize',
|
||||
title: 'Page Size',
|
||||
type: 'short-input',
|
||||
placeholder: '100',
|
||||
condition: { field: 'operation', value: 'list_all_lists' },
|
||||
},
|
||||
// Template fields
|
||||
{
|
||||
id: 'templateName',
|
||||
title: 'Template Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Template name',
|
||||
condition: { field: 'operation', value: ['create_template'] },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'templateId',
|
||||
title: 'Template ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Template ID',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['get_template', 'delete_template', 'create_template_version'],
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'generation',
|
||||
title: 'Template Generation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Dynamic', id: 'dynamic' },
|
||||
{ label: 'Legacy', id: 'legacy' },
|
||||
],
|
||||
value: () => 'dynamic',
|
||||
condition: { field: 'operation', value: 'create_template' },
|
||||
},
|
||||
{
|
||||
id: 'templateGenerations',
|
||||
title: 'Filter by Generation',
|
||||
type: 'short-input',
|
||||
placeholder: 'legacy, dynamic, or both',
|
||||
condition: { field: 'operation', value: 'list_templates' },
|
||||
},
|
||||
{
|
||||
id: 'templatePageSize',
|
||||
title: 'Page Size',
|
||||
type: 'short-input',
|
||||
placeholder: '20',
|
||||
condition: { field: 'operation', value: 'list_templates' },
|
||||
},
|
||||
{
|
||||
id: 'versionName',
|
||||
title: 'Version Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Version name',
|
||||
condition: { field: 'operation', value: 'create_template_version' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'templateSubject',
|
||||
title: 'Template Subject',
|
||||
type: 'short-input',
|
||||
placeholder: 'Email subject',
|
||||
condition: { field: 'operation', value: 'create_template_version' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'htmlContent',
|
||||
title: 'HTML Content',
|
||||
type: 'code',
|
||||
placeholder: '<html><body>{{name}}</body></html>',
|
||||
condition: { field: 'operation', value: 'create_template_version' },
|
||||
},
|
||||
{
|
||||
id: 'plainContent',
|
||||
title: 'Plain Text Content',
|
||||
type: 'long-input',
|
||||
placeholder: 'Plain text content',
|
||||
condition: { field: 'operation', value: 'create_template_version' },
|
||||
},
|
||||
{
|
||||
id: 'active',
|
||||
title: 'Active',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Yes', id: 'true' },
|
||||
{ label: 'No', id: 'false' },
|
||||
],
|
||||
value: () => 'true',
|
||||
condition: { field: 'operation', value: 'create_template_version' },
|
||||
},
|
||||
],
|
||||
|
||||
tools: {
|
||||
access: [
|
||||
'sendgrid_send_mail',
|
||||
'sendgrid_add_contact',
|
||||
'sendgrid_get_contact',
|
||||
'sendgrid_search_contacts',
|
||||
'sendgrid_delete_contacts',
|
||||
'sendgrid_create_list',
|
||||
'sendgrid_get_list',
|
||||
'sendgrid_list_all_lists',
|
||||
'sendgrid_delete_list',
|
||||
'sendgrid_add_contacts_to_list',
|
||||
'sendgrid_remove_contacts_from_list',
|
||||
'sendgrid_create_template',
|
||||
'sendgrid_get_template',
|
||||
'sendgrid_list_templates',
|
||||
'sendgrid_delete_template',
|
||||
'sendgrid_create_template_version',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => `sendgrid_${params.operation}`,
|
||||
params: (params) => {
|
||||
const {
|
||||
operation,
|
||||
mailSubject,
|
||||
mailTemplateId,
|
||||
listName,
|
||||
templateName,
|
||||
versionName,
|
||||
templateSubject,
|
||||
contactListIds,
|
||||
templateGenerations,
|
||||
listPageSize,
|
||||
templatePageSize,
|
||||
...rest
|
||||
} = params
|
||||
|
||||
// Map renamed fields back to tool parameter names
|
||||
return {
|
||||
...rest,
|
||||
...(mailSubject && { subject: mailSubject }),
|
||||
...(mailTemplateId && { templateId: mailTemplateId }),
|
||||
...(listName && { name: listName }),
|
||||
...(templateName && { name: templateName }),
|
||||
...(versionName && { name: versionName }),
|
||||
...(templateSubject && { subject: templateSubject }),
|
||||
...(contactListIds && { listIds: contactListIds }),
|
||||
...(templateGenerations && { generations: templateGenerations }),
|
||||
...(listPageSize && { pageSize: listPageSize }),
|
||||
...(templatePageSize && { pageSize: templatePageSize }),
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'Operation to perform' },
|
||||
apiKey: { type: 'string', description: 'SendGrid API key' },
|
||||
// Mail inputs
|
||||
from: { type: 'string', description: 'Sender email address' },
|
||||
fromName: { type: 'string', description: 'Sender name' },
|
||||
to: { type: 'string', description: 'Recipient email address' },
|
||||
toName: { type: 'string', description: 'Recipient name' },
|
||||
mailSubject: { type: 'string', description: 'Email subject' },
|
||||
content: { type: 'string', description: 'Email content' },
|
||||
contentType: { type: 'string', description: 'Content type' },
|
||||
cc: { type: 'string', description: 'CC email address' },
|
||||
bcc: { type: 'string', description: 'BCC email address' },
|
||||
replyTo: { type: 'string', description: 'Reply-to email address' },
|
||||
replyToName: { type: 'string', description: 'Reply-to name' },
|
||||
mailTemplateId: { type: 'string', description: 'Template ID for sending mail' },
|
||||
dynamicTemplateData: { type: 'json', description: 'Dynamic template data' },
|
||||
attachmentFiles: { type: 'json', description: 'Files to attach (UI upload)' },
|
||||
attachments: { type: 'array', description: 'Files to attach (UserFile array)' },
|
||||
// Contact inputs
|
||||
email: { type: 'string', description: 'Contact email' },
|
||||
firstName: { type: 'string', description: 'Contact first name' },
|
||||
lastName: { type: 'string', description: 'Contact last name' },
|
||||
customFields: { type: 'json', description: 'Custom fields' },
|
||||
contactId: { type: 'string', description: 'Contact ID' },
|
||||
contactIds: { type: 'string', description: 'Comma-separated contact IDs' },
|
||||
contacts: { type: 'json', description: 'Array of contact objects' },
|
||||
query: { type: 'string', description: 'Search query' },
|
||||
contactListIds: { type: 'string', description: 'Comma-separated list IDs for contact' },
|
||||
// List inputs
|
||||
listName: { type: 'string', description: 'List name' },
|
||||
listId: { type: 'string', description: 'List ID' },
|
||||
listPageSize: { type: 'number', description: 'Page size for listing lists' },
|
||||
// Template inputs
|
||||
templateName: { type: 'string', description: 'Template name' },
|
||||
templateId: { type: 'string', description: 'Template ID' },
|
||||
generation: { type: 'string', description: 'Template generation' },
|
||||
templateGenerations: { type: 'string', description: 'Filter templates by generation' },
|
||||
templatePageSize: { type: 'number', description: 'Page size for listing templates' },
|
||||
versionName: { type: 'string', description: 'Template version name' },
|
||||
templateSubject: { type: 'string', description: 'Template subject' },
|
||||
htmlContent: { type: 'string', description: 'HTML content' },
|
||||
plainContent: { type: 'string', description: 'Plain text content' },
|
||||
active: { type: 'boolean', description: 'Whether template version is active' },
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
messageId: { type: 'string', description: 'Email message ID (send_mail)' },
|
||||
id: { type: 'string', description: 'Resource ID' },
|
||||
jobId: { type: 'string', description: 'Job ID for async operations' },
|
||||
email: { type: 'string', description: 'Email address' },
|
||||
firstName: { type: 'string', description: 'First name' },
|
||||
lastName: { type: 'string', description: 'Last name' },
|
||||
contacts: { type: 'json', description: 'Array of contacts' },
|
||||
contactCount: { type: 'number', description: 'Number of contacts' },
|
||||
lists: { type: 'json', description: 'Array of lists' },
|
||||
templates: { type: 'json', description: 'Array of templates' },
|
||||
message: { type: 'string', description: 'Status or success message' },
|
||||
name: { type: 'string', description: 'Resource name' },
|
||||
generation: { type: 'string', description: 'Template generation' },
|
||||
},
|
||||
}
|
||||
209
apps/sim/blocks/blocks/smtp.ts
Normal file
209
apps/sim/blocks/blocks/smtp.ts
Normal file
@@ -0,0 +1,209 @@
|
||||
import { SmtpIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import type { SmtpSendMailResult } from '@/tools/smtp/types'
|
||||
|
||||
export const SmtpBlock: BlockConfig<SmtpSendMailResult> = {
|
||||
type: 'smtp',
|
||||
name: 'SMTP',
|
||||
description: 'Send emails via any SMTP mail server',
|
||||
longDescription:
|
||||
'Send emails using any SMTP server (Gmail, Outlook, custom servers, etc.). Configure SMTP connection settings and send emails with full control over content, recipients, and attachments.',
|
||||
docsLink: 'https://docs.sim.ai/tools/smtp',
|
||||
category: 'tools',
|
||||
bgColor: '#4A5568',
|
||||
icon: SmtpIcon,
|
||||
authMode: AuthMode.ApiKey,
|
||||
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'smtpHost',
|
||||
title: 'SMTP Host',
|
||||
type: 'short-input',
|
||||
placeholder: 'smtp.gmail.com, smtp.example.com',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'smtpPort',
|
||||
title: 'SMTP Port',
|
||||
type: 'short-input',
|
||||
placeholder: '587',
|
||||
required: true,
|
||||
value: () => '587',
|
||||
},
|
||||
{
|
||||
id: 'smtpUsername',
|
||||
title: 'SMTP Username',
|
||||
type: 'short-input',
|
||||
placeholder: 'your-email@example.com',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'smtpPassword',
|
||||
title: 'SMTP Password',
|
||||
type: 'short-input',
|
||||
placeholder: 'Your SMTP password',
|
||||
required: true,
|
||||
password: true,
|
||||
},
|
||||
{
|
||||
id: 'smtpSecure',
|
||||
title: 'Security Mode',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'TLS (Port 587)', id: 'TLS' },
|
||||
{ label: 'SSL (Port 465)', id: 'SSL' },
|
||||
{ label: 'None (Port 25)', id: 'None' },
|
||||
],
|
||||
value: () => 'TLS',
|
||||
required: true,
|
||||
},
|
||||
|
||||
{
|
||||
id: 'from',
|
||||
title: 'From',
|
||||
type: 'short-input',
|
||||
placeholder: 'sender@example.com',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'to',
|
||||
title: 'To',
|
||||
type: 'short-input',
|
||||
placeholder: 'recipient@example.com',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'subject',
|
||||
title: 'Subject',
|
||||
type: 'short-input',
|
||||
placeholder: 'Email subject',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'body',
|
||||
title: 'Body',
|
||||
type: 'long-input',
|
||||
placeholder: 'Email content',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'contentType',
|
||||
title: 'Content Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Plain Text', id: 'text' },
|
||||
{ label: 'HTML', id: 'html' },
|
||||
],
|
||||
value: () => 'text',
|
||||
required: false,
|
||||
},
|
||||
|
||||
// Attachments Section
|
||||
// File upload (basic mode)
|
||||
{
|
||||
id: 'attachmentFiles',
|
||||
title: 'Attachments',
|
||||
type: 'file-upload',
|
||||
canonicalParamId: 'attachments',
|
||||
placeholder: 'Upload files to attach',
|
||||
mode: 'basic',
|
||||
multiple: true,
|
||||
required: false,
|
||||
},
|
||||
// Variable reference (advanced mode)
|
||||
{
|
||||
id: 'attachments',
|
||||
title: 'Attachments',
|
||||
type: 'short-input',
|
||||
canonicalParamId: 'attachments',
|
||||
placeholder: 'Reference files from previous blocks',
|
||||
mode: 'advanced',
|
||||
required: false,
|
||||
},
|
||||
|
||||
// Advanced Options Section
|
||||
{
|
||||
id: 'fromName',
|
||||
title: 'From Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Display name for sender',
|
||||
mode: 'advanced',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
id: 'cc',
|
||||
title: 'CC',
|
||||
type: 'short-input',
|
||||
placeholder: 'cc1@example.com, cc2@example.com',
|
||||
mode: 'advanced',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
id: 'bcc',
|
||||
title: 'BCC',
|
||||
type: 'short-input',
|
||||
placeholder: 'bcc1@example.com, bcc2@example.com',
|
||||
mode: 'advanced',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
id: 'replyTo',
|
||||
title: 'Reply To',
|
||||
type: 'short-input',
|
||||
placeholder: 'reply@example.com',
|
||||
mode: 'advanced',
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
|
||||
tools: {
|
||||
access: ['smtp_send_mail'],
|
||||
config: {
|
||||
tool: () => 'smtp_send_mail',
|
||||
params: (params) => ({
|
||||
smtpHost: params.smtpHost,
|
||||
smtpPort: Number(params.smtpPort),
|
||||
smtpUsername: params.smtpUsername,
|
||||
smtpPassword: params.smtpPassword,
|
||||
smtpSecure: params.smtpSecure,
|
||||
from: params.from,
|
||||
to: params.to,
|
||||
subject: params.subject,
|
||||
body: params.body,
|
||||
contentType: params.contentType,
|
||||
fromName: params.fromName,
|
||||
cc: params.cc,
|
||||
bcc: params.bcc,
|
||||
replyTo: params.replyTo,
|
||||
attachments: params.attachments,
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
||||
inputs: {
|
||||
smtpHost: { type: 'string', description: 'SMTP server hostname' },
|
||||
smtpPort: { type: 'number', description: 'SMTP server port' },
|
||||
smtpUsername: { type: 'string', description: 'SMTP authentication username' },
|
||||
smtpPassword: { type: 'string', description: 'SMTP authentication password' },
|
||||
smtpSecure: { type: 'string', description: 'Security protocol (TLS, SSL, or None)' },
|
||||
from: { type: 'string', description: 'Sender email address' },
|
||||
to: { type: 'string', description: 'Recipient email address' },
|
||||
subject: { type: 'string', description: 'Email subject' },
|
||||
body: { type: 'string', description: 'Email body content' },
|
||||
contentType: { type: 'string', description: 'Content type (text or html)' },
|
||||
fromName: { type: 'string', description: 'Display name for sender' },
|
||||
cc: { type: 'string', description: 'CC recipients (comma-separated)' },
|
||||
bcc: { type: 'string', description: 'BCC recipients (comma-separated)' },
|
||||
replyTo: { type: 'string', description: 'Reply-to email address' },
|
||||
attachments: { type: 'array', description: 'Files to attach (UserFile array)' },
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the email was sent successfully' },
|
||||
messageId: { type: 'string', description: 'Message ID from SMTP server' },
|
||||
to: { type: 'string', description: 'Recipient email address' },
|
||||
subject: { type: 'string', description: 'Email subject' },
|
||||
error: { type: 'string', description: 'Error message if sending failed' },
|
||||
},
|
||||
}
|
||||
@@ -41,8 +41,10 @@ import { JinaBlock } from '@/blocks/blocks/jina'
|
||||
import { JiraBlock } from '@/blocks/blocks/jira'
|
||||
import { KnowledgeBlock } from '@/blocks/blocks/knowledge'
|
||||
import { LinearBlock } from '@/blocks/blocks/linear'
|
||||
import { LinkedInBlock } from '@/blocks/blocks/linkedin'
|
||||
import { LinkupBlock } from '@/blocks/blocks/linkup'
|
||||
import { MailchimpBlock } from '@/blocks/blocks/mailchimp'
|
||||
import { MailgunBlock } from '@/blocks/blocks/mailgun'
|
||||
import { ManualTriggerBlock } from '@/blocks/blocks/manual_trigger'
|
||||
import { McpBlock } from '@/blocks/blocks/mcp'
|
||||
import { Mem0Block } from '@/blocks/blocks/mem0'
|
||||
@@ -74,10 +76,12 @@ import { RouterBlock } from '@/blocks/blocks/router'
|
||||
import { S3Block } from '@/blocks/blocks/s3'
|
||||
import { SalesforceBlock } from '@/blocks/blocks/salesforce'
|
||||
import { ScheduleBlock } from '@/blocks/blocks/schedule'
|
||||
import { SendGridBlock } from '@/blocks/blocks/sendgrid'
|
||||
import { SentryBlock } from '@/blocks/blocks/sentry'
|
||||
import { SerperBlock } from '@/blocks/blocks/serper'
|
||||
import { SharepointBlock } from '@/blocks/blocks/sharepoint'
|
||||
import { SlackBlock } from '@/blocks/blocks/slack'
|
||||
import { SmtpBlock } from '@/blocks/blocks/smtp'
|
||||
import { StagehandBlock } from '@/blocks/blocks/stagehand'
|
||||
import { StagehandAgentBlock } from '@/blocks/blocks/stagehand_agent'
|
||||
import { StartTriggerBlock } from '@/blocks/blocks/start_trigger'
|
||||
@@ -152,6 +156,7 @@ export const registry: Record<string, BlockConfig> = {
|
||||
jira: JiraBlock,
|
||||
knowledge: KnowledgeBlock,
|
||||
linear: LinearBlock,
|
||||
linkedin: LinkedInBlock,
|
||||
linkup: LinkupBlock,
|
||||
mailchimp: MailchimpBlock,
|
||||
mcp: McpBlock,
|
||||
@@ -178,6 +183,9 @@ export const registry: Record<string, BlockConfig> = {
|
||||
pylon: PylonBlock,
|
||||
qdrant: QdrantBlock,
|
||||
resend: ResendBlock,
|
||||
sendgrid: SendGridBlock,
|
||||
mailgun: MailgunBlock,
|
||||
smtp: SmtpBlock,
|
||||
memory: MemoryBlock,
|
||||
reddit: RedditBlock,
|
||||
response: ResponseBlock,
|
||||
|
||||
@@ -1464,11 +1464,17 @@ export function DiscordIcon(props: SVGProps<SVGSVGElement>) {
|
||||
|
||||
export function LinkedInIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='4 4 42 42' width='1em' height='1em'>
|
||||
<path
|
||||
fill='currentColor'
|
||||
d='M41,4H9C6.24,4,4,6.24,4,9v32c0,2.76,2.24,5,5,5h32c2.76,0,5-2.24,5-5V9C46,6.24,43.76,4,41,4z M17,20v19h-6V20H17z M11,14.47c0-1.4,1.2-2.47,3-2.47s2.93,1.07,3,2.47c0,1.4-1.12,2.53-3,2.53C12.2,17,11,15.87,11,14.47z M39,39h-6c0,0,0-9.26,0-10 c0-2-1-4-3.5-4.04h-0.08C27,24.96,26,27.02,26,29c0,0.91,0,10,0,10h-6V20h6v2.56c0,0,1.93-2.56,5.81-2.56 c3.97,0,7.19,2.73,7.19,8.26V39z'
|
||||
/>
|
||||
<svg {...props} height='72' viewBox='0 0 72 72' width='72' xmlns='http://www.w3.org/2000/svg'>
|
||||
<g fill='none' fillRule='evenodd'>
|
||||
<path
|
||||
d='M8,72 L64,72 C68.418278,72 72,68.418278 72,64 L72,8 C72,3.581722 68.418278,-8.11624501e-16 64,0 L8,0 C3.581722,8.11624501e-16 -5.41083001e-16,3.581722 0,8 L0,64 C5.41083001e-16,68.418278 3.581722,72 8,72 Z'
|
||||
fill='#0072B1'
|
||||
/>
|
||||
<path
|
||||
d='M62,62 L51.315625,62 L51.315625,43.8021149 C51.315625,38.8127542 49.4197917,36.0245323 45.4707031,36.0245323 C41.1746094,36.0245323 38.9300781,38.9261103 38.9300781,43.8021149 L38.9300781,62 L28.6333333,62 L28.6333333,27.3333333 L38.9300781,27.3333333 L38.9300781,32.0029283 C38.9300781,32.0029283 42.0260417,26.2742151 49.3825521,26.2742151 C56.7356771,26.2742151 62,30.7644705 62,40.051212 L62,62 Z M16.349349,22.7940133 C12.8420573,22.7940133 10,19.9296567 10,16.3970067 C10,12.8643566 12.8420573,10 16.349349,10 C19.8566406,10 22.6970052,12.8643566 22.6970052,16.3970067 C22.6970052,19.9296567 19.8566406,22.7940133 16.349349,22.7940133 Z M11.0325521,62 L21.769401,62 L21.769401,27.3333333 L11.0325521,27.3333333 L11.0325521,62 Z'
|
||||
fill='#FFF'
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -4344,3 +4350,81 @@ export function PylonIcon(props: SVGProps<SVGSVGElement>) {
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function SendgridIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
width='800px'
|
||||
height='800px'
|
||||
viewBox='0 0 256 256'
|
||||
version='1.1'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
xmlnsXlink='http://www.w3.org/1999/xlink'
|
||||
preserveAspectRatio='xMidYMid'
|
||||
>
|
||||
<g>
|
||||
<path
|
||||
d='M256.000405,0 L256.000405,170.666936 L170.666936,170.666936 L170.666936,255.996382 L0.00201096905,255.996382 L0.002,170.666 L0,170.666936 L0,85.3314569 L85.3334681,85.3314569 L85.3334681,0 L256.000405,0 Z'
|
||||
fill='#9DD6E3'
|
||||
/>
|
||||
<polygon
|
||||
fill='#3F72AB'
|
||||
points='0.00201096905 255.996382 85.3354791 255.996382 85.3354791 170.662915 0.00201096905 170.662915'
|
||||
/>
|
||||
<polygon
|
||||
fill='#00A9D1'
|
||||
points='170.666936 170.666936 256.000405 170.666936 256.000405 85.3314569 170.666936 85.3314569'
|
||||
/>
|
||||
<polygon
|
||||
fill='#00A9D1'
|
||||
points='85.3334681 85.3334679 170.666936 85.3334679 170.666936 0 85.3334681 0'
|
||||
/>
|
||||
<polygon
|
||||
fill='#2191C4'
|
||||
points='85.3334681 170.664925 170.666936 170.664925 170.666936 85.3314569 85.3334681 85.3314569'
|
||||
/>
|
||||
<polygon
|
||||
fill='#3F72AB'
|
||||
points='170.666936 85.3334679 256.000405 85.3334679 256.000405 0 170.666936 0'
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function MailgunIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
fill='currentColor'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
xmlSpace='preserve'
|
||||
viewBox='0 0 512 512'
|
||||
>
|
||||
<path d='M256.5 159.5c-53.5 0-97 43.5-97 97s43.5 97 97 97 97-43.5 97-97-43.5-97-97-97m-151.1 97c0-83.4 67.7-151.1 151.1-151.1s151.1 67.7 151.1 151.1c0 5.8-.5 11-1 16.3-1 14.7 9.4 25.7 24.1 25.7 24.7 0 27.3-32 27.3-42.5 0-111.7-90.2-202-202-202S54 144.3 54 256s90.2 202 202 202c59.3 0 112.3-25.7 149.5-66.1l41.4 34.6C400.3 479 332.1 512 256 512 114.4 512 0 397.1 0 256 0 114.4 114.9 0 256 0c141.6 0 256 114.9 256 256 0 56.7-27.3 102.8-81.3 102.8-24.1 0-38.3-11-46.7-23.1-26.8 43-74 71.3-128.5 71.3-82.4.6-150.1-67.1-150.1-150.5m151.1-44.6c24.7 0 44.6 19.9 44.6 44.1 0 24.7-19.9 44.6-44.6 44.6s-44.6-19.9-44.6-44.6c.6-24.1 20-44.1 44.6-44.1' />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function SmtpIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
width='30'
|
||||
height='24'
|
||||
viewBox='0 0 30 24'
|
||||
fill='none'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
>
|
||||
<path
|
||||
d='M2.35742 5.83288L11.7674 12.1071C13.0656 12.9712 13.7141 13.404 14.4151 13.5725C15.0352 13.7208 15.681 13.7208 16.2998 13.5725C17.0008 13.404 17.6492 12.9712 18.9475 12.1071L28.3574 5.83288M8.82844 21.7219H21.8864C24.1513 21.7219 25.2837 21.7219 26.1492 21.2811C26.9097 20.8931 27.5278 20.2744 27.9152 19.5137C28.3574 18.6482 28.3574 17.5158 28.3574 15.2509V7.97102C28.3574 5.70616 28.3574 4.57373 27.9166 3.70823C27.5288 2.94727 26.9102 2.32858 26.1492 1.94084C25.2837 1.5 24.1513 1.5 21.8864 1.5H8.82844C6.56358 1.5 5.43115 1.5 4.56566 1.94084C3.80519 2.32881 3.187 2.94747 2.79961 3.70823C2.35742 4.57373 2.35742 5.70616 2.35742 7.97102V15.2509C2.35742 17.5158 2.35742 18.6482 2.79826 19.5137C3.186 20.2747 3.80469 20.8933 4.56566 21.2811C5.43115 21.7219 6.56358 21.7219 8.82844 21.7219Z'
|
||||
stroke='currentColor'
|
||||
strokeWidth='2.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<circle cx='24' cy='6' r='4' fill='currentColor' stroke='none' />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -157,6 +157,7 @@ export const auth = betterAuth({
|
||||
'asana',
|
||||
'pipedrive',
|
||||
'hubspot',
|
||||
'linkedin',
|
||||
|
||||
// Common SSO provider patterns
|
||||
...SSO_TRUSTED_PROVIDERS,
|
||||
@@ -1585,6 +1586,54 @@ export const auth = betterAuth({
|
||||
}
|
||||
},
|
||||
},
|
||||
// LinkedIn provider
|
||||
{
|
||||
providerId: 'linkedin',
|
||||
clientId: env.LINKEDIN_CLIENT_ID as string,
|
||||
clientSecret: env.LINKEDIN_CLIENT_SECRET as string,
|
||||
authorizationUrl: 'https://www.linkedin.com/oauth/v2/authorization',
|
||||
tokenUrl: 'https://www.linkedin.com/oauth/v2/accessToken',
|
||||
userInfoUrl: 'https://api.linkedin.com/v2/userinfo',
|
||||
scopes: ['profile', 'openid', 'email', 'w_member_social'],
|
||||
responseType: 'code',
|
||||
accessType: 'offline',
|
||||
prompt: 'consent',
|
||||
redirectURI: `${getBaseUrl()}/api/auth/oauth2/callback/linkedin`,
|
||||
getUserInfo: async (tokens) => {
|
||||
try {
|
||||
logger.info('Fetching LinkedIn user profile')
|
||||
|
||||
const response = await fetch('https://api.linkedin.com/v2/userinfo', {
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens.accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
logger.error('Failed to fetch LinkedIn user info', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
})
|
||||
throw new Error('Failed to fetch user info')
|
||||
}
|
||||
|
||||
const profile = await response.json()
|
||||
|
||||
return {
|
||||
id: profile.sub,
|
||||
name: profile.name || 'LinkedIn User',
|
||||
email: profile.email || `${profile.sub}@linkedin.user`,
|
||||
emailVerified: profile.email_verified || true,
|
||||
image: profile.picture || undefined,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Error in LinkedIn getUserInfo:', { error })
|
||||
return null
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
// Include SSO plugin when enabled
|
||||
|
||||
@@ -217,6 +217,8 @@ export const env = createEnv({
|
||||
WEBFLOW_CLIENT_ID: z.string().optional(), // Webflow OAuth client ID
|
||||
WEBFLOW_CLIENT_SECRET: z.string().optional(), // Webflow OAuth client secret
|
||||
TRELLO_API_KEY: z.string().optional(), // Trello API Key
|
||||
LINKEDIN_CLIENT_ID: z.string().optional(), // LinkedIn OAuth client ID
|
||||
LINKEDIN_CLIENT_SECRET: z.string().optional(), // LinkedIn OAuth client secret
|
||||
|
||||
// E2B Remote Code Execution
|
||||
E2B_ENABLED: z.string().optional(), // Enable E2B remote code execution
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
HubspotIcon,
|
||||
JiraIcon,
|
||||
LinearIcon,
|
||||
LinkedInIcon,
|
||||
MicrosoftExcelIcon,
|
||||
MicrosoftIcon,
|
||||
MicrosoftOneDriveIcon,
|
||||
@@ -59,6 +60,7 @@ export type OAuthProvider =
|
||||
| 'pipedrive'
|
||||
| 'hubspot'
|
||||
| 'salesforce'
|
||||
| 'linkedin'
|
||||
| string
|
||||
|
||||
export type OAuthService =
|
||||
@@ -94,6 +96,7 @@ export type OAuthService =
|
||||
| 'pipedrive'
|
||||
| 'hubspot'
|
||||
| 'salesforce'
|
||||
| 'linkedin'
|
||||
export interface OAuthProviderConfig {
|
||||
id: OAuthProvider
|
||||
name: string
|
||||
@@ -732,6 +735,23 @@ export const OAUTH_PROVIDERS: Record<string, OAuthProviderConfig> = {
|
||||
},
|
||||
defaultService: 'hubspot',
|
||||
},
|
||||
linkedin: {
|
||||
id: 'linkedin',
|
||||
name: 'LinkedIn',
|
||||
icon: (props) => LinkedInIcon(props),
|
||||
services: {
|
||||
linkedin: {
|
||||
id: 'linkedin',
|
||||
name: 'LinkedIn',
|
||||
description: 'Share posts and access profile data on LinkedIn.',
|
||||
providerId: 'linkedin',
|
||||
icon: (props) => LinkedInIcon(props),
|
||||
baseProviderIcon: (props) => LinkedInIcon(props),
|
||||
scopes: ['profile', 'openid', 'email', 'w_member_social'],
|
||||
},
|
||||
},
|
||||
defaultService: 'linkedin',
|
||||
},
|
||||
salesforce: {
|
||||
id: 'salesforce',
|
||||
name: 'Salesforce',
|
||||
@@ -1219,6 +1239,19 @@ function getProviderAuthConfig(provider: string): ProviderAuthConfig {
|
||||
supportsRefreshTokenRotation: true,
|
||||
}
|
||||
}
|
||||
case 'linkedin': {
|
||||
const { clientId, clientSecret } = getCredentials(
|
||||
env.LINKEDIN_CLIENT_ID,
|
||||
env.LINKEDIN_CLIENT_SECRET
|
||||
)
|
||||
return {
|
||||
tokenEndpoint: 'https://www.linkedin.com/oauth/v2/accessToken',
|
||||
clientId,
|
||||
clientSecret,
|
||||
useBasicAuth: false,
|
||||
supportsRefreshTokenRotation: false,
|
||||
}
|
||||
}
|
||||
case 'salesforce': {
|
||||
const { clientId, clientSecret } = getCredentials(
|
||||
env.SALESFORCE_CLIENT_ID,
|
||||
|
||||
56
apps/sim/tools/linkedin/get_profile.ts
Normal file
56
apps/sim/tools/linkedin/get_profile.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import type { GetProfileParams, GetProfileResponse } from '@/tools/linkedin/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const linkedInGetProfileTool: ToolConfig<GetProfileParams, GetProfileResponse> = {
|
||||
id: 'linkedin_get_profile',
|
||||
name: 'Get LinkedIn Profile',
|
||||
description: 'Retrieve your LinkedIn profile information',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'linkedin',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Access token for LinkedIn API',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => 'https://api.linkedin.com/v2/userinfo',
|
||||
method: 'GET',
|
||||
headers: (params: GetProfileParams): Record<string, string> => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'X-Restli-Protocol-Version': '2.0.0',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response): Promise<GetProfileResponse> => {
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
output: {},
|
||||
error: `Failed to get profile: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const profile = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
profile: {
|
||||
id: profile.sub,
|
||||
name: profile.name,
|
||||
email: profile.email,
|
||||
picture: profile.picture,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
2
apps/sim/tools/linkedin/index.ts
Normal file
2
apps/sim/tools/linkedin/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { linkedInGetProfileTool } from './get_profile'
|
||||
export { linkedInSharePostTool } from './share_post'
|
||||
157
apps/sim/tools/linkedin/share_post.ts
Normal file
157
apps/sim/tools/linkedin/share_post.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
import type {
|
||||
LinkedInProfileOutput,
|
||||
ProfileIdExtractor,
|
||||
SharePostParams,
|
||||
SharePostResponse,
|
||||
} from '@/tools/linkedin/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
// Helper function to extract profile ID from various response formats
|
||||
const extractProfileId: ProfileIdExtractor = (output: unknown): string | null => {
|
||||
if (typeof output === 'object' && output !== null) {
|
||||
const profileOutput = output as LinkedInProfileOutput
|
||||
return profileOutput.profile?.id || profileOutput.sub || profileOutput.id || null
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export const linkedInSharePostTool: ToolConfig<SharePostParams, SharePostResponse> = {
|
||||
id: 'linkedin_share_post',
|
||||
name: 'Share Post on LinkedIn',
|
||||
description: 'Share a post to your personal LinkedIn feed',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'linkedin',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Access token for LinkedIn API',
|
||||
},
|
||||
text: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The text content of your LinkedIn post',
|
||||
},
|
||||
visibility: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Who can see this post: "PUBLIC" or "CONNECTIONS" (default: "PUBLIC")',
|
||||
},
|
||||
},
|
||||
|
||||
// First request: Get user profile to obtain the person URN
|
||||
request: {
|
||||
url: () => 'https://api.linkedin.com/v2/userinfo',
|
||||
method: 'GET',
|
||||
headers: (params: SharePostParams) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'X-Restli-Protocol-Version': '2.0.0',
|
||||
}),
|
||||
},
|
||||
|
||||
// Use postProcess to make the actual post creation request
|
||||
postProcess: async (profileResult, params, executeTool) => {
|
||||
try {
|
||||
// Extract profile from the first request
|
||||
if (!profileResult.success || !profileResult.output) {
|
||||
return {
|
||||
success: false,
|
||||
output: {},
|
||||
error: 'Failed to fetch user profile',
|
||||
}
|
||||
}
|
||||
|
||||
// Get profile data from output
|
||||
const profileOutput = profileResult.output as LinkedInProfileOutput
|
||||
const authorId = extractProfileId(profileOutput)
|
||||
|
||||
if (!authorId) {
|
||||
return {
|
||||
success: false,
|
||||
output: {},
|
||||
error: 'Could not extract LinkedIn profile ID from response',
|
||||
}
|
||||
}
|
||||
|
||||
const authorUrn = `urn:li:person:${authorId}`
|
||||
|
||||
// Create the post
|
||||
const postData = {
|
||||
author: authorUrn,
|
||||
lifecycleState: 'PUBLISHED',
|
||||
specificContent: {
|
||||
'com.linkedin.ugc.ShareContent': {
|
||||
shareCommentary: {
|
||||
text: params.text,
|
||||
},
|
||||
shareMediaCategory: 'NONE',
|
||||
},
|
||||
},
|
||||
visibility: {
|
||||
'com.linkedin.ugc.MemberNetworkVisibility': params.visibility || 'PUBLIC',
|
||||
},
|
||||
}
|
||||
|
||||
const response = await fetch('https://api.linkedin.com/v2/ugcPosts', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
'X-Restli-Protocol-Version': '2.0.0',
|
||||
},
|
||||
body: JSON.stringify(postData),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.text()
|
||||
return {
|
||||
success: false,
|
||||
output: {},
|
||||
error: `LinkedIn API error: ${error}`,
|
||||
}
|
||||
}
|
||||
|
||||
const result = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
postId: result.id,
|
||||
},
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
output: {},
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response): Promise<SharePostResponse> => {
|
||||
// This handles the initial profile fetch response
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
output: {},
|
||||
error: `Failed to fetch profile: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const profile = await response.json()
|
||||
|
||||
// Return profile data for postProcess to use
|
||||
return {
|
||||
success: true,
|
||||
output: profile,
|
||||
}
|
||||
},
|
||||
}
|
||||
89
apps/sim/tools/linkedin/types.ts
Normal file
89
apps/sim/tools/linkedin/types.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface LinkedInProfile {
|
||||
sub: string
|
||||
name: string
|
||||
given_name: string
|
||||
family_name: string
|
||||
email?: string
|
||||
picture?: string
|
||||
email_verified?: boolean
|
||||
}
|
||||
|
||||
export interface LinkedInPost {
|
||||
author: string // URN format: urn:li:person:abc123
|
||||
lifecycleState: 'PUBLISHED'
|
||||
specificContent: {
|
||||
'com.linkedin.ugc.ShareContent': {
|
||||
shareCommentary: {
|
||||
text: string
|
||||
}
|
||||
shareMediaCategory: 'NONE' | 'ARTICLE' | 'IMAGE'
|
||||
media?: Array<{
|
||||
status: 'READY'
|
||||
description: {
|
||||
text: string
|
||||
}
|
||||
media: string // URN format
|
||||
title: {
|
||||
text: string
|
||||
}
|
||||
}>
|
||||
}
|
||||
}
|
||||
visibility: {
|
||||
'com.linkedin.ugc.MemberNetworkVisibility': 'PUBLIC' | 'CONNECTIONS'
|
||||
}
|
||||
}
|
||||
|
||||
export type LinkedInResponse = {
|
||||
success: boolean
|
||||
output: {
|
||||
postId?: string
|
||||
profile?: {
|
||||
id: string
|
||||
name: string
|
||||
email?: string
|
||||
picture?: string
|
||||
}
|
||||
}
|
||||
error?: string
|
||||
}
|
||||
|
||||
// Tool-specific type definitions
|
||||
export interface LinkedInProfileOutput {
|
||||
profile?: {
|
||||
id: string
|
||||
name?: string
|
||||
email?: string
|
||||
picture?: string
|
||||
}
|
||||
sub?: string
|
||||
id?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export interface SharePostParams {
|
||||
accessToken: string
|
||||
text: string
|
||||
visibility?: 'PUBLIC' | 'CONNECTIONS' | 'LOGGED_IN'
|
||||
mediaUrls?: string
|
||||
}
|
||||
|
||||
export interface SharePostResponse extends ToolResponse {
|
||||
output: {
|
||||
postId?: string
|
||||
postUrl?: string
|
||||
visibility?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface GetProfileParams {
|
||||
accessToken: string
|
||||
}
|
||||
|
||||
export interface GetProfileResponse extends ToolResponse {
|
||||
output: LinkedInProfileOutput
|
||||
}
|
||||
|
||||
export type ProfileIdExtractor = (output: unknown) => string | null
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError, type MailchimpMember } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpAddMember')
|
||||
|
||||
@@ -16,7 +16,7 @@ export interface MailchimpAddMemberParams {
|
||||
export interface MailchimpAddMemberResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
member: any
|
||||
member: MailchimpMember
|
||||
metadata: {
|
||||
operation: 'add_member'
|
||||
subscriberHash: string
|
||||
@@ -81,7 +81,7 @@ export const mailchimpAddMemberTool: ToolConfig<
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: any = {
|
||||
const body: Record<string, unknown> = {
|
||||
email_address: params.emailAddress,
|
||||
status: params.status,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError, type MailchimpMember } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpAddOrUpdateMember')
|
||||
|
||||
@@ -17,7 +17,7 @@ export interface MailchimpAddOrUpdateMemberParams {
|
||||
export interface MailchimpAddOrUpdateMemberResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
member: any
|
||||
member: MailchimpMember
|
||||
metadata: {
|
||||
operation: 'add_or_update_member'
|
||||
subscriberHash: string
|
||||
@@ -89,7 +89,7 @@ export const mailchimpAddOrUpdateMemberTool: ToolConfig<
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: any = {
|
||||
const body: Record<string, unknown> = {
|
||||
email_address: params.emailAddress,
|
||||
status_if_new: params.statusIfNew,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError, type MailchimpMember } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpAddSegmentMember')
|
||||
|
||||
@@ -14,7 +14,7 @@ export interface MailchimpAddSegmentMemberParams {
|
||||
export interface MailchimpAddSegmentMemberResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
member: any
|
||||
member: MailchimpMember
|
||||
metadata: {
|
||||
operation: 'add_segment_member'
|
||||
segmentId: string
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpMember } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpAddSubscriberToAutomation')
|
||||
@@ -14,7 +15,7 @@ export interface MailchimpAddSubscriberToAutomationParams {
|
||||
export interface MailchimpAddSubscriberToAutomationResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
subscriber: any
|
||||
subscriber: MailchimpMember
|
||||
metadata: {
|
||||
operation: 'add_subscriber_to_automation'
|
||||
workflowId: string
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpAudience } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpCreateAudience')
|
||||
@@ -16,7 +17,7 @@ export interface MailchimpCreateAudienceParams {
|
||||
export interface MailchimpCreateAudienceResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
list: any
|
||||
list: MailchimpAudience
|
||||
metadata: {
|
||||
operation: 'create_audience'
|
||||
listId: string
|
||||
@@ -81,7 +82,7 @@ export const mailchimpCreateAudienceTool: ToolConfig<
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: any = {
|
||||
const body: Record<string, unknown> = {
|
||||
name: params.audienceName,
|
||||
permission_reminder: params.permissionReminder,
|
||||
email_type_option: params.emailTypeOption === 'true',
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpBatchOperation } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpCreateBatchOperation')
|
||||
@@ -12,7 +13,7 @@ export interface MailchimpCreateBatchOperationParams {
|
||||
export interface MailchimpCreateBatchOperationResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
batch: any
|
||||
batch: MailchimpBatchOperation
|
||||
metadata: {
|
||||
operation: 'create_batch_operation'
|
||||
batchId: string
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError, type MailchimpCampaign } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpCreateCampaign')
|
||||
|
||||
@@ -14,7 +14,7 @@ export interface MailchimpCreateCampaignParams {
|
||||
export interface MailchimpCreateCampaignResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
campaign: any
|
||||
campaign: MailchimpCampaign
|
||||
metadata: {
|
||||
operation: 'create_campaign'
|
||||
campaignId: string
|
||||
@@ -67,7 +67,7 @@ export const mailchimpCreateCampaignTool: ToolConfig<
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: any = {
|
||||
const body: Record<string, unknown> = {
|
||||
type: params.campaignType,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpInterest } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpCreateInterest')
|
||||
@@ -14,7 +15,7 @@ export interface MailchimpCreateInterestParams {
|
||||
export interface MailchimpCreateInterestResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
interest: any
|
||||
interest: MailchimpInterest
|
||||
metadata: {
|
||||
operation: 'create_interest'
|
||||
interestId: string
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpInterestCategory } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpCreateInterestCategory')
|
||||
@@ -14,7 +15,7 @@ export interface MailchimpCreateInterestCategoryParams {
|
||||
export interface MailchimpCreateInterestCategoryResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
category: any
|
||||
category: MailchimpInterestCategory
|
||||
metadata: {
|
||||
operation: 'create_interest_category'
|
||||
interestCategoryId: string
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpLandingPage } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpCreateLandingPage')
|
||||
@@ -13,7 +14,7 @@ export interface MailchimpCreateLandingPageParams {
|
||||
export interface MailchimpCreateLandingPageResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
landingPage: any
|
||||
landingPage: MailchimpLandingPage
|
||||
metadata: {
|
||||
operation: 'create_landing_page'
|
||||
pageId: string
|
||||
@@ -60,7 +61,7 @@ export const mailchimpCreateLandingPageTool: ToolConfig<
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: any = {
|
||||
const body: Record<string, unknown> = {
|
||||
type: params.landingPageType,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpMergeField } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpCreateMergeField')
|
||||
@@ -14,7 +15,7 @@ export interface MailchimpCreateMergeFieldParams {
|
||||
export interface MailchimpCreateMergeFieldResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
mergeField: any
|
||||
mergeField: MailchimpMergeField
|
||||
metadata: {
|
||||
operation: 'create_merge_field'
|
||||
mergeId: string
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpSegment } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpCreateSegment')
|
||||
@@ -14,7 +15,7 @@ export interface MailchimpCreateSegmentParams {
|
||||
export interface MailchimpCreateSegmentResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
segment: any
|
||||
segment: MailchimpSegment
|
||||
metadata: {
|
||||
operation: 'create_segment'
|
||||
segmentId: string
|
||||
@@ -67,7 +68,7 @@ export const mailchimpCreateSegmentTool: ToolConfig<
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: any = {
|
||||
const body: Record<string, unknown> = {
|
||||
name: params.segmentName,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpAudience } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpGetAudience')
|
||||
@@ -12,7 +13,7 @@ export interface MailchimpGetAudienceParams {
|
||||
export interface MailchimpGetAudienceResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
list: any
|
||||
list: MailchimpAudience
|
||||
metadata: {
|
||||
operation: 'get_audience'
|
||||
listId: string
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpAudience } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpGetAudiences')
|
||||
@@ -13,7 +14,7 @@ export interface MailchimpGetAudiencesParams {
|
||||
export interface MailchimpGetAudiencesResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
lists: any[]
|
||||
lists: MailchimpAudience[]
|
||||
totalItems: number
|
||||
metadata: {
|
||||
operation: 'get_audiences'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpAutomation } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpGetAutomation')
|
||||
@@ -12,7 +13,7 @@ export interface MailchimpGetAutomationParams {
|
||||
export interface MailchimpGetAutomationResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
automation: any
|
||||
automation: MailchimpAutomation
|
||||
metadata: {
|
||||
operation: 'get_automation'
|
||||
workflowId: string
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpAutomation } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpGetAutomations')
|
||||
@@ -13,7 +14,7 @@ export interface MailchimpGetAutomationsParams {
|
||||
export interface MailchimpGetAutomationsResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
automations: any[]
|
||||
automations: MailchimpAutomation[]
|
||||
totalItems: number
|
||||
metadata: {
|
||||
operation: 'get_automations'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpBatchOperation } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpGetBatchOperation')
|
||||
@@ -12,7 +13,7 @@ export interface MailchimpGetBatchOperationParams {
|
||||
export interface MailchimpGetBatchOperationResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
batch: any
|
||||
batch: MailchimpBatchOperation
|
||||
metadata: {
|
||||
operation: 'get_batch_operation'
|
||||
batchId: string
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpBatchOperation } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpGetBatchOperations')
|
||||
@@ -13,7 +14,7 @@ export interface MailchimpGetBatchOperationsParams {
|
||||
export interface MailchimpGetBatchOperationsResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
batches: any[]
|
||||
batches: MailchimpBatchOperation[]
|
||||
totalItems: number
|
||||
metadata: {
|
||||
operation: 'get_batch_operations'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError, type MailchimpCampaign } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpGetCampaign')
|
||||
|
||||
@@ -12,7 +12,7 @@ export interface MailchimpGetCampaignParams {
|
||||
export interface MailchimpGetCampaignResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
campaign: any
|
||||
campaign: MailchimpCampaign
|
||||
metadata: {
|
||||
operation: 'get_campaign'
|
||||
campaignId: string
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError, type MailchimpCampaignContent } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpGetCampaignContent')
|
||||
|
||||
@@ -12,7 +12,7 @@ export interface MailchimpGetCampaignContentParams {
|
||||
export interface MailchimpGetCampaignContentResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: any
|
||||
content: MailchimpCampaignContent
|
||||
metadata: {
|
||||
operation: 'get_campaign_content'
|
||||
campaignId: string
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError, type MailchimpCampaignReport } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpGetCampaignReport')
|
||||
|
||||
@@ -12,7 +12,7 @@ export interface MailchimpGetCampaignReportParams {
|
||||
export interface MailchimpGetCampaignReportResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
report: any
|
||||
report: MailchimpCampaignReport
|
||||
metadata: {
|
||||
operation: 'get_campaign_report'
|
||||
campaignId: string
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError, type MailchimpCampaignReport } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpGetCampaignReports')
|
||||
|
||||
@@ -13,7 +13,7 @@ export interface MailchimpGetCampaignReportsParams {
|
||||
export interface MailchimpGetCampaignReportsResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
reports: any[]
|
||||
reports: MailchimpCampaignReport[]
|
||||
totalItems: number
|
||||
metadata: {
|
||||
operation: 'get_campaign_reports'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError, type MailchimpCampaign } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpGetCampaigns')
|
||||
|
||||
@@ -15,7 +15,7 @@ export interface MailchimpGetCampaignsParams {
|
||||
export interface MailchimpGetCampaignsResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
campaigns: any[]
|
||||
campaigns: MailchimpCampaign[]
|
||||
totalItems: number
|
||||
metadata: {
|
||||
operation: 'get_campaigns'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpInterest } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpGetInterest')
|
||||
@@ -14,7 +15,7 @@ export interface MailchimpGetInterestParams {
|
||||
export interface MailchimpGetInterestResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
interest: any
|
||||
interest: MailchimpInterest
|
||||
metadata: {
|
||||
operation: 'get_interest'
|
||||
interestId: string
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpInterestCategory } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpGetInterestCategory')
|
||||
@@ -13,7 +14,7 @@ export interface MailchimpGetInterestCategoryParams {
|
||||
export interface MailchimpGetInterestCategoryResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
category: any
|
||||
category: MailchimpInterestCategory
|
||||
metadata: {
|
||||
operation: 'get_interest_category'
|
||||
interestCategoryId: string
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpLandingPage } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpGetLandingPage')
|
||||
@@ -12,7 +13,7 @@ export interface MailchimpGetLandingPageParams {
|
||||
export interface MailchimpGetLandingPageResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
landingPage: any
|
||||
landingPage: MailchimpLandingPage
|
||||
metadata: {
|
||||
operation: 'get_landing_page'
|
||||
pageId: string
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpLandingPage } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpGetLandingPages')
|
||||
@@ -13,7 +14,7 @@ export interface MailchimpGetLandingPagesParams {
|
||||
export interface MailchimpGetLandingPagesResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
landingPages: any[]
|
||||
landingPages: MailchimpLandingPage[]
|
||||
totalItems: number
|
||||
metadata: {
|
||||
operation: 'get_landing_pages'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError, type MailchimpMember } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpGetMember')
|
||||
|
||||
@@ -13,7 +13,7 @@ export interface MailchimpGetMemberParams {
|
||||
export interface MailchimpGetMemberResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
member: any
|
||||
member: MailchimpMember
|
||||
metadata: {
|
||||
operation: 'get_member'
|
||||
subscriberHash: string
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError, type MailchimpTag } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpGetMemberTags')
|
||||
|
||||
@@ -13,7 +13,7 @@ export interface MailchimpGetMemberTagsParams {
|
||||
export interface MailchimpGetMemberTagsResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
tags: any[]
|
||||
tags: MailchimpTag[]
|
||||
totalItems: number
|
||||
metadata: {
|
||||
operation: 'get_member_tags'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError, type MailchimpMember } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpGetMembers')
|
||||
|
||||
@@ -15,7 +15,7 @@ export interface MailchimpGetMembersParams {
|
||||
export interface MailchimpGetMembersResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
members: any[]
|
||||
members: MailchimpMember[]
|
||||
totalItems: number
|
||||
metadata: {
|
||||
operation: 'get_members'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpMergeField } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpGetMergeFields')
|
||||
@@ -14,7 +15,7 @@ export interface MailchimpGetMergeFieldsParams {
|
||||
export interface MailchimpGetMergeFieldsResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
mergeFields: any[]
|
||||
mergeFields: MailchimpMergeField[]
|
||||
totalItems: number
|
||||
metadata: {
|
||||
operation: 'get_merge_fields'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpSegment } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpGetSegment')
|
||||
@@ -13,7 +14,7 @@ export interface MailchimpGetSegmentParams {
|
||||
export interface MailchimpGetSegmentResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
segment: any
|
||||
segment: MailchimpSegment
|
||||
metadata: {
|
||||
operation: 'get_segment'
|
||||
segmentId: string
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpMember } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpGetSegmentMembers')
|
||||
@@ -15,7 +16,7 @@ export interface MailchimpGetSegmentMembersParams {
|
||||
export interface MailchimpGetSegmentMembersResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
members: any[]
|
||||
members: MailchimpMember[]
|
||||
totalItems: number
|
||||
metadata: {
|
||||
operation: 'get_segment_members'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpTemplate } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpGetTemplate')
|
||||
@@ -12,7 +13,7 @@ export interface MailchimpGetTemplateParams {
|
||||
export interface MailchimpGetTemplateResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
template: any
|
||||
template: MailchimpTemplate
|
||||
metadata: {
|
||||
operation: 'get_template'
|
||||
templateId: string
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpTemplate } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpGetTemplates')
|
||||
@@ -13,7 +14,7 @@ export interface MailchimpGetTemplatesParams {
|
||||
export interface MailchimpGetTemplatesResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
templates: any[]
|
||||
templates: MailchimpTemplate[]
|
||||
totalItems: number
|
||||
metadata: {
|
||||
operation: 'get_templates'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError, type MailchimpCampaignContent } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpSetCampaignContent')
|
||||
|
||||
@@ -15,7 +15,7 @@ export interface MailchimpSetCampaignContentParams {
|
||||
export interface MailchimpSetCampaignContentResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
content: any
|
||||
content: MailchimpCampaignContent
|
||||
metadata: {
|
||||
operation: 'set_campaign_content'
|
||||
campaignId: string
|
||||
@@ -74,7 +74,7 @@ export const mailchimpSetCampaignContentTool: ToolConfig<
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: any = {}
|
||||
const body: Record<string, unknown> = {}
|
||||
|
||||
if (params.html) body.html = params.html
|
||||
if (params.plainText) body.plain_text = params.plainText
|
||||
|
||||
@@ -23,12 +23,377 @@ export interface MailchimpResponse<T> {
|
||||
paging?: MailchimpPagingInfo
|
||||
metadata: {
|
||||
operation: string
|
||||
[key: string]: any
|
||||
[key: string]: unknown
|
||||
}
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
// Member/Subscriber
|
||||
export interface MailchimpMember {
|
||||
id: string
|
||||
email_address: string
|
||||
unique_email_id?: string
|
||||
status: 'subscribed' | 'unsubscribed' | 'cleaned' | 'pending'
|
||||
merge_fields?: Record<string, unknown>
|
||||
interests?: Record<string, boolean>
|
||||
stats?: {
|
||||
avg_open_rate?: number
|
||||
avg_click_rate?: number
|
||||
}
|
||||
ip_signup?: string
|
||||
timestamp_signup?: string
|
||||
ip_opt?: string
|
||||
timestamp_opt?: string
|
||||
member_rating?: number
|
||||
last_changed?: string
|
||||
language?: string
|
||||
vip?: boolean
|
||||
email_client?: string
|
||||
location?: {
|
||||
latitude?: number
|
||||
longitude?: number
|
||||
gmtoff?: number
|
||||
dstoff?: number
|
||||
country_code?: string
|
||||
timezone?: string
|
||||
}
|
||||
tags?: Array<{ id: number; name: string }>
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
// Audience/List
|
||||
export interface MailchimpAudience {
|
||||
id: string
|
||||
name: string
|
||||
contact: {
|
||||
company: string
|
||||
address1: string
|
||||
address2?: string
|
||||
city: string
|
||||
state: string
|
||||
zip: string
|
||||
country: string
|
||||
phone?: string
|
||||
}
|
||||
permission_reminder: string
|
||||
campaign_defaults: {
|
||||
from_name: string
|
||||
from_email: string
|
||||
subject: string
|
||||
language: string
|
||||
}
|
||||
email_type_option: boolean
|
||||
stats?: {
|
||||
member_count?: number
|
||||
unsubscribe_count?: number
|
||||
cleaned_count?: number
|
||||
member_count_since_send?: number
|
||||
unsubscribe_count_since_send?: number
|
||||
cleaned_count_since_send?: number
|
||||
campaign_count?: number
|
||||
campaign_last_sent?: string
|
||||
merge_field_count?: number
|
||||
avg_sub_rate?: number
|
||||
avg_unsub_rate?: number
|
||||
target_sub_rate?: number
|
||||
open_rate?: number
|
||||
click_rate?: number
|
||||
last_sub_date?: string
|
||||
last_unsub_date?: string
|
||||
}
|
||||
date_created?: string
|
||||
list_rating?: number
|
||||
subscribe_url_short?: string
|
||||
subscribe_url_long?: string
|
||||
visibility?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
// Campaign
|
||||
export interface MailchimpCampaign {
|
||||
id: string
|
||||
type: 'regular' | 'plaintext' | 'absplit' | 'rss' | 'variate'
|
||||
create_time?: string
|
||||
archive_url?: string
|
||||
long_archive_url?: string
|
||||
status: 'save' | 'paused' | 'schedule' | 'sending' | 'sent'
|
||||
emails_sent?: number
|
||||
send_time?: string
|
||||
content_type?: string
|
||||
recipients?: {
|
||||
list_id: string
|
||||
list_name?: string
|
||||
segment_text?: string
|
||||
recipient_count?: number
|
||||
}
|
||||
settings?: {
|
||||
subject_line?: string
|
||||
preview_text?: string
|
||||
title?: string
|
||||
from_name?: string
|
||||
reply_to?: string
|
||||
use_conversation?: boolean
|
||||
to_name?: string
|
||||
folder_id?: string
|
||||
authenticate?: boolean
|
||||
auto_footer?: boolean
|
||||
inline_css?: boolean
|
||||
auto_tweet?: boolean
|
||||
fb_comments?: boolean
|
||||
timewarp?: boolean
|
||||
template_id?: number
|
||||
drag_and_drop?: boolean
|
||||
}
|
||||
tracking?: {
|
||||
opens?: boolean
|
||||
html_clicks?: boolean
|
||||
text_clicks?: boolean
|
||||
goal_tracking?: boolean
|
||||
ecomm360?: boolean
|
||||
google_analytics?: string
|
||||
clicktale?: string
|
||||
}
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
// Campaign Content
|
||||
export interface MailchimpCampaignContent {
|
||||
html?: string
|
||||
plain_text?: string
|
||||
archive_html?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
// Campaign Report
|
||||
export interface MailchimpCampaignReport {
|
||||
id: string
|
||||
campaign_title?: string
|
||||
type?: string
|
||||
emails_sent?: number
|
||||
abuse_reports?: number
|
||||
unsubscribed?: number
|
||||
send_time?: string
|
||||
bounces?: {
|
||||
hard_bounces?: number
|
||||
soft_bounces?: number
|
||||
syntax_errors?: number
|
||||
}
|
||||
forwards?: {
|
||||
forwards_count?: number
|
||||
forwards_opens?: number
|
||||
}
|
||||
opens?: {
|
||||
opens_total?: number
|
||||
unique_opens?: number
|
||||
open_rate?: number
|
||||
last_open?: string
|
||||
}
|
||||
clicks?: {
|
||||
clicks_total?: number
|
||||
unique_clicks?: number
|
||||
unique_subscriber_clicks?: number
|
||||
click_rate?: number
|
||||
last_click?: string
|
||||
}
|
||||
list_stats?: {
|
||||
sub_rate?: number
|
||||
unsub_rate?: number
|
||||
open_rate?: number
|
||||
click_rate?: number
|
||||
}
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
// Automation
|
||||
export interface MailchimpAutomation {
|
||||
id: string
|
||||
create_time?: string
|
||||
start_time?: string
|
||||
status: 'save' | 'paused' | 'sending'
|
||||
emails_sent?: number
|
||||
recipients?: {
|
||||
list_id: string
|
||||
list_name?: string
|
||||
segment_opts?: unknown
|
||||
}
|
||||
settings?: {
|
||||
title?: string
|
||||
from_name?: string
|
||||
reply_to?: string
|
||||
use_conversation?: boolean
|
||||
to_name?: string
|
||||
authenticate?: boolean
|
||||
auto_footer?: boolean
|
||||
inline_css?: boolean
|
||||
}
|
||||
tracking?: {
|
||||
opens?: boolean
|
||||
html_clicks?: boolean
|
||||
text_clicks?: boolean
|
||||
goal_tracking?: boolean
|
||||
ecomm360?: boolean
|
||||
google_analytics?: string
|
||||
clicktale?: string
|
||||
}
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
// Segment
|
||||
export interface MailchimpSegment {
|
||||
id: number
|
||||
name: string
|
||||
member_count?: number
|
||||
type: 'saved' | 'static' | 'fuzzy'
|
||||
created_at?: string
|
||||
updated_at?: string
|
||||
options?: {
|
||||
match?: 'any' | 'all'
|
||||
conditions?: Array<{
|
||||
condition_type?: string
|
||||
field?: string
|
||||
op?: string
|
||||
value?: unknown
|
||||
}>
|
||||
}
|
||||
list_id?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
// Template
|
||||
export interface MailchimpTemplate {
|
||||
id: number
|
||||
type: string
|
||||
name: string
|
||||
drag_and_drop?: boolean
|
||||
responsive?: boolean
|
||||
category?: string
|
||||
date_created?: string
|
||||
date_edited?: string
|
||||
created_by?: string
|
||||
edited_by?: string
|
||||
active?: boolean
|
||||
folder_id?: string
|
||||
thumbnail?: string
|
||||
share_url?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
// Landing Page
|
||||
export interface MailchimpLandingPage {
|
||||
id: string
|
||||
name: string
|
||||
title?: string
|
||||
description?: string
|
||||
template_id?: number
|
||||
status: 'draft' | 'published' | 'unpublished'
|
||||
list_id?: string
|
||||
store_id?: string
|
||||
web_id?: number
|
||||
created_at?: string
|
||||
published_at?: string
|
||||
unpublished_at?: string
|
||||
updated_at?: string
|
||||
url?: string
|
||||
tracking?: {
|
||||
opens?: boolean
|
||||
html_clicks?: boolean
|
||||
text_clicks?: boolean
|
||||
goal_tracking?: boolean
|
||||
ecomm360?: boolean
|
||||
google_analytics?: string
|
||||
clicktale?: string
|
||||
}
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
// Interest Category
|
||||
export interface MailchimpInterestCategory {
|
||||
list_id?: string
|
||||
id: string
|
||||
title: string
|
||||
display_order?: number
|
||||
type: 'checkboxes' | 'dropdown' | 'radio' | 'hidden'
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
// Interest
|
||||
export interface MailchimpInterest {
|
||||
category_id?: string
|
||||
list_id?: string
|
||||
id: string
|
||||
name: string
|
||||
subscriber_count?: string
|
||||
display_order?: number
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
// Merge Field
|
||||
export interface MailchimpMergeField {
|
||||
merge_id?: number
|
||||
tag: string
|
||||
name: string
|
||||
type:
|
||||
| 'text'
|
||||
| 'number'
|
||||
| 'address'
|
||||
| 'phone'
|
||||
| 'date'
|
||||
| 'url'
|
||||
| 'imageurl'
|
||||
| 'radio'
|
||||
| 'dropdown'
|
||||
| 'birthday'
|
||||
| 'zip'
|
||||
required?: boolean
|
||||
default_value?: string
|
||||
public?: boolean
|
||||
display_order?: number
|
||||
options?: {
|
||||
default_country?: number
|
||||
phone_format?: string
|
||||
date_format?: string
|
||||
choices?: string[]
|
||||
size?: number
|
||||
}
|
||||
help_text?: string
|
||||
list_id?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
// Batch Operation
|
||||
export interface MailchimpBatchOperation {
|
||||
id: string
|
||||
status: 'pending' | 'preprocessing' | 'started' | 'finalizing' | 'finished'
|
||||
total_operations?: number
|
||||
finished_operations?: number
|
||||
errored_operations?: number
|
||||
submitted_at?: string
|
||||
completed_at?: string
|
||||
response_body_url?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
// Tag
|
||||
export interface MailchimpTag {
|
||||
id: number
|
||||
name: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
// Error Response
|
||||
export interface MailchimpErrorResponse {
|
||||
type?: string
|
||||
title?: string
|
||||
status?: number
|
||||
detail?: string
|
||||
instance?: string
|
||||
errors?: Array<{
|
||||
field?: string
|
||||
message?: string
|
||||
}>
|
||||
}
|
||||
|
||||
// Helper function to extract server prefix from API key
|
||||
export function extractServerPrefix(apiKey: string): string {
|
||||
const parts = apiKey.split('-')
|
||||
@@ -45,9 +410,11 @@ export function buildMailchimpUrl(apiKey: string, path: string): string {
|
||||
}
|
||||
|
||||
// Helper function for consistent error handling
|
||||
export function handleMailchimpError(data: any, status: number, operation: string): never {
|
||||
export function handleMailchimpError(data: unknown, status: number, operation: string): never {
|
||||
logger.error(`Mailchimp API request failed for ${operation}`, { data, status })
|
||||
|
||||
const errorMessage = data.detail || data.title || data.error || data.message || 'Unknown error'
|
||||
const errorData = data as Record<string, unknown>
|
||||
const errorMessage =
|
||||
errorData.detail || errorData.title || errorData.error || errorData.message || 'Unknown error'
|
||||
throw new Error(`Mailchimp ${operation} failed: ${errorMessage}`)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError, type MailchimpMember } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpUnarchiveMember')
|
||||
|
||||
@@ -15,7 +15,7 @@ export interface MailchimpUnarchiveMemberParams {
|
||||
export interface MailchimpUnarchiveMemberResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
member: any
|
||||
member: MailchimpMember
|
||||
metadata: {
|
||||
operation: 'unarchive_member'
|
||||
subscriberHash: string
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpAudience } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpUpdateAudience')
|
||||
@@ -16,7 +17,7 @@ export interface MailchimpUpdateAudienceParams {
|
||||
export interface MailchimpUpdateAudienceResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
list: any
|
||||
list: MailchimpAudience
|
||||
metadata: {
|
||||
operation: 'update_audience'
|
||||
listId: string
|
||||
@@ -81,7 +82,7 @@ export const mailchimpUpdateAudienceTool: ToolConfig<
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: any = {}
|
||||
const body: Record<string, unknown> = {}
|
||||
|
||||
if (params.audienceName) body.name = params.audienceName
|
||||
if (params.permissionReminder) body.permission_reminder = params.permissionReminder
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError, type MailchimpCampaign } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpUpdateCampaign')
|
||||
|
||||
@@ -14,7 +14,7 @@ export interface MailchimpUpdateCampaignParams {
|
||||
export interface MailchimpUpdateCampaignResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
campaign: any
|
||||
campaign: MailchimpCampaign
|
||||
metadata: {
|
||||
operation: 'update_campaign'
|
||||
campaignId: string
|
||||
@@ -67,7 +67,7 @@ export const mailchimpUpdateCampaignTool: ToolConfig<
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: any = {}
|
||||
const body: Record<string, unknown> = {}
|
||||
|
||||
if (params.campaignSettings) {
|
||||
try {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpInterest } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpUpdateInterest')
|
||||
@@ -15,7 +16,7 @@ export interface MailchimpUpdateInterestParams {
|
||||
export interface MailchimpUpdateInterestResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
interest: any
|
||||
interest: MailchimpInterest
|
||||
metadata: {
|
||||
operation: 'update_interest'
|
||||
interestId: string
|
||||
@@ -78,7 +79,7 @@ export const mailchimpUpdateInterestTool: ToolConfig<
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: any = {}
|
||||
const body: Record<string, unknown> = {}
|
||||
|
||||
if (params.interestName) body.name = params.interestName
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpInterestCategory } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpUpdateInterestCategory')
|
||||
@@ -14,7 +15,7 @@ export interface MailchimpUpdateInterestCategoryParams {
|
||||
export interface MailchimpUpdateInterestCategoryResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
category: any
|
||||
category: MailchimpInterestCategory
|
||||
metadata: {
|
||||
operation: 'update_interest_category'
|
||||
interestCategoryId: string
|
||||
@@ -71,7 +72,7 @@ export const mailchimpUpdateInterestCategoryTool: ToolConfig<
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: any = {}
|
||||
const body: Record<string, unknown> = {}
|
||||
|
||||
if (params.interestCategoryTitle) body.title = params.interestCategoryTitle
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpLandingPage } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpUpdateLandingPage')
|
||||
@@ -13,7 +14,7 @@ export interface MailchimpUpdateLandingPageParams {
|
||||
export interface MailchimpUpdateLandingPageResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
landingPage: any
|
||||
landingPage: MailchimpLandingPage
|
||||
metadata: {
|
||||
operation: 'update_landing_page'
|
||||
pageId: string
|
||||
@@ -60,7 +61,7 @@ export const mailchimpUpdateLandingPageTool: ToolConfig<
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: any = {}
|
||||
const body: Record<string, unknown> = {}
|
||||
|
||||
if (params.landingPageTitle) body.title = params.landingPageTitle
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError, type MailchimpMember } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpUpdateMember')
|
||||
|
||||
@@ -17,7 +17,7 @@ export interface MailchimpUpdateMemberParams {
|
||||
export interface MailchimpUpdateMemberResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
member: any
|
||||
member: MailchimpMember
|
||||
metadata: {
|
||||
operation: 'update_member'
|
||||
subscriberHash: string
|
||||
@@ -89,7 +89,7 @@ export const mailchimpUpdateMemberTool: ToolConfig<
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: any = {}
|
||||
const body: Record<string, unknown> = {}
|
||||
|
||||
if (params.emailAddress) body.email_address = params.emailAddress
|
||||
if (params.status) body.status = params.status
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpMergeField } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpUpdateMergeField')
|
||||
@@ -14,7 +15,7 @@ export interface MailchimpUpdateMergeFieldParams {
|
||||
export interface MailchimpUpdateMergeFieldResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
mergeField: any
|
||||
mergeField: MailchimpMergeField
|
||||
metadata: {
|
||||
operation: 'update_merge_field'
|
||||
mergeId: string
|
||||
@@ -68,7 +69,7 @@ export const mailchimpUpdateMergeFieldTool: ToolConfig<
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: any = {}
|
||||
const body: Record<string, unknown> = {}
|
||||
|
||||
if (params.mergeName) body.name = params.mergeName
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpSegment } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpUpdateSegment')
|
||||
@@ -15,7 +16,7 @@ export interface MailchimpUpdateSegmentParams {
|
||||
export interface MailchimpUpdateSegmentResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
segment: any
|
||||
segment: MailchimpSegment
|
||||
metadata: {
|
||||
operation: 'update_segment'
|
||||
segmentId: string
|
||||
@@ -75,7 +76,7 @@ export const mailchimpUpdateSegmentTool: ToolConfig<
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: any = {}
|
||||
const body: Record<string, unknown> = {}
|
||||
|
||||
if (params.segmentName) body.name = params.segmentName
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { MailchimpTemplate } from './types'
|
||||
import { buildMailchimpUrl, handleMailchimpError } from './types'
|
||||
|
||||
const logger = createLogger('MailchimpUpdateTemplate')
|
||||
@@ -14,7 +15,7 @@ export interface MailchimpUpdateTemplateParams {
|
||||
export interface MailchimpUpdateTemplateResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
template: any
|
||||
template: MailchimpTemplate
|
||||
metadata: {
|
||||
operation: 'update_template'
|
||||
templateId: string
|
||||
@@ -67,7 +68,7 @@ export const mailchimpUpdateTemplateTool: ToolConfig<
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: any = {}
|
||||
const body: Record<string, unknown> = {}
|
||||
|
||||
if (params.templateName) body.name = params.templateName
|
||||
if (params.templateHtml) body.html = params.templateHtml
|
||||
|
||||
101
apps/sim/tools/mailgun/add_list_member.ts
Normal file
101
apps/sim/tools/mailgun/add_list_member.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import type { AddListMemberParams, AddListMemberResult } from '@/tools/mailgun/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const mailgunAddListMemberTool: ToolConfig<AddListMemberParams, AddListMemberResult> = {
|
||||
id: 'mailgun_add_list_member',
|
||||
name: 'Mailgun Add List Member',
|
||||
description: 'Add a member to a mailing list',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Mailgun API key',
|
||||
},
|
||||
listAddress: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Mailing list address',
|
||||
},
|
||||
address: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Member email address',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Member name',
|
||||
},
|
||||
vars: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'JSON string of custom variables',
|
||||
},
|
||||
subscribed: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether the member is subscribed',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.mailgun.net/v3/lists/${params.listAddress}/members`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Basic ${Buffer.from(`api:${params.apiKey}`).toString('base64')}`,
|
||||
}),
|
||||
body: (params) => {
|
||||
const formData = new FormData()
|
||||
formData.append('address', params.address)
|
||||
|
||||
if (params.name) {
|
||||
formData.append('name', params.name)
|
||||
}
|
||||
if (params.vars) {
|
||||
formData.append('vars', params.vars)
|
||||
}
|
||||
if (params.subscribed !== undefined) {
|
||||
formData.append('subscribed', params.subscribed ? 'yes' : 'no')
|
||||
}
|
||||
|
||||
return { body: formData }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response, params): Promise<AddListMemberResult> => {
|
||||
if (!response.ok) {
|
||||
const error = await response.json()
|
||||
throw new Error(error.message || 'Failed to add list member')
|
||||
}
|
||||
|
||||
const result = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
success: true,
|
||||
message: result.message,
|
||||
member: {
|
||||
address: result.member.address,
|
||||
name: result.member.name,
|
||||
subscribed: result.member.subscribed,
|
||||
vars: result.member.vars,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the member was added successfully' },
|
||||
message: { type: 'string', description: 'Response message' },
|
||||
member: { type: 'json', description: 'Added member details' },
|
||||
},
|
||||
}
|
||||
99
apps/sim/tools/mailgun/create_mailing_list.ts
Normal file
99
apps/sim/tools/mailgun/create_mailing_list.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import type { CreateMailingListParams, CreateMailingListResult } from '@/tools/mailgun/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const mailgunCreateMailingListTool: ToolConfig<
|
||||
CreateMailingListParams,
|
||||
CreateMailingListResult
|
||||
> = {
|
||||
id: 'mailgun_create_mailing_list',
|
||||
name: 'Mailgun Create Mailing List',
|
||||
description: 'Create a new mailing list',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Mailgun API key',
|
||||
},
|
||||
address: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Mailing list address (e.g., list@example.com)',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Mailing list name',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Mailing list description',
|
||||
},
|
||||
accessLevel: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Access level: readonly, members, or everyone',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => 'https://api.mailgun.net/v3/lists',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Basic ${Buffer.from(`api:${params.apiKey}`).toString('base64')}`,
|
||||
}),
|
||||
body: (params) => {
|
||||
const formData = new FormData()
|
||||
formData.append('address', params.address)
|
||||
|
||||
if (params.name) {
|
||||
formData.append('name', params.name)
|
||||
}
|
||||
if (params.description) {
|
||||
formData.append('description', params.description)
|
||||
}
|
||||
if (params.accessLevel) {
|
||||
formData.append('access_level', params.accessLevel)
|
||||
}
|
||||
|
||||
return { body: formData }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response, params): Promise<CreateMailingListResult> => {
|
||||
if (!response.ok) {
|
||||
const error = await response.json()
|
||||
throw new Error(error.message || 'Failed to create mailing list')
|
||||
}
|
||||
|
||||
const result = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
success: true,
|
||||
message: result.message,
|
||||
list: {
|
||||
address: result.list.address,
|
||||
name: result.list.name,
|
||||
description: result.list.description,
|
||||
accessLevel: result.list.access_level,
|
||||
createdAt: result.list.created_at,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the list was created successfully' },
|
||||
message: { type: 'string', description: 'Response message' },
|
||||
list: { type: 'json', description: 'Created mailing list details' },
|
||||
},
|
||||
}
|
||||
62
apps/sim/tools/mailgun/get_domain.ts
Normal file
62
apps/sim/tools/mailgun/get_domain.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import type { GetDomainParams, GetDomainResult } from '@/tools/mailgun/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const mailgunGetDomainTool: ToolConfig<GetDomainParams, GetDomainResult> = {
|
||||
id: 'mailgun_get_domain',
|
||||
name: 'Mailgun Get Domain',
|
||||
description: 'Get details of a specific domain',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Mailgun API key',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Domain name',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.mailgun.net/v3/domains/${params.domain}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Basic ${Buffer.from(`api:${params.apiKey}`).toString('base64')}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response, params): Promise<GetDomainResult> => {
|
||||
if (!response.ok) {
|
||||
const error = await response.json()
|
||||
throw new Error(error.message || 'Failed to get domain')
|
||||
}
|
||||
|
||||
const result = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
success: true,
|
||||
domain: {
|
||||
name: result.domain.name,
|
||||
smtpLogin: result.domain.smtp_login,
|
||||
smtpPassword: result.domain.smtp_password,
|
||||
spamAction: result.domain.spam_action,
|
||||
state: result.domain.state,
|
||||
createdAt: result.domain.created_at,
|
||||
type: result.domain.type,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the request was successful' },
|
||||
domain: { type: 'json', description: 'Domain details' },
|
||||
},
|
||||
}
|
||||
61
apps/sim/tools/mailgun/get_mailing_list.ts
Normal file
61
apps/sim/tools/mailgun/get_mailing_list.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import type { GetMailingListParams, GetMailingListResult } from '@/tools/mailgun/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const mailgunGetMailingListTool: ToolConfig<GetMailingListParams, GetMailingListResult> = {
|
||||
id: 'mailgun_get_mailing_list',
|
||||
name: 'Mailgun Get Mailing List',
|
||||
description: 'Get details of a mailing list',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Mailgun API key',
|
||||
},
|
||||
address: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Mailing list address',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.mailgun.net/v3/lists/${params.address}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Basic ${Buffer.from(`api:${params.apiKey}`).toString('base64')}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response, params): Promise<GetMailingListResult> => {
|
||||
if (!response.ok) {
|
||||
const error = await response.json()
|
||||
throw new Error(error.message || 'Failed to get mailing list')
|
||||
}
|
||||
|
||||
const result = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
success: true,
|
||||
list: {
|
||||
address: result.list.address,
|
||||
name: result.list.name,
|
||||
description: result.list.description,
|
||||
accessLevel: result.list.access_level,
|
||||
membersCount: result.list.members_count,
|
||||
createdAt: result.list.created_at,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the request was successful' },
|
||||
list: { type: 'json', description: 'Mailing list details' },
|
||||
},
|
||||
}
|
||||
83
apps/sim/tools/mailgun/get_message.ts
Normal file
83
apps/sim/tools/mailgun/get_message.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import type { GetMessageParams, GetMessageResult } from '@/tools/mailgun/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const mailgunGetMessageTool: ToolConfig<GetMessageParams, GetMessageResult> = {
|
||||
id: 'mailgun_get_message',
|
||||
name: 'Mailgun Get Message',
|
||||
description: 'Retrieve a stored message by its key',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Mailgun API key',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Mailgun domain',
|
||||
},
|
||||
messageKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Message storage key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.mailgun.net/v3/domains/${params.domain}/messages/${params.messageKey}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Basic ${Buffer.from(`api:${params.apiKey}`).toString('base64')}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response, params): Promise<GetMessageResult> => {
|
||||
if (!response.ok) {
|
||||
const error = await response.json()
|
||||
throw new Error(error.message || 'Failed to get message')
|
||||
}
|
||||
|
||||
const result = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
success: true,
|
||||
recipients: result.recipients,
|
||||
from: result.from,
|
||||
subject: result.subject,
|
||||
bodyPlain: result['body-plain'],
|
||||
strippedText: result['stripped-text'],
|
||||
strippedSignature: result['stripped-signature'],
|
||||
bodyHtml: result['body-html'],
|
||||
strippedHtml: result['stripped-html'],
|
||||
attachmentCount: result['attachment-count'],
|
||||
timestamp: result.timestamp,
|
||||
messageHeaders: result['message-headers'],
|
||||
contentIdMap: result['content-id-map'],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the request was successful' },
|
||||
recipients: { type: 'string', description: 'Message recipients' },
|
||||
from: { type: 'string', description: 'Sender email' },
|
||||
subject: { type: 'string', description: 'Message subject' },
|
||||
bodyPlain: { type: 'string', description: 'Plain text body' },
|
||||
strippedText: { type: 'string', description: 'Stripped text' },
|
||||
strippedSignature: { type: 'string', description: 'Stripped signature' },
|
||||
bodyHtml: { type: 'string', description: 'HTML body' },
|
||||
strippedHtml: { type: 'string', description: 'Stripped HTML' },
|
||||
attachmentCount: { type: 'number', description: 'Number of attachments' },
|
||||
timestamp: { type: 'number', description: 'Message timestamp' },
|
||||
messageHeaders: { type: 'json', description: 'Message headers' },
|
||||
contentIdMap: { type: 'json', description: 'Content ID map' },
|
||||
},
|
||||
}
|
||||
11
apps/sim/tools/mailgun/index.ts
Normal file
11
apps/sim/tools/mailgun/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
// Message Operations
|
||||
|
||||
export { mailgunAddListMemberTool } from './add_list_member'
|
||||
export { mailgunCreateMailingListTool } from './create_mailing_list'
|
||||
export { mailgunGetDomainTool } from './get_domain'
|
||||
export { mailgunGetMailingListTool } from './get_mailing_list'
|
||||
export { mailgunGetMessageTool } from './get_message'
|
||||
export { mailgunListDomainsTool } from './list_domains'
|
||||
export { mailgunListMessagesTool } from './list_messages'
|
||||
export { mailgunSendMessageTool } from './send_message'
|
||||
export type { SendMessageParams, SendMessageResult } from './types'
|
||||
50
apps/sim/tools/mailgun/list_domains.ts
Normal file
50
apps/sim/tools/mailgun/list_domains.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import type { ListDomainsParams, ListDomainsResult } from '@/tools/mailgun/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const mailgunListDomainsTool: ToolConfig<ListDomainsParams, ListDomainsResult> = {
|
||||
id: 'mailgun_list_domains',
|
||||
name: 'Mailgun List Domains',
|
||||
description: 'List all domains for your Mailgun account',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Mailgun API key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => 'https://api.mailgun.net/v3/domains',
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Basic ${Buffer.from(`api:${params.apiKey}`).toString('base64')}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response, params): Promise<ListDomainsResult> => {
|
||||
if (!response.ok) {
|
||||
const error = await response.json()
|
||||
throw new Error(error.message || 'Failed to list domains')
|
||||
}
|
||||
|
||||
const result = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
success: true,
|
||||
totalCount: result.total_count,
|
||||
items: result.items || [],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the request was successful' },
|
||||
totalCount: { type: 'number', description: 'Total number of domains' },
|
||||
items: { type: 'json', description: 'Array of domain objects' },
|
||||
},
|
||||
}
|
||||
81
apps/sim/tools/mailgun/list_messages.ts
Normal file
81
apps/sim/tools/mailgun/list_messages.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import type { ListMessagesParams, ListMessagesResult } from '@/tools/mailgun/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const mailgunListMessagesTool: ToolConfig<ListMessagesParams, ListMessagesResult> = {
|
||||
id: 'mailgun_list_messages',
|
||||
name: 'Mailgun List Messages',
|
||||
description: 'List events (logs) for messages sent through Mailgun',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Mailgun API key',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Mailgun domain',
|
||||
},
|
||||
event: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by event type (accepted, delivered, failed, opened, clicked, etc.)',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of events to return (default: 100)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = `https://api.mailgun.net/v3/${params.domain}/events`
|
||||
const queryParams = new URLSearchParams()
|
||||
|
||||
if (params.event) {
|
||||
queryParams.append('event', params.event)
|
||||
}
|
||||
if (params.limit) {
|
||||
queryParams.append('limit', params.limit.toString())
|
||||
}
|
||||
|
||||
const query = queryParams.toString()
|
||||
return query ? `${baseUrl}?${query}` : baseUrl
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Basic ${Buffer.from(`api:${params.apiKey}`).toString('base64')}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response, params): Promise<ListMessagesResult> => {
|
||||
if (!response.ok) {
|
||||
const error = await response.json()
|
||||
throw new Error(error.message || 'Failed to list messages')
|
||||
}
|
||||
|
||||
const result = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
success: true,
|
||||
items: result.items || [],
|
||||
paging: result.paging || {},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the request was successful' },
|
||||
items: { type: 'json', description: 'Array of event items' },
|
||||
paging: { type: 'json', description: 'Paging information' },
|
||||
},
|
||||
}
|
||||
129
apps/sim/tools/mailgun/send_message.ts
Normal file
129
apps/sim/tools/mailgun/send_message.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import type { SendMessageParams, SendMessageResult } from '@/tools/mailgun/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const mailgunSendMessageTool: ToolConfig<SendMessageParams, SendMessageResult> = {
|
||||
id: 'mailgun_send_message',
|
||||
name: 'Mailgun Send Message',
|
||||
description: 'Send an email using Mailgun API',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Mailgun API key',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Mailgun domain (e.g., mg.example.com)',
|
||||
},
|
||||
from: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sender email address',
|
||||
},
|
||||
to: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Recipient email address (comma-separated for multiple)',
|
||||
},
|
||||
subject: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Email subject',
|
||||
},
|
||||
text: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Plain text body of the email',
|
||||
},
|
||||
html: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'HTML body of the email',
|
||||
},
|
||||
cc: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'CC email address (comma-separated for multiple)',
|
||||
},
|
||||
bcc: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'BCC email address (comma-separated for multiple)',
|
||||
},
|
||||
tags: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Tags for the email (comma-separated)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.mailgun.net/v3/${params.domain}/messages`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Basic ${Buffer.from(`api:${params.apiKey}`).toString('base64')}`,
|
||||
}),
|
||||
body: (params) => {
|
||||
const formData = new FormData()
|
||||
formData.append('from', params.from)
|
||||
formData.append('to', params.to)
|
||||
formData.append('subject', params.subject)
|
||||
|
||||
if (params.text) {
|
||||
formData.append('text', params.text)
|
||||
}
|
||||
if (params.html) {
|
||||
formData.append('html', params.html)
|
||||
}
|
||||
if (params.cc) {
|
||||
formData.append('cc', params.cc)
|
||||
}
|
||||
if (params.bcc) {
|
||||
formData.append('bcc', params.bcc)
|
||||
}
|
||||
if (params.tags) {
|
||||
const tagArray = params.tags.split(',').map((t) => t.trim())
|
||||
tagArray.forEach((tag) => formData.append('o:tag', tag))
|
||||
}
|
||||
|
||||
return { body: formData }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response, params): Promise<SendMessageResult> => {
|
||||
if (!response.ok) {
|
||||
const error = await response.json()
|
||||
throw new Error(error.message || 'Failed to send message')
|
||||
}
|
||||
|
||||
const result = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
success: true,
|
||||
id: result.id,
|
||||
message: result.message || 'Queued. Thank you.',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the message was sent successfully' },
|
||||
id: { type: 'string', description: 'Message ID' },
|
||||
message: { type: 'string', description: 'Response message from Mailgun' },
|
||||
},
|
||||
}
|
||||
201
apps/sim/tools/mailgun/types.ts
Normal file
201
apps/sim/tools/mailgun/types.ts
Normal file
@@ -0,0 +1,201 @@
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface MailgunMessageHeaders {
|
||||
[key: string]: string | string[]
|
||||
}
|
||||
|
||||
export interface MailgunMessageItem {
|
||||
timestamp: number
|
||||
event: string
|
||||
recipient: string
|
||||
sender?: string
|
||||
subject?: string
|
||||
deliveryStatus?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export interface MailgunDomainItem {
|
||||
name: string
|
||||
state: string
|
||||
type: string
|
||||
created_at?: string
|
||||
smtp_login?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export interface MailgunPaging {
|
||||
first?: string
|
||||
next?: string
|
||||
previous?: string
|
||||
last?: string
|
||||
}
|
||||
|
||||
export interface MailgunMailingListMember {
|
||||
address: string
|
||||
name?: string
|
||||
subscribed: boolean
|
||||
vars?: Record<string, string | number | boolean | null>
|
||||
}
|
||||
|
||||
// Send Message
|
||||
export interface SendMessageParams {
|
||||
apiKey: string
|
||||
domain: string
|
||||
from: string
|
||||
to: string
|
||||
subject: string
|
||||
text?: string
|
||||
html?: string
|
||||
cc?: string
|
||||
bcc?: string
|
||||
tags?: string
|
||||
}
|
||||
|
||||
export interface SendMessageResult extends ToolResponse {
|
||||
output: {
|
||||
success: boolean
|
||||
id: string
|
||||
message: string
|
||||
}
|
||||
}
|
||||
|
||||
// Get Message
|
||||
export interface GetMessageParams {
|
||||
apiKey: string
|
||||
domain: string
|
||||
messageKey: string
|
||||
}
|
||||
|
||||
export interface GetMessageResult extends ToolResponse {
|
||||
output: {
|
||||
success: boolean
|
||||
recipients: string
|
||||
from: string
|
||||
subject: string
|
||||
bodyPlain: string
|
||||
strippedText: string
|
||||
strippedSignature: string
|
||||
bodyHtml: string
|
||||
strippedHtml: string
|
||||
attachmentCount: number
|
||||
timestamp: number
|
||||
messageHeaders: MailgunMessageHeaders
|
||||
contentIdMap: Record<string, string>
|
||||
}
|
||||
}
|
||||
|
||||
// List Messages (Events)
|
||||
export interface ListMessagesParams {
|
||||
apiKey: string
|
||||
domain: string
|
||||
event?: string
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface ListMessagesResult extends ToolResponse {
|
||||
output: {
|
||||
success: boolean
|
||||
items: MailgunMessageItem[]
|
||||
paging: MailgunPaging
|
||||
}
|
||||
}
|
||||
|
||||
// Create Mailing List
|
||||
export interface CreateMailingListParams {
|
||||
apiKey: string
|
||||
address: string
|
||||
name?: string
|
||||
description?: string
|
||||
accessLevel?: 'readonly' | 'members' | 'everyone'
|
||||
}
|
||||
|
||||
export interface CreateMailingListResult extends ToolResponse {
|
||||
output: {
|
||||
success: boolean
|
||||
message: string
|
||||
list: {
|
||||
address: string
|
||||
name: string
|
||||
description: string
|
||||
accessLevel: string
|
||||
createdAt: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get Mailing List
|
||||
export interface GetMailingListParams {
|
||||
apiKey: string
|
||||
address: string
|
||||
}
|
||||
|
||||
export interface GetMailingListResult extends ToolResponse {
|
||||
output: {
|
||||
success: boolean
|
||||
list: {
|
||||
address: string
|
||||
name: string
|
||||
description: string
|
||||
accessLevel: string
|
||||
membersCount: number
|
||||
createdAt: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add List Member
|
||||
export interface AddListMemberParams {
|
||||
apiKey: string
|
||||
listAddress: string
|
||||
address: string
|
||||
name?: string
|
||||
vars?: string
|
||||
subscribed?: boolean
|
||||
}
|
||||
|
||||
export interface AddListMemberResult extends ToolResponse {
|
||||
output: {
|
||||
success: boolean
|
||||
message: string
|
||||
member: {
|
||||
address: string
|
||||
name: string
|
||||
subscribed: boolean
|
||||
vars: Record<string, string | number | boolean | null>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// List Domains
|
||||
export interface ListDomainsParams {
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
export interface ListDomainsResult extends ToolResponse {
|
||||
output: {
|
||||
success: boolean
|
||||
totalCount: number
|
||||
items: MailgunDomainItem[]
|
||||
}
|
||||
}
|
||||
|
||||
// Get Domain
|
||||
export interface GetDomainParams {
|
||||
apiKey: string
|
||||
domain: string
|
||||
}
|
||||
|
||||
export interface GetDomainResult extends ToolResponse {
|
||||
output: {
|
||||
success: boolean
|
||||
domain: {
|
||||
name: string
|
||||
smtpLogin: string
|
||||
smtpPassword: string
|
||||
spamAction: string
|
||||
state: string
|
||||
createdAt: string
|
||||
type: string
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -419,6 +419,7 @@ import {
|
||||
linearUpdateProjectTool,
|
||||
linearUpdateWorkflowStateTool,
|
||||
} from '@/tools/linear'
|
||||
import { linkedInGetProfileTool, linkedInSharePostTool } from '@/tools/linkedin'
|
||||
import { linkupSearchTool } from '@/tools/linkup'
|
||||
import {
|
||||
mailchimpAddMemberTagsTool,
|
||||
@@ -495,6 +496,16 @@ import {
|
||||
mailchimpUpdateSegmentTool,
|
||||
mailchimpUpdateTemplateTool,
|
||||
} from '@/tools/mailchimp'
|
||||
import {
|
||||
mailgunAddListMemberTool,
|
||||
mailgunCreateMailingListTool,
|
||||
mailgunGetDomainTool,
|
||||
mailgunGetMailingListTool,
|
||||
mailgunGetMessageTool,
|
||||
mailgunListDomainsTool,
|
||||
mailgunListMessagesTool,
|
||||
mailgunSendMessageTool,
|
||||
} from '@/tools/mailgun'
|
||||
import { mem0AddMemoriesTool, mem0GetMemoriesTool, mem0SearchMemoriesTool } from '@/tools/mem0'
|
||||
import { memoryAddTool, memoryDeleteTool, memoryGetAllTool, memoryGetTool } from '@/tools/memory'
|
||||
import {
|
||||
@@ -756,6 +767,24 @@ import {
|
||||
salesforceUpdateOpportunityTool,
|
||||
salesforceUpdateTaskTool,
|
||||
} from '@/tools/salesforce'
|
||||
import {
|
||||
sendGridAddContactsToListTool,
|
||||
sendGridAddContactTool,
|
||||
sendGridCreateListTool,
|
||||
sendGridCreateTemplateTool,
|
||||
sendGridCreateTemplateVersionTool,
|
||||
sendGridDeleteContactsTool,
|
||||
sendGridDeleteListTool,
|
||||
sendGridDeleteTemplateTool,
|
||||
sendGridGetContactTool,
|
||||
sendGridGetListTool,
|
||||
sendGridGetTemplateTool,
|
||||
sendGridListAllListsTool,
|
||||
sendGridListTemplatesTool,
|
||||
sendGridRemoveContactsFromListTool,
|
||||
sendGridSearchContactsTool,
|
||||
sendGridSendMailTool,
|
||||
} from '@/tools/sendgrid'
|
||||
import {
|
||||
createDeployTool,
|
||||
createProjectTool,
|
||||
@@ -791,6 +820,7 @@ import {
|
||||
slackUpdateMessageTool,
|
||||
} from '@/tools/slack'
|
||||
import { smsSendTool } from '@/tools/sms'
|
||||
import { smtpSendMailTool } from '@/tools/smtp'
|
||||
import { stagehandAgentTool, stagehandExtractTool } from '@/tools/stagehand'
|
||||
import {
|
||||
stripeCancelPaymentIntentTool,
|
||||
@@ -1025,7 +1055,34 @@ export const tools: Record<string, ToolConfig> = {
|
||||
jina_read_url: readUrlTool,
|
||||
jina_search: jinaSearchTool,
|
||||
linkup_search: linkupSearchTool,
|
||||
linkedin_share_post: linkedInSharePostTool,
|
||||
linkedin_get_profile: linkedInGetProfileTool,
|
||||
resend_send: mailSendTool,
|
||||
sendgrid_send_mail: sendGridSendMailTool,
|
||||
sendgrid_add_contact: sendGridAddContactTool,
|
||||
sendgrid_get_contact: sendGridGetContactTool,
|
||||
sendgrid_search_contacts: sendGridSearchContactsTool,
|
||||
sendgrid_delete_contacts: sendGridDeleteContactsTool,
|
||||
sendgrid_create_list: sendGridCreateListTool,
|
||||
sendgrid_get_list: sendGridGetListTool,
|
||||
sendgrid_list_all_lists: sendGridListAllListsTool,
|
||||
sendgrid_delete_list: sendGridDeleteListTool,
|
||||
sendgrid_add_contacts_to_list: sendGridAddContactsToListTool,
|
||||
sendgrid_remove_contacts_from_list: sendGridRemoveContactsFromListTool,
|
||||
sendgrid_create_template: sendGridCreateTemplateTool,
|
||||
sendgrid_get_template: sendGridGetTemplateTool,
|
||||
sendgrid_list_templates: sendGridListTemplatesTool,
|
||||
sendgrid_delete_template: sendGridDeleteTemplateTool,
|
||||
sendgrid_create_template_version: sendGridCreateTemplateVersionTool,
|
||||
smtp_send_mail: smtpSendMailTool,
|
||||
mailgun_send_message: mailgunSendMessageTool,
|
||||
mailgun_get_message: mailgunGetMessageTool,
|
||||
mailgun_list_messages: mailgunListMessagesTool,
|
||||
mailgun_create_mailing_list: mailgunCreateMailingListTool,
|
||||
mailgun_get_mailing_list: mailgunGetMailingListTool,
|
||||
mailgun_add_list_member: mailgunAddListMemberTool,
|
||||
mailgun_list_domains: mailgunListDomainsTool,
|
||||
mailgun_get_domain: mailgunGetDomainTool,
|
||||
sms_send: smsSendTool,
|
||||
jira_retrieve: jiraRetrieveTool,
|
||||
jira_update: jiraUpdateTool,
|
||||
|
||||
118
apps/sim/tools/sendgrid/add_contact.ts
Normal file
118
apps/sim/tools/sendgrid/add_contact.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import type {
|
||||
AddContactParams,
|
||||
ContactResult,
|
||||
SendGridContactObject,
|
||||
SendGridContactRequest,
|
||||
} from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const sendGridAddContactTool: ToolConfig<AddContactParams, ContactResult> = {
|
||||
id: 'sendgrid_add_contact',
|
||||
name: 'SendGrid Add Contact',
|
||||
description: 'Add a new contact to SendGrid',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Contact email address',
|
||||
},
|
||||
firstName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Contact first name',
|
||||
},
|
||||
lastName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Contact last name',
|
||||
},
|
||||
customFields: {
|
||||
type: 'json',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'JSON object of custom field key-value pairs (use field IDs like e1_T, e2_N, e3_D, not field names)',
|
||||
},
|
||||
listIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list IDs to add the contact to',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => 'https://api.sendgrid.com/v3/marketing/contacts',
|
||||
method: 'PUT',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const contact: SendGridContactObject = {
|
||||
email: params.email,
|
||||
}
|
||||
|
||||
if (params.firstName) contact.first_name = params.firstName
|
||||
if (params.lastName) contact.last_name = params.lastName
|
||||
|
||||
if (params.customFields) {
|
||||
const customFields =
|
||||
typeof params.customFields === 'string'
|
||||
? JSON.parse(params.customFields)
|
||||
: params.customFields
|
||||
Object.assign(contact, customFields)
|
||||
}
|
||||
|
||||
const body: SendGridContactRequest = {
|
||||
contacts: [contact],
|
||||
}
|
||||
|
||||
if (params.listIds) {
|
||||
body.list_ids = params.listIds.split(',').map((id) => id.trim())
|
||||
}
|
||||
|
||||
return { body: JSON.stringify(body) }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response, params): Promise<ContactResult> => {
|
||||
if (!response.ok) {
|
||||
const error = await response.json()
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to add contact')
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
jobId: data.job_id,
|
||||
email: params?.email || '',
|
||||
firstName: params?.firstName,
|
||||
lastName: params?.lastName,
|
||||
message:
|
||||
'Contact is being added. This is an asynchronous operation. Use the job ID to track status.',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
jobId: { type: 'string', description: 'Job ID for tracking the async contact creation' },
|
||||
email: { type: 'string', description: 'Contact email address' },
|
||||
firstName: { type: 'string', description: 'Contact first name' },
|
||||
lastName: { type: 'string', description: 'Contact last name' },
|
||||
message: { type: 'string', description: 'Status message' },
|
||||
},
|
||||
}
|
||||
74
apps/sim/tools/sendgrid/add_contacts_to_list.ts
Normal file
74
apps/sim/tools/sendgrid/add_contacts_to_list.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import type { AddContactsToListParams, SendGridContactObject } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
export const sendGridAddContactsToListTool: ToolConfig<AddContactsToListParams, ToolResponse> = {
|
||||
id: 'sendgrid_add_contacts_to_list',
|
||||
name: 'SendGrid Add Contacts to List',
|
||||
description:
|
||||
'Add or update contacts and assign them to a list in SendGrid (uses PUT /v3/marketing/contacts)',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
listId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'List ID to add contacts to',
|
||||
},
|
||||
contacts: {
|
||||
type: 'json',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'JSON array of contact objects. Each contact must have at least: email (or phone_number_id/external_id/anonymous_id). Example: [{"email": "user@example.com", "first_name": "John"}]',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => 'https://api.sendgrid.com/v3/marketing/contacts',
|
||||
method: 'PUT',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const contactsArray: SendGridContactObject[] =
|
||||
typeof params.contacts === 'string' ? JSON.parse(params.contacts) : params.contacts
|
||||
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
list_ids: [params.listId],
|
||||
contacts: contactsArray,
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<ToolResponse> => {
|
||||
if (!response.ok) {
|
||||
const error = (await response.json()) as { errors?: Array<{ message?: string }> }
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to add contacts to list')
|
||||
}
|
||||
|
||||
const data = (await response.json()) as { job_id: string }
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
jobId: data.job_id,
|
||||
message: 'Contacts are being added to the list. This is an asynchronous operation.',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
jobId: { type: 'string', description: 'Job ID for tracking the async operation' },
|
||||
message: { type: 'string', description: 'Status message' },
|
||||
},
|
||||
}
|
||||
64
apps/sim/tools/sendgrid/create_list.ts
Normal file
64
apps/sim/tools/sendgrid/create_list.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import type { CreateListParams, ListResult, SendGridList } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const sendGridCreateListTool: ToolConfig<CreateListParams, ListResult> = {
|
||||
id: 'sendgrid_create_list',
|
||||
name: 'SendGrid Create List',
|
||||
description: 'Create a new contact list in SendGrid',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'List name',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => 'https://api.sendgrid.com/v3/marketing/lists',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
name: params.name,
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<ListResult> => {
|
||||
if (!response.ok) {
|
||||
const error = (await response.json()) as { errors?: Array<{ message?: string }> }
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to create list')
|
||||
}
|
||||
|
||||
const data = (await response.json()) as SendGridList
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
contactCount: data.contact_count,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'List ID' },
|
||||
name: { type: 'string', description: 'List name' },
|
||||
contactCount: { type: 'number', description: 'Number of contacts in the list' },
|
||||
},
|
||||
}
|
||||
75
apps/sim/tools/sendgrid/create_template.ts
Normal file
75
apps/sim/tools/sendgrid/create_template.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import type { CreateTemplateParams, SendGridTemplate, TemplateResult } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const sendGridCreateTemplateTool: ToolConfig<CreateTemplateParams, TemplateResult> = {
|
||||
id: 'sendgrid_create_template',
|
||||
name: 'SendGrid Create Template',
|
||||
description: 'Create a new email template in SendGrid',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Template name',
|
||||
},
|
||||
generation: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Template generation type (legacy or dynamic, default: dynamic)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => 'https://api.sendgrid.com/v3/templates',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
name: params.name,
|
||||
generation: params.generation || 'dynamic',
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<TemplateResult> => {
|
||||
if (!response.ok) {
|
||||
const error = (await response.json()) as { errors?: Array<{ message?: string }> }
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to create template')
|
||||
}
|
||||
|
||||
const data = (await response.json()) as SendGridTemplate
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
generation: data.generation,
|
||||
updatedAt: data.updated_at,
|
||||
versions: data.versions || [],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Template ID' },
|
||||
name: { type: 'string', description: 'Template name' },
|
||||
generation: { type: 'string', description: 'Template generation' },
|
||||
updatedAt: { type: 'string', description: 'Last update timestamp' },
|
||||
versions: { type: 'json', description: 'Array of template versions' },
|
||||
},
|
||||
}
|
||||
121
apps/sim/tools/sendgrid/create_template_version.ts
Normal file
121
apps/sim/tools/sendgrid/create_template_version.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import type {
|
||||
CreateTemplateVersionParams,
|
||||
SendGridTemplateVersionRequest,
|
||||
TemplateVersionResult,
|
||||
} from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const sendGridCreateTemplateVersionTool: ToolConfig<
|
||||
CreateTemplateVersionParams,
|
||||
TemplateVersionResult
|
||||
> = {
|
||||
id: 'sendgrid_create_template_version',
|
||||
name: 'SendGrid Create Template Version',
|
||||
description: 'Create a new version of an email template in SendGrid',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
templateId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Template ID',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Version name',
|
||||
},
|
||||
subject: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Email subject line',
|
||||
},
|
||||
htmlContent: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'HTML content of the template',
|
||||
},
|
||||
plainContent: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Plain text content of the template',
|
||||
},
|
||||
active: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether this version is active (default: true)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.sendgrid.com/v3/templates/${params.templateId}/versions`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: SendGridTemplateVersionRequest = {
|
||||
name: params.name,
|
||||
subject: params.subject,
|
||||
active: params.active !== undefined ? params.active : 1,
|
||||
}
|
||||
|
||||
if (params.htmlContent) {
|
||||
body.html_content = params.htmlContent
|
||||
}
|
||||
|
||||
if (params.plainContent) {
|
||||
body.plain_content = params.plainContent
|
||||
}
|
||||
|
||||
return { body: JSON.stringify(body) }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<TemplateVersionResult> => {
|
||||
if (!response.ok) {
|
||||
const error = await response.json()
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to create template version')
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
templateId: data.template_id,
|
||||
name: data.name,
|
||||
subject: data.subject,
|
||||
active: data.active === 1,
|
||||
htmlContent: data.html_content,
|
||||
plainContent: data.plain_content,
|
||||
updatedAt: data.updated_at,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Version ID' },
|
||||
templateId: { type: 'string', description: 'Template ID' },
|
||||
name: { type: 'string', description: 'Version name' },
|
||||
subject: { type: 'string', description: 'Email subject' },
|
||||
active: { type: 'boolean', description: 'Whether this version is active' },
|
||||
htmlContent: { type: 'string', description: 'HTML content' },
|
||||
plainContent: { type: 'string', description: 'Plain text content' },
|
||||
updatedAt: { type: 'string', description: 'Last update timestamp' },
|
||||
},
|
||||
}
|
||||
58
apps/sim/tools/sendgrid/delete_contacts.ts
Normal file
58
apps/sim/tools/sendgrid/delete_contacts.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import type { DeleteContactParams } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
export const sendGridDeleteContactsTool: ToolConfig<DeleteContactParams, ToolResponse> = {
|
||||
id: 'sendgrid_delete_contacts',
|
||||
name: 'SendGrid Delete Contacts',
|
||||
description: 'Delete one or more contacts from SendGrid',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
contactIds: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated contact IDs to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const ids = params.contactIds
|
||||
.split(',')
|
||||
.map((id) => id.trim())
|
||||
.join(',')
|
||||
return `https://api.sendgrid.com/v3/marketing/contacts?ids=${encodeURIComponent(ids)}`
|
||||
},
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<ToolResponse> => {
|
||||
if (!response.ok) {
|
||||
const error = (await response.json()) as { errors?: Array<{ message?: string }> }
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to delete contacts')
|
||||
}
|
||||
|
||||
const data = (await response.json()) as { job_id: string }
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
jobId: data.job_id,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
jobId: { type: 'string', description: 'Job ID for the deletion request' },
|
||||
},
|
||||
}
|
||||
51
apps/sim/tools/sendgrid/delete_list.ts
Normal file
51
apps/sim/tools/sendgrid/delete_list.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import type { DeleteListParams } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
export const sendGridDeleteListTool: ToolConfig<DeleteListParams, ToolResponse> = {
|
||||
id: 'sendgrid_delete_list',
|
||||
name: 'SendGrid Delete List',
|
||||
description: 'Delete a contact list from SendGrid',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
listId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'List ID to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.sendgrid.com/v3/marketing/lists/${params.listId}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<ToolResponse> => {
|
||||
if (!response.ok) {
|
||||
const error = (await response.json()) as { errors?: Array<{ message?: string }> }
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to delete list')
|
||||
}
|
||||
|
||||
// API returns 204 No Content on success
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
message: 'List deleted successfully',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
message: { type: 'string', description: 'Success message' },
|
||||
},
|
||||
}
|
||||
46
apps/sim/tools/sendgrid/delete_template.ts
Normal file
46
apps/sim/tools/sendgrid/delete_template.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import type { DeleteTemplateParams } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
export const sendGridDeleteTemplateTool: ToolConfig<DeleteTemplateParams, ToolResponse> = {
|
||||
id: 'sendgrid_delete_template',
|
||||
name: 'SendGrid Delete Template',
|
||||
description: 'Delete an email template from SendGrid',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
templateId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Template ID to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.sendgrid.com/v3/templates/${params.templateId}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<ToolResponse> => {
|
||||
if (!response.ok) {
|
||||
const error = (await response.json()) as { errors?: Array<{ message?: string }> }
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to delete template')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {},
|
||||
}
|
||||
66
apps/sim/tools/sendgrid/get_contact.ts
Normal file
66
apps/sim/tools/sendgrid/get_contact.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import type { ContactResult, GetContactParams, SendGridContact } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const sendGridGetContactTool: ToolConfig<GetContactParams, ContactResult> = {
|
||||
id: 'sendgrid_get_contact',
|
||||
name: 'SendGrid Get Contact',
|
||||
description: 'Get a specific contact by ID from SendGrid',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
contactId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Contact ID',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.sendgrid.com/v3/marketing/contacts/${params.contactId}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<ContactResult> => {
|
||||
if (!response.ok) {
|
||||
const error = (await response.json()) as { errors?: Array<{ message?: string }> }
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to get contact')
|
||||
}
|
||||
|
||||
const data = (await response.json()) as SendGridContact
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
email: data.email,
|
||||
firstName: data.first_name,
|
||||
lastName: data.last_name,
|
||||
createdAt: data.created_at,
|
||||
updatedAt: data.updated_at,
|
||||
listIds: data.list_ids,
|
||||
customFields: data.custom_fields,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Contact ID' },
|
||||
email: { type: 'string', description: 'Contact email address' },
|
||||
firstName: { type: 'string', description: 'Contact first name' },
|
||||
lastName: { type: 'string', description: 'Contact last name' },
|
||||
createdAt: { type: 'string', description: 'Creation timestamp' },
|
||||
updatedAt: { type: 'string', description: 'Last update timestamp' },
|
||||
listIds: { type: 'json', description: 'Array of list IDs the contact belongs to' },
|
||||
customFields: { type: 'json', description: 'Custom field values' },
|
||||
},
|
||||
}
|
||||
56
apps/sim/tools/sendgrid/get_list.ts
Normal file
56
apps/sim/tools/sendgrid/get_list.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import type { GetListParams, ListResult, SendGridList } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const sendGridGetListTool: ToolConfig<GetListParams, ListResult> = {
|
||||
id: 'sendgrid_get_list',
|
||||
name: 'SendGrid Get List',
|
||||
description: 'Get a specific list by ID from SendGrid',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
listId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'List ID',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.sendgrid.com/v3/marketing/lists/${params.listId}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<ListResult> => {
|
||||
if (!response.ok) {
|
||||
const error = (await response.json()) as { errors?: Array<{ message?: string }> }
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to get list')
|
||||
}
|
||||
|
||||
const data = (await response.json()) as SendGridList
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
contactCount: data.contact_count,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'List ID' },
|
||||
name: { type: 'string', description: 'List name' },
|
||||
contactCount: { type: 'number', description: 'Number of contacts in the list' },
|
||||
},
|
||||
}
|
||||
60
apps/sim/tools/sendgrid/get_template.ts
Normal file
60
apps/sim/tools/sendgrid/get_template.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import type { GetTemplateParams, SendGridTemplate, TemplateResult } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const sendGridGetTemplateTool: ToolConfig<GetTemplateParams, TemplateResult> = {
|
||||
id: 'sendgrid_get_template',
|
||||
name: 'SendGrid Get Template',
|
||||
description: 'Get a specific template by ID from SendGrid',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
templateId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Template ID',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.sendgrid.com/v3/templates/${params.templateId}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<TemplateResult> => {
|
||||
if (!response.ok) {
|
||||
const error = (await response.json()) as { errors?: Array<{ message?: string }> }
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to get template')
|
||||
}
|
||||
|
||||
const data = (await response.json()) as SendGridTemplate
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
generation: data.generation,
|
||||
updatedAt: data.updated_at,
|
||||
versions: data.versions || [],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Template ID' },
|
||||
name: { type: 'string', description: 'Template name' },
|
||||
generation: { type: 'string', description: 'Template generation' },
|
||||
updatedAt: { type: 'string', description: 'Last update timestamp' },
|
||||
versions: { type: 'json', description: 'Array of template versions' },
|
||||
},
|
||||
}
|
||||
23
apps/sim/tools/sendgrid/index.ts
Normal file
23
apps/sim/tools/sendgrid/index.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
// Mail Send
|
||||
|
||||
// Contact Management
|
||||
export { sendGridAddContactTool } from './add_contact'
|
||||
export { sendGridAddContactsToListTool } from './add_contacts_to_list'
|
||||
// List Management
|
||||
export { sendGridCreateListTool } from './create_list'
|
||||
// Template Management
|
||||
export { sendGridCreateTemplateTool } from './create_template'
|
||||
export { sendGridCreateTemplateVersionTool } from './create_template_version'
|
||||
export { sendGridDeleteContactsTool } from './delete_contacts'
|
||||
export { sendGridDeleteListTool } from './delete_list'
|
||||
export { sendGridDeleteTemplateTool } from './delete_template'
|
||||
export { sendGridGetContactTool } from './get_contact'
|
||||
export { sendGridGetListTool } from './get_list'
|
||||
export { sendGridGetTemplateTool } from './get_template'
|
||||
export { sendGridListAllListsTool } from './list_all_lists'
|
||||
export { sendGridListTemplatesTool } from './list_templates'
|
||||
export { sendGridRemoveContactsFromListTool } from './remove_contacts_from_list'
|
||||
export { sendGridSearchContactsTool } from './search_contacts'
|
||||
export { sendGridSendMailTool } from './send_mail'
|
||||
// Types
|
||||
export * from './types'
|
||||
58
apps/sim/tools/sendgrid/list_all_lists.ts
Normal file
58
apps/sim/tools/sendgrid/list_all_lists.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import type { ListAllListsParams, ListsResult, SendGridList } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const sendGridListAllListsTool: ToolConfig<ListAllListsParams, ListsResult> = {
|
||||
id: 'sendgrid_list_all_lists',
|
||||
name: 'SendGrid List All Lists',
|
||||
description: 'Get all contact lists from SendGrid',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of lists to return per page (default: 100)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://api.sendgrid.com/v3/marketing/lists')
|
||||
if (params.pageSize) {
|
||||
url.searchParams.append('page_size', params.pageSize.toString())
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<ListsResult> => {
|
||||
if (!response.ok) {
|
||||
const error = (await response.json()) as { errors?: Array<{ message?: string }> }
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to list all lists')
|
||||
}
|
||||
|
||||
const data = (await response.json()) as { result?: SendGridList[] }
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
lists: data.result || [],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
lists: { type: 'json', description: 'Array of lists' },
|
||||
},
|
||||
}
|
||||
70
apps/sim/tools/sendgrid/list_templates.ts
Normal file
70
apps/sim/tools/sendgrid/list_templates.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import type { ListTemplatesParams, SendGridTemplate, TemplatesResult } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const sendGridListTemplatesTool: ToolConfig<ListTemplatesParams, TemplatesResult> = {
|
||||
id: 'sendgrid_list_templates',
|
||||
name: 'SendGrid List Templates',
|
||||
description: 'Get all email templates from SendGrid',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
generations: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by generation (legacy, dynamic, or both)',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of templates to return per page (default: 20)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://api.sendgrid.com/v3/templates')
|
||||
if (params.generations) {
|
||||
url.searchParams.append('generations', params.generations)
|
||||
}
|
||||
if (params.pageSize) {
|
||||
url.searchParams.append('page_size', params.pageSize.toString())
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<TemplatesResult> => {
|
||||
if (!response.ok) {
|
||||
const error = (await response.json()) as { errors?: Array<{ message?: string }> }
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to list templates')
|
||||
}
|
||||
|
||||
const data = (await response.json()) as {
|
||||
result?: SendGridTemplate[]
|
||||
templates?: SendGridTemplate[]
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
templates: data.result || data.templates || [],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
templates: { type: 'json', description: 'Array of templates' },
|
||||
},
|
||||
}
|
||||
67
apps/sim/tools/sendgrid/remove_contacts_from_list.ts
Normal file
67
apps/sim/tools/sendgrid/remove_contacts_from_list.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import type { RemoveContactsFromListParams } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
export const sendGridRemoveContactsFromListTool: ToolConfig<
|
||||
RemoveContactsFromListParams,
|
||||
ToolResponse
|
||||
> = {
|
||||
id: 'sendgrid_remove_contacts_from_list',
|
||||
name: 'SendGrid Remove Contacts from List',
|
||||
description: 'Remove contacts from a specific list in SendGrid',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
listId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'List ID',
|
||||
},
|
||||
contactIds: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated contact IDs to remove from the list',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const contactIds = params.contactIds
|
||||
.split(',')
|
||||
.map((id) => id.trim())
|
||||
.join(',')
|
||||
return `https://api.sendgrid.com/v3/marketing/lists/${params.listId}/contacts?contact_ids=${encodeURIComponent(contactIds)}`
|
||||
},
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<ToolResponse> => {
|
||||
if (!response.ok) {
|
||||
const error = (await response.json()) as { errors?: Array<{ message?: string }> }
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to remove contacts from list')
|
||||
}
|
||||
|
||||
const data = (await response.json()) as { job_id?: string }
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
jobId: data.job_id,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
jobId: { type: 'string', description: 'Job ID for the request' },
|
||||
},
|
||||
}
|
||||
66
apps/sim/tools/sendgrid/search_contacts.ts
Normal file
66
apps/sim/tools/sendgrid/search_contacts.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import type { ContactsResult, SearchContactsParams, SendGridContact } from '@/tools/sendgrid/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const sendGridSearchContactsTool: ToolConfig<SearchContactsParams, ContactsResult> = {
|
||||
id: 'sendgrid_search_contacts',
|
||||
name: 'SendGrid Search Contacts',
|
||||
description: 'Search for contacts in SendGrid using a query',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'SendGrid API key',
|
||||
},
|
||||
query: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
"Search query (e.g., \"email LIKE '%example.com%' AND CONTAINS(list_ids, 'list-id')\")",
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => 'https://api.sendgrid.com/v3/marketing/contacts/search',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
query: params.query,
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<ContactsResult> => {
|
||||
if (!response.ok) {
|
||||
const error = (await response.json()) as { errors?: Array<{ message?: string }> }
|
||||
throw new Error(error.errors?.[0]?.message || 'Failed to search contacts')
|
||||
}
|
||||
|
||||
const data = (await response.json()) as {
|
||||
result?: SendGridContact[]
|
||||
contact_count?: number
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
contacts: data.result || [],
|
||||
contactCount: data.contact_count,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
contacts: { type: 'json', description: 'Array of matching contacts' },
|
||||
contactCount: { type: 'number', description: 'Total number of contacts found' },
|
||||
},
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user