跳转到内容

Module:Cite

来自Undertale Wiki

此模組用於各種 Cite 的參考格式設定。


--- 在整個 wiki 中產生引用格式,確保格式一致。
--  此模組接受多種類型的引用,並根據 <code>f</code> 表中的函式分別處理。
--  @module             cite
--  @alias              p
--  @require            Module:Date
--  @require            Module:User error
--  @require            Module:Yesno
--  @require            Module:Tags
--  @require            Module:Cite/data
--  @author             [[User:KockaAdmiralac]]
--  @author             [[User:Ceruleanwarbler2]]
--  @author             [[User:Jacky720]]
--  @see                [[:Category:Citation templates]]
--  <nowiki>
local p = {}

require('strict')

-- 模組依賴
local Date = require('Module:Date')
local yesno = require('Module:Yesno')
local tags = require('Module:Tags')
local data = mw.loadData('Module:Cite/data')

-- 私有邏輯

local DATE_FORMAT = '%Y年%m月%d日'

--- userError 的包裝函式,會將頁面加入使用者錯誤分類
local function err(text)
    return require('Module:User error')(text, 'Pages with user errors')
end

--- 檢查傳入的日期字串是否為有效日期
local function valid_date(d)
    return pcall(function()
        Date(d)
    end)
end

-- 各類型引用的處理方式
local f = {}

--- 處理 Twitter 引用
function f.twitter(args)
    if not args.id or not tonumber(args.id) then
        return err('推文的 Snowflake ID 無效或未指定')
    end
    if not args.author or not args['text'] then
        return err('推文作者或內容未指定')
    end
    if args.wayback and not tonumber(args.wayback) then
        return '歸檔 ID 無效'
    end
    if args.archivetoday and (not args.archivetimestamp or not valid_date(args.archivetimestamp)) then
        return err('未指定歸檔日期')
    end

    local snowflake = tonumber(args.id)
    local epoch = math.floor(snowflake / 4194304 + 1288834974657)
    local date1 = Date(math.floor(epoch / 1000))

    local date2
    if args.archivetimestamp then
        date2 = Date(args.archivetimestamp):fmt(DATE_FORMAT)
    elseif args.wayback then
        date2 = Date(args.wayback):fmt(DATE_FORMAT)
    end

    local deadurl = yesno(args['deleted'], false)
    local str = {

        args.text,
        ' - ['
    }

    if deadurl and args.archivetoday then
        table.insert(str, 'https://archive.today/')
        table.insert(str, args.archivetoday)
        table.insert(str, '/')
    else
        if deadurl and args.wayback then
            table.insert(str, 'https://web.archive.org/web/')
            table.insert(str, args.wayback)
            table.insert(str, '/')
        end
        table.insert(str, 'https://twitter.com/')
        table.insert(str, args.author)
        table.insert(str, '/status/')
        table.insert(str, args.id)
    end

    if data.twitter[args.author] then
        table.insert(str, ' ')
        table.insert(str, data.twitter[args.author])
        table.insert(str, ' (@')
        table.insert(str, args.author)
        table.insert(str, ') ')
    else
        table.insert(str, ' @')
        table.insert(str, args.author)
    end

    table.insert(str, ' 的 X (原 Twitter) 貼文],')
    table.insert(str, date1:fmt(DATE_FORMAT))

    if deadurl then
        if args.wayback or args.archivetoday then
            table.insert(str, ' (已於 ')
            table.insert(str, date2)
            table.insert(str, '歸檔)')
        else
            table.insert(str, ' (已刪除)')
        end
    end

    return table.concat(str)
end

--- 處理 Bluesky 引用
function f.bluesky(args)
    if not args.author or not args.text then
        return err('未指定作者或內容')
    end
    if not args.id then
        return err('未指定貼文 ID')
    end
    if not args.timestamp or not valid_date(args.timestamp) then
        return err('日期未指定或格式錯誤')
    end
    return table.concat({
        tags.replace(args['text']),
        ' - [https://bsky.app/profile/',
        args['author'],
        '/post/',
        args['id'],
        ' @',
        args['author'],
        ' 的 Bluesky 貼文],',
        Date(args.timestamp):fmt(DATE_FORMAT)
    })
end

--- 處理遊戲內文字引用
function f.game(frame_args)
    local args = {}
    for i, v in ipairs(frame_args) do
        args[i] = v
    end
    local num_args = #args
    if num_args == 0 then
        return err('未指定引用內容')
    end
    if num_args == 1 then
        return err('未指定引用來源')
    end
    local author = args[num_args]
    args[num_args] = nil
    return table.concat({
        tags.replace(table.concat(args, '<br />')),
        ' - ',
        author
    })
end

--- 處理 YouTube 引用
function f.youtube(args)
    if not args.id or not args.title then
        return err('影片 ID 或標題未指定')
    end
    local str = {

        args.title,
        ' - [https://youtu.be/',
        args.id
    }
    if args.timestamp then
        table.insert(str, '?t=')
        table.insert(str, args.timestamp)
    end
    table.insert(str, ' YouTube]')
    return table.concat(str)
end


--- 處理 Bilibili 引用
function f.bilibili(args)
    if not args.id or not args.title then
        return err('影片 BV 号或标题未指定')
    end

    local base = {'[https://www.bilibili.com/video/', args.id}
    local query = {}

    -- 分P
    if args.part and tonumber(args.part) then
        table.insert(query, 'p=' .. args.part)
    end

    -- 时间戳
    if args.timestamp and tonumber(args.timestamp) then
        table.insert(query, 't=' .. args.timestamp)
    end

    if #query > 0 then
        table.insert(base, '?' .. table.concat(query, '&'))
    end

    table.insert(base, ' 哔哩哔哩]')

    return table.concat({
        args.title,
        ' - ',
        table.concat(base)
    })
end

--- 處理 Tumblr 引用
function f.tumblr(args)
    if not args.author or not args.text then
        return err('未指定作者或內容')
    end
    if not args.id or not tonumber(args.id) then
        return err('貼文 ID 無效或未指定')
    end
    if not args.timestamp or not valid_date(args.timestamp) then
        return err('日期未指定或格式錯誤')
    end
    return table.concat({
        tags.replace(args.text),
        ' - [http://',
        args.author,
        '.tumblr.com/post/',
        args.id,
        ' ',
        args.author,
        ' 的 Tumblr 貼文], ',
        Date(args.timestamp):fmt(DATE_FORMAT)
    })
end

--- 原始碼處理章節轉換的中文字子程式
--- 原始碼處理章節轉換的中文字子程式
local function numberToChinese(num)
    local cn_digit = {
        [0] = "零", [1] = "一", [2] = "二", [3] = "三", [4] = "四",
        [5] = "五", [6] = "六", [7] = "七", [8] = "八", [9] = "九"
    }
    num = tonumber(num)
    if not num then return "" end
    if num <= 10 then
        return (num == 10) and "十" or cn_digit[num]
    elseif num < 20 then
        return "十" .. cn_digit[num % 10]
    elseif num < 100 then
        local tens = math.floor(num / 10)
        local units = num % 10
        return cn_digit[tens] .. "十" .. (units ~= 0 and cn_digit[units] or "")
    else
        return tostring(num)
    end
end
--- 處理遊戲原始碼引用
function f.code(args)
    local str = {}
    local hasLink = args.chapter == nil or tonumber(args.chapter) < 5
    if not args.script then
        return err('未指定腳本名稱')
    end
    if args.line and not tonumber(args.line) then
        return err('起始行號不是數字')
    end
    if args.line2 and not tonumber(args.line2) then
        return err('結束行號不是數字')
    end
    if args.chapter == nil then
        str = {
            '[[code:',
            args.script,
        }
    elseif hasLink then
        str = {
            '[[code:',
            'ch',
            args.chapter,
            '/',
            args.script,
        }
    end
    if hasLink then
        if args.line then
            table.insert(str, '#L')
            table.insert(str, args.line)
        end
        table.insert(str, '|')
    end
    table.insert(str, args.script)
    table.insert(str, ' 腳本')
    if hasLink then
        table.insert(str, ']]')
    end
    if args.line then
        table.insert(str, ',第')
        if args.line2 then
            table.insert(str, ' ')
            table.insert(str, args.line)
            table.insert(str, '–')
            table.insert(str, args.line2)
            table.insert(str, ' 行')
        else
            table.insert(str, ' ')
            table.insert(str, args.line)
            table.insert(str, ' 行')
        end
    end
    if args.chapter ~= nil then
        table.insert(str, ',第')
        table.insert(str, numberToChinese(args.chapter))
        table.insert(str, '章')
    end
    return table.concat(str)
end

--- 處理新聞引用
function f.news(args)
    if not args.text then
        return err('未指定引用內容')
    end
    if not args.title then
        return err('未指定新聞標題')
    end
    if not args.site then
        return err('未指定新聞來源網站')
    end
    if not args.url then
        return err('未指定新聞網址')
    end
    if not args.timestamp or not valid_date(args.timestamp) then
        return err('日期未指定或格式錯誤')
    end
    local author = {}
    if args.firstname then
        if args.lastname then
            table.insert(author, args.lastname)
            table.insert(author, ',')
        end
        table.insert(author, args.firstname)
        table.insert(author, ',')
    end
    return table.concat({
        tags.replace(args.text),
        ' - [',
        args.url,
        ' ',
        args.title,
        '](',
        table.concat(author),
        Date(args.timestamp):fmt(DATE_FORMAT),
        ')',
        args.site,
        '。'
    })
end

--- 處理 Twitch 片段引用
function f.twitch(args)
    local id = args[1] or args.id
    local title = args[2] or args.title
    local author = args[3] or args.author
    local timestamp = args[4] or args.timestamp
    if not id then
        return err('未指定 Twitch 片段網址代碼')
    end
    if not title then
        return err('未指定片段標題')
    end
    if not author then
        return err('未指定主播使用者名稱')
    end
    if not timestamp or not valid_date(timestamp) then
        return err('日期未指定或格式錯誤')
    end
    return table.concat({
        '"[https://clips.twitch.tv/',
        id,
        '「',
        title,
        '」] 是來自 [https://twitch.tv/',
        mw.ustring.lower(author),
        ' @',
        author,
        '] 的直播片段,錄於 ',
        Date(timestamp):fmt(DATE_FORMAT)
    })
end

-- 將所有處理方法對應到模組介面
for mname, method in pairs(f) do
    p[mname] = function(frame)
        return method(frame:getParent().args)
    end
end

return p