├── .editorconfig
├── .eslintrc.js
├── .eslintrc.prepublish.js
├── .gitignore
├── .prettierrc.js
├── .vscode
├── extensions.json
└── settings.json
├── CODE_OF_CONDUCT.md
├── LICENSE.md
├── README.md
├── SECURITY.md
├── assets
├── codechat_credentials.png
├── codechat_search_bar.png
├── cover.png
├── resource_send_message.png
├── settings_community_nodes.png
└── settings_community_nodes_codechat.png
├── credentials
└── CodeChatCredentialsApi.credentials.ts
├── gulpfile.js
├── nodes
├── .gitkeep
└── CodeChat
│ ├── CodeChat.d.ts
│ ├── CodeChat.fields.ts
│ ├── CodeChat.node.json
│ ├── CodeChat.node.ts
│ ├── Generic.func.ts
│ ├── codechat.svg
│ └── descriptions
│ ├── Chat.desc.ts
│ ├── Group.desc.ts
│ └── SendMessage.desc.ts
├── package-lock.json
├── package.json
├── tsconfig.json
└── tslint.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = tab
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [package.json]
12 | indent_style = space
13 | indent_size = 2
14 |
15 | [*.md]
16 | trim_trailing_whitespace = false
17 |
18 | [*.yml]
19 | indent_style = space
20 | indent_size = 2
21 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 |
4 | env: {
5 | browser: true,
6 | es6: true,
7 | node: true,
8 | },
9 |
10 | parser: '@typescript-eslint/parser',
11 | parserOptions: {
12 | project: ['./tsconfig.json'],
13 | sourceType: 'module',
14 | extraFileExtensions: ['.json'],
15 | },
16 | ignorePatterns: [
17 | '.eslintrc.js',
18 | '**/*.js',
19 | '**/node_modules/**',
20 | '**/dist/**',
21 | ],
22 |
23 | overrides: [
24 | {
25 | files: ['package.json'],
26 | plugins: ['eslint-plugin-n8n-nodes-base/'],
27 | extends: ['plugin:n8n-nodes-base/community'],
28 | rules: {
29 | 'n8n-nodes-base/community-package-json-name-still-default': 'off',
30 | }
31 | },
32 | {
33 | files: ['./credentials/**/*.ts'],
34 | plugins: ['eslint-plugin-n8n-nodes-base'],
35 | extends: ['plugin:n8n-nodes-base/credentials'],
36 | rules: {
37 | 'n8n-nodes-base/cred-class-field-documentation-url-missing': 'off',
38 | 'n8n-nodes-base/cred-class-field-documentation-url-miscased': 'off',
39 | },
40 | },
41 | {
42 | files: ['./nodes/**/*.ts'],
43 | plugins: ['eslint-plugin-n8n-nodes-base'],
44 | extends: ['plugin:n8n-nodes-base/nodes'],
45 | rules: {
46 | 'n8n-nodes-base/node-execute-block-missing-continue-on-fail': 'off',
47 | 'n8n-nodes-base/node-resource-description-filename-against-convention': 'off',
48 | 'n8n-nodes-base/node-param-fixed-collection-type-unsorted-items': 'off',
49 | 'n8n-nodes-base/node-execute-block-operation-missing-singular-pairing': 'off',
50 | 'n8n-nodes-base/node-execute-block-operation-missing-plural-pairing': 'off',
51 | },
52 | },
53 | ],
54 | };
55 |
--------------------------------------------------------------------------------
/.eslintrc.prepublish.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: './.eslintrc.js',
3 | overrides: [
4 | {
5 | files: ['package.json'],
6 | plugins: ['eslint-plugin-n8n-nodes-base'],
7 | rules: {
8 | 'n8n-nodes-base/community-package-json-name-still-default': 'error',
9 | },
10 | },
11 | ],
12 | };
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | .tmp
4 | tmp
5 | dist
6 | npm-debug.log*
7 | yarn.lock
8 | .vscode/launch.json
9 | .npmrc
10 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | /**
3 | * https://prettier.io/docs/en/options.html#semicolons
4 | */
5 | semi: true,
6 |
7 | /**
8 | * https://prettier.io/docs/en/options.html#trailing-commas
9 | */
10 | trailingComma: 'all',
11 |
12 | /**
13 | * https://prettier.io/docs/en/options.html#bracket-spacing
14 | */
15 | bracketSpacing: true,
16 |
17 | /**
18 | * https://prettier.io/docs/en/options.html#tabs
19 | */
20 | useTabs: true,
21 |
22 | /**
23 | * https://prettier.io/docs/en/options.html#tab-width
24 | */
25 | tabWidth: 2,
26 |
27 | /**
28 | * https://prettier.io/docs/en/options.html#arrow-function-parentheses
29 | */
30 | arrowParens: 'always',
31 |
32 | /**
33 | * https://prettier.io/docs/en/options.html#quotes
34 | */
35 | singleQuote: true,
36 |
37 | /**
38 | * https://prettier.io/docs/en/options.html#quote-props
39 | */
40 | quoteProps: 'as-needed',
41 |
42 | /**
43 | * https://prettier.io/docs/en/options.html#end-of-line
44 | */
45 | endOfLine: 'lf',
46 |
47 | /**
48 | * https://prettier.io/docs/en/options.html#print-width
49 | */
50 | printWidth: 100,
51 | };
52 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "dbaeumer.vscode-eslint",
4 | "EditorConfig.EditorConfig",
5 | "esbenp.prettier-vscode",
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.fontSize": 13,
3 | "editor.fontLigatures": true,
4 | "editor.letterSpacing": 0.5,
5 | "editor.smoothScrolling": true,
6 | "editor.tabSize": 2,
7 | "editor.codeActionsOnSave": {
8 | "source.fixAll.eslint": true,
9 | "source.fixAll": true
10 | }
11 | }
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at jan@n8n.io. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright 2022 n8n
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7 | of the Software, and to permit persons to whom the Software is furnished to do
8 | so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | [](https://t.me/codechatBR)
4 | [](https://api.whatsapp.com/send?phone=5531995918699)
5 | 
6 | 
7 | [](https://community.n8n.io/)
8 | 
9 | 
10 |
11 |
12 |
13 | 
14 |
15 | # n8n-nodes-codechat
16 |
17 | This is a [N8n](https://community.n8n.io/) community node. It allows you to use CodeChat Api to communicate with WhatsApp in your workflow.
18 |
19 | You can use most features provided by CodeChat. Like sending messages, creating and manipulating groups and managing chats.
20 |
21 | # Instalation
22 |
23 | 1. Go to **Settings** > **Community Nodes**, and Click Install a community node.
24 | 
25 |
26 | 2. Enter `n8n-nodes-codechat` in the **npm Package Name** field. And click **Install**.
27 | 
28 |
29 | Once installed, you can use the search bar to add the CodeChat node to your workflow.
30 | 
31 |
32 | 
33 |
34 | # Usage
35 |
36 | Node configuration is quite simple, just specify the credentials and use the available resources and operations.
37 |
38 | # Credentials
39 |
40 | All operations require a credential which is composed of the **Api Key**, **Instance Name** and the call **Base Url**. Which will be made available at the time of contracting the service.
41 | 
42 |
43 | # Resources
44 |
45 | This section describes which **CodeChat API** operations are available on this node.
46 |
47 | ## Resource Send Message
48 |
49 | | Operation | Method | Credentials required |
50 | | --------------------|--------|----------------------|
51 | | Send Text | POST | True |
52 | | Send Buttons | POST | True |
53 | | Send Template | POST | True |
54 | | Send Media | POST | True |
55 | | Send Media Base64 | POST | True |
56 | | send Link Preview | POST | True |
57 | | Send Contact | POST | True |
58 | | Send List | POST | True |
59 | | Send WhatsApp Audio | POST | True |
60 |
61 | All operations return a queue for tracking the shipment that can be monitored through the webhook.
62 |
63 | ```json
64 | {
65 | "header": {
66 | "queueId": "20aa6e5f-4545-4f90-b712-9a176c9509da",
67 | "status": "PROCESSING",
68 | "progress": 0
69 | },
70 | "data": {
71 | "jids": [
72 | "5531900000000@s.whatsapp.net"
73 | ]
74 | }
75 | }
76 | ```
77 |
78 | ## Resource Group
79 |
80 | | Operation | Method | Credentials required | Description |
81 | | -----------------------|--------|----------------------|---------------------------------------------------------------|
82 | | Accept Invite | PUT | True | Accept incoming group invite |
83 | | Change Expiration | PUT | True | Sets the expiration of group messages |
84 | | Create Group | POST | True | |
85 | | Fetch Participants | GET | True | |
86 | | Group Metadata | GET | True | Retrieve all information about the group and its participants |
87 | | Invite Code | GET | True | Generates the created group invitation |
88 | | Revoke Invite | PUT | True | |
89 | | Update Info | PUT | True | Update the group's subject or description |
90 | | Update Participants | PUT | True | |
91 | | Update Profile Picture | PUT | True | |
92 | | Update Settings | PUT | True | Update group chat and group handling rules |
93 |
94 | # Resource Chat
95 |
96 | | Operation | Method | Credentials required | Description |
97 | |----------------------|--------|----------------------|-------------------------------------------|
98 | | Block Contact | PUT | True | |
99 | | Business Profile | GET | True | Retrieve business contact information |
100 | | Mark Message As Read | PUT | True | |
101 | | Delete Message | DELETE | True | Delete for me or everyone |
102 | | On WhatsApp | POST | True | Check if the contact is on WhatsApp |
103 | | Profile Picture Url | GET | True | Retrieve a contact's profile picture |
104 | | Update Presence | PUT | True | compising - recording - available - pause |
105 | | Update Status | PUT | True | |
106 |
107 | # Compatibility
108 |
109 | Tested on n8n version 0.198.2 and CodeChat version 2.0.0
110 |
111 | ## License
112 |
113 | [MIT](https://github.com/code-chat-br/n8n-node-codechat/blob/main/LICENSE.md)
114 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | Use this section to tell people about which versions of your project are
6 | currently being supported with security updates.
7 |
8 | | Version | Supported |
9 | | ------- | ------------------ |
10 | | 5.1.x | :white_check_mark: |
11 | | 5.0.x | :x: |
12 | | 4.0.x | :white_check_mark: |
13 | | < 4.0 | :x: |
14 |
15 | ## Reporting a Vulnerability
16 |
17 | Use this section to tell people how to report a vulnerability.
18 |
19 | Tell them where to go, how often they can expect to get an update on a
20 | reported vulnerability, what to expect if the vulnerability is accepted or
21 | declined, etc.
22 |
--------------------------------------------------------------------------------
/assets/codechat_credentials.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-chat-br/n8n-nodes-codechat/d0ecca9de4a1e195c1e791429fe5acdcd7f8219c/assets/codechat_credentials.png
--------------------------------------------------------------------------------
/assets/codechat_search_bar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-chat-br/n8n-nodes-codechat/d0ecca9de4a1e195c1e791429fe5acdcd7f8219c/assets/codechat_search_bar.png
--------------------------------------------------------------------------------
/assets/cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-chat-br/n8n-nodes-codechat/d0ecca9de4a1e195c1e791429fe5acdcd7f8219c/assets/cover.png
--------------------------------------------------------------------------------
/assets/resource_send_message.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-chat-br/n8n-nodes-codechat/d0ecca9de4a1e195c1e791429fe5acdcd7f8219c/assets/resource_send_message.png
--------------------------------------------------------------------------------
/assets/settings_community_nodes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-chat-br/n8n-nodes-codechat/d0ecca9de4a1e195c1e791429fe5acdcd7f8219c/assets/settings_community_nodes.png
--------------------------------------------------------------------------------
/assets/settings_community_nodes_codechat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-chat-br/n8n-nodes-codechat/d0ecca9de4a1e195c1e791429fe5acdcd7f8219c/assets/settings_community_nodes_codechat.png
--------------------------------------------------------------------------------
/credentials/CodeChatCredentialsApi.credentials.ts:
--------------------------------------------------------------------------------
1 | import { ICredentialType, INodeProperties } from 'n8n-workflow';
2 |
3 | export class CodeChatCredentialsApi implements ICredentialType {
4 | name = 'codeChatApi';
5 | displayName = 'CodeChat API';
6 | properties: INodeProperties[] = [
7 | {
8 | displayName: 'Instance Name',
9 | name: 'instanceName',
10 | type: 'string',
11 | default: '',
12 | required: true,
13 | description: 'Name of the instance informed in the registration',
14 | },
15 |
16 | {
17 | displayName: 'Authorization (apiKey)',
18 | name: 'apiKey',
19 | type: 'string',
20 | default: '',
21 | required: true,
22 | description: 'System generated license code',
23 | },
24 |
25 | {
26 | displayName: 'Base URL',
27 | name: 'baseUrl',
28 | type: 'string',
29 | required: true,
30 | default: '',
31 | placeholder: 'https://api.codechat.rest',
32 | hint: 'Inform the url provided by your service provider',
33 | },
34 | ];
35 | documentationUrl = 'https://docs.codechat.dev/';
36 | }
37 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const { task, src, dest } = require('gulp');
3 |
4 | task('build:icons', copyIcons);
5 |
6 | function copyIcons() {
7 | const nodeSource = path.resolve('nodes', '**', '*.{png,svg}');
8 | const nodeDestination = path.resolve('dist', 'nodes');
9 |
10 | src(nodeSource).pipe(dest(nodeDestination));
11 |
12 | const credSource = path.resolve('credentials', '**', '*.{png,svg}');
13 | const credDestination = path.resolve('dist', 'credentials');
14 |
15 | return src(credSource).pipe(dest(credDestination));
16 | }
17 |
--------------------------------------------------------------------------------
/nodes/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code-chat-br/n8n-nodes-codechat/d0ecca9de4a1e195c1e791429fe5acdcd7f8219c/nodes/.gitkeep
--------------------------------------------------------------------------------
/nodes/CodeChat/CodeChat.d.ts:
--------------------------------------------------------------------------------
1 |
2 | export namespace RequestBody {
3 | export type IError = {
4 | error: string | string[];
5 | message: string;
6 | statusCode: number;
7 | };
8 |
9 | export type INumbers = {
10 | numbers: string;
11 | };
12 |
13 | export type IOptions = {
14 | options: {
15 | messageId: string;
16 | mentions: {
17 | everyone: boolean | string,
18 | mentioned: string[],
19 | };
20 | delay: number;
21 | };
22 | };
23 |
24 | export type IMediaData = {
25 | type: 'document' | 'image' | 'video' | 'sticker';
26 | source: string
27 | }
28 |
29 | export type IMedia = {
30 | caption?: string;
31 | url: string;
32 | mediaType: 'document' | 'image' | 'video' | 'sticker';
33 | }
34 |
35 | export type IButtons = {
36 | displayText: string;
37 | buttonId: string;
38 | }
39 |
40 | export type IButtonsMessage = {
41 | mediaData?: IMediaData;
42 | buttons?: {
43 | replyButtons: IButtons[]
44 | };
45 | buttonsMessage: {
46 | title: string;
47 | description: string;
48 | footerText?: string;
49 | buttons: IButtons[];
50 | mediaMessage?: IMedia
51 | };
52 | };
53 |
54 | export type ITemplateButtons = {
55 | buttonType: 'urlButton' | 'replyButton' | 'callButton';
56 | displayText: string;
57 | payload: string;
58 | };
59 |
60 | export type ITemplateMessage = {
61 | mediaData?: IMediaData;
62 | buttons?: {
63 | templateButtons: ITemplateButtons[];
64 | };
65 | templateMessage: {
66 | title: string;
67 | description: string;
68 | footerText?: string;
69 | buttons: ITemplateButtons[];
70 | mediaMessage?: IMedia
71 | };
72 | };
73 |
74 | export type IRow = {
75 | title: string;
76 | description: string;
77 | rowId: string;
78 | };
79 |
80 | export type Isection =
81 | | {
82 | title: string;
83 | rows: IRow[];
84 | } & { rowsProperty?: { rows: IRow[] } };
85 |
86 | export type IListMessage = {
87 | listMessage: {
88 | title: string;
89 | description: string;
90 | footerText: string;
91 | buttonText: string;
92 | sections: Isection[];
93 | };
94 | };
95 |
96 | export type IContactMessage = {
97 | contactMessage: {
98 | contacts: {
99 | wuid: string;
100 | phoneNumber: string;
101 | fullName: string;
102 | }[]
103 | }
104 | }
105 |
106 | export type IReadMessage = {
107 | readMessage: {
108 | messageId: string;
109 | fromMe: boolean;
110 | wuid: string;
111 | }
112 | };
113 |
114 | export type ICreateGroup = {
115 | participants: string | string[];
116 | groupSubject: string;
117 | groupDescription: string;
118 | profilePicture?: string;
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/nodes/CodeChat/CodeChat.fields.ts:
--------------------------------------------------------------------------------
1 | import { INodeProperties } from 'n8n-workflow';
2 | import {
3 | blockCobtactProperties,
4 | budinessProfileProperties,
5 | deleteMessageProperties,
6 | markMSGReadProperties,
7 | onWhatsappProperties,
8 | profilePictureProperties,
9 | statusContactPorperties,
10 | updatePresence,
11 | updateStatusProperties,
12 | } from './descriptions/Chat.desc';
13 | import {
14 | acceptInviteCodeProperties,
15 | changeExpirationProperties,
16 | createGroupPrperties,
17 | fetchParticipantsProperties,
18 | groupInviteCodeProperties,
19 | groupMetadataProperties,
20 | leaveGroupProperties,
21 | revokeInviteCodeProperties,
22 | updateGroupProperties,
23 | updateParticipantsPorperties,
24 | updatePropfilePictureProperties,
25 | updateRuleGroupProperties,
26 | } from './descriptions/Group.desc';
27 | import {
28 | buttonsProperties,
29 | contactProperties,
30 | linkPreviewProperties,
31 | listProperties,
32 | locationProperties,
33 | mediaBase64MessgeProperties,
34 | mediaMessageProperties,
35 | optionsProperties,
36 | templateProperties,
37 | textProperties,
38 | whatsAppAudioProperties,
39 | } from './descriptions/SendMessage.desc';
40 | import { formatNumber } from './Generic.func';
41 |
42 | const messageResource: INodeProperties[] = [
43 | {
44 | displayName: 'List Recipient Phone Numbers',
45 | name: 'listPhoneNumbers',
46 | type: 'json',
47 | default: [],
48 | placeholder: `[Array:['5531900000000, '5521911111111']] or 5531922222222`,
49 | description: 'This field supports both a list and a single number',
50 | hint: 'When entering a phone number, make sure to include the country code',
51 | routing: { send: { type: 'body', property: 'numbers', preSend: [formatNumber] } },
52 | displayOptions: { show: { resource: ['sendMessage'] } },
53 | },
54 |
55 | /**┌──────────────────────────────┐
56 | * │ Options Properties │
57 | * └──────────────────────────────┘
58 | */
59 | ...optionsProperties,
60 |
61 | {
62 | displayName: 'Operation',
63 | name: 'operation',
64 | noDataExpression: true,
65 | placeholder: '',
66 | required: true,
67 | type: 'options',
68 | options: [
69 | {
70 | name: 'Send Buttons',
71 | value: 'sendButtons',
72 | action: 'Send buttons a send message',
73 | },
74 | {
75 | name: 'Send Contact',
76 | value: 'sendContact',
77 | action: 'Send contact a send message',
78 | },
79 | {
80 | name: 'Send Link Preview',
81 | value: 'sendLinkPreview',
82 | action: 'Send link preview a send message',
83 | },
84 | {
85 | name: 'Send List',
86 | value: 'sendList',
87 | action: 'Send list a send message',
88 | },
89 | {
90 | name: 'Send Location',
91 | value: 'sendLocation',
92 | action: 'Send location a send message',
93 | },
94 | {
95 | name: 'Send Media',
96 | value: 'sendMedia',
97 | action: 'Send media a send message',
98 | },
99 | {
100 | name: 'Send Media Base64',
101 | value: 'sendMediaBase64',
102 | action: 'Send media base64 a send message',
103 | },
104 | {
105 | name: 'Send Template',
106 | value: 'sendTemplate',
107 | action: 'Send template a send message',
108 | },
109 | {
110 | name: 'Send Text',
111 | value: 'sendText',
112 | action: 'Send text a send message',
113 | },
114 | {
115 | name: 'Send WhatsApp Audio',
116 | value: 'sendWhatsAppAudio',
117 | action: 'Send whats app audio a send message',
118 | },
119 | ],
120 | default: 'sendText',
121 | displayOptions: { show: { resource: ['sendMessage'] } },
122 | },
123 |
124 | /**┌───────────────────────────┐
125 | * │ Text Properties │
126 | * └───────────────────────────┘
127 | */
128 | ...textProperties,
129 |
130 | /**┌──────────────────────────────┐
131 | * │ Buttons Properties │
132 | * └──────────────────────────────┘
133 | */
134 | ...buttonsProperties,
135 |
136 | /**┌───────────────────────────────┐
137 | * │ Template Properties │
138 | * └───────────────────────────────┘
139 | */
140 | ...templateProperties,
141 |
142 | /**┌────────────────────────────┐
143 | * │ Media Properties │
144 | * └────────────────────────────┘
145 | */
146 | ...mediaMessageProperties,
147 |
148 | /**┌───────────────────────────────────┐
149 | * │ Media Base64 Properties │
150 | * └───────────────────────────────────┘
151 | */
152 | ...mediaBase64MessgeProperties,
153 |
154 | /**┌───────────────────────────────┐
155 | * │ WhatsApp Properties │
156 | * └───────────────────────────────┘
157 | */
158 | ...whatsAppAudioProperties,
159 |
160 | /**┌───────────────────────────────┐
161 | * │ Location Properties │
162 | * └───────────────────────────────┘
163 | */
164 | ...locationProperties,
165 |
166 | /**┌───────────────────────────┐
167 | * │ List Properties │
168 | * └───────────────────────────┘
169 | */
170 | ...listProperties,
171 |
172 | /**┌───────────────────────────────────┐
173 | * │ Link Preview Properties │
174 | * └───────────────────────────────────┘
175 | */
176 | ...linkPreviewProperties,
177 |
178 | /**┌───────────────────────────────┐
179 | * │ Contact Properties │
180 | * └───────────────────────────────┘
181 | */
182 | ...contactProperties,
183 | ];
184 |
185 | const chatResource: INodeProperties[] = [
186 | {
187 | displayName: 'Operation',
188 | name: 'operation',
189 | noDataExpression: true,
190 | placeholder: '',
191 | required: true,
192 | type: 'options',
193 | options: [
194 | {
195 | name: 'Block Contact',
196 | value: 'blockContact',
197 | action: 'Block contact a chat',
198 | },
199 | {
200 | name: 'Business Profile',
201 | value: 'businesProfile',
202 | action: 'Business profile a chat',
203 | },
204 | {
205 | name: 'Contact Status',
206 | value: 'contactStatus',
207 | action: 'Contact status a chat',
208 | },
209 | {
210 | name: 'Delete Message',
211 | value: 'deleteMessage',
212 | action: 'Delete message a chat',
213 | },
214 | {
215 | name: 'Mark Message As Read',
216 | value: 'markMessageAsRead',
217 | action: 'Mark message as read a chat',
218 | },
219 | {
220 | name: 'On WhatsApp',
221 | value: 'onWhatsApp',
222 | action: 'On whats app a chat',
223 | },
224 | {
225 | name: 'Profile Picture Url',
226 | value: 'profilePictureUrl',
227 | action: 'Profile picture url a chat',
228 | },
229 | {
230 | name: 'Update Presence',
231 | value: 'updatePresence',
232 | action: 'Update presence a chat',
233 | },
234 | {
235 | name: 'Update Status',
236 | value: 'updateStatus',
237 | action: 'Update status a chat',
238 | },
239 | ],
240 | default: 'onWhatsApp',
241 | routing: { request: { ignoreHttpStatusErrors: true } },
242 | displayOptions: { show: { resource: ['chat'] } },
243 | },
244 |
245 | /**┌───────────────────────────────────┐
246 | * │ On WhatsApp Properties │
247 | * └───────────────────────────────────┘
248 | */
249 | ...onWhatsappProperties,
250 |
251 | /**┌───────────────────────────────────────┐
252 | * │ Update Presence Properties │
253 | * └───────────────────────────────────────┘
254 | */
255 | ...updatePresence,
256 |
257 | /**┌───────────────────────────────────┐
258 | * │ Read Message Properties │
259 | * └───────────────────────────────────┘
260 | */
261 | ...markMSGReadProperties,
262 |
263 | /**┌────────────────────────────────────┐
264 | * │ Delete Messge Properties │
265 | * └────────────────────────────────────┘
266 | */
267 | ...deleteMessageProperties,
268 |
269 | /**┌────────────────────────────────────┐
270 | * │ Block Contact Properties │
271 | * └────────────────────────────────────┘
272 | */
273 | ...blockCobtactProperties,
274 |
275 | /**┌─────────────────────────────────────┐
276 | * │ Status Contact Properties │
277 | * └─────────────────────────────────────┘
278 | */
279 | ...statusContactPorperties,
280 |
281 | /**┌────────────────────────────────────┐
282 | * │ Update Status Properties │
283 | * └────────────────────────────────────┘
284 | */
285 | ...updateStatusProperties,
286 |
287 | /**┌───────────────────────────────────────┐
288 | * │ Business Profile Properties │
289 | * └───────────────────────────────────────┘
290 | */
291 | ...budinessProfileProperties,
292 |
293 | /**┌──────────────────────────────────────┐
294 | * │ Profile Picture Properties │
295 | * └──────────────────────────────────────┘
296 | */
297 | ...profilePictureProperties,
298 | ];
299 |
300 | const groupResource: INodeProperties[] = [
301 | {
302 | displayName: 'Operation',
303 | name: 'operation',
304 | required: true,
305 | noDataExpression: true,
306 | type: 'options',
307 | options: [
308 | {
309 | name: 'Accept Invite',
310 | value: 'acceptInvite',
311 | action: 'Accept invite a group',
312 | },
313 | {
314 | name: 'Change Expiration',
315 | value: 'changeExpiration',
316 | action: 'Change expiration a group',
317 | },
318 | {
319 | name: 'Create Group',
320 | value: 'createGroup',
321 | action: 'Create group a group',
322 | },
323 | {
324 | name: 'Fetch Participants',
325 | value: 'fetchParticpants',
326 | action: 'Fetch participants a group',
327 | },
328 | {
329 | name: 'Group Metadata',
330 | value: 'groupMetadata',
331 | action: 'Group metadata a group',
332 | },
333 | {
334 | name: 'Invite Code',
335 | value: 'inviteCode',
336 | action: 'Invite code a group',
337 | },
338 | {
339 | name: 'Leave the Group',
340 | value: 'leaveGroup',
341 | action: 'Leave the group a group',
342 | },
343 | {
344 | name: 'Revoke Invite',
345 | value: 'revokeInvite',
346 | action: 'Revoke invite a group',
347 | },
348 | {
349 | name: 'Update Info',
350 | value: 'updateInfo',
351 | action: 'Update info a group',
352 | },
353 | {
354 | name: 'Update Participants',
355 | value: 'updateParticipants',
356 | action: 'Update participants a group',
357 | },
358 | {
359 | name: 'Update Profile Picture',
360 | value: 'updatePicture',
361 | action: 'Update profile picture a group',
362 | },
363 | {
364 | name: 'Update Rules',
365 | value: 'updateRule',
366 | action: 'Update rules a group',
367 | },
368 | ],
369 | default: 'createGroup',
370 | displayOptions: {
371 | show: { resource: ['group'] },
372 | },
373 | },
374 |
375 | /**┌───────────────────────────────────┐
376 | * │ Create Group Properties │
377 | * └───────────────────────────────────┘
378 | */
379 | ...createGroupPrperties,
380 |
381 | /**┌──────────────────────────────────┐
382 | * │ Invite Code Properties │
383 | * └──────────────────────────────────┘
384 | */
385 | ...groupInviteCodeProperties,
386 |
387 | /**┌────────────────────────────────────┐
388 | * │ Accept Invite Properties │
389 | * └────────────────────────────────────┘
390 | */
391 | ...acceptInviteCodeProperties,
392 |
393 | /**┌────────────────────────────────────┐
394 | * │ Revoke Invite Properties │
395 | * └────────────────────────────────────┘
396 | */
397 | ...revokeInviteCodeProperties,
398 |
399 | /**┌───────────────────────────────────┐
400 | * │ Update Group Properties │
401 | * └───────────────────────────────────┘
402 | */
403 | ...updateGroupProperties,
404 |
405 | /**┌─────────────────────────────────────────────┐
406 | * │ Update Profile Picture Properties │
407 | * └─────────────────────────────────────────────┘
408 | */
409 | ...updatePropfilePictureProperties,
410 |
411 | /**┌─────────────────────────────────────┐
412 | * │ Group Metadata Properties │
413 | * └─────────────────────────────────────┘
414 | */
415 | ...groupMetadataProperties,
416 |
417 | /**┌──────────────────────────────────────────┐
418 | * │ Update Participants Properties │
419 | * └──────────────────────────────────────────┘
420 | */
421 | ...updateParticipantsPorperties,
422 |
423 | /**┌──────────────────────────────────────┐
424 | * │ Update Settings Properties │
425 | * └──────────────────────────────────────┘
426 | */
427 | ...updateRuleGroupProperties,
428 |
429 | /**┌────────────────────────────────────────┐
430 | * │ Change Expiration Properties │
431 | * └────────────────────────────────────────┘
432 | */
433 | ...changeExpirationProperties,
434 |
435 | /**┌────────────────────────────────────────────┐
436 | * │ Retrieve Participants Properties │
437 | * └────────────────────────────────────────────┘
438 | */
439 | ...fetchParticipantsProperties,
440 |
441 | /**┌──────────────────────────────────┐
442 | * │ Leave Group Properties │
443 | * └──────────────────────────────────┘
444 | */
445 | ...leaveGroupProperties,
446 | ];
447 |
448 | export const codechatFields: INodeProperties[] = [
449 | /**┌─────────────────────────────┐
450 | * │ Resource: Message │
451 | * └─────────────────────────────┘
452 | */
453 | ...messageResource,
454 |
455 | /**┌──────────────────────────┐
456 | * │ Resource: Caht │
457 | * └──────────────────────────┘
458 | */
459 | ...chatResource,
460 |
461 | /**┌───────────────────────────┐
462 | * │ Resource: Group │
463 | * └───────────────────────────┘
464 | */
465 | ...groupResource,
466 | ];
467 |
--------------------------------------------------------------------------------
/nodes/CodeChat/CodeChat.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "node": "n8n-nodes-base.codechat",
3 | "nodeVersion": "1.0",
4 | "codexVersion": "1.0",
5 | "categories": ["Communication","Development"],
6 | "resources": {
7 | "credentialDocumentation": [
8 | {
9 | "url": "https://docs.codechat.dev/"
10 | }
11 | ],
12 | "primaryDocumentation": [
13 | {
14 | "url": "https://docs.codechat.dev/"
15 | }
16 | ]
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/nodes/CodeChat/CodeChat.node.ts:
--------------------------------------------------------------------------------
1 | import { INodeType, INodeTypeDescription } from 'n8n-workflow';
2 | import { codechatFields } from './CodeChat.fields';
3 | import { sendErrorPostReceive } from './Generic.func';
4 |
5 | export class CodeChat implements INodeType {
6 | description: INodeTypeDescription = {
7 | displayName: 'CodeChat - WhatsApp Api Cloud',
8 | name: 'codeChat',
9 | icon: 'file:codechat.svg',
10 | group: ['output'],
11 | version: 1,
12 | subtitle: '={{$parameter["resource"] + ": " + $parameter["operation"]}}',
13 | description: 'Rest api for communication with WhatsApp',
14 | defaults: { name: 'CodeChat' },
15 | credentials: [{ name: 'codeChatApi', required: true }],
16 | inputs: ['main'],
17 | outputs: ['main'],
18 | requestDefaults: {
19 | baseURL: '={{$credentials.baseUrl}}',
20 | headers: {
21 | apiKey: '={{$credentials.apiKey}}',
22 | },
23 | ignoreHttpStatusErrors: true,
24 | },
25 | properties: [
26 | {
27 | displayName: 'Resource',
28 | required: true,
29 | name: 'resource',
30 | type: 'options',
31 | noDataExpression: true,
32 | options: [
33 | { name: 'Send Message', value: 'sendMessage' },
34 | { name: 'Group', value: 'group' },
35 | { name: 'Chat', value: 'chat' },
36 | ],
37 | default: 'sendMessage',
38 | routing: { output: { postReceive: [sendErrorPostReceive] } },
39 | },
40 |
41 | ...codechatFields,
42 | ],
43 | };
44 | }
45 |
--------------------------------------------------------------------------------
/nodes/CodeChat/Generic.func.ts:
--------------------------------------------------------------------------------
1 | import {
2 | IExecuteSingleFunctions,
3 | IHttpRequestOptions,
4 | IN8nHttpFullResponse,
5 | INodeExecutionData,
6 | NodeApiError,
7 | } from 'n8n-workflow';
8 | import { RequestBody } from './CodeChat';
9 |
10 | export async function sendErrorPostReceive(
11 | this: IExecuteSingleFunctions,
12 | data: INodeExecutionData[],
13 | response: IN8nHttpFullResponse,
14 | ): Promise {
15 | const body = response?.body as RequestBody.IError;
16 | if (body?.error) {
17 | const node = this.getNode();
18 | const creds = await this.getCredentials('codeChatApi');
19 | Object.assign(node.credentials as {}, { creds });
20 |
21 | throw {
22 | httpCode: body.statusCode,
23 | context: body.error,
24 | cause: { error:body.error, message:body.message },
25 | node: this.getNode(),
26 | workflow: this.getWorkflow(),
27 | message: `${body.error} - statusCode ${body.statusCode}`,
28 | description: 'Check the type of properties and values entered',
29 | };
30 | }
31 | return data;
32 | }
33 |
34 | function isNotempty(value: string) {
35 | if (!value) return false;
36 | if (typeof value === 'string' && value === '') return false;
37 | return true;
38 | }
39 |
40 | function join(...paths: string[]) {
41 | let url = '';
42 | paths.forEach((path) => (url += `${path}/`));
43 | return url;
44 | }
45 |
46 | export function requestURL(...paths: string[]) {
47 | return join('{{$credentials.instanceName}}', ...paths);
48 | }
49 |
50 | export async function formatNumber(
51 | this: IExecuteSingleFunctions,
52 | requestOptions: IHttpRequestOptions,
53 | ): Promise {
54 | const body = requestOptions.body as RequestBody.INumbers & RequestBody.IError;
55 |
56 | const node = this.getNode();
57 | const creds = await this.getCredentials('codeChatApi');
58 | Object.assign(node.credentials as {}, creds);
59 |
60 | const numbers: string[] = [];
61 |
62 | if (!Array.isArray(body.numbers)) {
63 | if (
64 | !body?.numbers ||
65 | typeof body.numbers !== 'string' ||
66 | !(body.numbers as string).match(/\d+/g)
67 | ) {
68 | throw {
69 | httpCode: '400',
70 | context: 'Bad Request',
71 | cause: {
72 | error: [
73 | 'listPhoneNumbers cannot be empty',
74 | 'listPhoneNumbers must be a numeric string',
75 | ],
76 | message: 'Check the type of properties and values entered',
77 | },
78 | node,
79 | workflow: this.getWorkflow(),
80 | message: `${'Bad Request'} - statusCode 400`,
81 | };
82 | }
83 | numbers.push(body.numbers);
84 | }
85 |
86 | if (Array.isArray(body.numbers)) {
87 | const values: string[] = body.numbers;
88 | if (values.length === 0 || [...new Set(values)].length !== values.length) {
89 | throw {
90 | httpCode: '400',
91 | context: 'Bad Request',
92 | cause: {
93 | error: [
94 | 'listPhoneNumbers must not be an empty list',
95 | 'listPhoneNumbers must have unique values',
96 | ],
97 | message: 'Check the type of properties and values entered',
98 | },
99 | node,
100 | workflow: this.getWorkflow(),
101 | message: `${'Bad Request'} - statusCode 400`,
102 | };
103 | }
104 |
105 | values.map((v, index) => {
106 | if (!v.match(/\d+/g)) {
107 | throw {
108 | httpCode: '400',
109 | context: 'Bad Request',
110 | cause: {
111 | error: `listPhoneNumbers[${index}] must be a numeric string`,
112 | message: 'Check the type of properties and values entered',
113 | },
114 | node,
115 | workflow: this.getWorkflow(),
116 | message: `${'Bad Request'} - statusCode 400`,
117 | };
118 | }
119 |
120 | numbers.push(v);
121 | });
122 | }
123 | Object.assign(requestOptions.body as {}, { numbers: [...numbers] });
124 |
125 | return requestOptions;
126 | }
127 |
128 | export async function prepareShippingOptions(
129 | this: IExecuteSingleFunctions,
130 | requestOptions: IHttpRequestOptions,
131 | ): Promise {
132 | const body = requestOptions.body as RequestBody.IOptions;
133 |
134 | if(body?.options?.mentions?.everyone === 'everyone') {
135 | body.options.mentions.everyone = body.options.mentions.everyone === 'everyone';
136 | }
137 |
138 | const opts: { [key: string]: {} | number | string[] } = {};
139 | if (body?.options) {
140 | for (const [key, value] of Object.entries(body.options)) {
141 | if (isNotempty(value as string)) {
142 | if (key === 'quoted') {
143 | opts[key] = { messageId: value as string };
144 | continue;
145 | }
146 | opts[key] = value;
147 | }
148 | }
149 | }
150 |
151 | const optsKeys = Object.keys(opts);
152 |
153 | Object.assign(requestOptions.body as {}, { options: optsKeys.length > 0 ? opts : undefined });
154 |
155 | return requestOptions;
156 | }
157 |
158 | export async function sendButtonsMessage(
159 | this: IExecuteSingleFunctions,
160 | requestOptions: IHttpRequestOptions,
161 | ): Promise {
162 | const body = requestOptions.body as RequestBody.IButtonsMessage;
163 |
164 | if (body?.mediaData) {
165 | if (body.mediaData?.type && body.mediaData?.source) {
166 | body.buttonsMessage.mediaMessage = {
167 | mediaType: body.mediaData.type,
168 | url: body.mediaData.source,
169 | };
170 | }
171 | }
172 |
173 | const buttonFieldTypeProperty = this.getNodeParameter('buttonFieldTypeProperty');
174 | if (buttonFieldTypeProperty === 'collection') {
175 | body.buttonsMessage.buttons = body.buttons!.replyButtons;
176 | }
177 |
178 | delete body.buttons;
179 | delete body.mediaData;
180 |
181 | Object.assign(requestOptions.body as {}, { ...body });
182 |
183 | return requestOptions;
184 | }
185 |
186 | export async function sendTemplateMessage(
187 | this: IExecuteSingleFunctions,
188 | requestOptions: IHttpRequestOptions,
189 | ): Promise {
190 | const body = requestOptions.body as RequestBody.ITemplateMessage;
191 |
192 | if (body?.mediaData) {
193 | if (body.mediaData?.type && body.mediaData?.source) {
194 | body.templateMessage.mediaMessage = {
195 | mediaType: body.mediaData?.type,
196 | url: body.mediaData?.source,
197 | };
198 | }
199 | }
200 |
201 | const templateFieldTypeProperty = this.getNodeParameter('templateFieldTypeProperty');
202 | if (templateFieldTypeProperty === 'collection') {
203 | body.templateMessage.buttons = body.buttons!.templateButtons;
204 | }
205 |
206 | delete body.buttons;
207 | delete body.mediaData;
208 |
209 | Object.assign(requestOptions.body as {}, { ...body });
210 |
211 | return requestOptions;
212 | }
213 |
214 | export async function sendListMessage(
215 | this: IExecuteSingleFunctions,
216 | requestOptions: IHttpRequestOptions,
217 | ): Promise {
218 | const body = requestOptions.body as RequestBody.IListMessage;
219 |
220 | const listFieldTypeProperty = this.getNodeParameter('listFieldTypeProperty');
221 |
222 | let sections: RequestBody.Isection[];
223 |
224 | if (listFieldTypeProperty === 'collection') {
225 | const listMessage = {
226 | title: body.listMessage.title,
227 | description: body.listMessage.description,
228 | footerText: body.listMessage.footerText,
229 | buttonText: body.listMessage.buttonText,
230 | sections: (body.listMessage.sections as RequestBody.Isection[]).map((section) => {
231 | return {
232 | title: section.title,
233 | rows: section.rowsProperty!.rows,
234 | };
235 | }),
236 | };
237 |
238 | sections = listMessage.sections;
239 |
240 | Object.assign(requestOptions.body as {}, { listMessage });
241 | } else {
242 | sections = body.listMessage?.sections;
243 | }
244 |
245 | if (
246 | !Array.isArray(sections) ||
247 | sections.length === 0 ||
248 | sections.length !== [...new Set(sections)].length
249 | ) {
250 | throw new NodeApiError(
251 | this.getNode(),
252 | { error: ['List items must not be empty', 'List items must be unique'] },
253 | { message: 'Bad Request', description: 'check properties', httpCode: '400' },
254 | );
255 | }
256 |
257 | sections.forEach((section, index) => {
258 | if (!section?.title) {
259 | throw new NodeApiError(
260 | this.getNode(),
261 | { error: `Section[${index}].title is empty` },
262 | { message: 'Dad Request', description: 'Title cannot be empty', httpCode: '400' },
263 | );
264 | }
265 |
266 | if (
267 | !Array.isArray(section?.rows) ||
268 | !section.rows?.length ||
269 | section.rows.length === 0 ||
270 | [...new Set(section.rows)].length !== section.rows.length
271 | ) {
272 | throw new NodeApiError(
273 | this.getNode(),
274 | { error: 'Empty list items' },
275 | { message: 'Bad Request', description: 'List items cannot be empty', httpCode: '400' },
276 | );
277 | }
278 |
279 | if ([...new Set(section.rows)].length !== section.rows.length) {
280 | throw new NodeApiError(
281 | this.getNode(),
282 | { error: ['List items must not be empty', 'List items must be unique'] },
283 | { message: 'Bad Request', description: 'check properties', httpCode: '400' },
284 | );
285 | }
286 | });
287 |
288 | return requestOptions;
289 | }
290 |
291 | export async function sendContactMessage(
292 | this: IExecuteSingleFunctions,
293 | requestOptions: IHttpRequestOptions,
294 | ): Promise {
295 | const body = requestOptions.body as RequestBody.IContactMessage;
296 |
297 | const contactTypeProperty = this.getNodeParameter('contactTypeProperty');
298 | if (contactTypeProperty === 'collection') {
299 | Object.assign(requestOptions.body as {}, {
300 | contactsMessage: [...body.contactMessage.contacts],
301 | });
302 | }
303 |
304 | return requestOptions;
305 | }
306 |
307 | export async function readMessage(
308 | this: IExecuteSingleFunctions,
309 | requestOptions: IHttpRequestOptions,
310 | ): Promise {
311 | const body = requestOptions.body as RequestBody.IReadMessage;
312 | if (!Array.isArray(body?.readMessage)) {
313 | throw new NodeApiError(
314 | this.getNode(),
315 | { error: 'readMessagesProperty must be an array' },
316 | { message: 'Bad request', description: 'check properties', httpCode: '400' },
317 | );
318 | }
319 |
320 | requestOptions.body = [...body.readMessage];
321 |
322 | return requestOptions;
323 | }
324 |
325 | export async function createGroup(
326 | this: IExecuteSingleFunctions,
327 | requestOptions: IHttpRequestOptions,
328 | ): Promise {
329 | const body = requestOptions.body as RequestBody.ICreateGroup;
330 |
331 | if (!Array.isArray(body.participants)) {
332 | body.participants = [...body.participants.replace(/[' ']+/gm, '').split(/,/)];
333 | }
334 |
335 | const descriptionGruop = this.getNodeParameter(
336 | 'descriptionProperty.groupDescription.description',
337 | );
338 | const profilePicture = this.getNodeParameter(
339 | 'profilePictureProperty.profilePictureGroup.profilePicture',
340 | );
341 |
342 | Object.assign(requestOptions.body as {}, { descriptionGruop, profilePicture });
343 |
344 | return requestOptions;
345 | }
346 |
347 | export async function updateGroupIngo(
348 | this: IExecuteSingleFunctions,
349 | requestOptions: IHttpRequestOptions,
350 | ): Promise {
351 | const body = requestOptions.body as {};
352 |
353 | const keys = Object.keys(body) || [];
354 | if (keys.length === 0) {
355 | throw new NodeApiError(
356 | this.getNode(),
357 | { error: ['The Subject and Description properties are missing', 'Report at least one'] },
358 | { message: 'Bad Request', description: 'Properties not found', httpCode: '400' },
359 | );
360 | }
361 |
362 | return requestOptions;
363 | }
364 |
--------------------------------------------------------------------------------
/nodes/CodeChat/descriptions/Chat.desc.ts:
--------------------------------------------------------------------------------
1 | import { INodeProperties } from 'n8n-workflow';
2 | import { formatNumber, readMessage, requestURL } from '../Generic.func';
3 |
4 | export const onWhatsappProperties: INodeProperties[] = [
5 | {
6 | displayName: 'List Recipient Phone Numbers',
7 | name: 'listPhoneNumbers',
8 | type: 'json',
9 | default: [],
10 | placeholder: `[Array:['5531900000000, '5521911111111']] or 5531922222222`,
11 | description: 'This field supports both a list and a single number',
12 | hint: 'Check if the contact is a whatsapp contact. When entering a phone number, make sure to include the country code',
13 | routing: {
14 | send: { type: 'body', property: 'numbers', preSend: [formatNumber] },
15 | request: {
16 | url: '=' + requestURL('chat', 'onWhatsApp'),
17 | method: 'POST',
18 | },
19 | },
20 | displayOptions: {
21 | show: {
22 | resource: ['chat'],
23 | operation: ['onWhatsApp'],
24 | },
25 | },
26 | },
27 | ];
28 |
29 | export const updatePresence: INodeProperties[] = [
30 | {
31 | displayName: 'Recipient Phone Numbers',
32 | name: 'numberProperty',
33 | required: true,
34 | type: 'string',
35 | default: '',
36 | routing: { send: { type: 'query', property: 'number' } },
37 | displayOptions: {
38 | show: {
39 | resource: ['chat'],
40 | operation: ['updatePresence'],
41 | },
42 | },
43 | },
44 |
45 | {
46 | displayName: 'Precense Type',
47 | name: 'presenceTypeProperty',
48 | required: true,
49 | description: 'Simulate your presence in the chat',
50 | type: 'options',
51 | options: [
52 | {
53 | name: 'Available',
54 | value: 'available',
55 | },
56 | {
57 | name: 'Composing',
58 | value: 'composing',
59 | },
60 | {
61 | name: 'Paused',
62 | value: 'paused',
63 | },
64 | {
65 | name: 'Recording',
66 | value: 'recording',
67 | },
68 | {
69 | name: 'Unavailable',
70 | value: 'unavailable',
71 | },
72 | ],
73 | default: 'available',
74 | routing: { send: { type: 'body', property: 'presence' } },
75 | displayOptions: {
76 | show: {
77 | resource: ['chat'],
78 | operation: ['updatePresence'],
79 | },
80 | },
81 | },
82 |
83 | {
84 | displayName: 'Delay',
85 | name: 'delayProperty',
86 | required: true,
87 | type: 'number',
88 | default: 1200,
89 | routing: { send: { type: 'body', property: 'delay' } },
90 | displayOptions: {
91 | show: {
92 | resource: ['chat'],
93 | operation: ['updatePresence'],
94 | },
95 | },
96 | },
97 |
98 | {
99 | displayName: 'Set Routing',
100 | name: 'setRouting',
101 | type: 'hidden',
102 | default: '',
103 | routing: {
104 | request: {
105 | url: '=' + requestURL('chat', 'updatePresence'),
106 | method: 'PUT',
107 | },
108 | },
109 | displayOptions: {
110 | show: {
111 | resource: ['chat'],
112 | operation: ['updatePresence'],
113 | },
114 | },
115 | },
116 | ];
117 |
118 | export const markMSGReadProperties: INodeProperties[] = [
119 | {
120 | displayName: 'Mark Message as Read',
121 | name: 'readMessagesProperty',
122 | required: true,
123 | type: 'json',
124 | default: [],
125 | placeholder: `[Array:[messageId:'id',wuid:'123@s.whatsapp.net',fromMe:false]]`,
126 | routing: {
127 | send: { type: 'body', property: 'readMessage', preSend: [readMessage] },
128 | request: {
129 | url: '=' + requestURL('chat', 'markMessageAsRead'),
130 | method: 'PUT',
131 | },
132 | },
133 | displayOptions: {
134 | show: {
135 | resource: ['chat'],
136 | operation: ['markMessageAsRead'],
137 | },
138 | },
139 | },
140 | ];
141 |
142 | export const deleteMessageProperties: INodeProperties[] = [
143 | {
144 | displayName: 'Message ID',
145 | name: 'messgeIdProperty',
146 | required: true,
147 | type: 'string',
148 | default: '',
149 | placeholder: 'E10D3435A07F6111089C7875814A667F',
150 | routing: { send: { type: 'body', property: 'messageId' } },
151 | displayOptions: {
152 | show: {
153 | resource: ['chat'],
154 | operation: ['deleteMessage'],
155 | },
156 | },
157 | },
158 |
159 | {
160 | displayName: 'De',
161 | name: 'fromMeProperty',
162 | required: true,
163 | type: 'boolean',
164 | default: true,
165 | placeholder: '',
166 | routing: { send: { type: 'body', property: 'fromMe' } },
167 | displayOptions: {
168 | show: {
169 | resource: ['chat'],
170 | operation: ['deleteMessage'],
171 | },
172 | },
173 | },
174 |
175 | {
176 | displayName: 'Wuid',
177 | name: 'wuidProperty',
178 | required: true,
179 | type: 'string',
180 | default: '',
181 | placeholder: '5531900000000@s.whatsapp.net',
182 | routing: { send: { type: 'body', property: 'wuid' } },
183 | displayOptions: {
184 | show: {
185 | resource: ['chat'],
186 | operation: ['deleteMessage'],
187 | },
188 | },
189 | },
190 |
191 | {
192 | displayName: 'Participant',
193 | name: 'participantProperty',
194 | type: 'string',
195 | default: '',
196 | placeholder: '5531900000000@s.whatsapp.net',
197 | description: 'Pass this property if it is a group message',
198 | routing: { send: { type: 'body', property: 'participant' } },
199 | displayOptions: {
200 | show: {
201 | resource: ['chat'],
202 | operation: ['deleteMessage'],
203 | },
204 | },
205 | },
206 |
207 | {
208 | displayName: 'Set Routing',
209 | name: 'setRouting',
210 | type: 'hidden',
211 | default: '',
212 | routing: {
213 | request: {
214 | url: '=' + requestURL('chat', 'deleteMessage'),
215 | method: 'DELETE',
216 | },
217 | },
218 | displayOptions: {
219 | show: {
220 | resource: ['chat'],
221 | operation: ['deleteMessage'],
222 | },
223 | },
224 | },
225 | ];
226 |
227 | export const blockCobtactProperties: INodeProperties[] = [
228 | {
229 | displayName: 'Phone Number',
230 | name: 'numberProperty',
231 | required: true,
232 | type: 'string',
233 | default: '',
234 | routing: { send: { type: 'query', property: 'number' } },
235 | displayOptions: {
236 | show: {
237 | resource: ['chat'],
238 | operation: ['blockContact'],
239 | },
240 | },
241 | },
242 |
243 | {
244 | displayName: 'Action',
245 | name: 'actionProperty',
246 | required: true,
247 | type: 'options',
248 | options: [
249 | { name: 'Block', value: 'block' },
250 | { name: 'Unblock', value: 'unblock' },
251 | ],
252 | default: 'block',
253 | routing: { send: { type: 'body', property: 'action' } },
254 | displayOptions: {
255 | show: {
256 | resource: ['chat'],
257 | operation: ['blockContact'],
258 | },
259 | },
260 | },
261 |
262 | {
263 | displayName: 'Set Routing',
264 | name: 'setRouting',
265 | type: 'hidden',
266 | default: '',
267 | routing: {
268 | request: {
269 | url: '=' + requestURL('chat', 'blockContact'),
270 | method: 'PUT',
271 | },
272 | },
273 | displayOptions: {
274 | show: {
275 | resource: ['chat'],
276 | operation: ['blockContact'],
277 | },
278 | },
279 | },
280 | ];
281 |
282 | export const statusContactPorperties: INodeProperties[] = [
283 | {
284 | displayName: 'Recipient Phone Numbers',
285 | name: 'numberProperty',
286 | required: true,
287 | type: 'string',
288 | default: '',
289 | routing: {
290 | send: { type: 'query', property: 'number' },
291 | request: {
292 | url: '=' + requestURL('chat', 'fetchStatusContact'),
293 | method: 'GET',
294 | },
295 | },
296 | displayOptions: {
297 | show: {
298 | resource: ['chat'],
299 | operation: ['contactStatus'],
300 | },
301 | },
302 | },
303 | ];
304 |
305 | export const updateStatusProperties: INodeProperties[] = [
306 | {
307 | displayName: 'Status',
308 | name: 'StatusProperty',
309 | required: true,
310 | type: 'string',
311 | description: 'Update the status of the logged in number',
312 | default: '',
313 | routing: {
314 | send: { type: 'body', property: 'status' },
315 | request: {
316 | url: '=' + requestURL('chat', 'updateStaus'),
317 | method: 'PUT',
318 | },
319 | },
320 | displayOptions: {
321 | show: {
322 | resource: ['chat'],
323 | operation: ['updateStatus'],
324 | },
325 | },
326 | },
327 | ];
328 |
329 | export const budinessProfileProperties: INodeProperties[] = [
330 | {
331 | displayName: 'Phone Number',
332 | name: 'numberProperty',
333 | required: true,
334 | type: 'string',
335 | default: '',
336 | description: "Retrieve a contact's business information",
337 | placeholder: '5531900000000',
338 | routing: {
339 | send: { type: 'query', property: 'number' },
340 | request: {
341 | url: '=' + requestURL('chat', 'fetchBusinessProfile'),
342 | method: 'GET',
343 | },
344 | },
345 | displayOptions: {
346 | show: {
347 | resource: ['chat'],
348 | operation: ['businesProfile'],
349 | },
350 | },
351 | },
352 | ];
353 |
354 | export const profilePictureProperties: INodeProperties[] = [
355 | {
356 | displayName: 'Pnhone Number',
357 | name: 'numberProperty',
358 | description: 'Retrieve profile picture URL of some contact',
359 | required: true,
360 | type: 'string',
361 | default: '',
362 | placeholder: '5531900000000',
363 | routing: {
364 | send: { type: 'query', property: 'number' },
365 | request: {
366 | url: '=' + requestURL('chat', 'fetchProfilePictureUrl'),
367 | method: 'GET',
368 | },
369 | },
370 | displayOptions: {
371 | show: {
372 | resource: ['chat'],
373 | operation: ['profilePictureUrl'],
374 | },
375 | },
376 | },
377 | ];
378 |
--------------------------------------------------------------------------------
/nodes/CodeChat/descriptions/Group.desc.ts:
--------------------------------------------------------------------------------
1 | import { INodeProperties } from 'n8n-workflow';
2 | import { createGroup, requestURL, updateGroupIngo } from '../Generic.func';
3 |
4 | export const createGroupPrperties: INodeProperties[] = [
5 | {
6 | displayName: 'Subject',
7 | name: 'subjectProperties',
8 | required: true,
9 | type: 'string',
10 | default: '',
11 | displayOptions: {
12 | show: {
13 | resource: ['group'],
14 | operation: ['createGroup'],
15 | },
16 | },
17 | routing: { send: { type: 'body', property: 'groupSubject' } },
18 | },
19 |
20 | {
21 | displayName: 'Participants List',
22 | name: 'participantsList',
23 | type: 'json',
24 | default: [],
25 | placeholder: `[Array:['5531900000000, '5521911111111']] or 5531922222222`,
26 | description: 'This field supports a list, or a single number, or comma-separated numbers',
27 | hint: 'When entering a phone number, make sure to include the country code',
28 | routing: { send: { type: 'body', property: 'participants' } },
29 | displayOptions: {
30 | show: {
31 | resource: ['group'],
32 | operation: ['createGroup'],
33 | },
34 | },
35 | },
36 |
37 | {
38 | displayName: 'Description Group',
39 | name: 'descriptionProperty',
40 | placeholder: 'Add Description',
41 | type: 'fixedCollection',
42 | default: {},
43 | options: [
44 | {
45 | displayName: 'Description',
46 | name: 'groupDescription',
47 | values: [
48 | {
49 | displayName: 'Description',
50 | name: 'description',
51 | type: 'string',
52 | default: '',
53 | },
54 | ],
55 | },
56 | ],
57 | displayOptions: {
58 | show: {
59 | resource: ['group'],
60 | operation: ['createGroup'],
61 | },
62 | },
63 | },
64 |
65 | {
66 | displayName: 'Group Profile Picture',
67 | name: 'profilePictureProperty',
68 | placeholder: 'Add Picture',
69 | type: 'fixedCollection',
70 | default: {},
71 | options: [
72 | {
73 | displayName: 'Profile Picture',
74 | name: 'profilePictureGroup',
75 | values: [
76 | {
77 | displayName: 'Profile Picture',
78 | name: 'profilePicture',
79 | type: 'string',
80 | default: '',
81 | placeholder: 'url or base64',
82 | },
83 | ],
84 | },
85 | ],
86 | displayOptions: {
87 | show: {
88 | resource: ['group'],
89 | operation: ['createGroup'],
90 | },
91 | },
92 | },
93 |
94 | {
95 | displayName: 'Sert Routing',
96 | name: 'setRouting',
97 | type: 'hidden',
98 | default: '',
99 | displayOptions: {
100 | show: {
101 | resource: ['group'],
102 | operation: ['createGroup'],
103 | },
104 | },
105 | routing: {
106 | send: { preSend: [createGroup] },
107 | request: {
108 | url: '=' + requestURL('group', 'create'),
109 | method: 'POST',
110 | },
111 | },
112 | },
113 | ];
114 |
115 | export const groupInviteCodeProperties: INodeProperties[] = [
116 | {
117 | displayName: 'Group ID',
118 | name: 'groupIdProperty',
119 | required: true,
120 | placeholder: '123456789-123345@g.us',
121 | type: 'string',
122 | default: '',
123 | description: 'Retrieve the group invite you created',
124 | displayOptions: {
125 | show: {
126 | resource: ['group'],
127 | operation: ['inviteCode'],
128 | },
129 | },
130 | routing: {
131 | send: { type: 'query', property: 'groupJid' },
132 | request: {
133 | url: '=' + requestURL('group', 'invitionCode'),
134 | method: 'GET',
135 | },
136 | },
137 | },
138 | ];
139 |
140 | export const acceptInviteCodeProperties: INodeProperties[] = [
141 | {
142 | displayName: 'Group ID',
143 | name: 'groupIdProperty',
144 | required: true,
145 | placeholder: '123456789-123345@g.us',
146 | type: 'string',
147 | default: '',
148 | description: 'Group ID you want to accept the invitation',
149 | displayOptions: {
150 | show: {
151 | resource: ['group'],
152 | operation: ['acceptInvite'],
153 | },
154 | },
155 | routing: {
156 | send: { type: 'query', property: 'groupJid' },
157 | request: {
158 | url: '=' + requestURL('group', 'acceptInviteCode'),
159 | method: 'GET',
160 | },
161 | },
162 | },
163 | ];
164 |
165 | export const revokeInviteCodeProperties: INodeProperties[] = [
166 | {
167 | displayName: 'Group ID',
168 | name: 'groupIdProperty',
169 | required: true,
170 | placeholder: '123456789-123345@g.us',
171 | type: 'string',
172 | default: '',
173 | description: 'Group ID you want to revoke the invitation',
174 | displayOptions: {
175 | show: {
176 | resource: ['group'],
177 | operation: ['revokeInvite'],
178 | },
179 | },
180 | routing: {
181 | send: { type: 'query', property: 'groupJid' },
182 | request: {
183 | url: '=' + requestURL('group', 'revokeInviteCode'),
184 | method: 'GET',
185 | },
186 | },
187 | },
188 | ];
189 |
190 | export const updateGroupProperties: INodeProperties[] = [
191 | {
192 | displayName: 'Group ID',
193 | name: 'groupIdProperty',
194 | required: true,
195 | placeholder: '123456789-123345@g.us',
196 | type: 'string',
197 | default: '',
198 | description: 'Group ID you want to update',
199 | displayOptions: {
200 | show: {
201 | resource: ['group'],
202 | operation: ['updateInfo'],
203 | },
204 | },
205 | routing: { send: { type: 'query', property: 'groupJid' } },
206 | },
207 |
208 | {
209 | displayName: 'Subject',
210 | name: 'subjectProperty',
211 | type: 'string',
212 | default: '',
213 | description: 'Update group subject',
214 | displayOptions: {
215 | show: {
216 | resource: ['group'],
217 | operation: ['updateInfo'],
218 | },
219 | },
220 | routing: { send: { type: 'body', property: 'groupSubject' } },
221 | },
222 |
223 | {
224 | displayName: 'Description',
225 | name: 'descriptionProperty',
226 | type: 'string',
227 | default: '',
228 | description: 'Update group description',
229 | displayOptions: {
230 | show: {
231 | resource: ['group'],
232 | operation: ['updateInfo'],
233 | },
234 | },
235 | routing: { send: { type: 'body', property: 'groupDescription' } },
236 | },
237 |
238 | {
239 | displayName: 'Sert Routing',
240 | name: 'setRouting',
241 | type: 'hidden',
242 | default: '',
243 | displayOptions: {
244 | show: {
245 | resource: ['group'],
246 | operation: ['updateInfo'],
247 | },
248 | },
249 | routing: {
250 | send: { preSend: [updateGroupIngo] },
251 | request: {
252 | url: '=' + requestURL('group', 'updateInfo'),
253 | method: 'PUT',
254 | },
255 | },
256 | },
257 | ];
258 |
259 | export const updatePropfilePictureProperties: INodeProperties[] = [
260 | {
261 | displayName: 'Group ID',
262 | name: 'groupIdProperty',
263 | required: true,
264 | placeholder: '123456789-123345@g.us',
265 | type: 'string',
266 | default: '',
267 | description: 'Group ID you want to update',
268 | displayOptions: {
269 | show: {
270 | resource: ['group'],
271 | operation: ['updatePicture'],
272 | },
273 | },
274 | routing: { send: { type: 'query', property: 'groupJid' } },
275 | },
276 |
277 | {
278 | displayName: 'Profile Picture',
279 | name: 'profilePictureProperty',
280 | required: true,
281 | placeholder: 'url or base64',
282 | type: 'string',
283 | default: '',
284 | displayOptions: {
285 | show: {
286 | resource: ['group'],
287 | operation: ['updatePicture'],
288 | },
289 | },
290 | routing: { send: { type: 'body', property: 'urlOrBse64' } },
291 | },
292 |
293 | {
294 | displayName: 'Sert Routing',
295 | name: 'setRouting',
296 | type: 'hidden',
297 | default: '',
298 | displayOptions: {
299 | show: {
300 | resource: ['group'],
301 | operation: ['updatePicture'],
302 | },
303 | },
304 | routing: {
305 | request: {
306 | url: '=' + requestURL('group', 'updateProfilePicture'),
307 | method: 'PUT',
308 | },
309 | },
310 | },
311 | ];
312 |
313 | export const groupMetadataProperties: INodeProperties[] = [
314 | {
315 | displayName: 'Group ID',
316 | name: 'groupIdProperty',
317 | required: true,
318 | placeholder: '123456789-123345@g.us',
319 | type: 'string',
320 | default: '',
321 | description: 'Retrieve group information',
322 | displayOptions: {
323 | show: {
324 | resource: ['group'],
325 | operation: ['groupMetadata'],
326 | },
327 | },
328 | routing: {
329 | send: { type: 'query', property: 'groupJid' },
330 | request: {
331 | url: '=' + requestURL('group', 'fetchInfo'),
332 | method: 'GET',
333 | },
334 | },
335 | },
336 | ];
337 |
338 | export const updateParticipantsPorperties: INodeProperties[] = [
339 | {
340 | displayName: 'Group ID',
341 | name: 'groupIdProperty',
342 | required: true,
343 | placeholder: '123456789-123345@g.us',
344 | type: 'string',
345 | default: '',
346 | description: 'Group ID created by the number logged in',
347 | displayOptions: {
348 | show: {
349 | resource: ['group'],
350 | operation: ['updateParticipants'],
351 | },
352 | },
353 | routing: {
354 | send: { type: 'query', property: 'groupJid' },
355 | },
356 | },
357 |
358 | {
359 | displayName: 'Action',
360 | name: 'actionProperty',
361 | required: true,
362 | type: 'options',
363 | options: [
364 | { name: 'Add', value: 'add' },
365 | { name: 'Remove', value: 'remove' },
366 | { name: 'Promote Admin', value: 'promote' },
367 | { name: 'Demote', value: 'demote' },
368 | ],
369 | default: 'add',
370 | displayOptions: {
371 | show: {
372 | resource: ['group'],
373 | operation: ['updateParticipants'],
374 | },
375 | },
376 | routing: { send: { type: 'body', property: 'action' } },
377 | },
378 |
379 | {
380 | displayName: 'Sert Routing',
381 | name: 'setRouting',
382 | type: 'hidden',
383 | default: '',
384 | displayOptions: {
385 | show: {
386 | resource: ['group'],
387 | operation: ['updateParticipants'],
388 | },
389 | },
390 | routing: {
391 | request: {
392 | url: '=' + requestURL('group', 'updateParticipants'),
393 | method: 'PUT',
394 | },
395 | },
396 | },
397 | ];
398 |
399 | export const updateRuleGroupProperties: INodeProperties[] = [
400 | {
401 | displayName: 'Group ID',
402 | name: 'groupIdProperty',
403 | required: true,
404 | placeholder: '123456789-123345@g.us',
405 | type: 'string',
406 | default: '',
407 | description: 'Apply rules to the group',
408 | displayOptions: {
409 | show: {
410 | resource: ['group'],
411 | operation: ['updateParticipants'],
412 | },
413 | },
414 | routing: {
415 | send: { type: 'query', property: 'groupJid' },
416 | },
417 | },
418 |
419 | {
420 | displayName: 'Rules',
421 | name: 'ruleProperty',
422 | required: true,
423 | type: 'options',
424 | options: [
425 | { name: 'Comments: Admins', value: 'announcement' },
426 | { name: 'Comments: All', value: 'not_announcement' },
427 | { name: 'Edit Group: Admins', value: 'locked' },
428 | { name: 'Edit Group: All', value: 'unLocked' },
429 | ],
430 | default: 'announcement',
431 | displayOptions: {
432 | show: {
433 | resource: ['group'],
434 | operation: ['updateRule'],
435 | },
436 | },
437 | routing: {
438 | send: { type: 'body', property: 'updateRule' },
439 | },
440 | },
441 |
442 | {
443 | displayName: 'Sert Routing',
444 | name: 'setRouting',
445 | type: 'hidden',
446 | default: '',
447 | displayOptions: {
448 | show: {
449 | resource: ['group'],
450 | operation: ['updateRule'],
451 | },
452 | },
453 | routing: {
454 | request: {
455 | url: '=' + requestURL('group', 'updateRule'),
456 | method: 'PUT',
457 | },
458 | },
459 | },
460 | ];
461 |
462 | export const changeExpirationProperties: INodeProperties[] = [
463 | {
464 | displayName: 'Group ID',
465 | name: 'groupIdProperty',
466 | required: true,
467 | placeholder: '123456789-123345@g.us',
468 | type: 'string',
469 | default: '',
470 | description: 'Duration of group messages',
471 | displayOptions: {
472 | show: {
473 | resource: ['group'],
474 | operation: ['changeExpiration'],
475 | },
476 | },
477 | routing: {
478 | send: { type: 'query', property: 'groupJid' },
479 | },
480 | },
481 |
482 | {
483 | displayName: 'Rules',
484 | name: 'ruleProperty',
485 | required: true,
486 | type: 'options',
487 | options: [
488 | { name: 'No Expiration', value: 0 },
489 | { name: 'One Day', value: 1 },
490 | { name: 'One Week', value: 7 },
491 | { name: 'Quarterly', value: 90 },
492 | ],
493 | default: 0,
494 | displayOptions: {
495 | show: {
496 | resource: ['group'],
497 | operation: ['changeExpiration'],
498 | },
499 | },
500 | routing: {
501 | send: { type: 'body', property: 'updateRule' },
502 | },
503 | },
504 |
505 | {
506 | displayName: 'Sert Routing',
507 | name: 'setRouting',
508 | type: 'hidden',
509 | default: '',
510 | displayOptions: {
511 | show: {
512 | resource: ['group'],
513 | operation: ['changeExpiration'],
514 | },
515 | },
516 | routing: {
517 | request: {
518 | url: '=' + requestURL('group', 'changeExpiration'),
519 | method: 'PUT',
520 | },
521 | },
522 | },
523 | ];
524 |
525 | export const fetchParticipantsProperties: INodeProperties[] = [
526 | {
527 | displayName: 'Group ID',
528 | name: 'groupIdProperty',
529 | required: true,
530 | placeholder: '123456789-123345@g.us',
531 | type: 'string',
532 | default: '',
533 | description: 'Search for members of a group you belong to',
534 | displayOptions: {
535 | show: {
536 | resource: ['group'],
537 | operation: ['fetchParticpants'],
538 | },
539 | },
540 | routing: {
541 | send: { type: 'query', property: 'groupJid' },
542 | },
543 | },
544 |
545 | {
546 | displayName: 'Member Type',
547 | name: 'memberTypeProperty',
548 | required: true,
549 | type: 'options',
550 | options: [
551 | { name: 'All', value: 'all' },
552 | { name: 'Common', value: 'common' },
553 | { name: 'Admin', value: 'admin' },
554 | { name: 'Super Admin', value: 'superadmin' },
555 | ],
556 | default: 'all',
557 | displayOptions: {
558 | show: {
559 | resource: ['group'],
560 | operation: ['fetchParticpants'],
561 | },
562 | },
563 | routing: {
564 | send: { type: 'query', property: 'memberType' },
565 | },
566 | },
567 |
568 | {
569 | displayName: 'Sert Routing',
570 | name: 'setRouting',
571 | type: 'hidden',
572 | default: '',
573 | displayOptions: {
574 | show: {
575 | resource: ['group'],
576 | operation: ['fetchParticpants'],
577 | },
578 | },
579 | routing: {
580 | request: {
581 | url: '=' + requestURL('group', 'fetchParticipants'),
582 | method: 'GET',
583 | },
584 | },
585 | },
586 | ];
587 |
588 | export const leaveGroupProperties: INodeProperties[] = [
589 | {
590 | displayName: 'Group ID',
591 | name: 'groupIdProperty',
592 | required: true,
593 | placeholder: '123456789-123345@g.us',
594 | type: 'string',
595 | default: '',
596 | description: 'Group ID you want to leave',
597 | displayOptions: {
598 | show: {
599 | resource: ['group'],
600 | operation: ['leaveGroup'],
601 | },
602 | },
603 | routing: {
604 | send: { type: 'query', property: 'groupJid' },
605 | request: {
606 | url: '=' + requestURL('group', 'leave'),
607 | method: 'DELETE',
608 | },
609 | },
610 | },
611 | ];
612 |
--------------------------------------------------------------------------------
/nodes/CodeChat/descriptions/SendMessage.desc.ts:
--------------------------------------------------------------------------------
1 | import { INodeProperties } from 'n8n-workflow';
2 | import {
3 | prepareShippingOptions,
4 | requestURL,
5 | sendButtonsMessage,
6 | sendContactMessage,
7 | sendListMessage,
8 | sendTemplateMessage,
9 | } from '../Generic.func';
10 |
11 | export const optionsProperties: INodeProperties[] = [
12 | {
13 | displayName: 'Message Options',
14 | name: 'messageOptionsProperty',
15 | type: 'fixedCollection',
16 | default: {},
17 | typeOptions: { multipleValues: false },
18 | options: [
19 | {
20 | displayName: 'Options',
21 | name: 'optionsProperty',
22 | values: [
23 | {
24 | displayName: 'Quote Message',
25 | name: 'quotedProperty',
26 | default: '',
27 | hint: 'Enter the ID of the message you want to quote',
28 | placeholder: 'messageId',
29 | type: 'string',
30 | routing: {
31 | send: { type: 'body', property: 'options.quoted' },
32 | },
33 | },
34 |
35 | {
36 | displayName: 'Mention Contacts',
37 | name: 'mentionsProperty',
38 | placeholder: '',
39 | type: 'options',
40 | options: [
41 | { name: 'Everyone', value: 'everyone' },
42 | { name: 'One by One', value: 'onebyone' },
43 | ],
44 | default: 'onebyone',
45 | description: 'With "everyone" mention everyone in a group',
46 | routing: {
47 | send: { type: 'body', property: 'options.mentions.everyone' },
48 | },
49 | },
50 |
51 | {
52 | displayName: 'Mention Contact',
53 | name: 'mentionedProperty',
54 | default: '',
55 | hint: 'Insert a list with the contact(s) of the user(s) to be mentioned.',
56 | description: 'Mentions in both group chats and simple chats',
57 | placeholder: `[Array:['5531900000000, '5521911111111']]`,
58 | type: 'json',
59 | routing: {
60 | send: { type: 'body', property: 'options.mentions.mentioned' },
61 | },
62 | displayOptions: {
63 | show: {
64 | mentionsProperty: ['onebyone'],
65 | },
66 | },
67 | },
68 |
69 | {
70 | displayName: 'Delay Message',
71 | name: 'delayMessageProperty',
72 | default: '',
73 | description: 'Enter the delay with which each message will be delivered',
74 | placeholder: '1200 milliseconds',
75 | type: 'number',
76 | routing: {
77 | send: { type: 'body', property: 'options.delay' },
78 | },
79 | },
80 | ],
81 | },
82 | ],
83 | displayOptions: {
84 | show: {
85 | resource: ['sendMessage'],
86 | },
87 | },
88 | routing: {
89 | send: { preSend: [prepareShippingOptions] },
90 | },
91 | },
92 | ];
93 |
94 | export const textProperties: INodeProperties[] = [
95 | {
96 | displayName: 'Text Message',
97 | name: 'textProperty',
98 | required: true,
99 | default: '',
100 | description: 'The body of the message (max 4096 characters)',
101 | type: 'string',
102 | displayOptions: {
103 | show: {
104 | resource: ['sendMessage'],
105 | operation: ['sendText'],
106 | },
107 | },
108 | routing: {
109 | send: { type: 'body', property: 'textMessage.text' },
110 | request: {
111 | url: '=' + requestURL('message', 'sendText'),
112 | method: 'POST',
113 | },
114 | },
115 | },
116 | ];
117 |
118 | export const buttonsProperties: INodeProperties[] = [
119 | {
120 | displayName: 'Button Title',
121 | name: 'buttonTitleProperty',
122 | required: true,
123 | default: '',
124 | hint: '',
125 | type: 'string',
126 | displayOptions: {
127 | show: {
128 | resource: ['sendMessage'],
129 | operation: ['sendButtons'],
130 | },
131 | },
132 | routing: {
133 | send: { type: 'body', property: 'buttonsMessage.title' },
134 | },
135 | },
136 |
137 | {
138 | displayName: 'Button Description',
139 | name: 'buttonDescProperty',
140 | required: true,
141 | default: '',
142 | type: 'string',
143 | displayOptions: {
144 | show: {
145 | resource: ['sendMessage'],
146 | operation: ['sendButtons'],
147 | },
148 | },
149 | routing: {
150 | send: { type: 'body', property: 'buttonsMessage.description' },
151 | },
152 | },
153 |
154 | {
155 | displayName: 'Button Footer Text',
156 | name: 'buttonFooterProperty',
157 | default: '',
158 | type: 'string',
159 | displayOptions: {
160 | show: {
161 | resource: ['sendMessage'],
162 | operation: ['sendButtons'],
163 | },
164 | },
165 | routing: {
166 | send: { type: 'body', property: 'buttonsMessage.footerText' },
167 | },
168 | },
169 |
170 | {
171 | displayName: 'Button Field Type',
172 | name: 'buttonFieldTypeProperty',
173 | required: true,
174 | noDataExpression: true,
175 | placeholder: '',
176 | type: 'options',
177 | options: [
178 | { name: 'Collection', value: 'collection' },
179 | { name: 'JSON', value: 'json' },
180 | ],
181 | default: 'collection',
182 | displayOptions: {
183 | show: {
184 | resource: ['sendMessage'],
185 | operation: ['sendButtons'],
186 | },
187 | },
188 | },
189 |
190 | {
191 | displayName: 'Collection Field',
192 | name: 'collectionFieldProperty',
193 | required: true,
194 | placeholder: 'Add Reply Buttons',
195 | type: 'fixedCollection',
196 | default: {},
197 | typeOptions: { multipleValues: true, maxValue: 3 },
198 | options: [
199 | {
200 | displayName: 'Reply Buttons',
201 | name: 'replyButtons',
202 | values: [
203 | {
204 | displayName: 'Display Text',
205 | name: 'displayText',
206 | type: 'string',
207 | default: '',
208 | description: 'Unique text per button',
209 | required: true,
210 | },
211 | {
212 | displayName: 'Button ID',
213 | name: 'buttonId',
214 | type: 'string',
215 | default: '',
216 | description: 'Unique ID per button',
217 | required: true,
218 | },
219 | ],
220 | },
221 | ],
222 | displayOptions: {
223 | show: {
224 | resource: ['sendMessage'],
225 | operation: ['sendButtons'],
226 | buttonFieldTypeProperty: ['collection'],
227 | },
228 | },
229 | routing: {
230 | send: { type: 'body', property: 'buttons' },
231 | },
232 | },
233 |
234 | {
235 | displayName: 'JSON Field',
236 | name: 'jsonProperty',
237 | required: true,
238 | placeholder: `[Array:[{displayText: 'Button Text', buttonId: 'btnId01'}]]`,
239 | type: 'json',
240 | default: [],
241 | description: 'Map a JSON directly to this field',
242 | displayOptions: {
243 | show: {
244 | resource: ['sendMessage'],
245 | operation: ['sendButtons'],
246 | buttonFieldTypeProperty: ['json'],
247 | },
248 | },
249 | routing: {
250 | send: { type: 'body', property: 'buttonsMessage.buttons' },
251 | },
252 | },
253 |
254 | {
255 | displayName: 'Media Message',
256 | name: 'mediaMessageProperty',
257 | placeholder: 'Add Media Message',
258 | type: 'fixedCollection',
259 | default: {},
260 | typeOptions: { multipleValues: false },
261 | description: 'Embed media message to button',
262 | options: [
263 | {
264 | displayName: 'Media Message',
265 | name: 'embedMediaMessage',
266 | values: [
267 | {
268 | displayName: 'Media Type',
269 | name: 'mediaType',
270 | required: true,
271 | type: 'options',
272 | options: [
273 | { name: 'Image', value: 'image' },
274 | { name: 'Document', value: 'document' },
275 | { name: 'Video', value: 'video' },
276 | { name: 'Sticker', value: 'sticker' },
277 | ],
278 | default: 'image',
279 | routing: {
280 | send: { type: 'body', property: 'mediaData.type' },
281 | },
282 | },
283 | {
284 | displayName: 'Media Source',
285 | name: 'mediaSource',
286 | required: true,
287 | type: 'string',
288 | default: '',
289 | placeholder: 'url or base64',
290 | routing: {
291 | send: { type: 'body', property: 'mediaData.source' },
292 | },
293 | },
294 | ],
295 | },
296 | ],
297 | displayOptions: {
298 | show: {
299 | resource: ['sendMessage'],
300 | operation: ['sendButtons'],
301 | },
302 | },
303 | },
304 |
305 | {
306 | displayName: 'Set Routing',
307 | name: 'setRouting',
308 | type: 'hidden',
309 | default: '',
310 | displayOptions: {
311 | show: {
312 | resource: ['sendMessage'],
313 | operation: ['sendButtons'],
314 | },
315 | },
316 | routing: {
317 | request: {
318 | url: '=' + requestURL('message', 'sendButtons'),
319 | method: 'POST',
320 | },
321 | send: { preSend: [sendButtonsMessage] },
322 | },
323 | },
324 | ];
325 |
326 | export const templateProperties: INodeProperties[] = [
327 | {
328 | displayName: 'Template Title',
329 | name: 'templateTitleProperty',
330 | required: true,
331 | default: '',
332 | hint: '',
333 | type: 'string',
334 | displayOptions: {
335 | show: {
336 | resource: ['sendMessage'],
337 | operation: ['sendTemplate'],
338 | },
339 | },
340 | routing: {
341 | send: { type: 'body', property: 'templateMessage.title' },
342 | },
343 | },
344 |
345 | {
346 | displayName: 'Template Description',
347 | name: 'templateDescProperty',
348 | required: true,
349 | default: '',
350 | type: 'string',
351 | displayOptions: {
352 | show: {
353 | resource: ['sendMessage'],
354 | operation: ['sendTemplate'],
355 | },
356 | },
357 | routing: {
358 | send: { type: 'body', property: 'templateMessage.description' },
359 | },
360 | },
361 |
362 | {
363 | displayName: 'Template Footer Text',
364 | name: 'templateFooterProperty',
365 | default: '',
366 | type: 'string',
367 | displayOptions: {
368 | show: {
369 | resource: ['sendMessage'],
370 | operation: ['sendTemplate'],
371 | },
372 | },
373 | routing: {
374 | send: { type: 'body', property: 'templateMessage.footerText' },
375 | },
376 | },
377 |
378 | {
379 | displayName: 'Template Field Type',
380 | name: 'templateFieldTypeProperty',
381 | required: true,
382 | noDataExpression: true,
383 | placeholder: '',
384 | type: 'options',
385 | options: [
386 | { name: 'Collection', value: 'collection' },
387 | { name: 'JSON', value: 'json' },
388 | ],
389 | default: 'collection',
390 | displayOptions: {
391 | show: {
392 | resource: ['sendMessage'],
393 | operation: ['sendTemplate'],
394 | },
395 | },
396 | },
397 |
398 | {
399 | displayName: 'Collection Field',
400 | name: 'colectionProperty',
401 | required: true,
402 | placeholder: 'Add Template Buttons',
403 | type: 'fixedCollection',
404 | default: {},
405 | typeOptions: { multipleValues: true },
406 | options: [
407 | {
408 | displayName: 'Template Buttons',
409 | name: 'templateButtons',
410 | values: [
411 | {
412 | displayName: 'Button Type',
413 | name: 'buttonType',
414 | required: true,
415 | type: 'options',
416 | options: [
417 | { name: 'Url Button', value: 'urlButton' },
418 | { name: 'Call Button', value: 'callButton' },
419 | { name: 'Reply Button', value: 'replyButton' },
420 | ],
421 | default: 'replyButton',
422 | },
423 | {
424 | displayName: 'Display Text',
425 | name: 'displayText',
426 | required: true,
427 | type: 'string',
428 | default: '',
429 | },
430 | {
431 | displayName: 'Payload',
432 | name: 'payload',
433 | required: true,
434 | type: 'string',
435 | default: '',
436 | },
437 | ],
438 | },
439 | ],
440 | displayOptions: {
441 | show: {
442 | resource: ['sendMessage'],
443 | operation: ['sendTemplate'],
444 | templateFieldTypeProperty: ['collection'],
445 | },
446 | },
447 | routing: {
448 | send: { type: 'body', property: 'buttons' },
449 | },
450 | },
451 |
452 | {
453 | displayName: 'JSON Field',
454 | name: 'jsonProperty',
455 | required: true,
456 | placeholder: `[Array:[{buttonType: 'replyButton', displayText: 'Button Text', payload: 'btnId01'}]]`,
457 | type: 'json',
458 | default: [],
459 | description: 'Map a JSON directly to this field',
460 | displayOptions: {
461 | show: {
462 | resource: ['sendMessage'],
463 | operation: ['sendTemplate'],
464 | templateFieldTypeProperty: ['json'],
465 | },
466 | },
467 | routing: {
468 | send: { type: 'body', property: 'templateMessage.buttons' },
469 | },
470 | },
471 |
472 | {
473 | displayName: 'Media Message',
474 | name: 'mediaMessageProperty',
475 | placeholder: 'Add Media Message',
476 | type: 'fixedCollection',
477 | default: {},
478 | typeOptions: { multipleValues: false },
479 | description: 'Embed media message to button',
480 | options: [
481 | {
482 | displayName: 'Media Message',
483 | name: 'embedMediaMessage',
484 | values: [
485 | {
486 | displayName: 'Media Type',
487 | name: 'mediaType',
488 | required: true,
489 | type: 'options',
490 | options: [
491 | { name: 'Image', value: 'image' },
492 | { name: 'Document', value: 'document' },
493 | { name: 'Video', value: 'video' },
494 | { name: 'Sticker', value: 'sticker' },
495 | ],
496 | default: 'image',
497 | routing: {
498 | send: { type: 'body', property: 'mediaData.type' },
499 | },
500 | },
501 | {
502 | displayName: 'Media Source',
503 | name: 'mediaSource',
504 | required: true,
505 | type: 'string',
506 | default: '',
507 | placeholder: 'url or base64',
508 | routing: {
509 | send: { type: 'body', property: 'mediaData.source' },
510 | },
511 | },
512 | ],
513 | },
514 | ],
515 | displayOptions: {
516 | show: {
517 | resource: ['sendMessage'],
518 | operation: ['sendTemplate'],
519 | },
520 | },
521 | },
522 |
523 | {
524 | displayName: 'Set Routing',
525 | name: 'setRouting',
526 | type: 'hidden',
527 | default: '',
528 | displayOptions: {
529 | show: {
530 | resource: ['sendMessage'],
531 | operation: ['sendTemplate'],
532 | },
533 | },
534 | routing: {
535 | request: {
536 | url: '=' + requestURL('message', 'sendTemplate'),
537 | method: 'POST',
538 | },
539 | send: { preSend: [sendTemplateMessage] },
540 | },
541 | },
542 | ];
543 |
544 | export const mediaMessageProperties: INodeProperties[] = [
545 | {
546 | displayName: 'Media Type',
547 | name: 'mediaTypeProperty',
548 | required: true,
549 | placeholder: '',
550 | type: 'options',
551 | options: [
552 | { name: 'Image', value: 'image' },
553 | { name: 'Document', value: 'document' },
554 | { name: 'Video', value: 'video' },
555 | { name: 'Sticker', value: 'sticker' },
556 | ],
557 | default: 'image',
558 | routing: {
559 | send: { type: 'body', property: 'mediaMessage.mediaType' },
560 | },
561 | displayOptions: {
562 | show: {
563 | resource: ['sendMessage'],
564 | operation: ['sendMedia'],
565 | },
566 | },
567 | },
568 |
569 | {
570 | displayName: 'Media URL',
571 | name: 'mediaUrlProperty',
572 | required: true,
573 | type: 'string',
574 | default: '',
575 | placeholder: 'https://yourdomain.com/image.png',
576 | routing: {
577 | send: { type: 'body', property: 'mediaMessage.url' },
578 | },
579 | displayOptions: {
580 | show: {
581 | resource: ['sendMessage'],
582 | operation: ['sendMedia'],
583 | },
584 | },
585 | },
586 |
587 | {
588 | displayName: 'Media Caption',
589 | name: 'mediaCaptionProperty',
590 | type: 'fixedCollection',
591 | typeOptions: { multipleValues: false },
592 | default: {},
593 | options: [
594 | {
595 | displayName: 'Caption',
596 | name: 'captionProperty',
597 | values: [
598 | {
599 | displayName: 'Caption',
600 | name: 'caption',
601 | type: 'string',
602 | default: '',
603 | placeholder: 'caption - description - title',
604 | routing: {
605 | send: { type: 'body', property: 'mediaMessage.caption' },
606 | },
607 | },
608 | ],
609 | },
610 | ],
611 | displayOptions: {
612 | show: {
613 | resource: ['sendMessage'],
614 | operation: ['sendMedia'],
615 | },
616 | },
617 | },
618 |
619 | {
620 | displayName: 'Set Routing',
621 | name: 'setRouting',
622 | type: 'hidden',
623 | default: '',
624 | displayOptions: {
625 | show: {
626 | resource: ['sendMessage'],
627 | operation: ['sendMedia'],
628 | },
629 | },
630 | routing: {
631 | request: {
632 | url: '=' + requestURL('message', 'sendMedia'),
633 | method: 'POST',
634 | },
635 | },
636 | },
637 | ];
638 |
639 | export const mediaBase64MessgeProperties: INodeProperties[] = [
640 | {
641 | displayName: 'File Name',
642 | name: 'fileNameProperty',
643 | required: true,
644 | placeholder: 'table.xlsx',
645 | hint: 'Para que o tipo de arquivo seja identificado pelo WhatsApp, a extensão deve ser informada pelo WhatsApp.',
646 | type: 'string',
647 | default: '',
648 | routing: {
649 | send: { type: 'body', property: 'mediaMessage.fileName' },
650 | },
651 | displayOptions: {
652 | show: {
653 | resource: ['sendMessage'],
654 | operation: ['sendMediaBase64'],
655 | },
656 | },
657 | },
658 |
659 | {
660 | displayName: 'Media Type',
661 | name: 'mediaTypeProperty',
662 | required: true,
663 | placeholder: '',
664 | type: 'options',
665 | options: [
666 | { name: 'Image', value: 'image' },
667 | { name: 'Document', value: 'document' },
668 | { name: 'Video', value: 'video' },
669 | { name: 'Sticker', value: 'sticker' },
670 | ],
671 | default: 'image',
672 | routing: {
673 | send: { type: 'body', property: 'mediaMessage.mediaType' },
674 | },
675 | displayOptions: {
676 | show: {
677 | resource: ['sendMessage'],
678 | operation: ['sendMediaBase64'],
679 | },
680 | },
681 | },
682 |
683 | {
684 | displayName: 'Base64',
685 | name: 'base64Property',
686 | required: true,
687 | type: 'string',
688 | default: '',
689 | routing: {
690 | send: { type: 'body', property: 'mediaMessage.base64' },
691 | },
692 | displayOptions: {
693 | show: {
694 | resource: ['sendMessage'],
695 | operation: ['sendMediaBase64'],
696 | },
697 | },
698 | },
699 |
700 | {
701 | displayName: 'Media Caption',
702 | name: 'mediaCaptionProperty',
703 | type: 'fixedCollection',
704 | typeOptions: { multipleValues: false },
705 | default: {},
706 | options: [
707 | {
708 | displayName: 'Caption',
709 | name: 'captionProperty',
710 | values: [
711 | {
712 | displayName: 'Caption',
713 | name: 'caption',
714 | type: 'string',
715 | default: '',
716 | placeholder: 'caption - description - title',
717 | routing: {
718 | send: { type: 'body', property: 'mediaMessage.caption' },
719 | },
720 | },
721 | ],
722 | },
723 | ],
724 | displayOptions: {
725 | show: {
726 | resource: ['sendMessage'],
727 | operation: ['sendMediaBase64'],
728 | },
729 | },
730 | },
731 |
732 | {
733 | displayName: 'Set Routing',
734 | name: 'setRouting',
735 | type: 'hidden',
736 | default: '',
737 | displayOptions: {
738 | show: {
739 | resource: ['sendMessage'],
740 | operation: ['sendMediaBase64'],
741 | },
742 | },
743 | routing: {
744 | request: {
745 | url: '=' + requestURL('message', 'sendMediaBase64'),
746 | method: 'POST',
747 | },
748 | },
749 | },
750 | ];
751 |
752 | export const whatsAppAudioProperties: INodeProperties[] = [
753 | {
754 | displayName: 'WhatsApp Audio',
755 | name: 'whatsAppAudioProperty',
756 | required: true,
757 | placeholder: 'url or base64',
758 | default: '',
759 | type: 'string',
760 | routing: {
761 | send: { type: 'body', property: 'whatsappAudio.audio' },
762 | request: {
763 | url: '=' + requestURL('message', 'sendWhatsAppAudio'),
764 | method: 'POST',
765 | },
766 | },
767 | displayOptions: {
768 | show: {
769 | resource: ['sendMessage'],
770 | operation: ['sendWhatsAppAudio'],
771 | },
772 | },
773 | },
774 | ];
775 |
776 | export const locationProperties: INodeProperties[] = [
777 | {
778 | displayName: 'Latitude',
779 | name: 'latitudeProperty',
780 | required: true,
781 | default: '',
782 | type: 'number',
783 | placeholder: '-20.32568196333534',
784 | routing: {
785 | send: { type: 'body', property: 'locationMessage.latitude' },
786 | },
787 | displayOptions: {
788 | show: {
789 | resource: ['sendMessage'],
790 | operation: ['sendLocation'],
791 | },
792 | },
793 | },
794 |
795 | {
796 | displayName: 'Longitude',
797 | name: 'longitudeProperty',
798 | required: true,
799 | default: '',
800 | type: 'number',
801 | placeholder: '-20.32568196333534',
802 | routing: {
803 | send: { type: 'body', property: 'locationMessage.longitude' },
804 | },
805 | displayOptions: {
806 | show: {
807 | resource: ['sendMessage'],
808 | operation: ['sendLocation'],
809 | },
810 | },
811 | },
812 |
813 | {
814 | displayName: 'Name',
815 | name: 'nameProperty',
816 | default: '',
817 | description: 'City name - state of ... - district.',
818 | type: 'string',
819 | routing: {
820 | send: { type: 'body', property: 'locationMessage.name' },
821 | },
822 | displayOptions: {
823 | show: {
824 | resource: ['sendMessage'],
825 | operation: ['sendLocation'],
826 | },
827 | },
828 | },
829 |
830 | {
831 | displayName: 'Address',
832 | name: 'addressProperty',
833 | default: '',
834 | description: 'Location address - landmark - location name',
835 | type: 'string',
836 | routing: {
837 | send: { type: 'body', property: 'locationMessage.name' },
838 | },
839 | displayOptions: {
840 | show: {
841 | resource: ['sendMessage'],
842 | operation: ['sendLocation'],
843 | },
844 | },
845 | },
846 |
847 | {
848 | displayName: 'Add Type Buttons',
849 | name: 'addTypeButtonsProperty',
850 | description: 'Optional',
851 | type: 'fixedCollection',
852 | typeOptions: { multipleValues: false },
853 | options: [
854 | {
855 | displayName: 'Reply Buttons',
856 | name: 'replyButtonsProperty',
857 | values: [
858 | {
859 | displayName: 'Buttons for Location',
860 | name: 'buttonsLocation',
861 | required: true,
862 | placeholder: `[Array:[{displayText: 'Button Text', buttonId: 'btnId01'}]]`,
863 | type: 'json',
864 | default: [],
865 | description: 'Map a JSON directly to this field',
866 | hint: 'Maximum of three buttons ',
867 | routing: {
868 | send: { type: 'body', property: 'locationMessage.commonButtons.buttons' },
869 | },
870 | },
871 | ],
872 | },
873 | ],
874 | default: {},
875 | displayOptions: {
876 | show: {
877 | resource: ['sendMessage'],
878 | operation: ['sendLocation'],
879 | },
880 | },
881 | },
882 |
883 | {
884 | displayName: 'Set Routing',
885 | name: 'setRouting',
886 | type: 'hidden',
887 | default: '',
888 | displayOptions: {
889 | show: {
890 | resource: ['sendMessage'],
891 | operation: ['sendLocation'],
892 | },
893 | },
894 | routing: {
895 | request: {
896 | url: '=' + requestURL('message', 'sendLocation'),
897 | method: 'POST',
898 | },
899 | },
900 | },
901 | ];
902 |
903 | export const listProperties: INodeProperties[] = [
904 | {
905 | displayName: 'List Title',
906 | name: 'listTitleProperty',
907 | required: true,
908 | default: '',
909 | type: 'string',
910 | routing: {
911 | send: { type: 'body', property: 'listMessage.title' },
912 | },
913 | displayOptions: {
914 | show: {
915 | resource: ['sendMessage'],
916 | operation: ['sendList'],
917 | },
918 | },
919 | },
920 |
921 | {
922 | displayName: 'Description',
923 | name: 'listDescriptionProperty',
924 | required: true,
925 | default: '',
926 | type: 'string',
927 | routing: {
928 | send: { type: 'body', property: 'listMessage.description' },
929 | },
930 | displayOptions: {
931 | show: {
932 | resource: ['sendMessage'],
933 | operation: ['sendList'],
934 | },
935 | },
936 | },
937 |
938 | {
939 | displayName: 'Button Text',
940 | name: 'buttonTextProperty',
941 | required: true,
942 | default: '',
943 | description: 'List clickable button title',
944 | type: 'string',
945 | routing: {
946 | send: { type: 'body', property: 'listMessage.buttonText' },
947 | },
948 | displayOptions: {
949 | show: {
950 | resource: ['sendMessage'],
951 | operation: ['sendList'],
952 | },
953 | },
954 | },
955 |
956 | {
957 | displayName: 'Footer Text',
958 | name: 'footerTextProperty',
959 | default: '',
960 | description: 'Optional',
961 | type: 'string',
962 | routing: {
963 | send: { type: 'body', property: 'listMessage.footerText' },
964 | },
965 | displayOptions: {
966 | show: {
967 | resource: ['sendMessage'],
968 | operation: ['sendList'],
969 | },
970 | },
971 | },
972 |
973 | {
974 | displayName: 'List Field Type',
975 | name: 'listFieldTypeProperty',
976 | required: true,
977 | noDataExpression: true,
978 | placeholder: '',
979 | type: 'options',
980 | options: [
981 | { name: 'Collection', value: 'collection' },
982 | { name: 'JSON', value: 'json' },
983 | ],
984 | default: 'collection',
985 | displayOptions: {
986 | show: {
987 | resource: ['sendMessage'],
988 | operation: ['sendList'],
989 | },
990 | },
991 | },
992 |
993 | {
994 | displayName: 'List Section Fields',
995 | name: 'listSectionFieldsProperty',
996 | placeholder: 'Add Section',
997 | required: true,
998 | default: {},
999 | type: 'fixedCollection',
1000 | typeOptions: { multipleValues: true },
1001 | options: [
1002 | {
1003 | displayName: 'Sections',
1004 | name: 'sections',
1005 | values: [
1006 | {
1007 | displayName: 'Section Title',
1008 | name: 'title',
1009 | required: true,
1010 | type: 'string',
1011 | default: '',
1012 | },
1013 | {
1014 | displayName: 'Rows',
1015 | name: 'rowsProperty',
1016 | placeholder: 'Add Row',
1017 | required: true,
1018 | default: {},
1019 | type: 'fixedCollection',
1020 | typeOptions: { multipleValues: true },
1021 | options: [
1022 | {
1023 | displayName: 'Row',
1024 | name: 'rows',
1025 | values: [
1026 | {
1027 | displayName: 'Row Title',
1028 | name: 'title',
1029 | required: true,
1030 | type: 'string',
1031 | default: '',
1032 | },
1033 | {
1034 | displayName: 'Description',
1035 | name: 'description',
1036 | required: true,
1037 | type: 'string',
1038 | default: '',
1039 | },
1040 | {
1041 | displayName: 'Row ID',
1042 | name: 'rowId',
1043 | required: true,
1044 | type: 'string',
1045 | default: '',
1046 | description: 'Os rowIds devem ser únicos',
1047 | },
1048 | ],
1049 | },
1050 | ],
1051 | },
1052 | ],
1053 | },
1054 | ],
1055 | routing: {
1056 | send: { type: 'body', property: 'listMessage' },
1057 | },
1058 | displayOptions: {
1059 | show: {
1060 | resource: ['sendMessage'],
1061 | operation: ['sendList'],
1062 | listFieldTypeProperty: ['collection'],
1063 | },
1064 | },
1065 | },
1066 |
1067 | {
1068 | displayName: 'List Section JSON',
1069 | name: 'listSectionJSONProperty',
1070 | required: true,
1071 | description: 'Single elements Array',
1072 | placeholder: `[Array:[title:'Section Title',rows:[{title:'Row Title',description:'Description',rowId:'rowId01'}]]]`,
1073 | type: 'json',
1074 | default: [],
1075 | routing: {
1076 | send: { type: 'body', property: 'listMessage.sections' },
1077 | },
1078 | displayOptions: {
1079 | show: {
1080 | resource: ['sendMessage'],
1081 | operation: ['sendList'],
1082 | listFieldTypeProperty: ['json'],
1083 | },
1084 | },
1085 | },
1086 |
1087 | {
1088 | displayName: 'Set Routing',
1089 | name: 'setRouting',
1090 | type: 'hidden',
1091 | default: '',
1092 | displayOptions: {
1093 | show: {
1094 | resource: ['sendMessage'],
1095 | operation: ['sendList'],
1096 | },
1097 | },
1098 | routing: {
1099 | request: {
1100 | url: '=' + requestURL('message', 'sendList'),
1101 | method: 'POST',
1102 | },
1103 | send: { preSend: [sendListMessage] },
1104 | },
1105 | },
1106 | ];
1107 |
1108 | export const linkPreviewProperties: INodeProperties[] = [
1109 | {
1110 | displayName: 'Everyone',
1111 | name: 'modeLinkPreviewProperty',
1112 | type: 'options',
1113 | options: [
1114 | { name: 'Common', value: 'common' },
1115 | { name: 'Template', value: 'template' },
1116 | ],
1117 | default: 'common',
1118 | routing: {
1119 | send: { type: 'query', property: 'mode' },
1120 | },
1121 | displayOptions: {
1122 | show: {
1123 | resource: ['sendMessage'],
1124 | operation: ['sendLinkPreview'],
1125 | },
1126 | },
1127 | },
1128 |
1129 | {
1130 | displayName: 'Url',
1131 | name: 'urlLinkPreviewProperty',
1132 | required: true,
1133 | placeholder: 'https://github.com/jrCleber',
1134 | type: 'string',
1135 | default: '',
1136 | routing: {
1137 | send: { type: 'body', property: 'linkPreview.url' },
1138 | },
1139 | displayOptions: {
1140 | show: {
1141 | resource: ['sendMessage'],
1142 | operation: ['sendLinkPreview'],
1143 | },
1144 | },
1145 | },
1146 |
1147 | {
1148 | displayName: 'Text',
1149 | name: 'textLinkPreviewProperty',
1150 | description: 'Required for "template" mode',
1151 | type: 'string',
1152 | default: '',
1153 | routing: {
1154 | send: { type: 'body', property: 'linkPreview.text' },
1155 | },
1156 | displayOptions: {
1157 | show: {
1158 | resource: ['sendMessage'],
1159 | operation: ['sendLinkPreview'],
1160 | },
1161 | },
1162 | },
1163 |
1164 | {
1165 | displayName: 'Options',
1166 | name: 'optionsLinkPreviewProperty',
1167 | type: 'fixedCollection',
1168 | typeOptions: { multipleValues:false },
1169 | default: {},
1170 | options: [
1171 | {
1172 | displayName: 'Link Preview Options',
1173 | name: 'optionsProperty',
1174 | values: [
1175 | {
1176 | displayName: 'Title',
1177 | name: 'titleLinkPreviewProperty',
1178 | type: 'string',
1179 | default: '',
1180 | routing: {
1181 | send: { type:'body', property: 'linkPreview.title' },
1182 | },
1183 | },
1184 | {
1185 | displayName: 'Description',
1186 | name: 'descriptionLinkPreviewProperty',
1187 | type: 'string',
1188 | default: '',
1189 | routing: {
1190 | send: { type:'body', property: 'linkPreview.description' },
1191 | },
1192 | },
1193 | {
1194 | displayName: 'Image',
1195 | name: 'imageLinkPreviewProperty',
1196 | type: 'string',
1197 | default: '',
1198 | description: 'URL or base64',
1199 | routing: {
1200 | send: { type:'body', property: 'linkPreview.image' },
1201 | },
1202 | },
1203 | ],
1204 | },
1205 | ],
1206 | displayOptions: {
1207 | show: {
1208 | resource: ['sendMessage'],
1209 | operation: ['sendLinkPreview'],
1210 | },
1211 | },
1212 | },
1213 |
1214 | {
1215 | displayName: 'Set Routing',
1216 | name: 'setRouting',
1217 | type: 'hidden',
1218 | default: '',
1219 | displayOptions: {
1220 | show: {
1221 | resource: ['sendMessage'],
1222 | operation: ['sendLinkPreview'],
1223 | },
1224 | },
1225 | routing: {
1226 | request: {
1227 | url: '=' + requestURL('message', 'sendLinkPreview'),
1228 | method: 'POST',
1229 | },
1230 | },
1231 | },
1232 | ];
1233 |
1234 | export const contactProperties: INodeProperties[] = [
1235 | {
1236 | displayName: 'Contact Field Type',
1237 | name: 'contactTypeProperty',
1238 | required: true,
1239 | noDataExpression: true,
1240 | placeholder: '',
1241 | type: 'options',
1242 | options: [
1243 | { name: 'Collection', value: 'collection' },
1244 | { name: 'JSON', value: 'json' },
1245 | ],
1246 | default: 'collection',
1247 | displayOptions: {
1248 | show: {
1249 | resource: ['sendMessage'],
1250 | operation: ['sendContact'],
1251 | },
1252 | },
1253 | },
1254 |
1255 | {
1256 | displayName: 'Contact Fields',
1257 | name: 'contactFieldsProperty',
1258 | required: true,
1259 | default: {},
1260 | placeholder: '',
1261 | type: 'fixedCollection',
1262 | typeOptions: { multipleValues: true },
1263 | options: [
1264 | {
1265 | displayName: 'Contacts',
1266 | name: 'contacts',
1267 | values: [
1268 | {
1269 | displayName: 'Contact Name',
1270 | name: 'fullName',
1271 | placeholder: 'name',
1272 | required: true,
1273 | default: '',
1274 | type: 'string',
1275 | },
1276 | {
1277 | displayName: 'Whatsapp Unuque ID',
1278 | name: 'wuid',
1279 | placeholder: '5531900000000',
1280 | required: true,
1281 | default: '',
1282 | type: 'string',
1283 | },
1284 | {
1285 | displayName: 'Phone Number',
1286 | name: 'phoneNumber',
1287 | placeholder: '+55 31 9 0000-0000',
1288 | required: true,
1289 | default: '',
1290 | type: 'string',
1291 | },
1292 | ],
1293 | },
1294 | ],
1295 | routing: {
1296 | send: { type: 'body', property: 'contactMessage' },
1297 | },
1298 | displayOptions: {
1299 | show: {
1300 | resource: ['sendMessage'],
1301 | operation: ['sendContact'],
1302 | contactTypeProperty: ['collection'],
1303 | },
1304 | },
1305 | },
1306 |
1307 | {
1308 | displayName: 'Contact JSON',
1309 | name: 'contactJSONProperty',
1310 | required: true,
1311 | description: 'Single elements Array',
1312 | placeholder: `[Array:[fullName:'Contact name',wuid:'5531900000000',phoneNumber]]`,
1313 | type: 'json',
1314 | default: [],
1315 | routing: {
1316 | send: { type: 'body', property: 'contactMessage' },
1317 | },
1318 | displayOptions: {
1319 | show: {
1320 | resource: ['sendMessage'],
1321 | operation: ['sendContact'],
1322 | contactTypeProperty: ['json'],
1323 | },
1324 | },
1325 | },
1326 |
1327 | {
1328 | displayName: 'Set Routing',
1329 | name: 'setRouting',
1330 | type: 'hidden',
1331 | default: '',
1332 | displayOptions: {
1333 | show: {
1334 | resource: ['sendMessage'],
1335 | operation: ['sendContact'],
1336 | },
1337 | },
1338 | routing: {
1339 | request: {
1340 | url: '=' + requestURL('message', 'sendContact'),
1341 | method: 'POST',
1342 | },
1343 | send: { preSend: [sendContactMessage] },
1344 | },
1345 | },
1346 | ];
1347 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "n8n-nodes-codechat",
3 | "version": "0.2.0",
4 | "description": "Rest api for communication with WhatsApp",
5 | "keywords": [
6 | "n8n",
7 | "node",
8 | "no-code",
9 | "n8n-nodes-codechat",
10 | "n8n-community-nodes-codechat",
11 | "n8n-community-node-codechat",
12 | "n8n-community-node-package",
13 | "chat",
14 | "communication",
15 | "message",
16 | "send message",
17 | "whatsapp",
18 | "js-whatsapp",
19 | "whatsapp-api",
20 | "whatsapp-web",
21 | "whatsapp",
22 | "whatsapp-chat",
23 | "whatsapp-group",
24 | "automation",
25 | "multi-device",
26 | "bot"
27 | ],
28 | "license": "MIT",
29 | "homepage": "https://github.com/code-chat-br",
30 | "author": {
31 | "name": "codechat",
32 | "email": "suporte@codechat.rest"
33 | },
34 | "repository": {
35 | "type": "git",
36 | "url": "https://github.com/code-chat-br/n8n-node-codechat"
37 | },
38 | "main": "index.js",
39 | "scripts": {
40 | "build": "rm -rf ./dist && tsc && gulp build:icons",
41 | "dev": "tsc --watch",
42 | "format": "prettier nodes credentials --write",
43 | "lint": "tslint -p tsconfig.json -c tslint.json && eslint nodes credentials package.json",
44 | "lintfix": "tslint --fix -p tsconfig.json -c tslint.json && eslint nodes credentials package.json --fix",
45 | "prepublishOnly": "npm run build && npm run lint -c .eslintrc.prepublish.js nodes credentials package.json"
46 | },
47 | "files": [
48 | "dist"
49 | ],
50 | "n8n": {
51 | "n8nNodesApiVersion": 1,
52 | "credentials": [
53 | "dist/credentials/CodeChatCredentialsApi.credentials.js"
54 | ],
55 | "nodes": [
56 | "dist/nodes/CodeChat/CodeChat.node.js"
57 | ]
58 | },
59 | "devDependencies": {
60 | "@types/express": "^4.17.6",
61 | "@types/request-promise-native": "~1.0.15",
62 | "@typescript-eslint/eslint-plugin": "^5.39.0",
63 | "@typescript-eslint/parser": "^5.29.0",
64 | "eslint": "^8.25.0",
65 | "eslint-config-prettier": "^8.5.0",
66 | "eslint-plugin-n8n-nodes-base": "^1.5.4",
67 | "eslint-plugin-prettier": "^4.2.1",
68 | "gulp": "^4.0.2",
69 | "n8n-core": "^0.138.0",
70 | "n8n-workflow": "^0.107.0",
71 | "prettier": "^2.7.1",
72 | "tslint": "^6.1.2",
73 | "typescript": "~4.6.0"
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": [
4 | "es2017",
5 | "es2019"
6 | ],
7 | "types": [
8 | "node",
9 | ],
10 | "module": "commonjs",
11 | "noImplicitAny": true,
12 | "removeComments": true,
13 | "strictNullChecks": true,
14 | "strict": true,
15 | "preserveConstEnums": true,
16 | "resolveJsonModule": true,
17 | "declaration": true,
18 | "outDir": "./dist/",
19 | "target": "es2019",
20 | "sourceMap": true,
21 | "esModuleInterop": true,
22 | "useUnknownInCatchVariables": false,
23 | },
24 | "include": [
25 | "credentials/**/*",
26 | "nodes/**/*",
27 | "nodes/**/*.json",
28 | "package.json",
29 | ],
30 | }
31 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "linterOptions": {
3 | "exclude": [
4 | "node_modules/**/*"
5 | ]
6 | },
7 | "defaultSeverity": "error",
8 | "jsRules": {},
9 | "rules": {
10 | "array-type": [
11 | true,
12 | "array-simple"
13 | ],
14 | "arrow-return-shorthand": true,
15 | "ban": [
16 | true,
17 | {
18 | "name": "Array",
19 | "message": "tsstyle#array-constructor"
20 | }
21 | ],
22 | "ban-types": [
23 | true,
24 | [
25 | "Object",
26 | "Use {} instead."
27 | ],
28 | [
29 | "String",
30 | "Use 'string' instead."
31 | ],
32 | [
33 | "Number",
34 | "Use 'number' instead."
35 | ],
36 | [
37 | "Boolean",
38 | "Use 'boolean' instead."
39 | ]
40 | ],
41 | "class-name": true,
42 | "curly": [
43 | true,
44 | "ignore-same-line"
45 | ],
46 | "forin": true,
47 | "jsdoc-format": true,
48 | "label-position": true,
49 | "indent": [
50 | true,
51 | "tabs",
52 | 2
53 | ],
54 | "member-access": [
55 | true,
56 | "no-public"
57 | ],
58 | "new-parens": true,
59 | "no-angle-bracket-type-assertion": true,
60 | "no-any": true,
61 | "no-arg": true,
62 | "no-conditional-assignment": true,
63 | "no-construct": true,
64 | "no-debugger": true,
65 | "no-default-export": true,
66 | "no-duplicate-variable": true,
67 | "no-inferrable-types": true,
68 | "ordered-imports": [
69 | true,
70 | {
71 | "import-sources-order": "any",
72 | "named-imports-order": "case-insensitive"
73 | }
74 | ],
75 | "no-namespace": [
76 | true,
77 | "allow-declarations"
78 | ],
79 | "no-reference": true,
80 | "no-string-throw": true,
81 | "no-unused-expression": true,
82 | "no-var-keyword": true,
83 | "object-literal-shorthand": true,
84 | "only-arrow-functions": [
85 | true,
86 | "allow-declarations",
87 | "allow-named-functions"
88 | ],
89 | "prefer-const": true,
90 | "radix": true,
91 | "semicolon": [
92 | true,
93 | "always",
94 | "ignore-bound-class-methods"
95 | ],
96 | "switch-default": true,
97 | "trailing-comma": [
98 | true,
99 | {
100 | "multiline": {
101 | "objects": "always",
102 | "arrays": "always",
103 | "functions": "always",
104 | "typeLiterals": "ignore"
105 | },
106 | "esSpecCompliant": true
107 | }
108 | ],
109 | "triple-equals": [
110 | true,
111 | "allow-null-check"
112 | ],
113 | "use-isnan": true,
114 | "quotes": [
115 | "error",
116 | "single"
117 | ],
118 | "variable-name": [
119 | true,
120 | "check-format",
121 | "ban-keywords",
122 | "allow-leading-underscore",
123 | "allow-trailing-underscore"
124 | ]
125 | },
126 | "rulesDirectory": []
127 | }
128 |
--------------------------------------------------------------------------------