forked from felixonmars/dnsmasq-china-list
updater & find_redundant: rewrite in ruby
This commit is contained in:
parent
a11f555167
commit
b3e5feb64b
|
@ -32,7 +32,7 @@ ENV.fetch("JOBS", "1").to_i.times.each do
|
||||||
|
|
||||||
if v.check_domain_verbose(domain, enable_cdnlist: false, show_green: true)
|
if v.check_domain_verbose(domain, enable_cdnlist: false, show_green: true)
|
||||||
Filelock '.git.lock' do
|
Filelock '.git.lock' do
|
||||||
puts `./updater.py -a #{domain}`
|
puts `./updater.rb -a #{domain}`
|
||||||
puts `git commit -S -am "accelerated-domains: add #{domain}"` if $?.success?
|
puts `git commit -S -am "accelerated-domains: add #{domain}"` if $?.success?
|
||||||
puts `./update-local` if $?.success?
|
puts `./update-local` if $?.success?
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
''' Find redundant items in accelerated-domains.china.conf.
|
|
||||||
e.g. 'bar.foo.com' is redundant for 'foo.com'.
|
|
||||||
'''
|
|
||||||
|
|
||||||
|
|
||||||
from collections.abc import Iterable
|
|
||||||
|
|
||||||
|
|
||||||
def load(conf_file):
|
|
||||||
''' Parse conf file & Prepare data structure
|
|
||||||
Returns: [ ['abc', 'com'],
|
|
||||||
['bar', 'foo', 'com'],
|
|
||||||
... ]
|
|
||||||
'''
|
|
||||||
|
|
||||||
results = []
|
|
||||||
if isinstance(conf_file, str):
|
|
||||||
lines = open(conf_file, 'r').readlines()
|
|
||||||
elif isinstance(conf_file, Iterable):
|
|
||||||
lines = iter(conf_file)
|
|
||||||
for line in lines:
|
|
||||||
line = line.strip()
|
|
||||||
if line == '' or line.startswith('#'):
|
|
||||||
continue
|
|
||||||
# A domain name is case-insensitive and
|
|
||||||
# consists of several labels, separated by a full stop
|
|
||||||
domain_name = line.split('/')[1]
|
|
||||||
domain_name = domain_name.lower()
|
|
||||||
domain_labels = domain_name.split('.')
|
|
||||||
results.append(domain_labels)
|
|
||||||
|
|
||||||
# Sort results by domain labels' length
|
|
||||||
results.sort(key=len)
|
|
||||||
return results
|
|
||||||
|
|
||||||
|
|
||||||
def find(labelses):
|
|
||||||
''' Find redundant items by a tree of top-level domain label to sub-level.
|
|
||||||
`tree` is like { 'com': { 'foo: { 'bar': LEAF },
|
|
||||||
'abc': LEAF },
|
|
||||||
'org': ... }
|
|
||||||
'''
|
|
||||||
redundant = []
|
|
||||||
tree = {}
|
|
||||||
LEAF = 1
|
|
||||||
for labels in labelses:
|
|
||||||
domain = '.'.join(labels)
|
|
||||||
# Init root node as current node
|
|
||||||
node = tree
|
|
||||||
while len(labels) > 0:
|
|
||||||
label = labels.pop()
|
|
||||||
if label in node:
|
|
||||||
# If child node is a LEAF node,
|
|
||||||
# current domain must be an existed domain or a subdomain of an existed.
|
|
||||||
if node[label] == LEAF:
|
|
||||||
print(f"Redundant found: {domain} at {'.'.join(labels)}")
|
|
||||||
redundant.append(domain)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
# Create a leaf node if current label is last one
|
|
||||||
if len(labels) == 0:
|
|
||||||
node[label] = LEAF
|
|
||||||
# Create a branch node
|
|
||||||
else:
|
|
||||||
node[label] = {}
|
|
||||||
# Iterate to child node
|
|
||||||
node = node[label]
|
|
||||||
return redundant
|
|
||||||
|
|
||||||
|
|
||||||
def find_redundant(conf_file):
|
|
||||||
return find(load(conf_file))
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
find_redundant('accelerated-domains.china.conf')
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
#!/usr/bin/ruby
|
||||||
|
|
||||||
|
""" Find redundant items in accelerated-domains.china.conf.
|
||||||
|
e.g. 'bar.foo.com' is redundant for 'foo.com'.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def load(conf_file)
|
||||||
|
""" Parse conf file & Prepare data structure
|
||||||
|
Returns: [ ['abc', 'com'],
|
||||||
|
['bar', 'foo', 'com'],
|
||||||
|
... ]
|
||||||
|
"""
|
||||||
|
|
||||||
|
results = []
|
||||||
|
if conf_file.is_a? String
|
||||||
|
lines = File.readlines(conf_file)
|
||||||
|
elsif conf_file.is_a? Array
|
||||||
|
lines = conf_file
|
||||||
|
end
|
||||||
|
lines.map do |line|
|
||||||
|
line = line.chomp
|
||||||
|
next if line.empty? or line.start_with?('#')
|
||||||
|
# A domain name is case-insensitive and
|
||||||
|
# consists of several labels, separated by a full stop
|
||||||
|
domain_name = line.split('/')[1]
|
||||||
|
domain_name = domain_name.downcase
|
||||||
|
domain_labels = domain_name.split('.')
|
||||||
|
results << domain_labels
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sort results by domain labels' length
|
||||||
|
results.sort_by(&:length)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
LEAF = 1
|
||||||
|
def find(labelses)
|
||||||
|
""" Find redundant items by a tree of top-level domain label to sub-level.
|
||||||
|
`tree` is like { 'com': { 'foo: { 'bar': LEAF },
|
||||||
|
'abc': LEAF },
|
||||||
|
'org': ... }
|
||||||
|
"""
|
||||||
|
redundant = []
|
||||||
|
tree = {}
|
||||||
|
labelses.each do |labels|
|
||||||
|
domain = labels.join('.')
|
||||||
|
# Init root node as current node
|
||||||
|
node = tree
|
||||||
|
until labels.empty?
|
||||||
|
label = labels.pop
|
||||||
|
if node.include? label
|
||||||
|
# If child node is a LEAF node,
|
||||||
|
# current domain must be an existed domain or a subdomain of an existed.
|
||||||
|
if node[label] == LEAF
|
||||||
|
puts "Redundant found: #{domain} at #{labels.join('.')}"
|
||||||
|
redundant << domain
|
||||||
|
break
|
||||||
|
end
|
||||||
|
else
|
||||||
|
# Create a leaf node if current label is last one
|
||||||
|
if labels.empty?
|
||||||
|
node[label] = LEAF
|
||||||
|
# Create a branch node
|
||||||
|
else
|
||||||
|
node[label] = {}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
# Iterate to child node
|
||||||
|
node = node[label]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
redundant
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_redundant(conf_file)
|
||||||
|
return find(load(conf_file))
|
||||||
|
end
|
||||||
|
|
||||||
|
if __FILE__ == $0
|
||||||
|
find_redundant('accelerated-domains.china.conf')
|
||||||
|
end
|
83
updater.py
83
updater.py
|
@ -1,83 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from argparse import ArgumentParser
|
|
||||||
import idna
|
|
||||||
import sys
|
|
||||||
import find_redundant
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
parser = ArgumentParser(description="dnsmasq-china-list updater")
|
|
||||||
parser.add_argument(
|
|
||||||
'-a', '--add',
|
|
||||||
metavar="DOMAIN",
|
|
||||||
nargs="+",
|
|
||||||
help='Add one or more new domain(s) (implies -s)',
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
'-d', '--delete',
|
|
||||||
metavar="DOMAIN",
|
|
||||||
nargs="+",
|
|
||||||
default=[],
|
|
||||||
help='Remove one or more old domain(s) (implies -s)',
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
'-s', '--sort',
|
|
||||||
action='store_true',
|
|
||||||
default=True,
|
|
||||||
help='Sort the list (default action)',
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
'-f', '--file',
|
|
||||||
nargs=1,
|
|
||||||
default=["accelerated-domains.china.conf"],
|
|
||||||
help="Specify the file to update (accelerated-domains.china.conf by default)",
|
|
||||||
)
|
|
||||||
|
|
||||||
options = parser.parse_args()
|
|
||||||
|
|
||||||
with open(options.file[0]) as f:
|
|
||||||
lines = list(f)
|
|
||||||
|
|
||||||
changed = False
|
|
||||||
|
|
||||||
if options.add:
|
|
||||||
options.sort = True
|
|
||||||
|
|
||||||
for domain in options.add:
|
|
||||||
encoded_domain = idna.encode(domain).decode()
|
|
||||||
new_line = f"server=/{encoded_domain}/114.114.114.114\n"
|
|
||||||
disabled_line = f"#server=/{encoded_domain}/114.114.114.114"
|
|
||||||
if new_line in lines:
|
|
||||||
print(f"Domain already exists: {domain}")
|
|
||||||
else:
|
|
||||||
for line in lines:
|
|
||||||
if line.startswith(disabled_line):
|
|
||||||
print(f"Domain already disabled: {domain}")
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
print(f"New domain added: {domain}")
|
|
||||||
lines.append(new_line)
|
|
||||||
changed = True
|
|
||||||
|
|
||||||
options.delete += find_redundant.find_redundant(lines)
|
|
||||||
|
|
||||||
if options.delete:
|
|
||||||
options.sort = True
|
|
||||||
|
|
||||||
for domain in options.delete:
|
|
||||||
target_line = f"server=/{idna.encode(domain).decode()}/114.114.114.114\n"
|
|
||||||
if target_line not in lines:
|
|
||||||
print(f"Failed to remove domain {domain}: not found.")
|
|
||||||
else:
|
|
||||||
print(f"Domain removed: {domain}")
|
|
||||||
lines.remove(target_line)
|
|
||||||
changed = True
|
|
||||||
|
|
||||||
if (options.add or options.delete) and not changed:
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if options.sort:
|
|
||||||
lines.sort(key=lambda x: x.lstrip("#"))
|
|
||||||
|
|
||||||
with open(options.file[0], "w") as f:
|
|
||||||
f.write(''.join(filter(lambda line: line.strip(), lines)))
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
#!/usr/bin/ruby
|
||||||
|
require 'domain_name'
|
||||||
|
require 'optparse'
|
||||||
|
require 'ostruct'
|
||||||
|
|
||||||
|
options = OpenStruct.new
|
||||||
|
options.sort = true
|
||||||
|
options.file = "accelerated-domains.china.conf"
|
||||||
|
options.add = []
|
||||||
|
options.delete = []
|
||||||
|
OptionParser.new do |opts|
|
||||||
|
opts.banner = "dnsmasq-china-list updater"
|
||||||
|
|
||||||
|
opts.on("-s", "--[no-]sort", "Sort the list (default action)") do |s|
|
||||||
|
options.sort = s
|
||||||
|
end
|
||||||
|
|
||||||
|
opts.on("-f", "--file FILE", "Specify the file to update (accelerated-domains.china.conf by default)") do |f|
|
||||||
|
options.file = f
|
||||||
|
end
|
||||||
|
|
||||||
|
opts.on("-a", "--add domain1,domain2", Array, "Add domain(s) to the list (implies -s)") do |a|
|
||||||
|
options.add = a
|
||||||
|
options.sort = true
|
||||||
|
end
|
||||||
|
|
||||||
|
opts.on("-d", "--delete domain1,domain2", Array, "Remove domain(s) from the list (implies -s)") do |d|
|
||||||
|
options.delete = d
|
||||||
|
options.sort = true
|
||||||
|
end
|
||||||
|
end.parse!
|
||||||
|
|
||||||
|
lines = File.readlines(options.file).filter { |line| !line.empty? }
|
||||||
|
disabled_lines = lines.filter { |line| line.start_with?("#") }
|
||||||
|
|
||||||
|
changed = false
|
||||||
|
|
||||||
|
options.add.each do |domain|
|
||||||
|
domain = DomainName.normalize(domain)
|
||||||
|
new_line = "server=/#{domain}/114.114.114.114\n"
|
||||||
|
disabled_line = "#server=/#{domain}/114.114.114.114"
|
||||||
|
if lines.include? new_line
|
||||||
|
puts "Domain already exists: #{domain}"
|
||||||
|
else
|
||||||
|
if disabled_lines.any? { |line| line.start_with? disabled_line }
|
||||||
|
puts "Domain already disabled: #{domain}"
|
||||||
|
else
|
||||||
|
# Check for duplicates
|
||||||
|
test_domain = domain
|
||||||
|
while test_domain.include? '.'
|
||||||
|
test_domain = test_domain.partition('.').last
|
||||||
|
_new_line = "server=/#{test_domain}/114.114.114.114\n"
|
||||||
|
_disabled_line = "#server=/#{test_domain}/114.114.114.114"
|
||||||
|
if lines.include? _new_line
|
||||||
|
puts "Redundant domain already exists: #{test_domain}"
|
||||||
|
break
|
||||||
|
elsif disabled_lines.any? { |line| line.start_with? _disabled_line }
|
||||||
|
puts "Redundant domain already disabled: #{test_domain}"
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
next if test_domain.include? '.'
|
||||||
|
|
||||||
|
puts "New domain added: #{domain}"
|
||||||
|
lines << new_line
|
||||||
|
changed = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
options.delete.each do |domain|
|
||||||
|
domain = DomainName.normalize(domain)
|
||||||
|
target_line = "server=/#{domain}/114.114.114.114\n"
|
||||||
|
unless lines.include? target_line
|
||||||
|
puts "Failed to remove domain #{domain}: not found."
|
||||||
|
else
|
||||||
|
puts "Domain removed: #{domain}"
|
||||||
|
lines.delete(target_line)
|
||||||
|
changed = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fail "No changes made." if (options.add.length || options.delete.length) && !changed
|
||||||
|
|
||||||
|
lines.sort_by! { |x| x.delete_prefix("#") } if options.sort
|
||||||
|
|
||||||
|
File.write(options.file, lines.join)
|
Loading…
Reference in New Issue