Daily Subscription Converter #506
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Daily Subscription Converter | |
| on: | |
| schedule: | |
| - cron: '30 5 * * *' | |
| - cron: '30 17 * * *' | |
| push: | |
| workflow_dispatch: | |
| jobs: | |
| convert_and_push: | |
| runs-on: ubuntu-latest # از یک محیط لینوکس استفاده میکند. | |
| steps: | |
| - name: 📥 Checkout repository # کد مخزن را به محیط اجرا کپی میکند | |
| uses: actions/checkout@v3 | |
| - name: 💾 Cache Docker image for subconverter # کش کردن ایمیج داکر subconverter برای اجرای سریعتر | |
| id: cache-subconverter-docker | |
| uses: actions/cache@v3 | |
| with: | |
| path: /var/lib/docker # مسیری که ایمیجهای داکر در آن ذخیره میشوند. | |
| key: ${{ runner.os }}-docker-${{ hashFiles('**/Dockerfile') }} # کلید کش بر اساس سیستم عامل و تغییرات Dockerfile (اگر وجود داشته باشد) | |
| restore-keys: | | |
| ${{ runner.os }}-docker- | |
| - name: 🐳 Pull subconverter Docker image # اطمینان از وجود آخرین ایمیج subconverter | |
| run: docker pull metacubex/subconverter:latest | |
| - name: 📂 Create base output directory # ایجاد پوشه اصلی برای ذخیره خروجیها | |
| run: mkdir -p output_configs | |
| - name: ⚙️ Install yq # نصب yq برای خواندن فایل YAML | |
| run: sudo snap install yq | |
| - name: 📖 Read subconverter settings # خواندن تنظیمات از فایل YAML | |
| id: read_settings | |
| run: | | |
| SETTINGS_FILE="config/subconverter_settings.yml" | |
| ADVANCED_PARAMS="" | |
| if [ -f "$SETTINGS_FILE" ]; then | |
| echo "✅ خواندن تنظیمات از: $SETTINGS_FILE" | |
| # لیست تمام پارامترهای پیشرفته که میخواهید از فایل settings.yml بخوانید | |
| # این لیست باید با پارامترهای پشتیبانی شده توسط subconverter API مطابقت داشته باشد. | |
| PARAMS=( | |
| "emoji" "add_emoji" "remove_emoji" "append_type" "tfo" "udp" "list" "sort" | |
| "script" "insert" "scv" "fdn" "expand" "append_info" "prepend" "classic" | |
| "tls13" "new_name" "ua" "interval" "dev_id" "filename" "exclude" "include" "rename" | |
| ) | |
| for param in "${PARAMS[@]}"; do | |
| # استفاده از yq برای خواندن مقدار پارامتر. 2>/dev/null برای ساکت کردن خطاهای yq در صورت عدم وجود پارامتر. | |
| VALUE=$(yq e ".$param" "$SETTINGS_FILE" 2>/dev/null) | |
| # بررسی اینکه مقدار null نباشد و خالی نباشد | |
| if [ "$VALUE" != "null" ] && [ -n "$VALUE" ]; then | |
| # URL-encode کردن مقدار پارامتر برای استفاده در URL | |
| ENCODED_VALUE=$(python3 -c 'import urllib.parse; print(urllib.parse.quote(input(), safe=""))' <<< "$VALUE") | |
| ADVANCED_PARAMS+="&${param}=${ENCODED_VALUE}" | |
| fi | |
| done | |
| echo "ADVANCED_PARAMS=$ADVANCED_PARAMS" >> $GITHUB_OUTPUT | |
| else | |
| echo "⚠️ فایل تنظیمات '$SETTINGS_FILE' یافت نشد. از تنظیمات پیشفرض subconverter استفاده میشود." | |
| echo "ADVANCED_PARAMS=" >> $GITHUB_OUTPUT | |
| fi | |
| - name: 🚀 Start subconverter Docker container # راهاندازی کانتینر subconverter در حالت HTTP سرور | |
| run: | | |
| docker run -d -p 25500:25500 --name subconverter_instance metacubex/subconverter:latest | |
| # صبر کردن برای آماده شدن سرویس subconverter | |
| echo "⌛ صبر کردن برای راهاندازی subconverter..." | |
| # مقدار sleep را میتوان بر اساس سرعت runner و زمان راهاندازی subconverter تنظیم کرد. | |
| # 10 ثانیه معمولاً کافی است. | |
| sleep 10 | |
| - name: 🔄 Process subscription files # پردازش فایلهای سابسکریپشن | |
| run: | | |
| # مسیر پوشه حاوی فایلهای سابسکریپشن ورودی | |
| INPUT_DIR="input_subs" | |
| # مسیر پوشه اصلی برای ذخیره فایلهای کانفیگ تبدیل شده | |
| OUTPUT_BASE_DIR="output_configs" | |
| # پارامترهای پیشرفته از مرحله قبلی (خروجی read_settings) | |
| ADVANCED_PARAMS="${{ steps.read_settings.outputs.ADVANCED_PARAMS }}" | |
| # Initialize duplicate log ONCE | |
| echo "## Duplicate Filenames Log" > duplicate.error | |
| echo "" >> duplicate.error | |
| # بررسی اینکه آیا پوشه ورودی وجود دارد یا خیر | |
| if [ ! -d "$INPUT_DIR" ]; then | |
| echo "پوشه ورودی '$INPUT_DIR' یافت نشد. لطفاً فایلهای سابسکریپشن خود را در این پوشه قرار دهید." | |
| echo "عملیات تبدیل متوقف میشود." | |
| exit 0 # Workflow با موفقیت پایان مییابد اما عملیات تبدیل انجام نمیشود. | |
| fi | |
| # حلقه برای پردازش هر فایل .txt در پوشه ورودی | |
| # از find برای اطمینان از یافتن فقط فایلها و مدیریت بهتر نامها استفاده میشود. | |
| find "$INPUT_DIR" -maxdepth 1 -type f -name "*.txt" | while read sub_file; do | |
| echo "📂 در حال پردازش فایل: $sub_file" | |
| # استخراج نام فایل بدون پسوند (مثلا "clash" از "clash.txt") که به عنوان TARGET_FORMAT استفاده میشود | |
| # sed برای حذف پسوند .txt استفاده میشود. | |
| TARGET_FORMAT=$(basename "$sub_file" | sed 's/\.txt$//') | |
| # ایجاد زیرپوشه خروجی برای این فرمت هدف (مثلا output_configs/clash) | |
| CURRENT_OUTPUT_DIR="${OUTPUT_BASE_DIR}/${TARGET_FORMAT}" | |
| mkdir -p "$CURRENT_OUTPUT_DIR" | |
| echo "📁 ایجاد پوشه خروجی: $CURRENT_OUTPUT_DIR" | |
| LINE_COUNT=0 | |
| # خواندن هر خط از فایل | |
| while IFS= read -r line; do | |
| LINE_COUNT=$((LINE_COUNT+1)) | |
| trimmed_line=$(echo "$line" | xargs) # حذف فضای خالی اطراف خط | |
| if [ -n "$trimmed_line" ]; then # اگر خط خالی نباشد | |
| # جداسازی لینک و نام فایل دلخواه | |
| if [[ "$trimmed_line" == *,* ]]; then | |
| URL_TO_CONVERT_RAW=$(echo "$trimmed_line" | cut -d',' -f1) | |
| CUSTOM_OUTPUT_BASE_NAME=$(echo "$trimmed_line" | cut -d',' -f2-) # گرفتن همه چیز بعد از اولین کاما | |
| # تمیز کردن نام فایل سفارشی: حذف کاراکترهای نامعتبر و جایگزینی با '_' | |
| CUSTOM_OUTPUT_BASE_NAME=$(echo "$CUSTOM_OUTPUT_BASE_NAME" | sed 's/[^a-zA-Z0-9._/-]/_/g') | |
| OUTPUT_FILENAME="${CUSTOM_OUTPUT_BASE_NAME}.yaml" | |
| else | |
| # بازگشت به نامگذاری پیشفرض اگر کاما یافت نشد | |
| URL_TO_CONVERT_RAW="$trimmed_line" | |
| OUTPUT_FILENAME="${TARGET_FORMAT}_$(printf "%03d" $LINE_COUNT).yaml" | |
| echo "⚠️ هشدار: خط #$LINE_COUNT در فایل '$sub_file' فرمت 'URL,FILENAME' را ندارد. از نام پیشفرض استفاده میشود." | |
| fi | |
| # بررسی اینکه بخش URL واقعاً یک URL باشد (یک بررسی ساده) | |
| if [[ "$URL_TO_CONVERT_RAW" =~ ^https?:// ]]; then | |
| # echo "🔄 در حال تبدیل لینک #$LINE_COUNT از فایل '$sub_file' به فرمت '$TARGET_FORMAT'..." | |
| # URL-encode کردن لینک تکی با استفاده از پایتون | |
| URL_TO_CONVERT=$(python3 -c 'import urllib.parse; print(urllib.parse.quote(input(), safe=""))' <<< "$URL_TO_CONVERT_RAW") | |
| OUTPUT_FILE_PATH_IN_WORKSPACE="${CURRENT_OUTPUT_DIR}/${OUTPUT_FILENAME}" | |
| mkdir -p "$(dirname "$OUTPUT_FILE_PATH_IN_WORKSPACE")" | |
| # Check for duplicate filenames | |
| # if [ -f "$OUTPUT_FILE_PATH_IN_WORKSPACE" ]; then | |
| # echo "⚠️ Duplicate detected: '$OUTPUT_FILE_PATH_IN_WORKSPACE' from URL: $URL_TO_CONVERT_RAW" | tee -a duplicate.error | |
| # fi | |
| # ساخت URL کامل برای درخواست به API محلی subconverter | |
| # شامل لینک سابسکریپشن و پارامترهای پیشرفته | |
| FULL_API_URL="http://localhost:25500/sub?target=${TARGET_FORMAT}&url=${URL_TO_CONVERT}${ADVANCED_PARAMS}" | |
| # ارسال درخواست curl به subconverter محلی و ذخیره خروجی | |
| # -s: حالت ساکت (silent) | |
| # -S: نمایش خطاها حتی در حالت ساکت | |
| # -o: ذخیره خروجی در فایل مشخص شده | |
| # حذف -f برای جلوگیری از توقف Workflow در صورت خطای HTTP (مانند 400) | |
| curl -s -S -o "$OUTPUT_FILE_PATH_IN_WORKSPACE" "$FULL_API_URL" | |
| # بررسی وضعیت خروج دستور curl | |
| CURL_EXIT_CODE=$? | |
| # if [ "$CURL_EXIT_CODE" -eq 0 ]; then | |
| # echo "✅ تبدیل لینک #$LINE_COUNT با موفقیت انجام شد. خروجی در '${OUTPUT_FILE_PATH_IN_WORKSPACE}' ذخیره شد." | |
| if [ "$CURL_EXIT_CODE" -ne 0 ]; then | |
| echo "❌ خطا در تبدیل لینک #$LINE_COUNT از فایل '$sub_file' (کد خطا: $CURL_EXIT_CODE). لطفاً لاگهای GitHub Actions را برای جزئیات بیشتر بررسی کنید." | |
| echo " این لینک ممکن است نامعتبر باشد یا subconverter نتواند آن را پردازش کند. به پردازش لینکهای بعدی ادامه میدهیم." | |
| # میتوانید محتوای فایل خروجی را با یک پیام خطا پر کنید تا مشخص باشد تبدیل ناموفق بوده است. | |
| echo "Error: Failed to convert this link. Check GitHub Actions logs for details." > "$OUTPUT_FILE_PATH_IN_WORKSPACE" | |
| fi | |
| else | |
| echo "🚫 خطا: خط #$LINE_COUNT در فایل '$sub_file' حاوی URL معتبری نیست: '$URL_TO_CONVERT_RAW'. از آن رد میشویم." | |
| fi | |
| else | |
| echo "⚪ خط #$LINE_COUNT در فایل '$sub_file' خالی است. از آن رد میشویم." | |
| fi | |
| done < "$sub_file" | |
| # اگر هیچ لینکی در فایل یافت نشد، یک پیام بدهید | |
| if [ "$LINE_COUNT" -eq 0 ]; then | |
| echo "📭 فایل '$sub_file' هیچ لینک معتبری برای تبدیل نداشت." | |
| fi | |
| done | |
| - name: 🛑 Stop subconverter Docker container # توقف کانتینر subconverter | |
| if: always() # همیشه اجرا شود، حتی اگر مراحل قبلی خطا داشته باشند | |
| run: | | |
| echo "🛑 توقف و حذف کانتینر subconverter..." | |
| docker stop subconverter_instance || true # `|| true` برای جلوگیری از خطا در صورت عدم وجود کانتینر | |
| docker rm subconverter_instance || true # `|| true` برای جلوگیری از خطا در صورت عدم وجود کانتینر | |
| - name: 📝 Generate ERROR⚠️ LIST | |
| run: | | |
| echo "## NOT Converted Configs" > Error.md | |
| echo "" >> Error.md | |
| echo "|File Line Count|File URL|" >> Error.md | |
| echo "|-|-|" >> Error.md | |
| USER=$(echo "${{ github.repository }}" | cut -d'/' -f1) | |
| REPO=$(echo "${{ github.repository }}" | cut -d'/' -f2) | |
| find output_configs -type f -name "*.yaml" \ | |
| | while read file; do | |
| echo "$(wc -l < "$file") $file" | |
| done \ | |
| | sort -nr \ | |
| | while read LINE_COUNT file; do | |
| if [ "$LINE_COUNT" -lt 5 ]; then | |
| REL_PATH=$(echo "$file" | sed 's|^output_configs/||') | |
| echo "|[${LINE_COUNT}]|https://github.com/${USER}/${REPO}/blob/main/output_configs/${REL_PATH}|" >> Error.md | |
| fi | |
| done | |
| - name: 📝 Generate Output LIST | |
| run: | | |
| echo "## Exported Configs" > Output.list | |
| echo "" >> Output.list | |
| USER=$(echo "${{ github.repository }}" | cut -d'/' -f1) | |
| REPO=$(echo "${{ github.repository }}" | cut -d'/' -f2) | |
| find output_configs -type f -name "*.yaml" \ | |
| | while read file; do | |
| echo "$(wc -l < "$file") $file" | |
| done \ | |
| | sort -nr \ | |
| | while read LINE_COUNT file; do | |
| if [ "$LINE_COUNT" -gt 10 ]; then | |
| REL_PATH=$(echo "$file" | sed 's|^output_configs/||') | |
| echo "[${LINE_COUNT}]___${REL_PATH}|https://raw.githubusercontent.com/${USER}/${REPO}/refs/heads/main/output_configs/${REL_PATH}" >> Output.list | |
| fi | |
| done | |
| - name: 📑 Generate README.md | |
| run: | | |
| echo "# لیست پراکسی های استخراج شده از کالکتور های گیتهاب" > README.md | |
| echo "### لیست پراکسی استخراج شده خام" >> README.md | |
| echo "**فایلهای پیکربندی آماده استفاده:** مناسب برای proxy-providers" >> README.md | |
| echo "" >> README.md | |
| echo "|File URL|File Line Count|" >> README.md | |
| echo "|-|-|" >> README.md | |
| USER=$(echo "${{ github.repository }}" | cut -d'/' -f1) | |
| REPO=$(echo "${{ github.repository }}" | cut -d'/' -f2) | |
| EMOJIS=( | |
| "🌐" "🚀" "🔒" "⚡" "🛡️" # your originals | |
| "📦" "🛰️" "📡" "🔧" "🗂️" | |
| "💻" "📊" "📝" "🌀" "🌍" | |
| "💡" "📁" "⚙️" "🖧" "🔗" | |
| "🧩" "🔍" "🖥️" "🔄" "📶" | |
| "✈️" "🌟" "🧭" "🏷️" "🔑" | |
| ) | |
| IDX=0 | |
| # 🔢 Sort files by line count (descending) | |
| find output_configs -type f -name "*.yaml" \ | |
| | while read file; do | |
| echo "$(wc -l < "$file") $file" | |
| done \ | |
| | sort -nr \ | |
| | while read LINE_COUNT file; do | |
| if [ "$LINE_COUNT" -gt 10 ]; then | |
| REL_PATH=$(echo "$file" | sed 's|^output_configs/||') | |
| EMOJI=${EMOJIS[$((IDX % ${#EMOJIS[@]}))]} | |
| echo "| [${EMOJI} ${REL_PATH}](https://raw.githubusercontent.com/${USER}/${REPO}/refs/heads/main/output_configs/${REL_PATH})|[${LINE_COUNT}]|" >> README.md | |
| IDX=$((IDX + 1)) | |
| fi | |
| done | |
| cat << 'EOF' >> README.md | |
| - name: 👤 Configure Git user # پیکربندی اطلاعات Git برای commit کردن تغییرات | |
| run: | | |
| git config --global user.name "github-actions[bot]" | |
| git config --global user.email "github-actions[bot]@users.noreply.github.com" | |
| - name: 📤 Commit and push changes # commit کردن فایلهای تبدیل شده و push آنها به مخزن | |
| run: | | |
| # بررسی اینکه آیا پوشه خروجی وجود دارد و خالی نیست | |
| if [ -d "output_configs" ] && [ "$(ls -A output_configs)" ]; then | |
| git add output_configs duplicate.error Error.md Output.list README.md # اضافه کردن کل پوشه خروجی برای commit | |
| git commit -m "Update converted configs from daily subconverter action" || echo "No changes to commit" # اگر تغییری نبود، خطا نمیدهد. | |
| git push | |
| else | |
| echo "پوشه 'output_configs' خالی است یا وجود ندارد. هیچ تغییری برای commit وجود ندارد." | |
| fi |