跳转到内容

模組:沙盒/PexEric/1

维基百科,自由的百科全书

这是本页的一个历史版本,由PexEric留言 | 贡献2025年5月31日 (六) 18:30编辑。这可能和当前版本存在着巨大的差异。

local p = {}

-- 引入模块
local argsModule = require('Module:Arguments')
local tparamValue -- 延迟加载: require('Module:Template parameter value')

-- WikiProject banner shell 及其重定向的正则表达式列表
local WIKI_PROJECT_BANNER_SHELL_REDIRECTS = {
	"wiki%s*project%s*banner%s*shell",
	"w?pj?%s*banner%s*shell",
	"wiki%s*project%s*banners",
	"multiple%s*wikiprojects?",
	"wiki%s*project%s*shell",
	"pjbs",
	"[維维]基[专專][题題][橫横]幅",
	"多?[個个]?[維维]?基?[专專][题題][橫横]幅",
	"[維维]基[专專][题題]",
	"多?[個个]?[維维]?基?[专專][题題]",
	"[专專][题題][橫横]幅",
	"通用[評评][級级]",
}

---
-- 内部函数:从指定页面获取候选项目标题
-- @param cfg table 配置表,包含: title(页面标题), pattern(提取正则), black(黑名单), blackregex(黑名单正则)
-- @return table 候选项目标题数组
local function getCandidatesFromPage(cfg)
    local pageTitleObj = mw.title.new(cfg.title)
    if not pageTitleObj or not pageTitleObj.exists then
        mw.log("Module:CandidateProjectInfo - getCandidatesFromPage: 评选页面 '" .. cfg.title .. "' 未找到或无效。")
        return {}
    end
    local pageContent = pageTitleObj:getContent()
    if not pageContent then
        mw.log("Module:CandidateProjectInfo - getCandidatesFromPage: 评选页面 '" .. cfg.title .. "' 内容为空。")
        return {}
    end

    local matches = {}
    local blackList = {}
    if cfg.black and cfg.black ~= '' then
        for b in mw.text.gsplit(cfg.black, '|', true) do
            blackList[mw.text.trim(b)] = true
        end
    end

    for m in mw.ustring.gmatch(pageContent, cfg.pattern) do
        m = mw.text.trim(m)
        if m ~= '' and not blackList[m] and not (cfg.blackregex and cfg.blackregex ~= '' and mw.ustring.match(m, cfg.blackregex)) then
            table.insert(matches, m)
        end
    end
    if #matches == 0 then
        mw.log("Module:CandidateProjectInfo - getCandidatesFromPage: 在页面 '" .. cfg.title .. "' 使用模式 '" .. cfg.pattern .. "' 未找到候选项目。")
    end
    return matches
end

---
-- 内部函数:从讨论页获取维基专题名称
-- @param talkPageFullName string 讨论页完整标题
-- @return table 专题模板名称数组
local function getProjectsFromTalkPage(talkPageFullName)
    if not tparamValue then -- 延迟加载
        tparamValue = require('Module:Template parameter value')
    end

    mw.log("Module:CandidateProjectInfo - getProjectsFromTalkPage: 尝试为讨论页 '" .. talkPageFullName .. "' 获取专题。")

    local success, bannerContentOrError = tparamValue.getParameter(
        talkPageFullName,
        WIKI_PROJECT_BANNER_SHELL_REDIRECTS,
        '1', -- {{WikiProject banner shell}} 的第一个匿名参数
        {
            treat_as_regex = true, -- WIKI_PROJECT_BANNER_SHELL_REDIRECTS 使用正则
            template_index = 1, -- 获取第一个匹配的 banner shell
            -- ignore_blank = true -- 如果参数1为空,TPV应该返回空字符串而不是错误
        }
    )

    if not success then
        mw.log("Module:CandidateProjectInfo - getProjectsFromTalkPage: 调用 tparamValue.getParameter 失败,讨论页 '" .. talkPageFullName .. "'。错误: " .. tostring(bannerContentOrError))
        return {}
    end

    if not bannerContentOrError or bannerContentOrError:match("^%s*$") then
        mw.log("Module:CandidateProjectInfo - getProjectsFromTalkPage: WikiProject banner shell 参数1为空或未找到,讨论页 '" .. talkPageFullName .. "'。获取到的内容: " .. tostring(bannerContentOrError))
        return {}
    end
    
    mw.log("Module:CandidateProjectInfo - getProjectsFromTalkPage: 成功获取banner shell参数1内容,讨论页 '" .. talkPageFullName .. "'。内容片段: " .. mw.text.truncate(bannerContentOrError, 100, "..."))

    local projects = {}
    -- 从 banner shell 的参数1内容中提取模板名
    -- 正则含义: {{可选空格 捕获(非}|的字符) 可选空格 可选|...}}
    for name_part in mw.ustring.gmatch(bannerContentOrError, "{{%s*([^}|]+)") do
        local projectName = mw.text.trim(name_part)
        if projectName ~= "" and not projectName:match("^[Tt]alkheader") and not projectName:match("^[Pp]age assessments") then -- 过滤一些常见非专题模板
            local found = false -- 避免重复添加同一专题
            for _, existing_proj in ipairs(projects) do
                if existing_proj == projectName then
                    found = true
                    break
                end
            end
            if not found then
                table.insert(projects, projectName)
                mw.log("Module:CandidateProjectInfo - getProjectsFromTalkPage: 讨论页 '" .. talkPageFullName .. "' 找到专题: " .. projectName)
            end
        end
    end
    
    if #projects == 0 then
         mw.log("Module:CandidateProjectInfo - getProjectsFromTalkPage: 讨论页 '" .. talkPageFullName .. "' 的banner shell参数1内容中未解析出专题。原始内容片段: " .. mw.text.truncate(bannerContentOrError, 100, "..."))
    end
    return projects
end

---
-- 内部函数:格式化条目项(链接 + 前后缀应用于原始标题文本)
-- @param article_title_raw string 从评选页提取的原始条目标题
-- @param linkprefix_val string|nil 链接目标前缀
-- @param item_prefix_val string 条目项前缀
-- @param item_suffix_val string 条目项后缀
-- @return string 格式化后的条目项wikitext链接
local function formatArticleItem(article_title_raw, linkprefix_val, item_prefix_val, item_suffix_val)
    -- item_prefix 和 item_suffix 直接包裹原始纯文本标题
    local display_text_for_pipe = item_prefix_val .. article_title_raw .. item_suffix_val

    local core_link
    local link_target = article_title_raw -- 链接目标是原始标题

    if linkprefix_val and linkprefix_val ~= "" then
        core_link = string.format('[[:%s%s|%s]]', linkprefix_val, link_target, display_text_for_pipe)
    else
        core_link = string.format('[[:%s|%s]]', link_target, display_text_for_pipe)
    end
    return core_link
end

---
-- 函数1: 以表格形式列出候选项目及其所属专题
-- @param frame table Scribunto frame 对象
function p.listProjectsForCandidates(frame)
    local args = argsModule.getArgs(frame, {removeBlanks = false})

    local review_page_title = args.title
    local candidate_pattern = args.pattern
    if not review_page_title or review_page_title == '' or not candidate_pattern or candidate_pattern == '' then
        return '<span class="error">错误:参数 "title" 和 "pattern" 不能为空。</span>'
    end

    local candidate_cfg = {
        title = review_page_title,
        pattern = candidate_pattern,
        black = args.black,
        blackregex = args.blackregex
    }
    local candidates = getCandidatesFromPage(candidate_cfg)

    if #candidates == 0 then
        return args.ifnone or "暂无候选项目"
    end

    local item_prefix = args.item_prefix or ""
    local item_suffix = args.item_suffix or ""
    local tpl_prefix = args.tpl_prefix or "{{tl|"
    local tpl_suffix = args.tpl_suffix or "}}"
    local linkprefix = args.linkprefix

    local output_table = {}
    table.insert(output_table, '{| class="wikitable sortable"')
    table.insert(output_table, '! 条目 !! 所属专题')

    for _, article_title_raw in ipairs(candidates) do
        local projects_string = args.projectifnone or "<em>未找到所属专题信息</em>" -- 默认值
        local formatted_article = formatArticleItem(article_title_raw, linkprefix, item_prefix, item_suffix) -- 提前格式化条目链接

        if type(article_title_raw) == "string" and article_title_raw ~= "" then
            local mainArticleTitleObj = mw.title.new(article_title_raw)
            if mainArticleTitleObj then
                local talkPageTitleObj = mainArticleTitleObj:getTalkPage()
                if talkPageTitleObj then
                    -- 确保讨论页实际存在才去获取内容和专题
                    if talkPageTitleObj.exists then
                        local projects = getProjectsFromTalkPage(talkPageTitleObj:getFullText())
                        if #projects > 0 then
                            local projects_formatted_list = {}
                            for _, proj_name in ipairs(projects) do
                                table.insert(projects_formatted_list, tpl_prefix .. proj_name .. tpl_suffix)
                            end
                            projects_string = table.concat(projects_formatted_list, "、")
                        else
                            -- getProjectsFromTalkPage 内部会log,这里可以用默认的 "未找到"
                        end
                    else
                        mw.log("Module:CandidateProjectInfo - listProjectsForCandidates: 讨论页 '" .. talkPageTitleObj:getFullText() .. "' (来自条目 '".. article_title_raw .."') 不存在。")
                        projects_string = args.projectifnone or "<em>讨论页不存在</em>"
                    end
                else
                    mw.log("Module:CandidateProjectInfo - listProjectsForCandidates: 无法获取条目 '" .. article_title_raw .. "' 的讨论页对象。")
                    projects_string = args.projectifnone or "<em>无讨论页对象</em>"
                end
            else
                mw.log("Module:CandidateProjectInfo - listProjectsForCandidates: 无法为 '" .. article_title_raw .. "' 创建主条目title对象。")
                projects_string = "<em>无效条目名</em>"
            end
        else
            mw.log("Module:CandidateProjectInfo - listProjectsForCandidates: 候选列表中发现无效条目名: " .. tostring(article_title_raw))
            projects_string = "<em>无效条目名</em>"
            formatted_article = item_prefix .. tostring(article_title_raw) .. item_suffix -- 尽力显示
        end
        
        table.insert(output_table, '|-')
        table.insert(output_table, '| ' .. formatted_article .. '\n| ' .. projects_string)
    end

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

---
-- 函数2: 按指定专题筛选候选项目
-- @param frame table Scribunto frame 对象
function p.filterCandidatesByProjects(frame)
    local args = argsModule.getArgs(frame, {removeBlanks = false})

    local review_page_title = args.title
    local candidate_pattern = args.pattern
    if not review_page_title or review_page_title == '' or not candidate_pattern or candidate_pattern == '' then
        return '<span class="error">错误:参数 "title" 和 "pattern" 不能为空。</span>'
    end

    local project_regexes = {}
    for k, v in pairs(args) do
        if type(k) == 'string' and k:match("^project%d+$") and v and v ~= '' then
            table.insert(project_regexes, v)
        end
    end

    if #project_regexes == 0 then
        return '<span class="error">错误:必须提供至少一个 "projectN" 参数用于筛选。</span>'
    end

    local candidate_cfg = {
        title = review_page_title,
        pattern = candidate_pattern,
        black = args.black,
        blackregex = args.blackregex
    }
    local candidates = getCandidatesFromPage(candidate_cfg)

    if #candidates == 0 then
        return args.ifnone or "暂无候选项目"
    end

    local item_prefix = args.item_prefix or ""
    local item_suffix = args.item_suffix or ""
    local linkprefix = args.linkprefix

    local matching_articles_formatted = {}

    for _, article_title_raw in ipairs(candidates) do
        if type(article_title_raw) == "string" and article_title_raw ~= "" then
            local mainArticleTitleObj = mw.title.new(article_title_raw)
            if mainArticleTitleObj then
                local talkPageTitleObj = mainArticleTitleObj:getTalkPage()
                if talkPageTitleObj and talkPageTitleObj.exists then
                    local article_projects = getProjectsFromTalkPage(talkPageTitleObj:getFullText())
                    if #article_projects > 0 then
                        local matched_project = false
                        for _, actual_proj_name in ipairs(article_projects) do
                            for _, proj_regex in ipairs(project_regexes) do
                                local regex_match_success, _ = pcall(mw.ustring.match, actual_proj_name, proj_regex)
                                if regex_match_success and mw.ustring.match(actual_proj_name, proj_regex) then
                                    matched_project = true
                                    break
                                end
                            end
                            if matched_project then break end
                        end

                        if matched_project then
                            local formatted_article = formatArticleItem(article_title_raw, linkprefix, item_prefix, item_suffix)
                            table.insert(matching_articles_formatted, formatted_article)
                        end
                    end
                else
                    if talkPageTitleObj then
                         mw.log("Module:CandidateProjectInfo - filterCandidatesByProjects: 讨论页 '" .. talkPageTitleObj:getFullText() .. "' (来自条目 '".. article_title_raw .."') 不存在。")
                    else
                         mw.log("Module:CandidateProjectInfo - filterCandidatesByProjects: 无法获取条目 '" .. article_title_raw .. "' 的讨论页对象。")
                    end
                end
            else
                 mw.log("Module:CandidateProjectInfo - filterCandidatesByProjects: 无法为 '" .. article_title_raw .. "' 创建主条目title对象。")
            end
        else
            mw.log("Module:CandidateProjectInfo - filterCandidatesByProjects: 候选列表中发现无效条目名: " .. tostring(article_title_raw))
        end
    end

    if #matching_articles_formatted == 0 then
        return args.projectifnone or "没有找到符合指定专题条件的候选项目。"
    end

    return '# ' .. table.concat(matching_articles_formatted, '\n# ')
end

return p