افزونه مینی سئو

مشخصات مقاله:
بروزرسانی: شهریور ۱۴۰۴
افزونه مینی سئو
فهرست مطالب

افزونه مینی سئو

با افزونه مکمل سئو «مینی سئو»، تحلیل‌های هوشمند و پیشنهادهای کاربردی را مستقیماً در صفحه ویرایش نوشته خود دریافت کنید و با اطمینان محتوایی تولید کنید که گوگل عاشق آن می‌شود.

دستیار هوشمند و مکمل برای افزونه‌های Rank Math و Yoast.

  • شکارچی جایگاه ویژه گوگل 🎯

    با تحلیلگر AEO، مستقیم به بخش پاسخ‌های ویژه (Featured Snippets) بروید.
  • پادشاه نتایج محلی شوید 👑

    تحلیلگر GEO برای جذب مشتریان از شهر و منطقه خودتان.
  • دو متخصص در یک افزونه ✌️

    بهینه‌سازی همزمان برای پاسخ‌گویی (AEO) و جستجوی محلی (GEO).
  • چک‌لیست کاربردی، نه تئوری 📝

    پیشنهادهای عملی و دقیق برای رفع سریع ایرادات سئوی محتوا.
  • امتیازدهی آنی و هوشمند 📊

  • سازگار با ووکامرس 🛒
مینی سئو
/**
 * Plugin Name:       مینی سئو
 * Plugin URI:        https:/mrbarati.com/
 * Description:       تحلیلگر Aio و GEO، سازگار با Rank Math و Yoast SEO. مناسب برای نوشته، برگه و ووکامرس.
 * Version:           1.0.0
 * Author:            براتی طراح سایت
 * Author URI:        https://mrbarati.com/
 * License:           GPL-2.0-or-later
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * اگر کلاس از قبل وجود دارد، دوباره تعریف نکن — مناسب برای WP Code / snippets
 */
if ( ! class_exists( 'Mini_SEO_Analyzor' ) ) :

final class Mini_SEO_Analyzor {

    /**
     * Singleton instance
     * @var Mini_SEO_Analyzor|null
     */
    private static $instance = null;

    /**
     * دریافت نمونه
     */
    public static function instance() {
        if ( self::$instance === null ) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * سازنده: ثبت هوک‌ها
     */
    private function __construct() {
        add_action( 'add_meta_boxes', [ $this, 'register_meta_box' ] );
        add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
        add_action( 'wp_ajax_mini_seo_analyzor_run_analysis', [ $this, 'ajax_run_analysis' ] );
        add_action( 'wp_ajax_mini_seo_analyzor_toggle_temp_disable', [ $this, 'ajax_toggle_temp_disable' ] );
        add_action( 'save_post', [ $this, 'save_meta_box' ], 10, 2 );
        add_action( 'admin_notices', [ $this, 'check_seo_plugins_notice' ] );
    }

    /* ========================================================
     * رندر متاباکس
     * ======================================================== */

    public function register_meta_box(): void {
        $post_types = [ 'post', 'page', 'product' ];
        add_meta_box(
            'mini-seo-analyzor-box',
            __( 'تحلیلگر Aio و GEO', 'mini-seo-analyzor' ),
            [ $this, 'render_meta_box' ],
            $post_types,
            'normal',
            'high'
        );
    }

    public function render_meta_box( $post ): void {
        $temp_disable = get_post_meta( $post->ID, '_mini_seo_analyzor_temp_disable', true );
        $temp_disable = $temp_disable ? 1 : 0;
        $meta_nonce = wp_create_nonce( 'mini_seo_analyzor_meta_nonce' );
        $provinces_json = wp_json_encode( $this->get_provinces_list() );
        ?>
        <div class="mini-seo-analyzor-wrapper"
             data-post-id="<?php echo esc_attr( $post->ID ); ?>"
             data-temp-disable="<?php echo esc_attr( $temp_disable ); ?>"
             data-provinces="<?php echo esc_attr( $provinces_json ); ?>">
            <div class="mini-seo-tabs">
                <a href="#aio-panel" class="mini-seo-tab active" data-tab="aio-panel"><?php esc_html_e( 'تحلیل Aio', 'mini-seo-analyzor' ); ?></a>
                <a href="#geo-panel" class="mini-seo-tab" data-tab="geo-panel"><?php esc_html_e( 'تحلیل GEO', 'mini-seo-analyzor' ); ?></a>
                <a href="#aio-tutorial-panel" class="mini-seo-tab" data-tab="aio-tutorial-panel"><?php esc_html_e( 'راهنمای Aio', 'mini-seo-analyzor' ); ?></a>
                <a href="#geo-tutorial-panel" class="mini-seo-tab" data-tab="geo-tutorial-panel"><?php esc_html_e( 'راهنمای GEO', 'mini-seo-analyzor' ); ?></a>
            </div>

            <div id="aio-panel" class="mini-seo-panel active">
                <div class="analysis-results"><div class="loading-message"><?php esc_html_e( 'برای شروع تحلیل، محتوا را ویرایش کنید...', 'mini-seo-analyzor' ); ?></div></div>
            </div>

            <div id="geo-panel" class="mini-seo-panel">
                <div class="analysis-results"><div class="loading-message"><?php esc_html_e( 'در حال بارگذاری...', 'mini-seo-analyzor' ); ?></div></div>
            </div>

            <div id="aio-tutorial-panel" class="mini-seo-panel mini-seo-tutorial">
                <?php echo $this->get_aio_tutorial_content(); ?>
            </div>

            <div id="geo-tutorial-panel" class="mini-seo-panel mini-seo-tutorial">
                <?php echo $this->get_geo_tutorial_content(); ?>
            </div>

            <div class="mini-seo-controls" style="margin-top:12px;padding:12px;border:1px solid #e6e6e6;background:#fff;display:flex;align-items:center;gap:12px;">
                <label style="display:flex;align-items:center;gap:10px;cursor:pointer;margin:0;">
                    <input type="checkbox" id="mini_seo_analyzor_temp_disable" <?php checked( $temp_disable, 1 ); ?> />
                    <span style="font-weight:600;"><?php esc_html_e( 'غیر فعال سازی موقت', 'mini-seo-analyzor' ); ?></span>
                </label>

                <span id="mini_seo_analyzor_status_badge" style="display:inline-block;padding:6px 10px;border-radius:14px;font-weight:700;color:#fff;font-size:13px;">
                    <?php if ( $temp_disable ) : ?>
                        <span style="background:#c82333;padding:6px 10px;border-radius:14px;display:inline-block;"><?php esc_html_e( 'غیرفعال', 'mini-seo-analyzor' ); ?></span>
                    <?php else : ?>
                        <span style="background:#198754;padding:6px 10px;border-radius:14px;display:inline-block;"><?php esc_html_e( 'فعال', 'mini-seo-analyzor' ); ?></span>
                    <?php endif; ?>
                </span>

                <span style="color:#666;font-size:13px;margin-left:auto;"><?php esc_html_e( '(اگر تیک زده شود، آنالیز خودکار متوقف می‌شود)', 'mini-seo-analyzor' ); ?></span>

                <input type="hidden" id="mini_seo_analyzor_meta_nonce" value="<?php echo esc_attr( $meta_nonce ); ?>" />
            </div>
        </div>
        <?php
    }

    /* ========================================================
     * enqueue CSS/JS
     * ======================================================== */

    public function enqueue_assets( string $hook ): void {
        $screen = function_exists('get_current_screen') ? get_current_screen() : null;
        if ( ! $screen || ! in_array( $screen->post_type ?? '', [ 'post', 'page', 'product' ], true ) || ! in_array( $hook, [ 'post.php', 'post-new.php' ], true ) ) {
            return;
        }

        $css = ".mini-seo-tabs{display:flex;flex-wrap:wrap;border-bottom:2px solid #007cba;margin-bottom:0}.mini-seo-tab{padding:12px 18px;cursor:pointer;background:#f0f0f1;border:1px solid #ccc;border-bottom:none;margin-left:5px;font-weight:600;border-radius:8px 8px 0 0;position:relative;top:2px;transition:all .2s ease-in-out;text-decoration:none;color:#3c434a}.mini-seo-tab:hover{background:#e0e0e0}.mini-seo-tab.active{background:#007cba;color:#fff;border-color:#007cba}.mini-seo-panel{display:none;padding:25px;border:1px solid #ccc;border-top:none;background:#fff;clear:both}.mini-seo-panel.active{display:block}.mini-seo-panel .loading-message{padding:40px;text-align:center;color:#50575e;font-size:14px}.mini-seo-progress-bar{background-color:#e9ecef;border-radius:5px;overflow:hidden;margin:10px 0 25px;height:22px;direction:ltr;border:1px solid #ddd}.mini-seo-progress-bar-fill{height:100%;text-align:center;color:#fff;font-weight:700;font-size:13px;line-height:22px;transition:width .5s ease-in-out}.mini-seo-checklist{list-style:none;padding:0;margin:0}.mini-seo-checklist li{margin-bottom:14px;font-size:14px;padding-right:15px;border-right:4px solid;line-height:1.6}.mini-seo-checklist li.status-pass{border-color:#28a745}.mini-seo-checklist li.status-fail{border-color:#dc3545}.mini-seo-suggestion{font-size:13px;color:#50575e;margin:-8px 19px 15px 0;background:#f8f9fa;padding:10px;border-radius:4px;border:1px solid #e9ecef}.mini-seo-suggestion a{font-weight:700;text-decoration:none}.mini-seo-tutorial h4{font-size:16px;font-weight:700;border-bottom:2px solid #007cba;padding-bottom:5px;margin-top:25px;margin-bottom:15px}.mini-seo-tutorial p,.mini-seo-tutorial ul li{font-size:14px;line-height:1.8;color:#333}.mini-seo-tutorial code{background:#fffdf7;padding:3px 6px;border-radius:4px;font-size:15px;color:#b50000;border:1px solid #eee8d5;font-family:monospace}.mini-seo-tutorial strong{color:#005a87}";

        wp_register_style( 'mini-seo-analyzor-style', false, [], '1.2.0' );
        wp_enqueue_style( 'mini-seo-analyzor-style' );
        wp_add_inline_style( 'mini-seo-analyzor-style', $css );

        wp_register_script( 'mini-seo-analyzor-script', false, [ 'jquery' ], '1.2.0', true );
        wp_enqueue_script( 'mini-seo-analyzor-script' );

        $vars = [
            'ajax_url' => admin_url( 'admin-ajax.php' ),
            'ajax_nonce' => wp_create_nonce( 'mini_seo_analyzor_ajax_nonce' ),
        ];
        wp_localize_script( 'mini-seo-analyzor-script', 'mini_seo_analyzor_vars', $vars );

        $js = <<<JS
jQuery(document).ready(function($) {
    $('.mini-seo-tab').on('click', function(e) {
        e.preventDefault();
        const tabId = $(this).data('tab');
        $('.mini-seo-tab, .mini-seo-panel').removeClass('active');
        $(this).addClass('active');
        $('#' + tabId).addClass('active');
    });

    let analysisTimeout = null;
    const wrapper = $('.mini-seo-analyzor-wrapper');
    const postId = parseInt(wrapper.data('post-id')) || 0;
    let tempDisable = wrapper.data('temp-disable') === 1 || wrapper.data('temp-disable') === '1';

    function updateStatusBadge(state) {
        const badgeWrap = $('#mini_seo_analyzor_status_badge');
        badgeWrap.empty();
        if ( state ) {
            badgeWrap.append('<span style="background:#c82333;padding:6px 10px;border-radius:14px;display:inline-block;color:#fff;">غیرفعال</span>');
        } else {
            badgeWrap.append('<span style="background:#198754;padding:6px 10px;border-radius:14px;display:inline-block;color:#fff;">فعال</span>');
        }
    }

    function setTempDisableUI(state, options = {}) {
        tempDisable = !!state;
        wrapper.attr('data-temp-disable', tempDisable ? '1' : '0');
        $('#mini_seo_analyzor_temp_disable').prop('checked', tempDisable);
        updateStatusBadge(tempDisable);
        if (tempDisable) {
            $('#aio-panel .analysis-results, #geo-panel .analysis-results').html('<div class="loading-message">تحلیل موقتاً غیرفعال است.</div>');
        } else {
            if ( ! options.suppressPerform ) {
                performAnalysis();
            }
        }
    }

    updateStatusBadge(tempDisable);

    function collectEditorData() {
        let postTitle = '', postContent = '', metaDesc = '', focusKeyword = '';
        if (typeof wp !== 'undefined' && wp.data && wp.data.select) {
            try {
                const editor = wp.data.select('core/editor');
                if ( editor && editor.getEditedPostAttribute ) {
                    postTitle = editor.getEditedPostAttribute('title') || '';
                    postContent = editor.getEditedPostAttribute('content') || '';
                }
                if ( wp.data.select('rank-math-analyzer') ) {
                    const rankMathData = wp.data.select('rank-math-analyzer');
                    metaDesc = rankMathData.getMetaDescription ? rankMathData.getMetaDescription() : metaDesc;
                    try { focusKeyword = rankMathData.getKeyword('primary')?.value || focusKeyword; } catch(e){}
                } else if ( wp.data.select('yoast-seo/editor') ) {
                    const yoast = wp.data.select('yoast-seo/editor');
                    metaDesc = yoast.getSeoMeta ? (yoast.getSeoMeta()?.metadesc || '') : metaDesc;
                    focusKeyword = yoast.getSeoMeta ? (yoast.getSeoMeta()?.focuskw || '') : focusKeyword;
                }
            } catch (ex) {}
        }
        if (!postTitle) postTitle = $('#title').val() || '';
        if (!postContent) postContent = (typeof tinymce !== 'undefined' && tinymce.get('content')) ? tinymce.get('content').getContent() : $('#content').val() || '';
        if (!metaDesc) metaDesc = $('#yoast_wpseo_metadesc').val() || $('textarea[name="rank_math_description"]').val() || '';
        if (!focusKeyword) focusKeyword = $('#yoast_wpseo_focuskw').val() || $('input[name="rank_math_focus_keyword"]').val() || '';
        return { postTitle, postContent, metaDesc, focusKeyword };
    }

    function performAnalysis() {
        if ( tempDisable ) return;
        const loadingHtml = '<div class="loading-message">در حال تحلیل... لطفاً صبر کنید.</div>';
        $('#aio-panel .analysis-results, #geo-panel .analysis-results').html(loadingHtml);
        if (!postId) return;
        const data = collectEditorData();
        $.post(mini_seo_analyzor_vars.ajax_url, {
            action: 'mini_seo_analyzor_run_analysis',
            nonce: mini_seo_analyzor_vars.ajax_nonce,
            post_id: postId,
            title: data.postTitle,
            content: data.postContent,
            meta_description: data.metaDesc,
            focus_keyword: data.focusKeyword
        }, function(response) {
            if ( response && response.success ) {
                $('#aio-panel .analysis-results').html(response.data.aio_html);
                $('#geo-panel .analysis-results').html(response.data.geo_html);
            } else {
                const err = (response && response.data && response.data.message) ? response.data.message : 'یک خطای نامشخص رخ داد.';
                $('#aio-panel .analysis-results, #geo-panel .analysis-results').html('<div class="loading-message" style="color:red;">' + err + '</div>');
            }
        }).fail(function() {
            $('#aio-panel .analysis-results, #geo-panel .analysis-results').html('<div class="loading-message" style="color:red;">خطا در ارتباط با سرور.</div>');
        });
    }

    const debounceAnalysis = () => { clearTimeout(analysisTimeout); analysisTimeout = setTimeout(performAnalysis, 1500); };

    if ( typeof wp !== 'undefined' && wp.data && wp.data.subscribe ) {
        wp.data.subscribe(debounceAnalysis);
    } else {
        $('#title, #content').on('input keyup', debounceAnalysis);
        if ( typeof tinymce !== 'undefined' && tinymce.get('content') ) {
            tinymce.get('content').on('keyup input', debounceAnalysis);
        }
    }

    setTimeout(function() {
        if ( ! tempDisable ) performAnalysis();
    }, ۲۵۰۰);

    $('#mini_seo_analyzor_temp_disable').on('change', function() {
        const checked = $(this).is(':checked') ? 1 : 0;
        const prev = tempDisable ? 1 : 0;
        setTempDisableUI(checked, { suppressPerform: true });
        $('#mini_seo_analyzor_temp_disable').prop('disabled', true);
        $.post(mini_seo_analyzor_vars.ajax_url, {
            action: 'mini_seo_analyzor_toggle_temp_disable',
            nonce: $('#mini_seo_analyzor_meta_nonce').val(),
            post_id: postId,
            value: checked
        }, function(res) {
            if ( res && res.success && typeof res.data.value !== 'undefined' ) {
                const saved = parseInt(res.data.value, 10) ? 1 : 0;
                setTempDisableUI(saved, { suppressPerform: true });
                if ( saved === 0 ) {
                    performAnalysis();
                }
            } else {
                setTempDisableUI(prev, { suppressPerform: true });
                alert('خطا در دریافت پاسخ سرور. وضعیت بازگردانده شد.');
            }
        }).fail(function() {
            setTempDisableUI(prev, { suppressPerform: true });
            alert('خطا در ذخیره‌سازی وضعیت. دسترسی یا nonce بررسی شود.');
        }).always(function() {
            $('#mini_seo_analyzor_temp_disable').prop('disabled', false);
        });
    });
});
JS;
        wp_add_inline_script( 'mini-seo-analyzor-script', $js );
    }

    /* ========================================================
     * AJAX handlers
     * ======================================================== */

    public function ajax_run_analysis(): void {
        if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'mini_seo_analyzor_ajax_nonce' ) ) {
            wp_send_json_error( [ 'message' => 'Nonce نامعتبر.' ], ۴۰۰ );
        }

        $post_id = isset( $_POST['post_id'] ) ? absint( wp_unslash( $_POST['post_id'] ) ) : 0;
        if ( ! $post_id || ! current_user_can( 'edit_post', $post_id ) ) {
            wp_send_json_error( [ 'message' => 'دسترسی غیرمجاز.' ], ۴۰۳ );
        }

        $title          = isset( $_POST['title'] ) ? sanitize_text_field( wp_unslash( $_POST['title'] ) ) : '';
        $content_html   = isset( $_POST['content'] ) ? wp_kses_post( wp_unslash( $_POST['content'] ) ) : '';
        $meta_desc      = isset( $_POST['meta_description'] ) ? sanitize_text_field( wp_unslash( $_POST['meta_description'] ) ) : '';
        $focus_keyword  = isset( $_POST['focus_keyword'] ) ? sanitize_text_field( wp_unslash( $_POST['focus_keyword'] ) ) : '';
        $content_text   = wp_strip_all_tags( $content_html );

        if ( empty( $focus_keyword ) ) $focus_keyword = $this->get_focus_keyword_from_meta( $post_id );
        if ( empty( $meta_desc ) ) $meta_desc = $this->get_meta_description_from_meta( $post_id );

        if ( empty( $focus_keyword ) ) {
            wp_send_json_error( [ 'message' => 'کلمه کلیدی اصلی تنظیم نشده است! لطفاً ابتدا کلمه کلیدی را در باکس افزونه Rank Math یا Yoast SEO وارد کنید.' ] );
        }

        if ( get_post_meta( $post_id, '_mini_seo_analyzor_temp_disable', true ) ) {
            wp_send_json_error( [ 'message' => 'تحلیل به صورت موقت توسط کاربر غیرفعال شده است.' ] );
        }

        $schema_info = $this->get_active_schema_info( $post_id );
        $aio_html = $this->analyze_aio( $post_id, $title, $content_html, $content_text, $meta_desc, $focus_keyword, $schema_info );
        $geo_html = $this->analyze_geo( $post_id, $content_html, $content_text, $title, $meta_desc, $schema_info );

        wp_send_json_success( [ 'aio_html' => $aio_html, 'geo_html' => $geo_html ] );
    }

    public function ajax_toggle_temp_disable(): void {
        if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'mini_seo_analyzor_meta_nonce' ) ) {
            wp_send_json_error( [ 'message' => 'Nonce نامعتبر.' ], ۴۰۰ );
        }
        $post_id = isset( $_POST['post_id'] ) ? absint( wp_unslash( $_POST['post_id'] ) ) : 0;
        if ( ! $post_id || ! current_user_can( 'edit_post', $post_id ) ) {
            wp_send_json_error( [ 'message' => 'دسترسی غیرمجاز.' ], ۴۰۳ );
        }
        $value = isset( $_POST['value'] ) ? intval( wp_unslash( $_POST['value'] ) ) : 0;
        $value = $value ? 1 : 0;
        if ( $value ) {
            update_post_meta( $post_id, '_mini_seo_analyzor_temp_disable', 1 );
        } else {
            delete_post_meta( $post_id, '_mini_seo_analyzor_temp_disable' );
        }
        if ( function_exists( 'clean_post_cache' ) ) {
            clean_post_cache( $post_id );
        }
        wp_send_json_success( [ 'saved' => true, 'value' => $value ] );
    }

    /* ========================================================
     * ذخیره هنگام save_post (fallback)
     * ======================================================== */

    public function save_meta_box( $post_id, $post ) {
        if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;
        if ( ! current_user_can( 'edit_post', $post_id ) ) return;
        if ( isset( $_POST['mini_seo_analyzor_meta_nonce'] ) ) {
            $nonce = sanitize_text_field( wp_unslash( $_POST['mini_seo_analyzor_meta_nonce'] ) );
            if ( ! wp_verify_nonce( $nonce, 'mini_seo_analyzor_meta_nonce' ) ) return;
        }
        if ( isset( $_POST['mini_seo_analyzor_temp_disable'] ) ) {
            $val = sanitize_text_field( wp_unslash( $_POST['mini_seo_analyzor_temp_disable'] ) );
            if ( $val ) {
                update_post_meta( $post_id, '_mini_seo_analyzor_temp_disable', 1 );
            } else {
                delete_post_meta( $post_id, '_mini_seo_analyzor_temp_disable' );
            }
        }
    }

    /* ========================================================
     * نوتیفیکیشن اگر Rank Math و Yoast نصب نبودند
     * ======================================================== */

    public function check_seo_plugins_notice() {
        if ( ! is_admin() || ! current_user_can( 'edit_posts' ) ) return;
        $screen = function_exists('get_current_screen') ? get_current_screen() : null;
        if ( ! $screen || ! in_array( $screen->base ?? '', [ 'post', 'post-new' ], true ) ) return;
        if ( ! function_exists( 'is_plugin_active' ) ) {
            include_once ABSPATH . 'wp-admin/includes/plugin.php';
        }
        if ( ! is_plugin_active( 'seo-by-rank-math/rank-math.php' ) && ! is_plugin_active( 'wordpress-seo/wp-seo.php' ) ) {
            echo '<div class="notice notice-warning is-dismissible"><p>⚠️ هیچ پلاگین سئوی اصلی (Rank Math یا Yoast SEO) فعال نیست. برخی از داده‌های آنالیز Aio/GEO ممکن است کامل نباشند.</p></div>';
        }
    }

    /* ========================================================
     * توابع تحلیل (Aio / GEO)
     * ======================================================== */

    private function analyze_aio( int $post_id, string $title, string $content_html, string $content_text, string $meta_desc, string $keyword, array $schema_info ): string {
        $score = 0;
        $checks = [];

        // کلمه کلیدی در عنوان: ۱۵ امتیاز
        if ( $this->keyword_exists_as_whole_word( $title, $keyword ) ) $score += 15;
        $checks[] = ['label' => 'کلمه کلیدی در عنوان', 'status' => $this->keyword_exists_as_whole_word( $title, $keyword ), 'suggestion' => 'کلمه کلیدی اصلی باید در عنوان صفحه وجود داشته باشد. بهتر است در ابتدای عنوان بیاید.'];

        // کلمه کلیدی در توضیحات متا: ۱۰ امتیاز
        if ( $this->keyword_exists_as_whole_word( $meta_desc, $keyword ) ) $score += 10;
        $checks[] = ['label' => 'کلمه کلیدی در توضیحات متا', 'status' => $this->keyword_exists_as_whole_word( $meta_desc, $keyword ), 'suggestion' => 'استفاده از کلمه کلیدی در توضیحات متا (Meta Description) برای افزایش نرخ کلیک در نتایج گوگل ضروری است.'];

        // وضعیت ایندکس: ۱۵ امتیاز
        $is_indexable = $this->is_post_indexable( $post_id );
        if ( $is_indexable ) $score += 15;
        $checks[] = ['label' => 'وضعیت ایندکس: ' . ( $is_indexable ? 'ایندکس (Index)' : 'نوایندکس (NoIndex)' ), 'status' => $is_indexable, 'suggestion' => 'برای دیده شدن در گوگل، این صفحه باید قابل ایندکس باشد. تیک No Index را از تنظیمات سئو بردارید.'];

        // استفاده از H2 با کلمه کلیدی: ۱۰ امتیاز
        $has_h2_with_kw = (bool) preg_match( '/<h2[^>]*>.*' . preg_quote( $keyword, '/' ) . '.*<\/h2>/isu', $content_html );
        if ( $has_h2_with_kw ) $score += 10;
        $checks[] = ['label' => 'استفاده از تگ H2 با کلمه کلیدی', 'status' => $has_h2_with_kw, 'suggestion' => 'کلمه کلیدی اصلی را در حداقل یکی از زیرعنوان‌های H2 به کار ببرید تا ساختار محتوا برای گوگل واضح‌تر شود.'];
        
        // پیوندها: مجموعاً ۲۰ امتیاز (۱۰ برای داخلی، ۱۰ برای خارجی)
        $link_counts = $this->analyze_links_in_content( $content_html );
        $total_links = $link_counts['internal'] + $link_counts['external'];
        if ( $link_counts['internal'] > 0 ) $score += 10;
        if ( $link_counts['external'] > 0 ) $score += 10;
        
        $link_status = $total_links > 0;
        $link_label = "پیوندها: {$total_links} | خارجی: {$link_counts['external']} | داخلی: {$link_counts['internal']}";
        $suggestion = '';
        
        if ( ! $link_status ) { // فقط اگر هیچ لینکی نباشد، این پیشنهاد نمایش داده می‌شود
            $suggestion = 'محتوای شما هیچ پیوندی ندارد. برای بهبود سئو، به صفحات دیگر سایت خود (لینک داخلی) و به سایت‌های معتبر خارجی (لینک خارجی) ارجاع دهید.';
        } elseif ( $link_counts['internal'] === 0 ) { // اگر لینک خارجی هست ولی داخلی نیست
            $suggestion = 'هیچ لینک داخلی یافت نشد. با لینک دادن به سایر نوشته‌های مرتبط در سایت خود، به کاربران و گوگل در یافتن محتوای بیشتر کمک کنید.';
        } elseif ( $link_counts['external'] === 0 ) { // اگر لینک داخلی هست ولی خارجی نیست
            $suggestion = 'هیچ لینک خارجی یافت نشد. ارجاع دادن به منابع معتبر خارجی می‌تواند اعتبار محتوای شما را نزد گوگل افزایش دهد.';
        }
        
        $checks[] = ['label' => $link_label, 'status' => $link_status, 'suggestion' => $suggestion];

        // کلمه کلیدی در محتوا: ۱۰ امتیاز
        $keyword_count = $this->count_keyword_as_whole_word( $content_text, $keyword );
        if ( $keyword_count > 0 ) $score += 10;
        $checks[] = ['label' => "کلمه کلیدی در محتوا (تعداد: $keyword_count)", 'status' => $keyword_count > 0, 'suggestion' => 'حداقل ۱ بار از کلمه کلیدی در پاراگراف اول استفاده کنید. چگالی مناسب بین ۰.۵% تا ۱% است.'];

        // وجود اسکیما: ۱۰ امتیاز
        if ( $schema_info['found'] ) $score += 10;
        $checks[] = ['label' => 'وجود اسکیما (نوع: ' . esc_html($schema_info['type']) . ')', 'status' => $schema_info['found'], 'suggestion' => 'استفاده از نشانه‌گذاری اسکیمای ساختاریافته (مانند Article, FAQPage) به موتورهای جستجو کمک می‌کند محتوای شما را بهتر درک کنند.'];

        // کلمه کلیدی در متن Alt: 10 امتیاز
        $has_kw_in_alt = $this->check_keyword_in_image_alts( $post_id, $content_html, $keyword );
        if ( $has_kw_in_alt ) $score += 10;
        $checks[] = ['label' => 'کلمه کلیدی در متن Alt تصاویر', 'status' => $has_kw_in_alt, 'suggestion' => 'برای بهینه‌سازی تصاویر، کلمه کلیدی را در متن جایگزین (Alt) حداقل یکی از تصاویر محتوا یا تصویر شاخص به کار ببرید.'];

        // مجموع کل امتیازات: ۱۰۰
        return $this->render_progress_bar( 'امتیاز Aio', $score ) . $this->render_checklist( $checks );
    }

    private function analyze_geo( int $post_id, string $content_html, string $content_text, string $title, string $meta_desc, array $schema_info ): string {
        $score = 0;
        $checks = [];
        $cities_pattern = 'تهران|اصفهان|شیراز|مشهد|تبریز|کرج|اهواز|قم|کرمانشاه|ارومیه|رشت|ساری|یزد|کرمان';
        $full_text_for_city = $title . ' ' . $content_text . ' ' . $meta_desc;

        $has_city_in_content = (bool) preg_match( "/\b({$cities_pattern})\b/u", $full_text_for_city );
        if ($has_city_in_content) $score += 25;
        $checks[] = ['label' => 'ذکر نام شهر در محتوا یا عنوان', 'status' => $has_city_in_content, 'suggestion' => 'نام شهر هدف (مثلاً تهران) را در عنوان، توضیحات متا یا متن اصلی ذکر کنید.'];

        $local_seo_info = $this->get_local_seo_nap_info();
        $has_contact = !empty($local_seo_info['phone']) || !empty($local_seo_info['address']) || (bool) preg_match( '/(\d{8,11}|\b\d{3,4}-\d{7,8}\b)|(خیابان|بلوار|میدان|کوچه|پلاک)/u', $content_text );
        if ($has_contact) $score += 20;
        $checks[] = ['label' => 'وجود شماره تماس یا بخشی از آدرس', 'status' => $has_contact, 'suggestion' => 'اطلاعات تماس (تلفن) و آدرس را در تنظیمات Local SEO افزونه سئو یا در محتوای صفحه وارد کنید.'];

        $has_map = (bool) preg_match( '/<iframe[^>]+src=[\'\"][^\'\"]*google\.com\/(maps)/i', $content_html );
        if ($has_map) $score += 20;
        $checks[] = ['label' => 'جاسازی نقشه گوگل (Google Maps)', 'status' => $has_map, 'suggestion' => 'با قرار دادن نقشه گوگل در صفحه، موقعیت مکانی خود را به گوگل و کاربران به طور واضح نشان دهید.'];

        if ( $schema_info['is_local'] ) $score += 25;
        $checks[] = ['label' => 'استفاده از اسکیمای LocalBusiness', 'status' => $schema_info['is_local'], 'suggestion' => 'برای کسب و کارهای محلی، استفاده از اسکیمای LocalBusiness یا زیرمجموعه‌های آن برای نمایش در نتایج محلی گوگل حیاتی است.'];

        $has_city_in_alt = false;
        if (preg_match_all('/<img[^>]+alt=["\']([^"\']*)["\']/i', $content_html, $alt_matches)) {
            foreach ($alt_matches[1] as $alt_text) {
                if (preg_match("/\b({$cities_pattern})\b/u", $alt_text)) { $has_city_in_alt = true; break; }
            }
        }
        if($has_city_in_alt) $score += 10;
        $checks[] = ['label' => 'نام شهر در متن جایگزین (Alt) تصاویر', 'status' => $has_city_in_alt, 'suggestion' => 'نام شهر هدف را در متن جایگزین حداقل یکی از تصاویر صفحه به کار ببرید.'];

        return $this->render_progress_bar( 'امتیاز GEO', $score ) . $this->render_checklist( $checks );
    }

    /* ========================================================
     * توابع کمکی
     * ======================================================== */

    private function get_active_schema_info( int $post_id ): array {
        $result = [ 'found' => false, 'type' => 'یافت نشد', 'is_local' => false ];
        $found_types = [];

        if ( class_exists( '\RankMath' ) ) {
            $schemas = get_post_meta( $post_id, 'rank_math_schema', true );
            if ( ! empty( $schemas ) && is_array( $schemas ) ) {
                foreach ( array_keys( $schemas ) as $schema_type ) {
                    $found_types[] = ucfirst( $schema_type );
                }
            }
            if ( empty( $found_types ) && class_exists('\RankMath\Helper') ) {
                 $post_type = get_post_type( $post_id );
                 $default_schema = \RankMath\Helper::get_settings( "titles.pt_{$post_type}_default_schema_type" );
                 if ( ! empty( $default_schema ) ) {
                     $found_types[] = ucfirst($default_schema);
                 } elseif ( in_array($post_type, ['post', 'page'])) {
                     $found_types[] = 'Article';
                 } elseif ( $post_type === 'product' ) {
                     $found_types[] = 'Product';
                 }
            }
        }
        elseif ( function_exists( 'YoastSEO' ) ) {
            $page_type = get_post_meta( $post_id, '_yoast_wpseo_schema_page_type', true );
            $article_type = get_post_meta( $post_id, '_yoast_wpseo_schema_article_type', true );

            if ( ! empty( $article_type ) && $page_type === 'WebPage' ) {
                 $found_types[] = $page_type;
                 $found_types[] = $article_type;
            } elseif ( ! empty( $page_type ) ) {
                $found_types[] = $page_type;
            } elseif ( class_exists('WPSEO_Options') ){
                $post_type = get_post_type($post_id);
                $options_key = 'schema-page-type-' . $post_type;
                $default_type = \WPSEO_Options::get( $options_key, 'WebPage' );
                $found_types[] = $default_type;
            }
        }

        if ( ! empty( $found_types ) ) {
            $result['found'] = true;
            $unique_types = array_unique( array_filter( $found_types ) );
            $result['type'] = implode( ', ', $unique_types );
            foreach ( $unique_types as $type ) {
                if ( preg_match( '/(LocalBusiness|Restaurant|Store|ContactPage|AboutPage)/i', $type ) ) {
                    $result['is_local'] = true;
                    break;
                }
            }
        }

        return $result;
    }

    private function get_focus_keyword_from_meta( int $post_id ): string {
        $keyword = get_post_meta( $post_id, 'rank_math_focus_keyword', true );
        if ( ! empty( $keyword ) ) { $keywords = explode( ',', $keyword ); return trim( $keywords[0] ); }
        $keyword = get_post_meta( $post_id, '_yoast_wpseo_focuskw', true );
        if ( ! empty( $keyword ) ) { return trim( $keyword ); }
        return '';
    }

    private function get_meta_description_from_meta( int $post_id ): string {
        $desc = get_post_meta( $post_id, 'rank_math_description', true );
        if ( ! empty( $desc ) ) return trim( $desc );
        $desc = get_post_meta( $post_id, '_yoast_wpseo_metadesc', true );
        if ( ! empty( $desc ) ) return trim( $desc );
        return '';
    }

    private function keyword_exists_as_whole_word( string $haystack, string $needle ): bool {
        if ( empty( trim( $haystack ) ) || empty( trim( $needle ) ) ) return false;
        return preg_match( '/\b' . preg_quote( trim( $needle ), '/' ) . '\b/iu', $haystack ) === 1;
    }

    private function count_keyword_as_whole_word( string $haystack, string $needle ): int {
        if ( empty( trim( $haystack ) ) || empty( trim( $needle ) ) ) return 0;
        return preg_match_all( '/\b' . preg_quote( trim( $needle ), '/' ) . '\b/iu', $haystack );
    }

    private function is_post_indexable( int $post_id ): bool {
        if ( class_exists( 'RankMath\Post' ) ) {
            $robots = \RankMath\Post::get_meta( 'robots', $post_id );
            if ( is_array( $robots ) && in_array( 'noindex', $robots, true ) ) return false;
        }
        if ( get_post_meta( $post_id, '_yoast_wpseo_meta-robots-noindex', true ) === '1' ) return false;
        if ( '0' === get_option( 'blog_public' ) ) return false;
        return true;
    }

    private function analyze_links_in_content( string $html ): array {
        $counts = [ 'internal' => 0, 'external' => 0 ];
        if ( empty( $html ) ) return $counts;
        $site_host = wp_parse_url( home_url(), PHP_URL_HOST );
        if ( ! $site_host ) return $counts;
        $site_host = str_replace( 'www.', '', $site_host );
        if ( ! class_exists('DOMDocument') ) return $counts;
        libxml_use_internal_errors( true );
        $dom = new DOMDocument();
        @$dom->loadHTML( mb_convert_encoding( $html, 'HTML-ENTITIES', 'UTF-8' ) );
        libxml_clear_errors();
        foreach ( $dom->getElementsByTagName('a') as $link ) {
            $href = $link->getAttribute('href');
            if ( empty( $href ) || in_array( $href[0], [ '#', '?' ], true ) || strpos( $href, 'javascript:' ) === 0 || strpos($href, 'mailto:') === 0 ) continue;
            $host = wp_parse_url( $href, PHP_URL_HOST );
            if ( empty( $host ) || str_replace( 'www.', '', $host ) === $site_host ) {
                $counts['internal']++;
            } else {
                $counts['external']++;
            }
        }
        return $counts;
    }

    private function check_keyword_in_image_alts( int $post_id, string $content_html, string $keyword ): bool {
        if ( preg_match_all( '/<img[^>]+alt=["\']([^"\']*)["\']/i', $content_html, $matches ) ) {
            foreach ( $matches[1] as $alt_text ) {
                if ( ! empty( $alt_text ) && $this->keyword_exists_as_whole_word( $alt_text, $keyword ) ) {
                    return true;
                }
            }
        }
        if ( function_exists( 'has_post_thumbnail' ) && has_post_thumbnail( $post_id ) ) {
            $thumbnail_id = get_post_thumbnail_id( $post_id );
            $featured_image_alt = get_post_meta( $thumbnail_id, '_wp_attachment_image_alt', true );
            if ( ! empty( $featured_image_alt ) && $this->keyword_exists_as_whole_word( $featured_image_alt, $keyword ) ) {
                return true;
            }
        }
        return false;
    }

    private function get_local_seo_nap_info(): array {
        $nap = [ 'address' => '', 'phone' => '' ];
        if ( function_exists( 'rank_math_get_option' ) ) {
            $nap['address'] = rank_math_get_option( 'local_seo_address' );
            $nap['phone']   = rank_math_get_option( 'local_seo_phone' );
        }
        if ( empty( $nap['address'] ) && class_exists( 'WPSEO_Options' ) ) {
            if ( is_plugin_active( 'wpseo-local/wpseo-local.php' ) ) {
                $options        = get_option( 'wpseo_local' );
                $nap['address'] = $options['location_address'] ?? '';
                $nap['phone']   = $options['location_phone'] ?? '';
            }
        }
        return $nap;
    }
    
    private function get_provinces_list(): array {
        return [
            'آذربایجان شرقی' => 'تبریز', 'آذربایجان غربی' => 'ارومیه', 'اردبیل' => 'اردبیل', 'اصفهان' => 'اصفهان',
            'البرز' => 'کرج', 'ایلام' => 'ایلام', 'بوشهر' => 'بوشهر', 'تهران' => 'تهران',
            'چهارمحال و بختیاری' => 'شهرکرد', 'خراسان رضوی' => 'مشهد', 'خراسان جنوبی' => 'بیرجند', 'خراسان شمالی' => 'بجنورد',
            'خوزستان' => 'اهواز', 'زنجان' => 'زنجان', 'سمنان' => 'سمنان', 'سیستان و بلوچستان' => 'زاهدان',
            'فارس' => 'شیراز', 'قزوین' => 'قزوین', 'قم' => 'قم', 'کردستان' => 'سنندج',
            'کرمان' => 'کرمان', 'کرمانشاه' => 'کرمانشاه', 'کهگیلویه و بویراحمد' => 'یاسوج', 'گلستان' => 'گرگان',
            'گیلان' => 'رشت', 'لرستان' => 'خرم‌آباد', 'مازندران' => 'ساری', 'مرکزی' => 'اراک',
            'هرمزگان' => 'بندرعباس', 'همدان' => 'همدان', 'یزد' => 'یزد'
        ];
    }

    private function render_progress_bar( string $label, int $score ): string {
        $score = min( 100, max( 0, $score ) );
        $color = '#dc3545';
        if ( $score >= 80 ) $color = '#198754';
        elseif ( $score >= 50 ) $color = '#ffc107';
        $text_color = ( $score < 50 && $score > 0 ) ? '#000' : '#fff';
        return sprintf('<div><strong>%s: %d/100</strong></div><div class="mini-seo-progress-bar"><div class="mini-seo-progress-bar-fill" style="width:%d%%; background-color:%s; color:%s;">%d%%</div></div>', esc_html($label), $score, $score, esc_attr($color), esc_attr($text_color), $score);
    }

    private function render_checklist( array $checks ): string {
        $html = '<ul class="mini-seo-checklist">';
        foreach ( $checks as $check ) {
            $status_class = $check['status'] ? 'status-pass' : 'status-fail';
            $label_html = esc_html( $check['label'] );
            $html .= sprintf( '<li class="%s">%s</li>', esc_attr( $status_class ), $label_html );
            // پیشنهاد فقط در صورتی نمایش داده می‌شود که وضعیت Fail باشد و متنی برای پیشنهاد وجود داشته باشد
            if ( ! $check['status'] && ! empty( $check['suggestion'] ) ) {
                $suggestion_text = preg_replace('/(https?:\/\/[^\s]+)/', '<a href="$1" target="_blank" rel="noopener noreferrer">$1</a>', esc_html( $check['suggestion'] ));
                $html .= '<div class="mini-seo-suggestion">' . $suggestion_text . '</div>';
            }
        }
        $html .= '</ul>';
        return $html;
    }

    private function get_aio_tutorial_content(): string {
        return '<h4>راهنمای Aio (بهینه‌سازی برای موتور پاسخ‌گو)</h4><p>هدف <strong>Aio</strong>، ارائه پاسخی مستقیم، سریع و دقیق به سوالات کاربران است. با رعایت این موارد، شانس خود را برای نمایش در بخش پاسخ‌های ویژه گوگل (Featured Snippets) و کسب رتبه‌های بهتر افزایش می‌دهید.</p><p><strong>۱. کلمه کلیدی در عنوان و توضیحات متا:</strong> کلمه کلیدی باید در مهم‌ترین بخش‌های صفحه یعنی عنوان و توضیحات متا وجود داشته باشد.</p><p><strong>۲. وضعیت ایندکس:</strong> مهم‌ترین فاکتور فنی! اگر صفحه روی حالت <code>NoIndex</code> باشد، گوگل آن را در نتایج نمایش نخواهد داد.</p><p><strong>۳. استفاده از زیرعنوان H2 با کلمه کلیدی:</strong> برای مثال، اگر کلمه کلیدی شما <code>وردپرس</code> است، یک تگ H2 مانند <code><h2>وردپرس چیست؟</h2></code> تیک سبز را دریافت می‌کند.</p><p><strong>۴. لینک‌سازی داخلی و خارجی:</strong> با لینک دادن به صفحات مرتبط در سایت خودتان (داخلی) و به منابع معتبر دیگر (خارجی)، اعتبار صفحه را افزایش‌ می‌دهید.</p><p><strong>۵. وجود کلمه کلیدی در محتوا:</strong> کلمه کلیدی باید به تعداد طبیعی و مناسب در متن تکرار شود. از تکرار بیش از حد خودداری کنید.</p><p><strong>۶. اسکیما (Schema):</strong> اگر صفحه شما به سوالات متداول پاسخ می‌دهد یا یک مقاله است، با استفاده از اسکیمای مناسب (مثل `FAQPage` یا `Article`) به گوگل کمک می‌کنید تا محتوای شما را به‌عنوان پاسخ مستقیم نمایش دهد.</p><p><strong>۷. متن جایگزین (Alt) تصاویر:</strong> نوشتن متن جایگزین مناسب با کلمه کلیدی، به درک بهتر گوگل از محتوای تصویری و بهبود سئوی کلی صفحه کمک می‌کند.</p>';
    }

    private function get_geo_tutorial_content(): string {
        return '<h4>راهنمای GEO (بهینه‌سازی برای جستجوی محلی)</h4><p>هدف <strong>GEO</strong>، کسب رتبه برای جستجوهایی است که به یک موقعیت مکانی خاص مرتبط هستند (مثلاً <code>رستوران در تهران</code>). این تکنیک‌ها برای کسب‌و‌کارهای محلی حیاتی است.</p><p><strong>۱. ذکر نام شهر:</strong> نام شهر هدف خود را در عنوان، تیترها، متن اصلی و همچنین <strong>متن جایگزین (Alt) تصاویر</strong> به کار ببرید.</p><p><strong>۲. اطلاعات تماس و آدرس (NAP):</strong> درج اطلاعات نام (Name)، آدرس (Address) و شماره تلفن (Phone) یک سیگنال بسیار قوی برای سئوی محلی است.</p><p><strong>۳. جاسازی نقشه گوگل:</strong> قرار دادن نقشه گوگل در صفحه، موقعیت مکانی شما را به گوگل رسماً اعلام می‌کند.</p><p><strong>۴. استفاده از اسکیمای LocalBusiness:</strong> این یک زبان نشانه‌گذاری است که به گوگل کمک می‌کند کسب‌وکار محلی شما را عمیق‌تر درک کند. اگر کسب‌وکار محلی دارید، استفاده از این اسکیما یا انواع خاص‌تر آن (مثل <code>Restaurant</code>, <code>Store</code>, <code>Dentist</code>) <strong>بسیار ضروری</strong> است.</p>';
    }
}

Mini_SEO_Analyzor::instance();

endif;

افزونه مینی سئو نسخه پرو

تحلیلگر سئو AIO: بهینه‌سازی برای موتورهای پاسخ‌گو

با تحلیلگر AIO، محتوای خود را برای پاسخ مستقیم به نیاز کاربر و نمایش در بخش پاسخ‌های ویژه گوگل (Featured Snippets) بهینه کنید.

  • ✅ بررسی کلمه کلیدی: حضور هوشمندانه کلمه کلیدی در عنوان، متا، تیتر H2 و متن اصلی.

  • ✅ وضعیت ایندکس: اطمینان از اینکه صفحه شما توسط گوگل دیده می‌شود.

  • ✅ لینک‌سازی هوشمند: تحلیل کامل لینک‌های داخلی و خارجی برای افزایش اعتبار.

  • ✅ قدرت اسکیما: بررسی وجود اسکیماهای ساختاریافته برای درک بهتر محتوا توسط گوگل.

  • ✅ سئوی تصاویر: تحلیل متن جایگزین (Alt) تصاویر برای گرفتن ورودی از گوگل ایمیج.

مقاله مرتبط:  بلاک کردن درخواست‌های خارجی وردپرس

 تحلیلگر سئو GEO: پادشاهی در جستجوهای محلی

اگر کسب‌وکار محلی دارید، این بخش بلیط طلایی شما برای جذب مشتریان اطرافتان است.

  • ✅ شناسایی نام شهر: بررسی هوشمند نام شهر هدف در عنوان، محتوا و تصاویر.

  • ✅ اطلاعات تماس (NAP): تحلیل وجود آدرس و شماره تلفن به عنوان یک سیگنال قدرتمند محلی.

  • ✅ نقشه گوگل: تشخیص وجود نقشه گوگل برای تثبیت موقعیت مکانی شما.

  • ✅ اسکیمای محلی: بررسی تخصصی اسکیمای LocalBusiness برای درخشش در نتایج محلی.

 تحلیلگر Pico: بهینه‌سازی تصاویر و مدیا

سرعت سایت و تجربه کاربری را با تحلیل دقیق رسانه‌ها به سطح بالاتری ببرید.

  • ✅ فرمت WebP: شناسایی تصاویر قدیمی و پیشنهاد فرمت مدرن WebP برای افزایش سرعت.

  • ✅ متن جایگزین (Alt): شناسایی تصاویری که متن جایگزین ندارند تا هیچ فرصتی را از دست ندهید.

  • ✅ حجم و ابعاد استاندارد: تشخیص تصاویر سنگین و بسیار عریض که سرعت سایت را کم می‌کنند.

  • ✅ شناسایی ویدیو و صوت: امتیازدهی مثبت برای استفاده از ویدیو و پادکست جهت افزایش زمان ماندگاری کاربر.

 سازگاری و یکپارچگی کامل

  • هماهنگ با غول‌ها: کاملاً سازگار با افزونه‌های محبوب Rank Math و Yoast SEO.

  • پشتیبانی از ووکامرس: تحلیل دقیق و تخصصی صفحات محصولات فروشگاه شما.

  • مناسب برای همه: طراحی شده برای نوشته‌ها، برگه‌ها و محصولات ووکامرس.

مقاله مرتبط:  افزونه نمایش تاریخ بروزرسانی ها
خدمات پشتیبانی سایت وردپرسی
بعد از تکمیل فرم وبررسی اولیه باشما تماس می گیرم
۴/۵ - (۳ امتیاز)
Telegram
WhatsApp
Email
LinkedIn
پشتیبان سایت

یک پاسخ

دیدگاهتان را بنویسید

پانزده − 5 =