Skip to content

Instantly share code, notes, and snippets.

@honeyedoasis
Last active February 5, 2025 04:32
Show Gist options
  • Select an option

  • Save honeyedoasis/5cc83a1e75426b803eb0d26b17e6d361 to your computer and use it in GitHub Desktop.

Select an option

Save honeyedoasis/5cc83a1e75426b803eb0d26b17e6d361 to your computer and use it in GitHub Desktop.
Download Weverse DM

How to use

Video tutorial: https://www.youtube.com/watch?v=GSbwYbfVE6c

Make sure your browser's download folder is empty, downloading all these media can flood your download folder with files and it'll be annoying to move later.

  1. Login weverse
  2. Open member's DM
  3. Open development console (on chrome or firefox press F12)
  4. Navigate to the console tab
  5. Where you can type text (next to the >), copy the step1-scroll_top.js code into here and hit enter to run it
  6. Wait until it's done, takes about 5-10 min if you have lots of messages (an alert will pop up when it thinks it's done)
  7. Double check that it actually scrolled to the top, often it fails. Run the code again if it didn't work.
  8. Now copy and paste the code to download the DM media and text log step2-download.js (easier to copy by clicking the RAW button)
  9. If it successfully finished, there will be the media and 2 files in your download folder dm-log.txt and dm-log.tsv

Using the log txt file

If you want to translate the weverse dm messages, copy and paste the dm-log.txt content into a google drive document.

Use chrome's translate feature (right click on the webpage and click Translate to English) to translate the messages.

Extra info for using the dm-log.tsv file

The header for this csv file is in the format

  • DATE, TIME, TEXT, YOUR_TEXT, IMAGE, VIDEO, AUDIO

IMAGE, VIDEO and AUDIO are comma seperated strings which contain the name of the media file.

TEXT and YOUR_TEXT are using \v for the quote character since these can be multiline

async function scroll_top()
{
scroll = document.getElementsByClassName("DirectMessageRoomContainerView_scroll_content__W5LKA hidable_scrollbar_content")[0]
let last_height = 0
// keep trying to scroll every 500ms
let handle = setInterval(function()
{
scroll.scrollIntoView()
}, 500);
// if the scroll height hasn't changed for 10s, we are done
while (true)
{
last_height = scroll.scrollHeight
await new Promise(resolve => setTimeout(resolve, 10000));
if (scroll.scrollHeight == last_height)
{
clearInterval(handle);
alert('Finished scrolling')
return Promise.resolve("Finished")
}
}
}
scroll_top()
skip_user_msgs = false
skip_media = false
day = ''
out_data = []
async function wait_for_next_second()
{
let now = new Date()
let ms = now.getMilliseconds();
// if we are about to tick over to the next second, then wait a little bit
if (ms >= 950)
{
return new Promise(resolve => setTimeout(resolve("wait", 125)))
}
}
function get_expected_name_date(now, ext)
{
formattedDate = `${now.getFullYear()}${(now.getMonth() + 1).toString().padStart(2, '0')}${now.getDate().toString().padStart(2, '0')}${now.getHours().toString().padStart(2, '0')}${now.getMinutes().toString().padStart(2, '0')}${now.getSeconds().toString().padStart(2, '0')}`;
return 'weverse__' + formattedDate + ext;
}
function get_first_class(e, c)
{
return e.getElementsByClassName(c)[0]
}
function get_msg_text(e)
{
text_content = msg.getElementsByClassName("DirectMessageBodyTextView_text__wYswD")[0]
if (text_content == null)
{
text_content = msg.getElementsByClassName("DirectMessageBodyTextView_emoji__OE+ib")[0]
}
if (text_content)
{
return text_content.textContent
}
return ""
}
async function download_msgs()
{
msg_area = document.getElementsByClassName('DirectMessageRoomView_message_area__Ky585')[0]
for (msg of msg_area.children)
{
more_btn = get_first_class(msg, 'DirectMessageBodyTextView_more_button__yiw2o')
if (more_btn)
{
msg.scrollIntoView();
await new Promise(resolve => setTimeout(resolve, 100));
more_btn.click();
await new Promise(resolve => setTimeout(resolve, 100));
}
}
let total_num = msg_area.children.length
let msg_index = 0;
for (msg of msg_area.children)
{
console.log(`${msg_index}/${msg_area.children.length}`)
await process_msg(msg);
msg_index += 1
}
// for (r of out_data)
// {
// console.log(r)
// }
export_tsv('out.tsv', out_data)
alert('🍀 fromis_9 🍀 flover 🍀 forever 🍀')
}
async function process_msg(msg)
{
msg.scrollIntoView();
await new Promise(resolve => setTimeout(resolve, 20));
// read day
if (msg.classList.contains("DirectMessageLineDivderView_divider_text_wrap__ua6da"))
{
day = msg.textContent;
return Promise.resolve("TEXT")
}
// otherwise it should be a msg
is_artist_msg = msg.classList.contains("DirectMessageItemView_-artist__mk8Wp")
if (skip_user_msgs && !is_artist_msg)
{
return Promise.resolve("TEXT")
}
time = ''
time_elem = get_first_class(msg, 'DirectMessageItemView_time__ChpKR');
if (time_elem != null) time = time_elem.textContent
text = ''
img_btn = get_first_class(msg, "DirectMessageBodyImageView_image_button__C53xl")
audio_btn = get_first_class(msg, "DirectMessageBodyAudioView_viewer_button__3LRyj")
video_btn = get_first_class(msg, "DirectMessageBodyVideoView_play_button__WOFmB")
if (img_btn)
{
return skip_media ? Promise.resolve("skip") : await down_img(img_btn, day, time)
}
else if (audio_btn)
{
return skip_media ? Promise.resolve("skip") : await down_audio(audio_btn, day, time)
}
else if (video_btn)
{
return skip_media ? Promise.resolve("skip") : await down_video(video_btn, day, time)
}
else
{
down_text(msg, day, time, is_artist_msg)
}
// return new Promise(resolve => setTimeout(resolve("Testing", 100))
return Promise.resolve("TEXT")
// return new Promise(resolve => {
// resolve("Testing");
// });
}
// flicking-arrow-next
async function down_img(btn, day, time)
{
btn.click();
await new Promise(resolve => setTimeout(resolve, 750));
return new Promise(resolve =>
{
setTimeout(async function()
{
let had_next_arrow = false
let images = []
for (i = 0; i < 20; i++)
{
let next_arrow = get_first_class(document, "flicking-arrow-next")
let is_gif = false;
let img_content = get_first_class(document, 'ImageViewerView_image_content__aWEYs');
if (img_content != null)
{
is_gif = img_content.getAttribute('src').includes('.gif');
}
await wait_for_next_second()
let now = new Date();
let down_btn = document.getElementsByClassName("ImageViewerView_download_button__9ipF7")[0];
down_btn.click()
let will_instant_download = next_arrow == null && !had_next_arrow
if (will_instant_download)
{
// console.log('click down')
images.push(`${get_expected_name_date(now, is_gif ? '.gif' : '.jpg')}`)
}
else
{
had_next_arrow = true
}
if (had_next_arrow) // click the advanced arrow to download
{
await new Promise(resolve => setTimeout(resolve, 1000));
let down_btn_advanced = document.getElementsByClassName("ImageViewerView_layer_button__3VBzE")
// console.log(down_btn_advanced.length)
if (down_btn_advanced.length === 2)
{
// console.log('click advanced')
await wait_for_next_second()
let now = new Date();
down_btn_advanced[1].click()
images.push(`${get_expected_name_date(now, is_gif ? '.gif' : '.jpg')}`)
}
}
if (next_arrow == null)
{
if (images.length > 0)
{
let image_text = `${images.join(',')}`
// row = [day, time, text]
let row = { date: day, time: time, image: image_text}
out_data.push(row)
}
let close_btn = document.getElementsByClassName("ImageViewerView_close_button__9LG7D")[0]
close_btn.click()
resolve("Testing")
break;
}
else
{
// wait after clicking the next arrow
next_arrow.click()
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
}, 1000)
})
// return new Promise(resolve => setTimeout(resolve("Testing", 100))
}
async function down_audio(btn, day, time)
{
btn.click();
return new Promise(resolve =>
{
setTimeout(async function()
{
let down_btn = get_first_class(document,"ImageViewerView_download_button__9ipF7");
await wait_for_next_second()
let now = new Date();
down_btn.click()
let text = `${get_expected_name_date(now,'.mp4')}`
let row = { date: day, time: time, audio: text}
out_data.push(row)
let close_btn = get_first_class(document,"ImageViewerView_close_button__9LG7D")
close_btn.click()
resolve("Testing")
}, 2000)
})
}
async function down_video(btn, day, time)
{
btn.click();
return new Promise(resolve =>
{
setTimeout(async function()
{
let down_btn = get_first_class(document,"ImageViewerView_download_button__9ipF7");
await wait_for_next_second()
let now = new Date();
down_btn.click()
let text = `${get_expected_name_date(now,'.mp4')}`
let row = { date: day, time: time, video: text}
out_data.push(row)
let close_btn = get_first_class(document,"ImageViewerView_close_button__9LG7D")
close_btn.click()
resolve("Testing")
}, 2000)
})
}
function down_text(msg, day, time, is_artist_msg)
{
text = get_msg_text(msg)
if (text.length == 0)
{
return;
}
// console.log(`${day} ${time}: ${text}`)
if (is_artist_msg)
{
let row = {date: day, time: time, text: text}
out_data.push(row)
}
else
{
let row = {date: day, time: time, your_text: text}
out_data.push(row)
}
}
function get_row_str(row, simple)
{
if (simple)
{
let extra = ''
if (row.image) extra = `Sent image ${row.image}`
else if (row.video) extra = `Sent video ${row.video}`
else if (row.audio) extra = `Sent audio ${row.audio}`
let text = extra
let has_your_text = row.your_text != null
if (row.your_text) // its your text
{
text = row.your_text
}
else
{
text = row.text ? row.text : '';
}
let optional_tab = has_your_text ? '\t' : '';
return `${optional_tab}${row.date} ${row.time}: ${text}`
}
let temp_list = []
temp_list[0] = row.date
temp_list[1] = row.time
temp_list[2] = row.text ? `\v${row.text}\v` : ''
temp_list[3] = row.your_text ? `\v${row.your_text}\v` : ''
temp_list[4] = row.image ? row.image : ''
temp_list[5] = row.video ? row.video : ''
temp_list[6] = row.audio ? row.audio : ''
return temp_list.join('\t')
}
function save_to_file(filename, csv_file)
{
let blob = new Blob([csv_file], { type: 'text/tsv;charset=utf-8;' });
if (navigator.msSaveBlob) { // IE 10+
navigator.msSaveBlob(blob, filename);
}
else
{
let link = document.createElement("a");
if (link.download !== undefined) { // feature detection
// Browsers that support HTML5 download attribute
let url = URL.createObjectURL(blob);
link.setAttribute("href", url);
link.setAttribute("download", filename);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
}
function export_tsv(filename, rows)
{
let csv_file = '';
for (let i = 0; i < rows.length; i++)
{
// csvFile += processRow(rows[i]);
// console.log(rows[i])
csv_file += get_row_str(rows[i], false) + '\n';
}
save_to_file('dm-log.tsv', csv_file);
let simple_file = '';
for (let i = 0; i < rows.length; i++)
{
// csvFile += processRow(rows[i]);
// console.log(rows[i])
simple_file += get_row_str(rows[i], true) + '\n';
}
save_to_file('dm-log.txt', simple_file);
}
await download_msgs()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment