Add new JavaScript files for various services including information-center.js, complaint-center.js, and council-business-report.js. Also, add corresponding JSON files for menu data and HTML debug pages for detailed views. Remove obsolete manual JSON file.

This commit is contained in:
MacM1 2026-01-13 18:24:28 +07:00
parent a6ff259798
commit 3e1abb0bec
161 changed files with 336029 additions and 0 deletions

202
complaint-center.js Normal file
View File

@ -0,0 +1,202 @@
// หน้าแรก > งานบริการ > ศูนย์รับเรื่องราวร้องทุกข์
const { execSync } = require("child_process");
const cheerio = require("cheerio");
const fs = require("fs");
const path = require("path");
const axios = require("axios").default;
const BASE = "https://ladsawai.go.th";
const OUT = path.join(process.cwd(), "ศูนย์รับเรื่องราวร้องทุกข์");
fs.mkdirSync(OUT, { recursive: true });
function curlHtml(url) {
return execSync(
`curl -L -s "${url}" -H "User-Agent: Mozilla/5.0" -H "Accept-Language: th-TH,th;q=0.9"`,
{ encoding: "utf8", maxBuffer: 30 * 1024 * 1024 }
);
}
function absUrl(href) {
if (!href) return null;
if (href.startsWith("http")) return href;
if (href.startsWith("/")) return BASE + href;
return BASE + "/" + href;
}
function buildUrl(menuId, page) {
return `${BASE}/public/list/data/index/menu/${menuId}/page/${page}`;
}
function detectTotalPages($) {
let maxPage = 1;
$("a").each((_, a) => {
const t = $(a).text().trim();
if (/^\d+$/.test(t)) maxPage = Math.max(maxPage, Number(t));
});
return maxPage;
}
function extractFileLinksFromDetail(detailUrl) {
const html = curlHtml(detailUrl);
const $ = cheerio.load(html);
const files = [];
$("a.uploadconfig_link").each((_, a) => {
const el = $(a);
const href = el.attr("href");
const dataHref = el.attr("data-href");
const fileUrl = absUrl(dataHref || href);
if (!fileUrl) return;
const text = el.text().replace(/\s+/g, " ").trim() || null
let title = text
let downloadCount = 0
if(text && text.includes('ดาวน์โหลดแล้ว')){
try {
const splitList = text.split(' ดาวน์โหลดแล้ว ')
title = splitList[0]
downloadCount = parseInt(splitList[1].replace('ครั้ง', '').trim())
} catch (error) {
title = text
downloadCount = 0
}
}
files.push({
text: title,
url: fileUrl,
downloadCount: downloadCount
});
});
// fallback: ลิงก์ไฟล์แบบตรง ๆ
$("a[href]").each((_, a) => {
const href = $(a).attr("href");
const u = absUrl(href);
if (!u) return;
if (/\.(pdf|doc|docx|xls|xlsx|ppt|pptx|zip|rar)(\?|$)/i.test(u)) {
if (!files.some((f) => f.url === u)) {
files.push({ text: $(a).text().trim() || null, url: u });
}
}
});
return files;
}
// ✅ ยิง api /status/1/ เพื่อเอา path จริง
async function resolveRealFilePath(fileUrl) {
try {
// กันกรณีมี / ท้ายอยู่แล้ว
const statusUrl = fileUrl.replace(/\/$/, "") + "/status/1/";
const res = await axios.get(statusUrl, { timeout: 30000 });
return res?.data?.path || null;
} catch (e) {
return null;
}
}
async function scrapeOnePage(menuId, page, saveHtml = false) {
const url = buildUrl(menuId, page);
const html = curlHtml(url);
if (saveHtml) {
fs.writeFileSync(
path.join(OUT, `debug-menu-${menuId}-page-${page}.html`),
html,
"utf8"
);
}
const $ = cheerio.load(html);
let items = []
try {
const files = extractFileLinksFromDetail(url);
for(let i = 0; i < files.length; i++){
const file = files[i]
let realPath = null;
let fileObject = {
fileName: file.text,
fileUrl: file.url, // ไฟล์จากหน้า detail
filePath: "", // ✅ ของจริงจาก api /status/1/
downloadCount: file.downloadCount
}
try {
const fileUrl = file?.url ? absUrl(file.url) : null;
if (fileUrl) {
realPath = await resolveRealFilePath(fileUrl);
fileObject.filePath = `https://ladsawai.go.th/public/${realPath}`
}
} catch (error) {
realPath = null;
}
items.push(fileObject)
}
} catch (e) {
}
const output = {
source: url,
scrapedAt: new Date().toISOString(),
menuId,
page,
count: items.length,
items,
};
fs.writeFileSync(
path.join(OUT, `menu-${menuId}-page-${page}.json`),
JSON.stringify(output, null, 2),
"utf8"
);
console.log(`✅ page ${page} -> items ${items.length}`);
return { $, items };
}
(async function main() {
const menuId = 1321;
const first = await scrapeOnePage(menuId, 1, true);
const totalPages = detectTotalPages(first.$);
console.log("✅ totalPages =", totalPages);
const all = [];
const seen = new Set();
function addItems(items) {
for (const it of items) {
const key = `${it.title}|${it.detailUrl || ""}|${it.filePath || ""}`;
if (seen.has(key)) continue;
seen.add(key);
all.push(it);
}
}
addItems(first.items);
for (let p = 2; p <= totalPages; p++) {
const { items } = await scrapeOnePage(menuId, p, false);
addItems(items);
}
const merged = {
menuId,
totalPages,
scrapedAt: new Date().toISOString(),
totalItems: all.length,
items: all,
};
const outAll = path.join(OUT, `menu-${menuId}-all.json`);
fs.writeFileSync(outAll, JSON.stringify(merged, null, 2), "utf8");
console.log("🎉 Saved all:", outAll);
console.log("🎉 Total unique:", all.length);
})();

251
council-business-report.js Normal file
View File

@ -0,0 +1,251 @@
// หน้าแรก > งานบริการ > รายงานกิจการสภา
const { execSync } = require("child_process");
const cheerio = require("cheerio");
const fs = require("fs");
const path = require("path");
const axios = require("axios").default;
const BASE = "https://ladsawai.go.th";
const OUT = path.join(process.cwd(), "รายงานกิจการสภา");
fs.mkdirSync(OUT, { recursive: true });
function curlHtml(url) {
return execSync(
`curl -L -s "${url}" -H "User-Agent: Mozilla/5.0" -H "Accept-Language: th-TH,th;q=0.9"`,
{ encoding: "utf8", maxBuffer: 30 * 1024 * 1024 }
);
}
function absUrl(href) {
if (!href) return null;
if (href.startsWith("http")) return href;
if (href.startsWith("/")) return BASE + href;
return BASE + "/" + href;
}
function buildUrl(menuId, page) {
return `${BASE}/public/list/data/index/menu/${menuId}/page/${page}`;
}
function detectTotalPages($) {
let maxPage = 1;
$("a").each((_, a) => {
const t = $(a).text().trim();
if (/^\d+$/.test(t)) maxPage = Math.max(maxPage, Number(t));
});
return maxPage;
}
function extractFileLinksFromDetail(detailUrl) {
const html = curlHtml(detailUrl);
const $ = cheerio.load(html);
const files = [];
$("a.uploadconfig_link").each((_, a) => {
const el = $(a);
const href = el.attr("href");
const dataHref = el.attr("data-href");
const fileUrl = absUrl(dataHref || href);
if (!fileUrl) return;
files.push({
text: el.text().replace(/\s+/g, " ").trim() || null,
url: fileUrl,
});
});
// fallback: ลิงก์ไฟล์แบบตรง ๆ
$("a[href]").each((_, a) => {
const href = $(a).attr("href");
const u = absUrl(href);
if (!u) return;
if (/\.(pdf|doc|docx|xls|xlsx|ppt|pptx|zip|rar)(\?|$)/i.test(u)) {
if (!files.some((f) => f.url === u)) {
files.push({ text: $(a).text().trim() || null, url: u });
}
}
});
return files;
}
// ✅ ยิง api /status/1/ เพื่อเอา path จริง
async function resolveRealFilePath(fileUrl) {
try {
// กันกรณีมี / ท้ายอยู่แล้ว
const statusUrl = fileUrl.replace(/\/$/, "") + "/status/1/";
const res = await axios.get(statusUrl, { timeout: 30000 });
return res?.data?.path || null;
} catch (e) {
return null;
}
}
// ✅ limit concurrency แบบง่าย (กันยิงหนักเกิน)
async function mapLimit(arr, limit, mapper) {
const ret = [];
let i = 0;
async function worker() {
while (i < arr.length) {
const idx = i++;
ret[idx] = await mapper(arr[idx], idx);
}
}
const workers = Array.from({ length: Math.min(limit, arr.length) }, worker);
await Promise.all(workers);
return ret;
}
async function scrapeOnePage(menuId, page, saveHtml = false) {
const url = buildUrl(menuId, page);
const html = curlHtml(url);
if (saveHtml) {
fs.writeFileSync(
path.join(OUT, `debug-menu-${menuId}-page-${page}.html`),
html,
"utf8"
);
}
const $ = cheerio.load(html);
// ✅ แปลง rows เป็น array ก่อน
const rows = $(".row.data-row").toArray();
// ✅ ประมวลผลแบบมี limit (เช่น 5 concurrent)
const items = (await mapLimit(rows, 5, async (row) => {
const el = $(row);
const a = el.find("a.listdataconfig_link[href]").first();
if (!a.length) return null;
const title =
a.find("label.font-weight").text().replace(/\s+/g, " ").trim() ||
a.text().replace(/\s+/g, " ").trim();
if (!title) return null;
const detailUrl = absUrl(a.attr("href"));
let files = [];
let realPath = null;
try {
if (detailUrl) files = extractFileLinksFromDetail(detailUrl);
const firstFileUrl = files?.[0]?.url ? absUrl(files[0].url) : null;
if (firstFileUrl) {
realPath = await resolveRealFilePath(firstFileUrl);
}
} catch (e) {
files = [];
realPath = null;
}
let detailHtmlPath = ''
if (saveHtml) {
try {
// Extract id and menu from URL: /public/list/data/detail/id/{id}/menu/{menu}/page/{page}
const urlMatch = detailUrl.match(/\/id\/(\d+)\/menu\/(\d+)/);
const id = urlMatch ? urlMatch[1] : null;
// const menu = urlMatch ? urlMatch[2] : null;
const detailPageHtml = curlHtml(detailUrl);
detailHtmlPath = `debug-menu-${menuId}-detail-${id}.html`
fs.writeFileSync(
path.join(OUT, detailHtmlPath),
detailPageHtml,
"utf8"
);
} catch (error) {
console.error('error :', error)
detailHtmlPath = ''
}
}
if(realPath == null){
return {
title,
detailUrl: detailUrl || null,
detailPageHtml: detailHtmlPath ?? undefined, // ไฟล์จากหน้า detail
sourcePage: page,
sourceUrl: url,
};
}
return {
title,
detailUrl: detailUrl || null,
fileUrl: files?.[0]?.url ? absUrl(files[0].url) : null, // ไฟล์จากหน้า detail
filePath: `https://ladsawai.go.th/public/` + realPath, // ✅ ของจริงจาก api /status/1/
detailPageHtml: detailHtmlPath ?? undefined, // ไฟล์จากหน้า detail
sourcePage: page,
sourceUrl: url,
};
}))
.filter(Boolean); // ตัด null ออก
const output = {
source: url,
scrapedAt: new Date().toISOString(),
menuId,
page,
count: items.length,
items,
};
fs.writeFileSync(
path.join(OUT, `menu-${menuId}-page-${page}.json`),
JSON.stringify(output, null, 2),
"utf8"
);
console.log(`✅ page ${page} -> items ${items.length}`);
return { $, items };
}
(async function main() {
const menuId = 1275;
const first = await scrapeOnePage(menuId, 1, true);
const totalPages = detectTotalPages(first.$);
console.log("✅ totalPages =", totalPages);
const all = [];
const seen = new Set();
function addItems(items) {
for (const it of items) {
const key = `${it.title}|${it.detailUrl || ""}|${it.filePath || ""}`;
if (seen.has(key)) continue;
seen.add(key);
all.push(it);
}
}
addItems(first.items);
for (let p = 2; p <= totalPages; p++) {
const { items } = await scrapeOnePage(menuId, p, true);
addItems(items);
}
const merged = {
menuId,
totalPages,
scrapedAt: new Date().toISOString(),
totalItems: all.length,
items: all,
};
const outAll = path.join(OUT, `menu-${menuId}-all.json`);
fs.writeFileSync(outAll, JSON.stringify(merged, null, 2), "utf8");
console.log("🎉 Saved all:", outAll);
console.log("🎉 Total unique:", all.length);
})();

202
information-center.js Normal file
View File

@ -0,0 +1,202 @@
// หน้าแรก > งานบริการ > ศูนย์ข้อมูลข่าวสาร
const { execSync } = require("child_process");
const cheerio = require("cheerio");
const fs = require("fs");
const path = require("path");
const axios = require("axios").default;
const BASE = "https://ladsawai.go.th";
const OUT = path.join(process.cwd(), "ศูนย์ข้อมูลข่าวสาร");
fs.mkdirSync(OUT, { recursive: true });
function curlHtml(url) {
return execSync(
`curl -L -s "${url}" -H "User-Agent: Mozilla/5.0" -H "Accept-Language: th-TH,th;q=0.9"`,
{ encoding: "utf8", maxBuffer: 30 * 1024 * 1024 }
);
}
function absUrl(href) {
if (!href) return null;
if (href.startsWith("http")) return href;
if (href.startsWith("/")) return BASE + href;
return BASE + "/" + href;
}
function buildUrl(menuId, page) {
return `${BASE}/public/list/data/index/menu/${menuId}/page/${page}`;
}
function detectTotalPages($) {
let maxPage = 1;
$("a").each((_, a) => {
const t = $(a).text().trim();
if (/^\d+$/.test(t)) maxPage = Math.max(maxPage, Number(t));
});
return maxPage;
}
function extractFileLinksFromDetail(detailUrl) {
const html = curlHtml(detailUrl);
const $ = cheerio.load(html);
const files = [];
$("a.uploadconfig_link").each((_, a) => {
const el = $(a);
const href = el.attr("href");
const dataHref = el.attr("data-href");
const fileUrl = absUrl(dataHref || href);
if (!fileUrl) return;
const text = el.text().replace(/\s+/g, " ").trim() || null
let title = text
let downloadCount = 0
if(text && text.includes('ดาวน์โหลดแล้ว')){
try {
const splitList = text.split(' ดาวน์โหลดแล้ว ')
title = splitList[0]
downloadCount = parseInt(splitList[1].replace('ครั้ง', '').trim())
} catch (error) {
title = text
downloadCount = 0
}
}
files.push({
text: title,
url: fileUrl,
downloadCount: downloadCount
});
});
// fallback: ลิงก์ไฟล์แบบตรง ๆ
$("a[href]").each((_, a) => {
const href = $(a).attr("href");
const u = absUrl(href);
if (!u) return;
if (/\.(pdf|doc|docx|xls|xlsx|ppt|pptx|zip|rar)(\?|$)/i.test(u)) {
if (!files.some((f) => f.url === u)) {
files.push({ text: $(a).text().trim() || null, url: u });
}
}
});
return files;
}
// ✅ ยิง api /status/1/ เพื่อเอา path จริง
async function resolveRealFilePath(fileUrl) {
try {
// กันกรณีมี / ท้ายอยู่แล้ว
const statusUrl = fileUrl.replace(/\/$/, "") + "/status/1/";
const res = await axios.get(statusUrl, { timeout: 30000 });
return res?.data?.path || null;
} catch (e) {
return null;
}
}
async function scrapeOnePage(menuId, page, saveHtml = false) {
const url = buildUrl(menuId, page);
const html = curlHtml(url);
if (saveHtml) {
fs.writeFileSync(
path.join(OUT, `debug-menu-${menuId}-page-${page}.html`),
html,
"utf8"
);
}
const $ = cheerio.load(html);
let items = []
try {
const files = extractFileLinksFromDetail(url);
for(let i = 0; i < files.length; i++){
const file = files[i]
let realPath = null;
let fileObject = {
fileName: file.text,
fileUrl: file.url, // ไฟล์จากหน้า detail
filePath: "", // ✅ ของจริงจาก api /status/1/
downloadCount: file.downloadCount
}
try {
const fileUrl = file?.url ? absUrl(file.url) : null;
if (fileUrl) {
realPath = await resolveRealFilePath(fileUrl);
fileObject.filePath = `https://ladsawai.go.th/public/${realPath}`
}
} catch (error) {
realPath = null;
}
items.push(fileObject)
}
} catch (e) {
}
const output = {
source: url,
scrapedAt: new Date().toISOString(),
menuId,
page,
count: items.length,
items,
};
fs.writeFileSync(
path.join(OUT, `menu-${menuId}-page-${page}.json`),
JSON.stringify(output, null, 2),
"utf8"
);
console.log(`✅ page ${page} -> items ${items.length}`);
return { $, items };
}
(async function main() {
const menuId = 1585;
const first = await scrapeOnePage(menuId, 1, true);
const totalPages = detectTotalPages(first.$);
console.log("✅ totalPages =", totalPages);
const all = [];
const seen = new Set();
function addItems(items) {
for (const it of items) {
const key = `${it.title}|${it.detailUrl || ""}|${it.filePath || ""}`;
if (seen.has(key)) continue;
seen.add(key);
all.push(it);
}
}
addItems(first.items);
for (let p = 2; p <= totalPages; p++) {
const { items } = await scrapeOnePage(menuId, p, false);
addItems(items);
}
const merged = {
menuId,
totalPages,
scrapedAt: new Date().toISOString(),
totalItems: all.length,
items: all,
};
const outAll = path.join(OUT, `menu-${menuId}-all.json`);
fs.writeFileSync(outAll, JSON.stringify(merged, null, 2), "utf8");
console.log("🎉 Saved all:", outAll);
console.log("🎉 Total unique:", all.length);
})();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More