This commit is contained in:
greggy
2025-10-07 08:54:38 +02:00
parent d7682e2073
commit fd1d053d65
8 changed files with 502 additions and 210 deletions

View File

@@ -1,165 +0,0 @@
<?php
require_once 'doctrine-config.php';
use Entity\Werbenetzwerk;
use Entity\User;
use Entity\Kampagne;
try {
// Werbenetzwerk-Entity holen
// Werbenetzwerk-User holen
$werbenetzwerkUser = $entityManager->getRepository(User::class)->findByUsername('adcity-eu');
if (!$werbenetzwerkUser) {
echo "werbenetzwerkUser-Daten nicht gefunden.\n";
exit(1);
}
$werbenetzwerk = $entityManager->getRepository(Werbenetzwerk::class)->findByUser($werbenetzwerkUser);
if (!$werbenetzwerk) {
echo "Werbenetzwerk-Daten nicht gefunden.\n";
exit(1);
}
if (!$werbenetzwerkUser) {
echo "Kein Werbenetzwerk-User gefunden.\n";
exit(1);
}
// API-Parameter vorbereiten
$apiUrl = 'https://www.adcity.eu/interface/';
$params = [
'typ' => 'forcedtextlink',
'id' => $werbenetzwerk->getUserId(),
'pw' => $werbenetzwerk->getInterfacepasswort(),
'uebrig' => 20,
'reload' => 99999,
'verguetung' => 0.0001,
'ma' => 99999
];
$fullUrl = $apiUrl . '?' . http_build_query($params);
// API-Anfrage durchführen
$response = file_get_contents($fullUrl);
if ($response === false) {
echo "Fehler beim Abrufen der API-Daten.\n";
exit(1);
}
echo "Response received:\n";
echo $response . "\n\n";
// Flatfile-Response parsen (alle Kampagnen pipe-getrennt in einer Zeile)
$parts = explode('|', trim($response));
$kampagnenCount = 0;
$updatedCount = 0;
$createdCount = 0;
// Durch alle Kampagnen iterieren (je 5, 6 oder 7 Felder pro Kampagne)
$i = 0;
while ($i < count($parts)) {
// Mindestens 5 Felder erforderlich
if ($i + 4 >= count($parts)) {
break;
}
// Erste 5 Felder auslesen
$kid = $parts[$i];
$linktext = $parts[$i + 1];
$reload = (int) $parts[$i + 2];
$uebrig = (int) $parts[$i + 3];
$verdienst = (float) $parts[$i + 4];
// Prüfen ob MA-Feld vorhanden ist (6. Feld)
$ma = 0;
$mailtext = null;
$fieldsConsumed = 5;
// Heuristik: Wenn das nächste Feld numerisch ist und klein, ist es wahrscheinlich MA
if ($i + 5 < count($parts) && is_numeric($parts[$i + 5]) && $parts[$i + 5] < 1000) {
$ma = (int) $parts[$i + 5];
$fieldsConsumed = 6;
// Prüfen ob Nachrichtentext vorhanden ist (7. Feld)
if ($i + 6 < count($parts) && !is_numeric($parts[$i + 6])) {
$mailtext = $parts[$i + 6];
$fieldsConsumed = 7;
}
}
$i += $fieldsConsumed;
$kampagnenCount++;
// Link-URL und Bild-URL generieren (USERID und SEITENID werden später beim Einbau ersetzt)
$destUrl = "https://www.adcity.eu/codes/forcedtextlinkklick.php?id={USERID}&bid=$kid&aid={SEITENID}";
echo "Processing: $linktext (KID: $kid, MA: $ma, Reload: $reload, Uebrig: $uebrig)\n";
// Prüfen ob Kampagne bereits existiert (anhand user_id, KID und is_tr=1)
$existingKampagne = $entityManager->getRepository(Kampagne::class)
->findOneBy([
'user' => $werbenetzwerkUser,
'kid' => $kid,
'is_tr' => true
]);
if ($existingKampagne) {
// Kampagne aktualisieren (verguetung, reload, uebrig)
$existingKampagne->setVerguetung($verdienst);
$existingKampagne->setReload($reload);
$existingKampagne->setUebrig($uebrig);
$existingKampagne->setLinktext($linktext);
$existingKampagne->setMa($ma);
if ($mailtext) {
$existingKampagne->setMailtext($mailtext);
$existingKampagne->setIsMail(true);
}
$entityManager->persist($existingKampagne);
$updatedCount++;
echo " → Updated existing campaign (verguetung: $verdienst, reload: $reload, uebrig: $uebrig)\n";
} else {
// Neue Kampagne erstellen
$kampagne = new Kampagne();
$kampagne->setDestUrl($destUrl);
$kampagne->setKid($kid);
$kampagne->setLinktext($linktext);
$kampagne->setMa($ma);
$kampagne->setReload($reload);
$kampagne->setVerguetung($verdienst);
$kampagne->setUebrig($uebrig);
$kampagne->setIsFb(false);
$kampagne->setIsTr(true);
$kampagne->setIsMail(false);
if ($mailtext) {
$kampagne->setMailtext($mailtext);
$kampagne->setIsMail(true);
}
$kampagne->setUser($werbenetzwerkUser);
$entityManager->persist($kampagne);
$createdCount++;
echo " → Created new campaign\n";
}
}
$entityManager->flush();
echo "\n=== Summary ===\n";
echo "Total campaigns processed: $kampagnenCount\n";
echo "Created: $createdCount\n";
echo "Updated: $updatedCount\n";
echo "\nCron job completed successfully.\n";
} catch (\Exception $e) {
echo "Fehler: " . $e->getMessage() . "\n";
echo $e->getTraceAsString() . "\n";
exit(1);
}

View File

@@ -0,0 +1,14 @@
<?php
require_once __DIR__ . '/../doctrine-config.php';
use Cron\AdCityForcedTextlinkCron;
try {
$cron = new AdCityForcedTextlinkCron($entityManager);
$cron->run();
} catch (\Exception $e) {
echo "Fehler: " . $e->getMessage() . "\n";
echo $e->getTraceAsString() . "\n";
exit(1);
}

View File

@@ -12,6 +12,11 @@ require_once "config.php";
$paths = [__DIR__ . "/src/Entity"];
$isDevMode = true;
// Composer Autoloader für src/Cron und src/Repository
$loader = require __DIR__ . '/vendor/autoload.php';
$loader->addPsr4('Cron\\', __DIR__ . '/src/Cron');
$loader->addPsr4('Repository\\', __DIR__ . '/src/Repository');
// Doctrine ORM Konfiguration
$config = ORMSetup::createAttributeMetadataConfiguration($paths, $isDevMode);

View File

@@ -49,6 +49,7 @@ final class Version20251006214042 extends AbstractMigration
id INT AUTO_INCREMENT NOT NULL,
is_fb TINYINT(1) NOT NULL,
is_tr TINYINT(1) NOT NULL,
is_ft TINYINT(1) NOT NULL,
is_mail TINYINT(1) NOT NULL,
dest_url VARCHAR(255) NOT NULL,
kid VARCHAR(100) DEFAULT NULL,

View File

@@ -15,52 +15,10 @@ try {
exit(0);
}
// User für Zuweisung holen
$users = $entityManager->getRepository(User::class)->findAll();
if (count($users) === 0) {
echo "Keine User gefunden. Bitte erst User anlegen.\n";
exit(1);
}
// Keine Beispielkampagnen mehr einfügen
// AdCity-Kampagnen werden automatisch über den Cron eingefügt
// 10 Beispiel-Kampagnen erstellen
$kampagnenData = [
['is_fb' => true, 'is_tr' => false, 'is_mail' => false, 'dest_url' => 'https://example.com/promo1', 'linktext' => 'Super Angebot 1', 'ma' => 5, 'reload' => 24],
['is_fb' => false, 'is_tr' => true, 'is_mail' => false, 'dest_url' => 'https://example.com/promo2', 'linktext' => 'Mega Deal 2', 'ma' => 3, 'reload' => 12],
['is_fb' => true, 'is_tr' => true, 'is_mail' => false, 'dest_url' => 'https://example.com/promo3', 'linktext' => 'Exklusiv Angebot 3', 'ma' => 10, 'reload' => 48],
['is_fb' => false, 'is_tr' => false, 'is_mail' => true, 'dest_url' => 'https://example.com/promo4', 'linktext' => 'Newsletter Special 4', 'ma' => 2, 'reload' => 6, 'mailtext' => 'Hallo, schau dir unser tolles Angebot an!'],
['is_fb' => true, 'is_tr' => false, 'is_mail' => true, 'dest_url' => 'https://example.com/promo5', 'linktext' => 'Combo Deal 5', 'ma' => 7, 'reload' => 36, 'mailtext' => 'Exklusive Kombination nur für dich!'],
['is_fb' => false, 'is_tr' => true, 'is_mail' => false, 'dest_url' => 'https://example.com/promo6', 'linktext' => 'Traffic Boost 6', 'ma' => 4, 'reload' => 18],
['is_fb' => true, 'is_tr' => true, 'is_mail' => true, 'dest_url' => 'https://example.com/promo7', 'linktext' => 'All-in-One 7', 'ma' => 15, 'reload' => 72, 'mailtext' => 'Maximale Reichweite mit allen Kanälen!'],
['is_fb' => false, 'is_tr' => false, 'is_mail' => false, 'dest_url' => 'https://example.com/promo8', 'linktext' => 'Basic Campaign 8', 'ma' => 1, 'reload' => 3],
['is_fb' => true, 'is_tr' => false, 'is_mail' => false, 'dest_url' => 'https://example.com/promo9', 'linktext' => 'Facebook Power 9', 'ma' => 8, 'reload' => 24],
['is_fb' => false, 'is_tr' => true, 'is_mail' => true, 'dest_url' => 'https://example.com/promo10', 'linktext' => 'Traffic + Mail 10', 'ma' => 6, 'reload' => 30, 'mailtext' => 'Doppelte Power für deine Kampagne!'],
];
$userIndex = 0;
foreach ($kampagnenData as $data) {
$kampagne = new Kampagne();
$kampagne->setIsFb($data['is_fb']);
$kampagne->setIsTr($data['is_tr']);
$kampagne->setIsMail($data['is_mail']);
$kampagne->setDestUrl($data['dest_url']);
$kampagne->setLinktext($data['linktext']);
$kampagne->setMa($data['ma']);
$kampagne->setReload($data['reload']);
if (isset($data['mailtext'])) {
$kampagne->setMailtext($data['mailtext']);
}
// User rotierend zuweisen
$kampagne->setUser($users[$userIndex % count($users)]);
$userIndex++;
$entityManager->persist($kampagne);
}
$entityManager->flush();
echo "10 Beispiel-Kampagnen wurden erfolgreich eingefügt.\n";
echo "Keine Beispiel-Kampagnen eingefügt. Kampagnen werden über Cron-Jobs verwaltet.\n";
} catch (\Exception $e) {
echo "Fehler beim Einfügen der Daten: " . $e->getMessage() . "\n";
}

129
src/Cron/AbstractCron.php Normal file
View File

@@ -0,0 +1,129 @@
<?php
namespace Cron;
use Doctrine\ORM\EntityManager;
use Entity\User;
use Entity\Werbenetzwerk;
use Entity\Kampagne;
abstract class AbstractCron
{
protected EntityManager $entityManager;
protected User $werbenetzwerkUser;
protected Werbenetzwerk $werbenetzwerk;
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
/**
* Hauptmethode zum Ausführen des Cron-Jobs
*/
abstract public function run(): void;
/**
* Gibt den Username des Werbenetzwerk-Users zurück
*/
abstract protected function getWerbenetzwerkUsername(): string;
/**
* Lädt User und Werbenetzwerk-Daten aus der Datenbank
*/
protected function loadWerbenetzwerkData(): void
{
$username = $this->getWerbenetzwerkUsername();
// Werbenetzwerk-User holen
$this->werbenetzwerkUser = $this->entityManager
->getRepository(User::class)
->findByUsername($username);
if (!$this->werbenetzwerkUser) {
throw new \RuntimeException("Werbenetzwerk-User '$username' nicht gefunden.");
}
// Werbenetzwerk-Daten holen
$this->werbenetzwerk = $this->entityManager
->getRepository(Werbenetzwerk::class)
->findByUser($this->werbenetzwerkUser);
if (!$this->werbenetzwerk) {
throw new \RuntimeException("Werbenetzwerk-Daten für User '$username' nicht gefunden.");
}
}
/**
* Führt einen API-Call durch
*/
protected function fetchFromApi(string $url, array $params = []): string
{
$fullUrl = $url;
if (!empty($params)) {
$fullUrl .= '?' . http_build_query($params);
}
$this->log("Fetching from API: $fullUrl");
$response = file_get_contents($fullUrl);
if ($response === false) {
throw new \RuntimeException("Fehler beim Abrufen der API-Daten von: $fullUrl");
}
return $response;
}
/**
* Prüft ob eine Kampagne bereits existiert
*/
protected function findExistingKampagne(string $kid, bool $isFb = false, bool $isTr = false, bool $isMail = false): ?Kampagne
{
$criteria = [
'user' => $this->werbenetzwerkUser,
'kid' => $kid
];
if ($isFb) {
$criteria['is_fb'] = true;
}
if ($isTr) {
$criteria['is_tr'] = true;
}
if ($isMail) {
$criteria['is_mail'] = true;
}
return $this->entityManager
->getRepository(Kampagne::class)
->findOneBy($criteria);
}
/**
* Speichert alle Änderungen in der Datenbank
*/
protected function flush(): void
{
$this->entityManager->flush();
}
/**
* Gibt eine Log-Nachricht aus
*/
protected function log(string $message): void
{
echo "[" . date('Y-m-d H:i:s') . "] $message\n";
}
/**
* Gibt eine Zusammenfassung aus
*/
protected function printSummary(int $total, int $created, int $updated): void
{
$this->log("=== Summary ===");
$this->log("Total campaigns processed: $total");
$this->log("Created: $created");
$this->log("Updated: $updated");
}
}

View File

@@ -0,0 +1,336 @@
<?php
namespace Cron;
use Entity\Kampagne;
class AdCityForcedTextlinkCron extends AbstractCron
{
private const API_URL = 'https://www.adcity.eu/interface/';
private const KAMPAGNE_TYPE = 'forcedtextlink';
private const LOGIN_URL = 'https://www.adcity.eu/';
private const INTERFACE_URL = 'https://www.adcity.eu/index.php?seite=content/member/interface';
private const USERNAME = 'Greggy';
private const PASSWORD = 'zOP2458E';
private string $cookieFile;
/**
* Gibt den Username des Werbenetzwerk-Users zurück
*/
protected function getWerbenetzwerkUsername(): string
{
return 'adcity-eu';
}
/**
* Konstruktor
*/
public function __construct($entityManager)
{
parent::__construct($entityManager);
$this->cookieFile = sys_get_temp_dir() . '/adcity_cookies.txt';
}
/**
* Führt den Cron-Job aus
*/
public function run(): void
{
$this->log("Starting AdCity ForcedTextlink Cron...");
// Werbenetzwerk-Daten laden
$this->loadWerbenetzwerkData();
// Umrechnungskurs aktualisieren
$this->updateUmrechnungskurs();
// API-Anfrage durchführen
$response = $this->fetchFromApi(self::API_URL, [
'typ' => self::KAMPAGNE_TYPE,
'id' => $this->werbenetzwerk->getUserId(),
'pw' => $this->werbenetzwerk->getInterfacepasswort(),
'uebrig' => 20,
'reload' => 99999,
'verguetung' => 0.0001,
'ma' => 99999
]);
$this->log("Response received:");
$this->log($response);
// Response parsen und verarbeiten
$stats = $this->processResponse($response);
// Änderungen speichern
$this->flush();
// Zusammenfassung ausgeben
$this->printSummary($stats['total'], $stats['created'], $stats['updated']);
$this->log("Cron job completed successfully.");
}
/**
* Verarbeitet die API-Response
*/
private function processResponse(string $response): array
{
$parts = explode('|', trim($response));
$kampagnenCount = 0;
$updatedCount = 0;
$createdCount = 0;
// Durch alle Kampagnen iterieren (je 5, 6 oder 7 Felder pro Kampagne)
$i = 0;
while ($i < count($parts)) {
// Mindestens 5 Felder erforderlich
if ($i + 4 >= count($parts)) {
break;
}
// Erste 5 Felder auslesen
$kid = $parts[$i];
$linktext = $parts[$i + 1];
$reload = (int) $parts[$i + 2] * 60; // API gibt Stunden zurück, DB speichert Minuten
$uebrig = (int) $parts[$i + 3];
$verdienst = (float) $parts[$i + 4];
// Prüfen ob MA-Feld vorhanden ist (6. Feld)
$ma = 0;
$mailtext = null;
$fieldsConsumed = 5;
// Heuristik: Wenn das nächste Feld numerisch ist und klein, ist es wahrscheinlich MA
if ($i + 5 < count($parts) && is_numeric($parts[$i + 5]) && $parts[$i + 5] < 1000) {
$ma = (int) $parts[$i + 5];
$fieldsConsumed = 6;
// Prüfen ob Nachrichtentext vorhanden ist (7. Feld)
if ($i + 6 < count($parts) && !is_numeric($parts[$i + 6])) {
$mailtext = $parts[$i + 6];
$fieldsConsumed = 7;
}
}
$i += $fieldsConsumed;
$kampagnenCount++;
// Vergütung in Euro umrechnen
$verguetungEuro = $verdienst * $this->werbenetzwerk->getUmrechnungskurs();
// Kampagne verarbeiten
if ($this->processKampagne($kid, $linktext, $ma, $reload, $uebrig, $verdienst, $verguetungEuro, $mailtext)) {
$updatedCount++;
} else {
$createdCount++;
}
}
return [
'total' => $kampagnenCount,
'created' => $createdCount,
'updated' => $updatedCount
];
}
/**
* Verarbeitet eine einzelne Kampagne
*
* @return bool true wenn updated, false wenn created
*/
private function processKampagne(
string $kid,
string $linktext,
int $ma,
int $reload,
int $uebrig,
float $verdienst,
float $verguetungEuro,
?string $mailtext
): bool {
// Vergütung höher als 0.1 Euro filtern
if ($verguetungEuro > 0.1) {
$this->log("Skipping: $linktext (KID: $kid, Vergütung zu hoch: $verguetungEuro €)");
// Wenn Kampagne existiert, löschen
$existingKampagne = $this->entityManager
->getRepository(Kampagne::class)
->findOneBy([
'user' => $this->werbenetzwerkUser,
'kid' => $kid,
'is_ft' => true
]);
if ($existingKampagne) {
$this->entityManager->remove($existingKampagne);
$this->log(" → Deleted existing campaign with high vergütung");
}
return false;
}
// Link-URL generieren
$destUrl = "https://www.adcity.eu/codes/forcedtextlinkklick.php?id={USERID}&bid=$kid&aid={SEITENID}";
$this->log("Processing: $linktext (KID: $kid, MA: $ma, Reload: $reload, Uebrig: $uebrig)");
// Prüfen ob Kampagne bereits existiert (is_ft = true)
$existingKampagne = $this->entityManager
->getRepository(Kampagne::class)
->findOneBy([
'user' => $this->werbenetzwerkUser,
'kid' => $kid,
'is_ft' => true
]);
if ($existingKampagne) {
// Kampagne aktualisieren
$existingKampagne->setVerguetung($verguetungEuro);
$existingKampagne->setReload($reload);
$existingKampagne->setUebrig($uebrig);
$existingKampagne->setLinktext($linktext);
$existingKampagne->setMa($ma);
if ($mailtext) {
$existingKampagne->setMailtext($mailtext);
$existingKampagne->setIsMail(true);
}
$this->entityManager->persist($existingKampagne);
$this->log(" → Updated (verguetung: $verguetungEuro €, reload: $reload, uebrig: $uebrig)");
return true;
} else {
// Neue Kampagne erstellen
$kampagne = new Kampagne();
$kampagne->setDestUrl($destUrl);
$kampagne->setKid($kid);
$kampagne->setLinktext($linktext);
$kampagne->setMa($ma);
$kampagne->setReload($reload);
$kampagne->setVerguetung($verguetungEuro);
$kampagne->setUebrig($uebrig);
$kampagne->setIsFb(false);
$kampagne->setIsTr(false);
$kampagne->setIsFt(true);
$kampagne->setIsMail(false);
if ($mailtext) {
$kampagne->setMailtext($mailtext);
$kampagne->setIsMail(true);
}
$kampagne->setUser($this->werbenetzwerkUser);
$this->entityManager->persist($kampagne);
$this->log(" → Created new campaign");
return false;
}
}
/**
* Aktualisiert den Umrechnungskurs in der Datenbank
*/
private function updateUmrechnungskurs(): void
{
$this->log("Updating Umrechnungskurs...");
// Auf AdCity einloggen
$this->loginToAdCity();
// Umrechnungskurs von der Interface-Seite abrufen
$umrechnungskurs = $this->fetchUmrechnungskurs();
if ($umrechnungskurs !== null) {
$this->werbenetzwerk->setUmrechnungskurs($umrechnungskurs);
$this->entityManager->persist($this->werbenetzwerk);
$this->entityManager->flush();
$this->log("Umrechnungskurs updated: $umrechnungskurs");
} else {
$this->log("Warning: Could not fetch Umrechnungskurs");
}
// Cookie-Datei aufräumen
if (file_exists($this->cookieFile)) {
unlink($this->cookieFile);
}
}
/**
* Loggt sich auf AdCity.eu ein
*/
private function loginToAdCity(): void
{
$this->log("Logging in to AdCity...");
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => self::LOGIN_URL,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'username' => self::USERNAME,
'password' => self::PASSWORD,
'login' => 'Login'
]),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_COOKIEJAR => $this->cookieFile,
CURLOPT_COOKIEFILE => $this->cookieFile,
CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new \RuntimeException("Login failed with HTTP code: $httpCode");
}
$this->log("Login successful");
}
/**
* Ruft den Umrechnungskurs von der Interface-Seite ab
*/
private function fetchUmrechnungskurs(): ?float
{
$this->log("Fetching Umrechnungskurs from interface page...");
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => self::INTERFACE_URL,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_COOKIEFILE => $this->cookieFile,
CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200 || !$response) {
$this->log("Failed to fetch interface page (HTTP $httpCode)");
return null;
}
// Umrechnungskurs aus der HTML-Response extrahieren
// Suche nach Patterns wie "1 Pt = 0.0000000022 €" oder ähnlich
$patterns = [
'/1\s*Pt\s*=\s*([\d.]+)\s*€/i',
'/1\s*pt\s*=\s*([\d.]+)\s*euro/i',
'/Umrechnungskurs[:\s]*([\d.]+)/i',
'/([\d.]+)\s*€\s*pro\s*Pt/i'
];
foreach ($patterns as $pattern) {
if (preg_match($pattern, $response, $matches)) {
$umrechnungskurs = (float) $matches[1];
$this->log("Found Umrechnungskurs: $umrechnungskurs");
return $umrechnungskurs;
}
}
$this->log("Could not find Umrechnungskurs in response");
return null;
}
}

View File

@@ -20,6 +20,9 @@ class Kampagne
#[ORM\Column(type: 'boolean')]
private bool $is_tr = false;
#[ORM\Column(type: 'boolean')]
private bool $is_ft = false;
#[ORM\Column(type: 'boolean')]
private bool $is_mail = false;
@@ -101,6 +104,17 @@ class Kampagne
return $this;
}
public function getIsFt(): bool
{
return $this->is_ft;
}
public function setIsFt(bool $is_ft): self
{
$this->is_ft = $is_ft;
return $this;
}
public function getIsMail(): bool
{
return $this->is_mail;