Module:Cite
此模組用於各種 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