-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhostname_root_extractor.js
More file actions
198 lines (163 loc) · 6.21 KB
/
hostname_root_extractor.js
File metadata and controls
198 lines (163 loc) · 6.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
function onOpen() {
SpreadsheetApp.getUi().createMenu('Hostname/Root Extractor')
.addItem('Витягнути хостнейм', 'extractHostnames')
.addItem('Витягнути рут-домен', 'extractRootDomains')
.addToUi();
}
function extractHostnames() {
const { urlCol, resultCol, sheet, ui } = getColumnLetters();
if (!urlCol) return;
processURLs({
sheet,
urlCol,
resultCol,
transformFn: getHostname,
message: '✅ Готово! Hostname витягнено.'
});
}
function extractRootDomains() {
const ui = SpreadsheetApp.getUi();
// Перевірка на оновлення PSL
const lastUpdate = PropertiesService.getScriptProperties().getProperty('PSL_LAST_UPDATE');
const lastUpdateStr = lastUpdate
? Utilities.formatDate(new Date(lastUpdate), Session.getScriptTimeZone(), 'yyyy-MM-dd HH:mm:ss')
: 'не оновлювалась';
const response = ui.alert(
`📅 Дата останнього оновлення Public Suffix List: ${lastUpdateStr}\n\n` +
'🔄 Оновити список перед обробкою?\n\n' +
'----------------------------------------------------------\n' +
'⚠️ РЕКОМЕНДУЄМО ОНОВЛЮВАТИ PSL КОЖНІ 3 МІСЯЦІ!\n' +
'----------------------------------------------------------',
ui.ButtonSet.YES_NO
);
if (response === ui.Button.YES) {
updatePSL();
}
let pslRaw = PropertiesService.getScriptProperties().getProperty('PSL');
if (!pslRaw) {
ui.alert('Public Suffix List не знайдено. Зараз буде оновлено.');
updatePSL();
pslRaw = PropertiesService.getScriptProperties().getProperty('PSL');
if (!pslRaw) {
ui.alert('Не вдалося отримати Public Suffix List. Припинення операції.');
return;
}
}
const { urlCol, resultCol, sheet } = getColumnLetters();
if (!urlCol) return;
const pslList = parsePSL(pslRaw);
processURLs({
sheet,
urlCol,
resultCol,
transformFn: (url) => getRootDomainFromURL(url, pslList),
message: '✅ Готово! Рут-домен витягнено.'
});
}
// ========================
// 🔧 Утиліти
// ========================
function getColumnLetters() {
const ui = SpreadsheetApp.getUi();
const urlColResp = ui.prompt('Введіть ЛІТЕРУ(ENG) колонки з URL (наприклад A):');
const resultColResp = ui.prompt('Введіть ЛІТЕРУ(ENG) колонки для результату (наприклад B):');
const urlColLetter = urlColResp.getResponseText().toUpperCase();
const resultColLetter = resultColResp.getResponseText().toUpperCase();
if (!urlColLetter.match(/^[A-Z]+$/) || !resultColLetter.match(/^[A-Z]+$/)) {
ui.alert('Некоректна літера колонки.');
return {};
}
if (urlColLetter === resultColLetter) {
const overwriteResponse = ui.alert(
`Ви вказали одну й ту саму літеру для вхідних і вихідних даних: "${urlColLetter}".\n\n` +
'Це призведе до перезапису початкових URL-адрес. Ви впевнені, що хочете продовжити?',
ui.ButtonSet.YES_NO
);
if (overwriteResponse === ui.Button.NO) return {};
}
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const urlCol = columnLetterToIndex(urlColLetter);
const resultCol = columnLetterToIndex(resultColLetter);
return { urlCol, resultCol, sheet, ui };
}
function processURLs({ sheet, urlCol, resultCol, transformFn, message }) {
const ui = SpreadsheetApp.getUi();
const colValues = sheet.getRange(2, urlCol, sheet.getMaxRows() - 1).getValues();
const nonEmptyValues = colValues.filter(row => row[0] !== '');
if (nonEmptyValues.length === 0) {
ui.alert('⚠️error⚠️ Немає даних для обробки в обраній колонці.');
return;
}
const urlValues = colValues.slice(0, nonEmptyValues.length);
for (let i = 0; i < urlValues.length; i++) {
const url = urlValues[i][0];
const result = url ? transformFn(url.trim()) : '';
sheet.getRange(i + 2, resultCol).setValue(result);
}
ui.alert(message);
}
function updatePSL() {
const ui = SpreadsheetApp.getUi();
const url = 'https://publicsuffix.org/list/public_suffix_list.dat';
try {
const response = UrlFetchApp.fetch(url);
const content = response.getContentText();
PropertiesService.getScriptProperties().setProperty('PSL', content);
PropertiesService.getScriptProperties().setProperty('PSL_LAST_UPDATE', new Date().toISOString());
ui.alert('Public Suffix List успішно оновлено!');
} catch (e) {
ui.alert('Помилка оновлення PSL: ' + e.message);
}
}
function columnLetterToIndex(letter) {
let column = 0;
for (let i = 0; i < letter.length; i++) {
column *= 26;
column += letter.charCodeAt(i) - 64;
}
return column;
}
function parsePSL(pslRaw) {
return pslRaw
.split('\n')
.map(line => line.trim())
.filter(line => line && !line.startsWith('//') && !line.startsWith('!'));
}
function getRootDomainFromURL(url, pslList) {
try {
if (!/^https?:\/\//i.test(url)) {
url = 'http://' + url;
}
const hostnameMatch = url.match(/^https?:\/\/([^\/?#]+)(?:[\/?#]|$)/i);
if (!hostnameMatch) return 'Invalid URL';
const hostname = hostnameMatch[1].toLowerCase();
const parts = hostname.split('.');
for (let i = 0; i < parts.length; i++) {
const candidate = parts.slice(i).join('.');
if (pslList.includes(candidate)) {
if (i === 0) return hostname;
return parts.slice(i - 1).join('.');
}
}
if (parts.length >= 2) return parts.slice(-2).join('.');
return hostname;
} catch (e) {
return 'Invalid URL';
}
}
function getHostname(url) {
try {
if (!/^https?:\/\//i.test(url)) {
url = 'http://' + url;
}
const match = url.match(/^https?:\/\/([^\/?#]+)(?:[\/?#]|$)/i);
if (!match) return 'Invalid URL';
let hostname = match[1].toLowerCase();
if (hostname.startsWith('www.')) {
hostname = hostname.substring(4);
}
return hostname;
} catch {
return 'Invalid URL';
}
}