• 18-09-2026, 18:17:38
    #10
    Güzel görünüyor hocam, ellerinize, emeğinize sağlık
  • 18-09-2026, 18:23:34
    #11
    Üyeliği durduruldu
    Teyyare adlı üyeden alıntı: mesajı görüntüle
    Güzel görünüyor hocam, ellerinize, emeğinize sağlık
    teşekkürler
  • 19-09-2026, 14:44:24
    #12
    aşırı karışık geldi sen sıralama yükseltebilirmisin hizmet veriyormusunuz böyle bişeyi ücretli olarak
  • 19-09-2026, 16:07:00
    #13
    Merhaba eline sağlık hocam, sistemi kurdum ve bir kaç sorun gördüm ve düzelttim. Kullanacak arkadaşlar faydalanabilirler,

    Senaryo ekleniyor fakat panelde gözükmüyordu. Yapılan işlemlerde senaryo işlenmiyordu
    • Kaydedilen senaryonun görünmeme sorununu düzeltir.
    • Test Merkezi'ne Senaryo seçimi ekler.
    • Senaryoyu backend → Queue → Worker sistemine bağlar.
    • Senaryodaki URL'leri gerçekten ziyaret ettirir.
    • Her oturumda URL sırasını rastgele karıştırır.
    • Her sayfaya rastgele bekleme süresi verir.
    • Senaryo seçilmişse Contextual Click yerine yalnızca senaryo sayfalarını kullanır.
    • Senaryo seçilmemişse eski çalışma şekline dokunmaz.
    • Değişikliklerden önce otomatik yedek alır.
    • İşlem sonunda Docker container'ını yeniden build eder.
    cd /opt/trafficbot || exit 1
    
    bash <<'PATCH'
    set -euo pipefail
    
    cd /opt/trafficbot
    
    echo "=========================================="
    echo " ALPDAGTAS Senaryo Sistemi Patch"
    echo "=========================================="
    
    BACKUP="/opt/trafficbot/backup-scenario-$(date +%Y%m%d-%H%M%S)"
    
    mkdir -p "$BACKUP"
    
    cp src/web/WebPanelServer.ts "$BACKUP/"
    cp src/infrastructure/queue/QueueService.ts "$BACKUP/"
    cp src/application/traffic/TrafficOrchestrator.ts "$BACKUP/"
    cp public/app.js "$BACKUP/"
    
    echo "[1/3] Yedek alındı:"
    echo "$BACKUP"
    
    python3 - <<'PY'
    from pathlib import Path
    import re
    
    root = Path("/opt/trafficbot")
    
    # ==========================================================
    # QueueService.ts
    # Senaryo URL'lerini BullMQ job verisine ekle
    # ==========================================================
    
    p = root / "src/infrastructure/queue/QueueService.ts"
    s = p.read_text()
    
    if "scenarioUrls?: string[];" not in s:
    
        needle = """  intensity: 'low' | 'medium' | 'high';
    """
    
        if needle not in s:
            raise SystemExit(
                "HATA: QueueService.ts içerisinde intensity alanı bulunamadı."
            )
    
        s = s.replace(
            needle,
            needle + """  scenarioUrls?: string[];
    """,
            1
        )
    
        p.write_text(s)
    
        print("OK: QueueService scenarioUrls eklendi.")
    
    else:
        print("SKIP: QueueService zaten güncel.")
    
    
    # ==========================================================
    # WebPanelServer.ts
    # Senaryoyu test job'una bağla
    # ==========================================================
    
    p = root / "src/web/WebPanelServer.ts"
    s = p.read_text()
    
    
    if "scenarioUrls=(scenario?.paths||[])" not in s:
    
        match = re.search(
            r" private async queueTest\(site:any,requested:number,duration:number,ownerId:string\)\{.*?\}\n private visible",
            s,
            re.S
        )
    
        if not match:
            raise SystemExit(
                "HATA: WebPanelServer queueTest fonksiyonu bulunamadı."
            )
    
        new_queue = """ private async queueTest(site:any,requested:number,duration:number,ownerId:string,scenario?:any){const d=this.store.get(),n=Math.max(1,Math.min(5000,requested)),mins=Math.max(1,Math.min(1440,duration)),base=new URL(site.url),scenarioUrls=(scenario?.paths||[]).map((x:any)=>{try{const u=new URL(String(x),base);return u.origin===base.origin?u.toString():null}catch{return null}}).filter(Boolean) as string[],test:any={id:'test_'+Date.now().toString(36),siteId:site.id,siteName:site.name,url:site.url,requested:n,durationMinutes:mins,status:'queued',createdAt:new Date().toISOString(),jobIds:[],ownerId,scenarioId:scenario?.id||'',scenarioName:scenario?.name||''};this.store.addTest(test);const concurrency=Math.max(1,Math.min(500,Number(d.settings.maxConcurrency||5)));QueueService.setConcurrency(concurrency);const p=d.settings.proxy;const proxy=p?.enabled&&p.host&&p.port?{host:p.host,port:Number(p.port),username:p.username,password:p.password}:undefined;const ids:string[]=[];for(let i=0;i<n;i++){const id=await QueueService.addSession({url:site.url,durationMinutes:mins,intensity:'medium',proxy,scenarioUrls});if(id)ids.push(id)}this.store.updateTest(test.id,{jobIds:ids,status:ids.length?'running':'stopped'});return{testId:test.id,queued:ids.length,requested:n,scenarioId:scenario?.id||null,scenarioName:scenario?.name||null}}
     private visible"""
    
        s = (
            s[:match.start()]
            + new_queue
            + s[match.end():]
        )
    
        print("OK: queueTest senaryo desteği eklendi.")
    
    else:
        print("SKIP: queueTest zaten güncel.")
    
    
    # ----------------------------------------------------------
    # /api/tests POST içerisinde scenarioId desteği
    # ----------------------------------------------------------
    
    if "const scenarioId=String(b.scenarioId||'')" not in s:
    
        old = """if(p==='/api/tests'&&m==='POST'){if(!QueueService.isEnabled())return json(res,409,{error:'Bot kapalı. Sunucu bölümünden botu başlatın.'});const b=await body(req),d:any=this.visible(this.store.get(),me),site=d.sites.find((x:any)=>x.id===b.siteId);if(!site)return json(res,404,{error:'Site bulunamadı'});const n=Math.max(1,Math.min(5000,Number(b.requested||site.defaultConcurrency)));if(!this.store.consumeCredits(me.id,n))return json(res,402,{error:`Yetersiz kredi. ${n} kredi gerekli.`});return json(res,202,{ok:true,...await this.queueTest(site,n,Number(b.durationMinutes||site.durationMinutes),me.id)})}"""
    
        new = """if(p==='/api/tests'&&m==='POST'){if(!QueueService.isEnabled())return json(res,409,{error:'Bot kapalı. Sunucu bölümünden botu başlatın.'});const b=await body(req),d:any=this.visible(this.store.get(),me),site=d.sites.find((x:any)=>x.id===b.siteId);if(!site)return json(res,404,{error:'Site bulunamadı'});const scenarioId=String(b.scenarioId||''),scenario=scenarioId?d.scenarios.find((x:any)=>x.id===scenarioId&&x.siteId===site.id):undefined;if(scenarioId&&!scenario)return json(res,400,{error:'Seçilen senaryo bu siteye ait değil veya bulunamadı'});const n=Math.max(1,Math.min(5000,Number(b.requested||site.defaultConcurrency)));if(!this.store.consumeCredits(me.id,n))return json(res,402,{error:`Yetersiz kredi. ${n} kredi gerekli.`});return json(res,202,{ok:true,...await this.queueTest(site,n,Number(b.durationMinutes||site.durationMinutes),me.id,scenario)})}"""
    
        if old not in s:
            raise SystemExit(
                "HATA: /api/tests POST bölümü bulunamadı."
            )
    
        s = s.replace(old, new, 1)
    
        print("OK: API scenarioId desteği eklendi.")
    
    else:
        print("SKIP: API scenarioId zaten mevcut.")
    
    p.write_text(s)
    
    
    # ==========================================================
    # public/app.js
    # Senaryo listeleme + Test Merkezi seçimi
    # ==========================================================
    
    p = root / "public/app.js"
    s = p.read_text()
    
    
    # Senaryolar kaydoluyor ama görünmüyor hatası
    
    if "function scenarios(){let list=state.settings.scenarios||[];" in s:
    
        s = s.replace(
            "function scenarios(){let list=state.settings.scenarios||[];",
            "function scenarios(){let list=state.scenarios||[];",
            1
        )
    
        print("OK: Senaryo listeleme hatası düzeltildi.")
    
    else:
        print("SKIP: Senaryo listeleme zaten güncel.")
    
    
    # ----------------------------------------------------------
    # Test Merkezi'ne Senaryo dropdown
    # ----------------------------------------------------------
    
    if "Senaryo yok (rastgele gezinme)" not in s:
    
        new_tests = """function tests(){let scenarios=state.scenarios||[];return `<div class="card"><h2>Test Merkezi</h2><form class="form" onsubmit="startTest(event)"><label>Site<select name="siteId">${state.sites.map(s=>`<option value="${s.id}">${esc(s.name)}</option>`)}</select></label><label>Senaryo<select name="scenarioId"><option value="">Senaryo yok (rastgele gezinme)</option>${scenarios.map(x=>{let s=state.sites.find(z=>z.id===x.siteId);return `<option value="${x.id}">${esc(x.name)} · ${esc(s?.name||'')}</option>`}).join('')}</select></label><label>Toplam oturum<input name="requested" type="number" value="5" min="1" max="5000"></label><label>Süre (dk)<input name="duration" type="number" value="3" min="1"></label><button class="btn full">Testi Başlat</button></form></div><br><div class="card">${testTable(state.tests)}</div>`}"""
    
        s, count = re.subn(
            r"function tests\(\)\{.*?\}(?=function testTable)",
            new_tests,
            s,
            count=1,
            flags=re.S
        )
    
        if count != 1:
            raise SystemExit(
                "HATA: app.js tests() fonksiyonu bulunamadı."
            )
    
        print("OK: Test Merkezi'ne Senaryo seçimi eklendi.")
    
    else:
        print("SKIP: Senaryo seçimi zaten mevcut.")
    
    
    # ----------------------------------------------------------
    # startTest içerisine scenarioId
    # ----------------------------------------------------------
    
    if "scenarioId:f.scenarioId.value||''" not in s:
    
        old = """async function startTest(e){e.preventDefault();let f=e.target;try{let r=await api('/api/tests',{method:'POST',body:{siteId:f.siteId.value,requested:+f.requested.value,durationMinutes:+f.duration.value}});"""
    
        new = """async function startTest(e){e.preventDefault();let f=e.target;try{let r=await api('/api/tests',{method:'POST',body:{siteId:f.siteId.value,scenarioId:f.scenarioId.value||'',requested:+f.requested.value,durationMinutes:+f.duration.value}});"""
    
        if old not in s:
            raise SystemExit(
                "HATA: startTest() fonksiyonu bulunamadı."
            )
    
        s = s.replace(old, new, 1)
    
        print("OK: startTest scenarioId gönderecek.")
    
    else:
        print("SKIP: startTest zaten güncel.")
    
    p.write_text(s)
    
    
    # ==========================================================
    # TrafficOrchestrator.ts
    # Senaryoyu gerçekten çalıştır
    # ==========================================================
    
    p = root / "src/application/traffic/TrafficOrchestrator.ts"
    s = p.read_text()
    
    
    # run metoduna scenarioUrls parametresi
    
    old_signature = """  async run(session: Session, options: Partial<BrowserOptions> = {}): Promise<void> {"""
    
    new_signature = """  async run(session: Session, options: Partial<BrowserOptions> = {}, scenarioUrls: string[] = []): Promise<void> {"""
    
    if old_signature in s:
    
        s = s.replace(
            old_signature,
            new_signature,
            1
        )
    
        print("OK: TrafficOrchestrator scenarioUrls parametresi eklendi.")
    
    elif new_signature in s:
    
        print("SKIP: TrafficOrchestrator parametresi zaten mevcut.")
    
    else:
    
        raise SystemExit(
            "HATA: TrafficOrchestrator run() bulunamadı."
        )
    
    
    # ----------------------------------------------------------
    # Normal gezinme bloğunu senaryo destekli hale getir
    # ----------------------------------------------------------
    
    if "Scenario plan generated" not in s:
    
        block = re.search(
            r'      // Execute 4 steps with randomized "Thinking Heatmaps" \(non-linear stay times\).*?        await this\.performContextualClick\(\);\n      \}',
            s,
            re.S
        )
    
        if not block:
            raise SystemExit(
                "HATA: TrafficOrchestrator gezinme bloğu bulunamadı."
            )
    
        new_block = """      // Senaryo seçildiyse senaryo yolları kullanılır.
          // Senaryo seçilmediyse eski Contextual Click sistemi devam eder.
    
          const plannedScenarioUrls = Array.isArray(scenarioUrls)
            ? [...scenarioUrls.filter(Boolean)]
            : [];
    
          // Her oturumda URL sırasını ayrı ayrı karıştır.
          for (let i = plannedScenarioUrls.length - 1; i > 0; i--) {
    
            const j = Math.floor(
              Math.random() * (i + 1)
            );
    
            [
              plannedScenarioUrls[i],
              plannedScenarioUrls[j]
            ] = [
              plannedScenarioUrls[j],
              plannedScenarioUrls[i]
            ];
          }
    
          const numSteps =
            plannedScenarioUrls.length > 0
              ? plannedScenarioUrls.length
              : 4;
    
          // Oturum süresinin yaklaşık %80'i sayfalara dağıtılır.
          const totalLoopTime =
            Math.floor(config.durationMs * 0.8);
    
          // Her sayfaya farklı random ağırlık oluştur.
          const stayWeights = Array.from(
            { length: numSteps },
            () => 0.25 + (Math.random() * 1.75)
          );
    
          const totalWeight =
            stayWeights.reduce(
              (a, b) => a + b,
              0
            );
    
          // Minimum 3 saniye.
          const stayDurations =
            stayWeights.map(
              w =>
                Math.max(
                  3000,
                  Math.floor(
                    (w / totalWeight) *
                    totalLoopTime
                  )
                )
            );
    
          logger.info(
            'Scenario plan generated',
            {
              scenarioUrls: plannedScenarioUrls,
              stayDurations
            }
          );
    
          for (
            let i = 0;
            i < numSteps;
            i++
          ) {
    
            if (
              plannedScenarioUrls.length > 0
            ) {
    
              const targetUrl =
                plannedScenarioUrls[i];
    
              logger.info(
                `Scenario navigation ${i + 1}/${numSteps}`,
                {
                  url: targetUrl
                }
              );
    
              await this.engine.navigate(
                targetUrl
              );
    
              await this.engine.waitForNetworkIdle();
            }
    
            const currentStay =
              stayDurations[i];
    
            logger.info(
              `Step ${i+1}/${numSteps}: Staying for ${currentStay}ms...`
            );
    
            if (Config.HUMAN_BEHAVIOR) {
    
              const stepStart =
                Date.now();
    
              while (
                Date.now() - stepStart <
                currentStay
              ) {
    
                await BehaviorService.simulateRandomAction(
                  this.engine,
                  config.viewport,
                  {
                    intensity:
                      Config.BEHAVIOR_INTENSITY
                  }
                );
              }
    
            } else {
    
              await this.engine.wait(
                currentStay
              );
            }
    
            // Senaryo seçilmemişse eski sistem.
            if (
              plannedScenarioUrls.length === 0
            ) {
    
              await this.performContextualClick();
            }
          }"""
    
        s = (
            s[:block.start()]
            + new_block
            + s[block.end():]
        )
    
        print("OK: Random sıra ve random süreli senaryo motoru eklendi.")
    
    else:
    
        print("SKIP: Senaryo motoru zaten mevcut.")
    
    
    # ----------------------------------------------------------
    # Job'daki scenarioUrls'i run() metoduna aktar
    # ----------------------------------------------------------
    
    old_call = """    await this.run(session, {
          headless: Config.HEADLESS,
          platform: fingerprint.platform,
          fingerprintScript: FingerprintService.getInjectionScript(fingerprint)
        });"""
    
    new_call = """    await this.run(session, {
          headless: Config.HEADLESS,
          platform: fingerprint.platform,
          fingerprintScript: FingerprintService.getInjectionScript(fingerprint)
        }, Array.isArray(data.scenarioUrls) ? data.scenarioUrls : []);"""
    
    if old_call in s:
    
        s = s.replace(
            old_call,
            new_call,
            1
        )
    
        print("OK: Worker senaryo URL'lerini TrafficOrchestrator'a aktaracak.")
    
    elif new_call in s:
    
        print("SKIP: Worker bağlantısı zaten mevcut.")
    
    else:
    
        raise SystemExit(
            "HATA: runFromJob çağrısı bulunamadı."
        )
    
    p.write_text(s)
    
    
    print("")
    print("==========================================")
    print(" SENARYO PATCH BAŞARIYLA UYGULANDI")
    print("==========================================")
    PY
    
    
    echo ""
    echo "[2/3] Docker yeniden build ediliyor..."
    
    docker compose up -d --build traffic-bot
    
    
    echo ""
    echo "[3/3] Container durumu:"
    
    docker compose ps
    
    
    echo ""
    echo "=========================================="
    echo " PATCH TAMAMLANDI"
    echo "=========================================="
    echo ""
    echo "Paneli Ctrl + F5 ile yenileyin."
    echo ""
    echo "Yedek dizini:"
    echo "$BACKUP"
    echo ""
    PATCH
  • 19-09-2026, 18:22:23
    #14
    Üyeliği durduruldu
    UndeaD adlı üyeden alıntı: mesajı görüntüle
    Merhaba eline sağlık hocam, sistemi kurdum ve bir kaç sorun gördüm ve düzelttim. Kullanacak arkadaşlar faydalanabilirler,

    Senaryo ekleniyor fakat panelde gözükmüyordu. Yapılan işlemlerde senaryo işlenmiyordu
    • Kaydedilen senaryonun görünmeme sorununu düzeltir.
    • Test Merkezi'ne Senaryo seçimi ekler.
    • Senaryoyu backend → Queue → Worker sistemine bağlar.
    • Senaryodaki URL'leri gerçekten ziyaret ettirir.
    • Her oturumda URL sırasını rastgele karıştırır.
    • Her sayfaya rastgele bekleme süresi verir.
    • Senaryo seçilmişse Contextual Click yerine yalnızca senaryo sayfalarını kullanır.
    • Senaryo seçilmemişse eski çalışma şekline dokunmaz.
    • Değişikliklerden önce otomatik yedek alır.
    • İşlem sonunda Docker container'ını yeniden build eder.
    cd /opt/trafficbot || exit 1
    
    bash <<'PATCH'
    set -euo pipefail
    
    cd /opt/trafficbot
    
    echo "=========================================="
    echo " ALPDAGTAS Senaryo Sistemi Patch"
    echo "=========================================="
    
    BACKUP="/opt/trafficbot/backup-scenario-$(date +%Y%m%d-%H%M%S)"
    
    mkdir -p "$BACKUP"
    
    cp src/web/WebPanelServer.ts "$BACKUP/"
    cp src/infrastructure/queue/QueueService.ts "$BACKUP/"
    cp src/application/traffic/TrafficOrchestrator.ts "$BACKUP/"
    cp public/app.js "$BACKUP/"
    
    echo "[1/3] Yedek alındı:"
    echo "$BACKUP"
    
    python3 - <<'PY'
    from pathlib import Path
    import re
    
    root = Path("/opt/trafficbot")
    
    # ==========================================================
    # QueueService.ts
    # Senaryo URL'lerini BullMQ job verisine ekle
    # ==========================================================
    
    p = root / "src/infrastructure/queue/QueueService.ts"
    s = p.read_text()
    
    if "scenarioUrls?: string[];" not in s:
    
        needle = """  intensity: 'low' | 'medium' | 'high';
    """
    
        if needle not in s:
            raise SystemExit(
                "HATA: QueueService.ts içerisinde intensity alanı bulunamadı."
            )
    
        s = s.replace(
            needle,
            needle + """  scenarioUrls?: string[];
    """,
            1
        )
    
        p.write_text(s)
    
        print("OK: QueueService scenarioUrls eklendi.")
    
    else:
        print("SKIP: QueueService zaten güncel.")
    
    
    # ==========================================================
    # WebPanelServer.ts
    # Senaryoyu test job'una bağla
    # ==========================================================
    
    p = root / "src/web/WebPanelServer.ts"
    s = p.read_text()
    
    
    if "scenarioUrls=(scenario?.paths||[])" not in s:
    
        match = re.search(
            r" private async queueTest\(site:any,requested:number,duration:number,ownerId:string\)\{.*?\}\n private visible",
            s,
            re.S
        )
    
        if not match:
            raise SystemExit(
                "HATA: WebPanelServer queueTest fonksiyonu bulunamadı."
            )
    
        new_queue = """ private async queueTest(site:any,requested:number,duration:number,ownerId:string,scenario?:any){const d=this.store.get(),n=Math.max(1,Math.min(5000,requested)),mins=Math.max(1,Math.min(1440,duration)),base=new URL(site.url),scenarioUrls=(scenario?.paths||[]).map((x:any)=>{try{const u=new URL(String(x),base);return u.origin===base.origin?u.toString():null}catch{return null}}).filter(Boolean) as string[],test:any={id:'test_'+Date.now().toString(36),siteId:site.id,siteName:site.name,url:site.url,requested:n,durationMinutes:mins,status:'queued',createdAt:new Date().toISOString(),jobIds:[],ownerId,scenarioId:scenario?.id||'',scenarioName:scenario?.name||''};this.store.addTest(test);const concurrency=Math.max(1,Math.min(500,Number(d.settings.maxConcurrency||5)));QueueService.setConcurrency(concurrency);const p=d.settings.proxy;const proxy=p?.enabled&&p.host&&p.port?{host:p.host,port:Number(p.port),username:p.username,password:p.password}:undefined;const ids:string[]=[];for(let i=0;i<n;i++){const id=await QueueService.addSession({url:site.url,durationMinutes:mins,intensity:'medium',proxy,scenarioUrls});if(id)ids.push(id)}this.store.updateTest(test.id,{jobIds:ids,status:ids.length?'running':'stopped'});return{testId:test.id,queued:ids.length,requested:n,scenarioId:scenario?.id||null,scenarioName:scenario?.name||null}}
     private visible"""
    
        s = (
            s[:match.start()]
            + new_queue
            + s[match.end():]
        )
    
        print("OK: queueTest senaryo desteği eklendi.")
    
    else:
        print("SKIP: queueTest zaten güncel.")
    
    
    # ----------------------------------------------------------
    # /api/tests POST içerisinde scenarioId desteği
    # ----------------------------------------------------------
    
    if "const scenarioId=String(b.scenarioId||'')" not in s:
    
        old = """if(p==='/api/tests'&&m==='POST'){if(!QueueService.isEnabled())return json(res,409,{error:'Bot kapalı. Sunucu bölümünden botu başlatın.'});const b=await body(req),d:any=this.visible(this.store.get(),me),site=d.sites.find((x:any)=>x.id===b.siteId);if(!site)return json(res,404,{error:'Site bulunamadı'});const n=Math.max(1,Math.min(5000,Number(b.requested||site.defaultConcurrency)));if(!this.store.consumeCredits(me.id,n))return json(res,402,{error:`Yetersiz kredi. ${n} kredi gerekli.`});return json(res,202,{ok:true,...await this.queueTest(site,n,Number(b.durationMinutes||site.durationMinutes),me.id)})}"""
    
        new = """if(p==='/api/tests'&&m==='POST'){if(!QueueService.isEnabled())return json(res,409,{error:'Bot kapalı. Sunucu bölümünden botu başlatın.'});const b=await body(req),d:any=this.visible(this.store.get(),me),site=d.sites.find((x:any)=>x.id===b.siteId);if(!site)return json(res,404,{error:'Site bulunamadı'});const scenarioId=String(b.scenarioId||''),scenario=scenarioId?d.scenarios.find((x:any)=>x.id===scenarioId&&x.siteId===site.id):undefined;if(scenarioId&&!scenario)return json(res,400,{error:'Seçilen senaryo bu siteye ait değil veya bulunamadı'});const n=Math.max(1,Math.min(5000,Number(b.requested||site.defaultConcurrency)));if(!this.store.consumeCredits(me.id,n))return json(res,402,{error:`Yetersiz kredi. ${n} kredi gerekli.`});return json(res,202,{ok:true,...await this.queueTest(site,n,Number(b.durationMinutes||site.durationMinutes),me.id,scenario)})}"""
    
        if old not in s:
            raise SystemExit(
                "HATA: /api/tests POST bölümü bulunamadı."
            )
    
        s = s.replace(old, new, 1)
    
        print("OK: API scenarioId desteği eklendi.")
    
    else:
        print("SKIP: API scenarioId zaten mevcut.")
    
    p.write_text(s)
    
    
    # ==========================================================
    # public/app.js
    # Senaryo listeleme + Test Merkezi seçimi
    # ==========================================================
    
    p = root / "public/app.js"
    s = p.read_text()
    
    
    # Senaryolar kaydoluyor ama görünmüyor hatası
    
    if "function scenarios(){let list=state.settings.scenarios||[];" in s:
    
        s = s.replace(
            "function scenarios(){let list=state.settings.scenarios||[];",
            "function scenarios(){let list=state.scenarios||[];",
            1
        )
    
        print("OK: Senaryo listeleme hatası düzeltildi.")
    
    else:
        print("SKIP: Senaryo listeleme zaten güncel.")
    
    
    # ----------------------------------------------------------
    # Test Merkezi'ne Senaryo dropdown
    # ----------------------------------------------------------
    
    if "Senaryo yok (rastgele gezinme)" not in s:
    
        new_tests = """function tests(){let scenarios=state.scenarios||[];return `<div class="card"><h2>Test Merkezi</h2><form class="form" onsubmit="startTest(event)"><label>Site<select name="siteId">${state.sites.map(s=>`<option value="${s.id}">${esc(s.name)}</option>`)}</select></label><label>Senaryo<select name="scenarioId"><option value="">Senaryo yok (rastgele gezinme)</option>${scenarios.map(x=>{let s=state.sites.find(z=>z.id===x.siteId);return `<option value="${x.id}">${esc(x.name)} · ${esc(s?.name||'')}</option>`}).join('')}</select></label><label>Toplam oturum<input name="requested" type="number" value="5" min="1" max="5000"></label><label>Süre (dk)<input name="duration" type="number" value="3" min="1"></label><button class="btn full">Testi Başlat</button></form></div><br><div class="card">${testTable(state.tests)}</div>`}"""
    
        s, count = re.subn(
            r"function tests\(\)\{.*?\}(?=function testTable)",
            new_tests,
            s,
            count=1,
            flags=re.S
        )
    
        if count != 1:
            raise SystemExit(
                "HATA: app.js tests() fonksiyonu bulunamadı."
            )
    
        print("OK: Test Merkezi'ne Senaryo seçimi eklendi.")
    
    else:
        print("SKIP: Senaryo seçimi zaten mevcut.")
    
    
    # ----------------------------------------------------------
    # startTest içerisine scenarioId
    # ----------------------------------------------------------
    
    if "scenarioId:f.scenarioId.value||''" not in s:
    
        old = """async function startTest(e){e.preventDefault();let f=e.target;try{let r=await api('/api/tests',{method:'POST',body:{siteId:f.siteId.value,requested:+f.requested.value,durationMinutes:+f.duration.value}});"""
    
        new = """async function startTest(e){e.preventDefault();let f=e.target;try{let r=await api('/api/tests',{method:'POST',body:{siteId:f.siteId.value,scenarioId:f.scenarioId.value||'',requested:+f.requested.value,durationMinutes:+f.duration.value}});"""
    
        if old not in s:
            raise SystemExit(
                "HATA: startTest() fonksiyonu bulunamadı."
            )
    
        s = s.replace(old, new, 1)
    
        print("OK: startTest scenarioId gönderecek.")
    
    else:
        print("SKIP: startTest zaten güncel.")
    
    p.write_text(s)
    
    
    # ==========================================================
    # TrafficOrchestrator.ts
    # Senaryoyu gerçekten çalıştır
    # ==========================================================
    
    p = root / "src/application/traffic/TrafficOrchestrator.ts"
    s = p.read_text()
    
    
    # run metoduna scenarioUrls parametresi
    
    old_signature = """  async run(session: Session, options: Partial<BrowserOptions> = {}): Promise<void> {"""
    
    new_signature = """  async run(session: Session, options: Partial<BrowserOptions> = {}, scenarioUrls: string[] = []): Promise<void> {"""
    
    if old_signature in s:
    
        s = s.replace(
            old_signature,
            new_signature,
            1
        )
    
        print("OK: TrafficOrchestrator scenarioUrls parametresi eklendi.")
    
    elif new_signature in s:
    
        print("SKIP: TrafficOrchestrator parametresi zaten mevcut.")
    
    else:
    
        raise SystemExit(
            "HATA: TrafficOrchestrator run() bulunamadı."
        )
    
    
    # ----------------------------------------------------------
    # Normal gezinme bloğunu senaryo destekli hale getir
    # ----------------------------------------------------------
    
    if "Scenario plan generated" not in s:
    
        block = re.search(
            r'      // Execute 4 steps with randomized "Thinking Heatmaps" \(non-linear stay times\).*?        await this\.performContextualClick\(\);\n      \}',
            s,
            re.S
        )
    
        if not block:
            raise SystemExit(
                "HATA: TrafficOrchestrator gezinme bloğu bulunamadı."
            )
    
        new_block = """      // Senaryo seçildiyse senaryo yolları kullanılır.
          // Senaryo seçilmediyse eski Contextual Click sistemi devam eder.
    
          const plannedScenarioUrls = Array.isArray(scenarioUrls)
            ? [...scenarioUrls.filter(Boolean)]
            : [];
    
          // Her oturumda URL sırasını ayrı ayrı karıştır.
          for (let i = plannedScenarioUrls.length - 1; i > 0; i--) {
    
            const j = Math.floor(
              Math.random() * (i + 1)
            );
    
            [
              plannedScenarioUrls[i],
              plannedScenarioUrls[j]
            ] = [
              plannedScenarioUrls[j],
              plannedScenarioUrls[i]
            ];
          }
    
          const numSteps =
            plannedScenarioUrls.length > 0
              ? plannedScenarioUrls.length
              : 4;
    
          // Oturum süresinin yaklaşık %80'i sayfalara dağıtılır.
          const totalLoopTime =
            Math.floor(config.durationMs * 0.8);
    
          // Her sayfaya farklı random ağırlık oluştur.
          const stayWeights = Array.from(
            { length: numSteps },
            () => 0.25 + (Math.random() * 1.75)
          );
    
          const totalWeight =
            stayWeights.reduce(
              (a, b) => a + b,
              0
            );
    
          // Minimum 3 saniye.
          const stayDurations =
            stayWeights.map(
              w =>
                Math.max(
                  3000,
                  Math.floor(
                    (w / totalWeight) *
                    totalLoopTime
                  )
                )
            );
    
          logger.info(
            'Scenario plan generated',
            {
              scenarioUrls: plannedScenarioUrls,
              stayDurations
            }
          );
    
          for (
            let i = 0;
            i < numSteps;
            i++
          ) {
    
            if (
              plannedScenarioUrls.length > 0
            ) {
    
              const targetUrl =
                plannedScenarioUrls[i];
    
              logger.info(
                `Scenario navigation ${i + 1}/${numSteps}`,
                {
                  url: targetUrl
                }
              );
    
              await this.engine.navigate(
                targetUrl
              );
    
              await this.engine.waitForNetworkIdle();
            }
    
            const currentStay =
              stayDurations[i];
    
            logger.info(
              `Step ${i+1}/${numSteps}: Staying for ${currentStay}ms...`
            );
    
            if (Config.HUMAN_BEHAVIOR) {
    
              const stepStart =
                Date.now();
    
              while (
                Date.now() - stepStart <
                currentStay
              ) {
    
                await BehaviorService.simulateRandomAction(
                  this.engine,
                  config.viewport,
                  {
                    intensity:
                      Config.BEHAVIOR_INTENSITY
                  }
                );
              }
    
            } else {
    
              await this.engine.wait(
                currentStay
              );
            }
    
            // Senaryo seçilmemişse eski sistem.
            if (
              plannedScenarioUrls.length === 0
            ) {
    
              await this.performContextualClick();
            }
          }"""
    
        s = (
            s[:block.start()]
            + new_block
            + s[block.end():]
        )
    
        print("OK: Random sıra ve random süreli senaryo motoru eklendi.")
    
    else:
    
        print("SKIP: Senaryo motoru zaten mevcut.")
    
    
    # ----------------------------------------------------------
    # Job'daki scenarioUrls'i run() metoduna aktar
    # ----------------------------------------------------------
    
    old_call = """    await this.run(session, {
          headless: Config.HEADLESS,
          platform: fingerprint.platform,
          fingerprintScript: FingerprintService.getInjectionScript(fingerprint)
        });"""
    
    new_call = """    await this.run(session, {
          headless: Config.HEADLESS,
          platform: fingerprint.platform,
          fingerprintScript: FingerprintService.getInjectionScript(fingerprint)
        }, Array.isArray(data.scenarioUrls) ? data.scenarioUrls : []);"""
    
    if old_call in s:
    
        s = s.replace(
            old_call,
            new_call,
            1
        )
    
        print("OK: Worker senaryo URL'lerini TrafficOrchestrator'a aktaracak.")
    
    elif new_call in s:
    
        print("SKIP: Worker bağlantısı zaten mevcut.")
    
    else:
    
        raise SystemExit(
            "HATA: runFromJob çağrısı bulunamadı."
        )
    
    p.write_text(s)
    
    
    print("")
    print("==========================================")
    print(" SENARYO PATCH BAŞARIYLA UYGULANDI")
    print("==========================================")
    PY
    
    
    echo ""
    echo "[2/3] Docker yeniden build ediliyor..."
    
    docker compose up -d --build traffic-bot
    
    
    echo ""
    echo "[3/3] Container durumu:"
    
    docker compose ps
    
    
    echo ""
    echo "=========================================="
    echo " PATCH TAMAMLANDI"
    echo "=========================================="
    echo ""
    echo "Paneli Ctrl + F5 ile yenileyin."
    echo ""
    echo "Yedek dizini:"
    echo "$BACKUP"
    echo ""
    PATCH

    Teşekkürler
  • 19-09-2026, 21:07:44
    #15
    Teşekkürler hocam, emeğine sağlık..