$update Update object. * @return string What happened (for tests and the log). */ public static function handle( array $update ) { if ( empty( $update['message'] ) || ! is_array( $update['message'] ) ) { return 'ignored'; } $message = $update['message']; if ( empty( $message['chat']['id'] ) || ( isset( $message['chat']['type'] ) && 'private' !== $message['chat']['type'] ) ) { return 'ignored'; } $chat = (string) $message['chat']['id']; $text = isset( $message['text'] ) ? trim( (string) $message['text'] ) : ''; $name = trim( ( isset( $message['from']['first_name'] ) ? (string) $message['from']['first_name'] : '' ) . ' ' . ( isset( $message['from']['last_name'] ) ? (string) $message['from']['last_name'] : '' ) ); if ( 1 === preg_match( '/^\/start(?:@\w+)?(?:\s+(\S+))?$/', $text, $m ) ) { return self::start( $chat, isset( $m[1] ) ? $m[1] : '', $name ); } if ( 1 === preg_match( '/^\/stop(?:@\w+)?$/', $text ) ) { return self::stop( $chat ); } if ( '' === $text || '/' === $text[0] ) { return 'ignored'; } return self::customer_text( $chat, $text, $name ); } /** * /start [payload]. * * @param string $chat Chat. * @param string $payload Payload. * @param string $name Sender's name. * @return string */ private static function start( $chat, $payload, $name ) { $shop = wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ); if ( '' === $payload ) { /* translators: %s: shop name */ self::reply( $chat, sprintf( __( 'Hi! This is the order-updates bot of %s. To get updates about an order, open the Telegram link on your order confirmation page or in your order email.', 'orderchirp' ), $shop ) ); return 'start:help'; } $parsed = Link_Token::parse( $payload ); if ( $parsed && 'owner' === $parsed['type'] ) { return self::link_owner( $chat, $parsed['code'], $name, $shop ); } if ( $parsed && 'order' === $parsed['type'] ) { $order = wc_get_order( $parsed['order_id'] ); if ( $order instanceof \WC_Order && Link_Token::verify_order( $order->get_id(), $order->get_order_key(), $parsed['mac'], Settings::secret() ) ) { Subscribers::link_order( $order, $chat ); Message_Log::add( array( 'order_id' => $order->get_id(), 'direction' => 'in', 'event' => 'customer:subscribed', 'chat_id' => $chat, 'body' => '/start', 'status' => 'received', ) ); $order->add_order_note( __( 'The customer turned on order updates on Telegram (Orderchirp).', 'orderchirp' ) ); self::reply( $chat, sprintf( /* translators: 1: shop name, 2: order number, 3: order status. */ __( "You're connected ✅\n%1\$s will message you here about order #%2\$s.\nCurrent status: %3\$s\n\nSend /stop at any time to stop these messages.", 'orderchirp' ), $shop, $order->get_order_number(), wc_get_order_status_name( $order->get_status() ) ) ); return 'start:order'; } } self::reply( $chat, __( 'This link is not valid any more. Please open the Telegram link on your order page again.', 'orderchirp' ) ); return 'start:invalid'; } /** * Add the shop owner's chat from a one-time invite. * * @param string $chat Chat. * @param string $code Invite code. * @param string $name Sender's name. * @param string $shop Shop name. * @return string */ private static function link_owner( $chat, $code, $name, $shop ) { $invite = get_option( self::INVITE_OPTION ); $valid = is_array( $invite ) && isset( $invite['hash'], $invite['expires'] ) && (int) $invite['expires'] >= time() && hash_equals( (string) $invite['hash'], hash( 'sha256', $code ) ); if ( ! $valid ) { self::reply( $chat, __( 'This invite has expired. Create a new one on the Orderchirp settings page.', 'orderchirp' ) ); return 'start:owner-invalid'; } delete_option( self::INVITE_OPTION ); Subscribers::add_owner( $chat, '' !== $name ? $name : $chat ); /* translators: %s: shop name */ self::reply( $chat, sprintf( __( 'Done ✅ This chat will now get Orderchirp alerts for %s: new orders, low stock and customer replies.', 'orderchirp' ), $shop ) ); return 'start:owner'; } /** * Create a one-time owner invite and return its payload. * * @return string Payload for t.me/{bot}?start= */ public static function new_owner_invite() { $code = Link_Token::new_owner_code(); update_option( self::INVITE_OPTION, array( 'hash' => hash( 'sha256', $code ), 'expires' => time() + self::INVITE_TTL, ), false ); return 'a' . $code; } /** * /stop. * * @param string $chat Chat. * @return string */ private static function stop( $chat ) { Subscribers::unlink_chat( $chat ); $shop = wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ); /* translators: %s: shop name */ self::reply( $chat, sprintf( __( "Done. You won't get any more messages from %s here.", 'orderchirp' ), $shop ) ); return 'stop'; } /** * Free text from a customer: note on their newest order, copy to the owner. * * @param string $chat Chat. * @param string $text Text. * @param string $name Sender's name. * @return string */ private static function customer_text( $chat, $text, $name ) { if ( Subscribers::is_owner( $chat ) ) { return 'ignored:owner'; } $orders = Subscribers::orders_of_chat( $chat, 1 ); if ( ! $orders ) { return 'ignored:unknown'; } $order = $orders[0]; $text = Template::truncate( wp_strip_all_tags( $text ) ); /* translators: %s: message text */ $order->add_order_note( sprintf( __( 'Customer message on Telegram: %s', 'orderchirp' ), $text ) ); Message_Log::add( array( 'order_id' => $order->get_id(), 'direction' => 'in', 'event' => 'customer:message', 'chat_id' => $chat, 'body' => $text, 'status' => 'received', ) ); /** * A customer wrote to the bot about an order. * * @param \WC_Order $order Order. * @param string $text Text. * @param string $chat Chat id. */ do_action( 'orderchirp_customer_message', $order, $text, $chat ); if ( Settings::get( 'owner_replies' ) ) { foreach ( array_keys( Subscribers::owners() ) as $owner ) { Dispatcher::queue( array( 'order_id' => $order->get_id(), 'recipient' => 'owner', 'event' => 'owner:customer_message', 'chat_id' => (string) $owner, 'body' => sprintf( /* translators: 1: customer name, 2: order number, 3: message */ __( "💬 %1\$s about order #%2\$s:\n%3\$s", 'orderchirp' ), '' !== $name ? $name : __( 'A customer', 'orderchirp' ), $order->get_order_number(), $text ), 'link' => $order->get_edit_order_url(), ) ); } } return 'customer:message'; } /** * Answer right away (replies to the person who just wrote are not queued). * * @param string $chat Chat. * @param string $text Text. */ private static function reply( $chat, $text ) { /** * Whether to send direct replies. Tests switch this off. * * @param bool $send Default true. */ if ( ! apply_filters( 'orderchirp_send_replies', true ) || ! Settings::connected() ) { return; } Telegram_Api::from_settings()->send_message( $chat, $text ); } }