remi-domain-finder 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/COPYING +661 -0
- data/NOTES +41 -0
- data/README +19 -0
- data/Rakefile +9 -0
- data/bin/domain-finder +4 -0
- data/domain-finder.gemspec +22 -0
- data/lib/domain-finder.rb +6 -0
- data/lib/domain-finder/bin.rb +113 -0
- data/lib/domain-finder/daterange.rb +25 -0
- data/lib/domain-finder/domain.rb +9 -0
- data/lib/domain-finder/domaintools.rb +8 -0
- data/lib/domain-finder/google.rb +53 -0
- data/lib/domain-finder/moniker.rb +17 -0
- data/lib/domain-finder/secure_post.rb +12 -0
- data/spec/daterange_spec.rb +35 -0
- data/spec/domaintools_spec.rb +15 -0
- data/spec/google_spec.rb +54 -0
- data/spec/html/domaintools-1.html +1353 -0
- data/spec/html/google-1.html +4 -0
- data/spec/html/moniker-1.html +1607 -0
- data/spec/moniker_spec.rb +24 -0
- data/spec/spec_helper.rb +1 -0
- metadata +100 -0
data/NOTES
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
TODO
|
2
|
+
|
3
|
+
put all classes within DomainFinder namespace to not class with other libraries
|
4
|
+
|
5
|
+
turn into gem (github-compatible)
|
6
|
+
|
7
|
+
make a domain-finder console with some nice and easy built in commands for checking domains/etc
|
8
|
+
|
9
|
+
make it check and eval a .domainfinderrc or _domainfinderrc for easily overriding anything
|
10
|
+
like the default domain-finder command and whatever else
|
11
|
+
|
12
|
+
make domain-auction (shortcut to domain-finder auction)
|
13
|
+
|
14
|
+
make a shoes-based GUI specific to the auction functionality ... ./bin/domain-auction (maybe?)
|
15
|
+
|
16
|
+
NOTES
|
17
|
+
|
18
|
+
Auction Date google query examples:
|
19
|
+
|
20
|
+
"Auction Date" 01-15-2008 intitle:club site:whois.domaintools.com
|
21
|
+
intitle optional
|
22
|
+
|
23
|
+
"date should be atleast 2 weeks ago" (so it'll be in the cache, probably?)
|
24
|
+
|
25
|
+
expected usages ...
|
26
|
+
|
27
|
+
domain-finder auction
|
28
|
+
domain-finder auction --date 01-01-2008
|
29
|
+
domain-finder auction --date 01-01-2008..01-05-2008
|
30
|
+
domain-finder auction --date 01-01-2008 --keyword blog
|
31
|
+
domain-finder auction --date 01-01-2008 --keyword blog,seo
|
32
|
+
domain-finder auction --date 01-01-2008,01-04-2008..01-05-2008 --keyword blog,seo
|
33
|
+
|
34
|
+
Google usage ...
|
35
|
+
|
36
|
+
Google.search %{"Auction Date" blah ...}, :page => 2 should return an array of GoogleResult objects
|
37
|
+
|
38
|
+
Google.search_url(....).should == "http://q=....."
|
39
|
+
Google.fetch_url(...).should == string
|
40
|
+
Google.parse_results(str).should == []
|
41
|
+
Google.parse_result(hpricot thing).should == GoogleResult
|
data/README
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
domain-finder
|
2
|
+
=============
|
3
|
+
|
4
|
+
domain-finder is a little utility for checking the availability
|
5
|
+
of domains and, specifically, for showing some recently used but
|
6
|
+
no longer registered domain names, using a technique developed
|
7
|
+
by Mark Fulton of dotsauce.com
|
8
|
+
|
9
|
+
|
10
|
+
See NOTES for current TODO and future ideal example usage
|
11
|
+
|
12
|
+
|
13
|
+
License
|
14
|
+
-------
|
15
|
+
|
16
|
+
domain-finder is released under the terms of the GNU Affero General Public License
|
17
|
+
|
18
|
+
We reserve the right to re-release under a more permissive license in the future,
|
19
|
+
which will likely be the LGPL, allowing for library use by proprietery applications
|
data/Rakefile
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
desc "Generate and view specdoc"
|
2
|
+
task :specdoc do
|
3
|
+
system "spec -f specdoc spec/*_spec.rb" # kills BASH colors :(
|
4
|
+
end
|
5
|
+
|
6
|
+
desc "Generate and view HTML specdoc"
|
7
|
+
task :spechtml do
|
8
|
+
system "spec -f html spec/*_spec.rb > tmp/specdoc.html; firefox tmp/specdoc.html"
|
9
|
+
end
|
data/bin/domain-finder
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "domain-finder"
|
3
|
+
s.version = "0.1.0"
|
4
|
+
s.date = "2008-06-14"
|
5
|
+
s.summary = "Find available domains names"
|
6
|
+
s.email = "remi@remitaylor.com"
|
7
|
+
s.homepage = "http://github.com/remi/domain-finder"
|
8
|
+
s.description = "Find some previously used domain names and check availability"
|
9
|
+
s.has_rdoc = true
|
10
|
+
s.rdoc_options = ["--quiet", "--title", "domain-finder", "--opname", "index.html", "--line-numbers", "--main", "README", "--inline-source"]
|
11
|
+
s.extra_rdoc_files = ['README']
|
12
|
+
s.authors = ["remi Taylor"]
|
13
|
+
|
14
|
+
# generate using: $ ruby -e "puts Dir['**/**'].select{|x| File.file?x}.inspect"
|
15
|
+
s.files = ["bin/domain-finder", "COPYING", "lib/domain-finder.rb", "lib/domain-finder/bin.rb", "lib/domain-finder/domaintools.rb", "lib/domain-finder/google.rb", "lib/domain-finder/daterange.rb", "lib/domain-finder/moniker.rb", "lib/domain-finder/domain.rb", "lib/domain-finder/secure_post.rb", "domain-finder.gemspec", "Rakefile", "NOTES", "README", "tmp/specdoc.html", "spec/spec_helper.rb", "spec/google_spec.rb", "spec/moniker_spec.rb", "spec/daterange_spec.rb", "spec/html/google-1.html", "spec/html/moniker-1.html", "spec/html/domaintools-1.html", "spec/domaintools_spec.rb"]
|
16
|
+
|
17
|
+
s.add_dependency("remi-simplecli", ["> 0.0.0"])
|
18
|
+
s.add_dependency("hpricot", ["> 0.0.0"])
|
19
|
+
|
20
|
+
s.executables = ["domain-finder"]
|
21
|
+
s.default_executable = "domain-finder"
|
22
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'simplecli'
|
3
|
+
|
4
|
+
class DomainFinder::Bin
|
5
|
+
include SimpleCLI
|
6
|
+
|
7
|
+
def usage *args
|
8
|
+
puts <<doco
|
9
|
+
|
10
|
+
domain-finder == %{ domain availability utility }
|
11
|
+
|
12
|
+
Usage:
|
13
|
+
domain-finder -h/--help [Not Implemented Yet]
|
14
|
+
domain-finder -v/--version [Not Implemented Yet]
|
15
|
+
domain-finder command [options]
|
16
|
+
|
17
|
+
Examples:
|
18
|
+
domain-finder auction
|
19
|
+
|
20
|
+
Further help:
|
21
|
+
domain-finder commands # list all available commands
|
22
|
+
domain-finder help <COMMAND> # show help for COMMAND
|
23
|
+
domain-finder help # show this help message
|
24
|
+
|
25
|
+
doco
|
26
|
+
end
|
27
|
+
|
28
|
+
def available_help
|
29
|
+
<<doco
|
30
|
+
Usage: #{ script_name } available domain.com[ domain2.com]
|
31
|
+
|
32
|
+
Summary:
|
33
|
+
Check the availability of domains (max 500)
|
34
|
+
doco
|
35
|
+
end
|
36
|
+
def available *domains
|
37
|
+
available_domains = Domain.available? *domains
|
38
|
+
|
39
|
+
unless available_domains.empty?
|
40
|
+
puts "\nAvailable Domains:\n------------------"
|
41
|
+
available_domains.sort.each do |domain|
|
42
|
+
puts "- #{domain}"
|
43
|
+
end
|
44
|
+
else
|
45
|
+
puts "No Domains Available"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def auction_help
|
50
|
+
<<doco
|
51
|
+
Usage: #{ script_name } auction
|
52
|
+
|
53
|
+
Options:
|
54
|
+
-d, --dates Set of dates to use in search
|
55
|
+
-k, --keywords Comma-delimited keywords for domain titles
|
56
|
+
-p, --pages Number of Google pages to gather domains from
|
57
|
+
-f, --file Name of a text file to save available domain names to
|
58
|
+
|
59
|
+
Examples:
|
60
|
+
#{ script_name } auction --dates 05-01-2008,06-10-2008..06.15.2008
|
61
|
+
#{ script_name } auction --keywords seo,blog --dates 06-01-2008..07-01-2008
|
62
|
+
#{ script_name } auction -f domains.txt --keywords cat,dog -p 2
|
63
|
+
|
64
|
+
Summary:
|
65
|
+
Display available auction domains
|
66
|
+
doco
|
67
|
+
end
|
68
|
+
def auction *args
|
69
|
+
begin
|
70
|
+
options = { :dates => [Date.today - 14], :pages => 1 }
|
71
|
+
opts = OptionParser.new do |opts|
|
72
|
+
opts.on('-d','--dates DATE'){ |d| options[:dates] = DateRange.parse(d) }
|
73
|
+
opts.on('-k','--keywords WORDS'){ |w| options[:keywords] = w.split(',') }
|
74
|
+
opts.on('-p','--pages PAGES'){ |p| options[:pages] = p.to_i }
|
75
|
+
opts.on('-f','--file FILE'){ |f| options[:file] = f }
|
76
|
+
end
|
77
|
+
opts.parse! args
|
78
|
+
|
79
|
+
query = %{"Auction Date" #{ options[:dates].join(' OR ') } site:whois.domaintools.com }
|
80
|
+
query += options[:keywords].map { |k| "intitle:#{k}" }.join(' OR ') if options[:keywords]
|
81
|
+
|
82
|
+
puts "Searching google ... [#{query}]"
|
83
|
+
results = Google.search(query, :pages => options[:pages] )
|
84
|
+
|
85
|
+
print "Getting domains ."
|
86
|
+
auction_domains = results.inject([]) do |all,result|
|
87
|
+
if result.cached
|
88
|
+
print '.'
|
89
|
+
all + DomainTools.auction_domains( open(result.cached) )
|
90
|
+
else
|
91
|
+
all # no google cache for this result, continue on to the next result
|
92
|
+
end
|
93
|
+
end
|
94
|
+
print "\n"
|
95
|
+
|
96
|
+
puts "Checking availability of #{ auction_domains.length } domains ..."
|
97
|
+
available_domains = Domain.available? *auction_domains
|
98
|
+
|
99
|
+
unless available_domains.empty?
|
100
|
+
puts "\nAvailable Domains:\n------------------"
|
101
|
+
available_domains.sort.each do |domain|
|
102
|
+
puts "- #{domain}"
|
103
|
+
end
|
104
|
+
File.open(options[:file],'w'){|f| f << available_domains.sort.join("\n") } if options[:file]
|
105
|
+
else
|
106
|
+
puts "No Domains Available"
|
107
|
+
end
|
108
|
+
rescue => ex
|
109
|
+
puts "Exception: #{ex.inspect}"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
# Change default Date#to_s format
|
4
|
+
class Date
|
5
|
+
def to_s
|
6
|
+
self.strftime '%m-%d-%Y'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# Simple class for helping to parse string sets of dates and date ranges
|
11
|
+
class DateRange
|
12
|
+
|
13
|
+
def self.parse text
|
14
|
+
dates = text.gsub('-','/').split(',')
|
15
|
+
dates.inject([]) do |all,this|
|
16
|
+
if this.include?'..'
|
17
|
+
range_dates = this.split('..')
|
18
|
+
all + (Date.parse(range_dates.first)..Date.parse(range_dates.last)).to_a
|
19
|
+
else
|
20
|
+
all << Date.parse(this)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Simple class for searching google and getting results
|
2
|
+
class Google
|
3
|
+
|
4
|
+
BASE_SEARCH_URL = "http://www.google.com/search?q="
|
5
|
+
|
6
|
+
def self.search_url query, options = {}
|
7
|
+
url = BASE_SEARCH_URL + URI.encode(query)
|
8
|
+
url += "&start=#{ (options[:page] - 1) * 10 }" if options[:page] and options[:page] != 1
|
9
|
+
url
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.fetch_url url
|
13
|
+
open(url).read
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.parse_results html
|
17
|
+
( Hpricot(html) / 'div.g' ).map { |result| Result.new(result) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.search query, options = {}
|
21
|
+
if options[:pages] and options[:pages] > 1
|
22
|
+
results = []
|
23
|
+
options[:pages].times do |i|
|
24
|
+
results += parse_results( fetch_url(search_url(query, :page => i+1)) )
|
25
|
+
end
|
26
|
+
results
|
27
|
+
else
|
28
|
+
parse_results( fetch_url(search_url(query)) )
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Simple class representing a single Google result
|
33
|
+
class Result
|
34
|
+
attr_accessor :title, :url, :snippet, :cached
|
35
|
+
|
36
|
+
def initialize result
|
37
|
+
@title = ( result / 'h2.r' ).inner_text
|
38
|
+
@url = ( result / 'h2.r a' )[0]['href']
|
39
|
+
|
40
|
+
cached_element = ( result / 'div.std nobr a' )[0]
|
41
|
+
@cached = cached_element['href'] if cached_element and cached_element.inner_text == 'Cached'
|
42
|
+
|
43
|
+
snippet_element = ( result / 'div.std' )[0]
|
44
|
+
if snippet_element
|
45
|
+
@snippet = ""
|
46
|
+
snippet_element.children.each do |this|
|
47
|
+
break if this.to_s == '<br />'
|
48
|
+
@snippet << this.inner_text
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Simple class for batch searching domain availability
|
2
|
+
class Moniker
|
3
|
+
|
4
|
+
INTERNATIONAL_TEXT = "Select language Chinese Afrikaans Albanian Arabic Aragonese Armenian Assamese Asturian Avestan Awadhi Azerbaijani Balinese Baluchi Basa Bashkir Basque Belarusian Bengali Bhojpuri Bosnian Bulgarian Burmese Carib Catalan Chechen Chinese Chuvash Coptic Corsican Croatian Czech Danish Divehi Dogri Dutch English Estonian Fijian Finnish French Frisian Gaelic Georgian German Gondi Greek Gujarati Hebrew Hindi Hungarian Icelandic Indic Indonesian Ingush Irish Italian Japanese Javanese Kashmiri Kazakh Khmer Kirghiz Korean Kurdish Lao Latvian Lithuanian Luxembourgish Macedonian Malay Malayalam Maltese Maori Moldavian Nepali Norwegian Oriya Ossetian Panjabi Persian Polish Portuguese Pushto Rajasthani Romanian Russian Samoan Sanskrit Sardinian Serbian Sindhi Sinhalese Slovak Slovenian Somali Spanish Swahili Swedish Syriac Tajik Tamil Telugu Thai Tibetan Turkish Ukrainian Urdu Uzbek Vietnamese Welsh Yiddish"
|
5
|
+
|
6
|
+
def self.find_available *domains
|
7
|
+
data = { :cmd => 'check', :scope => 'batch', :domnamelist => domains.join("\n") }
|
8
|
+
parse_results SecurePost.post('https://www.moniker.com/pub/DomainCheckBatch', data)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.parse_results html
|
12
|
+
( Hpricot(html) / "td[@style='font-family: verdana; font-size: 8pt;']").map do
|
13
|
+
|td| td.inner_text.gsub('?','').sub(INTERNATIONAL_TEXT,'').strip
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# Simple class for helping to easily make a secure POST without messing with Net::HTTP
|
2
|
+
class SecurePost
|
3
|
+
|
4
|
+
def self.post url, data = {}
|
5
|
+
uri = URI.parse url
|
6
|
+
http = Net::HTTP.new uri.host, 443
|
7
|
+
http.use_ssl = true
|
8
|
+
resp, data = http.post(uri.path, data.map{ |k,v| "#{k}=#{v}" }.join('&'), {})
|
9
|
+
data
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe DateRange do
|
4
|
+
|
5
|
+
it '.parse should parse a single date' do
|
6
|
+
DateRange.parse("01-31-2008").first.should == Date.parse("01/31/2008")
|
7
|
+
DateRange.parse("01/31/2008").first.should == Date.parse("01/31/2008")
|
8
|
+
end
|
9
|
+
|
10
|
+
it '.parse should parse comma delimited dates' do
|
11
|
+
dates = DateRange.parse("01-15-2008,01-31-2008")
|
12
|
+
dates.length.should == 2
|
13
|
+
dates.first.should == Date.parse("01/15/2008")
|
14
|
+
dates.last.should == Date.parse("01/31/2008")
|
15
|
+
end
|
16
|
+
|
17
|
+
it '.parse should parse a date range' do
|
18
|
+
dates = DateRange.parse("01-10-2008..01-14-2008")
|
19
|
+
dates.length.should == 5
|
20
|
+
dates.first.should == Date.parse("01/10/2008")
|
21
|
+
dates[1].should == Date.parse("01/11/2008")
|
22
|
+
dates.last.should == Date.parse("01/14/2008")
|
23
|
+
end
|
24
|
+
|
25
|
+
it '.parse should parse comma delimited dates and date ranges' do
|
26
|
+
dates = DateRange.parse("01-10-2008..01-14-2008,02-01-2008,06-15-2005,12-20-1000..12-24-1000")
|
27
|
+
dates.length.should == 12
|
28
|
+
dates.first.should == Date.parse("01/10/2008")
|
29
|
+
dates.last.should == Date.parse("12/24/1000")
|
30
|
+
%w( 01/13/2008 02/01/2008 06/15/2005 12/21/1000 ).each do |date|
|
31
|
+
dates.should include(Date.parse(date))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe DomainTools do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
@example_html = File.read(File.dirname(__FILE__) + "/html/domaintools-1.html")
|
7
|
+
end
|
8
|
+
|
9
|
+
it '.auction_domains should return Domains at Auction' do
|
10
|
+
domains = DomainTools.auction_domains(@example_html)
|
11
|
+
domains.length.should == 13
|
12
|
+
domains.should include("EcoRus.net")
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
data/spec/google_spec.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe Google do
|
4
|
+
|
5
|
+
before(:all)do
|
6
|
+
@example_query = %{site:whois.domaintools.com "Auction Date" 06-01-2008}
|
7
|
+
@example_url = %{http://www.google.com/search?q=site:whois.domaintools.com%20%22Auction%20Date%22%2006-01-2008}
|
8
|
+
@example_html = File.read(File.dirname(__FILE__) + "/html/google-1.html")
|
9
|
+
end
|
10
|
+
|
11
|
+
it '.search_url should generate an escaped search url' do
|
12
|
+
Google.search_url(@example_query).should == @example_url
|
13
|
+
end
|
14
|
+
|
15
|
+
it '.search_url should support pagination' do
|
16
|
+
Google.search_url(@example_query, :page => 1 ).should == @example_url
|
17
|
+
Google.search_url(@example_query, :page => 2 ).should == @example_url + '&start=10'
|
18
|
+
Google.search_url(@example_query, :page => 10).should == @example_url + '&start=90'
|
19
|
+
end
|
20
|
+
|
21
|
+
it '.fetch_url should fetch the results of a full url' do
|
22
|
+
Google.should_receive(:open).with(@example_url).and_return(StringIO.new(@example_html))
|
23
|
+
Google.fetch_url(@example_url).should == @example_html
|
24
|
+
end
|
25
|
+
|
26
|
+
it '.parse_results should return array of GoogleResult objects' do
|
27
|
+
results = Google.parse_results @example_html
|
28
|
+
results.length.should == 10
|
29
|
+
first = results.first
|
30
|
+
|
31
|
+
first.title.should == "Auctionrus.com - Auction Rus"
|
32
|
+
first.url.should == "http://whois.domaintools.com/auctionrus.com"
|
33
|
+
first.cached.should == %{http://209.85.173.104/search?q=cache:f7qbaIH-UTEJ:whois.domaintools.com/auctionrus.com+site:whois.domaintools.com+%22Auction+Date%22+06-01-2008&hl=en&ct=clnk&cd=1&gl=us&ie=UTF-8}
|
34
|
+
first.snippet.should == "Domain, Auction Date. EcoRus.net, 06-01-2008. PlusRus.com, 06-01-2008. Exclusiv-Rus.com, 06-01-2008. SermonsRus.com, 06-01-2008. HaUroRus.com, 06-01-2008 ..."
|
35
|
+
end
|
36
|
+
|
37
|
+
it '.search should return GoogleResult objects for a query' do
|
38
|
+
Google.should_receive(:open).with(@example_url).and_return(StringIO.new(@example_html))
|
39
|
+
results = Google.search(@example_query)
|
40
|
+
results.length.should == 10
|
41
|
+
results.first.should be_a_kind_of(Google::Result)
|
42
|
+
end
|
43
|
+
|
44
|
+
it '.search should support pagination' do
|
45
|
+
Google.should_receive(:open).with(@example_url).and_return(StringIO.new(@example_html))
|
46
|
+
Google.should_receive(:open).with(@example_url + '&start=10').and_return(StringIO.new(@example_html))
|
47
|
+
Google.should_receive(:open).with(@example_url + '&start=20').and_return(StringIO.new(@example_html))
|
48
|
+
|
49
|
+
results = Google.search(@example_query, :pages => 3)
|
50
|
+
results.length.should == 30
|
51
|
+
results.first.should be_a_kind_of(Google::Result)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|