Skip to content

Instantly share code, notes, and snippets.

@ninjastic
Last active May 21, 2023 08:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ninjastic/d560848568339e5a2d7a25ddffb578d5 to your computer and use it in GitHub Desktop.
Save ninjastic/d560848568339e5a2d7a25ddffb578d5 to your computer and use it in GitHub Desktop.
Fixes bugged bbcode cauised by an older version of the Imgur to TalkImg script
(async () => {
// options
const startPage = 1
const basePostHistoryUrl = 'https://bitcointalk.org/index.php?action=profile;sa=showPosts'
const decoder = new TextDecoder('windows-1252')
const parser = new DOMParser()
let lastReq
const tags = ['img', 'b', 'size', 'pre', 'left', 'center', 'right', 'url', 'i', 'u', 's', 'btc', 'glow', 'shadow', 'hr', 'font', 'color', 'flash', 'email', 'ftp', 'table', 'tr', 'td', 'sup', 'sub', 'tt', 'code', 'quote', 'list', 'li']
const tagsRegexText = tags.map(tag => tag).join('|')
const tagsRegex = new RegExp(`\\[\\b(${tagsRegexText})\\b(=.*?)?(\\s.*?)?\\]`, 'g')
let emojiRegex = await fetch('https://proxy.ninjastic.space/?url=https://raw.githubusercontent.com/mathiasbynens/emoji-test-regex-pattern/main/dist/latest/javascript.txt').then(response => response.status === 200 ? response.text() : null)
if (!emojiRegex) {
console.log('%cERROR: Could not fetch Emoji regex', 'color: red; font-weight: bold;')
return
}
const encodeStr = (rawStr) => {
return rawStr.replace(new RegExp(`${emojiRegex}|[\u00A0-\u9999<>&]`, 'g'), (i) => `&#${i.codePointAt(0)};`)
}
const fetchThrottled = async (url, ...rest) => {
const timeRemaining = lastReq ? lastReq.getTime() + 1000 * 1 - new Date().getTime() : 0
if (timeRemaining > 0) {
await new Promise(resolve => setTimeout(resolve, timeRemaining))
}
lastReq = new Date()
return await fetch(url, ...rest)
}
const checkTopicLocked = async (topicId) => {
const url = `https://bitcointalk.org/index.php?action=post;topic=${topicId}`
const html = await fetchThrottled(url).then(async response => decoder.decode(await response.arrayBuffer()))
const $ = parser.parseFromString(html, 'text/html')
if ($.title === 'An Error Has Occurred!') {
return $.querySelector('#bodyarea > div:nth-child(1) > table > tbody > tr.windowbg > td')?.textContent.trim()
}
return undefined
}
const getSesc = async () => {
const html = await fetchThrottled('https://bitcointalk.org/more.php').then(async response => decoder.decode(await response.arrayBuffer()))
return html.match(/https\:\/\/bitcointalk\.org\/index\.php\?action=logout;sesc=(.*?)"\>/)?.at(1)
}
const getQuote = async ({ topicId, postId, sesc }) => {
const url = `https://bitcointalk.org/index.php?action=quotefast;xml;quote=${postId};topic=${topicId};sesc=${sesc}`
const html = await fetchThrottled(url).then(async response => decoder.decode(await response.arrayBuffer()))
const $ = parser.parseFromString(html, 'text/html')
const quote = $.querySelector('quote').textContent
return quote.replace(/^\[quote.*?\]\n?/, '').replace(/\[\/quote\]$/, '')
}
const editPost = async ({ topicId, postId, title, message, sesc }) => {
const formData = new FormData()
formData.append('topic', String(topicId))
formData.append('subject', encodeStr(title))
formData.append('message', encodeStr(message))
formData.append('sc', sesc)
formData.append('goback', String(1))
const { redirected } = await fetchThrottled(`https://bitcointalk.org/index.php?action=post2;msg=${postId}`, { method: 'POST', body: formData })
return redirected
}
const removeElements = (parent, elementSelectors) => {
for (const elementSelector of elementSelectors) {
for (const element of [...parent.querySelectorAll(elementSelector)]) {
element.remove()
}
}
}
const getPosts = async (page) => {
const url = `${basePostHistoryUrl};start=${((page ?? 1) - 1) * 20}`
const html = await fetchThrottled(url).then(async response => decoder.decode(await response.arrayBuffer()))
const $ = parser.parseFromString(html, 'text/html')
const postElements = [...$.querySelectorAll('table[width="85%"] table[width="100%"] tbody')]
.filter(element => element.querySelector('.post'))
const posts = []
for (const postElement of postElements) {
const titleElement = postElement.querySelector('tr[class=titlebg2] td:nth-child(2) a:last-child')
const title = titleElement.textContent.trim()
const [, topicId, postId] = titleElement.getAttribute('href').match(/topic=(\d+)\.msg(\d+)/)
const contentElement = postElement.querySelector('.post')
removeElements(contentElement, ['.code'])
const hasBuggedBbcode = contentElement.innerHTML.match(tagsRegex)
posts.push({ topicId, postId, title, hasBuggedBbcode })
}
return posts.filter(post => post.hasBuggedBbcode)
}
const html = await fetchThrottled(basePostHistoryUrl).then(async response => response.text())
const $ = parser.parseFromString(html, 'text/html')
const pages = [...$.querySelectorAll('.navPages[href*="sa=showPosts"]')]
.filter(element => !['«', '»'].includes(element.textContent))
.reduce((elements, currentElement) => {
if(!elements.find(element => element.href === currentElement.getAttribute('href'))) {
elements.push({ element: currentElement, page: Number(currentElement.textContent), href: currentElement.getAttribute('href') })
return elements
}
return elements
}, [])
const lastPageNum = pages[pages.length - 1].page ?? 1
console.log('%cAutomatically fix your broken posts (sorry!)', 'color: #fff; font-weight: bold; background-color: blue;')
console.log('Number of Pages:', lastPageNum)
if (startPage > lastPageNum) {
console.log('%cERROR: startPage is greater than your number of pages, please check.', 'color: red; font-weight: bold;')
return
}
for await (const page of Array.from({ length: lastPageNum - startPage + 1 }).map((_, i) => startPage + i)) {
console.log(`--------------------\nGetting posts on page ${page}/${lastPageNum} (${Math.floor(page / lastPageNum * 100)}%)`)
const posts = await getPosts(page)
if (posts.length > 0) {
console.log(`Found ${posts.length} posts`, posts)
}
for await (const post of posts) {
const topicError = await checkTopicLocked(post.topicId)
if (topicError) {
console.log(`[${post.postId}] Skipping post because topic returned error:`, topicError)
continue
}
const sesc = await getSesc()
const currPost = await getQuote({ topicId: post.topicId, postId: post.postId, sesc })
console.log(`[${post.postId}] Editing/refreshing post https://bitcointalk.org/index.php?topic=${post.topicId}.msg${post.postId}#msg${post.postId}`)
const edited = await editPost({ topicId: post.topicId, postId: post.postId, title: post.title, message: currPost, sesc })
if (!edited) {
console.log(`[${post.postId}] Could not edit post (maybe locked?)...`)
}
}
}
console.log('-- Finished! --')
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment