discord-new-music (5684B)
1 #!/usr/bin/env ruby 2 # Script that uses selenium-webdriver to go through unread messages in a 3 # server's channels, extract any links, and download them. Then marks those 4 # messages as read. 5 # Useful for downloading new music posted to channels. 6 require 'selenium-webdriver' 7 require 'json' 8 9 # Check for username 10 if ARGV.length != 2 11 warn 'Please pass the email and server as arguments' 12 exit 1 13 end 14 15 # Check if vault unlocked 16 puts "Retrieving auth info" 17 unless system('rbw unlocked') 18 warn 'Unlock bitwarden to continue' 19 exit 1 20 end 21 22 # Get auth details 23 user, server_name = ARGV 24 pass = `rbw get discord.com #{user}` 25 26 # Start the browser & load discord 27 puts "Starting Discord" 28 driver = Selenium::WebDriver.for :firefox 29 driver.manage.timeouts.implicit_wait = 10 # seconds 30 driver.get "https://discord.com/login" 31 32 # Sign in 33 puts "Logging in" 34 email_input = driver.find_element(name: "email", type: "text") 35 email_input.send_keys user 36 password_input = driver.find_element(name: "password", type: "password") 37 password_input.send_keys pass 38 email_input.submit 39 40 # Find the server and open it 41 puts "Loading server" 42 discord_server = driver.find_element(:xpath, "//div[contains(@aria-label, '#{server_name}')]") 43 3.times { driver.find_element(tag_name: 'body').send_keys :escape; sleep 1 } 44 discord_server.click 45 46 # Close all of the MOTHERFUCKING CUTESY popups that discord loves 47 puts "Closing all popups because fuck you Discord" 48 blocking_elements = driver.find_elements(:xpath, "//div[contains(@class, 'focusLock') and @aria-modal='true']") 49 unless blocking_elements.nil? 50 blocking_elements.each { |e| e.find_element(:xpath, ".//button").click; sleep 2 } 51 end 52 popouts = driver.find_elements(:xpath, "//div[contains(@id, 'popout_')]") 53 unless popouts.nil? 54 popouts.each { |e| e.find_element(:xpath, ".//button").click; sleep 2 } 55 end 56 57 # Grab the channel list and select only unread channels 58 puts "Retrieving unread channels" 59 channel_list = driver.find_element(tag_name: "div", id: "channels") 60 unread_channels = channel_list.find_elements(:xpath, ".//a[contains(@aria-label, 'unread')]") 61 puts "#{unread_channels.length} unread channels: #{unread_channels.map(&:text).to_s}" 62 63 64 fname = "new-music-#{Time.now.to_i}.json" 65 66 begin 67 all_new_messages = {} 68 unread_channels.each do |chan| 69 puts "Processing #{chan.text}" 70 # Select the channel 71 chan.click 72 73 # Find the new message separator and all following (new) messages 74 puts "|- finding new messages" 75 begin 76 new_messages_bar = driver.find_element(:xpath, "//div[contains(@id, 'new-messages-bar')]") 77 rescue Selenium::WebDriver::Error::NoSuchElementError 78 warn '|- could not find new messages bar' 79 next 80 end 81 82 begin 83 new_messages = new_messages_bar.find_elements(:xpath, "following-sibling::div[contains(@id, 'chat-messages')]") 84 rescue Selenium::WebDriver::Error::NoSuchElementError 85 warn '|- could not find new messages' 86 next 87 end 88 89 # This will be all music sent in the channel 90 music_sent = [] 91 92 # Go through new messages 93 puts "|- looping through new messages" 94 new_messages.each do |m| 95 # Try to find links in the message 96 begin 97 sent_links_elements = m.find_elements(:xpath, ".//div[contains(@class, 'messageContent')]/a") 98 links = sent_links_elements.map { |l| l.attribute('href') } 99 rescue Selenium::WebDriver::Error::NoSuchElementError 100 links = [] 101 end 102 103 # If there's at least one link 104 unless links.empty? 105 # Try to see who sent it 106 begin 107 author_name_element = m.find_element(:xpath, ".//span[contains(@class, 'username')]") 108 author_name = author_name_element.text 109 rescue Selenium::WebDriver::Error::NoSuchElementError 110 author_name = '' 111 end 112 # Add the link 113 music_sent << { from: author_name, links: links } 114 end 115 end 116 117 # If music was sent 118 unless music_sent.empty? 119 # Find the channel name 120 puts "|- links found" 121 begin 122 channel_name = chan.find_element(:xpath, ".//div[contains(@class, 'channelName')]").text 123 rescue Selenium::WebDriver::Error::NoSuchElementError 124 channel_name = Time.now.to_i.to_s 125 warn "|- could not find channel name, saving as #{channel_name}" 126 end 127 128 # Save the sent music under the channel name 129 all_new_messages[channel_name] = music_sent 130 131 # Mark the channel as read 132 puts "|- marking as read" 133 begin 134 button_mark_as_read = driver.find_element(:xpath, "//main[contains(@class, 'chatContent')]//button[@type='button' and contains(text(), 'Mark as read')]") 135 button_mark_as_read.click 136 sleep 3 137 rescue Selenium::WebDriver::Error::NoSuchElementError 138 warn "|- could not mark #{channel_name} as read" 139 end 140 end 141 end 142 143 if all_new_messages.empty? 144 puts "No new music!" 145 else 146 puts "Writing result to #{fname}" 147 File.write(fname, JSON.dump(all_new_messages)) 148 end 149 rescue => e 150 puts e.message 151 end 152 153 driver.quit 154 155 if File.exists? fname 156 print "Download all with youtube-dl? " 157 conf = $stdin.gets.chomp 158 if conf[0].downcase == "y" 159 if system("command -v youtube-dl >/dev/null 2>&1") 160 new_music = JSON.parse File.read(fname) 161 new_music.each do |channel_name, messages| 162 chan = channel_name.chars.select { |c| c =~ /[a-z]/ }.join 163 Dir.mkdir(chan) unless Dir.exists? chan 164 messages.each do |m| 165 m['links'].each do |l| 166 system(%Q{cd "#{chan}" && youtube-dl -ciw -f bestaudio -x "#{l}"}) 167 end 168 end 169 end 170 else 171 warn 'No youtube-dl present, not downloading' 172 end 173 else 174 puts "Not downloading" 175 end 176 end