ddns 4.0.0b2__py2.py3-none-any.whl → 4.0.0b4__py2.py3-none-any.whl

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.

Potentially problematic release.


This version of ddns might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ddns
3
- Version: 4.0.0b2
3
+ Version: 4.0.0b4
4
4
  Summary: automatically update DNS records to dynamic local IP [自动更新DNS记录指向本地IP]
5
5
  Home-page: https://ddns.newfuture.cc
6
6
  Author: NewFuture
@@ -1,20 +1,20 @@
1
- run.py,sha256=ZtBbWci0g4mmoje4gCWZELfoU44noGp6NpjDoZjvuh8,5905
2
- ddns-4.0.0b2.dist-info/licenses/LICENSE,sha256=MI-ECjp-Vl7WZLiSPY6r5VwrOReNiICVB1QCXiUGt_s,1111
1
+ run.py,sha256=VXerRIy_4aCN0MnW0JX-KalVfj8osBQ0BA6FuAgV51k,6068
2
+ ddns-4.0.0b4.dist-info/licenses/LICENSE,sha256=MI-ECjp-Vl7WZLiSPY6r5VwrOReNiICVB1QCXiUGt_s,1111
3
3
  dns/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  dns/alidns.py,sha256=fD8mzr7FJ5QxOWLA3ekoOhjeU1IFepOpxybrgZv2c9k,5782
5
5
  dns/callback.py,sha256=V8AG7kxtS0jIkyD9F7xnGJpu1L3E5KUcNDwOnQjcJKs,3208
6
6
  dns/cloudflare.py,sha256=OBq4TJwVHHiPMM6FJo-bz20FOHPvA-RSFbuGilw3tVw,5647
7
7
  dns/dnscom.py,sha256=Nhyl70JGnZY2IV0j5Ebgy_gYQGFb8hXTshaRG_rC0Ww,5297
8
- dns/dnspod.py,sha256=giivum1lyxfVtu8d-_muXhMxBcB-DZtpq_IGo7ApdFk,6143
8
+ dns/dnspod.py,sha256=52Hstd92-BkyEAicW5DkexfJiTww5KcNZkKs20HUIFE,6211
9
9
  dns/dnspod_com.py,sha256=9DW7Oz1f2fYSLDDGdwQKMp9OhRJ_CYB8bfzGhf0fIMI,339
10
10
  dns/he.py,sha256=X_WODK6vDTpHdAvPP_vUltaeAlHeG2jMPbzFkfYxWOg,2142
11
11
  dns/huaweidns.py,sha256=0A2jxcJ5zGaMxMMh7C9SsDewhP2zVRLI8FznB9BBBEc,8748
12
12
  util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- util/cache.py,sha256=nt_x_BUORwUAZZW13UNeEBOFrIksWWhdj9k4AGyvnU8,3337
14
- util/config.py,sha256=05kDaRkidutIkl5xmTpcN7nDlCxAZpJ1u4v-Y_bYGqo,6305
13
+ util/cache.py,sha256=LVH5jGZFimXmD7XCQCtZULhagybM7gSjMo-73yqttiA,3338
14
+ util/config.py,sha256=0yJ5Yfz6u__wrXm_SMqw6a9OuMf9usHOxXfRlB7C3mo,6836
15
15
  util/ip.py,sha256=GZzqrl_nq9JuGHTWhPB0fffpr94ITJA6BCVlS5KlWcE,3836
16
- ddns-4.0.0b2.dist-info/METADATA,sha256=HiPUUySDdJMwdJpDnQ-Yv0oTDog3S0v2p_MprXSwmAc,15269
17
- ddns-4.0.0b2.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
18
- ddns-4.0.0b2.dist-info/entry_points.txt,sha256=vpxxBE6Kepc-SHatr-FRRLmZecrTupYejyDB8ovJbLg,34
19
- ddns-4.0.0b2.dist-info/top_level.txt,sha256=dS3_ClQTIG1ousHykZ1P3AzQa-MKq4jT_rbm21D-KxE,13
20
- ddns-4.0.0b2.dist-info/RECORD,,
16
+ ddns-4.0.0b4.dist-info/METADATA,sha256=JWsNhE92etdBLN_Sq3s3wEjGtOyxsG2x01_VNap9JMI,15269
17
+ ddns-4.0.0b4.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
18
+ ddns-4.0.0b4.dist-info/entry_points.txt,sha256=vpxxBE6Kepc-SHatr-FRRLmZecrTupYejyDB8ovJbLg,34
19
+ ddns-4.0.0b4.dist-info/top_level.txt,sha256=dS3_ClQTIG1ousHykZ1P3AzQa-MKq4jT_rbm21D-KxE,13
20
+ ddns-4.0.0b4.dist-info/RECORD,,
dns/dnspod.py CHANGED
@@ -106,10 +106,11 @@ def get_domain_id(domain):
106
106
  return get_domain_id.domain_list[domain]
107
107
  else:
108
108
  try:
109
- info = request('Domain.Info', domain=domain)
110
- except Exception:
109
+ d_info = request('Domain.Info', domain=domain)
110
+ except Exception as e:
111
+ info("get_domain_id(%s) error: %s", domain, e)
111
112
  return
112
- did = info.get("domain", {}).get("id")
113
+ did = d_info.get("domain", {}).get("id")
113
114
  if did:
114
115
  get_domain_id.domain_list[domain] = did
115
116
  return did
run.py CHANGED
@@ -19,7 +19,7 @@ from util import ip
19
19
  from util.cache import Cache
20
20
  from util.config import init_config, get_config
21
21
 
22
- __version__ = "v4.0.0-beta2@2025-06-03T15:36:22+00:00" # CI 时会被Tag替换
22
+ __version__ = "v4.0.0-beta4@2025-06-10T17:58:37+00:00" # CI 时会被Tag替换
23
23
  __description__ = "automatically update DNS records to dynamic local IP [自动更新DNS记录指向本地IP]"
24
24
  __doc__ = """
25
25
  ddns[%s]
@@ -28,7 +28,7 @@ ddns[%s]
28
28
  Copyright (c) New Future (MIT License)
29
29
  """ % (__version__)
30
30
 
31
- environ["DDNS_VERSION"] = "v4.0.0-beta2"
31
+ environ["DDNS_VERSION"] = "v4.0.0-beta4"
32
32
 
33
33
  if getattr(sys, 'frozen', False):
34
34
  # https://github.com/pyinstaller/pyinstaller/wiki/Recipe-OpenSSL-Certificate
@@ -47,7 +47,7 @@ def get_ip(ip_type, index="default"):
47
47
  debug("get_ip(%s, %s)", ip_type, index)
48
48
  if index is False: # disabled
49
49
  return False
50
- elif isinstance(index, list): # 如果获取到的规则是列表,则依次判断列表中每一个规则,直到找到一个可以正确获取到的IP
50
+ elif isinstance(index, list): # 如果获取到的规则是列表,则依次判断列表中每一个规则,直到获取到IP
51
51
  for i in index:
52
52
  value = get_ip(ip_type, i)
53
53
  if value:
@@ -66,7 +66,7 @@ def get_ip(ip_type, index="default"):
66
66
  else:
67
67
  value = getattr(ip, index + "_v" + ip_type)()
68
68
  except Exception as e:
69
- error(e)
69
+ error("Failed to get %s address: %s", ip_type, e)
70
70
  return value
71
71
 
72
72
 
@@ -81,7 +81,7 @@ def change_dns_record(dns, proxy_list, **kw):
81
81
  try:
82
82
  return dns.update_record(domain, kw['ip'], record_type=record_type)
83
83
  except Exception as e:
84
- error(e)
84
+ error("Failed to update %s record for %s: %s", record_type, domain, e)
85
85
  return False
86
86
 
87
87
 
@@ -136,11 +136,11 @@ def main():
136
136
 
137
137
  info("DDNS[ %s ] run: %s %s", __version__, os_name, sys.platform)
138
138
  if get_config("config"):
139
- info("loaded Config from: %s", path.abspath(get_config('config')))
139
+ info('loaded Config from: %s', path.abspath(get_config('config')))
140
140
 
141
141
  proxy = get_config('proxy') or 'DIRECT'
142
142
  proxy_list = proxy if isinstance(
143
- proxy, list) else proxy.strip('; ').replace(',', ';').split(';')
143
+ proxy, list) else proxy.strip(';').replace(',', ';').split(';')
144
144
 
145
145
  cache_config = get_config('cache', True)
146
146
  if cache_config is False:
@@ -151,19 +151,21 @@ def main():
151
151
  cache = Cache(cache_config)
152
152
 
153
153
  if cache is False:
154
- info("Cache is disabled!")
155
- elif get_config("config_modified_time") is None or get_config("config_modified_time") >= cache.time:
156
- warning("Cache file is out of dated.")
154
+ info('Cache is disabled!')
155
+ elif not get_config('config_modified_time') or get_config('config_modified_time') >= cache.time:
156
+ warning('Cache file is outdated.')
157
157
  cache.clear()
158
158
  else:
159
- debug("Cache is empty.")
159
+ debug('Cache is empty.')
160
160
  update_ip('4', cache, dns, proxy_list)
161
161
  update_ip('6', cache, dns, proxy_list)
162
162
 
163
163
 
164
164
  if __name__ == '__main__':
165
- if sys.version_info.major == 3 and os_name == 'nt':
165
+ encoding = sys.stdout.encoding
166
+ if encoding is not None and encoding.lower() != 'utf-8':
167
+ # 兼容windows 和部分ASCII编码的老旧系统
166
168
  from io import TextIOWrapper
167
- sys.stdout = TextIOWrapper(sys.stdout.detach(), encoding='utf-8')
168
- sys.stderr = TextIOWrapper(sys.stderr.detach(), encoding='utf-8')
169
+ sys.stdout = TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
170
+ sys.stderr = TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
169
171
  main()
util/cache.py CHANGED
@@ -11,10 +11,9 @@ from time import time
11
11
 
12
12
  from logging import info, debug, warning
13
13
 
14
- try:
14
+ try: # python 3
15
15
  from collections.abc import MutableMapping
16
- except ImportError:
17
- # Python 2 imports
16
+ except ImportError: # python 2
18
17
  from collections import MutableMapping
19
18
 
20
19
 
util/config.py CHANGED
@@ -2,11 +2,9 @@
2
2
  # -*- coding:utf-8 -*-
3
3
  from argparse import Action, ArgumentParser, Namespace, RawTextHelpFormatter
4
4
  from json import load as loadjson, dump as dumpjson
5
- from os import stat, environ
5
+ from os import stat, environ, path
6
6
  from logging import error, getLevelName
7
7
 
8
- from time import time
9
-
10
8
  import sys
11
9
 
12
10
 
@@ -46,48 +44,66 @@ def init_config(description, doc, version):
46
44
  epilog=doc, formatter_class=RawTextHelpFormatter)
47
45
  parser.add_argument('-v', '--version',
48
46
  action='version', version=version)
49
- parser.add_argument('-c', '--config', help="run with config file [配置文件路径]")
47
+ parser.add_argument('-c', '--config', help='run with config file [配置文件路径]')
50
48
 
51
49
  # 参数定义
52
- parser.add_argument('--dns', help="DNS Provider [DNS服务提供商]", choices=[
50
+ parser.add_argument('--dns', help='DNS Provider [DNS服务提供商]', choices=[
53
51
  'alidns', 'cloudflare', 'dnscom', 'dnspod', 'dnspod_com', 'he', 'huaweidns', 'callback'])
54
- parser.add_argument('--id', help="api ID [授权账户]")
55
- parser.add_argument('--token', help="api token or Secret key [授权访问凭证或密钥]")
56
- parser.add_argument('--index4', nargs="*", action=ExtendAction,
57
- help="list to get ipv4 [IPV4 获取方式]")
58
- parser.add_argument('--index6', nargs="*", action=ExtendAction,
59
- help="list to get ipv6 [IPV6获取方式]")
60
- parser.add_argument('--ipv4', nargs="*", action=ExtendAction,
61
- help="ipv4 domain list [IPV4域名列表]")
62
- parser.add_argument('--ipv6', nargs="*", action=ExtendAction,
63
- help="ipv6 domain list [IPV6域名列表]")
64
- parser.add_argument('--ttl', type=int, help="ttl for DNS [DNS 解析 TTL 时间]")
65
- parser.add_argument('--proxy', nargs="*", action=ExtendAction,
66
- help="https proxy [设置http 代理,多代理逐个尝试直到成功]")
52
+ parser.add_argument('--id', help='api ID [授权账户]')
53
+ parser.add_argument('--token', help='api token or Secret key [授权访问凭证或密钥]')
54
+ parser.add_argument('--index4', nargs='*', action=ExtendAction,
55
+ help='list to get ipv4 [IPV4 获取方式]')
56
+ parser.add_argument('--index6', nargs='*', action=ExtendAction,
57
+ help='list to get ipv6 [IPV6获取方式]')
58
+ parser.add_argument('--ipv4', nargs='*', action=ExtendAction,
59
+ help='ipv4 domain list [IPV4域名列表]')
60
+ parser.add_argument('--ipv6', nargs='*', action=ExtendAction,
61
+ help='ipv6 domain list [IPV6域名列表]')
62
+ parser.add_argument('--ttl', type=int, help='ttl for DNS [DNS 解析 TTL 时间]')
63
+ parser.add_argument('--proxy', nargs='*', action=ExtendAction,
64
+ help='https proxy [设置http 代理,多代理逐个尝试直到成功]')
67
65
  parser.add_argument('--cache', type=str2bool, nargs='?',
68
- const=True, help="cache flag [启用缓存,可配配置路径或开关]")
69
- parser.add_argument('--log.file', metavar="LOG_FILE",
70
- help="log file [日志文件,默认标准输出]")
66
+ const=True, help='cache flag [启用缓存,可配配置路径或开关]')
67
+ parser.add_argument('--log.file', metavar='LOG_FILE',
68
+ help='log file [日志文件,默认标准输出]')
71
69
  parser.add_argument('--log.level', type=log_level,
72
- metavar="|".join(log_levels))
70
+ metavar='|'.join(log_levels))
73
71
 
74
72
  __cli_args = parser.parse_args()
75
- is_configfile_optional = get_config("token") or get_config("id")
73
+ is_configfile_required = not get_config("token") and not get_config("id")
76
74
  config_file = get_config("config")
77
- if not is_configfile_optional or config_file is not None:
78
- __load_config(config_file or "config.json", is_configfile_optional)
79
- __cli_args.config = config_file or "config.json"
75
+ if not config_file:
76
+ # 未指定配置文件且需要读取文件时,依次查找
77
+ cfgs = [
78
+ path.abspath('config.json'),
79
+ path.expanduser('~/.ddns/config.json'),
80
+ '/etc/ddns/config.json'
81
+ ]
82
+ config_file = next((cfg for cfg in cfgs if path.isfile(cfg)), cfgs[0])
83
+
84
+ if path.isfile(config_file):
85
+ __load_config(config_file)
86
+ __cli_args.config = config_file
87
+ elif is_configfile_required:
88
+ error('Config file is required, but not found: %s', config_file)
89
+ # 如果需要配置文件但没有指定,则自动生成
90
+ if generate_config(config_file):
91
+ sys.stdout.write(
92
+ 'Default configure file %s is generated.\n' % config_file)
93
+ sys.exit(1)
94
+ else:
95
+ sys.exit('fail to load config from file: %s\n' % config_file)
80
96
 
81
97
 
82
- def __load_config(path="config.json", skip_auto_generation=False):
98
+ def __load_config(config_path):
83
99
  """
84
100
  加载配置
85
101
  """
86
102
  global __config
87
103
  try:
88
- with open(path) as configfile:
104
+ with open(config_path, 'r') as configfile:
89
105
  __config = loadjson(configfile)
90
- __config["config_modified_time"] = stat(path).st_mtime
106
+ __config["config_modified_time"] = stat(config_path).st_mtime
91
107
  if 'log' in __config:
92
108
  if 'level' in __config['log'] and __config['log']['level'] is not None:
93
109
  __config['log.level'] = log_level(__config['log']['level'])
@@ -95,40 +111,10 @@ def __load_config(path="config.json", skip_auto_generation=False):
95
111
  __config['log.file'] = __config['log']['file']
96
112
  elif 'log.level' in __config:
97
113
  __config['log.level'] = log_level(__config['log.level'])
98
- except IOError:
99
- if skip_auto_generation:
100
- __config["config_modified_time"] = time()
101
- return
102
- error(' Config file `%s` does not exist!' % path)
103
- with open(path, 'w') as configfile:
104
- configure = {
105
- "$schema": "https://ddns.newfuture.cc/schema/v4.0.json",
106
- "id": "YOUR ID or EMAIL for DNS Provider",
107
- "token": "YOUR TOKEN or KEY for DNS Provider",
108
- "dns": "dnspod",
109
- "ipv4": [
110
- "newfuture.cc",
111
- "ddns.newfuture.cc"
112
- ],
113
- "ipv6": [
114
- "newfuture.cc",
115
- "ipv6.ddns.newfuture.cc"
116
- ],
117
- "index4": "default",
118
- "index6": "default",
119
- "ttl": None,
120
- "proxy": None,
121
- "log": {
122
- "level": "INFO",
123
- "file": None
124
- }
125
- }
126
- dumpjson(configure, configfile, indent=2, sort_keys=True)
127
- sys.stdout.write(
128
- "New template configure file `%s` is generated.\n" % path)
129
- sys.exit(1)
130
114
  except Exception as e:
131
- sys.exit('fail to load config from file: %s\n%s' % (path, e))
115
+ error('Failed to load config file `%s`: %s', config_path, e)
116
+ raise
117
+ # 重新抛出异常
132
118
 
133
119
 
134
120
  def get_config(key, default=None):
@@ -167,3 +153,41 @@ class ExtendAction(Action):
167
153
  else:
168
154
  items.append(values)
169
155
  setattr(namespace, self.dest, items)
156
+
157
+
158
+ def generate_config(config_path):
159
+ """
160
+ 生成配置文件
161
+ """
162
+ configure = {
163
+ '$schema': 'https://ddns.newfuture.cc/schema/v4.0.json',
164
+ 'id': 'YOUR ID or EMAIL for DNS Provider',
165
+ 'token': 'YOUR TOKEN or KEY for DNS Provider',
166
+ 'dns': 'dnspod',
167
+ 'ipv4': [
168
+ 'newfuture.cc',
169
+ 'ddns.newfuture.cc'
170
+ ],
171
+ 'ipv6': [
172
+ 'newfuture.cc',
173
+ 'ipv6.ddns.newfuture.cc'
174
+ ],
175
+ 'index4': 'default',
176
+ 'index6': 'default',
177
+ 'ttl': None,
178
+ 'proxy': None,
179
+ 'log': {
180
+ 'level': 'INFO',
181
+ 'file': None
182
+ }
183
+ }
184
+ try:
185
+ with open(config_path, 'w') as f:
186
+ dumpjson(configure, f, indent=2, sort_keys=True)
187
+ return True
188
+ except IOError:
189
+ error('Cannot open config file to write: `%s`!', config_path)
190
+ return False
191
+ except Exception as e:
192
+ error('Failed to write config file `%s`: %s', config_path, e)
193
+ return False
File without changes