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.
- package/LICENSE +21 -0
- package/README.md +204 -0
- package/dist/credentials/UmeApi.credentials.d.ts +7 -0
- package/dist/credentials/UmeApi.credentials.js +41 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +9 -0
- package/dist/nodes/FacebookComment/FacebookComment.node.d.ts +5 -0
- package/dist/nodes/FacebookComment/FacebookComment.node.js +155 -0
- package/dist/nodes/FacebookCommentLike/FacebookCommentLike.node.d.ts +5 -0
- package/dist/nodes/FacebookCommentLike/FacebookCommentLike.node.js +122 -0
- package/dist/nodes/FacebookLike/FacebookLike.node.d.ts +5 -0
- package/dist/nodes/FacebookLike/FacebookLike.node.js +160 -0
- package/dist/nodes/FacebookShare/FacebookShare.node.d.ts +5 -0
- package/dist/nodes/FacebookShare/FacebookShare.node.js +141 -0
- package/dist/nodes/FacebookStoryView/FacebookStoryView.node.d.ts +5 -0
- package/dist/nodes/FacebookStoryView/FacebookStoryView.node.js +122 -0
- package/dist/nodes/FacebookView/FacebookView.node.d.ts +5 -0
- package/dist/nodes/FacebookView/FacebookView.node.js +127 -0
- package/dist/nodes/InstagramComment/InstagramComment.node.d.ts +5 -0
- package/dist/nodes/InstagramComment/InstagramComment.node.js +145 -0
- package/dist/nodes/InstagramLike/InstagramLike.node.d.ts +5 -0
- package/dist/nodes/InstagramLike/InstagramLike.node.js +138 -0
- package/dist/nodes/InstagramView/InstagramView.node.d.ts +5 -0
- package/dist/nodes/InstagramView/InstagramView.node.js +151 -0
- package/dist/nodes/TikTokComment/TikTokComment.node.d.ts +5 -0
- package/dist/nodes/TikTokComment/TikTokComment.node.js +136 -0
- package/dist/nodes/TikTokCommentLike/TikTokCommentLike.node.d.ts +5 -0
- package/dist/nodes/TikTokCommentLike/TikTokCommentLike.node.js +122 -0
- package/dist/nodes/TikTokFavorite/TikTokFavorite.node.d.ts +5 -0
- package/dist/nodes/TikTokFavorite/TikTokFavorite.node.js +127 -0
- package/dist/nodes/TikTokLike/TikTokLike.node.d.ts +5 -0
- package/dist/nodes/TikTokLike/TikTokLike.node.js +141 -0
- package/dist/nodes/TikTokShare/TikTokShare.node.d.ts +5 -0
- package/dist/nodes/TikTokShare/TikTokShare.node.js +131 -0
- package/dist/nodes/TikTokView/TikTokView.node.d.ts +5 -0
- package/dist/nodes/TikTokView/TikTokView.node.js +136 -0
- package/dist/nodes/Ume/Ume.node.d.ts +5 -0
- package/dist/nodes/Ume/Ume.node.js +244 -0
- package/dist/nodes/Ume/operations-data.json +2246 -0
- package/dist/nodes/UmeSocialSeeding/UmeSocialSeeding.node.d.ts +5 -0
- package/dist/nodes/UmeSocialSeeding/UmeSocialSeeding.node.js +203 -0
- package/dist/nodes/UmeSocialSeeding/UmeSocialSeedingHelpers.d.ts +4 -0
- package/dist/nodes/UmeSocialSeeding/UmeSocialSeedingHelpers.js +382 -0
- package/dist/nodes/UmeSocialSeeding/operations-data.json +673 -0
- package/dist/nodes/UmeSocialSeeding/reaction.png +0 -0
- package/dist/nodes/UmeSocialSeeding/server-info.json +431 -0
- package/dist/nodes/UmeSocialSeeding/services-data-backup.json +3525 -0
- package/dist/nodes/UmeSocialSeeding/services-data-old.json +2704 -0
- package/dist/nodes/UmeSocialSeeding/services-data.json +7341 -0
- package/dist/nodes/UmeSocialSeeding/services-data.json.backup +3525 -0
- package/dist/nodes/UmeSocialSeeding/services-data.json.backup.1759909364606 +1927 -0
- package/dist/nodes/YouTubeComment/YouTubeComment.node.d.ts +5 -0
- package/dist/nodes/YouTubeComment/YouTubeComment.node.js +152 -0
- package/dist/nodes/YouTubeLike/YouTubeLike.node.d.ts +5 -0
- package/dist/nodes/YouTubeLike/YouTubeLike.node.js +141 -0
- package/dist/nodes/YouTubeView/YouTubeView.node.d.ts +5 -0
- package/dist/nodes/YouTubeView/YouTubeView.node.js +142 -0
- 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
|
+
}
|