n8n-nodes-ume-v4 4.0.0

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.
Files changed (58) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +204 -0
  3. package/dist/credentials/UmeApi.credentials.d.ts +7 -0
  4. package/dist/credentials/UmeApi.credentials.js +41 -0
  5. package/dist/index.d.ts +4 -0
  6. package/dist/index.js +9 -0
  7. package/dist/nodes/FacebookComment/FacebookComment.node.d.ts +5 -0
  8. package/dist/nodes/FacebookComment/FacebookComment.node.js +155 -0
  9. package/dist/nodes/FacebookCommentLike/FacebookCommentLike.node.d.ts +5 -0
  10. package/dist/nodes/FacebookCommentLike/FacebookCommentLike.node.js +122 -0
  11. package/dist/nodes/FacebookLike/FacebookLike.node.d.ts +5 -0
  12. package/dist/nodes/FacebookLike/FacebookLike.node.js +160 -0
  13. package/dist/nodes/FacebookShare/FacebookShare.node.d.ts +5 -0
  14. package/dist/nodes/FacebookShare/FacebookShare.node.js +141 -0
  15. package/dist/nodes/FacebookStoryView/FacebookStoryView.node.d.ts +5 -0
  16. package/dist/nodes/FacebookStoryView/FacebookStoryView.node.js +122 -0
  17. package/dist/nodes/FacebookView/FacebookView.node.d.ts +5 -0
  18. package/dist/nodes/FacebookView/FacebookView.node.js +127 -0
  19. package/dist/nodes/InstagramComment/InstagramComment.node.d.ts +5 -0
  20. package/dist/nodes/InstagramComment/InstagramComment.node.js +145 -0
  21. package/dist/nodes/InstagramLike/InstagramLike.node.d.ts +5 -0
  22. package/dist/nodes/InstagramLike/InstagramLike.node.js +138 -0
  23. package/dist/nodes/InstagramView/InstagramView.node.d.ts +5 -0
  24. package/dist/nodes/InstagramView/InstagramView.node.js +151 -0
  25. package/dist/nodes/TikTokComment/TikTokComment.node.d.ts +5 -0
  26. package/dist/nodes/TikTokComment/TikTokComment.node.js +136 -0
  27. package/dist/nodes/TikTokCommentLike/TikTokCommentLike.node.d.ts +5 -0
  28. package/dist/nodes/TikTokCommentLike/TikTokCommentLike.node.js +122 -0
  29. package/dist/nodes/TikTokFavorite/TikTokFavorite.node.d.ts +5 -0
  30. package/dist/nodes/TikTokFavorite/TikTokFavorite.node.js +127 -0
  31. package/dist/nodes/TikTokLike/TikTokLike.node.d.ts +5 -0
  32. package/dist/nodes/TikTokLike/TikTokLike.node.js +141 -0
  33. package/dist/nodes/TikTokShare/TikTokShare.node.d.ts +5 -0
  34. package/dist/nodes/TikTokShare/TikTokShare.node.js +131 -0
  35. package/dist/nodes/TikTokView/TikTokView.node.d.ts +5 -0
  36. package/dist/nodes/TikTokView/TikTokView.node.js +136 -0
  37. package/dist/nodes/Ume/Ume.node.d.ts +5 -0
  38. package/dist/nodes/Ume/Ume.node.js +244 -0
  39. package/dist/nodes/Ume/operations-data.json +2246 -0
  40. package/dist/nodes/UmeSocialSeeding/UmeSocialSeeding.node.d.ts +5 -0
  41. package/dist/nodes/UmeSocialSeeding/UmeSocialSeeding.node.js +203 -0
  42. package/dist/nodes/UmeSocialSeeding/UmeSocialSeedingHelpers.d.ts +4 -0
  43. package/dist/nodes/UmeSocialSeeding/UmeSocialSeedingHelpers.js +382 -0
  44. package/dist/nodes/UmeSocialSeeding/operations-data.json +673 -0
  45. package/dist/nodes/UmeSocialSeeding/reaction.png +0 -0
  46. package/dist/nodes/UmeSocialSeeding/server-info.json +431 -0
  47. package/dist/nodes/UmeSocialSeeding/services-data-backup.json +3525 -0
  48. package/dist/nodes/UmeSocialSeeding/services-data-old.json +2704 -0
  49. package/dist/nodes/UmeSocialSeeding/services-data.json +7341 -0
  50. package/dist/nodes/UmeSocialSeeding/services-data.json.backup +3525 -0
  51. package/dist/nodes/UmeSocialSeeding/services-data.json.backup.1759909364606 +1927 -0
  52. package/dist/nodes/YouTubeComment/YouTubeComment.node.d.ts +5 -0
  53. package/dist/nodes/YouTubeComment/YouTubeComment.node.js +152 -0
  54. package/dist/nodes/YouTubeLike/YouTubeLike.node.d.ts +5 -0
  55. package/dist/nodes/YouTubeLike/YouTubeLike.node.js +141 -0
  56. package/dist/nodes/YouTubeView/YouTubeView.node.d.ts +5 -0
  57. package/dist/nodes/YouTubeView/YouTubeView.node.js +142 -0
  58. package/package.json +62 -0
@@ -0,0 +1,5 @@
1
+ import type { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from "n8n-workflow";
2
+ export declare class UmeSocialSeeding implements INodeType {
3
+ description: INodeTypeDescription;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
+ }
@@ -0,0 +1,203 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.UmeSocialSeeding = void 0;
7
+ const n8n_workflow_1 = require("n8n-workflow");
8
+ const UmeSocialSeedingHelpers_1 = require("./UmeSocialSeedingHelpers");
9
+ const services_data_json_1 = __importDefault(require("./services-data.json"));
10
+ const BASE_URL = "https://ume.com.vn/api";
11
+ // Function to extract UID from Facebook URL
12
+ function extractUidFromUrl(input) {
13
+ // If input is already a number, return as is
14
+ if (/^\d+$/.test(input)) {
15
+ return input;
16
+ }
17
+ // Facebook URL patterns
18
+ const facebookPatterns = [
19
+ /facebook\.com\/.*\/posts\/pfbid([A-Za-z0-9]+)/, // For posts with pfbid
20
+ /facebook\.com\/.*\/posts\/(\d+)/, // For posts with numeric ID
21
+ /facebook\.com\/.*\/photo\/\?fbid=([\d]+)/, // Photo fbid
22
+ /facebook\.com\/.*\/permalink\.php\?story_fbid=([\d]+)/, // Story fbid
23
+ /facebook\.com\/(\d+)/, // Direct numeric ID
24
+ /facebook\.com\/pages\/[^\/]+\/(\d+)/, // Page with numeric ID
25
+ /facebook\.com\/([^\/\?]+)/, // Username (e.g., neversaynever504)
26
+ /pfbid([A-Za-z0-9]+)/, // Standalone pfbid
27
+ /story_fbid=([\d]+)/, // Story fbid param
28
+ /fbid=([\d]+)/, // General fbid param
29
+ ];
30
+ for (const pattern of facebookPatterns) {
31
+ const match = input.match(pattern);
32
+ if (match && match[1]) {
33
+ // For the posts/pfbid pattern, we need to reconstruct the full ID
34
+ if (pattern.toString().includes('posts\\/pfbid')) {
35
+ return 'pfbid' + match[1];
36
+ }
37
+ // For standalone pfbid pattern
38
+ else if (pattern.toString().includes('pfbid')) {
39
+ return 'pfbid' + match[1];
40
+ }
41
+ return match[1];
42
+ }
43
+ }
44
+ // Special handling for Facebook usernames (only if no patterns matched)
45
+ if (input.includes('facebook.com/') && !input.includes('/posts/') && !input.includes('/photo/') && !input.includes('permalink.php')) {
46
+ const usernameMatch = input.match(/facebook\.com\/([^\/\?]+)/);
47
+ if (usernameMatch && usernameMatch[1]) {
48
+ // Return username for Facebook pages (most APIs accept usernames)
49
+ return usernameMatch[1];
50
+ }
51
+ }
52
+ // If no pattern matches, return original input
53
+ return input;
54
+ }
55
+ function getPlatformDisplayName(platform) {
56
+ const platformNames = {
57
+ 'facebook': 'Facebook',
58
+ 'instagram': 'Instagram',
59
+ 'tiktok': 'TikTok',
60
+ 'youtube': 'YouTube',
61
+ 'telegram': 'Telegram',
62
+ 'twitter': 'Twitter/X',
63
+ 'threads': 'Threads',
64
+ 'shopee': 'Shopee',
65
+ };
66
+ return platformNames[platform] || platform.toUpperCase();
67
+ }
68
+ class UmeSocialSeeding {
69
+ constructor() {
70
+ this.description = {
71
+ displayName: "UME Social Seeding",
72
+ name: "umeSocialSeeding",
73
+ group: ["transform"],
74
+ version: 1,
75
+ subtitle: '={{$parameter["platform"]}} - {{$parameter["service"]}}',
76
+ description: "Hệ thống seeding mįŗ”ng xĆ£ hį»™i UME.COM.VN - Tăng Like, Sub, View cho cĆ”c nền tįŗ£ng mįŗ”ng xĆ£ hį»™i",
77
+ icon: "file:reaction.png",
78
+ defaults: {
79
+ name: "UME Social Seeding",
80
+ },
81
+ inputs: ["main"],
82
+ outputs: ["main"],
83
+ credentials: [
84
+ {
85
+ name: "umeApi",
86
+ required: true,
87
+ },
88
+ ],
89
+ properties: [
90
+ {
91
+ displayName: "Nền tảng",
92
+ name: "platform",
93
+ type: "options",
94
+ noDataExpression: true,
95
+ options: Object.keys(services_data_json_1.default).map(key => ({
96
+ name: getPlatformDisplayName(key),
97
+ value: key,
98
+ description: `CĆ”c dịch vỄ tăng tʰʔng tĆ”c cho ${getPlatformDisplayName(key)}`,
99
+ })),
100
+ default: "facebook",
101
+ description: "Chį»n nền tįŗ£ng mįŗ”ng xĆ£ hį»™i cįŗ§n seeding",
102
+ },
103
+ ...(0, UmeSocialSeedingHelpers_1.getDynamicProperties)(),
104
+ ],
105
+ };
106
+ }
107
+ async execute() {
108
+ var _a, _b, _c;
109
+ const returnData = [];
110
+ const items = this.getInputData();
111
+ const credentials = (await this.getCredentials("umeApi"));
112
+ if (!credentials || !credentials.token) {
113
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), "API token is missing in the UME credentials.");
114
+ }
115
+ const baseUrl = ((_a = credentials.baseUrl) !== null && _a !== void 0 ? _a : BASE_URL).replace(/\/+$/, "");
116
+ for (let itemIndex = 0; itemIndex < items.length; itemIndex += 1) {
117
+ const platform = this.getNodeParameter("platform", itemIndex);
118
+ const serviceName = this.getNodeParameter("service", itemIndex);
119
+ const platformData = services_data_json_1.default[platform];
120
+ if (!platformData) {
121
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid platform: ${platform}`, { itemIndex });
122
+ }
123
+ const serviceData = platformData[serviceName];
124
+ if (!serviceData) {
125
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid service: ${serviceName}`, { itemIndex });
126
+ }
127
+ // Build form data
128
+ const formBody = {
129
+ token: credentials.token,
130
+ };
131
+ // Add service-specific parameters
132
+ for (const parameter of serviceData.parameters) {
133
+ if (parameter.type === 'string') {
134
+ const value = this.getNodeParameter(parameter.name, itemIndex, "");
135
+ if (parameter.name === 'uid') {
136
+ // Extract UID from URL for Facebook services
137
+ const extractedValue = extractUidFromUrl(value.trim() || '');
138
+ formBody[parameter.name] = extractedValue;
139
+ }
140
+ else if (parameter.name === 'link') {
141
+ // For Instagram, TikTok, YouTube - send the URL as-is without extraction
142
+ formBody[parameter.name] = value.trim();
143
+ }
144
+ else if (parameter.name === 'note') {
145
+ // Set default note if empty
146
+ formBody[parameter.name] = value.trim() || "Auto-generated by n8n UME Social Seeding";
147
+ }
148
+ else if (value && value.trim() !== "") {
149
+ formBody[parameter.name] = value.trim();
150
+ }
151
+ }
152
+ else if (parameter.type === 'number') {
153
+ const value = this.getNodeParameter(parameter.name, itemIndex);
154
+ if (value !== undefined && value !== null && !isNaN(Number(value))) {
155
+ formBody[parameter.name] = Number(value).toString();
156
+ }
157
+ }
158
+ else if (parameter.type === 'options') {
159
+ const value = this.getNodeParameter(parameter.name, itemIndex, "");
160
+ if (value && value.trim() !== "") {
161
+ formBody[parameter.name] = value.trim();
162
+ }
163
+ }
164
+ }
165
+ // Make API request
166
+ const endpoint = serviceData.endpoint.replace(/^\/+/, "");
167
+ const requestOptions = {
168
+ method: "POST",
169
+ uri: `${baseUrl}/${endpoint}`,
170
+ form: formBody,
171
+ json: true,
172
+ };
173
+ let responseData;
174
+ try {
175
+ responseData = await this.helpers.request(requestOptions);
176
+ }
177
+ catch (error) {
178
+ if (error instanceof n8n_workflow_1.NodeOperationError) {
179
+ throw error;
180
+ }
181
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, { itemIndex });
182
+ }
183
+ const responseObject = typeof responseData === "object" && responseData !== null
184
+ ? responseData
185
+ : { data: responseData };
186
+ // Add service metadata to response
187
+ responseObject._umeService = {
188
+ platform,
189
+ service: serviceName,
190
+ description: ((_c = (_b = serviceData.parameters) === null || _b === void 0 ? void 0 : _b.find((p) => p.name === 'uid' || p.name === 'link')) === null || _c === void 0 ? void 0 : _c.description) || '',
191
+ endpoint: serviceData.endpoint,
192
+ };
193
+ returnData.push({
194
+ json: responseObject,
195
+ pairedItem: {
196
+ item: itemIndex,
197
+ },
198
+ });
199
+ }
200
+ return [returnData];
201
+ }
202
+ }
203
+ exports.UmeSocialSeeding = UmeSocialSeeding;
@@ -0,0 +1,4 @@
1
+ import type { INodeProperties } from "n8n-workflow";
2
+ export declare function getDynamicProperties(): INodeProperties[];
3
+ export declare function extractIdFromUrl(url: string, serviceKey?: string): string;
4
+ export declare function generateAutoNote(serviceName: string, platform: string, inputData: any): string;
@@ -0,0 +1,382 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getDynamicProperties = getDynamicProperties;
7
+ exports.extractIdFromUrl = extractIdFromUrl;
8
+ exports.generateAutoNote = generateAutoNote;
9
+ const services_data_json_1 = __importDefault(require("./services-data.json"));
10
+ function getServerDetails(serviceData, serverId) {
11
+ if (!serviceData || !serviceData.servers) {
12
+ return "KhÓng có thÓng tin chi tiết";
13
+ }
14
+
15
+ const server = serviceData.servers.find(s => s.id === serverId || s.server_id === serverId);
16
+ if (!server) {
17
+ return "KhÓng tìm thẄy thÓng tin server";
18
+ }
19
+
20
+ let details = [];
21
+
22
+ // Only show features, not price/quantity/status
23
+ if (server.features && server.features.length > 0) {
24
+ details.push("Features:");
25
+ server.features.forEach(feature => {
26
+ // Remove bullet points and add proper formatting
27
+ const cleanFeature = feature.replace(/^[-*]\s*/, '').trim();
28
+ details.push(`• ${cleanFeature}`);
29
+ });
30
+ } else {
31
+ details.push("KhĆ“ng có features đặc biệt");
32
+ }
33
+
34
+ return details.join('\n');
35
+ }
36
+
37
+ function getDynamicProperties() {
38
+ var _a, _b, _c;
39
+ const properties = [];
40
+ // Generate service options for each platform
41
+ for (const [platformKey, platformData] of Object.entries(services_data_json_1.default)) {
42
+ const platformServices = platformData;
43
+ const serviceOptions = Object.keys(platformServices).map(serviceName => ({
44
+ name: getServiceDisplayName(platformKey, serviceName),
45
+ value: serviceName,
46
+ }));
47
+ properties.push({
48
+ displayName: "Dịch vỄ",
49
+ name: "service",
50
+ type: "options",
51
+ noDataExpression: true,
52
+ options: serviceOptions,
53
+ default: ((_a = serviceOptions[0]) === null || _a === void 0 ? void 0 : _a.value) || "",
54
+ displayOptions: {
55
+ show: {
56
+ platform: [platformKey],
57
+ },
58
+ },
59
+ description: `Chį»n dịch vỄ ${getPlatformDisplayName(platformKey)} cįŗ§n sį»­ dỄng`,
60
+ });
61
+ // Add service-specific parameters
62
+ for (const [serviceName, serviceData] of Object.entries(platformServices)) {
63
+ const serviceParameters = [];
64
+ // Add server options from services data
65
+ const serverParam = (_b = serviceData.parameters) === null || _b === void 0 ? void 0 : _b.find((p) => p.name === 'server');
66
+ if (serverParam === null || serverParam === void 0 ? void 0 : serverParam.options) {
67
+ serviceParameters.push({
68
+ displayName: "Server",
69
+ name: "server",
70
+ type: "options",
71
+ default: serverParam.default || ((_c = serverParam.options[0]) === null || _c === void 0 ? void 0 : _c.value) || "",
72
+ options: serverParam.options,
73
+ displayOptions: {
74
+ show: {
75
+ platform: [platformKey],
76
+ service: [serviceName],
77
+ },
78
+ },
79
+ description: "Chį»n server Ä‘į»ƒ sį»­ dỄng dịch vỄ",
80
+ });
81
+ // Add server-specific details for each server
82
+ serverParam.options.forEach((serverOption) => {
83
+ serviceParameters.push({
84
+ displayName: `Chi tiįŗæt ${serverOption.name}`,
85
+ name: `server_details_${serverOption.value}`,
86
+ type: "string",
87
+ default: getServerDetails(serviceData, serverOption.value),
88
+ typeOptions: {
89
+ rows: 8,
90
+ },
91
+ displayOptions: {
92
+ show: {
93
+ platform: [platformKey],
94
+ service: [serviceName],
95
+ server: [serverOption.value],
96
+ },
97
+ },
98
+ description: "ThĆ“ng tin chi tiįŗæt về server đã chį»n",
99
+ noDataExpression: true,
100
+ });
101
+ });
102
+ }
103
+ // Add all service parameters except server (which we already added)
104
+ const serviceParametersData = serviceData.parameters || [];
105
+ for (const parameter of serviceParametersData) {
106
+ if (parameter.name !== 'server') {
107
+ const paramProperty = {
108
+ ...parameter,
109
+ displayOptions: {
110
+ show: {
111
+ platform: [platformKey],
112
+ service: [serviceName],
113
+ },
114
+ },
115
+ };
116
+ serviceParameters.push(paramProperty);
117
+ }
118
+ }
119
+ // Add general note if available
120
+ serviceParameters.push({
121
+ displayName: "Lưu ý chung",
122
+ name: `${serviceName}_general_note`,
123
+ type: "string",
124
+ default: getGeneralNote(platformKey, serviceName),
125
+ typeOptions: {
126
+ rows: 10,
127
+ },
128
+ displayOptions: {
129
+ show: {
130
+ platform: [platformKey],
131
+ service: [serviceName],
132
+ },
133
+ },
134
+ description: "Lʰu ý quan trį»ng khi sį»­ dỄng dịch vỄ",
135
+ noDataExpression: true,
136
+ });
137
+ properties.push(...serviceParameters);
138
+ }
139
+ }
140
+ return properties;
141
+ }
142
+ function getGeneralNote(platform, serviceName) {
143
+ const servicesData = require('./services-data.json');
144
+ const platformData = servicesData[platform];
145
+ const serviceData = platformData && platformData[serviceName];
146
+
147
+ if (serviceData && serviceData.warnings && serviceData.warnings.length > 0) {
148
+ return serviceData.warnings.map((warning, index) => (index + 1) + '. ' + warning).join('\n');
149
+ }
150
+
151
+ // Fallback for services without specific warnings
152
+ if (platform === 'facebook') {
153
+ return `Lʰu ý khi sį»­ dỄng dịch vỄ Facebook:
154
+
155
+ • Hướng dįŗ«n lįŗ„y link - Mį»™t số lį»—i phổ biįŗæn:
156
+ - Link avatar, bƬa (vĆ  nhį»› bįŗ­t nĆŗt Like)
157
+ - Link dẔng post cho video (link chứa từ "post")
158
+ - Link bĆ i chia sįŗ» (share bĆ i viįŗæt vĆ  share video)
159
+
160
+ • Mį»—i UID có thể mua tối đa 30-60 lįŗ§n tùy server
161
+
162
+ • Nįŗæu tăng video trong album, vui lòng sį»­ dỄng server thĆ­ch hợp
163
+
164
+ • Tįŗ„t cįŗ£ server khĆ“ng bįŗ£o hĆ nh tʰʔng tĆ”c khi bị tỄt
165
+
166
+ • CĆ”c trĘ°į»ng hợp hį»§y gói vĆ  khĆ“ng lĆŖn tʰʔng tĆ”c:
167
+ - Bài viết là avatar hoặc ảnh bìa
168
+ - Tʰʔng tƔc group cƓng khai, video vƠ livestream
169
+ - BĆ i viįŗæt có link sai hoįŗ·c tag ngĘ°į»i dùng bị chįŗ·n
170
+ - CĆ”c UID bị lį»—i do khĆ“ng cĆ“ng khai hoįŗ·c chʰa bįŗ­t nĆŗt Like`;
171
+ }
172
+
173
+ // Default note for other platforms
174
+ return `Lʰu ý khi sį»­ dỄng dịch vỄ:
175
+
176
+ • Chỉ hį»— trợ link/dữ liệu cĆ“ng khai
177
+
178
+ • Đảm bįŗ£o link/dữ liệu hợp lệ vĆ  hoįŗ”t động
179
+
180
+ • Kiểm tra kỹ trước khi đặt đʔn số lượng lį»›n`;
181
+ }
182
+
183
+
184
+
185
+
186
+ function getServiceDisplayName(platform, serviceName) {
187
+ const servicesData = require('./services-data.json');
188
+ const platformData = servicesData[platform];
189
+ const serviceData = platformData && platformData[serviceName];
190
+
191
+ if (serviceData && serviceData.displayName) {
192
+ return serviceData.displayName;
193
+ }
194
+
195
+ // Fallback for services without display names
196
+ return serviceName.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
197
+ }
198
+
199
+
200
+ function getPlatformDisplayName(platform) {
201
+ const platformNames = {
202
+ 'facebook': 'Facebook',
203
+ 'instagram': 'Instagram',
204
+ 'tiktok': 'TikTok',
205
+ 'youtube': 'YouTube',
206
+ 'telegram': 'Telegram',
207
+ 'twitter': 'Twitter/X',
208
+ 'threads': 'Threads',
209
+ 'shopee': 'Shopee',
210
+ };
211
+ return platformNames[platform] || platform.toUpperCase();
212
+ }
213
+
214
+ // List of service keys that require UID (URL-to-ID conversion applies)
215
+ const UID_REQUIRED_SERVICES = [
216
+ 'like_speed',
217
+ 'reactions',
218
+ 'follow',
219
+ 'like_page',
220
+ 'like_comment',
221
+ 'comment',
222
+ 'share',
223
+ 'buff_group',
224
+ 'share_group',
225
+ 'review',
226
+ 'view_100k_reel'
227
+ ];
228
+
229
+ // Extract ID from various platform URLs - ONLY for services that need UID
230
+ function extractIdFromUrl(url, serviceKey) {
231
+ if (!url || typeof url !== 'string') {
232
+ return url; // Return as-is if not a valid URL
233
+ }
234
+
235
+ // Only apply URL-to-ID conversion for services that require UID
236
+ if (!UID_REQUIRED_SERVICES.includes(serviceKey)) {
237
+ return url; // Return original URL for services that need URL
238
+ }
239
+
240
+ // Facebook URLs
241
+ if (url.includes('facebook.com')) {
242
+ // Extract pfbid from posts
243
+ const pfbidMatch = url.match(/pfbid([a-zA-Z0-9]+)/);
244
+ if (pfbidMatch) return 'pfbid' + pfbidMatch[1];
245
+
246
+ // Extract post ID
247
+ const postMatch = url.match(/\/posts\/([a-zA-Z0-9]+)/);
248
+ if (postMatch) return postMatch[1];
249
+
250
+ // Extract video ID
251
+ const videoMatch = url.match(/\/videos\/([a-zA-Z0-9]+)/);
252
+ if (videoMatch) return videoMatch[1];
253
+
254
+ // Extract story ID
255
+ const storyMatch = url.match(/\/stories\/[^\/]+\/([a-zA-Z0-9]+)/);
256
+ if (storyMatch) return storyMatch[1];
257
+
258
+ // Extract from permalink
259
+ const permalinkMatch = url.match(/permalink\/([a-zA-Z0-9]+)/);
260
+ if (permalinkMatch) return permalinkMatch[1];
261
+
262
+ // Extract numeric ID from username/profile
263
+ const profileMatch = url.match(/facebook\.com\/([^\/\?]+)/);
264
+ if (profileMatch && !profileMatch[1].includes('pages')) {
265
+ return profileMatch[1];
266
+ }
267
+ }
268
+
269
+ // Instagram URLs
270
+ if (url.includes('instagram.com')) {
271
+ // Extract post ID from /p/ or /reel/
272
+ const postMatch = url.match(/instagram\.com\/(p|reel)\/([a-zA-Z0-9-_]+)/);
273
+ if (postMatch) return postMatch[2];
274
+
275
+ // Extract username from profile
276
+ const profileMatch = url.match(/instagram\.com\/([^\/\?]+)/);
277
+ if (profileMatch) return profileMatch[1];
278
+ }
279
+
280
+ // TikTok URLs
281
+ if (url.includes('tiktok.com')) {
282
+ // Extract video ID
283
+ const videoMatch = url.match(/\/video\/([a-zA-Z0-9]+)/);
284
+ if (videoMatch) return videoMatch[1];
285
+
286
+ // Extract username
287
+ const profileMatch = url.match(/tiktok\.com\/@([^\/\?]+)/);
288
+ if (profileMatch) return profileMatch[1];
289
+ }
290
+
291
+ // YouTube URLs
292
+ if (url.includes('youtube.com') || url.includes('youtu.be')) {
293
+ // Extract video ID from youtu.be
294
+ const shortMatch = url.match(/youtu\.be\/([a-zA-Z0-9_-]+)/);
295
+ if (shortMatch) return shortMatch[1];
296
+
297
+ // Extract video ID from youtube.com/watch
298
+ const watchMatch = url.match(/[?&]v=([a-zA-Z0-9_-]+)/);
299
+ if (watchMatch) return watchMatch[1];
300
+
301
+ // Extract channel ID or custom name
302
+ const channelMatch = url.match(/youtube\.com\/(?:channel\/|c\/|@)?([^\/\?]+)/);
303
+ if (channelMatch) return channelMatch[1];
304
+ }
305
+
306
+ // Twitter/X URLs
307
+ if (url.includes('twitter.com') || url.includes('x.com')) {
308
+ // Extract tweet ID
309
+ const tweetMatch = url.match(/\/status\/([a-zA-Z0-9]+)/);
310
+ if (tweetMatch) return tweetMatch[1];
311
+
312
+ // Extract username
313
+ const profileMatch = url.match(/(?:twitter|x)\.com\/([^\/\?]+)/);
314
+ if (profileMatch) return profileMatch[1];
315
+ }
316
+
317
+ // Threads URLs
318
+ if (url.includes('threads.net')) {
319
+ // Extract post ID
320
+ const postMatch = url.match(/\/post\/([a-zA-Z0-9]+)/);
321
+ if (postMatch) return postMatch[1];
322
+
323
+ // Extract username
324
+ const profileMatch = url.match(/threads\.net\/@([^\/\?]+)/);
325
+ if (profileMatch) return profileMatch[1];
326
+ }
327
+
328
+ // Telegram URLs
329
+ if (url.includes('t.me')) {
330
+ // Extract post ID
331
+ const postMatch = url.match(/t\.me\/[^\/]+\/([a-zA-Z0-9]+)/);
332
+ if (postMatch) return postMatch[1];
333
+
334
+ // Extract channel/group name
335
+ const channelMatch = url.match(/t\.me\/([^\/\?]+)/);
336
+ if (channelMatch) return channelMatch[1];
337
+ }
338
+
339
+ // Shopee URLs
340
+ if (url.includes('shopee.vn')) {
341
+ // Extract product ID
342
+ const productMatch = url.match(/product\/([a-zA-Z0-9]+)/);
343
+ if (productMatch) return productMatch[1];
344
+
345
+ // Extract shop ID
346
+ const shopMatch = url.match(/shopee\.vn\/([^\/\?]+)/);
347
+ if (shopMatch) return shopMatch[1];
348
+ }
349
+
350
+ // Return original URL if no pattern matches
351
+ return url;
352
+ }
353
+
354
+ // Generate auto note from n8n execution context
355
+ function generateAutoNote(serviceName, platform, inputData) {
356
+ const timestamp = new Date().toISOString();
357
+ const date = new Date().toLocaleDateString('vi-VN');
358
+ const time = new Date().toLocaleTimeString('vi-VN');
359
+
360
+ let note = `šŸ¤– Generated from n8n UME node\n`;
361
+ note += `šŸ“… ${date} ${time}\n`;
362
+ note += `🌐 Platform: ${platform}\n`;
363
+ note += `⚔ Service: ${serviceName}`;
364
+
365
+ // Add relevant info based on input data
366
+ if (inputData.uid) {
367
+ note += `\nšŸ“ Target: ${inputData.uid}`;
368
+ }
369
+ if (inputData.count) {
370
+ note += `\nšŸ”¢ Quantity: ${inputData.count}`;
371
+ }
372
+ if (inputData.server) {
373
+ note += `\nšŸ–„ļø Server: ${inputData.server}`;
374
+ }
375
+ if (inputData.reaction) {
376
+ note += `\nā¤ļø Reaction: ${inputData.reaction}`;
377
+ }
378
+
379
+ note += `\nšŸ†” Order ID: AUTO-${Date.now()}`;
380
+
381
+ return note;
382
+ }