Mô đun:Citron/Spam

local p = {}
local lang = mw.language.getContentLanguage()
-- Initialize
local 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)
end
for _, domain in ipairs(domains) do
	domainLookupdomain = true
end
-- Main
local function isAdded(hostname)
	return domainLookuphostname == true
end
local 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, " · ")
end
local 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 = revisionsrevisionId
		    hostnameInnerBottomNode
		    	:node(
		    		mw.html.create("div")
		    			:addClass("citron-spam-revision")
		    			:wikitext(
		    				""
			    				.. revision.user
			    				.. " ("
			    				.. 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 node
end
local 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("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(
		        					""
			        					.. 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 Tên miền bị chặn. "
							.. tostring(
								mw.html.create("span")
									:addClass("citron-spam-info")
									:wikitext("ⓘ")
							)
						)
				)
				: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)
end
local function createTemplatestylesNode()
return mw.getCurrentFrame():extensionTag{
		name = "templatestyles", args = { src = "Module:Citron/Spam/styles.css" }
	}
end
function p.report(frame)
local date = frame.args"date"
return createTemplatestylesNode() .. createReport(date)
end
function 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")
end
return p