(string) ( $away['from'] ?? '' ),
'until' => (string) ( $away['until'] ?? '' ),
'note' => (string) ( $away['note'] ?? '' ),
);
}
/**
* Whether the owner is away today.
*
* @return bool
*/
public static function active() {
$away = self::get();
return '' !== $away['until'] && $away['until'] >= wp_date( 'Y-m-d' ) && $away['from'] <= wp_date( 'Y-m-d' );
}
/**
* The last day away, as people read it, e.g. "15 November 2026".
*
* @return string
*/
public static function until_text() {
$until = date_create_immutable( self::get()['until'] . ' 12:00', wp_timezone() );
return $until ? wp_date( get_option( 'date_format' ), $until->getTimestamp() ) : '';
}
/**
* Starts or ends the away time.
*
* @param string $until Last day away, Y-m-d, or "" to end it.
* @param string $note What people should know, e.g. who stands in, optional.
* @return bool
*/
public static function set( $until, $note = '' ) {
if ( '' === $until ) {
$away = self::get();
// The list of what came in stays for a few days.
$away['until'] = $away['from'] && $away['until'] > wp_date( 'Y-m-d' ) ? wp_date( 'Y-m-d', time() - DAY_IN_SECONDS ) : $away['until'];
return update_option( self::OPTION, $away, false );
}
if ( ! preg_match( '/^\d{4}-\d{2}-\d{2}$/', $until ) || $until < wp_date( 'Y-m-d' ) ) {
return false;
}
return update_option(
self::OPTION,
array(
'from' => wp_date( 'Y-m-d' ),
'until' => $until,
'note' => mb_substr( sanitize_textarea_field( $note ), 0, 300 ),
),
false
);
}
/**
* Without the AI assistant, a short confirmation of its own; with it, the assistant writes it
* and says when the answer comes.
*
* @param int $lead_id Lead ID.
*/
public static function reply( $lead_id ) {
if ( ! self::active() || Ai::enabled() ) {
return;
}
$lead = Leads::get( (int) $lead_id );
if ( ! $lead || 'new' !== $lead->status || in_array( $lead->form_plugin, array( 'manual', 'woocommerce', 'order' ), true ) || ! is_email( $lead->email ) ) {
return;
}
// One answer a day per address, so a form sent twice gets one.
if ( get_transient( 'dealroot_away_' . md5( strtolower( $lead->email ) ) ) ) {
return;
}
set_transient( 'dealroot_away_' . md5( strtolower( $lead->email ) ), 1, DAY_IN_SECONDS );
$business = Ai::business();
$note = self::get()['note'];
$body = sprintf(
/* translators: 1: date, 2: name of the business */
__( "Thank you for your message. We are away until %1\$s and will answer you personally after that.\n\nKind regards\n%2\$s", 'dealroot' ),
self::until_text(),
$business
);
if ( '' !== $note ) {
$body = str_replace( "\n\n", "\n\n" . $note . "\n\n", $body );
}
/* translators: %s: name of the business */
Ai::mail( $lead->email, sprintf( __( 'Your message to %s', 'dealroot' ), $business ), $body, true );
}
/**
* Enquiries that came while the owner was away and still wait, those likely worth the most first.
*
* @return object[]
*/
public static function waiting() {
global $wpdb;
$away = self::get();
if ( '' === $away['from'] ) {
return array();
}
$from = date_create_immutable( $away['from'] . ' 00:00', wp_timezone() );
$leads = (array) $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM %i WHERE status = 'new' AND form_plugin NOT IN ('manual', 'woocommerce') AND ai_kind NOT IN ('spam', 'job') AND created_at >= %s ORDER BY created_at DESC LIMIT 100",
Installer::table(),
gmdate( 'Y-m-d H:i:s', $from ? $from->getTimestamp() : time() )
)
);
foreach ( $leads as $lead ) {
$worth = null !== $lead->ai_budget ? (float) $lead->ai_budget : ( null !== $lead->value ? (float) $lead->value : 0.0 );
/**
* Filters what a lead is likely worth, e.g. with the expected value of Dealroot Pro.
*
* @param float $worth Amount in the currency of the site.
* @param object $lead Lead.
*/
$lead->worth = (float) apply_filters( 'dealroot_lead_worth', $worth, $lead );
}
usort(
$leads,
static function ( $a, $b ) {
$order = $b->worth <=> $a->worth;
return 0 !== $order ? $order : strcmp( $a->created_at, $b->created_at );
}
);
return $leads;
}
/**
* On the overview: while away, that the mode is on; back again, what came in.
*/
public static function panel() {
$away = self::get();
if ( '' === $away['until'] ) {
return;
}
$active = self::active();
$back = ! $active && $away['until'] < wp_date( 'Y-m-d' ) && $away['until'] >= wp_date( 'Y-m-d', time() - self::SHOW_DAYS * DAY_IN_SECONDS );
if ( ! $active && ! $back ) {
return;
}
$leads = self::waiting();
$currency = (string) Settings::get( 'currency' );
?>