跳转到内容

模組:Talkpage/sandbox

本页使用了标题或全文手工转换
维基百科,自由的百科全书
local f = {}

f.DATEPATTERN = '(%d-)年(%d-)月(%d-)日 %([一二三四五六日]-%) (%d-):(%d-) %(UTC%)'
f.USERPREFIX = {
    'user:', '用戶:', '用户:', '使用者:', 'u:', 'U:',
    'user talk:', 'user_talk:', '用戶討論:', '用户讨论:', '使用者討論:', 'ut:','UT:','Ut:',
    'special:用户贡献/', 'special:用戶貢獻/', 'special:使用者貢獻/', '特殊:用户贡献/', '特殊:用戶貢獻/', '特殊:使用者貢獻/', 
    'special:contributions/', '特殊:contributions/', 'special:contribs/', '特殊:contribs/' 
}
f.MINDATE = '0000年1月1日 (四) 00:00 (UTC)'
f.MAXDATE = '9999年12月31日 (五) 23:59 (UTC)'
-- 特殊處理頁面
f.PAGE_LEVEL_MAP = {
    ['Wikipedia:管理员布告板/编辑争议'] = '3',
    ['Wikipedia:当前的破坏'] = '3',
    ['Wikipedia:新闻动态候选'] = '3',
    ['Wikipedia:申请解除权限'] = '3',
    ['Wikipedia:请求保护页面'] = '3',
    ['Wikipedia:傀儡調查/案件/'] = '3',
	['Wikipedia:仲裁/請求/'] = '3',
	['Wikipedia:机器人/申请'] = '3',
	['Wikipedia:机器用户/申请'] = '3',
	['Wikipedia:頁面存廢討論/疑似侵權'] = '3',
	['Wikipedia:合并请求'] = '3',
	['Wikipedia:利益衝突/佈告板'] = '3',
	['Wikipedia:更改用户名'] = '3',
	['Wikipedia:防滥用过滤器/错误报告'] = '3',
	['Wikipedia:字词转换/地区词候选'] = '3',
	['Wikipedia:字词转换/修复请求'] = '3',
	
	['Wikipedia:權限申請/'] = '4',
    ['Wikipedia:新条目推荐/候选'] = '4',

	['Wikipedia:申请成为管理员/'] = '6',
	['Wikipedia:申请成为界面管理员/'] = '6',
	['Wikipedia:申请成为行政员/'] = '6',
	['Wikipedia:申请成为监督员/'] = '6',
}

-- TODO: Wikipedia:修订版本删除请求 依照{{tl|Revdel}}分割

--- 反序尋找字符串
local function rfind(s, pattern, init)
    local x, y
    local i = init or #s
    local len = #s
    x, y = string.find(string.reverse(s), string.reverse(pattern), len-i+1, true)
    if x then
        return len-y+1, len-x+1
    end
end

--- 生成用戶名鏈接。默認是指向用戶頁(IP用戶為貢獻頁)的鏈接。
-- @param name 用戶名
-- @param style 自定義樣式,其中$USER會被換成用戶名。
-- @param ipstyle IP用戶的自定義樣式。若未指定則與style相同。
-- @return 維基格式的用戶名鏈接
local function getuserlink(name, style, ipstyle)
	if name == '?' or name == nil then
		return '?'
	elseif string.find(name, '^%d-%.%d-%.%d-%.%d-$') or string.find(name, '^%~%d-%-%d-%-%d-$') then
        return string.gsub(ipstyle or style or '[[Special:用户贡献/$USER|$USER]]', '$USER', name)
    else
        return string.gsub(style or '[[User:$USER|$USER]]', '$USER', name)
    end
end

--- 去除掉影響分析的文字,例如「===」、「<pre>」等
-- @param orintext 原始討論串。
-- @return 經過預處理之後的討論串。去掉一些搗亂的內容。
local function preprocess(orintext, titlelevel)
    local text = orintext or ''
    text = string.gsub(text, titlelevel .. '=', '---')
	text = string.gsub(text, '\r', '\n')
	text = string.gsub(text, '%[%[%s*', '[[')
	text = string.gsub(text, '%]%]%s*', ']]')
	text = string.gsub(text, '&#126;', '~') -- 匿名用户
    text = string.gsub(text, '<pre>.-</pre>', '')
    text = string.gsub(text, '<nowiki>.-</nowiki>', '')
	text = string.gsub(text, '{{%s*移動至%s*|.-}}', '')
	text = string.gsub(text, '{{%s*移動自%s*|.-}}', '')
	text = string.gsub(text, '{{%s*移动至%s*|.-}}', '')
	text = string.gsub(text, '{{%s*移动自%s*|.-}}', '')
    text = string.gsub(text, '{{%s*Talkback%s*|.-}}', '')
    text = string.gsub(text, '{{%s*Deltalk%s*|.-}}', '')
    text = string.gsub(text, '{{%s*Nocount%s*|.-}}', '')
    text = string.gsub(text, '{{%s*Reaction%s*|.-}}', '')
    text = string.gsub(text, '{{%s*talkback%s*|.-}}', '')
    text = string.gsub(text, '{{%s*deltalk%s*|.-}}', '')
    text = string.gsub(text, '{{%s*nocount%s*|.-}}', '')
    text = string.gsub(text, '{{%s*reaction%s*|.-}}', '')
    return text
end

--- 從長長的討論串中找到特定主題。如果未指定主題則將所有主題分割開並存入table中。
-- @param orintext 原始討論串。
-- @param topic 待提取的主題。未指定的話則改為分割所有主題。
-- @param ispattern 表示topic是否為lua的匹配字符串。
-- @return 返回一個鍵為討論標題值為討論內容的table。如果沒有則返回nil。
local function extracttopic(args)
    local orintext = args.text
    local topic = args.topic
    local ispattern = args.ispattern
    local titlelevel = ""
    for i=1, tonumber(args.titlelevel or 2) do titlelevel = titlelevel .. "=" end
	local pageName = args.pageName

	-- DYKC特殊處理
	if pageName == 'Wikipedia:新条目推荐/候选' then
        orintext = string.gsub(orintext, '({{[ ]*[Dd][Yy][Kk][Ee]ntry[^{}]-|[ ]*article[ ]*=[ ]*([^|}]-)[ ]*([|}]))', function(full, article, suffix)
            return '<!--DYK_TITLE:' .. string.gsub(article, '_', ' ') .. '-->{{DYKEntry|article='..article..suffix
        end)
    end
	
    -- 預處理
    local text = titlelevel .. '\nx\n' .. preprocess(orintext, titlelevel) .. '\n' .. titlelevel

    local result = {}
    local ids = {}
    local id = 0
    local found = nil
    local topic2 = topic and mw.text.trim(topic)
    local pattern = '%s*(.-)%s*'..titlelevel..'%s*\n(.-)\n'..titlelevel
    for title, content in string.gmatch(text, pattern) do
        local ok = false
        if topic2 and (not ispattern) and title == topic2 then
            ok = true
        elseif topic2 and ispattern and (string.match(title, topic2)) then
            ok = true
        elseif not topic2 then
            ok = true
        end
		
		-- DYKC特殊處理
		if pageName == 'Wikipedia:新条目推荐/候选' then
			ok = false
			if mw.text.trim(title) == "" then
				local alt = string.match(content, '<!%-%-DYK_TITLE:(.-)%-%->')
	            if alt then
					title = alt
					ok = true
				end
			end
        end

        if ok then
            found = true
            id = id + 1

            local temptitle = title
            local i = 1
            temptitle = string.gsub(temptitle,'^[ ]*=[ ]*', '')
            temptitle = string.gsub(temptitle,'[ ]*=[ ]*$', '') 
			temptitle = string.gsub(temptitle,'\n', '<br>') 
			temptitle = string.gsub(temptitle,'<%s*.->', '')
			temptitle = mw.text.decode(temptitle)

			-- 移除链接、模板
			temptitle = string.gsub(temptitle, '%[%[[^%]]-|([^%]]-)%]%]', '%1')
			temptitle = string.gsub(temptitle, '%[%[:?([^%]]-)%]%]', '%1')
			temptitle = string.gsub(temptitle, '%[[^%s%]]+[ ]+([^%]]-)%]', '%1')
			temptitle = string.gsub(temptitle, '%[([^%]]-)%]', '%1')
			temptitle = string.gsub(temptitle, '{', '&#123;')
			temptitle = string.gsub(temptitle, '}', '&#125;')

            while result[temptitle] do
                i = i + 1
                temptitle = title .. ' (#' .. i .. ')'
            end
            result[temptitle] = content
            ids[id] = temptitle 
        end
    end

    return found and { data = result, ids = ids }
end

--- 將討論串上的日期轉為一個數字。
-- @param date 討論串格式的日期。
-- @return 將數字直接拼起來。不是時間戳。
local function date2num(date)
    local y,m,d,h,min = string.gmatch(date, f.DATEPATTERN)()
    if y and m and d and h and min then
        return y*100000000+m*1000000+d*10000+h*100+min
    else
        return 0
    end
end

--- 比較兩個日期的大小。
-- @param date1
-- @param date2
-- @return 若date1<date2則返回true。
local function comparedate(date1, date2)
    return date2num(date1) < date2num(date2)
end

--- 統計討論串中各個簽名的次數和最後發言時間。
-- @param orintext 原始討論串
-- @return
function f._count(orintext)
    -- 預處理
    local text = '\n' .. orintext
    local lowertext = string.lower(text)

    -- 尋找日期
    local result = {}
    local x, y
    y = 1
    repeat
        x, y = text.find(text, f.DATEPATTERN, y+1)
        if not x then
            break
        end

        local date = string.sub(text, x, y)

        -- 截取當前行以便於尋找用戶名
        local t1, t2 = rfind(text, '\n', x)
        local curtalk = string.sub(lowertext, t1, x-1)

        -- 尋找用戶名
        local name = nil
        for i, v in ipairs(f.USERPREFIX) do
            local t3, t4 = rfind(curtalk, '[[' .. v)
            if t4 then
                local t5 = (string.find(curtalk, '[/#|%]]', t4+1))
                if t5 then
                    -- 確定用戶名
                    name = string.sub(text, t1+t4, t1+t5-2)
					name = mw.text.trim(name)

                    -- 首字母大寫
                    local firstletter = string.byte(name, 1, 1)
                    if firstletter >= 97 and firstletter <=122 then
                        name = string.upper(string.sub(name, 1, 1)) .. string.sub(name, 2, -1)
                    end
                    break
                end
            end
        end
        name = name or '?'

        if not result[name] then
            result[name] = {count = 1, firstdate = date, lastdate = date}
        else
            result[name].count = result[name].count + 1
            if comparedate(result[name].lastdate, date) then
                result[name].lastdate = date
            end
            if comparedate(date, result[name].firstdate) then
                result[name].firstdate = date
            end
        end
    until false
    return result
end

local function maketable(counttable, args)
    local tabletype = args.tabletype and mw.text.trim(args.tabletype) or 'user'
    local top = '{| class="wikitable sortable"\n|-'

    if tabletype == 'all' then
        top = '{| class="wikitable"\n|-'
    end

    local result = { top }

    if tabletype == 'topic' then
        table.insert(result, '! 主題 !! 發言次數 !! 參與者人數 !! 發起人 !! 最後發言者 !! data-sort-type="number" | 最後發言時間')
        for _, k in ipairs(counttable.ids) do
            local v = counttable.topics[k]
			if v.count > 0 then
				local displayTitle = string.format('[[%s#%s|%s]]', args.pageName, k, k)
                table.insert(result, string.format('|-\n| %s || %s || %s || %s || %s || data-sort-value="%s" | %s',
                displayTitle, v.count, v.usercount, getuserlink(v.firstname, args.userlink, args.ipuserlink),
                getuserlink(v.lastname, args.userlink, args.ipuserlink), date2num(v.lastdate), v.lastdate))
            end
        end
    elseif tabletype == 'topicsimple' then
        table.insert(result, '! 主題 !! 發言次數 !! 最後發言者 !! data-sort-type="number" | 最後發言時間')
        for _, k in ipairs(counttable.ids) do
            local v = counttable.topics[k]
			
            if v.count > 0 then
				local displayTitle = string.format('[[%s#%s|%s]]', args.pageName, k, k)
                table.insert(result, string.format('|-\n| %s || %s || %s || data-sort-value="%s" | %s',
                displayTitle, v.count, getuserlink(v.lastname, args.userlink, args.ipuserlink), date2num(v.lastdate), v.lastdate))
            end
        end
    elseif tabletype == 'all' then
        table.insert(result, '! 主題 !! 用戶 !! 發言次數 !! data-sort-type="number" | 最後發言時間')
        for _, k in ipairs(counttable.ids) do
            local v = counttable.topics[k]
            if v.count > 0 then
				local displayTitle = string.format('[[%s#%s|%s]]', args.pageName, k, k)
                local first = displayTitle .. ' || '
                if v.usercount > 1 then
                    first = 'rowspan=' .. v.usercount .. ' | ' .. first
                end
                for k2, v2 in pairs(v.users) do
                    table.insert(result, string.format('|-\n| %s%s || %s || data-sort-value="%s" | %s',
                        first, getuserlink(k2, args.userlink, args.ipuserlink), v2.count, date2num(v2.lastdate), v2.lastdate))
                    first = ''
                end
            end
        end
    else
        table.insert(result, '! 用戶 !! 發言次數 !! data-sort-type="number" | 最後發言時間')
        for k, v in pairs(counttable.users) do
            table.insert(result, string.format('|-\n| %s || %s || data-sort-value="%s" | %s', getuserlink(k, args.userlink, args.ipuserlink), v.count, date2num(v.lastdate), v.lastdate))
        end
    end

    table.insert(result, '|}')
    return table.concat(result, '\n')
end

--- 對簽名數據進行匯總
-- @param stat 通過count函數得到的統計數據
-- @return 一個table,發言數、發言人數、最後發言者、最後發言時間
local function getstat(data)
    local count = 0
    local usercount = 0
    local lastdate = f.MINDATE
    local lastname = nil
    local firstdate = f.MAXDATE
    local firstname = nil

    for username, userinfo in pairs(data) do
        count = count + userinfo.count
        usercount = usercount + 1
        if comparedate(lastdate, userinfo.lastdate) then
            lastdate = userinfo.lastdate
            lastname = username
        end
        if comparedate(userinfo.firstdate, firstdate) then
            firstdate = userinfo.firstdate
            firstname = username
        end
    end

    return {
        count = count,
        usercount = usercount,
        firstdate = firstdate,
        firstname = firstname,
        lastdate = lastdate,
        lastname = lastname
    }
end

--- 統計討論串中各個簽名的次數和最後發言時間。
-- @param args
-- @return
local function getcount(args)
    local userdata = {}
    local datas = extracttopic(args) or { data = {}, ids = {} }
    local result = { users = userdata, topics = {}, ids = datas.ids }

    for _, title in ipairs(datas.ids) do
        local content = datas.data[title]
        local topicdata = f._count(content)

        for username, userinfo in pairs(topicdata) do
            local dat = userdata[username] or { count = 0, lastdate = f.MINDATE }
            dat.count = dat.count + userinfo.count
            dat.lastdate = comparedate(dat.lastdate, userinfo.lastdate) and userinfo.lastdate or dat.lastdate
            userdata[username] = dat
        end

        result.topics[title] = getstat(topicdata)
        result.topics[title].users = topicdata
    end

    return result
end

--- 獲取最後簽名。
-- @param args
-- @return 一個table,包含用戶名和日期。
local function getlastsign(args)
    local r = getcount(args)
    local lastname = nil
    local lastdate = f.MINDATE

    for k, v in pairs(r.users) do
        if comparedate(lastdate, v.lastdate) then
            lastname = k
            lastdate = v.lastdate
        end
    end

    return {name=lastname, date=lastdate}
end

--- 將維基百科模板上的參數轉為lua的table。
-- @param frame 維基百科模板上的參數。
-- @return 一個table,包括各個參數。
local function getargs(frame)
	local title = mw.title.new(frame.args[1])

    if not title then
		return {
			text = '',
			pageName = title.prefixedText,
	        topic = frame.args.topic,
	        ispattern = frame.args.ispattern,
	        userlink = frame.args.userlink,
	        ipuserlink = frame.args.ipuserlink,
	        tabletype = frame.args.type,
	        titlelevel = '2'
		} 
	end
    -- 重定向
    if title.isRedirect then title = title.redirectTarget end

	local titlelevel = frame.args.titlelevel
	local prefixText = title.prefixedText
	if not titlelevel or titlelevel == '' then
		for prefix, level in pairs(f.PAGE_LEVEL_MAP) do
            if string.find(prefixText, prefix, 1, true) == 1 then
                titlelevel = level
                break
            end
        end
        titlelevel = titlelevel or '2'
    end
	
    return {
        text = title:getContent(),
		pageName = title.prefixedText,
        topic = frame.args.topic,
        ispattern = frame.args.ispattern,
        userlink = frame.args.userlink,
        ipuserlink = frame.args.ipuserlink,
        tabletype = frame.args.type,
        titlelevel = titlelevel
    }
end

--- 統計討論頁發言情況。詳情見文檔頁。
-- @param frame 維基百科頁面名。
-- @return 一張表格。
function f.analyse(frame)
    local args = getargs(frame)
    return maketable(getcount(args), args)
end

--- 獲取最後一個發言的用戶。
-- @param frame 維基百科頁面名。
-- @return 一個用戶名,不使用鏈接。
function f.lastname(frame)
    return getlastsign(getargs(frame)).name
end

--- 獲取最後一個發言的日期。
-- @param frame 維基百科頁面名。
-- @return 一個日期。
function f.lastdate(frame)
    return getlastsign(getargs(frame)).date
end

--- 獲取最後一個簽名。
-- @param frame 維基百科頁面名。
-- @return 一個簽名鏈接附上一個日期。
function f.lastsign(frame)
    local args = getargs(frame)
    local lastsign = getlastsign(args)
    if lastsign.name then
        return string.format('%s %s', getuserlink(lastsign.name, args.userlink, args.ipuserlink), lastsign.date)
    end
end

--- 統計簽名數量。
-- @param frame 維基百科頁面名。
-- @return 一個數字。
function f.count(frame)
    local data = getcount(getargs(frame))
    local username = frame.args.user and mw.text.trim(frame.args.user)

    if not username then
        local num = 0
        for k, v in pairs(data.topics) do
            num = num + v.count
        end
        return num
    elseif data.users[username] then
        return data.users[username].count
    else
        return 0
    end
end

--- 統計簽名者數量。
-- @param frame 維基百科頁面名。
-- @return 一個數字。
function f.countuser(frame)
    local data = getcount(getargs(frame)).users

    local count = 0
    for _ in pairs(data) do
        count = count + 1
    end

    return count
end

-- 以下用於本地測試
--[[
f.countsigns = getcount
f.lastsign = getlastsign
f.maketable = maketable
]]

f.getcount = getcount

return f