[ 'label' => __( 'Find events', 'aisentic' ), 'description' => __( 'Dates, tickets and venues', 'aisentic' ), 'icon' => 'ticket', 'matches_page' => static fn(): bool => is_singular( 'etn' ) || is_post_type_archive( 'etn' ), ], 'label' => __( 'Eventin', 'aisentic' ), 'docs' => 'https://themewinter.com/docs/plugins/aisentic/how-to-manage-eventin-events-using-aisentics-ai-chatbot/', 'icon' => 'ticket', 'available' => fn(): bool => class_exists( 'Wpeventin' ), 'fields' => [ 'recommendedLimit' => [ 'type' => 'number', 'label' => __( 'Event Suggestions', 'aisentic' ), 'help' => __( 'Maximum number of events the assistant will show per search.', 'aisentic' ), 'min' => 1, 'max' => 12, 'default' => 3, ], 'searchCacheTtlSeconds' => [ 'type' => 'number', 'label' => __( 'Search Cache TTL (sec)', 'aisentic' ), 'help' => __( 'Seconds to cache event search results. Higher values reduce API calls but delay date and seat updates.', 'aisentic' ), 'min' => 30, 'default' => 300, ], 'outOfScopeMessage' => [ 'type' => 'textarea', 'label' => __( 'Out of Scope Message', 'aisentic' ), 'help' => __( 'Shown when a visitor asks something outside the assistant\'s scope, such as an existing ticket or a refund.', 'aisentic' ), 'default' => 'I can help you find events and get tickets. For a ticket you already bought, a refund, or account help, please contact support.', ], 'starterPrompts' => [ 'type' => 'prompts', 'label' => __( 'Starter Prompts', 'aisentic' ), 'help' => __( 'Quick-reply chips shown when the chat opens.', 'aisentic' ), 'max' => 8, 'maxChars' => 100, 'default' => [ 'What events are coming up?', 'Any free events?', 'Show me events this weekend', 'What is on this month?', ], ], ], ]; return $panels; } /** * Get services * * @return array */ public function get_services(): array { return []; } /** * Add the Eventin tools to the public chat toolset * * @param array $tools Existing tools. * @param Settings $settings Settings instance. * @return array */ public function add_tools( array $tools, Settings $settings ): array { if ( ! $this->is_active_and_enabled( $settings ) ) { return $tools; } $tools[] = new Eventin_Event_Search_Tool( $settings ); $tools[] = new Eventin_Ticket_Action_Tool( $settings ); return $tools; } /** * Append the Eventin selling prompt to the public chat instructions * * @param string $prompt Existing prompt. * @param Settings $settings Settings instance. * @return string */ public function add_instructions( string $prompt, Settings $settings ): string { if ( ! $this->is_active_and_enabled( $settings ) ) { return $prompt; } $out_of_scope = (string) ( new Public_Agent_Settings( $settings ) ) ->integration( 'eventin', 'outOfScopeMessage' ); return $prompt . "\n\n" . self::sales_prompt( $out_of_scope ); } /** * Build the full Eventin selling prompt fragment. * * @param string $out_of_scope_message Message shown for out-of-scope requests. * @return string */ public static function sales_prompt( string $out_of_scope_message ): string { $lines = []; $lines[] = '## Event Ticket Mode'; $lines[] = ''; $lines[] = 'You also help visitors find events on this website and get tickets. Be warm and clear. Never push.'; $lines[] = ''; $lines[] = '### Rules for Event Information'; $lines[] = '- Never invent an event, a date, a time, a place, a price, or a seat count. Always use `eventin-event-search`.'; $lines[] = '- Quote prices exactly as the search returns them. Never round and never estimate.'; $lines[] = '- If an event is sold out, say so plainly and offer another event.'; $lines[] = '- If `seats_left` is null, the ticket has no limit set. Do not say a number.'; $lines[] = ''; $lines[] = '### Rules for Times'; $lines[] = '- Always give the timezone with a time, because visitors read this from anywhere.'; $lines[] = '- Never say "tomorrow" or "next week" on its own. Give the date.'; $lines[] = ''; $lines[] = '### Search Protocol'; $lines[] = '- Call `eventin-event-search` as soon as the visitor asks about events. Do not answer from memory.'; $lines[] = '- One exploratory search per turn. The only allowed follow-up is a single broader retry when the first search returns nothing.'; $lines[] = '- If the first search returns zero events, you MUST retry once with a broader query before saying nothing was found.'; $lines[] = '- For a broad question ("what is on", "any events soon"), call the tool with an EMPTY query instead of searching those words.'; $lines[] = ''; $lines[] = '### Recommendation Policy'; $lines[] = '- The visitor already sees a card for EVERY event the search returns (image, date, place, price, seats). Do not repeat that list in text.'; $lines[] = '- In text, name at most 2 to 3 events. Lead with the best match and give one short reason tied to what the visitor said. Then point to the cards below.'; $lines[] = '- If nothing fits, say so honestly rather than offering a weak match.'; $lines[] = ''; $lines[] = '### Ticket Action Protocol'; $lines[] = '- Call `eventin-ticket-action` only after the visitor confirms which event and which ticket type they want.'; $lines[] = '- `eventin-ticket-action` only CHECKS. It creates a card that the visitor must tap.'; $lines[] = '- Phrase it as an offer, using the exact `button_label` from the result: "If you would like, tap the **** button below." Never name a different button.'; $lines[] = '- Never say "I booked it", "Done", or "your ticket is ready". The visitor must tap.'; $lines[] = '- Tapping the button does not hold a seat. Only finishing payment does. Never promise that a seat is saved.'; $lines[] = ''; $lines[] = '### What You Cannot Help With'; $lines[] = '- A ticket the visitor already bought: status, resending, or changing an attendee name'; $lines[] = '- Refunds, cancelling, or transferring a ticket'; $lines[] = '- Coupon codes and discounts, unless the website states them'; $lines[] = '- Account, login, or password problems'; $lines[] = '- Anything not about finding an event and getting a ticket'; $lines[] = ''; // Escape quotes and backslashes so a merchant message containing a double // quote cannot break out of the quoted framing around the refusal text. $lines[] = 'If a visitor asks about any of the above, respond: "' . addcslashes( $out_of_scope_message, '"\\' ) . '"'; $lines[] = ''; $lines[] = '### Tone'; $lines[] = '- Friendly, short, and plain. Visitors are deciding, not reading an essay.'; $lines[] = '- Match the visitor\'s language and energy.'; return implode( "\n", $lines ); } /** * Check if Eventin is active and the public integration is enabled * * @param Settings $settings Settings instance. * @return bool */ private function is_active_and_enabled( Settings $settings ): bool { // is_enabled() already checks the panel's `available` callback, which is // the class_exists( 'Wpeventin' ) guard. return ( new Public_Agent_Settings( $settings ) )->is_enabled( 'eventin' ); } }