n8n-nodes-wavy 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -41,6 +41,18 @@ class Wavy {
41
41
  name: 'Tool',
42
42
  value: 'tool',
43
43
  },
44
+ {
45
+ name: 'Group',
46
+ value: 'group',
47
+ },
48
+ {
49
+ name: 'Chat',
50
+ value: 'chat',
51
+ },
52
+ {
53
+ name: 'Contact',
54
+ value: 'contact',
55
+ },
44
56
  ],
45
57
  default: 'message',
46
58
  },
@@ -60,6 +72,16 @@ class Wavy {
60
72
  value: 'delete',
61
73
  action: 'Delete a message',
62
74
  },
75
+ {
76
+ name: 'Edit Message',
77
+ value: 'editMessage',
78
+ action: 'Edit a sent message',
79
+ },
80
+ {
81
+ name: 'React',
82
+ value: 'reactMessage',
83
+ action: 'React to a message',
84
+ },
63
85
  {
64
86
  name: 'Send Carousel',
65
87
  value: 'sendCarousel',
@@ -105,6 +127,11 @@ class Wavy {
105
127
  value: 'sendSticker',
106
128
  action: 'Send a sticker message',
107
129
  },
130
+ {
131
+ name: 'Send View Once',
132
+ value: 'sendViewOnce',
133
+ action: 'Send a view once media message',
134
+ },
108
135
  {
109
136
  name: 'Send Text',
110
137
  value: 'sendText',
@@ -226,10 +253,36 @@ class Wavy {
226
253
  displayOptions: {
227
254
  show: {
228
255
  resource: ['message'],
229
- operation: ['sendMedia', 'sendSticker'],
256
+ operation: ['sendMedia', 'sendSticker', 'sendViewOnce'],
230
257
  },
231
258
  },
232
259
  },
260
+ {
261
+ displayName: 'View Once Type',
262
+ name: 'viewOnceType',
263
+ type: 'options',
264
+ displayOptions: {
265
+ show: {
266
+ resource: ['message'],
267
+ operation: ['sendViewOnce'],
268
+ },
269
+ },
270
+ options: [
271
+ {
272
+ name: 'Image',
273
+ value: 'viewOnceImage',
274
+ },
275
+ {
276
+ name: 'Video',
277
+ value: 'viewOnceVideo',
278
+ },
279
+ {
280
+ name: 'Audio',
281
+ value: 'viewOnceAudio',
282
+ },
283
+ ],
284
+ default: 'viewOnceImage',
285
+ },
233
286
  {
234
287
  displayName: 'Caption',
235
288
  name: 'caption',
@@ -238,7 +291,7 @@ class Wavy {
238
291
  displayOptions: {
239
292
  show: {
240
293
  resource: ['message'],
241
- operation: ['sendMedia'],
294
+ operation: ['sendMedia', 'sendViewOnce'],
242
295
  },
243
296
  },
244
297
  },
@@ -572,6 +625,218 @@ class Wavy {
572
625
  },
573
626
  },
574
627
  },
628
+ {
629
+ displayName: 'Participants',
630
+ name: 'participants',
631
+ type: 'string',
632
+ default: '',
633
+ placeholder: 'number1,number2',
634
+ description: 'Comma-separated list of phone numbers',
635
+ displayOptions: {
636
+ show: {
637
+ resource: ['group'],
638
+ operation: ['create', 'updateParticipants'],
639
+ },
640
+ },
641
+ },
642
+ {
643
+ displayName: 'Subject',
644
+ name: 'subject',
645
+ type: 'string',
646
+ default: '',
647
+ displayOptions: {
648
+ show: {
649
+ resource: ['group'],
650
+ operation: ['create', 'updateSubject'],
651
+ },
652
+ },
653
+ },
654
+ {
655
+ displayName: 'Description',
656
+ name: 'description',
657
+ type: 'string',
658
+ default: '',
659
+ displayOptions: {
660
+ show: {
661
+ resource: ['group'],
662
+ operation: ['updateDescription'],
663
+ },
664
+ },
665
+ },
666
+ {
667
+ displayName: 'Action',
668
+ name: 'participantAction',
669
+ type: 'options',
670
+ options: [
671
+ { name: 'Add', value: 'add' },
672
+ { name: 'Remove', value: 'remove' },
673
+ { name: 'Promote', value: 'promote' },
674
+ { name: 'Demote', value: 'demote' },
675
+ ],
676
+ default: 'add',
677
+ displayOptions: {
678
+ show: {
679
+ resource: ['group'],
680
+ operation: ['updateParticipants'],
681
+ },
682
+ },
683
+ },
684
+ {
685
+ displayName: 'Setting',
686
+ name: 'groupSetting',
687
+ type: 'options',
688
+ options: [
689
+ { name: 'Announcement (Admins only)', value: 'announcement' },
690
+ { name: 'Not Announcement (All)', value: 'not_announcement' },
691
+ { name: 'Locked (Admins only)', value: 'locked' },
692
+ { name: 'Unlocked (All)', value: 'unlocked' },
693
+ ],
694
+ default: 'announcement',
695
+ displayOptions: {
696
+ show: {
697
+ resource: ['group'],
698
+ operation: ['updateSetting'],
699
+ },
700
+ },
701
+ },
702
+ {
703
+ displayName: 'Operation',
704
+ name: 'operation',
705
+ type: 'options',
706
+ noDataExpression: true,
707
+ displayOptions: {
708
+ show: {
709
+ resource: ['chat'],
710
+ },
711
+ },
712
+ options: [
713
+ { name: 'Archive', value: 'archive', action: 'Archive a chat' },
714
+ { name: 'Unarchive', value: 'unarchive', action: 'Unarchive a chat' },
715
+ { name: 'Pin', value: 'pin', action: 'Pin a chat' },
716
+ { name: 'Unpin', value: 'unpin', action: 'Unpin a chat' },
717
+ { name: 'Mute', value: 'mute', action: 'Mute a chat' },
718
+ { name: 'Unmute', value: 'unmute', action: 'Unmute a chat' },
719
+ { name: 'Mark Read', value: 'markRead', action: 'Mark chat as read' },
720
+ { name: 'Mark Unread', value: 'markUnread', action: 'Mark chat as unread' },
721
+ { name: 'Clear Chat', value: 'clear', action: 'Clear all messages in chat' },
722
+ { name: 'Delete Chat', value: 'delete', action: 'Delete entire chat' },
723
+ ],
724
+ default: 'archive',
725
+ },
726
+ {
727
+ displayName: 'JID',
728
+ name: 'jid',
729
+ type: 'string',
730
+ default: '',
731
+ required: true,
732
+ displayOptions: {
733
+ show: {
734
+ resource: ['chat', 'group', 'contact'],
735
+ },
736
+ },
737
+ description: 'The JID of the chat, group, or contact',
738
+ },
739
+ {
740
+ displayName: 'Mute Duration (ms)',
741
+ name: 'muteDuration',
742
+ type: 'number',
743
+ default: 28800000,
744
+ displayOptions: {
745
+ show: {
746
+ resource: ['chat'],
747
+ operation: ['mute'],
748
+ },
749
+ },
750
+ },
751
+ {
752
+ displayName: 'Operation',
753
+ name: 'operation',
754
+ type: 'options',
755
+ noDataExpression: true,
756
+ displayOptions: {
757
+ show: {
758
+ resource: ['contact'],
759
+ },
760
+ },
761
+ options: [
762
+ { name: 'Block', value: 'block', action: 'Block a contact' },
763
+ { name: 'Unblock', value: 'unblock', action: 'Unblock a contact' },
764
+ { name: 'Get Profile Picture', value: 'getProfilePicture', action: 'Get profile picture URL' },
765
+ { name: 'Update Status', value: 'updateStatus', action: 'Update your profile status (About)' },
766
+ ],
767
+ default: 'getProfilePicture',
768
+ },
769
+ {
770
+ displayName: 'Status',
771
+ name: 'status',
772
+ type: 'string',
773
+ default: '',
774
+ displayOptions: {
775
+ show: {
776
+ resource: ['contact'],
777
+ operation: ['updateStatus'],
778
+ },
779
+ },
780
+ },
781
+ {
782
+ displayName: 'Message Key (JSON)',
783
+ name: 'messageKey',
784
+ type: 'json',
785
+ default: '{}',
786
+ displayOptions: {
787
+ show: {
788
+ resource: ['message'],
789
+ operation: ['editMessage', 'reactMessage'],
790
+ },
791
+ },
792
+ description: 'The key object of the message to edit or react to',
793
+ },
794
+ {
795
+ displayName: 'New Text',
796
+ name: 'newText',
797
+ type: 'string',
798
+ default: '',
799
+ displayOptions: {
800
+ show: {
801
+ resource: ['message'],
802
+ operation: ['editMessage'],
803
+ },
804
+ },
805
+ },
806
+ {
807
+ displayName: 'Emoji',
808
+ name: 'emoji',
809
+ type: 'string',
810
+ default: '👍',
811
+ displayOptions: {
812
+ show: {
813
+ resource: ['message'],
814
+ operation: ['reactMessage'],
815
+ },
816
+ },
817
+ },
818
+ {
819
+ displayName: 'Operation',
820
+ name: 'operation',
821
+ type: 'options',
822
+ noDataExpression: true,
823
+ displayOptions: {
824
+ show: {
825
+ resource: ['group'],
826
+ },
827
+ },
828
+ options: [
829
+ { name: 'Create', value: 'create', action: 'Create a new group' },
830
+ { name: 'Leave', value: 'leave', action: 'Leave a group' },
831
+ { name: 'Get Invite Code', value: 'getInviteCode', action: 'Get group invite code' },
832
+ { name: 'Get Metadata', value: 'getMetadata', action: 'Get group metadata' },
833
+ { name: 'Update Description', value: 'updateDescription', action: 'Update group description' },
834
+ { name: 'Update Participants', value: 'updateParticipants', action: 'Add, remove, promote, or demote participants' },
835
+ { name: 'Update Settings', value: 'updateSetting', action: 'Update group settings' },
836
+ { name: 'Update Subject', value: 'updateSubject', action: 'Update group subject' },
837
+ ],
838
+ default: 'getMetadata',
839
+ },
575
840
  {
576
841
  displayName: 'Phone Numbers',
577
842
  name: 'phoneNumbers',
@@ -589,10 +854,43 @@ class Wavy {
589
854
  ],
590
855
  };
591
856
  }
857
+ sanitizeBaseUrl(url) {
858
+ let sanitized = url.trim();
859
+ if (sanitized.endsWith('/')) {
860
+ sanitized = sanitized.slice(0, -1);
861
+ }
862
+ if (sanitized.endsWith('/api')) {
863
+ sanitized = sanitized.slice(0, -4);
864
+ }
865
+ return sanitized;
866
+ }
867
+ async checkSessionConnected(credentials, sessionId) {
868
+ const options = {
869
+ method: 'GET',
870
+ uri: `${credentials.baseUrl}/api/sessions/${sessionId}`,
871
+ headers: {
872
+ 'X-API-Key': credentials.apiKey,
873
+ },
874
+ json: true,
875
+ };
876
+ try {
877
+ const response = await this.helpers.request(options);
878
+ if (!response.isConnected) {
879
+ throw new Error(`Session "${sessionId}" is not connected. Please connect it in the Wavy dashboard first.`);
880
+ }
881
+ }
882
+ catch (error) {
883
+ if (error.message.includes('not connected'))
884
+ throw error;
885
+ throw new Error(`Failed to verify session connectivity: ${error.message}`);
886
+ }
887
+ }
592
888
  async execute() {
593
889
  const items = this.getInputData();
594
890
  const returnData = [];
595
- const credentials = await this.getCredentials('wavyApi');
891
+ const rawCredentials = await this.getCredentials('wavyApi');
892
+ const baseUrl = this.sanitizeBaseUrl(rawCredentials.baseUrl);
893
+ const credentials = Object.assign(Object.assign({}, rawCredentials), { baseUrl });
596
894
  for (let i = 0; i < items.length; i++) {
597
895
  try {
598
896
  const resource = this.getNodeParameter('resource', i);
@@ -606,15 +904,28 @@ class Wavy {
606
904
  const sessionId = this.getNodeParameter('sessionId', i);
607
905
  body.to = to;
608
906
  if (operation === 'sendText') {
907
+ await this.checkSessionConnected(credentials, sessionId);
609
908
  endpoint = `/sessions/${sessionId}/send`;
610
909
  body.message = this.getNodeParameter('message', i);
611
910
  }
612
911
  else if (operation === 'sendMedia') {
912
+ await this.checkSessionConnected(credentials, sessionId);
613
913
  endpoint = `/send-media`;
614
914
  body.sender = sessionId;
615
915
  body.mediaUrl = this.getNodeParameter('mediaUrl', i);
616
916
  body.message = this.getNodeParameter('caption', i);
617
917
  }
918
+ else if (operation === 'sendViewOnce') {
919
+ await this.checkSessionConnected(credentials, sessionId);
920
+ endpoint = `/send-interactive`;
921
+ body.sender = sessionId;
922
+ const type = this.getNodeParameter('viewOnceType', i);
923
+ body.messageType = type;
924
+ body.data = {
925
+ mediaUrl: this.getNodeParameter('mediaUrl', i),
926
+ caption: this.getNodeParameter('caption', i),
927
+ };
928
+ }
618
929
  else if (operation === 'sendSticker') {
619
930
  endpoint = `/send-sticker`;
620
931
  body.sender = sessionId;
@@ -682,7 +993,7 @@ class Wavy {
682
993
  }
683
994
  }
684
995
  else if (operation === 'sendCarousel') {
685
- endpoint = `/sessions/${sessionId}/send-interactive`;
996
+ endpoint = `/send-interactive`;
686
997
  const payload = this.getNodeParameter('payload', i);
687
998
  body = Object.assign(Object.assign({}, body), payload);
688
999
  }
@@ -691,10 +1002,109 @@ class Wavy {
691
1002
  body.sender = sessionId;
692
1003
  body.key = this.getNodeParameter('messageKey', i);
693
1004
  }
1005
+ else if (operation === 'reactMessage') {
1006
+ await this.checkSessionConnected(credentials, sessionId);
1007
+ endpoint = `/messages/react`;
1008
+ body.jid = to;
1009
+ body.key = this.getNodeParameter('messageKey', i);
1010
+ body.emoji = this.getNodeParameter('emoji', i);
1011
+ }
1012
+ else if (operation === 'editMessage') {
1013
+ await this.checkSessionConnected(credentials, sessionId);
1014
+ endpoint = `/messages/edit`;
1015
+ body.jid = to;
1016
+ body.key = this.getNodeParameter('messageKey', i);
1017
+ body.newText = this.getNodeParameter('newText', i);
1018
+ }
1019
+ }
1020
+ else if (resource === 'group') {
1021
+ const sessionId = this.getNodeParameter('sessionId', i);
1022
+ await this.checkSessionConnected(credentials, sessionId);
1023
+ if (operation === 'create') {
1024
+ endpoint = `/groups/create`;
1025
+ body.subject = this.getNodeParameter('subject', i);
1026
+ body.participants = this.getNodeParameter('participants', i).split(',').map(p => p.trim());
1027
+ }
1028
+ else {
1029
+ const jid = this.getNodeParameter('jid', i);
1030
+ if (operation === 'leave') {
1031
+ endpoint = `/groups/${jid}/leave`;
1032
+ }
1033
+ else if (operation === 'getInviteCode') {
1034
+ endpoint = `/groups/${jid}/invite-code`;
1035
+ method = 'GET';
1036
+ }
1037
+ else if (operation === 'getMetadata') {
1038
+ endpoint = `/groups/${jid}`;
1039
+ method = 'GET';
1040
+ }
1041
+ else if (operation === 'updateDescription') {
1042
+ endpoint = `/groups/${jid}/description`;
1043
+ body.description = this.getNodeParameter('description', i);
1044
+ }
1045
+ else if (operation === 'updateParticipants') {
1046
+ endpoint = `/groups/${jid}/participants`;
1047
+ body.participants = this.getNodeParameter('participants', i).split(',').map(p => p.trim());
1048
+ body.action = this.getNodeParameter('participantAction', i);
1049
+ }
1050
+ else if (operation === 'updateSetting') {
1051
+ endpoint = `/groups/${jid}/settings`;
1052
+ body.setting = this.getNodeParameter('groupSetting', i);
1053
+ }
1054
+ else if (operation === 'updateSubject') {
1055
+ endpoint = `/groups/${jid}/update`;
1056
+ body.subject = this.getNodeParameter('subject', i);
1057
+ }
1058
+ }
1059
+ }
1060
+ else if (resource === 'chat') {
1061
+ const sessionId = this.getNodeParameter('sessionId', i);
1062
+ const jid = this.getNodeParameter('jid', i);
1063
+ await this.checkSessionConnected(credentials, sessionId);
1064
+ endpoint = `/chats/${jid}/modify`;
1065
+ const op = operation;
1066
+ if (op === 'archive') body.action = 'archive';
1067
+ else if (op === 'unarchive') body.action = 'unarchive';
1068
+ else if (op === 'pin') body.action = 'pin';
1069
+ else if (op === 'unpin') body.action = 'unpin';
1070
+ else if (op === 'mute') {
1071
+ body.action = 'mute';
1072
+ body.muteDuration = this.getNodeParameter('muteDuration', i);
1073
+ }
1074
+ else if (op === 'unmute') body.action = 'unmute';
1075
+ else if (op === 'markRead') body.action = 'markRead';
1076
+ else if (op === 'markUnread') body.action = 'markUnread';
1077
+ else if (op === 'clear') body.action = 'clear';
1078
+ else if (op === 'delete') body.action = 'delete';
1079
+ }
1080
+ else if (resource === 'contact') {
1081
+ const sessionId = this.getNodeParameter('sessionId', i);
1082
+ await this.checkSessionConnected(credentials, sessionId);
1083
+ if (operation === 'updateStatus') {
1084
+ endpoint = `/profile/status`;
1085
+ body.status = this.getNodeParameter('status', i);
1086
+ }
1087
+ else {
1088
+ const jid = this.getNodeParameter('jid', i);
1089
+ if (operation === 'block') {
1090
+ endpoint = `/contacts/block`;
1091
+ body.jid = jid;
1092
+ body.action = 'block';
1093
+ }
1094
+ else if (operation === 'unblock') {
1095
+ endpoint = `/contacts/block`;
1096
+ body.jid = jid;
1097
+ body.action = 'unblock';
1098
+ }
1099
+ else if (operation === 'getProfilePicture') {
1100
+ endpoint = `/contacts/${jid}/picture`;
1101
+ method = 'GET';
1102
+ }
1103
+ }
694
1104
  }
695
1105
  else if (resource === 'session') {
696
1106
  if (operation === 'getAll') {
697
- endpoint = '/sessions';
1107
+ endpoint = '/sessions/list';
698
1108
  method = 'GET';
699
1109
  }
700
1110
  else if (operation === 'create') {
@@ -66,12 +66,24 @@ class WavyTrigger {
66
66
  },
67
67
  ],
68
68
  };
69
+ this.sanitizeBaseUrl = (url) => {
70
+ let sanitized = url.trim();
71
+ if (sanitized.endsWith('/')) {
72
+ sanitized = sanitized.slice(0, -1);
73
+ }
74
+ if (sanitized.endsWith('/api')) {
75
+ sanitized = sanitized.slice(0, -4);
76
+ }
77
+ return sanitized;
78
+ };
69
79
  this.webhookMethods = {
70
80
  default: {
71
81
  async checkExists() {
72
82
  const webhookUrl = this.getNodeWebhookUrl('default');
73
83
  const sessionId = this.getNodeParameter('sessionId');
74
- const credentials = await this.getCredentials('wavyApi');
84
+ const rawCredentials = await this.getCredentials('wavyApi');
85
+ const baseUrl = this.nodeType.sanitizeBaseUrl(rawCredentials.baseUrl);
86
+ const credentials = Object.assign(Object.assign({}, rawCredentials), { baseUrl });
75
87
  const options = {
76
88
  method: 'GET',
77
89
  uri: `${credentials.baseUrl}/api/sessions/${sessionId}`,
@@ -91,7 +103,9 @@ class WavyTrigger {
91
103
  async create() {
92
104
  const webhookUrl = this.getNodeWebhookUrl('default');
93
105
  const sessionId = this.getNodeParameter('sessionId');
94
- const credentials = await this.getCredentials('wavyApi');
106
+ const rawCredentials = await this.getCredentials('wavyApi');
107
+ const baseUrl = this.nodeType.sanitizeBaseUrl(rawCredentials.baseUrl);
108
+ const credentials = Object.assign(Object.assign({}, rawCredentials), { baseUrl });
95
109
  const options = {
96
110
  method: 'POST',
97
111
  uri: `${credentials.baseUrl}/api/sessions/${sessionId}/webhook`,
@@ -110,7 +124,9 @@ class WavyTrigger {
110
124
  },
111
125
  async delete() {
112
126
  const sessionId = this.getNodeParameter('sessionId');
113
- const credentials = await this.getCredentials('wavyApi');
127
+ const rawCredentials = await this.getCredentials('wavyApi');
128
+ const baseUrl = this.nodeType.sanitizeBaseUrl(rawCredentials.baseUrl);
129
+ const credentials = Object.assign(Object.assign({}, rawCredentials), { baseUrl });
114
130
  const options = {
115
131
  method: 'POST',
116
132
  uri: `${credentials.baseUrl}/api/sessions/${sessionId}/webhook`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-wavy",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Node untuk Wavy gess",
5
5
  "files": [
6
6
  "dist",