Mô đun:Citron/Spam
Giao diện
local p = {}local lang = mw.language.getContentLanguage()-- Initializelocal title = mw.title.new('MediaWiki:BlockedExternalDomains.json')local content = title:getContent()local data = mw.text.jsonDecode(content)local domains = {}local domainLookup = {}for _, entry in ipairs(data) do table.insert(domains, entry.domain)endfor _, domain in ipairs(domains) do domainLookup[domain] = trueend-- Mainlocal function isAdded(hostname) return domainLookup[hostname] == trueendlocal function statsText(hostnames, feedbacks) local stats = { good = 0, bad = 0, added = 0 } for _, hostname in ipairs(hostnames) do for _, feedback in ipairs(feedbacks) do if feedback.hostname == hostname.hostname then if feedback.status == 0 then stats.good = stats.good + 1 else stats.bad = stats.bad + 1 end end end if isAdded(hostname.hostname) then stats.added = stats.added + 1 end end local texts = {} for key, value in pairs(stats) do if value ~= 0 then table.insert(texts, tostring( mw.html.create("span") :addClass("citron-spam-" .. key .. "-count") :wikitext(value) )) end end return table.concat(texts, " · ")endlocal function createHostnameListNode(title, node, hostnames, revisions, reportDate, id, feedbacks) if #hostnames == 0 then return nil end node :node( mw.html.create("div") :addClass("citron-spam-content-title-placeholder mw-collapsible" .. (id:find("uncertain") and " mw-collapsed" or "")) :css("position", "absolute") :attr("id", "mw-customcollapsible-" .. id) ) :node( mw.html.create("div") :addClass("citron-spam-content-title-wrapper mw-customtoggle-" .. id) :node( mw.html.create("div") :addClass("citron-spam-content-title") :wikitext(title .. " (" .. #hostnames .. ")") ) :node( mw.html.create("div") :addClass("citron-spam-content-feedback-stats") :wikitext(statsText(hostnames, feedbacks)) ) ) for _, hostname in ipairs(hostnames) do -- .citron-spam-hostname-inner-bottom local hostnameInnerBottomNode = mw.html.create("div") :addClass("citron-spam-hostname-inner-bottom mw-collapsible mw-collapsed") :attr("id", "mw-customcollapsible-citron-" .. reportDate) for _, revisionId in ipairs(hostname.revisionIds) do local revision = revisions[revisionId] hostnameInnerBottomNode :node( mw.html.create("div") :addClass("citron-spam-revision") :wikitext( "[[Special:Contributions/" .. revision.user .. "|" .. revision.user .. "]] ([[Special:Diff/" .. revisionId .. "|" .. revision.page .. "]])" ) ) end -- .citron-spam-feedbacks local feedbacksNode = mw.html.create("div") :addClass("citron-spam-feedbacks") local feedbacksCount = 0 for _, feedback in ipairs(feedbacks) do if feedback.hostname == hostname.hostname then feedbacksCount = feedbacksCount + 1 feedbacksNode:node( mw.html.create("div") :addClass("citron-spam-feedback " .. (feedback.status == 0 and "citron-spam-feedback-good" or "citron-spam-feedback-bad")) :attr("title", feedback.createdAt) :node(mw.html.create("span") :addClass("citron-spam-feedback-icon")) :node(mw.html.create("span") :addClass("citron-spam-feedback-user") :wikitext(feedback.user)) ) end end local statusClass, bgHostnameInnerTopLeftStart, bgHostnameInnerTopLeftEnd if hostname.score >= 0.8 then statusClass = "citron-spam-very-bad" bgHostnameInnerTopLeftStart = "var(--background-color-destructive-subtle--hover, #ffdad3)" bgHostnameInnerTopLeftEnd = "var(--background-color-destructive-subtle, #ffe9e5)" elseif hostname.score >= 0.5 then statusClass = "citron-spam-bad" bgHostnameInnerTopLeftStart = "light-dark(#ffdcb8, #572c19)" bgHostnameInnerTopLeftEnd = "light-dark(#ffead4, #361d13)" else statusClass = "citron-spam-uncertain" bgHostnameInnerTopLeftStart = "var(--background-color-progressive-subtle--hover, #dce3f9)" bgHostnameInnerTopLeftEnd = "var(--background-color-progressive-subtle, #f1f4fd)" end local added = isAdded(hostname.hostname) local addedClass = added and "citron-spam-added" or "citron-spam-not-added" local addedText = added and "Đã thêm" or "Chưa thêm" local score = string.format("%.0f%%", hostname.score * 100) node :node( mw.html.create("div") :addClass("citron-spam-hostname mw-collapsible" .. (id:find("uncertain") and " mw-collapsed" or "")) :attr("id", "mw-customcollapsible-" .. id) :node( mw.html.create("div") :addClass("citron-spam-hostname-time") :wikitext(hostname.time) ) :node( mw.html.create("div") :addClass("citron-spam-hostname-inner") :attr("data-added", tostring(added)) :node( mw.html.create("div") :addClass("citron-spam-hostname-inner-top") :css("--percentage", score) :css("--background-hostname-inner-top-left-start", bgHostnameInnerTopLeftStart) :css("--background-hostname-inner-top-left-end", bgHostnameInnerTopLeftEnd) :css("--background-hostname-inner-top-left", "linear-gradient(to right, var(--background-hostname-inner-top-left-start) var(--percentage), var(--background-hostname-inner-top-left-end) var(--percentage))") :node( mw.html.create("div") :addClass("citron-spam-hostname-inner-top-left " .. statusClass) :node( mw.html.create("div") :addClass("citron-spam-hostname-hostname") :wikitext(hostname.hostname) ) :node( mw.html.create("div") :addClass("citron-spam-hostname-score") :wikitext(score) ) ) :node( mw.html.create("div") :addClass("citron-spam-hostname-inner-top-right") :node(feedbacksCount ~= 0 and feedbacksNode or nil) :node( mw.html.create("div") :addClass("citron-spam-hostname-status " .. addedClass) :wikitext(addedText) ) ) ) :node(hostnameInnerBottomNode) ) ) end return nodeendlocal function createReport(date) local today = os.date("%Y-%m-%d") == date; local title = mw.title.new("Project:Citron/Spam/" .. date .. ".json") local content = title:getContent() local wrapperNode = mw.html.create("div") :addClass("citron-spam-wrapper") :node( mw.html.create("div") :addClass("citron-spam-header") :node( mw.html.create("div") :addClass("citron-spam-title") :wikitext("[[Project:Citron/Spam|Citron · Spam]]") ) :node( mw.html.create("div") :addClass("citron-spam-actions") :node( mw.html.create("span") :addClass("mw-customtoggle-citron-" .. date) :wikitext("Xem phiên bản") ) :node( today and mw.html.create("span") :addClass("citron-spam-support") :attr("data-date", date) :wikitext("Hỗ trợ đánh giá") or nil ) :node( mw.html.create("span") :addClass("citron-spam-date") :wikitext( "[[Project:Citron/Spam/" .. date .. ".json|" .. date .. "]]" ) ) ) ) if content == nil then wrapperNode:node( mw.html.create("div") :addClass("citron-spam-empty") :wikitext("Không có báo cáo tại thời điểm được cung cấp") ) return tostring(wrapperNode) end local data = mw.text.jsonDecode(content) local very_bad_hostnames = {} local bad_hostnames = {} local uncertain_hostnames = {} for _, hostname in ipairs(data.hostnames) do if hostname.score >= 0.8 then table.insert(very_bad_hostnames, hostname) elseif hostname.score >= 0.5 then table.insert(bad_hostnames, hostname) else table.insert(uncertain_hostnames, hostname) end end local contentVeryBadNode = createHostnameListNode( "Có khả năng rất cao là xấu", mw.html.create("div"):addClass("citron-spam-content"), very_bad_hostnames, data.revisions, date, "citron-" .. date .. "-very-bad", data.feedbacks ) local contentBadNode = createHostnameListNode( "Có khả năng cao là xấu", mw.html.create("div"):addClass("citron-spam-content"), bad_hostnames, data.revisions, date, "citron-" .. date .. "-bad", data.feedbacks ) local contentUncertainNode = createHostnameListNode( "Chưa chắc chắn", mw.html.create("div"):addClass("citron-spam-content"), uncertain_hostnames, data.revisions, date, "citron-" .. date .. "-uncertain", data.feedbacks ) if #data.hostnames > 0 then wrapperNode:node(contentVeryBadNode) wrapperNode:node(contentBadNode) wrapperNode:node(contentUncertainNode) else wrapperNode:node( mw.html.create("div") :addClass("citron-spam-empty") :wikitext("Chưa có dữ liệu") ) end wrapperNode :node( mw.html.create("div") :addClass("citron-spam-footer") :node( mw.html.create("div") :addClass("citron-spam-last-update") :wikitext( "Cập nhật lúc " .. lang:formatDate("H:i, j F, Y", data.updatedAt) .. " (UTC). Truy cập [[Special:BlockedExternalDomains|Tên miền bị chặn]]. " .. tostring( mw.html.create("span") :addClass("citron-spam-info") :wikitext("[[:vi:User:Plantaest/Citron|ⓘ]]") ) ) ) :node( mw.html.create("div") :addClass("citron-spam-status") :attr("data-today", tostring(today)) :wikitext(today and "Đang cập nhật" or "Kết thúc cập nhật") ) ) return tostring(wrapperNode)endlocal function createTemplatestylesNode() return mw.getCurrentFrame():extensionTag{ name = "templatestyles", args = { src = "Module:Citron/Spam/styles.css" } }endfunction p.report(frame) local date = frame.args["date"] return createTemplatestylesNode() .. createReport(date)endfunction p.main(frame) local dates = {} local reports = {} for i = 0, 6 do table.insert( dates, lang:formatDate("Y-m-d", "-" .. i .. " days") ) end for _, date in ipairs(dates) do table.insert(reports, createReport(date)) end return createTemplatestylesNode() .. table.concat(reports, "\n")endreturn p