__( 'Rooster One Google Ads', 'ai-eshop-optimizer' ),
'description' => __( 'Turn what your shop already knows — the nightly boost ranking, a running competition, an event, a landing page with a form — into a complete Google Ads campaign: ad copy, descriptions and keywords, written from your own catalogue and grounded in your own demand signals. You read the whole ad before anything exists. Rooster then creates it inside your Google Ads account PAUSED, and only you can turn it on.', 'ai-eshop-optimizer' ),
'bullets' => array(
__( 'Campaigns composed from the same intelligence that ranks your organic promotion', 'ai-eshop-optimizer' ),
__( 'Every headline, description and keyword reviewable before it is created', 'ai-eshop-optimizer' ),
__( 'Created PAUSED in your own account — Rooster can never start your spend', 'ai-eshop-optimizer' ),
__( 'Sales or lead-form goals, with spend flowing back into the ROI cockpit', 'ai-eshop-optimizer' ),
),
);
}
public function register_hooks() {
add_action( 'wp_ajax_aieo_ads_compose', array( $this, 'ajax_compose' ) );
add_action( 'wp_ajax_aieo_ads_push', array( $this, 'ajax_push' ) );
add_action( 'wp_ajax_aieo_ads_activate', array( $this, 'ajax_activate' ) );
add_action( 'wp_ajax_aieo_ads_archive', array( $this, 'ajax_archive' ) );
}
private function guard() {
check_ajax_referer( self::NONCE, 'nonce' );
if ( ! current_user_can( self::CAP ) ) {
wp_send_json_error( array( 'msg' => 'forbidden' ), 403 );
}
}
private function engine_ready() {
return class_exists( 'AIEO_Ads_Plan' ) && class_exists( 'AIEO_Ads_Composer' ) && class_exists( 'AIEO_Ads_Google_Driver' );
}
public function ajax_compose() {
$this->guard();
if ( ! $this->engine_ready() ) {
wp_send_json_error( array( 'msg' => __( 'The campaign engine is not loaded on this site.', 'ai-eshop-optimizer' ) ) );
}
$source_type = isset( $_POST['source_type'] ) ? sanitize_key( wp_unslash( $_POST['source_type'] ) ) : '';
if ( ! in_array( $source_type, self::SOURCES, true ) ) {
wp_send_json_error( array( 'msg' => __( 'Choose what the campaign should advertise.', 'ai-eshop-optimizer' ) ) );
}
$source_id = isset( $_POST['source_id'] ) ? absint( wp_unslash( $_POST['source_id'] ) ) : 0;
if ( 'boost_slate' !== $source_type && $source_id <= 0 ) {
wp_send_json_error( array( 'msg' => __( 'Pick which one to advertise.', 'ai-eshop-optimizer' ) ) );
}
$raw_budget = isset( $_POST['daily_budget'] ) ? sanitize_text_field( wp_unslash( $_POST['daily_budget'] ) ) : '';
$budget = (float) str_replace( ',', '.', $raw_budget );
if ( $budget <= 0 ) {
wp_send_json_error( array( 'msg' => __( 'Set a daily budget above zero.', 'ai-eshop-optimizer' ) ) );
}
$objective = isset( $_POST['objective'] ) ? sanitize_key( wp_unslash( $_POST['objective'] ) ) : '';
if ( ! in_array( $objective, array( '', AIEO_Ads_Plan::OBJECTIVE_SALES, AIEO_Ads_Plan::OBJECTIVE_LEADS ), true ) ) {
$objective = '';
}
$name = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( $_POST['name'] ) ) : '';
$plan = AIEO_Ads_Composer::compose( array(
'source_type' => $source_type,
'source_id' => $source_id,
'daily_budget' => $budget,
'objective' => $objective,
'name' => $name,
) );
if ( is_wp_error( $plan ) ) {
wp_send_json_error( array( 'msg' => $plan->get_error_message() ) );
}
$plan_id = AIEO_Ads_Plan::save_draft( (array) $plan );
if ( is_wp_error( $plan_id ) ) {
wp_send_json_error( array( 'msg' => $plan_id->get_error_message() ) );
}
wp_send_json_success( array(
'id' => (int) $plan_id,
'msg' => __( 'Draft written. Read it under Plans before creating it in Google.', 'ai-eshop-optimizer' ),
) );
}
public function ajax_push() {
$this->guard();
if ( ! $this->engine_ready() ) {
wp_send_json_error( array( 'msg' => __( 'The campaign engine is not loaded on this site.', 'ai-eshop-optimizer' ) ) );
}
$plan_id = isset( $_POST['id'] ) ? absint( wp_unslash( $_POST['id'] ) ) : 0;
if ( $plan_id <= 0 ) {
wp_send_json_error( array( 'msg' => __( 'Unknown plan.', 'ai-eshop-optimizer' ) ) );
}
$res = AIEO_Ads_Google_Driver::push( $plan_id );
if ( is_wp_error( $res ) ) {
wp_send_json_error( array( 'msg' => $res->get_error_message() ) );
}
$msg = __( 'Created in Google Ads and PAUSED. It cannot spend until you enable it there yourself.', 'ai-eshop-optimizer' );
if ( ! empty( $res['keyword_warning'] ) ) {
$msg .= ' ' . sprintf(
__( 'The campaign is fine, but the keywords did not go in: %s', 'ai-eshop-optimizer' ),
(string) $res['keyword_warning']
);
}
wp_send_json_success( array( 'msg' => $msg ) );
}
public function ajax_activate() {
$this->guard();
if ( ! $this->engine_ready() ) {
wp_send_json_error( array( 'msg' => __( 'The campaign engine is not loaded on this site.', 'ai-eshop-optimizer' ) ) );
}
$plan_id = isset( $_POST['id'] ) ? absint( wp_unslash( $_POST['id'] ) ) : 0;
if ( $plan_id <= 0 || ! AIEO_Ads_Plan::mark_activated( $plan_id ) ) {
wp_send_json_error( array( 'msg' => __( 'Could not record that. Reload the page and try again.', 'ai-eshop-optimizer' ) ) );
}
wp_send_json_success( array( 'msg' => __( 'Noted as live.', 'ai-eshop-optimizer' ) ) );
}
public function ajax_archive() {
$this->guard();
if ( ! $this->engine_ready() ) {
wp_send_json_error( array( 'msg' => __( 'The campaign engine is not loaded on this site.', 'ai-eshop-optimizer' ) ) );
}
$plan_id = isset( $_POST['id'] ) ? absint( wp_unslash( $_POST['id'] ) ) : 0;
if ( $plan_id <= 0 || ! AIEO_Ads_Plan::archive( $plan_id ) ) {
wp_send_json_error( array( 'msg' => __( 'Could not archive that plan.', 'ai-eshop-optimizer' ) ) );
}
wp_send_json_success( array( 'msg' => __( 'Archived. Anything already in Google Ads is untouched.', 'ai-eshop-optimizer' ) ) );
}
public function render_subtab() {
if ( ! $this->engine_ready() ) {
echo '
' . esc_html__( 'The campaign engine is not loaded on this site.', 'ai-eshop-optimizer' ) . '
';
return;
}
$nonce = wp_create_nonce( self::NONCE );
$this->render_compose_section();
$this->render_plans_section();
$this->render_safety_section();
$this->print_js( $nonce );
}
private function render_connection_hint() {
$url = add_query_arg(
array( 'page' => AIEO_DMM::MENU_SLUG, 'dmm_tab' => 'ad_connectors' ),
admin_url( 'admin.php' )
);
$html = wp_kses_post( sprintf(
__( 'Sending a campaign needs a connected Google Ads account. Connect one under %1$sSettings → Ad spend import%2$s, which also pulls your spend back in for ROI reporting.', 'ai-eshop-optimizer' ),
'',
''
) );
echo \Rooster\Admin\Guidance::text( wp_strip_all_tags( $html ), 'guidance', array( 'html' => $html ) );
}
private function render_compose_section() {
$currency = function_exists( 'get_woocommerce_currency' ) ? (string) get_woocommerce_currency() : '';
list( $open, $close ) = AIEO_DMM_UI::card( array(
'anchor' => 'ads-compose-a-campaign',
'icon' => 'pencil-line',
'title' => __( 'Compose a campaign', 'ai-eshop-optimizer' ),
'description' => __( 'Pick something the shop already has and Rooster writes the campaign around it — headlines, descriptions and keywords, in the language of your own catalogue. Composing costs nothing and creates nothing in Google: you get a draft to read first.', 'ai-eshop-optimizer' ),
'footer' => '
'
. '
',
'footnote' => __( 'Writing the copy uses AI credits. Google is not contacted at this step.', 'ai-eshop-optimizer' ),
) );
echo $open;
echo '
';
}
private function render_safety_section() {
list( $open, $close ) = AIEO_DMM_UI::card( array(
'anchor' => 'ads-nothing-here-can-spend-your-money',
'icon' => 'shield',
'title' => __( 'Nothing here can spend your money', 'ai-eshop-optimizer' ),
'description' => __( 'Composing a plan never contacts Google; creating one puts the campaign, ad and keywords into your account PAUSED, and only you can switch it on in Google Ads.', 'ai-eshop-optimizer' ),
'essential' => true,
) );
echo $open;
echo \Rooster\Admin\Guidance::text( __( 'Rooster creates every campaign in Google Ads with the status PAUSED, and it has no way to enable one. A paused campaign shows no ads and spends nothing, however long it sits there.', 'ai-eshop-optimizer' ) );
echo '
';
$points = array(
__( 'Composing a plan writes it here only — Google is not contacted at all.', 'ai-eshop-optimizer' ),
__( 'Creating it in Google puts the campaign, ad and keywords in your account, paused.', 'ai-eshop-optimizer' ),
__( 'You read the full ad first, and you turn it on inside Google Ads — or never.', 'ai-eshop-optimizer' ),
__( '"I activated it" only records that you did it; it does not switch anything on.', 'ai-eshop-optimizer' ),
);
foreach ( $points as $point ) {
echo '