文档图示 模块文档[创建]
------------------------------------------------------------------------                          Module:Rfx                              ---- This is a library for retrieving information about requests      ---- for adminship and requests for bureaucratship on the English     ---- Wikipedia. Please see the module documentation for instructions. ------------------------------------------------------------------------local libraryUtil = require('libraryUtil')local lang = mw.getContentLanguage()local textSplit = mw.text.splitlocal umatch = mw.ustring.matchlocal newTitle = mw.title.newlocal validSignPrefixes = {['u']=1, ['user']=1, ['用户']=1, ['用戶']=1,['ut']=1, ['user talk']=1, ['用户讨论']=1, ['用戶討論']=1,['special:contribs']=1, ['特殊:contribs']=1,['special:contributions']=1, ['特殊:contributions']=1,['特殊:用户贡献']=1, ['特殊:用戶貢獻']=1,['special:用户贡献']=1, ['special:用戶貢獻']=1}local rfx = {}local corrections = require('Module:Rfx/correction')----------------------------------------         Helper functions         ----------------------------------------local function getTitleObject(title)local success, titleObject = pcall(newTitle, title)if success and titleObject thenreturn titleObjectelsereturn nilendendlocal function parseVoteBoundaries(section)-- Returns an array containing the raw wikitext of RfX votes in a given section.section = section:match('^.-\n#(.*)$') -- Strip non-votes from the start.if not section thenreturn {}end-- WhitePhosphorus: Do not discard anything, or we may lose votes.--- See [[special:permalink/45633636]].-- section = section:match('^(.-)\n[^#]') or section -- Discard subsequent numbered lists.local comments = textSplit(section, '\n#')local votes = {}for i, comment in ipairs(comments) doif comment:find('^[^#*;:].*%S') thenvotes[#votes + 1] = commentendendreturn votesendlocal function parseVote(vote)-- parses a username from an RfX vote.local b, e, link, username, page, colon, slash, prefix = nil, 0while true do-- extract all linksb, e, link = vote:find('%[%[[_%s]*:?[_%s]*(.-)[_%s]*%]%]', e+1)if not link thenbreakend-- some strange links like User__  ___talk:_ __ Example is also valid.link = link:gsub('|.*', ''):gsub('[%s_]+', ' '):gsub('([:/]) ', '%1')colon, slash = link:find('/'), link:find(':')if colon thenprefix = link:sub(1, colon-1):lower()if validSignPrefixes[prefix] thenusername = link:sub(colon+1)endendif slash thenprefix = link:sub(1, slash-1):lower()if validSignPrefixes[prefix] thenusername = link:sub(slash+1)endendendif not username thenreturn string.format( "'''签名-{zh-cn:解析;zh-tw:剖析}-失败''':''%s''", vote )endreturn username:match('^[^/#]*')endlocal function parseVoters(votes)local voters = {}for i, vote in ipairs(votes) dovoters[#voters + 1] = parseVote(vote)endreturn votersendlocal function dupesExist(...)local exists = {}local tables = {...}local dupes = {}for i, usernames in ipairs(tables) dofor j, username in ipairs(usernames) dousername = lang:ucfirst(username)if exists[username] thendupes[username] = trueelseexists[username] = trueendendendlocal result = {}for username, _ in pairs(dupes) dotable.insert(result, username)endreturn resultend--------------------------------------------   Define the constructor function    --------------------------------------------function rfx.new(title)local obj = {}local data = {}local checkSelf = libraryUtil.makeCheckSelfFunction( 'Module:Rfx', 'rfx', obj, 'rfx object' )-- Get the title object and check to see whether we are a subpage of WP:RFA or WP:RFB.title = getTitleObject(title)if not title thenreturn nilendfunction data:getTitleObject()checkSelf(self, 'getTitleObject')return titleendif title.namespace == 4 thenlocal rootText = title.rootTextif rootText == '申请成为管理员' thendata.type = 'rfa'elseif rootText == '申请成为行政员' thendata.type = 'rfb'elseif rootText == '申请成为用户查核员' thendata.type = 'rfcu'elseif rootText == '申请成为监督员' thendata.type = 'rfo'elseif rootText == '申请成为界面管理员' thendata.type = 'rfia'elsereturn nilendlocal n = umatch(title.subpageText, '^第(%d+)次$')if n ~= nil thendata.attempt = nelsedata.attempt = '1'endelsereturn nilend-- Get the page content and divide it into sections.local pageText = title:getContent()if not pageText thenreturn nilendframe = mw.getCurrentFrame()pageText = string.gsub(pageText, "{{%s*[Ff]ollow[Ll]ast[Ii]ndent|(.*)%s*}}",function (s)return frame:expandTemplate{ title = 'FollowLastIndent', args = { s } }end)-- FIXME: 反对?--- 其实这个不太重要,毕竟大家都用RfA模版,后者生成出来是繁体的。local introText, supportText, opposeText, neutralText = umatch(pageText,'^(.-)\n====%s*支持%s*====(.-)'.. '\n====%s*反對%s*====(.-)'.. '\n====%s*中立%s*====(.-)'.. '\n====%s*意見%s*====.*')if not introText thenintroText, supportText, opposeText, neutralText = umatch(pageText,"^(.-\n'''[^\n]-''')\n.-".. "\n'''支持'''(.-)\n'''反對'''(.-)\n'''中立'''(.-)'''意見'''.*")end-- Get vote counts.local supportVotes, opposeVotes, neutralVotesif supportText and opposeText and neutralText thensupportVotes = parseVoteBoundaries(supportText)opposeVotes = parseVoteBoundaries(opposeText)neutralVotes = parseVoteBoundaries(neutralText)endlocal correction = corrections[title.text] or {0, 0, 0}local supports, opposes, neutralsif supportVotes and opposeVotes and neutralVotes thensupports = #supportVotes + correction[1]data.supports = math.max(supports, 0)opposes = #opposeVotes + correction[2]data.opposes = math.max(opposes, 0)neutrals = #neutralVotes + correction[3]data.neutrals = math.max(neutrals, 0)end-- Voter methods and dupe check.function data:getSupportUsers()checkSelf(self, 'getSupportUsers')if supportVotes thenreturn parseVoters(supportVotes)elsereturn nilendendfunction data:getOpposeUsers()checkSelf(self, 'getOpposeUsers')if opposeVotes thenreturn parseVoters(opposeVotes)elsereturn nilendendfunction data:getNeutralUsers()checkSelf(self, 'getNeutralUsers')if neutralVotes thenreturn parseVoters(neutralVotes)elsereturn nilendendfunction data:dupesExist()checkSelf(self, 'dupesExist')local supportUsers = self:getSupportUsers()local opposeUsers = self:getOpposeUsers()local neutralUsers = self:getNeutralUsers()if not (supportUsers and opposeUsers and neutralUsers) thenreturn nilendreturn dupesExist(supportUsers, opposeUsers, neutralUsers)endif supports and opposes thenlocal total = supports + opposesif total <= 0 thendata.percent = 0elsedata.percent = math.floor((supports / total * 100) + 0.5)endendif introText thendata.rawEndTime = umatch(introText, '%d+年%d+月%d+日%s*%([日一二三四五六]%)%s*%d+:%d+ %(UTC%)')if data.rawEndTime thenlocal Y, n, j, m, s = umatch(data.rawEndTime, '(%d+)年(%d+)月(%d+)日%s*%([日一二三四五六]%)%s*(%d+):(%d+) %(UTC%)')data.endTime = string.format('%04d-%02d-%02dT%02d:%02dZ', Y, n, j, m, s)end-- === [[User:Example|Nickname]] ===data.user = umatch(introText, '===%s*%[%[[_%s]*[uU]ser[_%s]*:[_%s]*([^\n]-)|[^\n]-%]%]%s*===') or-- === [[U:Example|Nickname]] ===umatch(introText, '===%s*%[%[[_%s]*[uU][_%s]*:[_%s]*([^\n]-)|[^\n]-%]%]%s*===') or-- === [[用户:Example|Nickname]] ===umatch(introText, '===%s*%[%[[_%s]*用户[_%s]*:[_%s]*([^\n]-)|[^\n]-%]%]%s*===') or-- === [[用戶:Example|Nickname]] ===umatch(introText, '===%s*%[%[[_%s]*用戶[_%s]*:[_%s]*([^\n]-)|[^\n]-%]%]%s*===') or-- === [[User:Example]] ===umatch(introText, '===%s*%[%[[_%s]*[uU]ser[_%s]*:[_%s]*([^\n]-)%]%]%s*===') or-- === [[U:Example]] ===umatch(introText, '===%s*%[%[[_%s]*[uU][_%s]*:[_%s]*([^\n]-)%]%]%s*===') or-- === [[用户:Example]] ===umatch(introText, '===%s*%[%[[_%s]*用户[_%s]*:[_%s]*([^\n]-)%]%]%s*===') or-- === [[用戶:Example]] ===umatch(introText, '===%s*%[%[[_%s]*用戶[_%s]*:[_%s]*([^\n]-)%]%]%s*===') or-- === User:Example ===umatch(introText, '===%s*[uU]ser[_%s]*:[_%s]*([^\n]-)%s*===') or-- === U:Example ===umatch(introText, '===%s*[uU][_%s]*:[_%s]*([^\n]-)%s*===') or-- === 用户:Example ===umatch(introText, '===%s*用户[_%s]*:[_%s]*([^\n]-)%s*===') or-- === 用戶:Example ===umatch(introText, '===%s*用戶[_%s]*:[_%s]*([^\n]-)%s*===') or-- === Example ===umatch(introText, '===%s*([^\n]-)%s*===')end-- Methods for seconds left and time left.function data:getSecondsLeft()checkSelf(self, 'getSecondsLeft')local endTime = self.endTimeif not endTime thenreturn nilendlocal now = tonumber(lang:formatDate("U"))local success, endTimeU = pcall(lang.formatDate, lang, 'U', endTime)if not success thenreturn nilendendTimeU = tonumber(endTimeU)if not endTimeU thenreturn nilendlocal secondsLeft = endTimeU - nowif secondsLeft <= 0 thenreturn 0elsereturn secondsLeftendendfunction data:getTimeLeft()checkSelf(self, 'getTimeLeft')local secondsLeft = self:getSecondsLeft()if not secondsLeft thenreturn nilendreturn mw.ustring.gsub(lang:formatDuration(secondsLeft, {'days', 'hours'}), ' and', ',')endfunction data:getReport()-- Gets the URI object for Jimmy's RfA Analysis toolcheckSelf(self, 'getReport')return mw.uri.new('//tools.wmflabs.org/jimmy/cgi-bin/rfa.py?title=' .. mw.uri.encode(title.prefixedText))endfunction data:getStatus()-- Gets the current status of the RfX. Returns either "successful", "unsuccessful",-- "open", or "pending closure". Returns nil if the status could not be found.checkSelf( self, 'getStatus' )-- 中文维基百科的RfX并没有有效判断成功与失败的方法,只能判断结束与否。local secondsLeft = self:getSecondsLeft()if secondsLeft and secondsLeft > 0 thenreturn '投票中'elseif secondsLeft and secondsLeft <= 0 thenreturn '已结束'elsereturn nilendend-- Specify which fields are read-only, and prepare the metatable.local readOnlyFields = {getTitleObject = true,['type'] = true,getSupportUsers = true,getOpposeUsers = true,getNeutralUsers = true,supports = true,opposes = true,neutrals = true,endTime = true,rawEndTime = true,percent = true,user = true,dupesExist = true,getSecondsLeft = true,getTimeLeft = true,getReport = true,getStatus = true}local function pairsfunc( t, k )local vrepeatk = next( readOnlyFields, k )if k == nil thenreturn nilendv = t[k]until v ~= nilreturn k, vendreturn setmetatable( obj, {__pairs = function ( t )return pairsfunc, t, nilend,__index = data,__newindex = function( t, key, value )if readOnlyFields[ key ] thenerror( '下标"' .. key .. '"只读', 2 )elserawset( t, key, value )endend,__tostring = function( t )return t:getTitleObject().prefixedTextend} )endreturn rfx