dotfiles

My personal shell configs and stuff
git clone git://git.alex.balgavy.eu/dotfiles.git
Log | Files | Refs | Submodules | README | LICENSE

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