diff --git a/run.py b/run.py index 553d3e9..df8eb7e 100755 --- a/run.py +++ b/run.py @@ -7,14 +7,21 @@ import argparse import json import time +import os +import tempfile -import ip from dns import alidns, dnspod +from util import ip +from util.cache import Cache DNS = dnspod +CACHE_FILE = os.path.join(tempfile.gettempdir(), 'ddns.cache') def get_config(key=None, default=None, file="config.json"): + """ + 读取配置 + """ if not hasattr(get_config, "config"): try: with open(file) as configfile: @@ -27,31 +34,38 @@ def get_config(key=None, default=None, file="config.json"): return get_config.config -def update(): - print "=" * 25 + " " + time.ctime() + " " + "=" * 25 - index4 = get_config('index4') or "default" - if str(index4).isdigit(): - ipv4 = ip.local_v4(index4) +def update_ip(Type, cache): + """ + 更新IP + """ + ipname = 'ipv' + Type + domains = get_config(ipname) + if not domains: + return None + + index = get_config('index' + Type) or "default" + if str(index).isdigit(): + value = getattr(ip, "local_v" + Type)(index) + else: - ipv4 = getattr(ip, index4 + "_v4")() - print 'update ipv4 to:', ipv4 - if ipv4 != None: - for domain in get_config('ipv4'): - print DNS.update_record(domain, ipv4, 'A') + value = getattr(ip, index + "_v" + Type)() - v6_domains = get_config("ipv6") or "default" - if len(v6_domains) > 0: - index6 = get_config('index6') - if str(index6).isdigit(): - ipv6 = ip.local_v6(index6) - else: - ipv6 = getattr(ip, index6 + "_v6")() - print 'update ipv6 to:', ipv6 - if ipv6 != None: - for domain in v6_domains: - print DNS.update_record(domain, ipv6, 'AAAA') + if value is None: + return False + elif value == cache[ipname]: + print '.', + else: + cache[ipname] = value + print 'update %s to: %s' % (ipname, value) + record_type = (Type == '4') and 'A' or 'AAAA' + for domain in domains: + print DNS.update_record(domain, value, record_type=record_type) -if __name__ == '__main__': + +def main(): + """ + 更新 + """ parser = argparse.ArgumentParser() parser.add_argument('-c', default="config.json") get_config(file=parser.parse_args().c) @@ -60,4 +74,14 @@ if __name__ == '__main__': DNS.ID, DNS.TOKEN = get_config('id'), get_config('token') DNS.PROXY = get_config('proxy') ip.DEBUG = get_config('debug') - update() + + cache = Cache(CACHE_FILE) + if len(cache) < 1: + print "=" * 25 + " " + time.ctime() + " " + "=" * 25 + + update_ip('4', cache) + update_ip('6', cache) + + +if __name__ == '__main__': + main() diff --git a/util/__init__.py b/util/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/util/cache.py b/util/cache.py new file mode 100644 index 0000000..8dde6bc --- /dev/null +++ b/util/cache.py @@ -0,0 +1,139 @@ +# -*- coding: utf-8 -*- +r""" +cache module +文件缓存 +""" + +import logging as LOG +import os +import pickle +import time + +try: + from collections.abc import MutableMapping +except ImportError: + # Python 2 imports + from collections import MutableMapping + + +class Cache(MutableMapping): + """ + using file to Cache data as dictionary + """ + + def __init__(self, path, sync=False): + self.__data = {} + self.__filename = path + self.__sync = sync + self.__changed = False + self.load() + + @property + def time(self): + """ + 缓存修改时间 + """ + return self.__time + + def load(self, path=None): + """ + load data from path + """ + if not path: + path = self.__filename + + LOG.debug('load cache data from %s', path) + if os.path.isfile(path): + with open(self.__filename, 'r') as data: + self.__data = pickle.load(data) + self.__time = os.stat(path).st_mtime + else: + LOG.info('cache file not exist') + self.__data = {} + self.__time = time.time() + return self + + def data(self, key=None, default=None): + """ + 获取当前字典或者制定得键值 + """ + if self.__sync: + self.load() + + if key is None: + return self.__data + else: + return self.__data.get(key, default) + + def sync(self): + """Sync the write buffer with the cache files and clear the buffer. + """ + if self.__changed: + with open(self.__filename, 'w') as data: + pickle.dump(self.__data, data) + LOG.debug('save cache data to %s', self.__filename) + self.__changed = False + return self + + def close(self): + """Sync the write buffer, then close the cache. + If a closed :class:`FileCache` object's methods are called, a + :exc:`ValueError` will be raised. + """ + self.sync() + del self.__data + del self.__filename + del self.__time + self.__sync = False + + def __setitem__(self, key, value): + if self.data(key) != value: + self.__data[key] = value + self.__changed = True + if self.__sync: + self.sync() + + def __delitem__(self, key): + if key in self.data(): + del self.__data[key] + self.__changed = True + if self.__sync: + self.sync() + + def __getitem__(self, key): + return self.data(key) + + def __iter__(self): + for key in self.data(): + yield key + + def __len__(self): + return len(self.data()) + + def __contains__(self, key): + return key in self.data() + + def __str__(self): + return self.data().__str__() + + def __del__(self): + self.close() + + +def main(): + """ + test + """ + LOG.basicConfig(level=LOG.DEBUG) + # LOG.debug('test log') + cache = Cache('test.txt') + cache['s'] = ['a', 's'] + print cache + print cache['s'] + print cache.time + cache['t'] = '哈哈' + print cache.time + + +if __name__ == '__main__': + main() diff --git a/ip.py b/util/ip.py similarity index 100% rename from ip.py rename to util/ip.py