send( (string) aioseo404To301()->options->telegram->botToken, (string) aioseo404To301()->options->telegram->chatId, self::format( $item ) ); if ( $result['ok'] ) { // Stamp the last successful delivery and clear any stale // error so the settings box reflects a healthy integration. aioseo404To301()->internalOptions->internal->telegramLastSentAt = gmdate( 'Y-m-d H:i:s' ); aioseo404To301()->internalOptions->internal->telegramLastError = ''; } else { aioseo404To301()->internalOptions->internal->telegramLastError = $result['error']; } } /** * Render an event payload into an HTML-formatted Telegram message. * * Uses Telegram's HTML parse mode, so every interpolated value is * escaped for the `&`, `<` and `>` entities Telegram recognises. * * @since 4.0.3 * * @param array $item Event payload. * * @return string */ private static function format( array $item ): string { $type = ( 'redirect' === ( $item['type'] ?? '' ) ) ? 'redirect' : '404'; $site = self::escape( (string) ( $item['site'] ?? '' ) ); if ( 'redirect' === $type ) { /* translators: %s: site name. */ $heading = sprintf( __( '↪️ Redirect served on %s', '404-to-301' ), $site ); } else { /* translators: %s: site name. */ $heading = sprintf( __( '⚠️ 404 detected on %s', '404-to-301' ), $site ); } $lines = [ $heading, '' ]; $lines[] = self::line( __( 'URL', '404-to-301' ), (string) ( $item['url'] ?? '' ) ); if ( 'redirect' === $type ) { $lines[] = self::line( __( 'Redirected to', '404-to-301' ), (string) ( $item['target'] ?? '' ) ); $lines[] = self::line( __( 'Status', '404-to-301' ), (string) ( $item['status'] ?? '' ) ); } else { $lines[] = self::line( __( 'Hits', '404-to-301' ), (string) ( $item['hits'] ?? 1 ) ); } $lines[] = self::line( __( 'Referer', '404-to-301' ), (string) ( $item['referer'] ?? '' ) ); $lines[] = self::line( __( 'IP', '404-to-301' ), (string) ( $item['ip'] ?? '' ) ); $lines[] = self::line( __( 'User-Agent', '404-to-301' ), (string) ( $item['ua'] ?? '' ) ); $lines[] = self::line( __( 'Method', '404-to-301' ), (string) ( $item['method'] ?? '' ) ); $lines[] = self::line( __( 'Time', '404-to-301' ), (string) ( $item['time'] ?? '' ) ); // Drop empty rows so a missing referer / IP doesn't leave a // dangling "Referer:" label in the message. $lines = array_filter( $lines, static function ( $line ) { return '' !== $line; } ); return implode( "\n", $lines ); } /** * Build a single `Label: value` line, escaped for HTML parse mode. * * Returns an empty string when the value is empty so the caller can * filter the row out entirely. * * @since 4.0.3 * * @param string $label Field label (already translated, no markup). * @param string $value Field value. * * @return string */ private static function line( string $label, string $value ): string { $value = trim( $value ); if ( '' === $value ) { return ''; } return '' . self::escape( $label ) . ': ' . self::escape( $value ); } /** * Escape a value for Telegram's HTML parse mode. * * Telegram only requires `&`, `<` and `>` to be encoded; quotes and * other characters are passed through literally. * * @since 4.0.3 * * @param string $value Raw value. * * @return string */ private static function escape( string $value ): string { return str_replace( [ '&', '<', '>' ], [ '&', '<', '>' ], $value ); } }