/** * Dorabot – đổi địa chỉ cũ sang địa chỉ mới sau sáp nhập 2025 ngay trong Google Sheets. * Cài: Tiện ích mở rộng → Apps Script → dán toàn bộ đoạn này → Lưu → tải lại bảng tính. * Menu "Dorabot địa chỉ" → "Đặt khoá API" (lấy khoá miễn phí ở https://dorabot.io/dia-chi/nha-phat-trien/). * * Dùng: =DIACHIMOI(A2) → địa chỉ mới của 1 ô * =DIACHIMOI(A2:A200) → cả cột (tối đa 200 dòng mỗi công thức, gọi API 1 lần) * =DIACHIMOI(A2:A200; TRUE) → thêm cột "mức tin cậy" (cao / kiem_tra) và ghi chú * Kết quả được nhớ 6 giờ nên sửa bảng tính không làm tốn thêm lượt. */ var DORABOT_API = 'https://dorabot.io/dia-chi/api/v1/doi.php'; function onOpen() { SpreadsheetApp.getUi().createMenu('Dorabot địa chỉ') .addItem('Đặt khoá API', 'dorabotSetKey') .addItem('Hướng dẫn', 'dorabotHelp') .addToUi(); } function dorabotSetKey() { var ui = SpreadsheetApp.getUi(); var r = ui.prompt('Khoá API Dorabot', 'Dán khoá dạng dk_... (lấy ở dorabot.io/dia-chi/nha-phat-trien/)', ui.ButtonSet.OK_CANCEL); if (r.getSelectedButton() === ui.Button.OK) { PropertiesService.getDocumentProperties().setProperty('DORABOT_KEY', r.getResponseText().trim()); ui.alert('Đã lưu khoá cho bảng tính này.'); } } function dorabotHelp() { SpreadsheetApp.getUi().alert('Gõ =DIACHIMOI(A2) hoặc =DIACHIMOI(A2:A200; TRUE). Địa chỉ cũ cần đủ phường/xã, quận/huyện, tỉnh. ' + 'Không có khoá: dùng chung hạn mức thử rất nhỏ. Có khoá miễn phí: 100 địa chỉ/ngày. Gói trả phí: 500–1.000/ngày.'); } /** * Đổi địa chỉ cũ (trước 01/7/2025) sang địa chỉ mới sau sáp nhập. * @param {A2:A200} diaChi Ô hoặc vùng địa chỉ cũ. * @param {TRUE} chiTiet Thêm cột mức tin cậy và ghi chú. * @return Địa chỉ mới. * @customfunction */ function DIACHIMOI(diaChi, chiTiet) { var rows = Array.isArray(diaChi) ? diaChi.map(function (r) { return String(r[0] || '').trim(); }) : [String(diaChi || '').trim()]; if (rows.length > 200) throw new Error('Tối đa 200 dòng mỗi công thức. Chia thành nhiều công thức.'); var cache = CacheService.getDocumentCache(); var out = new Array(rows.length), need = [], needIdx = []; rows.forEach(function (a, i) { if (!a) { out[i] = null; return; } var c = cache.get('dk1:' + Utilities.base64Encode(Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, a, Utilities.Charset.UTF_8))); if (c) out[i] = JSON.parse(c); else { need.push(a); needIdx.push(i); } }); if (need.length) { var key = PropertiesService.getDocumentProperties().getProperty('DORABOT_KEY') || ''; var opt = { method: 'post', contentType: 'application/json', muteHttpExceptions: true, payload: JSON.stringify({ addresses: need }), headers: {} }; if (key) opt.headers.Authorization = 'Bearer ' + key; var res = UrlFetchApp.fetch(DORABOT_API, opt); var j = JSON.parse(res.getContentText()); if (!j.thanh_cong) throw new Error(j.loi || 'Lỗi gọi Dorabot'); j.ket_qua.forEach(function (r, k) { var v = r.thanh_cong ? [r.dia_chi_moi, (r.tin_cay && r.tin_cay.muc) || '', (r.tin_cay && r.tin_cay.ghi_chu) || ''] : ['', 'loi', r.mo_ta || r.loi || '']; out[needIdx[k]] = v; try { cache.put('dk1:' + Utilities.base64Encode(Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, need[k], Utilities.Charset.UTF_8)), JSON.stringify(v), 21600); } catch (e) {} }); } return out.map(function (v) { return v ? (chiTiet ? v : [v[0] || ('Lỗi: ' + v[2])]) : (chiTiet ? ['', '', ''] : ['']); }); }