28 กรกฎาคม 2569

Mikrotik script for update cloudflare DNS IP

# ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# 1. USER CONFIGURATION
# ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# OPTIONAL: Paste your Record ID here later to bypass the automatic discovery phase.
# If left empty "", the script will auto-fetch, log it, and update the DNS immediately.
:local cfRecordId ""

:local cfToken "YOUR_CLOUDFLARE_API_TOKEN"
:local cfZoneId "YOUR_CLOUDFLARE_ZONE_ID"
:local cfRecordName "ddns.domain.com"
:local wanInterface "ether1"
:local dnsServer "1.1.1.1"

# ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# 2. STEP 1: INITIAL IP CHECK (Zero API overhead if IP matches)
# ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:local currentIp [/ip address get [/ip address find interface=$wanInterface] address]
:set currentIp [:pick $currentIp 0 [:find $currentIp "/"]]

:local cloudflareIp ""
:do {
    :set cloudflareIp [:resolve $cfRecordName server=$dnsServer]
} on-error={
    :log info "CF-DDNS: Cannot resolve $cfRecordName via $dnsServer, assuming IP has changed."
}

# Only execute further steps if an update is genuinely required
:if ($currentIp != $cloudflareIp) do={
    :log info "CF-DDNS: IP mismatch detected ($cloudflareIp vs $currentIp). Proceeding..."
    
    :local httpHeaders "Authorization: Bearer $cfToken,Content-Type: application/json"
    :local finalRecordId $cfRecordId
    :local proceedWithUpdate true

    # ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    # 3. STEP 2: CONDITIONAL RECORD ID FETCH
    # ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    :if ([:len $finalRecordId] = 0) do={
        :log warning "CF-DDNS: cfRecordId is empty. Fetching ID from Cloudflare..."
        
        :local getUrl "https://api.cloudflare.com/client/v4/zones/$cfZoneId/dns_records"
        :local fetchResult ""
        
        :do {
            :set fetchResult [/tool fetch url=$getUrl http-header-field=$httpHeaders as-value output=user]
        } on-error={
            :log error "CF-DDNS: Failed to connect to Cloudflare API during discovery phase."
            :set proceedWithUpdate false
        }

        :if ($fetchResult != "") do={
            :local responseData ($fetchResult->"data")
            :local idKeyword "\"id\":\""
            :local idPos [:find $responseData $idKeyword]
            
            :if ($idPos > 0) do={
                :local idStart ($idPos + [:len $idKeyword])
                :local idEnd [:find $responseData "\"" $idStart]
                :set finalRecordId [:pick $responseData $idStart $idEnd]
                
                # Log the discovered ID for optional user optimization
                :log warning "=========================================================="
                :log warning "CF-DDNS: [AUTO-DISCOVERY SUCCESS] Your Record ID is: $finalRecordId"
                :log warning "CF-DDNS: Tip: Copy this ID and paste it into ':local cfRecordId' to save 1 API call per update!"
                :log warning "=========================================================="
            } else={
                :log error "CF-DDNS: DNS Record Name not found on Cloudflare. Update aborted."
                :set proceedWithUpdate false
            }
        }
    }

    # ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    # 4. STEP 3: PUSH UPDATE TO CLOUDFLARE
    # ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    :if ($proceedWithUpdate = true) do={
        :log info "CF-DDNS: Pushing IP update to Cloudflare..."
        
        :local putUrl ("https://api.cloudflare.com/client/v4/zones/" . $cfZoneId . "/dns_records/" . $finalRecordId)
        :local putData "{\"type\":\"A\",\"name\":\"$cfRecordName\",\"content\":\"$currentIp\",\"ttl\":60,\"proxied\":false}"
        
        :do {
            /tool fetch url=$putUrl http-method=put http-data=$putData http-header-field=$httpHeaders as-value output=user
            :log info "CF-DDNS: Successfully updated $cfRecordName to $currentIp"
        } on-error={
            :log error "CF-DDNS: Failed to update IP on Cloudflare API."
        }
    }
} else={
    # Quiet loop: IP is still valid, script exits here silently
    # :log info "CF-DDNS: IP remains unchanged ($currentIp). No action needed."
}