跳转到内容

模組:沙盒/PexEric/1

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

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

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
        return {}
    end
    local pageContent = pageTitleObj:getContent()
    if not pageContent then
        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
    return matches
end

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

    local success, bannerContent = tparamValue.getParameter(
        talkPageName,
        WIKI_PROJECT_BANNER_SHELL_REDIRECTS,
        '1', -- {{WikiProject banner shell}} 的第一个匿名参数
        {
            treat_as_regex = true, -- WIKI_PROJECT_BANNER_SHELL_REDIRECTS 使用正则
            template_index = 1 -- 获取第一个匹配的 banner shell
        }
    )

    if not success or not bannerContent or bannerContent:match("^%s*$") then
        return {} -- 未成功获取或内容为空
    end

    local projects = {}
    -- 从 banner shell 的参数1内容中提取模板名
    for name_part in mw.ustring.gmatch(bannerContent, "{{%s*([^}|]+)") do
        local projectName = mw.text.trim(name_part)
        if projectName ~= "" 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)
            end
        end
    end
    return projects
end

---
-- 内部函数:格式化条目项(链接 + 前后缀)
-- @param article_title string 条目标题
-- @param linkprefix_val string|nil 链接目标前缀
-- @param item_prefix_val string 条目项前缀
-- @param item_suffix_val string 条目项后缀
-- @return string 格式化后的条目项文本
local function formatArticleItem(article_title, linkprefix_val, item_prefix_val, item_suffix_val)
    local display_title = article_title:gsub('_', ' ') -- 链接显示文本替换下划线
    local core_link
    if linkprefix_val and linkprefix_val ~= "" then
        core_link = string.format('[[:%s%s|%s]]', linkprefix_val, article_title, display_title)
    else
        core_link = string.format('[[:%s|%s]]', article_title, display_title)
    end
    return item_prefix_val .. core_link .. item_suffix_val
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 in ipairs(candidates) do
        local talkPageTitleObj = mw.title.new(article_title)
        if talkPageTitleObj then -- 仅当条目对象有效时继续
            local talkPageTitle = talkPageTitleObj:getTalkPage()
            if talkPageTitle then -- 仅当讨论页对象有效时继续
                local projects = getProjectsFromTalkPage(talkPageTitle:getFullText())
                local projects_formatted_list = {}
                if #projects > 0 then
                    for _, proj_name in ipairs(projects) do
                        table.insert(projects_formatted_list, tpl_prefix .. proj_name .. tpl_suffix)
                    end
                end

                local projects_string
                if #projects_formatted_list > 0 then
                    projects_string = table.concat(projects_formatted_list, "、") -- 顿号分隔
                else
                    projects_string = args.projectifnone or "<em>未找到所属专题信息</em>"
                end

                local formatted_article = formatArticleItem(article_title, linkprefix, item_prefix, item_suffix)

                table.insert(output_table, '|-')
                table.insert(output_table, '| ' .. formatted_article .. '\n| ' .. projects_string)
            end
        end
        -- 如果 talkPageTitleObj 或 talkPageTitle 无效,则自然跳到下一次循环
    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 = {} -- 存储 projectN 参数的正则表达式
    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 in ipairs(candidates) do
        local talkPageTitleObj = mw.title.new(article_title)
        if talkPageTitleObj then -- 仅当条目对象有效时继续
            local talkPageTitle = talkPageTitleObj:getTalkPage()
            if talkPageTitle then -- 仅当讨论页对象有效时继续
                local article_projects = getProjectsFromTalkPage(talkPageTitle: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
                            -- 使用 pcall 防范无效正则表达式引发错误
                            local success_match, _ = pcall(mw.ustring.match, actual_proj_name, proj_regex)
                            if success_match and mw.ustring.match(actual_proj_name, proj_regex) then
                                matched_project = true
                                break -- 当前条目的一个专题已匹配,无需再检查此条目的其他专题
                            end
                        end
                        if matched_project then
                            break -- 当前条目已匹配,无需再检查此条目的其他专题针对其他 projectN 正则
                        end
                    end

                    if matched_project then
                        local formatted_article = formatArticleItem(article_title, linkprefix, item_prefix, item_suffix)
                        table.insert(matching_articles_formatted, formatted_article)
                    end
                end
            end
        end
        -- 如果 talkPageTitleObj 或 talkPageTitle 无效,则自然跳到下一次循环
    end

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

    -- 输出为 # 号有序列表
    return '# ' .. table.concat(matching_articles_formatted, '\n# ')
end

return p