ChatGPTさんにdipperをPythonで書いてもらった。

実際にはdipperと同じ機能をPythonを使ってオブジェクト指向で実装してもらおうという試み。
びっくりするくらい賢いんだけど、やはりこの辺はAIだなぁ~と感じるところもちらほら。

スクリプトを作るレベルに関しては、俺なんかよりもずっと上。
ただし、やっぱりそのままでは動かないからデバッグの必要はある。
まぁこの辺は指示の出し方が悪い可能性もあるが。

おかげで少しだけPythonの使い方とオブジェクト指向の使い方がつかめた気がした。
AIらしいなぁと思うところはやはりプログラムのセンスはまだないのかなと思った。
具体的に言うと、処理の速度とかじゃなくて、そういう実装をするとこんなにわかりやすいのか!!
ってことはなかったかなぁ。

ブログとかで見ると、たまに「おお!?そんな風に実装できるのか!」すげぇーってなるんですけど、
そういうことはなかった。
いい意味でもそうじゃない意味でも無難な実装をしてくれる。

で2日位かけてコーディングをChatGPT、デバッグを俺がしつつ、なんとなくdipperと同じ機能を搭載できた。
しっかりデバッグはしてないので粗さは全然あるんだけど。

あと、Pythonは慣れてくると結構とっつきやすいとも思った。
何より、これはPython本体の話ではないけど、
VSCodeのPython拡張機能でデバッガーが使えるのがかなりデカかった。
どういう事かというと、処理をステップで追っかけて
その都度の変数の値もチェックできるので、デバッガーを使ったことある人ならめっちゃ楽。

シェルスクリプトはデバッガーの拡張見つからなかったので、
処理の途中にデバッグ用の処理を入れて確認していたんだけど、
デバッガーのおかげでブレイクポイント張ってステップで追っかけられるから10倍くらいデバッグがはかどる。

但し、最初の環境づくりはやっぱり必要でもあった。
Windowsだとちょっと手間かも。
VSCodeがあればほぼやってくれるけど。

まぁそんな感じで実際のソースを公開します。

GitHubはこちら

[MyDNS_user1]
username = mydnsxxxxxx
password = xxxxxxx
ipv4_url = https://ipv4.mydns.jp/login.html
ipv6_url = https://ipv6.mydns.jp/login.html
domain = example1.com
use_ipv4 = True
use_ipv6 = True

[MyDNS_user2]
username = mydnsxxxxxx
password = xxxxxxx
ipv4_url = https://ipv4.mydns.jp/login.html
ipv6_url = https://ipv6.mydns.jp/login.html
domain = example2.com
use_ipv4 = True
use_ipv6 = True

[GoogleDomains_user1]
username = xxxxxx
password = xxxxxx
url = https://domains.google.com/nic/update
domain = example3.com
use_ipv4 = True
use_ipv6 = False

[GoogleDomains_user2]
username = xxxxxxx
password = xxxxxxx
url = https://domains.google.com/nic/update
domain = example4.com
use_ipv4 = False
use_ipv6 = True

[Schedule]
notification_interval_sec = 3600
check_interval_sec = 60
import requests
import configparser
import subprocess
import time
import threading

# DDNSのベースクラスで、DDNSアドレスを更新するための共通のメソッドを提供します。
class DDNSUpdater:
    def __init__(self, username, password, domain, use_ipv4=False, use_ipv6=False):
        self.username = username
        self.password = password
        self.domain = domain
        self.use_ipv4 = use_ipv4
        self.use_ipv6 = use_ipv6

    def update_address(self, ipv4_address=None, ipv6_address=None):
        # IPv4アドレスの更新が要求され、サブクラスが実装していない場合にはNotImplementedErrorを発生させます。
        if self.use_ipv4 and ipv4_address:
            raise NotImplementedError("Subclasses must implement update_address() method.")

        # IPv6アドレスの更新が要求され、サブクラスが実装していない場合にはNotImplementedErrorを発生させます。
        if self.use_ipv6 and ipv6_address:
            raise NotImplementedError("Subclasses must implement update_address() method.")

# DDNSUpdaterのサブクラスで、MyDNSのアドレスを更新するための具体的な実装を提供します。
class MyDNSUpdater(DDNSUpdater):
    def __init__(self, username, password, ipv4_url, ipv6_url, domain, use_ipv4=False, use_ipv6=False):
        super().__init__(username, password, domain, use_ipv4, use_ipv6)
        self.ipv4_url = ipv4_url
        self.ipv6_url = ipv6_url
    
    def update_address(self, ipv4_address=None, ipv6_address=None):
        # IPv4アドレスの使用が設定されており、かつipv4_addressが指定されている場合には、MyDNSのIPv4アドレスを更新します。
        if self.use_ipv4 and ipv4_address:
            response = requests.get(self.ipv4_url, auth=(self.username, self.password))
            if response.status_code == 200:
                print("MyDNS address update successful.")
            else:
                print("Failed to update MyDNS address.")
           
        # IPv6アドレスの使用が設定されており、かつipv6_addressが指定されている場合には、MyDNSのIPv6アドレスを更新します。
        if self.use_ipv6 and ipv6_address:
            response = requests.get(self.ipv6_url, auth=(self.username, self.password))
            if response.status_code == 200:
                print("MyDNS address update successful.")
            else:
                print("Failed to update MyDNS address.")

# DDNSUpdaterのサブクラスで、Google Domainsのアドレスを更新するための具体的な実装を提供します。
class GoogleDomainsUpdater(DDNSUpdater):
    def __init__(self, username, password, url, domain, use_ipv4=False, use_ipv6=False):
        super().__init__(username, password, domain, use_ipv4, use_ipv6)
        self.url = url
  
    def update_address(self, ipv4_address=None, ipv6_address=None):
        # IPv4アドレスの使用が設定されており、かつipv4_addressが指定されている場合には、Google DomainsのIPv4アドレスを更新します。
        if self.use_ipv4 and ipv4_address:
            url = f"{self.url}?hostname={self.domain}&myip={ipv4_address}"
            response = requests.get(url, auth=(self.username, self.password))
        # IPv6アドレスの使用が設定されており、かつipv6_addressが指定されている場合には、Google DomainsのIPv6アドレスを更新します。
        elif self.use_ipv6 and ipv6_address:
            url = f"{self.url}?hostname={self.domain}&myip={ipv6_address}"
            response = requests.get(url, auth=(self.username, self.password))
        else:
            return

        if response.status_code == 200:
            if response.text.startswith("good") or response.text.startswith("nochg"):
                print("Google Domains address update successful.")
            else:
                print("Failed to update Google Domains address.")
        else:
            print("Failed to update Google Domains address.")

# DDNSユーザーとドメインのマッピングを管理し、アドレスの更新とチェックを実行します。
class DDNSManager:
    def __init__(self):
        self.mappings = {}
    
    def add_ddns_user(self, user):
        self.mappings[user.domain] = user
    
    def update_ddns_address(self, domain):
        user = self.mappings.get(domain)
        if user:
            if user.use_ipv4:
                my_ipv4 = self.get_my_ip()
                user.update_address(ipv4_address=my_ipv4)
            if user.use_ipv6:
                my_ipv6 = self.get_my_ip(ipv6=True)
                user.update_address(ipv6_address=my_ipv6)
    
    def check_ddns_address(self, domain):
        user = self.mappings.get(domain)
        if user:
            if user.use_ipv4:
                domain_ipv4 = self.get_domain_ip(user.domain)
                my_ipv4 = self.get_my_ip()
                if my_ipv4 != domain_ipv4:
                    user.update_address(ipv4_address=my_ipv4)
            if user.use_ipv6:
                domain_ipv6 = self.get_domain_ip(user.domain, ipv6=True)
                my_ipv6 = self.get_my_ip(ipv6=True)
                if my_ipv6 != domain_ipv6:
                    user.update_address(ipv6_address=my_ipv6)
    
    def get_my_ip(self, ipv6=False):
        if ipv6:
            result = subprocess.run(["dig", "@ident.me", "-6", "+short"], capture_output=True, text=True)
        else:
            result = subprocess.run(["dig", "@ident.me", "-4", "+short"], capture_output=True, text=True)
        
        if result.returncode == 0:
            return result.stdout.strip()
        else:
            return None
    
    def get_domain_ip(self, domain, ipv6=False):
        if ipv6:
            result = subprocess.run(["dig", domain, "AAAA", "+short"], capture_output=True, text=True)
        else:
            result = subprocess.run(["dig", domain, "A", "+short"], capture_output=True, text=True)
        
        if result.returncode == 0:
            return result.stdout.strip()
        else:
            return None

# DDNSManagerを定期的に呼び出してアドレスを更新するためのスレッドを提供します。
class DDNSUpdaterThread(threading.Thread):
    def __init__(self, ddns_manager, interval_time, use_ipv4=False, use_ipv6=False):
        super().__init__()
        self.ddns_manager = ddns_manager
        self.interval_time = interval_time
        self.use_ipv4 = use_ipv4
        self.use_ipv6 = use_ipv6
    
    def run(self):
        while True:
            for domain in self.ddns_manager.mappings.keys():
                self.ddns_manager.update_ddns_address(domain)
            time.sleep(self.interval_time)

# DDNSManagerを定期的に呼び出してアドレスをチェックするためのスレッドを提供します。
class DDNSCheckerThread(threading.Thread):
    def __init__(self, ddns_manager, interval_time, use_ipv4=False, use_ipv6=False):
        super().__init__()
        self.ddns_manager = ddns_manager
        self.interval_time = interval_time
        self.use_ipv4 = use_ipv4
        self.use_ipv6 = use_ipv6
    
    def run(self):
        while True:
            for domain in self.ddns_manager.mappings.keys():
                self.ddns_manager.check_ddns_address(domain)
            time.sleep(self.interval_time)

# コンフィグファイルからDDNSManagerをロードするためのクラスです。
class DDNSConfigLoader:
    @classmethod
    def load_config(cls, filename):
        config = configparser.ConfigParser()
        config.read(filename)
        
        ddns_manager = DDNSManager()

        for section in config.sections():
            if section.startswith('MyDNS_user'):
                username = config.get(section, 'username')
                password = config.get(section, 'password')
                ipv4_url = config.get(section, 'ipv4_url')
                ipv6_url = config.get(section, 'ipv6_url')
                domain = config.get(section, 'domain')
                use_ipv4 = config.getboolean(section, 'use_ipv4')
                use_ipv6 = config.getboolean(section, 'use_ipv6')
                mydns = MyDNSUpdater(username, password, ipv4_url, ipv6_url, domain, use_ipv4, use_ipv6)
                ddns_manager.add_ddns_user(mydns)

            if section.startswith('GoogleDomains_user'):
                username = config.get(section, 'username')
                password = config.get(section, 'password')
                url = config.get(section, 'url')
                domain = config.get(section, 'domain')
                use_ipv4 = config.getboolean(section, 'use_ipv4')
                use_ipv6 = config.getboolean(section, 'use_ipv6')
                google_domains = GoogleDomainsUpdater(username, password, url, domain, use_ipv4, use_ipv6)
                ddns_manager.add_ddns_user(google_domains)
        
        return ddns_manager

def main():
    # コンフィグファイルの読み込み
    config = configparser.ConfigParser()
    config.read('config.ini')
    ddns_manager = DDNSConfigLoader.load_config('config.ini')

    # アドレス通知処理の非同期実行
    notification_interval = int(config.get('Schedule', 'notification_interval_sec'))
    notification_thread = DDNSUpdaterThread(ddns_manager, notification_interval, use_ipv4=True, use_ipv6=True)
    notification_thread.start()

    # アドレスチェック処理の非同期実行
    check_interval = int(config.get('Schedule', 'check_interval_sec'))
    check_thread = DDNSCheckerThread(ddns_manager, check_interval, use_ipv4=True, use_ipv6=True)
    check_thread.start()

    # メインスレッドの実行を継続
    while True:
        time.sleep(1)

if __name__ == "__main__":
    main()

ChatGPTさんにクラス図も書いてもらった。

    +-------------------------------------+
    |             DDNSUpdater             |
    +-------------------------------------+
    | - username: str                     |
    | - password: str                     |
    | - domain: str                       |
    | - use_ipv4: bool                     |
    | - use_ipv6: bool                     |
    +-------------------------------------+
    | + __init__(username, password,      |
    |    domain, use_ipv4=False,          |
    |    use_ipv6=False)                  |
    | + update_address(ipv4_address=None, |
    |    ipv6_address=None)               |
    +-------------------------------------+
                  ^
                  |
         +-------------------+
         |                   |
+---------------------+ +-----------------------+
|    MyDNSUpdater     | | GoogleDomainsUpdater  |
+---------------------+ +-----------------------+
| - ipv4_url: str    | | - url: str             |
| - ipv6_url: str    | +-----------------------+
+---------------------+
| + __init__(username,|
|    password,        |
|    ipv4_url,        |
|    ipv6_url,        |
|    domain,          |
|    use_ipv4=False,  |
|    use_ipv6=False)  |
| + update_address(ipv4_address=None,|
|    ipv6_address=None)                 |
+---------------------+
                  ^
                  |
            +-------------+
            |             |
      +----------------+ +-------------------+
      | DDNSManager    | | DDNSConfigLoader  |
      +----------------+ +-------------------+
      | - mappings: dict                   |
      +----------------+                   |
      | + add_ddns_user(user)              |
      | + update_ddns_address(domain)      |
      | + check_ddns_address(domain)       |
      | + get_my_ip(ipv6=False)            |
      | + get_domain_ip(domain, ipv6=False)|
      +-----------------------------------+
                  ^
                  |
      +-----------------------+
      |                       |
+------------------+ +------------------+
| DDNSUpdaterThread| | DDNSCheckerThread|
+------------------+ +------------------+
| - ddns_manager: DDNSManager         |
| - interval_time: int                |
| - use_ipv4: bool                    |
| - use_ipv6: bool                    |
+------------------+ +------------------+
| + run()                            |
+------------------+ +------------------+

 

正直自分には処理の2~3割程度しか理解できていない。
大本の処理の流れは自分が指示をしたので理解できてはいるんだけど。

ちなみにコメントも全てChatGPTさんによるものです。
うん。普通にすげぇよ。

ただし、途中で急に欲が出たのか、処理を大幅に変えてきて、
デバッグが案の定大変になったので、その修正を没にすることもあった。
心なしその後の返信がしょぼんとして見えたのは気のせいなんだろうか?。。。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です