Skip to content

Instantly share code, notes, and snippets.

@natchiketa
Last active January 14, 2019 22:39
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 natchiketa/3734665f593b86cebd700fd30251eb81 to your computer and use it in GitHub Desktop.
Save natchiketa/3734665f593b86cebd700fd30251eb81 to your computer and use it in GitHub Desktop.
Ruby script to find the nearest ~50 NordVPN servers with a low load. Requires (free) API key from ipstack.com
#!/usr/bin/env ruby
require 'net/http'
require 'json'
# SET THIS TO YOUR LOCATION (an easy way is to go to maps.google.com and click the map and copy
# paste from the new URL)
MY_LATLNG = [ YOUR_LAT, YOUR_LONG ]
# GET A FREE API KEY FROM ipstack.com
IPSTACK_API_KEY = 'YOUR_IPSTACK_API_KEY'
class ProgressBar
def initialize(units=60)
@units = units.to_f
end
def print(completed, total)
norm = 1.0 / (total / @units)
progress = (completed * norm).ceil
pending = @units - progress
Kernel.print "[#{'=' * progress }#{' ' * ( pending )}] #{percentage(completed, total)}%\r"
end
def percentage(completed, total)
( ( completed / total.to_f ) * 100 ).round
end
end
def get(url, country_code = 'us')
uri = URI(url)
response = Net::HTTP.get(uri)
response = JSON.parse(response)
end
def distance loc1, loc2
rad_per_deg = Math::PI/180 # PI / 180
rkm = 6371 # Earth radius in kilometers
rm = rkm * 1000 # Radius in meters
dlat_rad = (loc2[0]-loc1[0]) * rad_per_deg # Delta, converted to rad
dlon_rad = (loc2[1]-loc1[1]) * rad_per_deg
lat1_rad, lon1_rad = loc1.map {|i| i * rad_per_deg }
lat2_rad, lon2_rad = loc2.map {|i| i * rad_per_deg }
a = Math.sin(dlat_rad/2)**2 + Math.cos(lat1_rad) * Math.cos(lat2_rad) * Math.sin(dlon_rad/2)**2
c = 2 * Math::atan2(Math::sqrt(a), Math::sqrt(1-a))
rm * c # Delta in meters
end
def table_columns(data, label_hash)
label_hash.each_with_object({}) do |(col, label), h|
h[col] = {
label: label,
width: [data.map { |g| "#{g[col]}".size }.max, label.size].max
}
end
end
def write_header(columns)
puts "| #{ columns.map { |_,g| g[:label].ljust(g[:width]) }.join(' | ') } |"
end
def write_divider(columns)
puts "+-#{ columns.map { |_,g| "-"*g[:width] }.join("-+-") }-+"
end
def write_line(columns, line)
str = line.keys.map { |k| "#{line[k]}".ljust(columns[k][:width]) }.join(" | ")
puts "| #{str} |"
end
servers = get 'https://nordvpn.com/api/server/stats'
servers.select!{ |k, v| k.is_a?(String) && k.start_with?('us') && v["percent"] && v["percent"] < 10 }
server_names = []
servers.each { |k, v| server_names << { :host => k, :percent => v["percent"] } }
server_names = server_names.sort_by {|s| s[:percent] }
# server_names = server_names.slice(0, )
# EXAMPLE RESPONSE
# {
# "ip":"155.94.183.5",
# "type":"ipv4",
# "continent_code":"NA",
# "continent_name":"North America",
# "country_code":"US",
# "country_name":"United States",
# "region_code":"CA",
# "region_name":"California",
# "city":"Los Angeles",
# "zip":"90014",
# "latitude":34.0494,
# "longitude":-118.2641,
# "location":{
# "geoname_id":5368361,
# "capital":"Washington D.C.",
# "languages":[
# {
# "code":"en",
# "name":"English",
# "native":"English"
# }
# ],
# "country_flag":"http://assets.ipstack.com/flags/us.svg",
# "country_flag_emoji":"🇺🇸",
# "country_flag_emoji_unicode":"U+1F1FA U+1F1F8",
# "calling_code":"1",
# "is_eu":false
# }
# }
total_server_count = server_names.length
progress_bar = ProgressBar.new
server_names = server_names.each_with_index.map do |s, i|
progress_bar.print(i, total_server_count)
json = get "http://api.ipstack.com/#{s[:host]}?access_key=#{IPSTACK_API_KEY}"
location = "#{json["city"]}, #{json["region_code"]}"
dist_mi = (distance(MY_LATLNG, [json["latitude"], json["longitude"]]) * 0.000621371).round rescue '-'
{
key: s[:host].scan(/us([0-9]+).*/)[0][0],
host: s[:host],
location: location,
distance: dist_mi,
percent: "#{s[:percent]}%"
}
end
server_names.reject!{|s| s[:distance] == '-'}
server_names = server_names.sort_by {|s| s[:distance] }
headers = { key: "Key", host: "Server", location: "Location", distance: "Distance", percent: "Health" }
columns = table_columns(server_names, headers)
write_divider(columns)
write_header(columns)
write_divider(columns)
server_names.each { |s| write_line(columns, s) }
write_divider(columns)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment