Violets-Purgatory/pageUpdater.js

571 lines
22 KiB
JavaScript
Raw Normal View History

2024-02-08 12:30:38 -06:00
const path = require('path'),
2024-02-12 08:46:49 -06:00
fs = require('fs'),
2024-05-28 18:33:32 -05:00
minify = require('@node-minify/core'),
uglifyJs = require("@node-minify/uglify-js"),
htmlMinifier = require("minify-html"),
2024-09-30 21:34:39 -05:00
cssMinifier = require("clean-css"),
2024-05-28 18:33:32 -05:00
activityToHTML = require("./overcomplicatedStatuses.js"),
2024-05-31 01:05:50 -05:00
randomThemer = require("./randomThemer.js"),
himalaya = require("himalaya"),
2024-06-25 01:02:28 -05:00
glob = require("glob"),
2024-06-29 16:19:02 -05:00
api = require("./api.js"),
2024-08-19 02:03:11 -05:00
child_process = require("child_process")
2024-02-08 12:30:38 -06:00
var constants = JSON.parse(fs.readFileSync(path.join(__dirname, 'constants.json')))
2024-02-08 17:52:26 -06:00
var highlightedWords = constants.highlightedWords
var quotes = constants.quotes
var titles = constants.titles
2024-02-08 12:30:38 -06:00
2024-06-26 20:11:41 -05:00
var commitCount = "600+"
2024-02-08 17:28:17 -06:00
2024-02-12 08:46:49 -06:00
var uptime = Date.now()
var lastPregen = 0
2024-02-08 16:03:40 -06:00
var pregenFiles = []
2024-06-25 18:41:15 -05:00
var javascriptCache = {}
2024-07-04 19:58:03 -05:00
var testing = process.argv[2] == "test"
var globResult = glob.globSync("**/static/**/*.html", { absolute: true })
for (var i = 0; i < globResult.length; i++) {
var result = globResult[i]
pregenFiles.push({
"absolutePath": result,
"path": result.substring(result.indexOf("static") + 7),
"html": undefined
})
}
2024-06-27 00:45:19 -05:00
(async function () {
2024-06-26 05:09:19 -05:00
globResult = glob.globSync("**/static/**/*.js", { absolute: true })
for (var i = 0; i < globResult.length; i++) {
javascriptCache[globResult[i]] = await minify({
compressor: uglifyJs,
content: fs.readFileSync(globResult[i]).toString()
})
}
})()
function firstToUpper(str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
2024-08-08 10:09:21 -05:00
function onlyIfExists(string, check) {
if (check) {
return string
} else {
return ""
}
}
2024-10-23 10:52:50 -05:00
function makeHtmlSafe(str="") {
return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
2024-04-23 09:08:04 -05:00
function timeFormatter(seconds) {
seconds = Math.ceil(seconds)
var minutes = Math.ceil(seconds / 60)
var hours = Math.floor(minutes / 60)
if (seconds <= 60) {
return 'about ' + seconds + ' seconds'
} else if (minutes < 60) {
return `${minutes} Minutes`
}
return `${hours} hours and ${minutes % 60} minutes`
}
function pathReplacer(html) {
while (html.includes("{PATH_")) {
var pagePath = html.substring(html.indexOf("{PATH_"))
pagePath = pagePath.substring(6, pagePath.indexOf('}'))
2024-05-31 01:05:50 -05:00
var stringIndex = `{PATH_${pagePath}}`
pagePath = pagePath.toLowerCase()
2024-05-31 01:05:50 -05:00
var pageHTML = fs.readFileSync(path.join(__dirname, 'static', pagePath, 'index.html')).toString()
pageHTML = pageHTML.substring(pageHTML.indexOf('<main>') + 6, pageHTML.indexOf('</main>'))
html = html.replaceAll(stringIndex, pageHTML)
}
return html
}
2024-02-18 08:01:58 -06:00
2024-08-22 08:34:14 -05:00
function highlighter(json, full = true, linkParent = false) {
for (var i = 0; i < json.length; i++) {
var element = json[i]
if (element.type == "element") {
if (element.children.length > 0) {
var valid = true
2024-07-07 23:33:16 -05:00
if (element.attributes) {
for (var x in element.attributes) {
var attribute = element.attributes[x]
2024-08-11 03:51:21 -05:00
if (attribute.key == "class" && attribute.value.includes("noHighlight")) {
2024-07-07 23:33:16 -05:00
valid = false
break
}
}
}
if (element.tagName == "code") {
valid = false
}
2024-07-07 23:33:16 -05:00
if (valid) {
2024-08-22 08:34:14 -05:00
element.children = highlighter(element.children, full, linkParent || element.tagName == "a")
2024-07-07 23:33:16 -05:00
}
}
} else if (element.type == "text") {
var index = 0
for (let i = 0; i < highlightedWords.length; i++) {
var dict = highlightedWords[i]
for (let x = 0; x < dict.words.length; x++) {
index += 1
var term = dict.words[x];
var termProps = dict
var reg = term
if (termProps.caseInsensitive) {
reg = new RegExp(`(${term})`, "gi")
}
2024-06-19 19:40:05 -05:00
element.content = element.content.replaceAll(`{${term}}`, "TEMPORARY_REPLACE")
element.content = element.content.replaceAll(reg, "{TERM" + index + "}")
element.content = element.content.replaceAll("TEMPORARY_REPLACE", `${term}`)
}
}
if (full) {
var index = 0
for (let i = 0; i < highlightedWords.length; i++) {
var dict = highlightedWords[i]
for (let x = 0; x < dict.words.length; x++) {
index += 1
var termKey = "{TERM" + index + "}"
var termProps = dict
while (element.content.includes(termKey)) {
var endRegex = " "
var termIndex = element.content.indexOf(termKey)
var spanEnd = element.content.indexOf(endRegex, termIndex)
if (spanEnd == -1) {
spanEnd = element.content.length
}
2024-06-19 19:40:05 -05:00
var spanStart = element.content.substring(0, termIndex).lastIndexOf(endRegex) + 1
2024-06-19 19:40:05 -05:00
// if (highTable[index] == "ULTRAKILL") {
// console.log(startContent, " ---- ", endContent)
// }
2024-06-19 19:40:05 -05:00
var startContent = element.content.substring(spanStart - 1, termIndex)
var endContent = element.content.substring(termIndex + termKey.length, spanEnd)
2024-06-19 19:40:05 -05:00
2024-10-02 17:10:23 -05:00
if (startContent.includes("(") && !endContent.includes(")")) {
var newSpanEnd = element.content.indexOf(")", spanStart) + 1
var newEndContent = element.content.substring(termIndex + termKey.length, newSpanEnd)
if (newEndContent.includes("<") || newEndContent.includes("TERM")) {
spanStart += 1
startContent = startContent.substring(2)
} else {
spanEnd = newSpanEnd
endContent = newEndContent
}
2024-10-02 17:10:23 -05:00
}
else if (endContent.includes(")") && !startContent.includes("(")) {
var newSpanStart = element.content.substring(0, spanStart).lastIndexOf("(")
var newStartContent = element.content.substring(newSpanStart - 1, termIndex)
if (newStartContent.includes("<") || newStartContent.includes("TERM")) {
spanEnd -= 1
endContent = endContent.substring(0, endContent.length - 1)
} else {
spanStart = newSpanStart
startContent = newStartContent
}
2024-10-02 17:10:23 -05:00
}
2024-06-25 13:39:56 -05:00
var style = termProps.style || ""
var classes = termProps.classes || ""
var link = termProps.link || ""
2024-06-19 19:40:05 -05:00
if (termProps.color) {
style += `color: ${termProps.color};`
}
2024-06-19 19:40:05 -05:00
if (termProps.italicized) {
style += "font-style: italic;"
}
2024-06-19 19:40:05 -05:00
if (termProps.outline) {
var width = 2
// style += `text-shadow: -1px -1px 0 ${termProps.outline}, 1px -1px 0 ${termProps.outline}, -1px 1px 0 ${termProps.outline}, 1px 1px 0 ${termProps.outline};`
style += `-webkit-text-stroke: 1px ${termProps.outline};`
// ^ Not in use because it looks bad :30
}
2024-06-26 20:07:02 -05:00
if (termProps.bold) {
classes += "bold"
}
2024-06-19 19:40:05 -05:00
if (style.length > 2) {
style = `style="${style}"`
}
if (classes.length > 2) {
classes = `class="${classes}"`
2024-06-27 00:45:19 -05:00
}
var stuff = (startContent + dict.words[x] + endContent).trim()
2024-09-11 15:08:42 -05:00
if (!stuff.includes("span")) {
var replacement = `<span ${style} ${classes}>${stuff}</span>`
2024-09-11 15:08:42 -05:00
if (link && !linkParent) {
replacement = `<a href="${link}">${replacement}</a>`
// console.log(replacement)
}
element.content = element.content.substring(0, spanStart) + replacement + element.content.substring(spanEnd)
} else {
element.content = element.content.replace(termKey, dict.words[x])
}
}
}
2024-09-11 15:08:42 -05:00
}
// element.content = element.content.replaceAll(termKey, replacement)
}
}
}
return json
}
2024-06-19 19:40:05 -05:00
function converter(html, dynamic = true) {
var startTime = Date.now()
var config = JSON.parse(fs.readFileSync(path.join(__dirname, 'config/config.json')))
var staticReplacers = {
"ALL_HIGHLIGHTS": () => {
var addedHTML = ""
for (var i = 0; i < highlightedWords.length; i++) {
addedHTML += highlightedWords[i].words.join(", ")
addedHTML += ", "
}
return addedHTML.substring(0, addedHTML.length - 2)
},
"BRANCH_NAME": () => {
if (process.env.BRANCH == "dev") {
return "Stable"
}
return "Beta"
},
"BRANCH_SUB": () => {
if (process.env.BRANCH == "dev") {
return ""
}
return "beta."
},
2024-05-26 15:38:47 -05:00
"COMMIT_COUNT": commitCount,
2024-02-12 08:46:49 -06:00
"QUOTE_COUNT": quotes.length,
2024-06-26 05:09:19 -05:00
"DISCORD_STATUS": () => {
if (api.lanyard) {
return `<span style="color: ${constants.discStatuses[api.lanyard.discord_status].color};" class="statusColor">${constants.discStatuses[api.lanyard.discord_status].text}</span>` +
2024-06-26 05:09:19 -05:00
`<style>.pfp { border-color: ${constants.discStatuses[api.lanyard.discord_status].color} }</style>`;
}
2024-06-26 05:09:19 -05:00
2024-06-20 14:21:13 -05:00
return "";
2024-06-26 05:09:19 -05:00
},
2024-06-26 19:18:44 -05:00
"TOPBAR": `<div id="topbar">
<h3><a class="chip" href="/">Home</a>
<a class="chip" href="/socials">Socials</a>
<a class="chip" href="/stats">Stats</a>
2024-09-10 00:29:03 -05:00
<a class="chip" href="/faq">Nerd FAQ</a>
<a class="chip" href="/blog">Blog</a></h3>
2024-06-26 19:18:44 -05:00
</div>`,
2024-06-08 18:31:34 -05:00
"CUSTOM_STATUS": () => {
if (api.lanyard && api.lanyard.custom_status) {
2024-06-27 00:13:43 -05:00
var status = api.lanyard.custom_status
2024-06-08 18:31:34 -05:00
var addedHTML = "<hr/><p>"
if (status.emoji) {
if (status.emoji.id) {
2024-06-11 22:02:08 -05:00
addedHTML += `<img src="/emojis/${status.emoji.id}" title="${status.emoji.name}" class="emoji"/>`
2024-06-19 19:40:05 -05:00
} else {
2024-06-16 15:00:23 -05:00
addedHTML += status.emoji.name + " "
2024-06-08 18:31:34 -05:00
}
}
2024-06-26 05:09:19 -05:00
2024-06-27 00:13:43 -05:00
addedHTML += makeHtmlSafe(status.text)
2024-06-08 18:31:34 -05:00
addedHTML += "</p>"
return addedHTML
}
return ""
},
"SELECTED_VIDEO": () => {
if (api.lanyard && api.lanyard.video) {
2024-09-29 16:35:38 -05:00
return `<h2><hr/>Predetermined weekly video!</h2><p>I would call it random but I actually select them manually.</p>
2024-05-31 01:05:50 -05:00
<br/>
2024-08-19 07:04:42 -05:00
<video controls="true" src="${api.lanyard.video.url}"></video>`
}
return ``
},
2024-05-28 18:33:32 -05:00
"WEATHER_MODIFIER": randomThemer.returnTheme(),
"WEATHER_TEXT": "",
2024-05-20 11:34:25 -05:00
"ANNOUNCEMENT": fs.readFileSync(path.join(__dirname, "config/announcement.html")),
2024-11-14 01:59:13 -06:00
"SOCIALS": (full) => {
if (api.lanyard && api.lanyard.socials) {
2024-06-25 01:02:28 -05:00
var socials = api.lanyard.socials
var html = `<div class="grid-container">`
2024-06-19 14:23:16 -05:00
var socialsTable = Object.keys(socials)
for (var i = 0; i < socialsTable.length; i++) {
var category = socialsTable[i]
var sites = socials[category]
var sitesTable = Object.keys(sites)
html += `<div class="grid-child"><div><h3>${category}</h3>`
for (var x = 0; x < sitesTable.length; x++) {
var siteName = sitesTable[x]
var siteData = sites[siteName]
2024-11-14 01:59:13 -06:00
if (siteData.main || full) {
html += `<a class="chip ${siteData.pref || ""}pref" ${onlyIfExists(`href="${siteData.url}"`, siteData.url)}>${siteName}: <span class="noHighlight">${siteData.name}</span></a>`
}
2024-06-19 14:23:16 -05:00
}
html += "</div></div>"
}
html += "</div>"
} else {
html = "<h2>Failed to load?!</h2><p>Uh oh</p>"
2024-06-19 14:23:16 -05:00
}
return html
},
2024-07-07 23:33:16 -05:00
"BLOG_POSTS": () => {
var addedHTML = ""
for (var i in api.blogPosts) {
var post = api.blogPosts[i]
2024-07-09 16:28:20 -05:00
if (!post.hidden) {
addedHTML +=
`<div class="post">
2024-07-09 16:28:20 -05:00
<a style="text-decoration: none;" href="./${post.folder}">
<h2>${post.title}</h2>
<p style="color: white; font-size: 1rem;">${post.desc}</p>
<p style="color: darkgray; font-size: 1rem;">Path: <span class="noHighlight">/${post.folder}/</span></p>
</a>
</div>`
}
2024-07-07 23:33:16 -05:00
}
if (!api.blogConnected) {
addedHTML += "<p>Not connected to blog server :(</p>"
} else if (addedHTML.length < 10) {
addedHTML += "<p>No blog posts found... <br>wait <br> huh ??? <br>what???????<br> how ???????????????<br> WHY ?!<br> Violet must've fucked up like. REALLY bad. <br> We're so cooked</p>"
}
return addedHTML
},
"SUBPAGE_JS": () => {
return `
<script src="../js/cash.js"></script>
<script src="../js/main.js"></script>
<script src="../js/constants.js"></script>
<script src="../js/lanyardSocket.js"></script>
<script src="../js/timeFormatter.js"></script>
`
2024-11-14 01:59:13 -06:00
},
"TEST_KEYWORD": (arg1, arg2, arg3) => {
return `<p>The arguements you inputted are "${arg1}" and "${arg2}". Wow!</p>`
2024-07-07 23:33:16 -05:00
}
2024-02-12 08:46:49 -06:00
}
2024-05-31 01:05:50 -05:00
var realtimeReplacers = {
2024-06-25 19:02:29 -05:00
"API_CONNECTED": api.connected.toString(),
2024-06-25 01:02:28 -05:00
"ACTIVITIES": activityToHTML.activitiesToHTML(api.lanyard),
2024-06-29 16:13:49 -05:00
"SPINCOUNT": api.spins.toString(),
"UPTIME": timeFormatter((Date.now() - uptime) / 1000),
2024-06-25 01:02:28 -05:00
"LAST_LANYARD": timeFormatter((Date.now() - api.lastLanyardUpdate) / 1000),
"RANDOM_TITLE": titles[Math.floor(Math.random() * titles.length)],
"RANDOM_QUOTE": quotes[Math.floor(Math.random() * quotes.length)],
"LAST_PREGEN": timeFormatter((Date.now() - lastPregen) / 1000)
}
if (dynamic) {
var replacers = realtimeReplacers
} else {
var replacers = staticReplacers
}
html = pathReplacer(html)
var rpTable = Object.keys(replacers)
for (let index = 0; index < rpTable.length; index++) {
const text = rpTable[index];
2024-11-14 01:59:13 -06:00
const braceText = `{${text}}`
if (typeof replacers[text] == "function" && replacers[text].length == 0) {
replacers[text] = replacers[text]()
}
while (html.includes(braceText)) {
var posOfKeyword = html.indexOf(braceText)
var keywordLength = braceText.length
var args = undefined
if (html.charAt(posOfKeyword + keywordLength) == "(") {
args = html.substring(posOfKeyword + keywordLength + 1)
keywordLength = args.substring(0, args.indexOf(")")).length + keywordLength + 2
args = args.substring(0, args.indexOf(")"))
args = args.split(",").map(item => item.trim())
}
var fnString = replacers[text]
if (typeof fnString == "function") {
fnString = fnString.apply(null, args)
}
if (dynamic) {
fnString = himalaya.stringify(highlighter(himalaya.parse(fnString)))
}
html = html.substring(0, posOfKeyword) + fnString + html.substring(posOfKeyword + keywordLength)
// break
2024-06-19 19:40:05 -05:00
}
}
2024-05-31 01:05:50 -05:00
2024-06-19 19:40:05 -05:00
if (!dynamic) {
if (html.includes("<body>")) {
var bodyHTML = htmlMinifier.minify(html.substring(html.indexOf("<body>") + 6, html.lastIndexOf("</body>")))
var parsedHTML = himalaya.parse(bodyHTML)
} else {
var parsedHTML = himalaya.parse(html)
}
2024-06-18 07:34:57 -05:00
2024-06-19 19:40:05 -05:00
parsedHTML = highlighter(parsedHTML)
2024-02-28 19:02:30 -06:00
2024-06-19 19:40:05 -05:00
parsedHTML = himalaya.stringify(parsedHTML)
if (html.includes("<body>")) {
parsedHTML = "<body>" + parsedHTML + "</body>"
html = html.substring(0, html.indexOf("<body>")) + parsedHTML + html.substring(html.indexOf("</body>") + 7)
} else {
html = parsedHTML
}
}
2024-06-19 19:40:05 -05:00
if (dynamic) {
html = html.replaceAll("{LOAD_TIME}", (Date.now() - startTime).toString() + "ms")
}
2024-04-23 09:08:04 -05:00
2024-02-08 12:30:38 -06:00
return html
}
2024-02-08 17:30:52 -06:00
module.exports = {
2024-03-08 10:49:47 -06:00
getActivities: function () {
2024-06-25 01:02:28 -05:00
return htmlMinifier.minify(converter(activityToHTML.activitiesToHTML(api.lanyard)))
2024-03-08 10:49:47 -06:00
},
2024-05-28 18:33:32 -05:00
middleWare: async function (req, res, next) {
2024-02-08 17:30:52 -06:00
2024-02-12 11:12:15 -06:00
var filePath = (req.baseUrl + req.path).trim()
2024-04-18 21:39:08 -05:00
if (!filePath.includes(".")) {
2024-02-12 11:12:15 -06:00
if (filePath.charAt(filePath.length - 1) != '/') {
res.redirect(filePath + '/')
return
}
filePath = path.join(filePath, '/index.html')
2024-02-08 17:30:52 -06:00
}
2024-02-12 11:12:15 -06:00
2024-02-08 17:30:52 -06:00
filePath = path.join(__dirname, 'static', filePath || 'index.html')
if (fs.existsSync(filePath)) {
var data = fs.readFileSync(filePath).toString()
2024-05-31 01:05:50 -05:00
2024-03-30 01:26:45 -05:00
res.contentType(path.basename(filePath))
if (filePath.includes(".html")) {
for (var i = 0; i < pregenFiles.length; i++) {
if (pregenFiles[i].html && pregenFiles[i].absolutePath == filePath) {
data = pregenFiles[i].html
}
}
data = converter(data, true)
// console.log(data)
2024-05-31 01:05:50 -05:00
2024-02-08 17:30:52 -06:00
}
2024-09-30 21:34:39 -05:00
if (filePath.includes(".html")) {
2024-05-28 18:33:32 -05:00
data = htmlMinifier.minify(data)
2024-09-30 21:34:39 -05:00
} else if (filePath.includes(".css")) {
2024-09-30 21:39:55 -05:00
data = new cssMinifier().minify(data).styles
2024-09-30 21:34:39 -05:00
} else if (filePath.includes(".js")) {
2024-06-25 18:41:15 -05:00
data = javascriptCache[filePath]
2024-04-20 12:15:04 -05:00
}
2024-04-20 06:50:20 -05:00
2024-03-30 01:26:45 -05:00
res.send(data)
}
2024-07-07 23:33:16 -05:00
else {
next()
2024-02-08 17:30:52 -06:00
}
2024-07-07 23:33:16 -05:00
},
converter: converter
2024-02-08 12:30:38 -06:00
}
2024-02-08 17:28:17 -06:00
async function updateCommits() {
2024-08-19 02:03:11 -05:00
// var commits = gitCommitCount()
var commits = child_process.execSync("git rev-list --count HEAD")
2024-06-29 16:19:02 -05:00
// var siteResponse = await (await fetch(`https://git.violets-purgatory.dev/bingus_violet/violets-purgatory/src/branch/${process.env.BRANCH || "origin"}`)).text()
// var commits = siteResponse.substring(0, siteResponse.indexOf("Commits"))
// commits = commits.substring(commits.lastIndexOf("<b>") + 3, commits.lastIndexOf("</b>"))
2024-06-18 03:00:44 -05:00
// ^ this works for Forgejo (basically everything i use that isnt Github, E.G. Codeberg)
2024-05-31 01:05:50 -05:00
2024-04-21 19:13:18 -05:00
// commits = commits.substring(commits.lastIndexOf(">") + 1)
// ^ This works for Github (fuck you Github)
2024-08-19 02:09:29 -05:00
commitCount = commits.toString().trim()
2024-04-17 11:21:02 -05:00
if (process.env.BRANCH == "dev") {
2024-05-16 11:55:35 -05:00
commitCount += " | Beta site"
2024-04-17 11:21:02 -05:00
}
2024-02-08 17:28:17 -06:00
}
2024-02-12 08:46:49 -06:00
updateCommits()
// Lanyard Stuffs
2024-06-20 14:21:13 -05:00
function pregenerate() {
lastPregen = Date.now()
2024-06-20 14:21:13 -05:00
for (var i = 0; i < pregenFiles.length; i++) {
var startTime = Date.now()
pregenFiles[i].html = converter(fs.readFileSync(pregenFiles[i].absolutePath).toString(), false)
pregenFiles[i].html = pregenFiles[i].html.replaceAll("{PREGEN_TIME}", Date.now() - startTime)
}
2024-06-27 00:55:38 -05:00
for (var i = 0; i < pregenFiles.length; i++) {
pregenFiles[i].html = pregenFiles[i].html.replaceAll("{PREGEN_TOTAL}", Date.now() - lastPregen)
}
2024-06-20 14:21:13 -05:00
}
pregenerate()
api.events.on("lanyardConnect", pregenerate)
2024-07-07 23:33:16 -05:00
api.events.on("blogUpdate", pregenerate)
api.events.on("lanyardUpdate", async () => {
if (!api.lanyard.activityChanged) {
pregenerate()
}
2024-06-26 05:09:19 -05:00
2024-06-25 01:02:28 -05:00
for (var i = 0; i < api.lanyard.activities.length; i++) {
var activity = api.lanyard.activities[i]
if (activity.type == 4 && activity.emoji) {
2024-02-12 08:46:49 -06:00
2024-06-25 01:02:28 -05:00
if (activity.emoji.id) {
if (activity.emoji.animated) {
var emoji = Buffer.from(await (await fetch(`https://cdn.discordapp.com/emojis/${activity.emoji.id}.gif?quality=lossless`)).arrayBuffer())
} else {
var emoji = Buffer.from(await (await fetch(`https://cdn.discordapp.com/emojis/${activity.emoji.id}.png?quality=lossless`)).arrayBuffer())
2024-06-08 18:31:34 -05:00
}
2024-06-25 01:02:28 -05:00
fs.writeFileSync(path.join(__dirname, "cached/emojis", activity.emoji.id), emoji)
2024-06-08 18:31:34 -05:00
}
2024-02-12 08:46:49 -06:00
}
2024-06-25 01:02:28 -05:00
}
})