Module:Cite tweet

local TwitterSnowflake = require('Module:TwitterSnowflake')

local err_msgs_t = { -- a sequence of snowflake date error messages; all must be terminated with a semicolon (;)

' |date= / |number= mismatch;', -- [1]

' |date= required;', -- [2]

' Invalid |number=;', -- [3]

' Missing or empty |number=;', -- [4]

' Missing or empty |user=;' -- [5]

}

--[[--------------------------< S U P P R E S S _ U R L _ I N _ T I T L E >------------------------------------

This function searches for and suppresses urls in |title=, |script-title=, and |trans-title= parameters so that

{{cite web}} won't emit 'External link in |title=' error messages when rendered In the redering, urls are correctly

formed as they were in the original tweet. The function looks for valid schemes and then wraps them in

.. tags.

]]

local function suppress_url_in_title (frame, title)

local schemes = { -- schemes commonly found in tweets

'https://',

'http://',

'ftp://',

}

if title then -- when there is a title, suppress any urls with known schemes; abandon else

for _, scheme in ipairs (schemes) do -- spin through the list of schemes looking for a match

title = title:gsub (scheme, frame:callParserFunction ('#tag', {'nowiki', scheme})); -- replace the scheme with its nowiki'd form (a strip marker)

end

end

return title; -- done; return modified or not</p> <p>end</p> <p>--[[--------------------------< D A T E _ N U M B E R _ U R L _ G E T >----------------------------------------</p> <p>extract |date= and |number= parameter values if present. Extract date from |number= and compare to |date=.</p> <p>contruct |url= for {{cite web}} from the base url and |number= and |user=</p> <p>returns nothing; adds date, number, url to <cite_args_t>; adds error message(s) to <errors_t>.</p> <p>]]</p> <p>local function date_number_url_get (args_t, cite_args_t, errors_t)</p> <p>local err_msg_index;</p> <p>cite_args_t.url = 'https://x.com/'; -- initialize with minimal base url because {{cite web}} requires |url=</p> <p>if not args_t.user then</p> <p>table.insert (errors_t, err_msgs_t[5]); -- error: missing or empty |user=</p> <p>end</p> <p>if not args_t.date and not args_t.number then</p> <p>err_msg_index = 4; -- error: missing or empty |number=</p> <p>elseif tonumber (args_t.number) then -- |number= without |date=? use number</p> <p>if tonumber(args_t.number) then</p> <p>cite_args_t.date = args_t.date or (args_t.number and TwitterSnowflake.snowflakeToDate{ args = {id_str = args_t.number} });</p> <p>else</p> <p>cite_args_t.date = args_t.date;</p> <p>end</p> <p>cite_args_t.number = args_t.number;</p> <p>if args_t.user then -- |number= appears to have a valid value; if |user= has a value</p> <p>cite_args_t.url = cite_args_t.url .. args_t.user .. '/status/' .. args_t.number; -- construct |url= for {{cite web}}</p> <p>end</p> <p>elseif args_t.number then -- |number= with a value that can't be converted to a number; invalid</p> <p>err_msg_index = 3; -- error: invalid number (couldn't convert to number)</p> <p>elseif not args_t.number then -- |date= without |number= use date</p> <p>cite_args_t.date = args_t.date; -- |date= has a value, use it</p> <p>err_msg_index = 4; -- error: missing or empty |number=</p> <p>end</p> <p>if err_msg_index then</p> <p>table.insert (errors_t, err_msgs_t[err_msg_index]); -- invalid number or missing necessary parameters so abandon</p> <p>return;</p> <p>end</p> <p>err_msg_index = TwitterSnowflake.datecheck ({ args = { -- returns error message index number on error; nil else</p> <p>id_str = args_t.number or '',</p> <p>date = args_t.date or '',</p> <p>error1 = 1, -- these numbers are indexes into <err_msgs_t> to override snowflake default error messages</p> <p>error2 = 2, -- done this way to avoid long string comparison looking for</p> <p>error3 = 3 -- the undated-pre-twitter-epoch-post message</p> <p>}});</p> <p>if 2 == err_msg_index then -- when no date and posted before twitter epoch</p> <p>cite_args_t.date = nil; -- suppress default date because {{cite tweet}} should not claim in its own voice that the undated post was posted 2010-11-04</p> <p>end</p> <p>table.insert (errors_t, err_msgs_t[err_msg_index]); -- add error message</p> <p>end</p> <p>--[[--------------------------< M A I N >----------------------------------------------------------------------</p> <p>construct parameter set for {{cite web}} from {{cite tweet}} parameters; do some error checking</p> <p>]]</p> <p>local function main (frame)</p> <p>local args_t = require ('Module:Arguments').getArgs (frame);</p> <p>local cite_args_t = {</p> <p>title = suppress_url_in_title (frame, args_t.title),</p> <p>['script-title'] = suppress_url_in_title (frame, args_t['script-title']),</p> <p>['trans-title'] = suppress_url_in_title (frame, args_t['trans-title']),</p> <p>language = args_t.language,</p> <p>last1 = args_t.last1 or args_t.last,</p> <p>first1 = args_t.first1 or args_t.first,</p> <p>author1 = args_t.author1 or args_t.author,</p> <p>['author-link'] = args_t['author-link'] or args_t.authorlink,</p> <p>others = args_t.retweet and ('Retweeted by ' .. args_t.retweet),</p> <p>via = args_t.link == 'no' and 'Twitter' or '<a href='?title=Twitter'>Twitter</a>',</p> <p>type = args_t.link == 'no' and 'Tweet' or '<a href='?title=Tweet_%28social_media%29'>Tweet</a>',</p> <p>location = args_t.location, -- why |location=? tweets are online; there is no publication place</p> <p>['access-date'] = args_t['access-date'] or args_t.accessdate,</p> <p>['archive-date'] = args_t['archive-date'] or args_t.archivedate,</p> <p>['archive-url'] = args_t['archive-url'] or args_t.archiveurl,</p> <p>['url-status'] = args_t['url-status'],</p> <p>['url-access'] = args_t['url-access'],</p> <p>quote = args_t.quote,</p> <p>ref = args_t.ref,</p> <p>df = args_t.df,</p> <p>mode = args_t.mode</p> <p>}</p> <p>local errors_t = {'<span class="cs1-visible-error citation-comment"> <kbd>{{<a href='?title=Template%3ACite_tweet'>Cite tweet</a>}}</kbd>:'}; -- initialize sequence of error messages with style tag</p> <p>date_number_url_get (args_t, cite_args_t, errors_t); -- add |date=, |number=, |url= to <cite_args_t></p> <p>local author = ((cite_args_t.last1 and cite_args_t.first1) and cite_args_t.last1 .. ', ' .. cite_args_t.first1) or -- concatenate |last= with |first= for |author-mask=</p> <p>(cite_args_t.last1 and cite_args_t.last1) or -- only |last= for |author-mask=</p> <p>(cite_args_t.author1 and cite_args_t.author1:gsub('^%(%((.+)%)%)$', '%1')); -- |author= or |author1= stripped of accept-as-written markup for |author-mask=</p> <p>if author and args_t.user then</p> <p>cite_args_t['author-mask'] = author .. ' [@' .. (args_t.user or '') .. ']' -- concatenate <author> and |user= into |author-mask=</p> <p>elseif args_t.user then</p> <p>cite_args_t.author1 = '((' .. args_t.user .. '))'; -- just the user name for cs1|2 metadata</p> <p>cite_args_t['author-mask'] = '@' .. args_t.user; -- make a mask for display</p> <p>else -- here when neither <author> nor |user=</p> <p>cite_args_t.author1 = nil; -- so unset</p> <p>end</p> <p>local rendering = require ('Module:Citation/CS1')._citation (nil, cite_args_t, {CitationClass = 'web'});</p> <p>---------- error messaging ----------</p> <p>if errors_t[2] then -- errors_t[2] nil when no errors</p> <p>if rendering:find ('cs1-visible-error', 1, true) then -- rendered {{cite web}} with errors will have this string</p> <p>errors_t[1] = errors_t[1]:gsub ('> <', '>; <'); -- insert semicolon to terminate cs1|2 error message string</p> <p>end</p> <p>errors_t[#errors_t] = errors_t[#errors_t]:gsub (';$',' (<a href='?title=Template%3ACite_tweet%23Error_detection'>help</a>)'); -- replace trailing semicolon with help link</p> <p>table.insert (errors_t, '</span>'); -- close style span tag</p> <p>if mw.title.getCurrentTitle():inNamespace (0) then -- mainspace only</p> <p>table.insert (errors_t, '<a href='?title=Category%3ACite_tweet_templates_with_errors'>Category:Cite tweet templates with errors</a>'); -- add error category</p> <p>end</p> <p>rendering = rendering .. table.concat (errors_t); -- append error messaging, help links and catagories</p> <p>end</p> <p>return rendering;</p> <p>end</p> <p>--[[--------------------------< E X P O R T S >----------------------------------------------------------------</p> <p>]]</p> <p>return {</p> <p>main = main,</p> <p>-- temporary; there are {{#invoke:cite tweet||...}} invokes in article space that need</p> <p>[''] = main, -- to be changed to {{#invoke:cite tweet|main|...}} before this export can be removed</p> <p>}</p></div></section></div></main> <footer class="site-footer"> <div class="footer-container"> <div class="footer-links"> <a href="/about.php">About</a> <a href="/help.php">Help</a> <a href="/updates.php">Updates</a> <a href="/contact.php">Contact</a> <a href="/privacy.php">Privacy</a> <a href="/terms.php">Terms</a> <a href="https://github.com/yourusername/friendly-wiki" target="_blank" rel="noopener">GitHub</a> </div> <div class="footer-copy"> © 2025 Friendly Wiki. All rights reserved. </div> </div> </footer> <script> const toggle = document.getElementById('mobileMenuToggle'); const menu = document.getElementById('mobileMenu'); toggle.addEventListener('click', () => { menu.classList.toggle('active'); }); </script> <!-- Collapsible toggle --> <script> document.addEventListener("DOMContentLoaded", function () { const toggles = document.querySelectorAll('.section-toggle'); toggles.forEach(toggle => { toggle.addEventListener('click', function () { const section = toggle.closest('.collapsible'); const body = section.querySelector('.wiki-body'); body.classList.toggle('collapsed'); }); }); }); </script>