Changeset 3712213
- Timestamp:
- 09/25/2026 01:17:17 AM (4 weeks ago)
- Location:
- sellsmith
- Files:
-
- 27 added
- 14 edited
-
tags/1.2.0 (added)
-
tags/1.2.0/admin (added)
-
tags/1.2.0/admin/class-sellsmith-admin.php (added)
-
tags/1.2.0/admin/class-sellsmith-leads.php (added)
-
tags/1.2.0/assets (added)
-
tags/1.2.0/assets/admin.css (added)
-
tags/1.2.0/assets/admin.js (added)
-
tags/1.2.0/assets/widget.css (added)
-
tags/1.2.0/assets/widget.js (added)
-
tags/1.2.0/includes (added)
-
tags/1.2.0/includes/class-sellsmith-chat.php (added)
-
tags/1.2.0/includes/class-sellsmith-conversations.php (added)
-
tags/1.2.0/includes/class-sellsmith-cron.php (added)
-
tags/1.2.0/includes/class-sellsmith-gemini.php (added)
-
tags/1.2.0/includes/class-sellsmith-install.php (added)
-
tags/1.2.0/includes/class-sellsmith-privacy.php (added)
-
tags/1.2.0/includes/class-sellsmith-prompt.php (added)
-
tags/1.2.0/includes/class-sellsmith-rest.php (added)
-
tags/1.2.0/includes/class-sellsmith-settings.php (added)
-
tags/1.2.0/includes/class-sellsmith-tools.php (added)
-
tags/1.2.0/includes/class-sellsmith-widget.php (added)
-
tags/1.2.0/languages (added)
-
tags/1.2.0/languages/index.php (added)
-
tags/1.2.0/languages/sellsmith.pot (added)
-
tags/1.2.0/readme.txt (added)
-
tags/1.2.0/sellsmith.php (added)
-
tags/1.2.0/uninstall.php (added)
-
trunk/admin/class-sellsmith-admin.php (modified) (1 diff)
-
trunk/admin/class-sellsmith-leads.php (modified) (5 diffs)
-
trunk/assets/admin.css (modified) (1 diff)
-
trunk/assets/widget.css (modified) (1 diff)
-
trunk/assets/widget.js (modified) (7 diffs)
-
trunk/includes/class-sellsmith-chat.php (modified) (1 diff)
-
trunk/includes/class-sellsmith-conversations.php (modified) (6 diffs)
-
trunk/includes/class-sellsmith-privacy.php (modified) (2 diffs)
-
trunk/includes/class-sellsmith-rest.php (modified) (2 diffs)
-
trunk/includes/class-sellsmith-settings.php (modified) (4 diffs)
-
trunk/includes/class-sellsmith-widget.php (modified) (2 diffs)
-
trunk/languages/sellsmith.pot (modified) (1 diff)
-
trunk/readme.txt (modified) (3 diffs)
-
trunk/sellsmith.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
sellsmith/trunk/admin/class-sellsmith-admin.php
r3587090 r3712213 745 745 <?php 746 746 self::panel_close(); 747 748 self::panel_open( __( 'When the chat opens', 'sellsmith' ) ); 749 ?> 750 <?php 751 self::checkbox_row( 752 'start_step', 753 __( 'Start questions', 'sellsmith' ), 754 __( 'Ask "Are you already a customer?" first. Customers give their order number and email, new visitors give their email. Then they type their question.', 'sellsmith' ) 755 ); 756 ?> 757 <div class="ss-field"> 758 <span class="ss-label"><?php esc_html_e( 'Email', 'sellsmith' ); ?></span> 759 <div class="ss-radio-group"> 760 <label class="ss-radio"><input type="radio" name="<?php echo self::field_name( 'start_email' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>" value="required" <?php checked( 'required', Sellsmith_Settings::get( 'start_email' ) ); ?> /> <?php esc_html_e( 'Required, so you can always reply', 'sellsmith' ); ?></label> 761 <label class="ss-radio"><input type="radio" name="<?php echo self::field_name( 'start_email' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>" value="optional" <?php checked( 'optional', Sellsmith_Settings::get( 'start_email' ) ); ?> /> <?php esc_html_e( 'Optional, visitors can skip it', 'sellsmith' ); ?></label> 762 </div> 763 </div> 764 <?php 765 self::text_row( 'start_question', __( 'Question', 'sellsmith' ) ); 766 self::text_row( 'start_yes', __( 'Button for customers', 'sellsmith' ) ); 767 self::text_row( 'start_no', __( 'Button for new visitors', 'sellsmith' ) ); 768 ?> 769 <p class="ss-hint"><?php esc_html_e( 'With WooCommerce, the order number is checked against your orders. Only you see the result, in the Inbox. The visitor is never told whether an order exists.', 'sellsmith' ); ?></p> 770 <?php 771 self::panel_close(); 747 772 } 748 773 -
sellsmith/trunk/admin/class-sellsmith-leads.php
r3587090 r3712213 178 178 echo '<form method="get" class="sellsmith-leads-search">'; 179 179 echo '<input type="hidden" name="page" value="sellsmith-leads" />'; 180 echo '<p class="search-box"><input type="search" name="s" value="' . esc_attr( $search ) . '" placeholder="' . esc_attr__( 'Search city or message…', 'sellsmith' ) . '" /> ';180 echo '<p class="search-box"><input type="search" name="s" value="' . esc_attr( $search ) . '" placeholder="' . esc_attr__( 'Search email, order, city or message…', 'sellsmith' ) . '" /> '; 181 181 submit_button( __( 'Search', 'sellsmith' ), 'secondary', '', false ); 182 182 echo '</p></form>'; … … 285 285 $note = ''; 286 286 $contact_val = ''; 287 $meta = array(); 287 288 if ( ! empty( $conv['visitor_meta'] ) ) { 288 $meta = json_decode( $conv['visitor_meta'], true ); 289 if ( is_array( $meta ) ) { 289 $decoded = json_decode( $conv['visitor_meta'], true ); 290 if ( is_array( $decoded ) ) { 291 $meta = $decoded; 290 292 $note = isset( $meta['note'] ) ? $meta['note'] : ''; 291 293 $contact_val = isset( $meta['contact'] ) ? $meta['contact'] : ''; … … 297 299 self::meta_item( __( 'City', 'sellsmith' ), $conv['visitor_city'] ? $conv['visitor_city'] : '' ); 298 300 self::meta_item( __( 'Contact', 'sellsmith' ), $contact_val ? $contact_val : '' ); 301 self::meta_item( __( 'Visitor', 'sellsmith' ), Sellsmith_Conversations::visitor_line( $meta ) ); 299 302 self::meta_item( __( 'Note', 'sellsmith' ), $note ? $note : '' ); 300 303 self::meta_item( __( 'WhatsApp', 'sellsmith' ), $conv['wa_clicked'] ? __( 'Clicked the button', 'sellsmith' ) : '' ); 301 304 echo '</div>'; 305 306 $order_url = Sellsmith_Conversations::visitor_order_url( $meta ); 307 if ( $order_url ) { 308 echo '<p><a class="button" href="' . esc_url( $order_url ) . '">' . esc_html__( 'Open their order', 'sellsmith' ) . '</a></p>'; 309 } 302 310 303 311 $products = self::product_links( $conv['product_ids'] ); … … 361 369 } 362 370 $meta = json_decode( $row['visitor_meta'], true ); 363 if ( ! is_array( $meta ) || empty( $meta['contact'] )) {371 if ( ! is_array( $meta ) ) { 364 372 return ''; 373 } 374 375 // Who they said they are in the start questions, under the contact. 376 $who = ''; 377 if ( ! empty( $meta['start'] ) ) { 378 if ( 'customer' === $meta['start'] ) { 379 $who = __( 'Customer', 'sellsmith' ); 380 if ( ! empty( $meta['order_number'] ) ) { 381 $who .= ' #' . $meta['order_number'] . ( ( isset( $meta['order_match'] ) && 'found' === $meta['order_match'] ) ? ' ✓' : '' ); 382 } 383 } else { 384 $who = __( 'New visitor', 'sellsmith' ); 385 } 386 $who = '<br /><small class="sellsmith-who">' . esc_html( $who ) . '</small>'; 387 } 388 389 if ( empty( $meta['contact'] ) ) { 390 return $who; 365 391 } 366 392 … … 369 395 370 396 if ( 'email' === $type ) { 371 return '<a href="' . esc_url( 'mailto:' . $value ) . '">' . esc_html( $value ) . '</a>' ;397 return '<a href="' . esc_url( 'mailto:' . $value ) . '">' . esc_html( $value ) . '</a>' . $who; 372 398 } 373 399 if ( 'phone' === $type ) { 374 return '<a href="' . esc_url( 'https://wa.me/' . rawurlencode( $value ) ) . '" target="_blank" rel="noopener noreferrer">' . esc_html( $value ) . '</a>' ;375 } 376 return esc_html( $value ) ;400 return '<a href="' . esc_url( 'https://wa.me/' . rawurlencode( $value ) ) . '" target="_blank" rel="noopener noreferrer">' . esc_html( $value ) . '</a>' . $who; 401 } 402 return esc_html( $value ) . $who; 377 403 } 378 404 -
sellsmith/trunk/assets/admin.css
r3587090 r3712213 872 872 color: var(--ss-ink); 873 873 } 874 875 /* customer or new visitor, under the contact in the inbox list */ 876 .sellsmith-who { color: var(--ss-muted); } 874 877 875 878 /* ---------- leads: transcript ---------- */ -
sellsmith/trunk/assets/widget.css
r3587090 r3712213 246 246 } 247 247 248 /* start questions ("Are you already a customer?") */ 249 #sellsmith-root .ss-start-choices { 250 display: flex; 251 flex-wrap: wrap; 252 justify-content: flex-end; 253 gap: 7px; 254 align-self: stretch; 255 animation: ss-pop .26s cubic-bezier(.22, 1, .36, 1); 256 } 257 #sellsmith-root .ss-start-choice { 258 min-height: 42px; 259 padding: 0 15px; 260 border: 1.5px solid var(--ss-brand); 261 border-radius: 999px; 262 background: #fff; 263 color: var(--ss-brand); 264 font-size: 14px; 265 font-weight: 600; 266 transition: background .15s, color .15s; 267 } 268 #sellsmith-root .ss-start-choice:hover { background: var(--ss-brand); color: var(--ss-on-brand); } 269 #sellsmith-root .ss-start-form { 270 background: var(--ss-sand); 271 border-radius: 13px; 272 border-bottom-left-radius: 5px; 273 padding: 11px 12px; 274 align-self: stretch; 275 display: flex; 276 flex-direction: column; 277 gap: 8px; 278 animation: ss-pop .26s cubic-bezier(.22, 1, .36, 1); 279 } 280 #sellsmith-root .ss-start-form .ss-input { flex: none; width: 100%; min-height: 42px; } 281 #sellsmith-root .ss-start-err { font-size: 13px; color: #b42318; } 282 #sellsmith-root .ss-start-err[hidden] { display: none; } 283 #sellsmith-root .ss-start-go { 284 min-height: 42px; 285 border-radius: 10px; 286 background: var(--ss-brand); 287 color: var(--ss-on-brand); 288 font-weight: 600; 289 } 290 #sellsmith-root .ss-start-go:disabled { opacity: .55; cursor: default; } 291 #sellsmith-root .ss-input:disabled { background: #f4f5f7; cursor: not-allowed; } 292 /* 16px stops iPhones from zooming the page when a field is tapped */ 293 @media (max-width: 480px) { 294 #sellsmith-root .ss-start-form .ss-input { font-size: 16px; } 295 } 296 248 297 /* input bar */ 249 298 #sellsmith-root .ss-bar { -
sellsmith/trunk/assets/widget.js
r3587090 r3712213 6 6 var cfg = sellsmithWidget; 7 7 var t = cfg.i18n; 8 // wp_localize_script turns top-level true/1 into "1" and false/0 into "" or "0", 9 // and "0" is truthy in JS, so on/off settings are read through this. 10 function on(v) { return v === true || v === 1 || v === "1"; } 8 11 var resumeToken = cfg.resumeToken || ""; 9 12 … … 11 14 // so strip it from the address bar right away: that keeps it out of browser 12 15 // history, out of Referer headers, and out of any link the visitor copies. 13 if (resumeToken && window.history && history.replaceState) { 16 // Always window.history here: the chat keeps its own "var history" further down, 17 // which hides the browser's one inside this function and crashed every reply link. 18 if (resumeToken && window.history && window.history.replaceState) { 14 19 try { 15 20 var _u = new URL(window.location.href); 16 21 if (_u.searchParams.has("sellsmith_chat")) { 17 22 _u.searchParams.delete("sellsmith_chat"); 18 history.replaceState(null, "", _u.toString());23 window.history.replaceState(null, "", _u.toString()); 19 24 } 20 25 } catch (e) { /* older browser: leave the URL as-is */ } … … 39 44 var CONSENT_KEY = "sellsmith_consent"; 40 45 var EMAIL_ASKED_KEY = "sellsmith_email_asked"; 46 var START_KEY = "sellsmith_start_done"; 41 47 42 48 function store(key, val) { … … 258 264 /* ---------- consent gate ---------- */ 259 265 function needsConsent() { 260 return cfg.consent === 1&& read(CONSENT_KEY) !== "1";266 return on(cfg.consent) && read(CONSENT_KEY) !== "1"; 261 267 } 262 268 … … 272 278 box.appendChild(ok); 273 279 log.appendChild(box); 280 } 281 282 /* ---------- start questions ---------- */ 283 /* "Are you already a customer?" before the first message. Customers give their 284 order number and email, new visitors their email, so the owner can always 285 reply, even after the visitor has left. Turned on in Settings > Appearance. */ 286 function historyHasUser() { 287 for (var i = 0; i < history.length; i++) { 288 if (history[i].r === "user") return true; 289 } 290 return false; 291 } 292 293 function needsStart() { 294 return on(cfg.startStep) && !!cfg.startUrl && !resumeToken && read(START_KEY) !== session && !historyHasUser(); 295 } 296 297 function lockBar(on) { 298 input.disabled = on; 299 sendBtn.disabled = on; 300 input.placeholder = on ? (t.startLocked || t.placeholder) : t.placeholder; 301 } 302 303 function validEmail(v) { 304 return /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(v); 305 } 306 307 function showStart() { 308 lockBar(true); 309 addBubble("ai", t.startQuestion, true); 310 311 var row = el("div", "ss-start-choices"); 312 var yes = el("button", "ss-start-choice", t.startYes); 313 var no = el("button", "ss-start-choice", t.startNo); 314 yes.type = "button"; 315 no.type = "button"; 316 row.appendChild(yes); 317 row.appendChild(no); 318 log.appendChild(row); 319 log.scrollTop = log.scrollHeight; 320 321 yes.addEventListener("click", function () { row.remove(); startForm("customer"); }); 322 no.addEventListener("click", function () { row.remove(); startForm("new"); }); 323 yes.focus(); 324 } 325 326 function startForm(type) { 327 var customer = type === "customer"; 328 var choice = customer ? t.startYes : t.startNo; 329 addBubble("user", choice, true); 330 addBubble("ai", customer ? t.startCustomer : t.startNew, true); 331 332 var form = el("form", "ss-start-form"); 333 form.noValidate = true; 334 335 var orderInp = null; 336 if (customer) { 337 orderInp = el("input", "ss-input"); 338 orderInp.type = "text"; 339 orderInp.maxLength = 40; 340 orderInp.autocomplete = "off"; 341 orderInp.placeholder = t.startOrder; 342 orderInp.setAttribute("aria-label", t.startOrder); 343 form.appendChild(orderInp); 344 } 345 346 var emailInp = el("input", "ss-input"); 347 emailInp.type = "email"; 348 emailInp.maxLength = 120; 349 emailInp.autocomplete = "email"; 350 emailInp.placeholder = customer ? t.startEmailBuy : t.startEmailNew; 351 emailInp.setAttribute("aria-label", emailInp.placeholder); 352 if (cfg.userEmail) emailInp.value = cfg.userEmail; 353 form.appendChild(emailInp); 354 355 var err = el("p", "ss-start-err"); 356 err.setAttribute("role", "alert"); 357 err.hidden = true; 358 form.appendChild(err); 359 360 var go = el("button", "ss-start-go", t.startGo); 361 go.type = "submit"; 362 form.appendChild(go); 363 364 if (cfg.startEmail === "optional") { 365 var skip = el("button", "ss-email-skip", t.startSkip); 366 skip.type = "button"; 367 skip.addEventListener("click", function () { 368 submitStart(type, choice, "", orderInp ? orderInp.value.trim() : "", form, go, err); 369 }); 370 form.appendChild(skip); 371 } 372 373 form.addEventListener("submit", function (e) { 374 e.preventDefault(); 375 var email = emailInp.value.trim(); 376 var bad = email ? !validEmail(email) : cfg.startEmail !== "optional"; 377 if (bad) { 378 err.textContent = t.startBadEmail; 379 err.hidden = false; 380 emailInp.setAttribute("aria-invalid", "true"); 381 emailInp.focus(); 382 return; 383 } 384 emailInp.removeAttribute("aria-invalid"); 385 submitStart(type, choice, email, orderInp ? orderInp.value.trim() : "", form, go, err); 386 }); 387 388 log.appendChild(form); 389 log.scrollTop = log.scrollHeight; 390 (orderInp || (emailInp.value ? go : emailInp)).focus(); 391 } 392 393 function submitStart(type, choice, email, order, form, go, err) { 394 var skipBtn = form.querySelector(".ss-email-skip"); 395 function busyForm(onOff) { 396 go.disabled = onOff; 397 if (skipBtn) skipBtn.disabled = onOff; 398 } 399 busyForm(true); 400 fetch(cfg.startUrl, { 401 method: "POST", 402 headers: { "Content-Type": "application/json", "X-WP-Nonce": cfg.nonce }, 403 body: JSON.stringify({ session_id: session, type: type, email: email, order: order, nonce: cfg.nonce }) 404 }) 405 .then(function (r) { return r.json().then(function (d) { return { ok: r.ok, d: d }; }); }) 406 .then(function (res) { 407 if (res.ok && res.d && res.d.ok) { 408 finishStart(type, choice, email, order, form); 409 return; 410 } 411 busyForm(false); 412 err.textContent = (res.d && res.d.message) ? res.d.message : t.error; 413 err.hidden = false; 414 }) 415 .catch(function () { 416 busyForm(false); 417 err.textContent = t.error; 418 err.hidden = false; 419 }); 420 } 421 422 function finishStart(type, choice, email, order, form) { 423 form.remove(); 424 // Keep the answers in the saved on-screen chat, so a reload reads the same. 425 history.push({ r: "ai", c: t.startQuestion }); 426 history.push({ r: "user", c: choice }); 427 var parts = []; 428 if (order) parts.push(t.startOrderShort + " " + order); 429 if (email) parts.push(email); 430 if (parts.length) addBubble("user", parts.join(" · ")); 431 addBubble("ai", email ? t.startThanks : t.startThanksShort); 432 store(START_KEY, session); 433 // Pro's soft email ask has nothing left to ask for. 434 if (email) store(EMAIL_ASKED_KEY, session); 435 lockBar(false); 436 input.focus(); 437 emit("startdone", { type: type, email: !!email }); 274 438 } 275 439 … … 406 570 addBubble("ai", cfg.welcome); 407 571 } 572 if (needsStart()) { showStart(); return; } 408 573 input.focus(); 409 574 } … … 504 669 } 505 670 emit("newchat", { session: session }); 671 if (needsStart()) { showStart(); return; } 672 lockBar(false); 506 673 input.focus(); 507 674 } -
sellsmith/trunk/includes/class-sellsmith-chat.php
r3587090 r3712213 253 253 $user_count = Sellsmith_Conversations::count_user_messages( $conversation_id ); 254 254 255 if ( $has_contact && ! $had_contact ) { 256 // They just left their contact: confirm once, then go quiet. 255 if ( ( $has_contact && ! $had_contact ) || ( $had_contact && 1 === $user_count && '' !== $message ) ) { 256 // They just left their contact, or gave it in the start questions and this 257 // is their first message: confirm once, then go quiet. 257 258 $reply = __( 'Got it! 🙏 We will reply to you here soon. You can add more details if you like.', 'sellsmith' ); 258 259 } elseif ( $has_contact ) { -
sellsmith/trunk/includes/class-sellsmith-conversations.php
r3587090 r3712213 305 305 $meta['contact'] = $value; 306 306 $meta['contact_type'] = $type; 307 $meta['mode'] = 'message'; 307 // Only tag a conversation that has no mode yet. A live chat must stay live, 308 // or it drops out of the owner's live inbox the moment the visitor saves an email. 309 if ( empty( $meta['mode'] ) ) { 310 $meta['mode'] = 'message'; 311 } 308 312 309 313 $wpdb->update( … … 318 322 319 323 return $type; 324 } 325 326 /** 327 * Save the answers from the start questions ("Are you already a customer?"). 328 * Never changes the conversation mode, and never replaces a contact that is 329 * already saved on it. 330 * 331 * @param int $conversation_id Conversation id. 332 * @param array $data start ('customer'|'new'), email, order_number, 333 * plus the order check fields for customers. 334 */ 335 public static function save_start( $conversation_id, $data ) { 336 $conversation_id = (int) $conversation_id; 337 $meta = self::get_meta( $conversation_id ); 338 339 $changes = array( 340 'start' => ( isset( $data['start'] ) && 'customer' === $data['start'] ) ? 'customer' : 'new', 341 'start_at' => time(), 342 ); 343 344 $email = isset( $data['email'] ) ? sanitize_email( (string) $data['email'] ) : ''; 345 if ( '' !== $email && is_email( $email ) && empty( $meta['contact'] ) ) { 346 $changes['contact'] = $email; 347 $changes['contact_type'] = 'email'; 348 } 349 350 foreach ( array( 'order_number', 'order_match' ) as $key ) { 351 $changes[ $key ] = isset( $data[ $key ] ) ? (string) $data[ $key ] : ''; 352 } 353 foreach ( array( 'order_id', 'orders_by_email', 'last_order_id' ) as $key ) { 354 $changes[ $key ] = isset( $data[ $key ] ) ? absint( $data[ $key ] ) : 0; 355 } 356 357 self::merge_meta( $conversation_id, $changes ); 358 359 do_action( 'sellsmith_lead_updated', $conversation_id ); 360 } 361 362 /** 363 * One plain line for the owner saying who the visitor is, from the start 364 * questions. Admin, owner alerts and the live console only: it names orders, 365 * so it is never sent to the visitor. 366 * 367 * @param array $meta Decoded visitor_meta. 368 * @param bool $details Add the order's products, date and status (loads the order). 369 * @return string Empty when the visitor never answered the start questions. 370 */ 371 public static function visitor_line( $meta, $details = true ) { 372 if ( ! is_array( $meta ) || empty( $meta['start'] ) ) { 373 return ''; 374 } 375 if ( 'customer' !== $meta['start'] ) { 376 return __( 'New visitor', 'sellsmith' ); 377 } 378 379 $number = isset( $meta['order_number'] ) ? (string) $meta['order_number'] : ''; 380 $match = isset( $meta['order_match'] ) ? (string) $meta['order_match'] : ''; 381 $id = isset( $meta['order_id'] ) ? (int) $meta['order_id'] : 0; 382 383 if ( '' === $number ) { 384 $count = isset( $meta['orders_by_email'] ) ? (int) $meta['orders_by_email'] : 0; 385 $last = isset( $meta['last_order_id'] ) ? (int) $meta['last_order_id'] : 0; 386 if ( ! $count ) { 387 return __( 'Customer, no order number given', 'sellsmith' ) . ( function_exists( 'wc_get_order' ) ? ', ' . __( 'no orders under this email', 'sellsmith' ) : '' ); 388 } 389 /* translators: 1: number of orders ("5+" at most), 2: newest order number. */ 390 return sprintf( __( 'Customer, no order number given, %1$s orders under this email (newest #%2$d)', 'sellsmith' ), $count >= 5 ? '5+' : (string) $count, $last ); 391 } 392 393 switch ( $match ) { 394 case 'found': 395 $line = sprintf( 396 /* translators: %s: order number. */ 397 __( 'Customer, order #%s checked', 'sellsmith' ), 398 $number 399 ); 400 $more = $details ? self::order_details( $id ) : ''; 401 return '' !== $more ? $line . ': ' . $more : $line; 402 case 'email_mismatch': 403 /* translators: %s: order number. */ 404 return sprintf( __( 'Customer, order #%s exists but under a different email', 'sellsmith' ), $number ); 405 case 'not_found': 406 /* translators: %s: order number. */ 407 return sprintf( __( 'Customer, order #%s not found in this shop', 'sellsmith' ), $number ); 408 default: 409 /* translators: %s: order number. */ 410 return sprintf( __( 'Customer, order #%s', 'sellsmith' ), $number ); 411 } 412 } 413 414 /** 415 * Products, date and status of an order, for the owner. 416 * 417 * @param int $order_id Order id. 418 * @return string 419 */ 420 private static function order_details( $order_id ) { 421 if ( ! $order_id || ! function_exists( 'wc_get_order' ) ) { 422 return ''; 423 } 424 $order = wc_get_order( $order_id ); 425 if ( ! $order || ! is_a( $order, 'WC_Order' ) ) { 426 return ''; 427 } 428 429 $names = array(); 430 foreach ( $order->get_items() as $item ) { 431 $names[] = $item->get_name(); 432 } 433 434 $parts = array(); 435 if ( $names ) { 436 $parts[] = implode( ', ', array_slice( $names, 0, 3 ) ) . ( count( $names ) > 3 ? ' +' . ( count( $names ) - 3 ) : '' ); 437 } 438 $created = $order->get_date_created(); 439 if ( $created ) { 440 $parts[] = $created->date_i18n( get_option( 'date_format' ) ); 441 } 442 $parts[] = wc_get_order_status_name( $order->get_status() ); 443 444 return implode( ', ', $parts ); 445 } 446 447 /** 448 * Admin link to the order a customer named, when it exists in this shop. 449 * 450 * @param array $meta Decoded visitor_meta. 451 * @return string URL or ''. 452 */ 453 public static function visitor_order_url( $meta ) { 454 $id = ( is_array( $meta ) && ! empty( $meta['order_id'] ) ) ? (int) $meta['order_id'] : 0; 455 if ( ! $id && is_array( $meta ) && empty( $meta['order_number'] ) && ! empty( $meta['last_order_id'] ) ) { 456 $id = (int) $meta['last_order_id']; 457 } 458 if ( ! $id || ! function_exists( 'wc_get_order' ) || ! current_user_can( 'edit_shop_orders' ) ) { 459 return ''; 460 } 461 $order = wc_get_order( $id ); 462 return ( $order && is_a( $order, 'WC_Order' ) ) ? $order->get_edit_order_url() : ''; 320 463 } 321 464 … … 621 764 if ( ! empty( $args['search'] ) ) { 622 765 $like = '%' . $wpdb->esc_like( $args['search'] ) . '%'; 623 $where .= " AND (c.visitor_city LIKE %s OR c.session_id LIKE %s OR EXISTS (SELECT 1 FROM {$msg} m2 WHERE m2.conversation_id = c.id AND m2.content LIKE %s))"; 624 $params = array( $like, $like, $like ); 766 // visitor_meta holds the saved email and any order number from the start questions. 767 $where .= " AND (c.visitor_city LIKE %s OR c.session_id LIKE %s OR c.visitor_meta LIKE %s OR EXISTS (SELECT 1 FROM {$msg} m2 WHERE m2.conversation_id = c.id AND m2.content LIKE %s))"; 768 $params = array( $like, $like, $like, $like ); 625 769 } 626 770 … … 712 856 713 857 /** 714 * Conversation ids whose messages contain a given email (privacy tools). 858 * Conversation ids that hold a given email, in a message or as the saved 859 * contact (privacy tools). 715 860 * 716 861 * @param string $email Email address. … … 719 864 public static function ids_by_email( $email, $page = 1 ) { 720 865 global $wpdb; 721 $msg = $wpdb->prefix . 'sw_messages'; 866 $conv = $wpdb->prefix . 'sw_conversations'; 867 $msg = $wpdb->prefix . 'sw_messages'; 722 868 723 869 $like = '%' . $wpdb->esc_like( $email ) . '%'; … … 726 872 $ids = $wpdb->get_col( 727 873 $wpdb->prepare( 728 "SELECT DISTINCT conversation_id FROM {$msg} WHERE content LIKE %s ORDER BY conversation_id ASC LIMIT 10 OFFSET %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 874 "SELECT c.id FROM {$conv} c WHERE c.visitor_meta LIKE %s OR EXISTS (SELECT 1 FROM {$msg} m WHERE m.conversation_id = c.id AND m.content LIKE %s) ORDER BY c.id ASC LIMIT 10 OFFSET %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 875 $like, 729 876 $like, 730 877 $offset -
sellsmith/trunk/includes/class-sellsmith-privacy.php
r3587090 r3712213 69 69 } 70 70 71 $meta = Sellsmith_Conversations::meta( $id ); 72 $items = array( 73 array( 74 'name' => __( 'Started', 'sellsmith' ), 75 'value' => $conv['started_at'], 76 ), 77 array( 78 'name' => __( 'City', 'sellsmith' ), 79 'value' => $conv['visitor_city'], 80 ), 81 ); 82 if ( ! empty( $meta['contact'] ) ) { 83 $items[] = array( 84 'name' => __( 'Contact', 'sellsmith' ), 85 'value' => $meta['contact'], 86 ); 87 } 88 if ( ! empty( $meta['order_number'] ) ) { 89 $items[] = array( 90 'name' => __( 'Order number', 'sellsmith' ), 91 'value' => $meta['order_number'], 92 ); 93 } 94 $items[] = array( 95 'name' => __( 'Transcript', 'sellsmith' ), 96 'value' => $transcript, 97 ); 98 71 99 $data[] = array( 72 100 'group_id' => 'sellsmith_conversations', 73 101 'group_label' => __( 'Chat conversations', 'sellsmith' ), 74 102 'item_id' => 'sellsmith-conversation-' . $id, 75 'data' => array( 76 array( 77 'name' => __( 'Started', 'sellsmith' ), 78 'value' => $conv['started_at'], 79 ), 80 array( 81 'name' => __( 'City', 'sellsmith' ), 82 'value' => $conv['visitor_city'], 83 ), 84 array( 85 'name' => __( 'Transcript', 'sellsmith' ), 86 'value' => $transcript, 87 ), 88 ), 103 'data' => $items, 89 104 ); 90 105 } … … 103 118 */ 104 119 public static function erase( $email, $page = 1 ) { 105 $ids = Sellsmith_Conversations::ids_by_email( $email, $page ); 120 // Always page 1: each pass deletes what it found, so an offset would skip rows. 121 $ids = Sellsmith_Conversations::ids_by_email( $email, 1 ); 106 122 107 123 if ( $ids ) { -
sellsmith/trunk/includes/class-sellsmith-rest.php
r3587090 r3712213 93 93 ), 94 94 'nonce' => array( 95 'type' => 'string', 96 'required' => false, 97 'sanitize_callback' => 'sanitize_text_field', 98 ), 99 ), 100 ) 101 ); 102 103 register_rest_route( 104 self::NS, 105 '/start', 106 array( 107 'methods' => WP_REST_Server::CREATABLE, 108 'callback' => array( __CLASS__, 'start' ), 109 'permission_callback' => array( __CLASS__, 'verify_widget_nonce' ), 110 'args' => array( 111 'session_id' => array( 112 'type' => 'string', 113 'required' => true, 114 'sanitize_callback' => 'sanitize_text_field', 115 ), 116 'type' => array( 117 'type' => 'string', 118 'required' => true, 119 'sanitize_callback' => 'sanitize_key', 120 ), 121 'email' => array( 122 'type' => 'string', 123 'required' => false, 124 'sanitize_callback' => 'sanitize_text_field', 125 ), 126 'order' => array( 127 'type' => 'string', 128 'required' => false, 129 'sanitize_callback' => 'sanitize_text_field', 130 ), 131 'nonce' => array( 95 132 'type' => 'string', 96 133 'required' => false, … … 191 228 192 229 return rest_ensure_response( $payload ); 230 } 231 232 /** 233 * Save the answers to the start questions: customer or not, their email, and 234 * for customers the order number, checked against this shop's orders. The 235 * answer never says whether an order exists; only the owner sees that. 236 * 237 * @param WP_REST_Request $request Request. 238 */ 239 public static function start( WP_REST_Request $request ) { 240 241 if ( ! Sellsmith_Settings::get( 'widget_enabled' ) || ! Sellsmith_Settings::get( 'start_step' ) ) { 242 return new WP_Error( 'sellsmith_off', __( 'Chat is not available right now.', 'sellsmith' ), array( 'status' => 403 ) ); 243 } 244 245 // Per-IP rate limit, so the form cannot be used to flood the inbox with rows. 246 $ip = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : ''; 247 $key = 'sellsmith_start_' . md5( $ip ); 248 $hits = (int) get_transient( $key ); 249 if ( $hits >= 30 ) { 250 return new WP_Error( 'sellsmith_rate', __( 'Too many requests. Please wait a moment.', 'sellsmith' ), array( 'status' => 429 ) ); 251 } 252 set_transient( $key, $hits + 1, HOUR_IN_SECONDS ); 253 254 $session_id = (string) $request->get_param( 'session_id' ); 255 if ( ! preg_match( '/^[A-Za-z0-9\-]{8,64}$/', $session_id ) ) { 256 return new WP_Error( 'sellsmith_session', __( 'Please refresh the page and try again.', 'sellsmith' ), array( 'status' => 400 ) ); 257 } 258 259 $type = (string) $request->get_param( 'type' ); 260 if ( ! in_array( $type, array( 'customer', 'new' ), true ) ) { 261 return new WP_Error( 'sellsmith_type', __( 'Please pick an answer.', 'sellsmith' ), array( 'status' => 400 ) ); 262 } 263 264 $email = trim( (string) $request->get_param( 'email' ) ); 265 if ( '' !== $email ) { 266 $email = sanitize_email( $email ); 267 if ( ! $email || ! is_email( $email ) ) { 268 return new WP_Error( 'sellsmith_email', __( 'Please enter a valid email.', 'sellsmith' ), array( 'status' => 400 ) ); 269 } 270 } elseif ( 'optional' !== Sellsmith_Settings::get( 'start_email' ) ) { 271 return new WP_Error( 'sellsmith_email', __( 'Please enter your email.', 'sellsmith' ), array( 'status' => 400 ) ); 272 } 273 274 $order = ''; 275 if ( 'customer' === $type ) { 276 $order = preg_replace( '/[^A-Za-z0-9\-_\/]/', '', ltrim( trim( (string) $request->get_param( 'order' ) ), '#' ) ); 277 $order = substr( (string) $order, 0, 40 ); 278 } 279 280 $conversation_id = Sellsmith_Conversations::get_or_create( $session_id ); 281 if ( ! $conversation_id ) { 282 return new WP_Error( 'sellsmith_conv', __( 'Something went wrong. Please try again.', 'sellsmith' ), array( 'status' => 500 ) ); 283 } 284 285 $data = array( 286 'start' => $type, 287 'email' => $email, 288 'order_number' => $order, 289 ); 290 if ( 'customer' === $type ) { 291 $data = array_merge( $data, self::match_order( $order, $email ) ); 292 } 293 294 Sellsmith_Conversations::save_start( $conversation_id, $data ); 295 296 return rest_ensure_response( array( 'ok' => true ) ); 297 } 298 299 /** 300 * Look a customer's order up in WooCommerce. An order only counts as found 301 * when its billing email is the email they typed. 302 * 303 * @param string $number Order number as typed (cleaned), or ''. 304 * @param string $email Email as typed (valid), or ''. 305 * @return array order_match ('found'|'email_mismatch'|'not_found'|''), order_id, 306 * orders_by_email, last_order_id. 307 */ 308 private static function match_order( $number, $email ) { 309 $out = array( 310 'order_match' => '', 311 'order_id' => 0, 312 'orders_by_email' => 0, 313 'last_order_id' => 0, 314 ); 315 316 if ( ! sellsmith_has_wc() || ! function_exists( 'wc_get_order' ) ) { 317 return $out; 318 } 319 320 if ( '' !== $email && function_exists( 'wc_get_orders' ) ) { 321 $ids = wc_get_orders( 322 array( 323 'billing_email' => $email, 324 'limit' => 5, 325 'orderby' => 'date', 326 'order' => 'DESC', 327 'return' => 'ids', 328 ) 329 ); 330 $ids = is_array( $ids ) ? array_values( $ids ) : array(); 331 $out['orders_by_email'] = count( $ids ); 332 $out['last_order_id'] = $ids ? (int) $ids[0] : 0; 333 } 334 335 if ( '' === $number ) { 336 return $out; 337 } 338 339 // Same filter as WooCommerce's own order tracking form, so shops that use 340 // custom order numbers (sequential number plugins) map them to the real id. 341 $id = (string) apply_filters( 'woocommerce_shortcode_order_tracking_order_id', $number ); 342 $order = ctype_digit( $id ) ? wc_get_order( (int) $id ) : false; 343 344 if ( ! $order || ! is_a( $order, 'WC_Order' ) ) { 345 $out['order_match'] = 'not_found'; 346 return $out; 347 } 348 349 $out['order_id'] = $order->get_id(); 350 $out['order_match'] = ( '' !== $email && strtolower( $order->get_billing_email() ) === strtolower( $email ) ) ? 'found' : 'email_mismatch'; 351 352 return $out; 193 353 } 194 354 -
sellsmith/trunk/includes/class-sellsmith-settings.php
r3587090 r3712213 48 48 'teaser_text' => __( 'Need help finding something?', 'sellsmith' ), 49 49 'show_credit' => 0, 50 // Start questions (asked when the chat opens). 51 'start_step' => 0, 52 'start_email' => 'required', 53 'start_question' => __( 'Are you already a customer?', 'sellsmith' ), 54 'start_yes' => __( 'Yes, I have an order', 'sellsmith' ), 55 'start_no' => __( 'No, not yet', 'sellsmith' ), 50 56 // WhatsApp. 51 57 'wa_number' => '', … … 69 75 'connection' => array( 'api_key', 'model', 'custom_model' ), 70 76 'profile' => array( 'store_about', 'delivery_info', 'delivery_areas', 'cod_enabled', 'open_parcel', 'return_policy', 'discount_info', 'payment_methods', 'extra_info' ), 71 'appearance' => array( 'widget_position', 'widget_color', 'widget_title', 'welcome_message', 'teaser_text', 'show_credit' ),77 'appearance' => array( 'widget_position', 'widget_color', 'widget_title', 'welcome_message', 'teaser_text', 'show_credit', 'start_step', 'start_email', 'start_question', 'start_yes', 'start_no' ), 72 78 'whatsapp' => array( 'wa_number', 'wa_enabled' ), 73 79 'advanced' => array( 'rate_limit', 'max_messages', 'retention_days', 'consent_required', 'keep_data' ), … … 79 85 */ 80 86 public static function checkbox_keys() { 81 return array( 'cod_enabled', 'widget_enabled', 'wa_enabled', 'consent_required', 'keep_data', 'show_credit' );87 return array( 'cod_enabled', 'widget_enabled', 'wa_enabled', 'consent_required', 'keep_data', 'show_credit', 'start_step' ); 82 88 } 83 89 … … 169 175 case 'chat_mode': 170 176 $existing[ $key ] = in_array( $raw, array( 'ai', 'message' ), true ) ? $raw : 'ai'; 177 break; 178 179 case 'start_email': 180 $existing[ $key ] = in_array( $raw, array( 'required', 'optional' ), true ) ? $raw : 'required'; 181 break; 182 183 case 'start_question': 184 case 'start_yes': 185 case 'start_no': 186 // A cleared field falls back to the default wording, never an empty button. 187 $raw = sanitize_text_field( $raw ); 188 $existing[ $key ] = '' !== $raw ? $raw : self::defaults()[ $key ]; 171 189 break; 172 190 -
sellsmith/trunk/includes/class-sellsmith-widget.php
r3587090 r3712213 73 73 'context' => self::page_context(), 74 74 'showCredit' => ! empty( $s['show_credit'] ), 75 // Start questions ("Are you already a customer?"), asked when the chat opens. 76 'startStep' => ! empty( $s['start_step'] ) ? 1 : 0, 77 'startEmail' => ( 'optional' === $s['start_email'] ) ? 'optional' : 'required', 78 'startUrl' => esc_url_raw( rest_url( 'sellsmith/v1/start' ) ), 79 // A logged-in visitor's own email, to fill the field in for them. 80 'userEmail' => ( ! empty( $s['start_step'] ) && is_user_logged_in() ) ? wp_get_current_user()->user_email : '', 75 81 'i18n' => array( 76 82 'placeholder' => __( 'Type a message', 'sellsmith' ), … … 90 96 'sent' => __( 'Sent', 'sellsmith' ), 91 97 'seen' => __( 'Seen', 'sellsmith' ), 98 // Start questions. 99 'startQuestion' => '' !== trim( (string) $s['start_question'] ) ? $s['start_question'] : Sellsmith_Settings::defaults()['start_question'], 100 'startYes' => '' !== trim( (string) $s['start_yes'] ) ? $s['start_yes'] : Sellsmith_Settings::defaults()['start_yes'], 101 'startNo' => '' !== trim( (string) $s['start_no'] ) ? $s['start_no'] : Sellsmith_Settings::defaults()['start_no'], 102 'startCustomer' => __( 'Great! What is your order number, and the email you ordered with?', 'sellsmith' ), 103 'startNew' => __( 'What is your email? If we cannot reply right away, we will answer you there.', 'sellsmith' ), 104 'startOrder' => __( 'Order number (if you have it)', 'sellsmith' ), 105 'startEmailBuy' => __( 'Email you ordered with', 'sellsmith' ), 106 'startEmailNew' => __( 'Your email', 'sellsmith' ), 107 'startGo' => __( 'Continue', 'sellsmith' ), 108 'startSkip' => __( 'Skip', 'sellsmith' ), 109 'startBadEmail' => __( 'Please enter a valid email.', 'sellsmith' ), 110 'startOrderShort' => __( 'Order', 'sellsmith' ), 111 'startThanks' => ( 'message' === $s['chat_mode'] ) 112 ? __( 'Thanks! Now type your question below. If we are not here right away, we will reply to you by email.', 'sellsmith' ) 113 : __( 'Thanks! Now type your question below.', 'sellsmith' ), 114 'startThanksShort' => __( 'Thanks! Now type your question below.', 'sellsmith' ), 115 'startLocked' => __( 'Pick an answer above first', 'sellsmith' ), 92 116 ), 93 117 ); -
sellsmith/trunk/languages/sellsmith.pot
r3587090 r3712213 1 # Copyright (C) 2026 DevTonic Studios 2 # This file is distributed under the GPLv2 or later. 1 3 msgid "" 2 4 msgstr "" 3 "Project-Id-Version: Sellsmith 1.1.0\n" 5 "Project-Id-Version: Sellsmith – AI Chatbot, Lead Capture & Sales Chat 1.2.0\n" 6 "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/sellsmith\n" 7 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" 8 "Language-Team: LANGUAGE <LL@li.org>\n" 4 9 "MIME-Version: 1.0\n" 5 10 "Content-Type: text/plain; charset=UTF-8\n" 6 11 "Content-Transfer-Encoding: 8bit\n" 7 "POT-Creation-Date: 2026-06-12 23:16+0000\n" 12 "POT-Creation-Date: 2026-09-25T00:52:32+00:00\n" 13 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 "X-Generator: WP-CLI 2.12.0\n" 8 15 "X-Domain: sellsmith\n" 9 16 17 #. Plugin Name of the plugin 18 #: sellsmith.php 19 msgid "Sellsmith – AI Chatbot, Lead Capture & Sales Chat" 20 msgstr "" 21 22 #. Plugin URI of the plugin 23 #: sellsmith.php 24 msgid "https://devtonicstudios.com/sellsmith" 25 msgstr "" 26 27 #. Description of the plugin 28 #: sellsmith.php 29 msgid "An AI sales agent for your store. Answers buyers from your real product data, captures leads, and hands hot buyers to your WhatsApp. Runs on your own free Gemini API key." 30 msgstr "" 31 32 #. Author of the plugin 33 #: sellsmith.php 34 msgid "DevTonic Studios" 35 msgstr "" 36 37 #. Author URI of the plugin 38 #: sellsmith.php 39 msgid "https://devtonicstudios.com" 40 msgstr "" 41 42 #: admin/class-sellsmith-admin.php:33 43 #: admin/class-sellsmith-admin.php:34 44 msgid "Sellsmith" 45 msgstr "" 46 47 #: admin/class-sellsmith-admin.php:71 48 msgid "WooCommerce is not active. Sellsmith will still chat and capture leads, and product answers will switch on automatically when WooCommerce is installed." 49 msgstr "" 50 51 #: admin/class-sellsmith-admin.php:100 52 msgid "Testing…" 53 msgstr "" 54 55 #: admin/class-sellsmith-admin.php:118 56 msgid "AI key" 57 msgstr "" 58 59 #: admin/class-sellsmith-admin.php:119 60 msgid "Business profile" 61 msgstr "" 62 63 #: admin/class-sellsmith-admin.php:120 64 msgid "Appearance" 65 msgstr "" 66 67 #: admin/class-sellsmith-admin.php:121 68 #: admin/class-sellsmith-leads.php:197 69 #: admin/class-sellsmith-leads.php:303 70 msgid "WhatsApp" 71 msgstr "" 72 73 #: admin/class-sellsmith-admin.php:122 74 msgid "Advanced" 75 msgstr "" 76 77 #: admin/class-sellsmith-admin.php:123 78 #: admin/class-sellsmith-admin.php:872 79 msgid "Sellsmith Pro" 80 msgstr "" 81 82 #: admin/class-sellsmith-admin.php:219 83 #: admin/class-sellsmith-admin.php:318 84 msgid "Chat on my store" 85 msgstr "" 86 87 #: admin/class-sellsmith-admin.php:221 88 msgid "Buyers can see and use the chat." 89 msgstr "" 90 91 #: admin/class-sellsmith-admin.php:221 92 msgid "Paused. Buyers do not see the chat right now." 93 msgstr "" 94 95 #: admin/class-sellsmith-admin.php:228 96 msgid "How it answers buyers" 97 msgstr "" 98 99 #: admin/class-sellsmith-admin.php:234 100 msgid "AI answers" 101 msgstr "" 102 103 #: admin/class-sellsmith-admin.php:236 104 msgid "The AI replies about products, price and delivery." 105 msgstr "" 106 107 #: admin/class-sellsmith-admin.php:237 108 msgid "Needs your Gemini key. Add it in the AI key settings." 109 msgstr "" 110 111 #: admin/class-sellsmith-admin.php:242 112 msgid "Just collect messages" 113 msgstr "" 114 115 #: admin/class-sellsmith-admin.php:243 116 msgid "No AI. Save each question and contact as a lead." 117 msgstr "" 118 119 #: admin/class-sellsmith-admin.php:250 120 msgid "Also let buyers reach a person" 121 msgstr "" 122 123 #: admin/class-sellsmith-admin.php:266 124 msgid "Open settings" 125 msgstr "" 126 127 #: admin/class-sellsmith-admin.php:267 128 msgid "Open inbox" 129 msgstr "" 130 131 #: admin/class-sellsmith-admin.php:284 132 msgid "Right now your chat is OFF. Buyers do not see a chat on your store. Turn it on below." 133 msgstr "" 134 135 #: admin/class-sellsmith-admin.php:288 136 msgid "Your chat is ON. It saves each buyer's question and contact as a lead." 137 msgstr "" 138 139 #: admin/class-sellsmith-admin.php:292 140 msgid "Your chat is ON. The AI answers buyers about products, price and delivery." 141 msgstr "" 142 143 #: admin/class-sellsmith-admin.php:296 144 msgid "Your chat is ON, but the AI needs your Gemini key to answer. Add it in the AI key settings." 145 msgstr "" 146 147 #: admin/class-sellsmith-admin.php:357 148 #: admin/class-sellsmith-admin.php:852 149 msgid "Live chat" 150 msgstr "" 151 152 #: admin/class-sellsmith-admin.php:357 153 msgid "Pro" 154 msgstr "" 155 156 #: admin/class-sellsmith-admin.php:358 157 msgid "Reply to buyers yourself in real time, with no page refresh. In Sellsmith Pro." 158 msgstr "" 159 160 #: admin/class-sellsmith-admin.php:379 161 msgid "Home" 162 msgstr "" 163 164 #: admin/class-sellsmith-admin.php:415 165 msgid "Save settings" 166 msgstr "" 167 168 #: admin/class-sellsmith-admin.php:446 169 msgid "Setup complete. Your chat is live." 170 msgstr "" 171 172 #: admin/class-sellsmith-admin.php:452 173 msgid "1. Connect your free Gemini key" 174 msgstr "" 175 176 #: admin/class-sellsmith-admin.php:453 177 msgid "2. Tell the AI about your store" 178 msgstr "" 179 180 #: admin/class-sellsmith-admin.php:454 181 msgid "3. Turn the chat on" 182 msgstr "" 183 184 #. translators: %d: completed steps out of 3 185 #: admin/class-sellsmith-admin.php:461 186 #, php-format 187 msgid "Setup: %d of 3 steps done" 188 msgstr "" 189 190 #. translators: %s: link to Google AI Studio 191 #: admin/class-sellsmith-admin.php:548 192 #, php-format 193 msgid "Get a free Gemini API key from %s (takes 1 minute, no card needed)." 194 msgstr "" 195 196 #: admin/class-sellsmith-admin.php:553 197 msgid "Connect to Gemini" 198 msgstr "" 199 200 #: admin/class-sellsmith-admin.php:557 201 msgid "Gemini API key" 202 msgstr "" 203 204 #: admin/class-sellsmith-admin.php:559 205 msgid "A key is saved. Leave blank to keep it, or paste a new key to replace it." 206 msgstr "" 207 208 #: admin/class-sellsmith-admin.php:560 209 msgid "Paste your key, then press Test connection." 210 msgstr "" 211 212 #: admin/class-sellsmith-admin.php:566 213 msgid "AI model" 214 msgstr "" 215 216 #: admin/class-sellsmith-admin.php:574 217 msgid "Gemma gives the most free chats per day; the Gemini models give more polished replies. All five support product lookups." 218 msgstr "" 219 220 #: admin/class-sellsmith-admin.php:577 221 msgid "Free-tier limits by model" 222 msgstr "" 223 224 #: admin/class-sellsmith-admin.php:581 225 msgid "Model" 226 msgstr "" 227 228 #: admin/class-sellsmith-admin.php:582 229 msgid "Free chats / day*" 230 msgstr "" 231 232 #: admin/class-sellsmith-admin.php:583 233 msgid "Per minute" 234 msgstr "" 235 236 #: admin/class-sellsmith-admin.php:584 237 msgid "Product lookups" 238 msgstr "" 239 240 #: admin/class-sellsmith-admin.php:588 241 #: admin/class-sellsmith-admin.php:589 242 #: admin/class-sellsmith-admin.php:590 243 #: admin/class-sellsmith-admin.php:591 244 #: admin/class-sellsmith-admin.php:592 245 msgid "Yes" 246 msgstr "" 247 248 #. translators: %s: link to Google AI Studio 249 #: admin/class-sellsmith-admin.php:599 250 #, php-format 251 msgid "*Free limits depend on your Google account and region. These are typical values. Gemma gives the most free chats but slightly rougher replies. Paid use is cheapest on the Flash-Lite models. %s." 252 msgstr "" 253 254 #: admin/class-sellsmith-admin.php:600 255 msgid "Check your live limits" 256 msgstr "" 257 258 #: admin/class-sellsmith-admin.php:608 259 msgid "Custom model id (optional)" 260 msgstr "" 261 262 #: admin/class-sellsmith-admin.php:609 263 msgid "For advanced users. If filled, this overrides the dropdown. Useful when Google releases new models." 264 msgstr "" 265 266 #: admin/class-sellsmith-admin.php:613 267 msgid "Test your key" 268 msgstr "" 269 270 #: admin/class-sellsmith-admin.php:616 271 msgid "Test connection" 272 msgstr "" 273 274 #: admin/class-sellsmith-admin.php:625 275 msgid "Important: keep the free tier free." 276 msgstr "" 277 278 #: admin/class-sellsmith-admin.php:626 279 msgid "Do not enable billing on the Google Cloud project of this key. If billing is on, Google charges every request instead of using the free daily limit. A key from a fresh AI Studio project is perfect." 280 msgstr "" 281 282 #: admin/class-sellsmith-admin.php:635 283 msgid "The AI answers from this page plus your live product data. These are the questions real buyers ask on WhatsApp. Write your answers in any language, the AI adapts." 284 msgstr "" 285 286 #: admin/class-sellsmith-admin.php:638 287 msgid "About your store" 288 msgstr "" 289 290 #: admin/class-sellsmith-admin.php:642 291 msgid "Store overview" 292 msgstr "" 293 294 #: admin/class-sellsmith-admin.php:644 295 msgid "Example: Stride sells men and women sports shoes. Based in Lahore, shipping all Pakistan since 2021." 296 msgstr "" 297 298 #: admin/class-sellsmith-admin.php:649 299 msgid "Delivery" 300 msgstr "" 301 302 #: admin/class-sellsmith-admin.php:653 303 msgid "Delivery charges and time" 304 msgstr "" 305 306 #: admin/class-sellsmith-admin.php:654 307 msgid "Buyers ask: \"What are delivery charges?\" and \"When will I receive it?\"" 308 msgstr "" 309 310 #: admin/class-sellsmith-admin.php:655 311 msgid "Example: Rs 200 flat, free over Rs 5,000. Lahore 1-2 days, rest of Pakistan 2-4 days." 312 msgstr "" 313 314 #: admin/class-sellsmith-admin.php:659 315 msgid "Delivery areas" 316 msgstr "" 317 318 #: admin/class-sellsmith-admin.php:660 319 msgid "Buyers ask: \"Do you deliver to my area?\"" 320 msgstr "" 321 322 #: admin/class-sellsmith-admin.php:661 323 msgid "Example: All Pakistan via Leopards and TCS. No international shipping." 324 msgstr "" 325 326 #: admin/class-sellsmith-admin.php:665 327 msgid "Cash on delivery" 328 msgstr "" 329 330 #: admin/class-sellsmith-admin.php:666 331 msgid "We accept cash on delivery" 332 msgstr "" 333 334 #: admin/class-sellsmith-admin.php:671 335 msgid "Policies & payment" 336 msgstr "" 337 338 #: admin/class-sellsmith-admin.php:675 339 msgid "Open parcel policy" 340 msgstr "" 341 342 #: admin/class-sellsmith-admin.php:676 343 msgid "Buyers ask: \"Can I open and check the parcel before paying?\" Very common in COD markets." 344 msgstr "" 345 346 #: admin/class-sellsmith-admin.php:677 347 msgid "Example: Yes, you can open and check the parcel in front of the rider before paying." 348 msgstr "" 349 350 #: admin/class-sellsmith-admin.php:681 351 msgid "Faults, returns and warranty" 352 msgstr "" 353 354 #: admin/class-sellsmith-admin.php:682 355 msgid "Buyers ask: \"What if the product has a fault?\"" 356 msgstr "" 357 358 #: admin/class-sellsmith-admin.php:683 359 msgid "Example: 7-day free replacement for any fault. Exchange only, no cash refund." 360 msgstr "" 361 362 #: admin/class-sellsmith-admin.php:687 363 msgid "Discounts and bulk deals" 364 msgstr "" 365 366 #: admin/class-sellsmith-admin.php:688 367 msgid "Buyers ask: \"Any discount?\" and \"What if I buy 2?\"" 368 msgstr "" 369 370 #: admin/class-sellsmith-admin.php:689 371 msgid "Example: 10% off on 2 or more pairs. No other discounts right now." 372 msgstr "" 373 374 #: admin/class-sellsmith-admin.php:693 375 msgid "Payment methods" 376 msgstr "" 377 378 #: admin/class-sellsmith-admin.php:695 379 msgid "Example: COD, JazzCash, Easypaisa, bank transfer." 380 msgstr "" 381 382 #: admin/class-sellsmith-admin.php:700 383 msgid "Anything else" 384 msgstr "" 385 386 #: admin/class-sellsmith-admin.php:704 387 msgid "Notes for the AI" 388 msgstr "" 389 390 #: admin/class-sellsmith-admin.php:705 391 msgid "Size guides, store timings, special instructions. Optional." 392 msgstr "" 393 394 #: admin/class-sellsmith-admin.php:713 395 msgid "Look and feel" 396 msgstr "" 397 398 #: admin/class-sellsmith-admin.php:716 399 msgid "Position" 400 msgstr "" 401 402 #: admin/class-sellsmith-admin.php:718 403 msgid "Bottom right" 404 msgstr "" 405 406 #: admin/class-sellsmith-admin.php:719 407 msgid "Bottom left (good for RTL sites)" 408 msgstr "" 409 410 #: admin/class-sellsmith-admin.php:723 411 msgid "Brand color" 412 msgstr "" 413 414 #: admin/class-sellsmith-admin.php:726 415 msgid "Pick a color" 416 msgstr "" 417 418 #: admin/class-sellsmith-admin.php:733 419 msgid "This one color is used across the whole chat: the button, the header, and your reply bubbles. Click the swatch to pick, or type a hex code like #a0561b." 420 msgstr "" 421 422 #: admin/class-sellsmith-admin.php:736 423 msgid "Chat title" 424 msgstr "" 425 426 #: admin/class-sellsmith-admin.php:737 427 msgid "Welcome message" 428 msgstr "" 429 430 #: admin/class-sellsmith-admin.php:738 431 msgid "Pop-up greeting" 432 msgstr "" 433 434 #: admin/class-sellsmith-admin.php:741 435 msgid "Credit link" 436 msgstr "" 437 438 #: admin/class-sellsmith-admin.php:742 439 msgid "Show a small \"AI agent by Sellsmith\" link at the bottom of the chat" 440 msgstr "" 441 442 #: admin/class-sellsmith-admin.php:748 443 msgid "When the chat opens" 444 msgstr "" 445 446 #: admin/class-sellsmith-admin.php:753 447 msgid "Start questions" 448 msgstr "" 449 450 #: admin/class-sellsmith-admin.php:754 451 msgid "Ask \"Are you already a customer?\" first. Customers give their order number and email, new visitors give their email. Then they type their question." 452 msgstr "" 453 454 #: admin/class-sellsmith-admin.php:758 455 msgid "Email" 456 msgstr "" 457 458 #: admin/class-sellsmith-admin.php:760 459 msgid "Required, so you can always reply" 460 msgstr "" 461 462 #: admin/class-sellsmith-admin.php:761 463 msgid "Optional, visitors can skip it" 464 msgstr "" 465 466 #: admin/class-sellsmith-admin.php:765 467 msgid "Question" 468 msgstr "" 469 470 #: admin/class-sellsmith-admin.php:766 471 msgid "Button for customers" 472 msgstr "" 473 474 #: admin/class-sellsmith-admin.php:767 475 msgid "Button for new visitors" 476 msgstr "" 477 478 #: admin/class-sellsmith-admin.php:769 479 msgid "With WooCommerce, the order number is checked against your orders. Only you see the result, in the Inbox. The visitor is never told whether an order exists." 480 msgstr "" 481 482 #: admin/class-sellsmith-admin.php:775 483 #: admin/class-sellsmith-admin.php:787 484 msgid "WhatsApp handoff" 485 msgstr "" 486 487 #: admin/class-sellsmith-admin.php:780 488 msgid "WhatsApp number" 489 msgstr "" 490 491 #: admin/class-sellsmith-admin.php:781 492 msgid "International format, digits only, no + and no spaces. Example for Pakistan: 923001234567" 493 msgstr "" 494 495 #: admin/class-sellsmith-admin.php:788 496 msgid "Let the AI offer a WhatsApp button to serious buyers, with their order details pre-written" 497 msgstr "" 498 499 #: admin/class-sellsmith-admin.php:795 500 msgid "No WhatsApp Business API and no Meta approval needed. The button uses an official wa.me link that opens your normal WhatsApp." 501 msgstr "" 502 503 #: admin/class-sellsmith-admin.php:801 504 msgid "Limits & privacy" 505 msgstr "" 506 507 #: admin/class-sellsmith-admin.php:806 508 msgid "Message limit per chat, then WhatsApp" 509 msgstr "" 510 511 #: admin/class-sellsmith-admin.php:807 512 msgid "After this many buyer messages in one chat, the assistant stops and offers the WhatsApp button instead. Keeps each chat cheap and sends hot buyers straight to you. Default 10." 513 msgstr "" 514 515 #: admin/class-sellsmith-admin.php:812 516 msgid "Message limit per visitor per hour" 517 msgstr "" 518 519 #: admin/class-sellsmith-admin.php:813 520 msgid "Protects your free daily AI limit from abuse. Default 20." 521 msgstr "" 522 523 #: admin/class-sellsmith-admin.php:818 524 msgid "Auto-delete old conversations after (days)" 525 msgstr "" 526 527 #: admin/class-sellsmith-admin.php:819 528 msgid "Leave 0 to keep every conversation forever. Set a number (for example 90) and conversations older than that are removed automatically each day, to save space and respect privacy." 529 msgstr "" 530 531 #: admin/class-sellsmith-admin.php:824 532 msgid "Privacy consent" 533 msgstr "" 534 535 #: admin/class-sellsmith-admin.php:825 536 msgid "Ask visitors to accept a short privacy note before the first message (recommended for EU stores)" 537 msgstr "" 538 539 #: admin/class-sellsmith-admin.php:829 540 msgid "On uninstall" 541 msgstr "" 542 543 #: admin/class-sellsmith-admin.php:830 544 msgid "Keep my settings and saved conversations if I delete the plugin" 545 msgstr "" 546 547 #: admin/class-sellsmith-admin.php:837 548 msgid "Privacy note: visitor chat messages are sent to Google Gemini for processing using your API key. Mention this in your privacy policy." 549 msgstr "" 550 551 #: admin/class-sellsmith-admin.php:852 552 msgid "Reply to buyers yourself in real time." 553 msgstr "" 554 555 #: admin/class-sellsmith-admin.php:853 556 msgid "Lead scoring" 557 msgstr "" 558 559 #: admin/class-sellsmith-admin.php:853 560 msgid "See Hot, Warm, and Cold buyers at a glance." 561 msgstr "" 562 563 #: admin/class-sellsmith-admin.php:854 564 msgid "Hot-lead alerts" 565 msgstr "" 566 567 #: admin/class-sellsmith-admin.php:854 568 msgid "Get an email the moment a serious buyer chats." 569 msgstr "" 570 571 #: admin/class-sellsmith-admin.php:855 572 msgid "Reply by email" 573 msgstr "" 574 575 #: admin/class-sellsmith-admin.php:855 576 msgid "Answer a saved chat straight from your inbox." 577 msgstr "" 578 579 #: admin/class-sellsmith-admin.php:856 580 msgid "Agent branding" 581 msgstr "" 582 583 #: admin/class-sellsmith-admin.php:856 584 msgid "Show your photo or logo as the chat avatar." 585 msgstr "" 586 587 #: admin/class-sellsmith-admin.php:862 588 msgid "Sellsmith Pro is active. Here is everything it adds and where to set each one up." 589 msgstr "" 590 591 #: admin/class-sellsmith-admin.php:864 592 msgid "Sellsmith Pro is installed. Activate your license to switch these on." 593 msgstr "" 594 595 #: admin/class-sellsmith-admin.php:866 596 msgid "These features are part of Sellsmith Pro." 597 msgstr "" 598 599 #: admin/class-sellsmith-admin.php:873 600 msgid "Active" 601 msgstr "" 602 603 #: admin/class-sellsmith-admin.php:884 604 #: admin/class-sellsmith-leads.php:44 605 #: admin/class-sellsmith-leads.php:45 606 #: sellsmith.php:103 10 607 msgid "Settings" 11 608 msgstr "" 12 609 13 msgid "Sellsmith" 14 msgstr "" 15 16 msgid "WooCommerce is not active. Sellsmith will still chat and capture leads, and product answers will switch on automatically when WooCommerce is installed." 17 msgstr "" 18 19 msgid "Testing…" 20 msgstr "" 21 22 msgid "Connection" 23 msgstr "" 24 25 msgid "Business Profile" 26 msgstr "" 27 28 msgid "Chat Widget" 29 msgstr "" 30 31 msgid "WhatsApp" 32 msgstr "" 33 34 msgid "Advanced" 35 msgstr "" 36 37 msgid "Save settings" 38 msgstr "" 39 40 msgid "1. Connect your free Gemini key" 41 msgstr "" 42 43 msgid "2. Fill your business profile" 44 msgstr "" 45 46 msgid "3. Add your WhatsApp number" 47 msgstr "" 48 49 msgid "Setup complete. Turn on the widget in the Chat Widget tab and your assistant is live." 50 msgstr "" 51 52 msgid "Setup: %d of 3 steps done" 53 msgstr "" 54 55 msgid "Get a free Gemini API key from %s (takes 1 minute, no card needed)." 56 msgstr "" 57 58 msgid "Gemini API key" 59 msgstr "" 60 61 msgid "A key is saved. Leave blank to keep it, or paste a new key to replace it." 62 msgstr "" 63 64 msgid "Paste your key, then press Test connection." 65 msgstr "" 66 67 msgid "AI model" 68 msgstr "" 69 70 msgid "Flash Lite gives the biggest free daily limit and is fast enough for sales chat." 71 msgstr "" 72 73 msgid "Custom model id (optional)" 74 msgstr "" 75 76 msgid "For advanced users. If filled, this overrides the dropdown. Useful when Google releases new models." 77 msgstr "" 78 79 msgid "Test" 80 msgstr "" 81 82 msgid "Test connection" 83 msgstr "" 84 85 msgid "Important: keep the free tier free." 86 msgstr "" 87 88 msgid "Do not enable billing on the Google Cloud project of this key. If billing is on, Google charges every request instead of using the free daily limit. A key from a fresh AI Studio project is perfect." 89 msgstr "" 90 91 msgid "The AI answers from this page plus your live product data. These are the questions real buyers ask on WhatsApp. Write your answers in any language, the AI adapts." 92 msgstr "" 93 94 msgid "About your store" 95 msgstr "" 96 97 msgid "Example: Stride sells men and women sports shoes. Based in Lahore, shipping all Pakistan since 2021." 98 msgstr "" 99 100 msgid "Delivery charges and time" 101 msgstr "" 102 103 msgid "Buyers ask: \"What are delivery charges?\" and \"When will I receive it?\"" 104 msgstr "" 105 106 msgid "Example: Rs 200 flat, free over Rs 5,000. Lahore 1-2 days, rest of Pakistan 2-4 days." 107 msgstr "" 108 109 msgid "Delivery areas" 110 msgstr "" 111 112 msgid "Buyers ask: \"Do you deliver to my area?\"" 113 msgstr "" 114 115 msgid "Example: All Pakistan via Leopards and TCS. No international shipping." 116 msgstr "" 117 118 msgid "Cash on delivery" 119 msgstr "" 120 121 msgid "We accept cash on delivery" 122 msgstr "" 123 124 msgid "Open parcel policy" 125 msgstr "" 126 127 msgid "Buyers ask: \"Can I open and check the parcel before paying?\" Very common in COD markets." 128 msgstr "" 129 130 msgid "Example: Yes, you can open and check the parcel in front of the rider before paying." 131 msgstr "" 132 133 msgid "Faults, returns and warranty" 134 msgstr "" 135 136 msgid "Buyers ask: \"What if the product has a fault?\"" 137 msgstr "" 138 139 msgid "Example: 7-day free replacement for any fault. Exchange only, no cash refund." 140 msgstr "" 141 142 msgid "Discounts and bulk deals" 143 msgstr "" 144 145 msgid "Buyers ask: \"Any discount?\" and \"What if I buy 2?\"" 146 msgstr "" 147 148 msgid "Example: 10% off on 2 or more pairs. No other discounts right now." 149 msgstr "" 150 151 msgid "Payment methods" 152 msgstr "" 153 154 msgid "Example: COD, JazzCash, Easypaisa, bank transfer." 155 msgstr "" 156 157 msgid "Anything else the AI should know" 158 msgstr "" 159 160 msgid "Size guides, store timings, special instructions. Optional." 161 msgstr "" 162 163 msgid "Show widget" 164 msgstr "" 165 166 msgid "Show the chat widget on my store" 167 msgstr "" 168 169 msgid "Position" 170 msgstr "" 171 172 msgid "Bottom right" 173 msgstr "" 174 175 msgid "Bottom left (good for RTL sites)" 176 msgstr "" 177 178 msgid "Brand color" 179 msgstr "" 180 181 msgid "Widget title" 182 msgstr "" 183 184 msgid "Welcome message" 185 msgstr "" 186 187 msgid "Teaser bubble text" 188 msgstr "" 189 190 msgid "WhatsApp number" 191 msgstr "" 192 193 msgid "International format, digits only, no + and no spaces. Example for Pakistan: 923001234567" 194 msgstr "" 195 196 msgid "WhatsApp handoff" 197 msgstr "" 198 199 msgid "Let the AI offer a WhatsApp button to serious buyers, with their order details pre-written" 200 msgstr "" 201 202 msgid "No WhatsApp Business API and no Meta approval needed. The button uses an official wa.me link that opens your normal WhatsApp." 203 msgstr "" 204 205 msgid "Messages per visitor per hour" 206 msgstr "" 207 208 msgid "Protects your free daily AI limit from abuse. Default 20." 209 msgstr "" 210 211 msgid "Privacy consent" 212 msgstr "" 213 214 msgid "Ask visitors to accept a short privacy note before the first message (recommended for EU stores)" 215 msgstr "" 216 217 msgid "On uninstall" 218 msgstr "" 219 220 msgid "Keep my settings and saved conversations if I delete the plugin" 221 msgstr "" 222 223 msgid "Privacy note: visitor chat messages are sent to Google Gemini for processing using your API key. Mention this in your privacy policy." 224 msgstr "" 225 226 msgid "Leads" 227 msgstr "" 228 610 #: admin/class-sellsmith-admin.php:892 611 msgid "Activate your license" 612 msgstr "" 613 614 #: admin/class-sellsmith-admin.php:898 615 msgid "Get Sellsmith Pro" 616 msgstr "" 617 618 #: admin/class-sellsmith-leads.php:26 619 #: admin/class-sellsmith-leads.php:27 620 msgid "Dashboard" 621 msgstr "" 622 623 #: admin/class-sellsmith-leads.php:34 624 #: admin/class-sellsmith-leads.php:35 625 #: admin/class-sellsmith-leads.php:107 626 msgid "Inbox" 627 msgstr "" 628 629 #: admin/class-sellsmith-leads.php:101 630 msgid "All chats" 631 msgstr "" 632 633 #: admin/class-sellsmith-leads.php:156 229 634 msgid "Deleted." 230 635 msgstr "" 231 636 637 #: admin/class-sellsmith-leads.php:161 232 638 msgid "Chats today" 233 639 msgstr "" 234 640 641 #: admin/class-sellsmith-leads.php:162 235 642 msgid "Chats, last 7 days" 236 643 msgstr "" 237 644 645 #: admin/class-sellsmith-leads.php:163 238 646 msgid "WhatsApp clicks, last 7 days" 239 647 msgstr "" 240 648 241 msgid "Free version keeps the last 30 days of conversations." 242 msgstr "" 243 244 msgid "Search city or message…" 245 msgstr "" 246 649 #. translators: %d: number of days. 650 #: admin/class-sellsmith-leads.php:170 651 #, php-format 652 msgid "Conversations older than %d days are deleted automatically. Change this in Settings → Advanced." 653 msgstr "" 654 655 #: admin/class-sellsmith-leads.php:174 656 msgid "All conversations are kept. Set an auto-delete period in Settings → Advanced if you prefer." 657 msgstr "" 658 659 #: admin/class-sellsmith-leads.php:180 660 msgid "Search email, order, city or message…" 661 msgstr "" 662 663 #: admin/class-sellsmith-leads.php:181 247 664 msgid "Search" 248 665 msgstr "" 249 666 667 #: admin/class-sellsmith-leads.php:191 668 #: admin/class-sellsmith-leads.php:298 669 #: includes/class-sellsmith-privacy.php:74 250 670 msgid "Started" 251 671 msgstr "" 252 672 673 #: admin/class-sellsmith-leads.php:192 253 674 msgid "First message" 254 675 msgstr "" 255 676 677 #: admin/class-sellsmith-leads.php:193 678 #: admin/class-sellsmith-leads.php:299 679 #: includes/class-sellsmith-privacy.php:78 256 680 msgid "City" 257 681 msgstr "" 258 682 683 #: admin/class-sellsmith-leads.php:194 684 #: admin/class-sellsmith-leads.php:300 685 #: includes/class-sellsmith-privacy.php:84 686 msgid "Contact" 687 msgstr "" 688 689 #: admin/class-sellsmith-leads.php:195 259 690 msgid "Products" 260 691 msgstr "" 261 692 693 #: admin/class-sellsmith-leads.php:196 262 694 msgid "Messages" 263 695 msgstr "" 264 696 697 #: admin/class-sellsmith-leads.php:204 265 698 msgid "No conversations yet. They will appear here as soon as visitors start chatting." 266 699 msgstr "" 267 700 701 #: admin/class-sellsmith-leads.php:214 268 702 msgid "(no message)" 269 703 msgstr "" 270 704 705 #: admin/class-sellsmith-leads.php:223 271 706 msgid "View" 272 707 msgstr "" 273 708 709 #: admin/class-sellsmith-leads.php:224 710 #: admin/class-sellsmith-leads.php:347 274 711 msgid "Delete this conversation?" 275 712 msgstr "" 276 713 714 #: admin/class-sellsmith-leads.php:224 277 715 msgid "Delete" 278 716 msgstr "" 279 717 718 #: admin/class-sellsmith-leads.php:229 280 719 msgid "clicked" 281 720 msgstr "" 282 721 722 #: admin/class-sellsmith-leads.php:243 283 723 msgid "Delete selected conversations?" 284 724 msgstr "" 285 725 726 #: admin/class-sellsmith-leads.php:243 286 727 msgid "Delete selected" 287 728 msgstr "" 288 729 289 msgid "Back to leads" 290 msgstr "" 291 730 #: admin/class-sellsmith-leads.php:272 731 msgid "Back to inbox" 732 msgstr "" 733 734 #: admin/class-sellsmith-leads.php:275 292 735 msgid "Conversation not found" 293 736 msgstr "" 294 737 738 #: admin/class-sellsmith-leads.php:282 295 739 msgid "Conversation" 296 740 msgstr "" 297 741 742 #: admin/class-sellsmith-leads.php:301 743 msgid "Visitor" 744 msgstr "" 745 746 #: admin/class-sellsmith-leads.php:302 298 747 msgid "Note" 299 748 msgstr "" 300 749 750 #: admin/class-sellsmith-leads.php:303 301 751 msgid "Clicked the button" 302 752 msgstr "" 303 753 754 #: admin/class-sellsmith-leads.php:308 755 msgid "Open their order" 756 msgstr "" 757 758 #: admin/class-sellsmith-leads.php:313 304 759 msgid "Products discussed:" 305 760 msgstr "" 306 761 762 #: admin/class-sellsmith-leads.php:322 307 763 msgid "Buyer" 308 764 msgstr "" 309 765 766 #: admin/class-sellsmith-leads.php:324 767 msgid "You" 768 msgstr "" 769 770 #: admin/class-sellsmith-leads.php:326 310 771 msgid "AI" 311 772 msgstr "" 312 773 774 #: admin/class-sellsmith-leads.php:347 313 775 msgid "Delete conversation" 314 776 msgstr "" 315 777 778 #: admin/class-sellsmith-leads.php:379 779 msgid "Customer" 780 msgstr "" 781 782 #: admin/class-sellsmith-leads.php:384 783 #: includes/class-sellsmith-conversations.php:376 784 msgid "New visitor" 785 msgstr "" 786 787 #: includes/class-sellsmith-chat.php:28 788 #: includes/class-sellsmith-chat.php:90 789 #: includes/class-sellsmith-rest.php:242 790 msgid "Chat is not available right now." 791 msgstr "" 792 793 #: includes/class-sellsmith-chat.php:36 794 #: includes/class-sellsmith-rest.php:256 795 msgid "Please refresh the page and try again." 796 msgstr "" 797 798 #: includes/class-sellsmith-chat.php:49 799 msgid "You are sending messages very fast. Please wait a minute and try again." 800 msgstr "" 801 802 #: includes/class-sellsmith-chat.php:94 803 msgid "Please type a message first." 804 msgstr "" 805 806 #: includes/class-sellsmith-chat.php:99 807 #: includes/class-sellsmith-chat.php:154 808 msgid "Our assistant is taking a short break. Message us directly and we will reply fast." 809 msgstr "" 810 811 #: includes/class-sellsmith-chat.php:105 812 #: includes/class-sellsmith-chat.php:222 813 #: includes/class-sellsmith-rest.php:282 814 msgid "Something went wrong. Please try again." 815 msgstr "" 816 817 #: includes/class-sellsmith-chat.php:112 818 msgid "Thanks for chatting! To get full details and place your order, please message us on WhatsApp. Our team will help you finish up there." 819 msgstr "" 820 821 #: includes/class-sellsmith-chat.php:217 822 msgid "This chat link is no longer valid. Please start a new chat." 823 msgstr "" 824 825 #: includes/class-sellsmith-chat.php:234 826 msgid "Thanks! We have your messages and will reply to you soon." 827 msgstr "" 828 829 #: includes/class-sellsmith-chat.php:258 830 msgid "Got it! 🙏 We will reply to you here soon. You can add more details if you like." 831 msgstr "" 832 833 #: includes/class-sellsmith-chat.php:264 834 msgid "Thanks for your message! What is the best email or WhatsApp number to reach you?" 835 msgstr "" 836 837 #: includes/class-sellsmith-chat.php:267 838 msgid "Please also leave your email or WhatsApp number so we can reply." 839 msgstr "" 840 841 #: includes/class-sellsmith-conversations.php:387 842 msgid "Customer, no order number given" 843 msgstr "" 844 845 #: includes/class-sellsmith-conversations.php:387 846 msgid "no orders under this email" 847 msgstr "" 848 849 #. translators: 1: number of orders ("5+" at most), 2: newest order number. 850 #: includes/class-sellsmith-conversations.php:390 851 #, php-format 852 msgid "Customer, no order number given, %1$s orders under this email (newest #%2$d)" 853 msgstr "" 854 855 #. translators: %s: order number. 856 #: includes/class-sellsmith-conversations.php:397 857 #, php-format 858 msgid "Customer, order #%s checked" 859 msgstr "" 860 861 #. translators: %s: order number. 862 #: includes/class-sellsmith-conversations.php:404 863 #, php-format 864 msgid "Customer, order #%s exists but under a different email" 865 msgstr "" 866 867 #. translators: %s: order number. 868 #: includes/class-sellsmith-conversations.php:407 869 #, php-format 870 msgid "Customer, order #%s not found in this shop" 871 msgstr "" 872 873 #. translators: %s: order number. 874 #: includes/class-sellsmith-conversations.php:410 875 #, php-format 876 msgid "Customer, order #%s" 877 msgstr "" 878 879 #: includes/class-sellsmith-gemini.php:27 880 msgid "Please enter an API key first." 881 msgstr "" 882 883 #. translators: %s: error message 884 #: includes/class-sellsmith-gemini.php:45 885 #, php-format 886 msgid "Could not reach Google: %s" 887 msgstr "" 888 889 #: includes/class-sellsmith-gemini.php:53 890 msgid "Unknown error." 891 msgstr "" 892 893 #. translators: 1: HTTP status code, 2: API error message 894 #: includes/class-sellsmith-gemini.php:57 895 #, php-format 896 msgid "Key rejected (HTTP %1$d): %2$s" 897 msgstr "" 898 899 #. translators: %d: number of models available 900 #: includes/class-sellsmith-gemini.php:76 901 #, php-format 902 msgid "Key is valid. Connection working. %d models available to this key." 903 msgstr "" 904 905 #. translators: %s: model id 906 #: includes/class-sellsmith-gemini.php:83 907 #, php-format 908 msgid "Note: the selected model \"%s\" was not in the list. It may still work, or pick another model." 909 msgstr "" 910 911 #: includes/class-sellsmith-install.php:19 316 912 msgid "Sellsmith requires PHP 7.4 or newer. Please ask your host to upgrade PHP." 317 913 msgstr "" 318 914 915 #: includes/class-sellsmith-install.php:20 319 916 msgid "Plugin activation error" 320 917 msgstr "" 321 918 919 #: includes/class-sellsmith-privacy.php:31 920 #: includes/class-sellsmith-privacy.php:44 921 msgid "Sellsmith chat conversations" 922 msgstr "" 923 924 #: includes/class-sellsmith-privacy.php:90 925 msgid "Order number" 926 msgstr "" 927 928 #: includes/class-sellsmith-privacy.php:95 929 msgid "Transcript" 930 msgstr "" 931 932 #: includes/class-sellsmith-privacy.php:101 933 msgid "Chat conversations" 934 msgstr "" 935 936 #: includes/class-sellsmith-privacy.php:146 937 msgid "This site uses an AI chat assistant (Sellsmith). Messages you type in the chat are sent to Google Gemini for processing in order to generate replies. Conversations, including any city or contact details you share, are stored on this site so the store can follow up on your questions, and may be deleted after a retention period the store has set." 938 msgstr "" 939 940 #: includes/class-sellsmith-rest.php:184 941 msgid "Your session expired. Please refresh the page and try again." 942 msgstr "" 943 944 #: includes/class-sellsmith-rest.php:201 945 #: includes/class-sellsmith-rest.php:250 946 #: includes/class-sellsmith-rest.php:368 947 msgid "Too many requests. Please wait a moment." 948 msgstr "" 949 950 #: includes/class-sellsmith-rest.php:261 951 msgid "Please pick an answer." 952 msgstr "" 953 954 #: includes/class-sellsmith-rest.php:268 955 #: includes/class-sellsmith-widget.php:109 956 msgid "Please enter a valid email." 957 msgstr "" 958 959 #: includes/class-sellsmith-rest.php:271 960 msgid "Please enter your email." 961 msgstr "" 962 963 #: includes/class-sellsmith-rest.php:374 964 msgid "This chat is no longer available." 965 msgstr "" 966 967 #: includes/class-sellsmith-settings.php:46 968 msgid "Store Assistant" 969 msgstr "" 970 971 #: includes/class-sellsmith-settings.php:47 972 msgid "Salam! Ask me about any product, price, or delivery." 973 msgstr "" 974 975 #: includes/class-sellsmith-settings.php:48 976 msgid "Need help finding something?" 977 msgstr "" 978 979 #: includes/class-sellsmith-settings.php:53 980 msgid "Are you already a customer?" 981 msgstr "" 982 983 #: includes/class-sellsmith-settings.php:54 984 msgid "Yes, I have an order" 985 msgstr "" 986 987 #: includes/class-sellsmith-settings.php:55 988 msgid "No, not yet" 989 msgstr "" 990 991 #: includes/class-sellsmith-settings.php:95 992 msgid "Gemma 4 31B: most free chats/day (~1,500), basic replies" 993 msgstr "" 994 995 #: includes/class-sellsmith-settings.php:96 996 msgid "Gemini 3.1 Flash-Lite: recommended (~500/day, smart + free)" 997 msgstr "" 998 999 #: includes/class-sellsmith-settings.php:97 1000 msgid "Gemini 3.5 Flash: smartest replies, low free limit" 1001 msgstr "" 1002 1003 #: includes/class-sellsmith-settings.php:98 1004 msgid "Gemini 2.5 Flash-Lite: cheapest for paid use" 1005 msgstr "" 1006 1007 #: includes/class-sellsmith-settings.php:99 1008 msgid "Gemini 2.5 Flash: stable all-rounder" 1009 msgstr "" 1010 1011 #: includes/class-sellsmith-widget.php:82 322 1012 msgid "Type a message" 323 1013 msgstr "" 324 1014 1015 #: includes/class-sellsmith-widget.php:83 325 1016 msgid "Send message" 326 1017 msgstr "" 327 1018 1019 #: includes/class-sellsmith-widget.php:84 328 1020 msgid "Open chat" 329 1021 msgstr "" 330 1022 1023 #: includes/class-sellsmith-widget.php:85 331 1024 msgid "Close chat" 332 1025 msgstr "" 333 1026 1027 #: includes/class-sellsmith-widget.php:86 1028 msgid "Start new chat" 1029 msgstr "" 1030 1031 #: includes/class-sellsmith-widget.php:87 334 1032 msgid "Online · replies in seconds" 335 1033 msgstr "" 336 1034 1035 #: includes/class-sellsmith-widget.php:88 337 1036 msgid "Chat on WhatsApp" 338 1037 msgstr "" 339 1038 1039 #: includes/class-sellsmith-widget.php:89 340 1040 msgid "Continue on WhatsApp" 341 1041 msgstr "" 342 1042 1043 #: includes/class-sellsmith-widget.php:90 343 1044 msgid "This chat uses AI (Google Gemini) to answer. Your messages are processed by Google." 344 1045 msgstr "" 345 1046 1047 #: includes/class-sellsmith-widget.php:91 346 1048 msgid "Okay, start chat" 347 1049 msgstr "" 348 1050 1051 #: includes/class-sellsmith-widget.php:92 349 1052 msgid "Could not send. Please try again." 350 1053 msgstr "" 351 1054 1055 #: includes/class-sellsmith-widget.php:93 352 1056 msgid "AI agent by" 353 1057 msgstr "" 354 1058 355 msgid "Sellsmith chat conversations" 356 msgstr "" 357 358 msgid "Chat conversations" 359 msgstr "" 360 361 msgid "Transcript" 362 msgstr "" 363 364 msgid "This site uses an AI chat assistant (Sellsmith). Messages you type in the chat are sent to Google Gemini for processing in order to generate replies. Conversations, including any city or contact details you share, are stored on this site for up to 30 days so the store can follow up on your questions." 365 msgstr "" 366 367 msgid "Store Assistant" 368 msgstr "" 369 370 msgid "Salam! Ask me about any product, price, or delivery." 371 msgstr "" 372 373 msgid "Need help finding something?" 374 msgstr "" 375 376 msgid "Gemini Flash Lite (recommended, highest free limit)" 377 msgstr "" 378 379 msgid "Gemini Flash (smarter, lower free limit)" 380 msgstr "" 381 382 msgid "Chat is not available right now." 383 msgstr "" 384 385 msgid "Please refresh the page and try again." 386 msgstr "" 387 388 msgid "Please type a message first." 389 msgstr "" 390 391 msgid "You are sending messages very fast. Please wait a minute and try again." 392 msgstr "" 393 394 msgid "Our assistant is taking a short break. Message us directly and we will reply fast." 395 msgstr "" 396 397 msgid "Something went wrong. Please try again." 398 msgstr "" 399 400 msgid "Please enter an API key first." 401 msgstr "" 402 403 msgid "Could not reach Google: %s" 404 msgstr "" 405 406 msgid "Unknown error." 407 msgstr "" 408 409 msgid "Key rejected (HTTP %1$d): %2$s" 410 msgstr "" 411 412 msgid "Key is valid. Connection working. %d models available to this key." 413 msgstr "" 414 415 msgid "Note: the selected model \"%s\" was not in the list. It may still work, or pick another model." 416 msgstr "" 1059 #: includes/class-sellsmith-widget.php:94 1060 msgid "Your email or WhatsApp number" 1061 msgstr "" 1062 1063 #: includes/class-sellsmith-widget.php:95 1064 msgid "Hi! 👋 Ask your question here and we will get back to you." 1065 msgstr "" 1066 1067 #: includes/class-sellsmith-widget.php:96 1068 msgid "Sent" 1069 msgstr "" 1070 1071 #: includes/class-sellsmith-widget.php:97 1072 msgid "Seen" 1073 msgstr "" 1074 1075 #: includes/class-sellsmith-widget.php:102 1076 msgid "Great! What is your order number, and the email you ordered with?" 1077 msgstr "" 1078 1079 #: includes/class-sellsmith-widget.php:103 1080 msgid "What is your email? If we cannot reply right away, we will answer you there." 1081 msgstr "" 1082 1083 #: includes/class-sellsmith-widget.php:104 1084 msgid "Order number (if you have it)" 1085 msgstr "" 1086 1087 #: includes/class-sellsmith-widget.php:105 1088 msgid "Email you ordered with" 1089 msgstr "" 1090 1091 #: includes/class-sellsmith-widget.php:106 1092 msgid "Your email" 1093 msgstr "" 1094 1095 #: includes/class-sellsmith-widget.php:107 1096 msgid "Continue" 1097 msgstr "" 1098 1099 #: includes/class-sellsmith-widget.php:108 1100 msgid "Skip" 1101 msgstr "" 1102 1103 #: includes/class-sellsmith-widget.php:110 1104 msgid "Order" 1105 msgstr "" 1106 1107 #: includes/class-sellsmith-widget.php:112 1108 msgid "Thanks! Now type your question below. If we are not here right away, we will reply to you by email." 1109 msgstr "" 1110 1111 #: includes/class-sellsmith-widget.php:113 1112 #: includes/class-sellsmith-widget.php:114 1113 msgid "Thanks! Now type your question below." 1114 msgstr "" 1115 1116 #: includes/class-sellsmith-widget.php:115 1117 msgid "Pick an answer above first" 1118 msgstr "" -
sellsmith/trunk/readme.txt
r3630208 r3712213 5 5 Tested up to: 7.0 6 6 Requires PHP: 7.4 7 Stable tag: 1. 1.07 Stable tag: 1.2.0 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 50 50 * Chat widget with your color, position, and welcome message 51 51 * Two modes: AI mode (answers from your products) or Message mode (no API key, just collect each visitor's question and contact as a lead) 52 * Start questions: the chat can open with "Are you already a customer?". Customers give their order number and email, new visitors give their email, so you can always reply, even after they leave 52 53 * 5-minute setup wizard with a 3-step checklist 53 54 * Business profile built from the 9 real buyer questions, including cash on delivery and open parcel policy … … 138 139 == Changelog == 139 140 141 = 1.2.0 = 142 * New: Start questions. The chat can open with "Are you already a customer?". Customers give their order number and the email they ordered with, new visitors give their email, before they type. Turn it on in Settings, Appearance. The email can be required or optional. 143 * New: With WooCommerce, the order number is checked against your orders. The result shows only in your Inbox, never to the visitor. 144 * New: The Inbox search also finds emails and order numbers. 145 * Fix: Saving a visitor's email no longer moves a live chat (Sellsmith Pro) out of the live inbox. 146 * Fix: The privacy eraser removes every chat for an email, not only the first ten, and the export and eraser also find an email saved from the chat. 147 * Fix: The privacy consent note did not show, even when it was turned on. 148 * Fix: Opening a chat from its email link ("Open my chat") stopped the chat from loading. 149 140 150 = 1.1.0 = 141 151 * New: Message mode runs the widget with no API key. Visitors leave their question and their email or WhatsApp number, and it is saved as a lead in your inbox for you to follow up. Great for going live in seconds while you set up Gemini. -
sellsmith/trunk/sellsmith.php
r3587090 r3712213 4 4 * Plugin URI: https://devtonicstudios.com/sellsmith 5 5 * Description: An AI sales agent for your store. Answers buyers from your real product data, captures leads, and hands hot buyers to your WhatsApp. Runs on your own free Gemini API key. 6 * Version: 1. 1.06 * Version: 1.2.0 7 7 * Requires at least: 6.0 8 8 * Requires PHP: 7.4 … … 17 17 defined( 'ABSPATH' ) || exit; 18 18 19 define( 'SELLSMITH_VERSION', '1. 1.0' );19 define( 'SELLSMITH_VERSION', '1.2.0' ); 20 20 define( 'SELLSMITH_DB_VERSION', '1' ); 21 21 define( 'SELLSMITH_FILE', __FILE__ );
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)