<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های محمدحسن ستاریان</title>
        <link>https://virgool.io/feed/@mh_sattarian</link>
        <description>توسعه دهنده؛ متمرکز بر برنامه‌نویسی سمت وب و هوش مصنوعی.  linktr.ee/mh_sattarian</description>
        <language>fa</language>
        <pubDate>2026-06-19 03:36:08</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/5179/avatar/8ZkgvY.jpg?height=120&amp;width=120</url>
            <title>محمدحسن ستاریان</title>
            <link>https://virgool.io/@mh_sattarian</link>
        </image>

                    <item>
                <title>چای ۱۲: فرمت‌دهی ویدئو برای Video Strickerهای تلگرام با استفاده از FFmpeg</title>
                <link>https://virgool.io/internerds/tea-12-format-tg-video-sticker-using-ffmpeg-adyyqpqlsqfv</link>
                <description>مقدمهتلگرام (Telegram) در آخرین بروزرسانی اخیرش، یکی از معدود ویژگی‌هایی که از رقیب نه‌چندان قدر ولی پرطرفدارش واتس‌اپ (Whatsapp) کمتر داشت رو جبران کرد و پشتیبانی از استیکر‌های ویدئویی (Video Sticker) رو اضافه کرد.استیکرهای ویدئویی، تصاویر متحرک بدون صدایی با فرمت فایل WEBM هستند که مثل استیکر‌های معمولی و استیکر‌های انیمیشنی (Animated Sticker) به‌صورت پک دسته‌بندی شده و ذخیره می‌شوند.نمونه‌ای از یک استیکر ویدئویی -به پیکسلی بودن تصویر توجه کنید.تفاوت با GIFهابرخلاف گیف‌ها (جیف [GIF])، استیکرهای ویدئویی از لایه شفاف (Transparent) در تصویر پشتیبانی کرده (VP9 Codec) و بر اساس تنظیمات انکودینگ (Encoding) تصویر می‌توانند حجم کمتری داشته باشند.تفاوت با Animated Stickerهااستیکرهای انیمیشنی تصاویری برداری (وکتوری [Vector]) با فرمت فایل TGS هستند که با استفاده از تکنولوژی Lottie تولید شده و نمایش داده می‌شوند. برای تولید آن‌ها با استفاده از برنامه Adobe After Effect و با کمک شاخه‌ای از افزونه Bodymovin، تصاویر متحرک به فرمت مخصوص Lottie (فرمتی در قالب JSON) تبدیل شده و سپس به فرمت فایل TGS تبدیل می‌شوند.اما استیکر‌های ویدئویی پیچیدگی کمتری داشته و تصاویری پیکسلی (رستری [Raster]) هستند -همانند فیلم‌هایی کوتاه و بدون صدا- که فرمت فایل WEBM و کدک (Codec) VP9 ذخیره شده‌اند.آشنایی با FFmpegدر خلاصه‌ترین حالت، FFmpeg یک کتاب‌خانه، فریم‌ورک و مجموعه‌ای از برنامه‌های تحت خط فرمان (CLI)   چندسکویی (Cross-Platform) با امکانات فراوان برای نمایش، تبدیل، تغییر و ویرایش در ویدئوها و تصاویر (و تقریبا هر فرمت نمایش اطلاعات دیگری) است که به‌صورت متن‌باز توسعه و به‌صورت رایگان ارائه می‌شود. در این پست با استفاده از رابط خط فرمان FFmpeg یک ویدئو با فرمت mp4 را به فرمت مربوطه تبدیل می‌کنیم.پیشنهادImageMagick ابزار تحت خط فرمان دیگری برای کار با تصاویر است که پیش از این در پست زیر اون رو بررسی کردم:آشنایی با ImageMagick برای کار با تصاویراستفاده از رابط خط فرمان FFMPEG بسیار ساده بوده و قالب اجرای دستورات با استفاده از دستور ffmpeg --help قابل مشاهده است:$ ffmpeg --help
usage: ffmpeg [options] [[infile options] -i infile] ... {[outfile options] outfile}...نصببرای تصب FFmpeg بر اساس پلتفرم و سیستم خود یکی از روش‌های زیر را استفاده کنید:ویندوز# Download the zip file
https://github.com/BtbN/FFmpeg-Builds/releases

# using chocolatey package manager
choco install ffmpegاطلاعات بیشترلینوکس# Ubuntu
sudo apt install ffmpeg

# Arch
pacman -S ffmpegاطلاعات بیشترپیش‌نیازهای استیکر‌های ویدئویی (Video Sticker)برای اطلاعات بیشتر مستندات انکودینگ (Encoding) را مرور کنید.یکی از اضلاع (طول یا عرض) ویدئو باید دقیقا ۵۱۲ پیکسل باشد - ضلع دیگر می‌تواند ۵۱۲ پیکسل یا کمتر باشد.طول ویدئو باید کمتر از ۳ ثانیه باشد.فریم ریت تصویر می‌تواند تا ۳۰ فریم بر ثانیه (30FPS) باشد.ویدئو باید یک لایه شفاش (Transparent) داشته باشد (طبق گفته مستندات این مورد موقتی‌ست).سایز ویدئو نباید از ۲۵۶ کیلوبایت (256KB) تجاوز کند.ویدئو باید در فرمت WEBM باشد و توسط کدک VP9 انکود (Encode) شده باشد.ویدئو نباید هیچ کانال صوتی داشته باشد (بدون صدا باشد).بهتر است ویدئو تکرار شوند باشد تا تجربه کاربری بهتری ایجاد کند.فرمت‌دهی ویدئو توسط FFmpegبرای فرمت‌دهی ویدئو به فرمت مورد نیاز می‌توانیم از دستور زیر استفاده کنیم:ffmpeg -i video.mp4 -c:v vp9 -pix_fmt yuva420p -b:v 1M -vf &amp;quotfps=30,scale=512:-1&amp;quot -an video.webmتوضیح تنظیمات دستور (Flags)- ‏i | اجباری | آدرس(های) فایل ورودی- ‏‏c:v | اجباری | تنظیم کدک؛ کدک VP9 از کانال آلفا (Alpha Channel) برای شفایت تصویر (Transparency) پشتیبانی می‌کند. - ‏pix_fmt | اجباری | فرمت پیکسل‌ها؛ فرمت yuva420p برای پشتبانی از کانال آلفا لازم است.- ‏b:v | اختیاری | تنظیمات بیت ریت ویدئو- ‏vf | اختیاری | تنظیمات فیلترهای ویدئو؛ استفاده از fps=30 برای تعیین فریم ریت ویدئو.- ‏vf | اجباری | تنظیمات فیلترهای ویدئو؛ استفاده از scale برای مشخص کردن ابعاد تصویر دیدئوی خروجی (در اینجا طول ویدئو ۵۱۲ پیکسل تعیین شده و عرض ویدئو بر اساس نسبت تصویر به‌صورت خودکار تعیین خواهد شد).- ‏an | اختیاری | تعیین بدون صدا بودن ویدئو خروجی. اگر ویدئوی ورودی بدون صداست نیازی به استفاده نیست.منابعمستندات استیکرهای تلگراماطلاعات کدک VP9 در مستندات FFmpegاین بلاگ پست عالی با عنوان «Alpha Masking with FFMPEG»این پست، قسمت دوازدهم از چای، مجموعه‌ای در باب «چیزی که امروز یادگرفتم» است. باقی چای‌ها رو می‌تونید از اینجا مشاهده کنید و در مورد فلسفه‌ی این کار بخونید.</description>
                <category>محمدحسن ستاریان</category>
                <author>محمدحسن ستاریان</author>
                <pubDate>Thu, 03 Feb 2022 16:39:25 +0330</pubDate>
            </item>
                    <item>
                <title>چای ۱۱: حل مشکل نگاشت پورت‌های WSL 2</title>
                <link>https://virgool.io/internerds/tea-11-wsl2-portforwarding-hv7xtv3fvkxt</link>
                <description>مقدمه‏Windows Subsystem for Linux یا به اختصار WSL به توسعه‌دهندگان اجازه میده تا یک محیط GNU/Linux  رو بدون سربار ماشین‌های مجازی به‌صورت مستقیم روی ویندوز اجزا کنن. اجرای دستورات و برنامه‌های بر پایه POSIX روی ویندوز ویژگی‌ای‌ست که مایکروسافت مدت زیادی به دنبال آن بوده و بعد از فراگیر شدن روش‌های ایزوله‌سازی و کانتیر کردن سیستم‌ها (containerization) و لزوم به اجرای داکر (Docker) روی ویندوز و در نتیجه تغییر رویکردهای مایکروسافت، به‌صورت جدی‌تر دنبال شد.نسخه اولیه WSL در سال ۲۰۱۶ به‌عنوان یک لایه رابط کرنل (Kernel Interface) و بدون هیچ کرنل لینوکسی، پس از تلاش‌های ویندوز برای توسعه Hyper-V ظاهر شد که اجازه اجرای یک فضای کاربری* برپایه GNU مانند ابونتو (Ubuntu)‌ را می‌داد [۱].نسخه دوم WSL در سال ۲۰۱۹ با تغییراتی در معماری (همراه با ادغام Hyper-V داخل ویندوز) معرفی‌شد و یک کرنل واقعی لینوکس است که روی یک ماشین‌مجازی خیلی سبک اجرا می‌شود. در این نسخه، همچنین دسترسی به شبکه و دسترسی مشترک به فضای کاری ویندوز و لینوکس (فایل‌ها و برنامه‌ها) بهبودهای چشمگیری داشته و فرآیند نصب و فعال‌سازی نیز بسیار ساده شده‌‌ست. اخیرا و در آخرین بیلدهای (Build) ویندوز ۱۰ و در ویندوز ۱۱، با معرفیِ پشتیبانی از GPU و همچنین WSLg، برنامه‌های لینوکسی می‌توانند از شتاب‌دهنده‌های سخت‌افزاری بهره‌برده و یا برنامه‌های دارای رابط کاربری گرافیکی (GUI)، در کنار برنامه‌های ویندوزی اجرا شده و قابل دسترس باشند [۱].می‌توانید شرح تفاوت‌ها و تغییرات بین WSL1 و WSL2 را در مستندات WSL بخوانید.برای تجربه WSL اگر از نسخه‌های بروز ویندوز ۱۰ و یا ویندوز ۱۱ استفاده می‌کنید، ابتدا از دستور زیر برای راه‌اندازی کرنل استفاده کنین و سپس از Microsoft Store یک توزیع لینوکسی از بین توزیع‌های موجود را نصب کنید.  همین!wsl --installمشکل نگاشت پورت‌ها (Port Forwarding)در کنار ویژگی‌های بی‌نظیر WSL و انعطاف‌پذیری و دسترسی‌پذیری‌ای که برای توسعه‌دهندگان بوجود میاره، مشکلات جزئی* کمی هم داره که یکی از اون‌ها مشکلاتی در مسیریابی پورت‌ها به‌خصوص هنگام دسترسی به سرور‌های محلی (Local Servers) اجرا شده، از سمت ویندوز است.با اجرای یک برنامه‌ی تحت شبکه، برای مثال اجرای یک سرور روی WSL، باید بتوان از سمت ویندوز و از طریق شبکه داخلی (localhost) به آن‌ها دسترسی داشت (شبکه باید یک شبکه خصوصی [Private] باشد). این اتفاق به‌صورت خودکار در WSL1 انجام می‌شد، اما متأسفانه رفتار پیش‌فرض در WSL2 این نیست [۳].از آنجایی که WSL2 از یک آداپتور مجازی (Virtual Network Adapter) برای اتصال به شبکه استفاده می‌کند، با هربار ورود به ویندوز و اجرای اولیه WSL، کرنل یک IP جدید در شبکه داخلی، مانند IP زیر دریافت می‌کند [۳]:❯ hostname -I
172.31.6.171سرور‌های اجرا شده در WSL از این آدرس در دسترس هستند،‌ اما همانطور که اشاره شد این آدرس هربار تغییر کرده و نیاز داریم تا با استفاده از localhost ویندوز، مانند آدرس زیر، بتوانیم به آن‌ها دسترسی داشته باشیم.localhost:8080این مشکل در هربار بوت شدن ویندوز اتفاق نمی‌افتد؛ اما با این حال به دلیل اینکه حتی با ری‌استارت کردن  WSL2 و یا خاموش کردن فایروال (Firewall) ویندوز این مشکل رفع نمی‌شود، باعث می‌شود در کارکرد WSL2 عدم قطعیت ایجاد کرده و آن را مختل کند [۲].یکی از دلایل احتمالی این اتفاق -همانطور که در این issue به آن اشاره شده- به‌دلیل روشن‌بودن تنظیم Fast Startup است، با این‌حال به‌دلیل اینکه شخصاً علاقه‌ای به خاموش‌کردن این گزینه نداشتم، به دنبال راه‌حل دیگری بودم.لازم به ذکر است می‌توانید برای خاموش‌کردن WSL، دستور زیر را در CMD یا PowerShell ویندوز اجرا کنید:# PowerShell 
PS C:\Users\[username]&gt; wsl --shutdownرفع مشکل نگاشت پورت‌هابرای رفع این مشکل، باید همانطور که در مستندات WSL توضیح داده شده، پورت‌های لازم را به WSL نگاشت یا فوروارد کنیم (Port Forwarding) که برای سهولت کار و خودکار‌سازی آن، از ابزار netsh استفاده می‌کنیم.برای اینکار یک فایل با فرمت ps1 (برای مثال wsl2-port-forwarding.ps1) ساخته و اسکریپت زیر را در آن کپی کنید [۲ و ۳]:در صورت عدم نمایش:  gist.github.com/mhsattarian/bb55026b1e34cf8357c37289d693cec4 https://gist.github.com/mhsattarian/bb55026b1e34cf8357c37289d693cec4 این اسکریپت برای اجرا نیاز به دسترسی Admin داشته (به‌اصطلاح Elevated Permissions) و در ابتدا برای دریافت این دسترسی‌ها درخواستی فراخوانی می‌کند. سپس آدرس IP فعلی WSL2 را دریافت کرده و با استفاده از دستور netsh پورت‌های مشخص‌شده در آرایه ports$ را به این آدرس فوروارد (نگاشت) می‌کند. در کنار این کار قوانین نامرتبط در فایروال را نیز حذف می‌کند [۲ و ۳].می‌توانید با تغییر آرایه ports$، پورت‌های دلخواه خودتون رو مشخص کنید. بدین‌صورت برنامه‌ها و سرورهای اجرا شده در WSL2 از طریق localhost در ویندوز قابل دسترسی خواهند بود. در نهایت می‌توانید برای مشاهده پورت‌های فوروارد شده از دستور زیر استفاده کنید [۳]:netsh interface portproxy show v4tov4اجرای خودکار اسکریپتبا هربار اجرای اسکریپت بالا تنظیمات نگاشت پورت‌ها به درستی تنظیم (Set) شده و برنامه‌ها و سرور‌های WSL2 از طریق localhost در ویندوز در دسترس خواهند بود. بااین‌حال، اجرای اسکریپت هربار که WSL به مشکل می‌خورد شاید چندان راحت یا مقبول نباشد؛ برای سهولت بیشتر می‌توان با استفاده از Task Scheduler ویندوز این اسکریپت را در هربار بوت سیستم اجرا کنیم [۲].برای اینکار وارد Task Scheduler شده و از منوی سمت راست روی Create Task کلیک کنید؛ در پنجره‌ی باز شده یک نام برای این تسک مشخص کرده و وارد تب Triggers شوید، روی گزینه New کلیک کرده و زمان اجرای تسک را برابر At startup قرار دهید. پیشنهاد میشه برای تأخیرِ (Delay) اجرای تسک نیز مقداری (مثلاً ۳۰ ثانیه) را مشخص کنید:مشخص کردن زمان اجرای تسک در تب Triggersسپس وارد تب Actions شده و روی گزینه New کلیک کنید. به‌عنوان برنامه‌ای که باید اجرا شود به‌صورت زیر PowerShell ویندوز را مشخص کنید:Powershell.exeبه‌عنوان آرگومان‌ها به‌صورت زیر، آدرس اسکریپت را به‌همراه فلگ ExecutionPolicy- با مقدار Bypass وارد کنید:C:\Users\[username]\[path_to_script]\wsl2-port-forwarding.ps1 -ExecutionPolicy Bypassمشخص کردن برنامه اجرا شده در تب Actionsاگر از لپتاپ استفاده می‌کنید و در صورت امکان در تب Conditions تیک گزینه زیر را بردارید تا تسک زمانی که لپتاپ درحال شارژ نیست نیز اجرا شود:تنظیم اجرای تسک در زمان استفاده از باتری داخلی لپتاپبا زدن گزینه‌ی OK این تسک را ذخیره کنید. بدین‌صورت با هربار بوت ویندوز این تسک اجرا شده و دیگر نیازی به اجرای دستی آن نیست.پانویس*فضای کاربری: یک سیستم‌عامل مدرن، معمولاً حافظه‌ی مجازی را به دو بخش فضای هسته و فضای کاربری مجزا می‌کند. در اینجا منظور از فضای کاربری یک سیستم عامل لنوکسی (تعدیل شده) بدون کرنل لینوکس است.*مشکلات جزئی WSL: دیگر مشکلاتی که برخوردم شامل، اجرا نشدن WSL بعد از فراخوانی‌ست که با ری‌استارت WSL درست شده و مشکل دیگر آزاد نشدن رم اشغال شده توسط WSL پس از پایان یک پردازش (Process) است که طبق وعده‌های مایکروسافت در آپدیت‌های آینده باید رفع شود.منابع[۱] صفحه ویکی‌پدیا Windows Subsystem for Linux[۲] کامنت‌های issueهای ریپازیتوری WSL در گیت‌هاب (Github)[۳] این پست با موضوع مشابه در dev.toاین پست، قسمت یازدهم از چای، مجموعه‌ای در باب «چیزی که امروز یادگرفتم» است. باقی چای‌ها رو می‌تونید از اینجا مشاهده کنید و در مورد فلسفه‌ی این کار بخونید.</description>
                <category>محمدحسن ستاریان</category>
                <author>محمدحسن ستاریان</author>
                <pubDate>Sun, 24 Oct 2021 19:12:17 +0330</pubDate>
            </item>
                    <item>
                <title>چای ۹: تبدیل GeoJSON به SVG با استفاده از D3</title>
                <link>https://virgool.io/internerds/geojson-2-svg-ykbcx12vjdot</link>
                <description>مقدمهجئوجیسون (GeoJSON) یک فرمت ذخیره‌سازی و نمایش اطلاعات جغرافیایی (به همراه خصوصیات غیر برداری) برپایه فرمت JSON است که در برنامه‌ها و سیستم‌های مرتبط با اطلاعات جغرافیایی استفاده زیادی دارد.حتی دیتابیس‌هایی مثل mongoDB از این فرمت منحصرا پشتیبانی کرده و از از یک داده JSON عادی متمایز می‌کنند.این فرمت از داده‌های جغرافیایی مختلفی مثل نقطه (Point)، خط (LineString)، پولیگان (Polygon) و مجموعه‌هایی از آن‌ها -مثلا MultiPoint- پشتیبانی می‌کنه که در GeoJSON RFC تبیین شدن.یک نمونه ساده از یک نقطه به‌فرمت  GeoJSON به‌صورت زیر است:نمایش یک نمونه GeoJSON در سایت geojson.io برای نمایش بصری داده‌های جغرافیایی‌ از روش‌های مختلفی استفاده می‌شود، این داده‌ها را می‌توان به‌صورت تایل (کاشی [Tile]) شده و یا به‌صورت واحد، به فرمت‌های مختلف تصویر رستری (پیکسلی [raster])‌ یا وکتوری (برداری [vector]) نمایش داد. در این پست می‌خواهیم داده‌های جغرافیایی به فرمت GeoJSON رو به‌فرمت SVG که یکی از روش‌های مرسوم نمایش داده‌های گرافیکی -عموما تصاویر- و یکی از انعطاف‌پذیر‌ترین، کم‌حجم‌ترین و دردسترس‌ترین فرمت‌های استفاده شده در وب است تبدیل کنیم.آماده‌سازیبرای این تبدیل از کتاب‌خونه D3 استفاده می‌کنیم و نیاز به یک محیط اجرای جاوا اسکریپت (Javascript) داریم که بتونیم توسط اون به DOM هم دسترسی داشته باشیم. بنابراین اگر از NodeJS برای این تبدیل استفاده می‌کنید یکی از پکیج‌های پیاده‌ساز DOM مثل jsdom و یا پکیجی مانند d3-node را استفاده کنید.داده‌های GeoJSONبرای ساخت یک داده جغرافیایی به فرمت GeoJSON ابزار‌های حرفه‌ای زیادی مانند نرم‌افزار QGIS وجود دارن، اما برای سادگی‌کار از ابزار آنلاین geojson.io ساخت شرکت mapbox استفاده می‌کنیم. این سایت رو باز کنید و با استفاده از نوار ابزار سمت راست یک یا چند شکل (نقطه یا پولیگان یا غیره) رسم کنید:رسم یک پولی‌گان (Polygon) در geojson.ioتبدیل GeoJSON به SVGابتدا با استفاده از D3 یک SVG به سایز مورد نظرمون می‌سازیم:const width = 200;
const height = 100;

const svg = d3
  .create(&amp;quotsvg&amp;quot)
  .attr(&amp;quotwidth&amp;quot, width)
  .attr(&amp;quotheight&amp;quot, height);قبل از تبدیل کد GeoJSON به یک تصویر SVG، نکته مهمی رو لازم است بدونیم:مختصات نقاط در فرمت GeoJSON به‌صورت پادساعت‌گرد ذخیره می‌شوند، درحالی‌که، D3 به‌صورت پیش‌فرض، یک عارضه‌را به‌صورت ساعت‌گرد انتظار داشته و در‌نظر می‌گیرد.برای اینکه مختصات پولیگان خودمون رو به‌صورت ساعت‌گرد تبدیل کنیم، از ابزار mapbox/geojson-rewind استفاده می‌کنیم:const _geoJson = rewind(polygon, true);حال برای نمایش عارضه GeoJSON و تبدیل اون به SVG از d3-geo استفاده می‌کنیم که به‌صورت پیش‌فرض داخل D3 موجوده ولی به‌صورت یک کتابخانه جدا نیز ارائه میشه. برای اینکار کافیه تا با استفاده از d3.geoPath یک تابع path ایجاد کنیم و از آن در ساخت المان path داخل SVG خودمون استفاده کنیم.با اینکه با استفاده از این روش فایل GeoJSON ما به‌درستی به SVG تبدیل شده، سایز نمایش اون درست نیست و درواقع کل محدوده مختصات جغرافیایی (کل نقشه جهان) به محدوده SVG ما نگاشت شده و در نتیجه پولیگان رسم‌شده بسیار کوچک خواهد بود.برای اینکه بتونیم پولیگان را به اندازه فعلی SVG تصویر کنیم و به‌اصطلاح project کنیم به‌صورت زیر عمل می‌کنیم:پولیگان تبدیل شده درحالت‌های تصویر نشده و تصویر شده به سایز SVGدر این روش، مختصات تبدیل شده توسط سیستم تصویر مرکاتور (سیستمی برای نمایش نقشه جهان به‌صورت تخت) را به‌سایز فعلی SVG متناسب می‌کنیم.نتیجه نهایی رو می‌تونید در مثال زیر مشاهده کنید: https://stackblitz.com/edit/geojson-svg-d3?embed=1&amp;file=index.js&amp;hideExplorer=1 این پست، قسمت نهم از چای، مجموعه‌ای در باب «چیزی که امروز یادگرفتم» است. باقی چای‌ها رو می‌تونید از اینجا مشاهده کنید و در مورد فلسفه‌ی این کار بخونید.</description>
                <category>محمدحسن ستاریان</category>
                <author>محمدحسن ستاریان</author>
                <pubDate>Sat, 17 Oct 2020 20:10:18 +0330</pubDate>
            </item>
                    <item>
                <title>ساخت API برای Archillect با استفاده از Cloudflare Workers</title>
                <link>https://virgool.io/internerds/cloudflare-workers-archillect-api-hn4cbntamacf</link>
                <description>توی این پست با استفاده از Cloudflare Workers که یک سرویس Edge Computing بدون‌سرور (serverless) با جاوا اسکریپت و  APIای مشابه ServiceWorker‌هاست، با استفاده از اسکریپتی که در زمان پاسخ به ریکوئست اجرا می‌شه، دو API Endpoint-طور می‌سازیم و یک دکمه دانلود به صفحه اضافه می‌کنیم.نتیجه نهایی رو می‌تونید در archillect.mhsattarian.workers.dev و کد‌ها رو در ریپازیتوری گیت‌هاب ببینید.معرفیقبل از توضیح پروژه و انجام اون، تو این بخش ‌به‌صورت مختصر به معرفی سرویس‌ها و مفاهیمی که دونستن اون‌ها ‌بد نیست و در درک چگونگی کارکرد پروژه کمک می‌کنه می‌پردازم. اگر با مفاهیم/سرویس‌های بدون‌سرور، Edge Computing و Cloudflare و سرویس ورکر‌ها (service workers) آشنایی دارین می‌تونین این بخش رو رد کنین.‏Archillectپروژه Archillectپروژه Archillect که اسم اون از ترکیب دو کلمه Archive  و Intellect گرفته شده، یک ربات [برنامه] هوش مصنوعی (AI) است که ساخته شده تا نوعی محتوی تصویری رو در شبکه‌های مختلف به اشتراک بذاره. پست‌هایی که انتخاب می‌کنه معمولا از Tumblr هستن و تا الان توی توییتر، تلگرام، اینستاگرام و چند شبکه دیگه منتشر می‌شن. علاوه‌بر ربات توییتر Archillect، ربات دیگری به اسم Archillect Links هم لینک هر عکسی که منتشر می‌شه رو زیر همون عکس رو ریپلای می‌کنه.‏Cloudflareدر ساده‌ترین حالت، ‏Cloudflare یک HTTP Cache هست که در حال حاضر در ۲۰۰ موقعیت در سرتاسر جهان درحال اجراست. اما  Cloudflare سرویس‌های بیشتری از محدود ویژگی‌هایی که استاندارد HTTP برای HTTP Cacheها مشخص می‌کند ارائه می‌دهد، مثل DNS و SSL، مقابله با حملات،‌توزیع بار و موارد دیگه. ‏Cloudflare با ارائه سرویس‌هایی مثل مدیریت DNS رایگان، افزونه‌های شخص‌ثالث برای پروسس ریکوئست‌ها و سرویس‌هایی مانند 1.1.1.1 و Warp امروزه بسیار مورد استفاده و قدرتمنده. ‏Edge Computingبرای آشنایی با مدل اجرای بدون‌سرور (serveless) پیشنهاد می‌کنم قسمت «پردازش ابری (Cloud Computing)» از مقدمه پست «تبدیل توییت به عکس با استفاده از توابع بدون سرور Netlify» رو مطالعه کنین.آمازون AWS به عنوان یکی از بزرگترین سرویس دهنده‌های مدل اجرای بدون‌سرور (serverless) در نقاط مختلفی از دنیا مزارع‌سرورهای (server farm) زیادی، به‌صورت دقیق‌تر ۷۷ مرکز، برای پوشش سرویس‌هاش ایجاد کرده. نقشه پوشش این سرور‌ها:نقشه پوشش جغرافیایی سرورهای Amazon AWSدر نظر بگیرید اگر کاربری در غرب آفریقا درخواستی برای یکی از محتوی/سرویس‌های AWS ارسال کند،‌ پکت‌های شبکه (packets) راه زیادی برای فراهم کردن این محتوی/سرویس طی می‌کنند.سرویس‌دهنده‌های دیگه نیز مثل Cloudflare و Fastly، با وجود اینکه نسبت به آمازون شرکت‌های بسیار کوچک‌تری محسوب می‌شن، به دلیل نیاز به پوشش سرتاسری و بهبود سرویس‌های DNS و CDNاشون زیرساخت‌های سرور بزرگ و زیادی در سرتاسر دنیا دارند. نقشه پوشش سرور‌های این دو کمپانی:نقشه پوشش جغرافیایی سرورهای Cloudflareنقشه پوشش جغرافیایی سرورهای Fastlyبه دلیل نیاز به  یک شبکه جهانی برای ارائه CDN و ملاحظات امنیتی، این کمپانی‌ها سرور‌های بسیار بیشتری در گستره جغرافیایی وسیع‌تری در سراسر جهان‌دارند. در نهایت، تمامی این مراکز ساختمون‌های بزرگی با تعداد زیادی سرور هستند که نتیجه ترکیب اون‌ها یک شبکه عظیمه که امکان اجرا در تقریبا همه‌جا (از نظر فیزیکی) -در لبه (Edge) دریافت و پروسس درخواست‌ها- رو میده. چیزی مشابه Write Once, Run Anywhere از نظر فیزیکی.برخلاف سرویس AWS آمازون که برنامه‌ها در مراکز مشخص با هزینه مشخص اجرا می‌شن؛ این دو کمپانی با همکاری هم امکانی ایجاد کردند که برنامه‌ها یکبار نوشته شده و همزمان در تمامی این مراکز مستقر (deploy) بشن.البته آمازون سرویس مشابه به اسم Lambda Edge داره که امکانات کم و بیش مشابهی رو ارائه میده.همچنین برای اجرای برنامه‌ها، بجای ایجاد زمان اجراهای (runtime) مختلف، اجازه می‌دهند که کدهای web assembly رو در Edge اجرا کنیم.‏Cloudflare Workersاگر با جاوا اسکریپت آشنا باشین، احتمالا با مفهوم workerها و به‌ویژه service workerها آشنا هستین؛ ورکر‌ها در حالت کلی اسکریپت‌هایی هستند که قراره در یک ترد (thread) مجزا اجرا بشن و اجرای برنامه اصلی رو که تنها در یک ترد قابل اجراست مختل نکنن.سرویس ورکر‌ها نوع خاصی از ورکرها هستند که وظایف مخصوصی در قبال Web App کردن وب‌سایت دارن، وظایفی مثل کش کردن (برای استفاده آفلاین)، ارسال ناتیفیکیشن (push message)، همگام‌سازی در پس‌زمینه (background sync) و موارد مثل این. به بیان دیگه سرویس ورکر‌ها یک پراکسی قابل برنامه‌ریزی (programmable network proxy) هستند که اجازه می‌دهند چگونگی مدیریت درخواست‌های (requests) صفحه رو کنترل کنیم. ورکرهای کلودفلر (‏Cloudflare Workers) یک راهکار بدون سرور (serverless) و مشابه سرویس‌ورکر‌ها برای edge computing هستند که برای خیلی‌کارها منجمله مدیریت درخواست‌ها استفاده میشن. برنامه‌های نوشته شده که به لطف web assembly با زبان‌های javaScript، Rust و ++C قابل نوشته‌شدن هستن، در تمامی سرور نود‌های (Fastly + Cloudflare) دپلوی می‌شن. نتیجه تمامی این‌ها قابلیت نوشتن اسکریپت‌هاییست که به‌شدت قدرتمند هستن،‌ واقعا سریع اجرا می‌شن و انعطاف‌پذیری بسیار زیادی به برنامه‌نویس‌ها میدن.استفاده از ورکر‌های Cloudflare با محدودیت‌های بسیار سخاوت‌مندانه ۱۰۰ هزار ریکوئست در روز و محدودیت‌هایی در حجم فایل‌های ذخیره سازی رایگان است.مقدمهبه دلیل علاقه‌ای که به پروژه Archillect و محتوی‌ای که به اشتراک می‌ذاره داشتم، می‌خواستم یک API داشته باشم که با استفاده از اون بتونم از این محتوی توی پروژه‌های مختلف استفاده کنم (مشابه استفاده عکس‌های تصادفی از unsplash) یا اون‌ها رو آرشیو کنم.این پروژه API داره اما متاسفانه عمومی نبوده و تنها برای حمایت‌کننده‌ها از طریق Patreon قابل استفاده است.خوشبختانه عکس‌های Archillect با ID ای به‌ترتیب به‌اشتراک‌گذاری (شماره [index])، منتشر می‌شن و این امکان رو می‌داد تا با دونستن ID آخرین عکس به اشتراک گذاشته شده، ساخت یک index برای یک تصادفی خیلی راحت بشه.با دونستن این مورد، اولین روشی که به‌ذهنم رسید استفاده از RSS Feed و ترکیب اون با سرویس‌هایی مثل Integromat برای بدست آوردن ID آخرین محتوی به‌اشتراک‌گذاشته شده بود. اما Archillect هیچ Feedای ارائه نمی‌داد؛ برای همین سرویس‌هایی مثل rss.app رو تست کردم تا بتونم خودم برای اون یک Feed درست کنم. اما این سرویس‌ هم محدودیت‌های زیادی مثل محدود بودن تعداد feedها،‌ تبلیغات و به‌روز‌رسانی کند فیدها (هر ۲۴ ساعت) داشتن.مدتی روش‌های مختلف دیگه‌ای رو تست کردم تا یاد این ویدئو از Wes Bos افتادم:مشابه پست «تبدیل توییت به عکس با استفاده از توابع بدون سرور Netlify» در این پست هم از راهنمایی‌های این ویدئو کمک گرفته‌شده. https://youtu.be/48NWaLkDcME توی این ویدئو Wes Bos با استفاده از یک Cloudflare worker و اتصالش به دامنه خودش یک پروکسی برای دسترسی مستقیم به یک محتوی (عکس یا GIF) از یک صفحه می‌کنه. تقریبا مشابه کاری که ما قصد داریم بکنیم.آماده سازیدر این پروژه از nodejs استفاده می‌کنیم. اگر اون رو نصب ندارید می‌تونید از سایت اصلی Nodejs و یا با استفاده از مدیر بسته (package manger) سیستم‌عاملتون دانلود و نصب کنید.اگر اطمینان ندارید، ورژن LTS را نصب کنید، همچنین پیشنهاد می‌کنم از یک مدیر نسخه (version manager) برای nodejs مثل nvm یا n استفاده کنید:اگر از لینوکس استفاده می‌کنید و node رو نصب ندارید، انتخاب خوبیه که از اول با نصب یکی از اونها اقدام به نصب node کنید. برای مثال با این دستور زیر n رو نصب کنید: curl -L git.io/n-install | bashپس از نصب با اجزای دستورات زیر تو ترمینال از درستی نصبشون مطمئن بشید:node -vnpm -vهمچنین برای این پروژه لازم است در Cloudflare ثبت‌نام کرده و یک اکانت بسازید: سایت Cloudflareبرای ساختن یک Worker می‌تونید وارد حساب Cloudflare خودتون بشین و روی گزینه ورکر از منوی سمت راست کلیک کنین:ساخت Worker جدیداما برای اینکه بتونیم به‌صورت لوکال پروژه رو تست کنیم و در نهایت به‌صورت اتوماتیک اون رو دپلوی کنیم، همچنین اینکه برای مشاهده نتیجه تغییرات مجبور به اعمال تغییرات روی سرور (دستی یا اتوماتیک) نباشیم و همچنین از محدودیت ۱۰۰هزار ریکوئست در روز خرج نکنیم از ابزاری که Cloudflare معرفی کرده به اسم Wrangler استفاده می‌کنیم.‏wrangler‏wrangler ابزاری تحت خط‌فرمان است که به راحتی با npm  نصب میشه:npm install -g @cloudflare/wranglerبعد از نصب نیاز است تا با وارد کردن دستور زیر احراز هویت انجام بدیم:wrangler configبعد از اجرای این دستور مراحل احراز هویت توضیح داده میشه که به‌صورت خلاصه نیاز هست تا وارد اکانت خودتون بشین و از قسمت توکن‌ها یک توکن ایجاد کنین و اون توکن رو در ترمینال وارد کنید.حالا برای ایجاد پروژه از دستور زیر استفاده می‌کنیم. ایجاد پروژه جدید محدود به استفاده از این روش و قالب پیش‌فرض خود کلودفلر نیست و می‌تونید خودتون پروژه‌ای مشابه رو ایجاد کنید، اما برای سادگی و سرعت از همین دستور استفاده می‌کنیم:wrangler generate my-app
# or
wrangler generate my-app https://github.com/cloudflare/worker-template-routerدر اینجا نامی که به پروژه می‌دهیم my-app است و در دایرکتوری‌ای با همین نام ساخته خواهد شد.بعد از ساخته شدن قالب پروژه، پیامی مشاهده می‌کنیم که نیاز است فیلد‌هایی در فایل wrangler.toml رو تغییر بدیم. برای اینکار وارد دایرکتوری پروژه شده و این فایل رو باز می‌کنیم.وارد اکانت کلودفلر خودتون بشید و مانند تصویر «ساخت Worker جدید» از منوی سمت راست Workers رو انتخاب کنید. در ستون سمت راست داخل کادر می‌تونین account_id خودتون رو پیدا کنید.این مقدار رو کپی‌کرده و داخل فایل wrangler.toml در قسمت account_id قرار بدین. توجه ‌کنید که این مقدار باید به‌صورت string باشد، بنابراین کاراکتر‌های &quot; را حذف نکنید.مستندات و نحوه نصب و آماده‌سازی دقیق‌تر رو می‌تونید از این لینک بخونید.اجرای اولیه برنامهفایل index.js رو باز کنید. این فایل فایل اصلی پروژه است که در ابتدا اجرا می‌شود. مقدار این فایل مشابه زیر است:فایل اصلی پروژه با تغییر مقدار پراپرتی main داخل فایل package.json قابل تنظیم است.addEventListener(&#039;fetch&#039;, event =&gt; {
  event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
  return new Response(&#039;Hello worker!&#039;, {
    headers: { &#039;content-type&#039;: &#039;text/plain&#039; },
  })
}همونطور که مشاهده می‌کنید، مشابه service workerهای جاوا اسکریپت عمل‌کرده و با دریافت یک ریکوئست  (که با استفاده از eventListenerای که به اونت fetch متصل کرده متوجه اون می‌شه) و پروسس اون، ریسپانس (جواب [response]) رو ارسال می‌کنه.برای اجرا پروژه کافیه از دستور زیر استفاده کنیم:wrangler previewبا اجرای این دستور یک محیط تست (playground) پروژه در مرورگر باز می‌شود:آدرسی که در آدرس‌بار مشاهده می‌کنید به‌عنوان آدرس این ورکر در نظر گرفته می‌شود.دریافت مستقیم تصاویردر ساده‌ترین حالت در این پروژه، می‌خواهیم سایت archillect بدون تغییر نمایش داده بشه. برای اینکار بدین صورت عمل می‌کنیم که ریکوئست دریافت شده رو گرفته و آدرس اون رو به سایت Archillect تغییر میدیم و ریکوئست جدید رو (که با تابع fetch جاوا اسکریپت ایجاد کردیم) به‌عنوان ریسپانس برمی‌گردونیم.این اسکریپت مشابه یک پروکسی برای Archillect عمل کرده و با مشاهده آدرس این ورکر، انگار سایت Archillect رو باز کردیم:بعد از هربار تغییر کد‌ها از دستور wrangler preview برای مشاهده و اجرای ورکر استفاده کنید.ایجاد پروکسی به سایت Archillectپیش از این گفتم که هر تصویر یک id که نشان‌دهنده index اون هست داره و با اضافه‌کردن این id به انتهای آدرس سایت Archillect می‌تونیم صفحه مربوط به اون عکس رو مشاهده کنیم.الان می‌تونیم با اضافه کردن id در انتخاب آدرس ورکر خودمون (که اینجا از آدرس example.com برای نمایش اون استفاده می‌شه) صفحه تصویر رو ببینیم:حالا می‌خواهیم یک آدرس در اختیار داشته باشیم که با وارد کردن اون و اضافه کردن id‌ تصویر، فایل تصویر رو در اختیار داشته باشیم. برای اینکار نیاز هست تا در هنگام دریافت ریکوئست و قبل از ارسال ریسپانس، یعنی زمانی نتیجه ریکوئست ما (ریکوئست تغییر مسیر داده شده) به Archillect مشخص میشه، اون رو پروسس کنیم.نتیحه این ریکوئست طبیعتا یک فایل HTML ‌است؛ برای پروسس این فایل و بدست آوردن یا تغییر مقدار‌هایی که می‌خوایم می‌تونیم از توابع دستکاری string خود javascript استفاده کنیم و یا از API ای که Cloudflare داخل Workerهاش در اختیارمون می‌ذاره به‌نام HTMLRewriter استفاده کنیم.در این قسمت از روش اول استفاده می‌کنیم، برای اینکه بتونیم فایل عکس‌ها رو در آدرسی مثل آدرس زیر در اختیار داشته باشیم، نیاز داریم تا در‌خواست‌ها به آدرس img/ رو به صفحه عکس (آدرس بدون img/) منتقل کرده و با دریافت نتیجه به‌صورت یک فایل HTML،‌ اون رو برای پیدا کردن آدرس عکس پروسس کنیم و در نهایت به آدرس اصلی عکس ریکوئست زده و نتیجه رو برگردانیم.# &lt;worker-address&gt;/&lt;image_id&gt;/img
e.g. example.com/288580/imgبرای گرفتن آدرس اصلی تصویر، در نتیجه ریکوئست‌مون به Archillect (خط ۲۷)، دنبال متا تگ مربوط به open graph با عبارت og:image می‌گردیم که ساختار زیر رو داره (خط ۳۱) و سپس مقدار content اون رو استخراج می‌کنیم:&lt;meta property=&amp;quotog:image&amp;quot content=&amp;quothttps://66.media.tumblr.com/be97eb9f660e30c01b38b1bbbba1d9e6/tumblr_phi9bhxWnj1vyjf2do1_640.jpg&amp;quot&gt;ساخت endpointای برای دریافت فایل تصویردر نتیجه با اضافه کردن img/ در انتهای آدرس هر تصویر،‌می‌تونیم فایل اون تصویر رو در اختیار داشته باشیم.به سرعت بی‌نظیر اجرای این workerها توجه کنید.اضافه کردن دکمه دانلودحال می‌خواهیم به صفحه تصویر (صفحه تصویر در Archillect) یک دکمه دانلود اضافه کنیم. برای اینکار، مشابه قسمت قبلی نیاز است تا فایل HTML صفحه رو پروسس کنیم این‌بار از روش دوم استفاده می‌کنیم.فرم کلی استفاده از HTMLRewriter به صورت زیر است:new HTMLRewriter()
  .on(&#039;*&#039;, new ElementHandler())
  .onDocument(new DocumentHandler())
  .transform(res)برای استفاده از HTMLRewriter، یک instance جدید از این کلاس درست می‌کنیم و با استفاده از متد on و دادن یک سلکتور (Selector) به فرمت سلکتور‌های CSS و یک کلاس مثل ElementHandler به فرم زیر که باید یک یا چند متد element، coments و text به ترتیب برای هندل کردن المان‌های صفحه، کامنت‌ها و متن‌های صفحه پیاده‌سازی کنه یک متد خودمون برای اعمال تغییرات روی صفحه رو می‌سازیم:class ElementHandler {
   element(element) {
      // An incoming element, such as `div`
      console.log(`Incoming element: ${element.tagName}`)
   }
    comments(comment) {
      // An incoming comment
    }
    text(text) {
      // An incoming piece of text
    }
 }سپس با صدا کردن متد transform و دادن مقدار متن HTML خودمون تغییرات را اعمال می‌کنیم.نتیجه در تصویر زیر قابل مشاهده است.به دکمه دانلود در پایین سمت راست تصویر دقت کنید.اضافه کردن دکمه دانلودساخت endpoint دریافت ID آخرین تصویربرای داشتن endpointای که بتونیم با استفاده از اون ID آخرین عکسی که تا الان منتشر شده رو بگیریم، مشابه قسمت‌های قبل کافیست المان آخرین تصویر را پیدا کرده و شماره ID را از آن استخراج کنیم:دریافت ID آخرین تصویر به‌اشتراک گذاشته شدهمنابعتصویر پس‌زمینه: کاور از editorX و فونت از صابر راستی‌کردارمطالب مربوط به بخش «Edge Computing» از ارائه Steve Klabnik</description>
                <category>محمدحسن ستاریان</category>
                <author>محمدحسن ستاریان</author>
                <pubDate>Fri, 24 Jul 2020 19:50:22 +0430</pubDate>
            </item>
                    <item>
                <title>چای ۷: اجرای دستورات قبلی در ترمینال</title>
                <link>https://virgool.io/internerds/tea-7-terminal-history-ubiallmxbdri</link>
                <description>برای اینکه دستوراتی که قبلا توی shell اجرا کردیم رو دوباره اجرا کنیم معمولا history رو نگاه می‌کنیم:history | grep &lt;search_term&gt;اما روش‌های راحت‌تری هم برای این‌کار هستن که بررسی می‌کنیم:تمامی این اپراتور‌ها هم در bash و هم در zsh قابل استفاده هستند.اجرای دستور قبلیبرای اجرای آخرین دستوری که وارد کردیم می‌تونیم کلید بالا ⬆️ رو بزنیم تا دستور انتخاب بشه، اما اگر بخوایم از دستور قبلی توی دستور فعلی استفاده کنیم، می‌تونیم از از اپراتور !! استفاده کنیم:هنگام استفاده از کلید بالا ⬆️ اگر وسط نوشتن دستوری باشین، فقط بین دستوراتی که مشابه تا اینجای دستور نوشته شده هستن جستجو می‌کنه؛ برای دیدن دستورات قبلی بدون در نظر گرفتن شباهت از Ctrl+P استفاده کنین.pacman -S edex-ui
sudo !!
sudo pacman -S edex-uiیکی از کاربرد‌های زیاد این روش می‌تونه اضافه کردن sudo به ابتدای دستور قبلی باشه.اجرای دستور قبلی توسط اپراتور !!اجرای دستور nام historyاز اولین اجرای سیستم دستوراتی که توی ترمینال اجرا می‌شن توی history ذخیره می‌شن که با همین دستور هم لیست اون‌ها رو میشه دید. هرکدوم از این دستورات یک index دارن که همینطور نشون دهنده ترتیب اجرای اون‌ها هم هست. با دونستن شماره یک دستور و با کمک عملگر ! می‌شه اون دستور رو دوباره اجرا کرد:# history : 105 ping google.com
!105
ping google.comاجرای دستور nام historyجستجو در دستورهای قبلیجستو با حرف شروعبرای استفاده و اجرا دوباره آخرین دستوری که با حرف مثلا q شروع شده می‌تونیم از اپراتور q! استفاده کنیم:توجه کنید که q جزو اپراتور نبوده و جای اون هرکدوم از حروف رو می‌تونیم بذاریم.!s
serve -s . # last command used starting with &#039;s&#039;جستو با حرف شروع جستجو پویا در تاریخچه دستوراتبرای اینکه آخرین دستوری که یک عبارت داخلش استفاده شده، می‌تونیم با استفاده از ترکیب Ctrl+R، جستجو در دستورات قبل رو فعال کرده و عبارت مورد جستجو رو وارد کنیم:# press Ctrl+R
(zsh)    bck-i-search: _ 
(bash)  (reverse-i-search)`&#039;:برای خارج شدن از این حالت، از Ctrl+G یا Ctrl+Q استفاده کنین.جستجو پویا در تاریخچه دستورات جستجو پویا با استفاده از fzf‏fzf یک ابزار جستجوی فازی تحت خط فرمان هست که به کمک اون می‌تونیم خروجی هر دستوری رو به‌صورت فازی جستجو کنیم. می‌تونید fzf رو از مدیر بسته سیستم‌عاملتون نصب کنید.برای استفاده از fzf برای جستجو در history به‌صورت زیر عمل می‌کنیم:history | fzf +s --tacجستجو پویا با استفاده از fzf استفاده از آرگومان‌های دستور قبلاستفاده از آخرین آرگومان دستور قبلبرای تنها استفاده از آخرین آرگومان دستور قبلی می‌تونیم از اپراتور $! استفاده کنیم:echo arg1 arg2 arg3
cd !$
echo arg3استفاده از آخرین آرگومان دستور قبل استفاده از اولین آرگومان دستور قبلبرای استفاده از اولین آرگومان دستور قبلی می‌تونیم از اپراتور ^! استفاده کنیم:echo arg1 arg2 arg3
cd !^
echo arg1استفاده از اولین آرگومان دستور قبل استفاده از دستور قبلی بدون آرگومانبرای استفاده از دستور قبلی بدون آرگومان‌هاش می‌تونیم از اپراتور #! استفاده کنیم:echo arg1 arg2 arg3
cd !#
echo echoاستفاده از دستور قبلی بدون آرگوماناین پست، قسمت هفتم از چای، مجموعه‌ای در باب «چیزی که امروز یادگرفتم» است. باقی چای‌ها رو می‌تونید از اینجا مشاهده کنید و در مورد فلسفه‌ی این کار بخونید.</description>
                <category>محمدحسن ستاریان</category>
                <author>محمدحسن ستاریان</author>
                <pubDate>Mon, 13 Jul 2020 23:10:03 +0430</pubDate>
            </item>
                    <item>
                <title>تبدیل توییت به عکس با استفاده از توابع بدون سرور Netlify</title>
                <link>https://virgool.io/internerds/tweet2img-euitzv4omwr6</link>
                <description>هدف نهایی این پست یا درواقع چیزی که در انتها خواهیم ساخت، یک سرویس تبدیل توییت به عکسه که احتمالا بتونه اشتراک‌گذاری توییت‌ها رو در شبکه‌های اجتماعی دیگه یا آرشیو کردن اون‌ها رو راحت‌تر کنه. اما، جذاب‌تر و مهم‌تر از نتیجه نهایی، نحوه ساخت این سرویسه که در ادامه به تفصیل توضیح داده خواهد شد.خلاصه (TL;DR)برای ساخت این سرویس از Puppeteer یک مرورگر وب بدون واسط (Headless Browser) برای رندر یک صفحه وب (المان Embed توییتر) و تبدیل اون به عکس استفاده می‌کنیم که داخل یک تابع بدون سرور (Serverless Function) اجرا می‌شه. در عین حال، به هیچ احراز هویت و API Key احتیاج نداریم و هیچ هزینه‌ای هم پرداخت نمی‌کنیم. توی این پروژه تماما از جاوا اسکریپت (Javascript) استفاده می‌کنیم، سمت فرانت (Front-end) که البته خیلی وارد جزئیاتش نمی‌شم از فریم‌ورک Svelte، سمت بک (Back-end) هم از Nodejs برای توسعه یک تابع بدون سرور استفاده می‌کنیم که در نهایت روی Netlify دپلوی (مستقر [deploy]) میشه.در ادامه، ابتدا کمی سرویس و معماری بدون‌سرور رو بررسی می‌کنیم، برنامه رو به‌صورت محلی (local) توسعه می‌دیم و تست می‌کنیم و در انتها روی سرویس Netlify مستقر (deploy) می‌کنیم.شمای کلی پروژهقبل از ادامه و بررسی پروژه، می‌تونید نتیجه نهایی رو در tweet2img و کد‌ها رو در ریپازیتوری گیت‌هاب ببینید.لازم به ذکره قسمت‌هایی از این آموزش از راهنمایی‌های این ویدئو از Wes Bos کمک گرفته شده: https://youtu.be/A0Ww-SU7K5E مقدمهپردازش ابری (Cloud Computing)معماری‌های مختلفی در طول سال‌ها برای توسعه سیستم‌های پردازشیِ ابری معرفی و ارائه شده‌اند که یکی از هدف‌های اون‌ها مثل بخش‌های دیگه محاسبات کامپیوتری، تقسیم و جدا کردن سیستم به بخش‌های مستقل و قابل مدیریت است که در نتیجه اون توسعه‌دهندگان وارد جزئیات کمتری برای استقرار (دپلوی [deploy]) سرویس‌های خودشون بشن. در نتیجه سرویس‌های ابری مختلفی با سطوح انتزاع مختلفی برای استفاده موجود هستند، کاربر می‌تواند پایین‌ترین سطح انتزاع را انتخاب کرده و جزئیات سرور مثل میزان فضای ذخیره‌سازی و مصرف حافظه را کنترل کند و یا تنها در سطح مدیریت‌شده‌تری قرار گرفته و تنها درگیر انتخاب سیستم‌عامل و نصب نرم‌افزار‌های مورد نیاز خود داخل یک ماشین مجازی (Virtual Machine) شود.معماری‌های پردازش ابری و موارد مورد نیاز به رسیدگیتوابع بدون سرور (Serverless Functions)xkcd 927تابع به‌عنوان سرویس ([Function As A Service [FAAS) دسته‌بندی‌ای از سرویس‌های پردازش ابری‌ست که اپلیکیشن‌های ساخته شده توسط این مدل، از معماری بدون سرور استفاده می‌کنند. بدون سرور (Serverless) یک معماری و مدل اجرا (Execution Model) پردازش ابری است که در اون ارائه دهنده سرویس ابری (Cloud Provider) سرور را مدیریت کرده و به‌صورت پویا (dynamically) منابع مورد نیاز سرور را اختصاص میده. بنابراین توسعه دهنده می‌تواند تنها روی توسعه کد خودش تمرکز بکنه و ارائه دهنده سرویس باقی جزئیات را مدیریت می‌کنه.بدون سرور به این معنی نیست که هیچ سروری اجرای کد‌ها رو برعهده نداره -که عملا منطقی نیست- اما به این منظوره که پیچیدگی‌های ایجاد و نگهداری سرورها حذف شده و تنها ماهیت و کد لازم برای اجرای سرویس نیازمند پیاده‌سازی‌ست.از دیگر مزیت‌های این مدل اجرا، کاهش هزینه‌های استفرار و ارائه سرویس‌ها و همچنین استفاده بهینه از منابع سروره، چون که کاربر هزینه‌ای برای زمان‌هایی که سرور بی‌کار (idle) است پرداخت نمی‌کنه.ارائه‌دهنده‌های اصلی سرویس‌های ابریتقریبا تمامی ارائه‌ دهندگان اصلی سرویس‌های ابری سرویس‌های FAAS ارائه می‌دهند؛ در اکثر اون‌ها برای استفاده از این مدل اجرا نیاز است تا اپلیکیشن باندل (bundle) شده و در قالب یک فایل Zip برای اجرا روی سرویس قرار گیرد.در این پروژه از سرویس Netlify برای دپلوی برنامه استفاده می‌کنیم که سرویس تابع بدون سرور اون (Netlify Functions) از سرویس AWS Lambda آمازون استفاده می‌کنه. همچنین، امکاناتی برای تست لوکال (محلی [local]) توابع -بدون سرور- و باندل کردن، دپلوی و ورژن‌بندی اون‌ها در اختیار میذاره که به‌شدت توسعه پروژه رو ساده می‌کنه.صورت کلی یک تابع بدون سرور سرویس Netlify به‌صورت زیره:// functions/foo.js
exports . handler = async ( event , context ) =&gt; {
    return {
        statusCode : 200 , 
        body : “ We are now split 
    }; 
}این تابع هروقت ریکوئست‌ای (درخواست [request]) به آدرس مشخص شده اون (اینجا: img/) ارسال بشه، اجرا می‌شه و در نهایت هر مقداری برگردانده (return) شود به‌عنوان جواب ریکوئست ارسال می‌شه. در پلن رایگان، Netlify نهایتا ۱۰ ثانیه فرصت اجرا به هر تابع می‌دهد. ساخت خودکار تصویرپروژه Ronin از 100r.coبرای ساخت سرویس تبدیل توییت به عکسمون، نیاز داریم تا بتونیم به‌صورت برنامه‌نویسی شده یک تصویر بسازیم؛ برای این کار روش‌های مختلفی وجود داره که بیشتر محدود به زبان انگلیسی (به دلیل عدم پشتیبانی از فونت فارسی)، کُند و یا بدون امکانات شخصی‌سازی هستن. همچنین روش‌هایی نظیر استفاده از Context API با وجود قدرت و سرعت زیاد، به دلیل تفاوت محتوی توییت‌ها، مشخص نبودن طول توییت -نه نهایت طول- و همینطور تصاویر، پیش‌نمایش لینک‌ها و توییت‌های ضمیمه شده آن، مشخص‌نیست و نیاز به محاسبه نسبتا پیچیده داره، رسیدن به نتیجه مطلوب اصلا راحت نیست. یکی از روش‌هایی که مدتی‌ست مرسوم شده و به نسبت بسیار ساده است، استفاده از قدرت بی‌نظیر HTML و CSS برای ساخت المان‌های دیداری و سپس تبدیلش به تصویره؛ استفاده از این روش، قدرت شخصی سازی بی‌نظیری به ما میده و در عین حال سریع و قابل اتکاست.چیدمان تصویر با استفاده از Puppeteerبرای اینکه تصویر را با استفاده از تکنولوژی‌های وب بسازیم، نیاز به ابزاری داریم تا بتونه عملیات رندر (چیدمان [Render]) صفحه وب متشکل از HTML و CSS مورد نظر رو انجام بده و توانایی ارائه خروجی تصویر هم داشته باشه. برای اینکار از یک مرورگر وب بدون واسط (Headless Browser) به نام Puppeteer استفاده می‌کنیم که امروزه بسیار پر استفاده است.مرورگر وب بدون واسط (Headless Browser)یک مرورگر بدون واسطه، یک مرورگر وب بدون رابط گرافیکی و با قابلیت کنترل بالا و خودکار است که امکان پردازش و رندر یک صفحه وب همانند یک مرورگر عادی را در محیطی قابل برنامه‌ریزی و مدیریت شده می‌دهد.دو نمونه از ابزارهای کنترل و کار با این مرورگر‌ها PhantomJS و Puppeteer هستند که هر دو از موتور V8 کرومیوم استفاده می‌کنند. همینطور مرورگر کروم نصب شده روی سیستم نیز می‌تواند در حالت بدون واسط اجرا شود.‏Puppeteer این امکان رو میده تا داخل کد جاوا اسکریپت خودمون یک مرورگر کروم اجرا کرده، صفحه مورد نظر رو داخل اون رندر کرده و از اون یک اسکرین‌شات (Screenshot) ذخیره کنیم.آماده سازیهمون‌طور که بالاتر گفته شد، در این پروژه از nodejs استفاده می‌کنیم. اگر اون رو نصب ندارید می‌تونید از سایت اصلی Nodejs و یا با استفاده از مدیر بسته (package manger) سیستم‌عاملتون دانلود و نصب کنید.اگر اطمینان ندارید، ورژن LTS را نصب کنید، همچنین پیشنهاد می‌کنم از یک مدیر نسخه (version manager) برای nodejs مثل nvm یا n استفاده کنید:اگر از لینوکس استفاده می‌کنید و node رو نصب ندارید، انتخاب خوبیه که از اول با نصب یکی از این دو اقدام به نصبشون بکنید. برای مثال با این دستور زیر n رو نصب کنید:                   curl -L git.io/n-install | bashپس از نصب با اجزای دستورات زیر تو ترمینال از درستی نصبشون مطمئن بشید:node -vnpm -vهمچنین برای این پروژه لازم است در Netlify ثبت‌نام کرده و یک تیم بسازید: سایت Netlifyساخت پروژه و مفاهیم اولیهبرای شروع توی یک دایرکتوری جدید یک پروژه ایجاد کنید. از اونجایی که در این پروژه از فریمورک svelte استفاده می‌کنم،‌ پروژه خودم (با نام tweet2img) رو از template پیش‌فرض خودشون با استفاده از degit که یک ابزار scaffolding هست می‌سازم:npx degit sveltejs/template tweet2imgتوجه کنید که هیچ لزومی به استفاده از svelte نیست و پروژه خودتون رو به‌هر صورتی که تمایل دارید بسازید.با وارد کردن این دستور، پروژه‌ای با ساختار تصویر زیر ساخته می‌شه:ساخت پروژهحالا، وارد دایرکتوری پروژه شده و با استفاده از دستور زیر پکیج‌های مورد نیاز پروژه (dependencies) رو نصب می‌کنیم:npm iهمچنین می‌تونیم با استفاده از دستور npm start پروژه رو اجرا و مشاهده کنیم.آماده‌سازی netlify-cliبرای اینکه ‌بتونیم توابع بدون سرور خودمون رو به‌صورت لوکال تست کنیم و در نهایت مستقیم ‌اون‌ها رو منتشر کنیم، نیاز به ابزاری داریم تا بتونه مشابه محیط اجرای نهایی (پس از انتشار) اون‌ها رو اجرا بکنه. برای اینکار Netlify ابزاری تحت ترمینال به نام netlify-cli دارد. این پکیج رو به‌صورت عمومی (global) روی سیستم نصب می‌کنیم:npm i -g netlify-cliبا اجرای این دستور netlify-cli به‌صورت عمومی روی سیستم نصب می‌شود و از هر دایرکتوری‌ای می‌تونیم اون رو اجرا کنیم. در اولین استفاده لازم است با استفاده از دستور زیر احراز هویت کنیم:netlify loginبا اجرای این دستور اگر در netlify لاگین باشین صفحه‌ای مشابه تصویر زیر باز می‌شود، روی دکمه Authorize کلیک کنید:احراز هویت در netlifyپس احراز هویت، netlify اطلاعات مربوط به کاربر فعلی را در آدرس زیر ذخیره می‌کند:~/.netlify/config.jsonبرای مطالعه بیشتر در مورد احراز هویت و تغییر توکن و موارد دیگه این قسمت از مستندات رو بخونید.اجرای اولیه پروژهحالا می‌توانیم وارد دایرکتوری پروژه شده و با استفاده از دستور زیر پروژه رو به‌صورت لوکال اجرا کنیم:netlify devبا اجرای این دستور، netlify-cli به‌صورت خودکار نوع پروژه و دستور لازم برای اجرای اون رو تشخیص داده و پروژه رو روی پورت 8888: اجرا می‌کنه.لازم به ذکره که netlify پروژه رو با استفاده از دستور npm start روی پورت پیش‌فرض دستور که اینجا 5000: هست اجرا می‌کنه و بعد یک پروکسی به اون پروژه توی پورت 8888: ایجاد می‌کنه تا با اینکار آدرس‌های مربوط به توابع بدون سرور رو مدیریت کنه؛ همینطور بتونه تغییرات فرانت پروژه و تغییرات توابع رو به‌صورت مجزا اجرا و اعمال کنه.حال با مشاهده آدرس localhost:8888 توی مرورگر می‌تونیم نمایی اولیه از پروژه داشته باشیم:اجرای پروژه اولیهتنظیمات Netlifyبرای شخصی‌سازی و تنظیم Netlify از یک فایل TOML استفاده می‌کنیم. برای اینکار در root پروژه یک فایل با نام netlify.toml بسازید:ساخت فایل تنظیمات Netlifyسپس کدهای زیر را داخل فایل کپی کنید. با قرار دادن این کدها، موارد زیر را برای زمان ساخت پروژه (مشخص شده توسط [build]) مشخص می‌کنیم:دایرکتوری اصلی پروژه که لازم است serve بشود دایرکتوری public است.توابع بدون سرور ما در دایرکتوری api قرار خواهند گرفت.برای ساخت پروژه لازم است از دستور npm run build استفاده شود.[build]
  publish = &amp;quotpublic&amp;quot
  functions = &amp;quotapi&amp;quot
  command = &amp;quotnpm run build&amp;quotاگر برخلاف این پروژه از هیچ فرم‌ورکی استفاده نمی‌کردین و نیازی به هیچ دستوری برای ساخت (build) پروژه نبود مقدار اون رو برابر # قرار بدین.ساخت اولین تابع بدون سرورساختار توابع بدون سرور به این صورته که هر دایرکتوری داخل دایرکتوری‌ای که برای توابع بدون سرور تنظیم کردیم (اینجا api/.) یک endpoint به آدرس اسم اون دایرکتوری خواهد ساخت که اسکریپت js با همون اسم رو اجرا می‌کنه.برای مثال با ساختن دایرکتوری و اسکریپت زیر:./api/random/random.jsیک endpoint به آدرس زیر خواهیم داشت که نتیجه اجرای اسکریپت random.js رو با هر ریکوئست پاسخ میده./.netlify/functions/randomمشابه توضیح بالا دایرکتوری و فایل random رو در  root پروژه می‌سازیم:ساخت api با نام randomهدف این تابع این است تا با هر ریکوئست یک عدد تصادفی (random) به عنوان ریسپانس برگرداند. این تابع رو به‌صورت زیر می‌نویسیم:// api/random/random.js
exports.handler = async (event, context) =&gt; {
    return {
        statusCode: 200,
        body: Math.random().toString(),
    };
};
حال می‌توانیم با وارد کردن آدرس زیر در مرورگر نتیجه اجرای این دستور رو مشاهده کنیم:http://localhost:8888/.netlify/functions/randomایجاد اعداد تصادفی با استفاده از تابع بدون سرور randomدریافت پارامتر‌ها و متد درخواستبه ورودی‌های تابعی که در کد خودمون export کردیم دقت کنید. این ورودی‌ها اطلاعات لازم درباره درخواست ارسال شده و محیط اجرای تابع رو در اختیار ما می‌ذارن. شکل کلی اون‌ها به‌صورت زیره:exports.handler = async (event, context, callback) =&gt; {}پارامتر callback برای ارسال response استفاده می‌شده که ما در مثال قبل تنها با return کرد یک object اینکار رو انجام دادیم. بجای return کردن میشه از تابع callback به‌صورت زیر استفاده کرد:exports.handler = function(event, context, callback) {
    callback(null, { // null indicates no error
        statusCode: 200,
        body: &amp;quotHello, World&amp;quot
    });
}ورودی event، اطلاعات زیر رو در اختیار ما می‌ذاره:// whats inside event parameter:

{
    &amp;quotpath&amp;quot: &amp;quotPath parameter&amp;quot,
    &amp;quothttpMethod&amp;quot: &amp;quotIncoming request&#039;s method name&amp;quot
    &amp;quotheaders&amp;quot: {Incoming request headers}
    &amp;quotqueryStringParameters&amp;quot: {query string parameters }
    &amp;quotbody&amp;quot: &amp;quotA JSON string of the request payload.&amp;quot
    &amp;quotisBase64Encoded&amp;quot: &amp;quotA boolean flag to indicate if the applicable request payload is Base64-encode&amp;quot
}بنابراین با استفاده از event.httpMethod می‌تونیم نوع درخواست ارسال شده رو کنترل کنیم و برای مثال تنها به‌ درخواست‌های POST پاسخ بدیم و با استفاده از event.queryStringParameters می‌تونیم پارامتر‌های ارسال شده توی URL رو به‌صورت یک Object در اختیار داشته باشیم.برای نمونه اسکریپت greet رو در آدرس زیر می‌سازیم:./api/greet/greet.jsو کد زیر رو توی اون قرار بدید:exports.handler = async (event, context) =&gt; {
    const { name } = event.queryStringParameters;
    return {
        statusCode: 200,
        body: `Hello, ${name || &amp;quotworld&amp;quot}!`,
    };
};تغییرات داخل فایل توابع به‌صورت آنی بعد از ذخیره شدن فایل، اعمال می‌شن اما با ساخت تابع جدید نیاز به اجرای دوباره دستور netlify dev است. بنابراین دستور را دوباره اجرا کنید تا endpoint این تابع ساخته شود.حال با وارد کردن آدرس زیر و قرار دادن یک اسم در انتها می‌تونید پیامی حاوی اون اسم رو برگردونید:http://localhost:8888/.netlify/functions/greet?name=اگر اسمی فرستاده نشود بجای آن world قرار می‌‌گیرد.اجرای تابع greetتغییر آدرس توابعاجرای توابع با همچین آدرس‌هایی نه قشنگه و نه راحت. برای همین لازم داریم درخواست‌ها رو از یک آدرس ساده‌تر redirect کنیم به این آدرس. برای مثال مشخص کنیم که هر ریکوئست که به random/ رسید اون رو به netlify/functions/random./ پاس بده:/random  --&gt;    /.netlify/functions/randomبرای اینکار Netlify با استفاده از یک فایل اجازه مشخص کردن ریدایرکت‌ها رو به ما میده:داخل دایرکتوری public یک فایل به نام redirects_ می‌سازیم و داخل اون در هر سطر مشخص می‌کنیم با دریافت ریکوئست به هر آدرس، به کدام آدرس و با کدام status code ریدایرکت شویم:این موارد رو با space از هم جدا کرده و در هر سطر یک مورد را وارد می‌کنیم./random /.netlify/functions/random 200
/greet /.netlify/functions/greet 200حال با وارد کردن آدرس‌های زیر می‌تونیم توابع رو اجرا کنیم:http://localhost:8888/randomhttp://localhost:8888/greetنصب پکیج و استفاده در تابع بدون سرورهمانطور که در مقدمه پست اشاره شد، سرویس‌های ارائه دهنده سرویس FAAs،‌ توابع بدون سرور رو به‌صورت یک فایل bundle شده به فرمت zip دریافت و اجرا می‌کنند. در اینجا عملیات باندل کردن و آماده سازی توابع رو netlify-cli با استفاده از webpack به‌صورت خودکار انجام میده. به‌صورت دقیق‌تر، از ورژن ۲.۷ عملیات باندل کردن به‌صورت خودکار انجام می‌شه و قبل از اون باید از ابزارهایی مثل netlify-lambda استفاده می‌شد.در اینجا هم اگر می‌خواهید از import بجای require استفاده کنید نیاز است تا با استفاده از netlify-lambda و تنظیم اون تابع رو build کنید.بنابراین می‌تونیم بدون اعمال تنظیمات خاصی از پکیج‌های شخص‌ثالث (third-part packages) در پروژه خودمون استفاده کنیم.برای تست این مورد یک تابع جدید با نام quote در آدرس زیر بسازید:./api/quote/quote.jsو ترمینال را در این آدرس باز کرده و با استفاده از دستور زیر یک پکیج Initialize کنید:npm init -yبا اجرای این دستور فایل package.json در دایکتوری تابع ساخته شده و می‌تونیم پکیج‌هایی که لازم داریم رو نصب کنیم. در این تابع می‌خوام از جملات master Yoda استفاده کنم. برای همین پکیج yodaquotes رو نصب می‌کنم:npm i yodaquotesبعد از نصب این پکیج، می‌تونم از اون توی تابع خودم استفاده کنم:const yodaQuotes = require(&amp;quotyodaquotes&amp;quot);

exports.handler = async (event, context) =&gt; ({
    statusCode: 200,
    body: yodaQuotes(),
});مجددا بعد از ساخت این تابع دستور netlify dev رو ری‌استارت کنید و مقادیر لازم برای ری‌دایرکت کردن تابع رو در فایل redirects_ قرار بدین.الان با وارد کردن آدرس زیر می‌تونیم از دستورات یودا بهره‌مند بشیم:دریافت یک quote از Yodaبالاخرهتبدیل توییت به عکس با استفاده از puppeteerتوابعی که در قسمت‌های قبل درست کردیم صرفا جنبه آموزشی داشتن تا ببینیم چطوری توابع بدون سرور بسازیم و از اون‌ها استفاده کنیم. اما بالاخره رسیدیم به هدف اصلی این پست، که تبدیل توییت به عکسه.همونطور که قبلا اشاره شد برای تبدیل توییت به عکس از puppeteer استفاده می‌کنیم. برای این کار المان embed یک توییت رو با استفاده از Oembed API توییتر دریافت می‌کنیم.اگر سعی کرده باشید از twitter یک API key بگیرید احتمالا می‌دونید که پروسه راحت و سریعی نیست و چندین مرحله پرسش و پاسخ رو طی می‌کنید که ممکنه تا چند روز طول می‌کشه. اما ما اینجا از Oembed API استفاده می‌کنیم که یک API عمومی و رایگان هست.این API به این صورت عمل می‌کنه که با دریافت آدرس یک توییت، اطلاعات لازم برای embed کردن توییت شامل یک کد HTML مربوط به اون رو ارائه میده:نتیجه Oembed API توییتراگر این کد HTML خروجی‌ای مشابه تصویر زیر رندر می‌کنه:المان embed یک توییت از Fermat&#039;s Libraryما از این المان استفاده کرده و با استفاده از puppeteer از اون یک اسکرین‌شات می‌گیریم.ایجاد و راه‌اندازی پروژهبرای شروع مشابه آخرین مثال قسمت قبل، یک تابع جدید (مثلا img) می‌سازیم و یک پکیج initialize می‌کنیم و پکیج‌های لازم رو نصب می‌کنیم که در ادامه بررسی می‌شن:cd api/img
npm init -y
npm i chrome-aws-lambda puppeteer-core node-fetch waaitدر اینجا دلیل اینکه چرا خود puppeteer رو نصب نکرده و puppeteer-core رو نصب کردیم به این خاطره که با نصب puppeteer، همراه خودش یک مرورگر کروم رو هم نصب می‌کنه که ما احتیاجی نداریم. به عنوان مرورگر ما از chrome-aws-lambda استفاده می‌کنیم که یک نسخه باینری از مرورگر کروم است که برای اجرا در محیط‌های aws lambda آمازون و cloud functions گوگل بهینه و آماده شده.همونطور که قبلا اشاره شد، Netlify از سرویس‌های aws آمازون استفاده می‌کنه.پکیج‌های node-fetch و waait پکیج‌های دلخواهی هستند که احتیاجی بهشون نداریم اما برای راحتی کار ازشون استفاده کردم. می‌تونید این پکیج‌هارو نصب نکرده یا از پکیج‌های مشابهشون استفاده کنید.کد اولیه اسکرین‌شاتبرای اسکرین‌شات گرفتن، از کد زیر از Leigh Halliday استفاده کردم که نحوه استفاده از کروم رو در اجرای لوکال و هنگام اجرا روی سرور تفکیک کرده: https://gist.github.com/mhsattarian/c420dddc9390d641ed962ce4ec2d7c36 در این کد توی تابع getOptions با پارامتر isDev مشخص می‌شود که درحال اجرای کد در لوکال هستیم و یا روی سرور که بر اساس اون مروگر کروم نصب شده روی سیستم (در حالت بدون واسطه [headless] و مخفی [incognito]) و یا chrome-aws-lambda به‌عنوان مرورگر مورد استفاده انتخاب می‌شود.تابع getScreenshot هم وظیفه اجرای مرورگر (تابع launch)، باز کردن صفحه به آدرس مورد نظر (تابع goto و پارامتر url) و اسکرین شات گرفتن از آن (تابع screenshot) را بر عهده دارد. همینطور، با استفاده از تابع setViewPort مشخص می‌کنیم که document در هنگام لود صفحه چه اندازه‌ای باشه و از آرگومان deviceScaleFactor برای این استفاده کردیم که اندازه document بزرگتر باشه و تصاویر با کیفیت‌تری در هنگام اسکرین‌شات گرفتن خروجی بگیریم.اعمال تغییرات۱. از اونجایی که ما می‌خواهیم المان Embed توییت به‌صورت HTML رو استفاده کنیم و همچنین نه از کل صفحه که از یک المان خاص احتیاج‌داریم اسکرین‌شات گرفته بشه، کد رو به‌صورت زیر تغییر میدیم: https://gist.github.com/mhsattarian/8f380c851b2eaaae11b299f330f66670 در اینجا بجای استفاده از متد page.goto از تکه کد زیر برای رندر یک تکه کد HTML استفاده کردیم:بالاتر توضیح داده‌شد که این تکه HTML قرار است از سرویس Oembed توییتر دریافت شود.page.setContent(html, 
    { waitUntil: [&amp;quotnetworkidle0&amp;quot, &amp;quotdomcontentloaded&amp;quot], }
);که در این کد مشخص کردیم مقدار متغیر html رو رندر کرده و تا هنگام لود تمامی المان‌ها صبر کن.۲. تغییر دیگر، استفاده از متد page.evaluate برای اعمال یک اسکریپت جاوا اسکریپت درون صفحه رندر شده است. با استفاده از این تابع می‌توانیم یک اسکریپت دلخواه را درون صفحه اجرا کرده و خروجی‌ای از آن دریافت کنیم. در اینجا از این تابع برای محاسبه مختصات باکس المان embed با کمک متد getBoundingClientRect استفاده کردیم تا بعدا از اون برای مشخص کردن محدوده مورد نظر برای اسکرین‌شات گرفتن استفاده کنیم. نکته جانبیتوییتر پیش از این از web components در المان خود استفاده می‌کرد و المان‌هایی که نیاز به استایل‌دهی آن‌ها داشتیم، در shadow root قرار می‌گرفتند. استایل دهی به این المان‌ها از طریق css به راحتی ممکن نبود و به همین دلیل می‌بایست که از متد element.shadowRoot برای دریافت المان‌های صفحه استفاده می‌کردیم.اما حدود یک ماه پیش با تغییر المان embed خودشون و استفاده از iframe تا حدودی این کار -در روش استفاده ما- راحت‌تر شده.ساخت تابع بدون سرور و نحوه استفاده الان که نحوه اسکرین‌شات رو بررسی کردیم می‌تونیم تابع بدون سرور خودمون رو بنویسیم. برای اینکار فایل اسکریپتی که نوشتیم (api/img/img.js/.) رو باز می‌کنیم و محتوی کد بالا و سپس کد زیر رو وارد می‌کنیم: https://gist.github.com/mhsattarian/447f6d0105e6652f344a01879e16e083  آدرس توییت مورد نظر رو از پارامتر url دریافت کرده (خط ۶) و با استفاده از Oembed API توییتر کد HTML المان رو دریافت می‌کنیم (خط ۸).سپس مقدار Environment Variable ای با نام CHROME رو می‌خونیم تا با استفاده از اون مشخص کنیم آیا در اجرای محلی هستیم یا اجرای روی سرور (خط ۱۳).برای اینکه این مقدار در اجراهای لوکال مشخص شود، در فایل package.json به صورت زیر اسکریپتی برای اجرای پروژه می‌نویسیم:نوشتن اسکریپت برای شروع پروژههمینطور پکیج cross-env رو هم به‌صورت زیر نصب می‌کنیم:npm i cross-envبا استفاده از تابع getScreenshot ای که نوشتیم نتیجه اسکرین‌شات رو به‌صورت بافر (Buffer) دریافت می‌کنیم (خط ۱۴).در نهایت مثل قبل یک Object را return می‌کنیم که به عنوان body بافر عکس که تبدیل به Base64 شده را قرار میدیم. همینطور با پراپرتی isBase64Encoded مشخص می‌کنیم که مقدار body یک فایل به فرمت base64 است (خط ۱۶).نتیجه اجرای تمامی این مراحل، تابعی است که با دریافت آدرس یک توییت در پارامتر url اش می‌تونه اون رو تبدیل به تصویر کرده و خروجی رو برگردونه. برای مثال آدرس یک توییت رو در انتهای آدرس زیر قرار بدین:http://localhost:8888/img?url=نتیجه تبدیل توییت به عکسدر انتها، برای انتشار و استقرار سایتتون می‌تونید از دستور زیر استفاده کنید:netlify deploy --prodبا استفاده از این دستور، پروژه شما build شده و با نامی که انتخاب کردید منتشر خواهد شد.خلاصهتولید عکس با استفاده از تکنولوژی‌های وب انعطاف‌پذیری زیادی به ما میده در عین‌حال به‌شدت راحت و سریعه. پروژه‌های زیادی از همین روش در دنیای وب برای تولید تصویر، thumbnail و داده‌های مشابه استفاده می‌کنند. همینطور سرویس‌هایی مانند HCTI هستند که API برای اتوماتیک کردن همین پروسه در اختیار می‌گذارن. توابع بدون سرور هم امکان پیاده‌سازی سرویس‌های سمت بک رو -بیشتر مواقع- در راحت‌ترین شرایط فراهم می‌کنن که سرویس‌های بسیاری بجز Netlify برای استفاده رایگان از این مدل اجرا مثل Vercel و Begin وجود دارن.امیدوارم جالب بوده باشه براتون.دیگر منابعتصویر پس‌زمینه:  کاور از editorX و فونت از صابر راستی‌کردارنمودار ابتدای پست، ساخته شده به کمک cloudcraft.coمقایسه معماری‌های پردازش ابری، ساخته شده به کمک excalidrawتصویر سازی بخش «توابع بدون سرور»، از xkcdنمودار ارائه‌دهندگان سرویس‌های ابری از وبلاگ maxkelsenتصویر بخش «ساخت خودکار تصاویر» از پروژه Ronin از 100r.coتصویر بخش «چیدمان تصویر با استفاده از Puppeteer» از buddy.worksپاراگراف اول توضیح «مرورگر وب بدون واسط» از ویکی‌پدیاتوییت نمایش داده‌شده در ابتدای بخش «بالاخره، تبدیل توییت به عکس...» از Fermats Libraryتوییت تبدیل شده به عکس در تصویر انتهایی از Sam Altman</description>
                <category>محمدحسن ستاریان</category>
                <author>محمدحسن ستاریان</author>
                <pubDate>Thu, 09 Jul 2020 22:29:22 +0430</pubDate>
            </item>
                    <item>
                <title>چای ۶: پنهان‌کردن عمدی فایل‌ها در لینوکس</title>
                <link>https://virgool.io/internerds/tea-6-linux-hide-file-directory-ebwrtwome0ru</link>
                <description>همونطور که می‌دانیم، در فایل سیستم لینوکس، فایل‌هایی که با . (نقطه [Dot]) شروع می‌شوند، پنهان یا به اصطلاح hidden هستند؛ بدین معنی که با مشاهده دایرکتوری (فولدر) شامل این فایل‌ها توسط برنامه‌های مدیریت فایل (File managers) و یا استفاده از دستوراتی مانند ls، آن‌ها نمایش داده و یا پروسس نمی‌شوند.هرچند طبیعتا با فعال کردن نمایش فایل‌های پنهان در برنامه‌های مدیریت فایل توسط Ctrl+H و یا استفاده از دستور ls توسط فلگ ‎-a، این فایل‌ها قابل مشاهده می‌شوند.برای آنکه فایل‌ها و حتی دایرکتوری‌هایی که امکان تغییرنام آن‌ها (اضافه کردن . ابتدای نام) وجود ندارد را پنهان کنیم، می‌توانیم در کنار آن فایل/دایرکتوری یک فایل با نام «‎.hidden» ایجاد کرده و اسم فایل/دایکتوری‌ها را داخل آن به‌صورت یک عنوان در هر سطر می‌نویسیم:پنهان کردن دایرکتوری n فرمت فایل ‎.hidden توسط برنامه‌های پیش‌فرض مدیریت فایل دسکتاپ‌های Gnome ،KDE و Cinnamon یعنی Nautilus و Dolphin و Nemo پشتیبانی می‌شود.منبعاین پست، قسمت ششم از چای، مجموعه‌ای در باب «چیزی که امروز یادگرفتم» است. باقی چای‌ها رو می‌تونید از اینجا مشاهده کنید و در مورد فلسفه‌ی این کار بخونید.</description>
                <category>محمدحسن ستاریان</category>
                <author>محمدحسن ستاریان</author>
                <pubDate>Tue, 16 Jun 2020 23:21:46 +0430</pubDate>
            </item>
                    <item>
                <title>چای ۵: اسکریپت نویسی Shell، بازه‌ها در حلقه for</title>
                <link>https://virgool.io/internerds/tea-5-shell-range-wnicbipm7yft</link>
                <description>به عنوان آدمی که ذره‌ای علاقه به طراحی و گرافیک داره، هیچوقت مخالف رابط‌های گرافیکی (GUI) نبوده و نیستم، اما انجام خیلی از کارها با استفاده از CLI به دلیل قابل برنامه‌نویسی بودن، به میزان بسیار زیادی راحت‌تر و سریع‌تره به‌خصوص کارهایی که قاعده‌مند و قابل تکرار هستند، مثل استفاده از GIT، ایجاد دایکتوری‌های تو‌درتو، تبدیل فایل‌ها و تحت تاثیر قرار دادن افراد!در این قسمت از چای، خیلی کوتاه با استفاده از چند مثال، با عملگر بازه (range operator) در اسکریپت نویسی shell آشنا می‌شیم.حلقه forدر حالت عادی نوشتن یک حلقه for در shell script به‌صورت زیر است:#!/bin/bash
# Basic for loop

for (( c=1; c&lt;=5; c++ ))
do  
   echo $c
doneکه می‌توان آن را در یک خط به‌صورت زیر نوشت:❯ for ((c=1;c&lt;=5;c++)); do echo $c; doneبا اجرای این حلقه اعداد ۱ تا ۵ به‌صورت زیر چاپ می‌شوند:1 
2 
3 
4 
5حلقه forعملگر بازه (range)عملگر بازه‌ها نوشتن حلقه‌های for را ساده‌تر کرده و حتی این اجازه را می‌دهند که بدون نوشتن حلقه for، دستورات را تکرار کرده و نوشتن برنامه‌های تک-خطی (One-liner program) رو بسیار ساده‌تر می‌کنه. فرمت و نحوه استفاده بازه‌ها به‌صورت زیر است:# range operator (pseudo code)
{start..end..step}به مثال‌های زیر توجه کنید:چاپ اعداد بین ۱ تا ۱۰# print numbers from 1 to 10
❯ for i in {1..10}; do echo $i; done
1 
2 
3 
4 
5 
6 
7 
8 
9 
10چاپ اعداد فرد بین ۱ تا ۱۰# print odd numbers from 1 to 10
❯ for i in {1..10..2}; do echo $i; done
1 
3 
5 
7 
9استفاده از بازه‌ها بدون حلقه forهمانطور که گفته شد، از عملگر بازه‌ها می‌توان خارج از حلقه for نیز استفاده کرد؛ اینکار انعطاف‌پذیری زیادی به دستورات میده و اجازه میده بدون نوشتن حلقه for دستورات را تکرار کنیم. به مثال زیر دقت کنین:چاپ اعداد بین ۱ تا ۱۰۰❯ echo {1..100} 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100ساختن دایرکتوری‌هایی با حروف الفبا❯ mkdir {a..z}ساختن دایرکتوری‌هایی با حروف الفبا  تبدیل تصاویر با نام‌های ترکیبی حروف و اعداد ❯ convert {a..b}{1..2}.png output%0d.jpgدر این مثال با استفاده از imagemagick تصاویر زیر رو از png به jpg تبدیل می‌کنیم:تبدیل تصاویر a1، a2، b1 و b2 از png به jpg با استفاده از imagemagickبا کنارهم گذاشتن چند بازه (range) می‌توان تمام جایگشت‌های ترکیب آن‌ها را تولید کرد.در مثال فوق: a1, a2, b1, b2برای آشنایی بیشتر و بهتر با اسکریپت‌نویسی shell این آموزش رو مطالعه کنید.این پست، قسمت پنجم از چای، مجموعه‌ای در باب «چیزی که امروز یادگرفتم» است. باقی چای‌ها رو می‌تونید از اینجا مشاهده کنید و در مورد فلسفه‌ی این کار بخونید.</description>
                <category>محمدحسن ستاریان</category>
                <author>محمدحسن ستاریان</author>
                <pubDate>Sat, 23 May 2020 19:03:25 +0430</pubDate>
            </item>
                    <item>
                <title>چای ۴: خودکارسازی تلگرام، دانلود مدیا‌های یک کانال/گروه</title>
                <link>https://virgool.io/internerds/tea-4-telegram-python-hrfvujw0uemv</link>
                <description>تلگرام به دلیل APIهای فراوانی که ارائه میده، علاوه‌بر اینکه پلتفرم بسیار پویا و خوبیه، قابلیت خودکارسازی بسیار خوبی هم داره. به عنوان نمونه یکی از این خودکار‌سازی‌ها شرایطی را در نظر بگیرین که به هر دلیل نیاز داریم فایل‌های به اشتراک گذاشته شده در یک گروه یا کانال تلگرام را به‌‌صورت دسته‌ای دانلود کنیم، برای مثال، می‌خواهیم ۱۰ فایل آخر، یا فایل‌هایی که پسوند xd. دارند و یا تمامی تصاویر را دانلود کنیم. طبیعتا انجام این‌کار به‌صورت خودکار و برنامه‌نویسی شده بسیار راحت‌تر است. بنابراین در این آموزش با استفاده از پایتون و کتابخونه Telethon تمامی آهنگ‌هایی که در کانال cctracks@ به اشتراک گذاشته شده رو دانلود می‌کنیم.نکتهآهنگ‌هایی که در کانال cctracks@ به اشتراک گذاشته‌شده اند گواهی‌نامه Creative Commons داشته و اشتراک‌گذاری آن‌ها مجاز است.صادقانه خواهش می‌کنم از این روش برای دانلود محتوی دارای حق کپی‌رایت (آهنگ، کتاب و ...) استفاده نکنید. این پست جنبه آموزشی داشته و هدف آن نقض قوانین کپی‌رایت نیست.روشی که در این آموزش استفاده می‌کنیم با توسعه ربات‌های تلگرام متفاوت است، در این روش ما به یک برنامه اجازه می‌دهیم به پلتفرم تلگرام با استفاده از اکانت ما دسترسی داشته باشد. البته کتابخونه Telethon امکان دسترسی به تلگرام به عنوان یک ‌ربات را هم می‌دهد. اما ربات‌های تلگرام قادر به اینکار نیستند و به محتوی گروه‌ها و کانال‌هایی که در آن عضو نیستند دسترسی ندارند.پیش‌نیاز‌هاپایتون ۳+مدیر بسته pipپکیج‌های Telethon و tqdmهمانطور که گفته‌شد برای اینکار نیاز داریم تا یک برنامه با استفاده از اکانت ما به پلتفرم تلگرام وصل شود، برای اینکار نیاز است این برنامه به‌صورتی احراز هویت شود.دریافت اعتبار‌نامه‌هابرای احراز هویت ربات لازم است دو رشته کد api_id و api_hash را از تلگرام دریافت کنیم. برای اینکار وارد  my.telegram.org و سپس قسمت API development tools می‌شویم:صفحه API development toolsدر این صفحه اطلاعات مربوط به برنامه، نظیر نام و توضیحات و نوع آن را به دلخواه خود مشخص کرده و با کلیک بر روی Create application کد‌های  api_id و api_hash را دریافت کنید.این کد‌ها منحصرا برای شما بوده و نباید به اشتراک گذاشته شوند، حتما توجه کنید که آن‌ها را در دسترسی عموم قرار ندین.با استفاده از این کد‌ها برنامه می‌تواند اقدام به احراز هویت کرده و به اکانت ما دسترسی داشته باشد. خوشبختانه کتابخانه Telethon عمل احراز هویت را بسیار ساده کرده که در ادامه بررسی می‌کنیم.دسترسی به پیام‌های یک کانال/گروه تلگرامپس از دریافت اعتبارنامه‌های لازم (api_id و api_hash) می‌توانیم با استفاده از اون‌ها امکان دسترسی به اکانت خودمون و در نتیجه دسترسی به محتوی کانال/گروه‌هایی که در آن‌ها عضو هستیم را به یک برنامه رابط بدهیم. برای اینکار از پایتون و کتابخونه Telethon استفاده می‌کنیم. Telethon امکان استفاده از API تلگرام را بسیار ساده کرده و می‌توان از آن برای کنترل ربات‌ها و اکانت شخصی استفاده کرد، در واقع با استفاده از آن می‌توان هم به عنوان بات و هم به عنوان یک اکانت حقیقی به تلگرام دسترسی داشت.کتابخونه Telethon به راحتی با pip نصب می‌شود:نکته بهتر است قبل از نصب آن‌ها یک محیط مجازی برای این پروژه بسازیم و بعد اقدام به نصب کنیم. (اختیاری)pip install telethonاحراز هویتدر اولین استفاده نیاز داریم تا اقدام به احراز هویت بکنیم. برای اینکار بدین صورت عمل می‌کنیم:from telethon import TelegramClient, events, sync

# These example values won&#039;t work. You must get your own api_id and
# api_hash from https://my.telegram.org, under API Development.
api_id = 12345
api_hash = &#039;0123456789abcdef0123456789abcdef&#039;

client = TelegramClient(&#039;session_name&#039;, api_id, api_hash)
client.start()‌با اجرای این کد،‌ از شما خواسته می‌شود یک bot token و یا یک شماره موبایل برای ورود وارد کنید. از اونجایی که ما نیاز به دسترسی به گروه‌ها و کانال‌های خودمون رو داریم شماره خودمون رو وارد می‌کنیم. با وارد کردن شماره موبایل به صورت 989123456789+  پیامی در تلگرام حاوی کد تایید هویت ارسال می‌شود. این کد را وارد می‌کنیم. در ادامه اگر برای اکانتمون cloud password برای Two-step verification تنظیم کرده باشیم، ‌نیاز است تا آن را هم وارد کنیم:احراز هویتدر نهایت یک فایل به نام session_name.session در کنار برنامه ما ذخیره می‌شود که از آن برای احراز هویت استفاده می‌کنیم.توجه کنید که می‌توانیم sessionهای مختلف با هویت‌های مختلف با اعتبارنامه‌هایی که داریم (api_id و api_hash) داشته باشیم.از این به بعد با استفاده از این فایل احراز هویت را انجام داده و دیگر نیاز به مراحل بالا نیست،‌ مگر برای ورود به یک اکانت دیگر.حالا، برای شروع  یک پیام به خودمون (saved messages) می‌فرستیم:from telethon.sync import TelegramClient, events

api_id = &lt;YOUR APP ID&gt;
api_hash = &amp;quot&lt;YOUR API HASH&gt;&amp;quot

with TelegramClient(&#039;session_name&#039;, api_id, api_hash) as client:
    client.send_message(&#039;me&#039;, &#039;Hello, myself!&#039;)نتیجه رو می‌تونید تو GIF زیر ببینین:ارسال پیام به خودنکتههمونطور که می‌بینین میشه از TelegramClient به عنوان یک context manager استفاده کرد. همچنین، می‌توانیم به لیست تمامی گروه‌ها و کانال‌هایی که در آن‌ها عضو هستیم به‌صورت زیر دسترسی داشته باشیم:from telethon.sync import TelegramClient, events

api_id = &lt;YOUR APP ID&gt;
api_hash = &amp;quot&lt;YOUR API HASH&gt;&amp;quot

with TelegramClient(&#039;session_name&#039;, api_id, api_hash) as client:
    for dialog in client.iter_dialogs():
        print(dialog.name, &#039;has ID&#039;, dialog.id)دانلود مدیا‌های یک کانال/گروهحال که کمی با Telethon آشنا شدیم،‌ می‌تونیم مدیا‌های  کانال cctracks@ رو دانلود کنیم. اگر در این کانال عضو نیستید عضو شوید و کد زیر را اجرا کنید:from telethon.sync import TelegramClient, events
from tqdm import  tqdm
import os

api_id = &lt;YOUR APP ID&gt;
api_hash = &amp;quot&lt;YOUR API HASH&gt;&amp;quot

with TelegramClient(&#039;session_name&#039;, api_id, api_hash) as client:
    messages = client.get_messages(&#039;cctracks&#039;, limit=50)
    print(len(messages))
    for msg in tqdm(messages):
        msg_file = msg.file
        if msg_file and msg_file.mime_type == &#039;audio/mpeg&#039;:
            msg.download_media(file=os.path.join(&#039;musics&#039;, msg_file.name))ابتدا با استفاده از متد get_messages و مشخص کردن limit=50، تا ۵۰ پیام آخر کانال را دریافت کردیم. اگر با استفاده از limit تعداد پیام‌ها مشخص نشوند، تنها یک پیام خوانده می‌شود.سپس با استفاده از متد download_media که هر آبجکت پیام (اینجا  msg) دارد می‌توانیم به راحتی آن را دانلود  کنیم.فایل‌های دانلود شدهاین پست، قسمت چهارم از چای، مجموعه‌ای در باب «چیزی که امروز یادگرفتم» است. باقی چای‌ها رو می‌تونید از اینجا مشاهده کنید و در مورد فلسفه اینکار بخونید.</description>
                <category>محمدحسن ستاریان</category>
                <author>محمدحسن ستاریان</author>
                <pubDate>Wed, 20 May 2020 16:33:05 +0430</pubDate>
            </item>
                    <item>
                <title>چای ۲: دسترسی به کنسول مرورگر در موبایل</title>
                <link>https://virgool.io/internerds/tea-2-browser-console-mobile-v0n6vyotwtdt</link>
                <description>شاید کلا چندبار، ولی پیش‌اومده که بخوام وقتی یک سایت رو روی موبایل واقعی تست می‌کنم، بتونم المان‌ها رو inspect کنم یا کنسول (console) رو چک کنم. خوشبختانه برای اینکار راه‌حل‌هایی وجود داره که در ادامه بررسی می‌کنیم، اما لازم به ذکره، چندان راحت نیستن!پروژه Erudaنمونه استفاده از Erudaبرای استفاده از Eruda، باید اسکریپت اون به سایت اضافه بشه. برای اینکار می‌تونیم اسکریپت زیر رو در آدرس‌بار وارد کنیم:javascript\:(function () { var script = document.createElement(&#039;script&#039;); script.src=&amp;quot//cdn.jsdelivr.net/npm/eruda&quot; document.body(script); script. = function () { eruda.init() } })();توجه کنید که قسمت :\javascript رو به :javascript تغییر بدین (\ رو حذف کنین).به دلیل محدودیت ویرگول مجبور به اضافه این کاراکتر شدم تا تمامی اسکریپت به درستی نمایش داده بشه.برای اینکه هردفعه مجبور به اینکار نباشیم و راحت‌تر استفاده کنیم، پیشنهاد خودشون اضافه کردن اسکریپت بالا به‌‌صورت بوکمارکلت (Bookmarklet) هست تا با اجرای اون Eruda به صفحه اضافه بشه. برای اینکار، کافیه این اسکریپت رو با نامی که دوست دارین Bookmark کنین و دفعه بعدی برای اجرای اون فقط از آدرس‌بار این بوکمارک رو انتخاب کنین.استفاده از Remote debuggingاستفاده از این روش چهار محدودیت داره: تنها برای گوشی‌های اندروید و مرورگر کروم قابلیت استفاده داره، برای اون نیاز هست تنظیمات مربوط به توسعه‌دهندگان (Developers Options) موبایل فعال شود و لازم است موبایل با استفاده از کابل به سیستم وصل شود.برای فعال کردن Developer Options، وارد تنظیمات موبایل شده و از منوی About phone یا مشابه آن، گزینه مربوط به Build number را پیدا کنید و روی آن چندین بار تپ (tap) کنین تا این تنظیمات فعال شود.سپس وارد Developer options بشین و گزینه مربوط به USB debugging رو فعال کنین.در ضمن، توجه کنید که اگر از برنامه‌های سازنده رمز یکبار مصرف (OTP) استفاده می‌کنید، ممکن است پیغام خطا و یا درخواست خاموش کردن این تنظمیات را دریافت کنین.بعد از اینکه تنظیمات توسعه دهندگان (همینطور USB debugging) رو فعال کردید، موبایل رو با استفاده از کابل به سیستم وصل کنین در صورتی که پیغامی مربوط به دسترسی در موبایل ظاهر شد، اجازه آن را بدهید و وارد آدرس زیر بشین:chrome://inspectدر این صفحه در قسمت Remote Target دستگاه‌های شناخته شده را به همراه وضعیت اتصال و تب‌های (tab) مرورگر کروم دستگاه مشاهده می‌کنید:همونطور که احتمالا می‌بینین، قابلیت دسترسی به کنسول service workerها هم امکان پذیره.صفحه chrome://inspectحال آدرس دلخواه خود را باز کرده و گزینه inspect مقابل نام آن تب را انتخاب کنین.با استفاده از input که مقابل اسم دستگاه می‌بینید می‌تو‌نین برای باز کردن یک تب جدید در آدرس دلخواه استفاده کنین. توجه کنین که مرورگر کروم روی موبایل باز باشد.نمونه inspect با استفاده از remote debugging+ مشاهده‌ی پروژه‌های localیکی از ویژگی‌های جالب دیگه کروم امکان به اشتراک گذاری یک پورت داخلی با استفاده از Port forwarding هست. با استفاده از این روش می‌تونیم پروژه‌ای که روی سیستم برای مثلا روی پورت ۳۰۰۰ در حال اجراست رو  به یکی از پورت‌های موبایل متناظر (map) کنیم تا بتونیم پروژه رو مستقیم داخل موبایل مشاهده کنیم.برای اطلاعات و گزینه‌های بیشتر می‌تونید پست «چگونه یک لوکال سرور ران کنیم؟ اند مور» رو مشاهده کنین.برای اینکار از روی گزینه Port forwarding در بالای کلیک کرده و در سمت چپ پورت مورد نظر روی سیستم و در سمت راست آدرس مورد نظر برای دسترسی به این پورت را وارد کنید:اگر آدرس مشخصی مد نظر ندارید، localhost:3000 و یا با پورت مشابه را انتخاب کنید.نمونه استفاده از port forwardingحالا می‌تونین روی موبایل به این آدرس (localhost:3000) برین و برنامه‌ای که اجرا بود رو مشاهده کنین.این پست، قسمت دوم از چای، مجموعه‌ای در باب «چیزی که امروز یادگرفتم» است. باقی چای‌ها رو می‌تونید از اینجا مشاهده کنید و در مورد فلسفه اینکار بخونید.</description>
                <category>محمدحسن ستاریان</category>
                <author>محمدحسن ستاریان</author>
                <pubDate>Fri, 15 May 2020 03:49:01 +0430</pubDate>
            </item>
                    <item>
                <title>چای ۱: بروزرسانی Google Sheets با استفاده از پایتون</title>
                <link>https://virgool.io/internerds/tea-1-googlesheets-python-fixw1jmvrjx6</link>
                <description>استفاده از Google sheets در کنار پایتون، امکانات و‌ انعطاف‌پذیری خیلی خوبی به توسعه‌دهندگان برای انجام کارها میده، برای مثال اگر می‌خواین درصد پیشرفت یک پروسه (process) رو به صورت اتومات داخل spreadsheet بروز کنید تا بتونین با استفاده از چارت نمایش بدین، یا اطلاعات یک Sheet رو برای محاسبات آماری داخل Jupyter NoteBooks وارد کنید، و یا حتی، یک دیتابیس ساده و سریع می‌خواین که اعمال CRUD رو پشتیبانی کنه، استفاده از google sheets گزینه سریع و راحتیه. در این پست، ‌به عنوان اولین قسمت چای، با استفاده پایتون و gspread کتابخونه‌ای که Anton Burnashev توسعه داده تنها توی چند خط کد، روی Google Sheets داده اضافه، ویرایش و حذف می‌کنیم. قبل از شروع، موارد زیر رو آماده کنین:پیش‌نیاز‌هاپایتون ۲.۶+ یا ۳+مدیر بسته pipیک حساب‌کاربری Google و یک Google Sheet که باهاش کار کنیم.برای شروع می‌تونید از این Sheet که در این آموزش استفاده می‌کنم، استفاده کنید. کافیه لینک رو باز کنید و اون رو کپی کنید:نحوه ایجاد کپی از Sheet مورد استفاده این پستاحراز هویتبرای دسترسی به spreadsheetتون روی Google Sheets به‌صورت برنامه‌نویسی شده، نیاز دارید تا از  Google API Console یک سرویس و گواهی‌نامه OAuth2 دریافت کنید. برای اینکار قبل از هرکار نیاز است یک پروژه ایجاد کنیم، برای ایجاد پروژه  وارد کنسول بشین و مطابق مراحل زیر یک پروژه ایجاد کنید:روی Create Project کلیک کنید.نام پروژه رو انتخاب کنید.روی Create کلیک کنید.ساخت پروژه جدید در Google API consoleبرای دسترسی به Google Sheet‌s نیاز است سرویس‌های Google Drive API و Google Sheets API را به ترتیب فعال کنیم. برای اینکار وارد کنسول خود شده و روی دکمه ENABLE APIS AND SERVICES کلیک کنید و هرکدام از موارد گفته شده را جستجو کرده و روی Enable کلیک کنید.همچنین می‌توانید روی لینک هرکدام در همین پست کلیک کرده و آن‌ها را Enable کنید.اگر دکمه Enable برای شما غیر فعال است، احتمالا پروژه ساخته نشده‌است. از اجرای درست مرحله قبل مطمئن شوید و یا تا زمانی که پروژه کامل ساخته شود، صبر کنید.بعد از فعال شدن Google Sheet API، به داشبوردتون منتقل می‌شین، حالا مطابق مراحل زیر تنظیمات API را انجام دهید تا یک ربات با اختیارات لازم برای کار با spreadheetمون بسازیم: در صفحه داشبورد، روی Create Credentials کلیک کنید.اگر این گزینه را پیدا نکردین از منوی سمت چپ وارد بخش Credentials شده و روی گزینه CREATE CREDENTIALS و سپس گزینه آخر کلیک کنید:ایحاد اعتبارنامه (Credential) جدیداز منوی اول (نوع API)، مورد Google Sheet API را انتخاب کنید.از منوی دوم (نوع دسترسی)، گزینه Web server را انتخاب کنید.در قسمت بعدی (دسترسی مورد نیاز)، گزینه Application Data رو انتخاب کنید.در قسمت بعدی (استفاده از سرویس‌های ابری گوگل)، گزینه No را انتخاب کرده و روی دکمه آبی‌رنگ کلیک کنید.در صفحه بعد، یک نام به این Service Account داده و Role مربوطه را برابر Project/Editor قرار دهید.یک service account را می‌توان به صورت یک ربات با یک اکانت Google تصور کرد که با تنظیماتی که کردیم، تنها به Google Sheetsای که اضافه شود دسترسی دارد.فعال کردن سرویس Google Sheets APIپس از اتمام مراحل، یک فایل json دانلود می‌شود که نام آن را client_secret.json می‌گذاریم و در ادامه از آن برای احراز هویت استفاده می‌کنیم. این فایل رو باز کرده و Email مربوط به ربات (service account) رو پیدا و کپی کنید، سپس به Google Sheet مورد نظرتون برید و اون Sheet رو با این ربات اشتراک بذارین.اضافه کردن ایمیل ربات به Google Sheets خواندن اطلاعات Spreadsheetپس از تنظیم دسترسی‌ها و گواهی‌نامه‌های (credentials) لازم، حالا می‌تونیم با استفاده از پایتون به Sheet مورد نظر دسترسی داشته باشیم.برای اینکار درواقع به رباتی که در مرحله قبل به Sheet اضافه کردیم فرمان می‌دهیم.برای ادامه به دو پکیج پایتونی نیاز داریم:پکیج oauth2client برای احراز هویت توسط OAuth2.0 (اختیاری)پکیج gspread برای کار با Google Sheetsاین پکیج‌ها با استفاده از pip به‌راحتی نصب می‌شوند:توجه‌کنید که، بهتر است قبل از نصب آن‌ها یک محیط مجازی برای این پروژه بسازیم و بعد اقدام به نصب کنیم. (اختیاری) pip install gspread oauth2clientسپس یک فایل پایتون (برای مثال spreadsheet.py) ساخته و کد زیر را کپی کنید:import gspread

# Find a workbook by name and open the first sheet,
# Make sure you use the right name here.
client = gspread.service_account(filename=&#039;client_secret.json&#039;)
sheet = client.open(&amp;quotvirgool_template&amp;quot).sheet1

# Extract and print all of the values
list_of_records = sheet.get_all_records()
print(list_of_records)حال، مقادیر زیر را در کد، مطابق اطلاعات Sheet خودتون تغییر بدید:فایل client_secret.json باید در کنار این فایل قرار داشته باشد. برای استفاده از آن در مکان دیگر آدرس را در خط ۵ تغییر دهید.مقدار  virgool_template در خط ۶ را برابر نام Sheet بذارید (اگر از همین Sheet استفاده می‌کنید نیازی به تغییر نیست).مقدار sheet1 در همان سطر را مطابق صفحه مورد نظر خود تغییر دهید. (اختیاری) حال کد مورد نظر را اجرا کنید:python3 spreadsheet.pyمشاهده می‌کنیم که اطلاعات Sheet به صورت لیستی (List) از دیکشنری‌ها (Dictionaries) ‌چاپ می‌شود:نتیجه خواندن اطلاعات Google Sheet در اینجا ما اطلاعات Sheet را به‌صورت یک لیست از دیکشنری‌‌ها دریافت کردیم که هر کدام از المان‌های این دیکشنری‌ها مطابق یکی از ستون‌های جدول در Sheet است و در هر جدول اولین سطر به عنوان header در نظر گرفته می‌شود (در جدول ما Title و ...).اگر بخواهیم اطلاعات را به‌صورت لیستی از لیست‌ها دریافت کنیم می‌توانیم از متد زیر استفاده کنیم:sheet.get_all_values()همینطور، اگر تنها داده‌های یک سطر، ستون و یا سلول خاص از جدول را بخواهیم، می‌توانیم از متد‌های زیر استفاده کنیم:# Getting data for a cell by (row, column) indices 
sheet.cell(1, 1).value  

# Getting data for a cell using A1 notation 
sheet.acell(&#039;A1&#039;).value

# Getting data for a row by index
sheet.row_values(1)

# Getting data for a column by index
sheet.col_values(1)اضافه‌کردن، ویرایش و حذف اطلاعات Spreadsheetبرای نوشتن در یکی از خانه‌های جدول می‌توانیم از متد update_cell به‌صورت زیر استفاده کنیم:# update cell using row and column coordinates
sheet.update_cell(1, 1, &amp;quotI just wrote to a google sheet using Python, amazing!&amp;quot)

# update cell using A1 notation
worksheet.update(&#039;B1&#039;, &#039;Bingo!&#039;)به عنوان نمونه، در مثال زیر ‌مقدار پیشرفت Task3 در جدولمان را هر ثانیه ۱۰ درصد افزایش می‌دهیم:نمونه بروزرسانی مقدار یک سلول در جدولبه‌صورت مشابه، برای اضافه کردن یک سطر به جدول:row = [&amp;quotI&#039;m&amp;quot, &amp;quotinserting&amp;quot, &amp;quota&amp;quot, &amp;quotrow&amp;quot, &amp;quotinto&amp;quot, &amp;quota,&amp;quot, &amp;quotgoogle sheet&amp;quot, &amp;quotwith&amp;quot, &amp;quotPython&amp;quot]
index = 10
sheet.insert_row(row, index)برای پاک‌کردن محتویات یک سلول می‌توانیم محتویات آن را برابر یک رشته (String) خالی (&quot;&quot;) قرار دهیم،‌ اما برای پاک‌کردن یک ردیف از جدول،‌ می‌توانیم از متد delete_row استفاده کنیم:sheet.delete_rows(10)توجه‌کنید که متد‌هایی که بررسی کردیم،‌ تنها بخش کوچکی از کتابخانه gspread هستند، برای اطلاعات بیشتر و آشنایی با باقی متد‌ها، مستندات آن را بخوانید.کتابخانه gspread توی ورژن‌های جدیدش عمل احراز هویت رو به‌صورت خودکار انجام میده،‌ اما پیش از این نیاز بود تا خود کاربر اینکار رو انجام بده. از اونجایی که ممکنه با کد‌هاش مواجه بشین، روش انجام کار بدین صورت بود:import gspread
from oauth2client.service_account import ServiceAccountCredentials

# use creds to create a client to interact with the Google Drive API
scope = [&#039;https://spreadsheets.google.com/feeds&#039;, &#039;https://www.googleapis.com/auth/drive&#039;]
creds = ServiceAccountCredentials.from_json_keyfile_name(&#039;client_secret.json&#039;, scope)
client = gspread.authorize(creds)

# Find a workbook by name and open the first sheet,
# Make sure you use the right name here.
sheet = client.open(&amp;quotvirgool_template&amp;quot).sheet1

# Extract and print all of the values
list_of_records = sheet.get_all_records()
print(list_of_records)منابع‌ویدیو معرفی Jupyter Notebooks از Andrew Ng مدرس استنفورد و موسس deeplearning.aiاین پست از وبلاگ twilio مستندات gspreadاین پست، قسمت سوم از چای، مجموعه‌ای در باب «چیزی که امروز یادگرفتم» است. باقی چای‌ها رو می‌تونید از اینجا مشاهده کنید و در مورد فلسفه‌ی این کار بخونید.</description>
                <category>محمدحسن ستاریان</category>
                <author>محمدحسن ستاریان</author>
                <pubDate>Thu, 14 May 2020 19:42:32 +0430</pubDate>
            </item>
                    <item>
                <title>چای: چیزی که امروز یادگرفتم.</title>
                <link>https://virgool.io/internerds/tea-0-s29w12brbfyn</link>
                <description>چیزی که امروز یاد‌گرفتم، یا «چای» یک مجموعه پست هست که من و حسین قراره دنبال کنیم و توی اون چیزهای جدیدی که یاد میگیریم رو منتشر کنیم.داستان از اینجا شروع شد که مدت‌ها قبل ریپازیتوری til از Josh Branchaud رو دیده بودم که ایده جالبی داره، به اشتراک گذاری چیزهایی که امروز یادگرفتم! توی این ریپازیتوری Josh یک سری مطلب منتشر می‌کنه با عنوان TIL یا درواقع Today I learned که هدف از این مجموعه اینه که موارد کوچیکی که نکته‌خاصی دارن یا بعدا مجدد استفاده می‌شن و بخاطر کوچکی نیازی به یک بلاگ پست ندارن رو به اشتراک بذاره.مدت‌ها گذشت و احتمالا اون هم در کنار خیلی‌های دیگه تو قبرستون tabهایی که نگه داشتم تا بعدا ببینم گم شد، تا اینکه چندوقت پیش که طبق عادت داشتم نگاه می‌نداختم به فید‌های daily.dev، چشمم به یکی از پست‌های Remy Sharp خورد که توی اون می‌خواست نحوه مدیریت TILهاش رو آموزش بده. همونطور که خودش می‌نویسه که وقتی این ایده رو دیدم گفتم منم میخوام همچین چیزی درست کنم، من و حسین هم به فکر افتادیم که یک مجموعه مشابه با Today I Learned منتشر کنیم که هیچ قانونی برای زمان انتشار، حجم آموزش و موضوعش نداشته باشه و توی اون چیز‌های مختلفی که یاد میگیریم رو منتشر کنیم،‌ تا هم خودمون بعدا بتونیم استفاده کنیم و هم دیگران.چای، اسمی هست که برای این مجموعه انتخاب کردیم، چون اختصاری برای چیزی که امروز یادگرفتم هست و مضمونی که ما دنبالش بودیم رو می‌رسونه.تصویرسازی از Austin Scottiدر ادامه لیست چای‌هایی که تا الان منتشر شده میاد و آپدیت میشه.چای ۱: بروزرسانی Google Sheets با استفاده از پایتونچای ۲: دسترسی به کنسول مرورگر در موبایلچای ۳: چطوری کلاس‌های مجازی دانشگاه رو ضبط کنم؟چای ۴: خودکارسازی تلگرام، دانلود مدیا‌های یک کانال/گروهچای ۵: اسکریپت نویسی Shell، بازه‌ها در حلقه forچای ۶: پنهان‌کردن عمدی فایل‌ها در لینوکسچای ۷: اجرای دستورات قبلی در ترمینالچای ۸: بریدن و چسباندن ویدیو‌ها بدون خونریزیچای ۹: تبدیل GeoJSON به SVG با استفاده از D3چای ۱۰: رفع مشکل No Audio Output Deviceچای ۱۱: حل مشکل نگاشت پورت‌های WSL 2چای ۱۲: فرمت‌دهی ویدئو برای Video Strickerهای تلگرام با استفاده از FFmpeg</description>
                <category>محمدحسن ستاریان</category>
                <author>محمدحسن ستاریان</author>
                <pubDate>Thu, 14 May 2020 18:49:30 +0430</pubDate>
            </item>
                    <item>
                <title>آموزش مقدماتی فلسک (Flask)</title>
                <link>https://virgool.io/@mh_sattarian/flask-intro-ramtc05udgzv</link>
                <description>مقدمه و آشناییفلسک یک فریم‌ورک وب مبتنی بر پایتون است برای ایجاد سریع و ساده وب‌سرور که توسط آرمین‌روناچر توسعه داده‌شده است. تلاش برای ساده‌نگه داشتن طراحی فلسک و کوچکی فریم‌ورک و قائل نشدن بسیاری از پیش‌فرض ها برای برنامه‌نویسان دلیلی‌است که این بسته‌نرم افزار را یک میکروفریم‌ورک می‌نامند. فلسک رایگان و متن‌باز بوده و  با مجوز آزاد BSD منتشر شده است.کار با فلسک به قدری ساده است که اگر کمی با زبان پایتون آشنا باشید با دیدن اولین کدهای فلسک با ساختار این فریم‌ورک آشنا می‌شوید. فریم‌ورک فلسک با همه‌ی سادگی خود بسیار قدرتمند و کاراست و به هیچ عنوان کوچک بودن فریم‌ورک را نمی‌توان با ضعیف بودن آن برابر دانست و امکانات فریم‌ورک‌های بزرگی مثل جنگو و ریلز را با اندکی جستوجو به دست می‌دهد. برای مثال از سرویس‌های بزرگی که از فلسک استفاده می‌کنند می‌توان از Pinterest و Linkedin نام برد که به مناسب بودن فلسک برای توسعه‌ی سرویس‌های اینترنتی اشاره دارد.دو کتابخانه‌ی اساسی پایه‌‌های قدرتمند فلسک را ساخته‌اند؛ به عنوان واسط وب‌سرور (WSGI) از کتابخانه‌ی werkzeug و برای تمپلیت‌انجین از Jinja2 استفاده کرده اند که هردو کتابخانه توسط تیم توسعه ی فلسک توسعه یافته شده است.بطور پیش‌فرض برای کار با دیتابیس‌ها، اعتبارسنجی فرم ها و کاربران و از این دست کارها ابزار مشخصی در فلسک تعریف نشده است و می‌توان از ابزار‌های شخص ثالث (Third party applications) که برای آن توسعه داده شده‌اند استفاده کرد.نصبآماده کردن محیط مجازیبرای نصب و راه‌انداری پروژه‌ها معقول‌ است یک محیط‌ مجازی توسعه ساخت تا در حین کار و حتی یادگیری، مفسر عمومی و پکیج‌های عمومی سیستم دچار تغییر و کانفلیکت نشوند. محیط‌های توسعه‌مجازی یک کپی از مفسر پایتون را بصورت خصوصی در دایرکتوری پروژه شما ایجاد می‌کنند و بعد از فعال کردن این مفسر پکیج‌ها هم بصورت خصوصی در دایرکتوری پروژه نصب می‌شوند.نحوه ایجاد و فعال‌سازی محیط مجازی را از اینجا بخوانید: https://dataio.ir/python-virtual-env-ewixug6vdwuy نصب فلسکحالا نوبت نصب بسته‌ی فلسک است؛ متداول ترین روش نصب بسته‌های پایتون استفاده از مدیر بسته‌ی pip است که با استفاده از آن فلسک با دستور زیر نصب می‌شود :pip install flaskبرای بررسی صحت نصب فلسک مفسر پایتون را بصورت تعاملی در ترمینال باز کنید (تنها دستور python را نوشته و  enter کنید) و بسته‌ی فلسک را با دستور زیر فراخوانی کنید :import flaskاگر خطایی رخ نداد فلسک درست نصب شده است و می‌توانید با اجرای دستور زیر نسخه نصب شده را بررسی کنید:flask.__version__ساختار برنامه‌ها در فلسکفلسک هیچ محدودیتی درمورد ساختاربندی فایل‌های پروژه برای شما ایجاد نمی‌کند و می‌توانید ساختار خودتان را داشته باشید یا از روش‌های متداول جامعه‌ی فلسک استفاده کنید.ساخت برنامه های فلسکشروع سریعکدهای یک اپلیکیشن ساده‌ی فلسک که عبارت Hello World را در یک صفحه ی وب نمایان می‌کند بصورت زیر است :from flask import Flask
app = Flask(__name__)

@app.route(&#039;/&#039;)
def hello_world():
    return &#039;Hello World!&#039;

if __name__ == &#039;__main__&#039;:
    app.run()این برنامه از چند بخش تشکیل شده است : فراخوانی و تعریف فلسک، تعریف مسیرها، اجرای فلسک.فراخوانی و تعریف فلسکبرای ساخت برنامه‌های فلسک، نیاز است تا ابتدا فلسک را در فایل پایتون خود import کنید و سپس یک نمونه (instance) از Flask بسازید. یعنی این بخش:from flask import Flask
app = Flask(__name__)تنها آرگومان مورد نیاز برای کانستراکتور کلاس فلسک، نام ماژول یا پکیج main است که برای بیشتر برنامه ها &#x60;__name__&#x60; در پایتون مقدار درستی است.می‌توانیم برای نام اینستنس فلسک (در مثال بالا app)، از هر نام متغیر معتبری استفاده کنیم.فلسک روی pip یک بسته از ماژول‌های مرتبط به هم است و نه یک ماژول و به همین دلیل هر ماژول باید از درون بسته ی flask جداگانه ایمپورت بشود.تعریف مسیرهامسیرها همان آدرس‌های (url های) مختلفی هستند که عملیات‌های متفاوتی بر اساس آن‌‌ها انجام می‌شود. مثلا site.domain مسیر ریشه و site.domain/subdomain  یک مسیر دیگر در همان سایت تعریف می‌کند. در کد مثال ما مسیر بصورت زیر تعریف شده بود :@app.route(&#039;/&#039;)
def hello_world():
    return &#039;Hello World!&#039;در اینجا یک دکوریتور (decorator) از نوع ()route  از app (که در قسمت قبل ساختیم) برای تابع Hello world تعریف کرده و آدرس مسیر (&#x27;/&#x27;) را به آن بفرستید. مسیرها در فلسک اساس ساخت برنامه‌ها هستند. با استفاده از مسیر‌ها می‌توانید به سادگی بخش‌های مختلف وب‌اپلیکیشن خود را بسازید و مدیریت کنید.اجرای فلسکدر آخر به پایتون می‌گوییم که فلسک را روی یک وب‌سرور اجرا کند که دستورات آن بصورت زیر است :if __name__ == &#039;__main__&#039;:
    app.run()این شرط برای بررسی این موضوع است که آیا این فایل پایتون مستقیما اجرا می‌شود (برقراری شرط) و یا به عنوان یک ماژول در یک برنامه دیگر وارد شده است (عدم برقراری شرط).این کد را در یک فایل مانند app.py ذخیره کرده و در خط فرمان با دستور python app.py اجرا کنید. خروجی روی ترمینال چیزی شبیه این است:* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)که آدرس 127.0.0.1:5000 را که در خروجی مشخص شده است اگر در مرورگر خود باز کنید روی صفحه عبارت Hello World به صورت زیر نمایان می‌شود، (فلسک به صورت پیش‌فرض پورت ۵۰۰۰ را برای اجرا در نظر می‌گیرد):تابع run می‌تواند پارامتر‌ ای با عنوان host داشته باشد که مشخص می‌کند سرور برروی چه آدرسی اجرا شود، همچنین توسط پارامتر port  می‌توان پورتی که سرور روی آن اجرا شود را مشخص کرد و توسط پارامتر debug و دادن مقدار true به آن می‌توان مشخص کرد که لاگ‌ها و گزارشات مربوط به اشکال یابی برنامه نیز چاپ شوند.app.run(host=&#039;0.0.0.0&#039;, port=8080, debug=True)برای اینکه سرور از طریق کامپیوتر‌های متصل به همین شبکه قابل مشاهده و استفاده باشد به پارامتر  host  مقدار 0.0.0.0  را می‌دهیم.توجه کنید که پارامتر host از جنس string، پارامتر port از جنس integer و پارامتر debug از جنس boolean است.برنامه‌های کاربردی‌تراستفاده از متغیرها در آدرسدر تعریف قوانین آدرس‌دهی می‌توان به آدرس‌ها با استفاده از عملگرهای &lt;&gt;  متغیر اضافه کرد مانند &lt;variable_name&gt;  تا بخشی از آدرس به صورت متغیر توسط تابع در دسترس باشد. همچنین می‌توان نوع متغیر را با استفاده از یک مبدل مانند &lt;converter:variable_name&gt;  مشخص کرد:from flask import Flask
app = Flask(__name__)

@app.route(&#039;/user/&lt;username&gt;&#039;)
def show_user_profile&#40;username&#41;: 
    # show the user profile for that user
    return (f&#039;User {username}&#039;)
    
@app.route(&#039;/post/&lt;int:post_id&gt;&#039;)
def show_post(post_id):
    # show the post with the given id, the id is an integer
    return (f&#039;Post {post_id}&#039;)
    
@app.route(&#039;/path/&lt;path:subpath&gt;&#039;)
def show_subpath(subpath):
    # show the subpath after /path/
    return (f&#039;Subpath {subpath}&#039;)
    	
app.run()نوع پیش‌فرص برای متغیرها string است، اما همانطور که مشاهده می‌کنید انواع integer و path (مسیر) را نیز می‌توانید استفاده کنید.آدرس‌های منحصر به فرد و هدایت آدرسبه نحوه آدرس دهی در برنامه زیر دقت کنید:@app.route(&#039;/projects/&#039;)
def projects():
    return &#039;The project page&#039;

@app.route(&#039;/about&#039;)
def about():
    return &#039;The about page&#039;آدرس تعریف شده برای projects شبیه آدرس دایرکتوی است و در انتهای خود یک &#x27;/&#x27; دارد. اگر به این آدرس بدون &#x27;/&#x27; برویم فلسک ما را به آدرس اصلی (آدرس با &#x27;/&#x27;) هدایت می‌کند.آدرس تعریف شده برای about  شبیه آدرس فایل است و در انتهای خود &#x27;/&#x27; ندارد.  اگر به این آدرس با &#x27;/&#x27; برویم با خطای ۴۰۴ (صفحه‌ای یافت نشد) مواجه خواهیم شد. این امر این امکان را می‌دهد تا آدرس‌های تعریف شده به این سبک منحصر به فرد بوده و کمک می‌کند موتورهای جستوجو یک صفحه را دوبار ایندکس نکنند.متدهای HTTPبرنامه‌ها هنگام دسترسی به آدرس‌ها از متد‌های HTTP مختلفی استفاده می‌کنند. به صورت پیش‌فرض یک آدرس تنها به متد GET پاسخ می‌دهد، اما با مشخص کردن متدهای لازم در پارامتر methods از دکوریتور ()route می‌توان به متدهای مختلف پاسخ داد:from flask import request

@app.route(&#039;/login&#039;, methods=[&#039;GET&#039;, &#039;POST&#039;])
def login():
    if request.method == &#039;POST&#039;:
        return do_the_login()
    else:
        return show_the_login_form()در کد با بررسی نوع (method) درخواست (request) عملیات متفاوتی انجام می‌شود. این بدین معنی است که می‌توانیم در یک آدرس یکسان، پاسخ‌های متفاوتی بنا بر متد درخواست که GET باشد یا POST بدهیم.استفاده از قالب‌ها (templates)فلسک این امکان را می‌دهد تا با دریافت ریکوئست در یک آدرس، یک صفحه‌ HTML رندر شده و به نمایش درآید. برای اینکار فلسک از فولدربندی خاصی استفاده می‌کند. در فولدر پروژه باید دو دایرکتوری به نام های static و templates ایجاد کنید که در دایرکتوری templates فایل های html و در دایرکتوری static فایل‌های css و js خودتون رو قرار بدهید. سپس با استفاده از تابع render_template می‌توانیم مشخص کنیم کدام یک از فایل‌های داخل دایرکتوری templates رندر شده و به نمایش در بیاید. به برنامه زیر توجه کنید:from flask import Flask, render_template
 
app = Flask(__name__)
 
@app.route(&amp;quot/&amp;quot)
def index():
    return &amp;quotHello World !&amp;quot
    
@app.route(&amp;quot/welcome&amp;quot)
def welcome():
    return render_template(&#039;welcome.html&#039;)
if __name__ == __main__:
    app.run()اگر کاربر به صفحه welcome/ برود فانکشن به جای متن خالی، نتیجه رندر فایل HTML را return می‌کند. توجه داشته باشید که این فایل HTML باید داخل پوشه templates قرار گرفته باشد تا فلسک آن را شناسایی بکند.استفاده در بخشی از برنامهاز آنجایی که فلسک تنها به عنوان دکوریتور برای توابعی که خود مشخص می‌کنیم قرار می‌گیرد و آن‌‌ها را به آدرس‌های مشخص شده مرتبط می‌کند، می‌توان از آن در یک برنامه در کنار دیگر توابع استفاده کرد و نیازی به جدا سازی فایل مربوط به serve و اجرای برنامه از فایلی که عملیات برنامه در آن پیاده‌سازی شده نیست. به عنوان مثال به فایل زیر توجه کنید:import keras
import flask

app = flask.Flask(__name__)

def load_model()
	... loads model

def prepare_image():
	... proccess image for prediction
	return image

@app.route(&amp;quot/predict&amp;quot, methods=[&amp;quotPOST&amp;quot])
def predict():
	if flask.request.method == &amp;quotPOST&amp;quot:
		if flask.request.files.get(&amp;quotimage&amp;quot):

			... gets image and call prepare_image() and do predict

	return flask.jsonify(data)


if __name__ == &amp;quot__main__&amp;quot:
	load_model()
	app.run()این برنامه یک برنامه فلسک برای پیش‌بینی نتایج یک مدل یادگیری‌ماشین است که عکس ارسالی به endpoint با آدرس predict/  و متد POST  را آپلود کرده و با predict آن نتیجه را برمی‌گرداند. با اجرای کد، مدل مربوطه لود شده و سرور شروع به گوش دادن به endpoint ذکر شده برای دریافت درخواست‌ها می‌کند و در صورت دریافت درخواستی حاوی تصویر، توابع مربوطه برای predict عکس را صدا زده و نتیجه را برمی‌گرداند.نکته این برنامه در این است که توابعی در آن استفاده شده که توسط فلسک مدیریت نشده و به هیچ endpoint‌ای مرتبط نیستند و این توابع تنها یکبار هنگام اجرای برنامه صدا زده شده و عملیات‌های initialization را انجام می‌دهند.افرونه‌هافلسک یک فریم‌ورک بسیار توسعه‌پذیر و متن‌باز است و می‌توانید بصورت ماژولار کدهای آن را تقسیم بندی و استفاده کنید. همچنین پلاگین‌های بسیاری هم‌اکنون برای آن نوشته شده است که میتوانید در صفحه‌ی extension های فلسک تعدادی از بهترین افزونه‌های فلسک را بیابید و استفاده کنید.منابعمقاله «میکروفریم‌ورک فلسک - معرفی» از سایت کدرزمقاله «آشنایی با میکرو-فریمورک فلسک»‌ از سایت هایومطالعه بیشترسری آموزش «The Flask Mega-Tutorial» از miguelgrinbergکتاب Flask Web Development از انتشارات OReillyاین پست پیش از این در تیر ۹۷ در وبلاگ بینایی ماشین منتشر شده بود.</description>
                <category>محمدحسن ستاریان</category>
                <author>محمدحسن ستاریان</author>
                <pubDate>Fri, 08 Nov 2019 12:35:10 +0330</pubDate>
            </item>
                    <item>
                <title>محیط‌های مجازی پایتون (Python virtual environments)</title>
                <link>https://dataio.ir/python-virtual-env-ewixug6vdwuy</link>
                <description>مقدمهمحیط‌های مجازی (virtual environments) از مهمترین شیوه‌های آزموده شده (Best practice) در توسعه نرم‌افزار تحت پایتون هستند. از آنجایی که با نصب پکیج‌های پایتون، همگی این پکیج‌ها در یک مسیر‌ مشخصی نصب می‌شوند، ممکن است باعث ایجاد مشکلاتی شوند. برای مثال حالتی را در نظر بگیرید که در ایجاد پروژه‌های مختلف به نسخه‌های متفاوتی از برخی کتابخانه‌ها نیاز دارید؛ در این صورت چگونه می‌توانید چندین نسخه‌ متفاوت از یک کتابخانه‌ را در پروژه‌های مختلف پایتون استفاده کنید؟ فرض کنید می‌خواهیم بر روی توسعه دو وب‌سایت؛ یکی توسط نسخه (۱.۸) وب فریم‌ورک جنگو (Django) و دیگری بر روی یک نسخه قدیمی (۰.۹۶) از آن کار کنیم، ولی نمی‌توانیم! چراکه نمی‌شود هر دوی این نسخه‌ها را با هم در پایتون (دایرکتوری site-packages) نصب داشت. یا فرض کنید که برای تست پکیج‌هایی، می‌خواهیم بدون نصب آن‌ها در کنار پروژه اصلی، از آن‌ها استفاده کنیم؛ در این وضعیت راه حل ایجاد محیط‌هایی مجازی (Virtual Environments) برای توسعه پروژه‌های مورد نظر است، ابزاری که محیط توسعه و اجرای هر پروژه پایتون را به همراه تمام وابستگی‌های (Dependencies) آن از پروژه‌های دیگر جدا یا ایزوله (AKA. Sandboxing) می‌کند.این ویژگی ایزوله کردن که محیط‌های مجازی فراهم می‌کنند، در توسعه میکروسرویس‌ها و کانتینرها بسیار مورد استفاده قرار می‌گیرد و استفاده از محیط‌های مجازی را امری بدیهی می‌کند.پکیج‌های پایتون بسته به اینکه پکیج‌های پیش‌فرض زبان (system packages) هستند یا پکیج‌های شخص‌ثالثی هستند که شما توسط PIP یا easy_install نصب کرده‌اید (site packages)، در مکان‌های زیر نصب می‌شوند:&amp;quot&amp;quot&amp;quot Python REPL &amp;quot&amp;quot&amp;quot

# system packages
&gt;&gt;&gt; import sys
&gt;&gt;&gt; sys.prefix
&#039;/System/Library/Frameworks/Python.framework/Versions/3.5&#039;


# site packages
&gt;&gt;&gt; import site
&gt;&gt;&gt; site.getsitepackages()
[
  &#039;/System/Library/Frameworks/Python.framework/Versions/3.5/Extras/lib/python&#039;,
  &#039;/Library/Python/3.5/site-packages&#039;
] در ادامه به بررسی ابزارهای زیر می‌پردازیم:condavirtualenvvirtualenvwrapperpyvenvکوندا (Conda)یکی از روش‌های مدیریت ورژن‌های پایتون و همچنین ایجاد محیط‌های مجازی پایتون استفاده از ابزاری به نام کوندا (conda) است؛ این ابزار که همراه پکیج Anaconda و یا miniconda عرضه می‌شود، ویژگی‌های زیر را دارد:ساختار مشخص و ساده : شناخت ساختار فولدربندی آن بسیار ساده است.مدیریت پنهان فایل‌ها : فایل‌های مربوطه را خارج از فولدر خود نصب نمی‌کند.انعطاف‌پذیری : علاوه‌بر پکیج‌های زیادی که خود پشتیبانی می‌کند، پکیج‌های pip نیز در محیط‌های ساخته شده توسط کوندا قابل نصب هستند.چندمنظوره: علاوه بر مدیریت محیط‌های مجازی و نسخه‌های پایتون برای زبان‌های دیگر مانند R هم استفاده می‌شود.تفاوت پکیج‌های Anaconda و minicoda در این است که Anaconda را می‌توان یک پکیج کامل از ابزارهای داده‌کاوی و یادگیری ماشین دانست که همراه خود علاوه‌بر ابزار کوندا، ابزارهای دیگری مانند spyder، jupyter و تعداد زیادی از پکیج‌های پایتون مورد نیاز این زمینه را نصب می‌کند؛ درحالی‌که miniconda صرفا ابزار کوندا برای مدیریت ورژن‌ها و مفسر‌های پایتون و تعداد کمی از کتابخانه‌ها را در اختیار می‌گذارد.نصببرای نصب Conda از این آموزش استفاده کنید.ساخت محیط مجازی توسط Condaبرای ساخت محیط مجازی ای با نام myenv و نصب پایتون ۳.۴ و پکیج‌های package1 و package2 با ورژن دلخواه به صورت زیر عمل می‌کنیم:conda create -n myenv [python=3.4 package1 package2=version]ورود و خروج به محیط مجازیبرای ورود و فعال سازی محیط مجازی ای با نام myenv به صورت زیر عمل می‌کنیم:# Windows
activate myenv

# Linux
source activate myenvدر صورتی که به درستی وارد محیط مجازی شوید، prompt خط فرمان تغییر می‌کند. برای مثال با فعال کردن محیط myenv توسط دستورات بالا به صورت زیر تبدیل می‌شود:(myenv) C:\&gt;اکنون می‌توانیم در پروژه خود به کتابخانه‌ها، pip، دایرکتوری site-packages و مفسری اختصاصی دسترسی داشته باشیم. همچنین با فعال کردن یک محیط مجازی، فایل‌های اجرایی مربوط به این محیط درون متغیر PATH قرار می‌گیرند تا دستورات مورد استفاده به سادگی در دسترس باشند.برای خروج و غیر فعال کردن محیط از دستور زیر استفاده می‌کنیم:# Windows
deactivate

# Linux
source deactivateبرای لیست کردن محیط‌های مجازی ساخته شده از دستور conda env list و برای لیست کردن پکیج‌ها درون یک محیط از دستورات conda list و یا pip list با توجه به اینکه با کدام روش پکیج‌ها را نصب کرده‌ایم استفاده می‌کنیم.حذف محیط مجازیبرای حذف محیط مجازی ای که دیگر به آن احتیاج نداریم به صورت زیر عمل می‌کنیم:conda remove -n yourenvname -allبرای مطالعه بیشتر در مورد کوندا و کنترل محیط‌های مجازی توسط آن به منابع مربوطه در انتهای پست مراجعه کنید.از آنجایی که Conda پکیجی چندمنظوره بوده و امکانات و نرم‌افزار‌های زیادی را همراه خود دارد، حجیم بوده و راه‌اندازی آن ممکن است به زمان بیشتری نیاز داشته باشد. روش دیگر ساخت محیط‌های مجازی استفاده از پکیج virtualenv و یا افزونه آن virtualenvwrapper است که در ادامه توضیح داده خواهند شد.‏virtualenvاز معروف‌ترین و پراستفاده ترین روش‌های ساخت محیط‌های مجازی برای پایتون استفاده از پکیج virtualenv  است که در ادامه بررسی میکنیم.نصباین پکیج به راحتی با استفاده از پیپ (pip) به صورت زیر قابل نصب است:# Windows
pip install virtualenv

# Linux (installs system-wide)
sudo -H pip install virtualenvچنانچه بر روی سیستم عاملی هر دو نسخه پایتون 2x یا 3x نصب است؛ این موضوع که virtualenv را توسط pip کدام نسخه نصب نمایید، اهمیت چندانی ندارد. چرا که امکان استفاده از آن برای دیگر نسخه‌ها نیز وجود دارد.ساخت محیط مجازی توسط virtualenvاکنون برای ایجاد یک محیط مجازی از دستور virtualenv ENV  استفاده می‌شود که منظور از ENV  در آن، نشانی دایرکتوری دلخواهی است که قصد داریم محیط مجازی در آن ایجاد گردد:virtualenv Documents/ENV/دستور بالا موجب ایجاد یک محیط مجازی در مسیر /Documents/ENV  سیستم عامل، بر پایه مفسر پایتونی که از pip آن برای نصب virtualenv استفاده کردیم می‌شود و چنانچه بخواهیم محیط مجازی خود را بر پایه‌ نسخه‌ موجود دیگری از پایتون ایجاد نماییم، لازم است با استفاده از فلگ  python--  نشانی مفسر آن مشخص گردد [صفحه راهنما + این پاسخ ]:# Versions Already installed
virtualenv --python=python2 ENV
virtualenv --python=python3 ENV

## Versions user downloaded
# Windows
virtualenv --python=C:\Python25\python.exe Documents\ENV\
# Linux
virtualenv --python=/opt/python3.3/bin/python ENVدر نمونه کد‌ بالا، نسخه‌های۲.۷ و ۳.۴ پایتون از پیش بر روی سیستم عامل نصب بوده و نسخه ۳.۳ و ۲.۵ توسط کاربر در مسیرهای مشخص شده نصب شده است.ورود و خروج به محیط مجازیبرای ورود و فعال سازی محیط مجازی‌ای با نام myenv به صورت زیر عمل می‌کنیم:# Windows
&gt; cd Documents\SampleENV\
&gt; Scripts\activate.bat

(SampleENV)&gt;

# Linux
$ cd Documents/SampleENV/
$ source bin/activate

(SampleENV)$توجه کنید که با ورود موفق به محیط مجازی، prompt خط فرمان چگونه تغییر می‌کند.اکنون می‌توانیم در پروژه خود به کتابخانه‌ها، pip، دایرکتوری site-packages و مفسری اختصاصی دسترسی داشته باشیم. همچنین با فعال کردن یک محیط مجازی، فایل‌های اجرایی مربوط به این محیط درون متغیر PATH قرار می‌گیرند تا همانند تا دستورات مورد استفاده به سادگی در دسترس باشند.در لینوکس می‌توانید با اجرای دستورات which python3 و which pip3 بررسی کنید که مسیر فایل اجرایی مفسر پایتون و pip از مسیر معمول آن (usr/bin/python3/)‌ متفاوت است.پس، برای هر پروژه‌ای کافیست داخل پروژه یکبار با فراخوانی virtualenv محیط مجازی را بسازید و پس از آن هرباری که داخل دایرکتوری پروژه مورد نظر می‌شوید آن محیط را فعال کنید.برای خروج و غیر فعال کردن محیط از دستور زیر استفاده می‌کنیم:# Windows
(SampleENV)&gt; deactivate.bat

# Linux
(SampleENV)$ deactivateاگر از لینوکس استفاده می‌کنید، با اضافه کردن alias های زیر می‌توان کار ورود و خروج از محیط‌های مجازی را ساده‌تر کرد:alias ae=&#039;deactivate &amp;&gt;/dev/null; source ./venv/bin/activate&#039;
alias de=&#039;deactivate &amp;&gt;/dev/null&#039;‏virtualenvwrapper‏virtualenvwrapper، همونطور که از اسمش پیداست یک افزونه و wrapper برای virtualenv است که علاوه بر راحت کردن کار با آن، ایرادات زیر را نیز از آن رفع می‌کند. توجه کنید که virtualenv پیش‌نیاز این پکیج نبوده و لازم به نصب آن نیست:جلوگیری از ساخت دایرکتوری مربوط به مدیریت پایتون و محیط مجازی کنار فایل‌های پروژه (در صورت استفاده از git، باید این فولدر را در gitignore. اضافه کنید)لزوم به اعمال فعال سازی و غیر فعال سازیو موارد دیگراین پکیج برای لینوکس توسعه داده شده است و برای ویندوز یک توزیع از این پکیج با نام virtualenvwrapper-win قابل استفاده است.نصباین پکیج به راحتی با استفاده از پیپ (pip) به صورت زیر قابل نصب است:# Windows
pip install virtualenvwrapper-win

# Linux (installs system-wide)
sudo -H pip install virtualenvwrapperپکیج virtualenvwrapper برخلاف virtualenv و مانند conda، تمام محیط‌های مجازی را داخل یک دایرکتوری که ما مشخص می‌کنیم ذخیره می‌کند که اینکار باعث جداسازی کد‌ها و پکیج‌های پایتونی ای که نصب می‌کنیم بدون ایجاد ایراداتی که اشاره شد می‌شود.پیش از ساخت محیط‌های مجازی، اگر از لینوکس استفاده می‌کنید دو دستور پایین را داخل فایل bashrc. (یا zshrc. یا کانفیگ فایل هر شل مورد استفاده) قرار می‌دهیم:# virtualenvwrapper configuration
export WORKON_HOME=$HOME/.virtualenvs # Save all virtual environment in this path
source /usr/local/bin/virtualenvwrapper.sh # Activate it pleaseو اگر از ویندوز استفاده می‌کنید متغیری با نام WORKON_HOME  در Environment Variable ویندوز اضافه کرده و مقدار آن را برابر USERPROFILE%\Envs%  می‌گذاریم. [آموزش]با اینکار فولدر virtualenvs. را در لینوکس و فولدر Envs را در ویندوز و در مسیر‌های مشخص شده برای ذخیره سازی محیط‌های مجازی مشخص می‌کنیم.ساخت محیط مجازی توسط virtualenvwrapperبرای ساخت محیط مجازی ای با نام myenv و ورود به آن از دستور زیر استفاده می‌کنیم:mkvirtualenv myenvورود و خروج به محیط مجازیبرای ورود و فعال سازی محیط مجازی ای با نام myenv به صورت زیر عمل می‌کنیم:workon my_first_venvو برای خروج و غیر فعال کردن محیط از دستور زیر استفاده می‌کنیم:deactivateبرای لیست کردن محیط‌های مجازی از دستور lsvirtualenv و یا دستور workon بدون ورودی استفاده می‌کنیم.حذف محیط مجازیبرای حذف محیط مجازی ای که دیگر به آن احتیاج نداریم به صورت زیر عمل می‌کنیم:rmvirtualenv myenvیک دستور جالب این پکیج mktmpenv است که یک محیط مجازی با اسم تصادفی ساخته و آن‌را فعال می‌کند و با غیر فعال کردن آن محیط مجازی را پاک می‌کند که برای تست یک کد یا پکیج به سرعت ایده‌آل است.‏pyvenvدر نسخه‌های از ۳.۳ به بعد پایتون، ماژولی با نام venv برای ایجاد محیط‌های مجازی به کتابخانه استاندارد پایتون افزوده شده است که می‌توان از آن به جای نصب virtualenv استفاده نمود و نیازی به نصب ندارد؛ برای این منظور از دستور pyvenv  و با الگویی مشابه pyvenv ENV  استفاده می‌گردد. تنها عیب این روش این است که نمی‌توان در آن ورژن پایتون را برای محیط‌های مختلف تغییر داد و همگی ورژن پایتون نصب شده روی سیستم را خواهند داشت.ساخت محیط مجازی توسط pyvenvبرای ساخت محیط مجازی ای با نام myenv و ورود به آن از دستور زیر استفاده می‌کنیم:# Windows
python C:\Python34\Tools\Scripts\pyvenv.py Documents\myenv\
# Or
python -m venv Documents\myenv\

#Linux
pyvenv Documents/myenv/ورود و خروج به محیط مجازیبرای ورود و فعال سازی محیط مجازی ای با نام myenv به صورت زیر عمل می‌کنیم:# Windows
cd Documents\myenv\
Scripts\activate.bat

# Prompt will change like this
(myenv)&gt;

# Linux
cd Documents/myenv/
source bin/activate

# Prompt will change like this
(myenv)$و برای خروج و غیر فعال کردن محیط از دستور زیر استفاده می‌کنیم:# Windows
(myenv)&gt; deactivate.bat

# Linux
(myenv)$ deactivateمنابعکوندا (Conda)مقاله «چرا به محیط‌های پایتون نیاز دارید و چگونه آن‌ها را با کوندا مدیریت کنیم» در مدیوممقاله «ساخت محیط‌های مجازی برای پایتون توسط کوندا» از سری مقاله‌های دستورالعمل‌های ۲ دقیقه‌ای برای دانشمندان‏virtualenvفصل «ایجاد محیط مجازی» از کتاب آنلاین پایتون کدرزمقاله «پایتونیک - معرفی Virtual Environment‌ها قسمت اول» در ویرگول‏virtualenvwrapperمقاله «پایتونیک - معرفی Virtual Environment‌ها قسمت دوم» در ویرگولمستندات virtualenvwrapper‏pyvenvفصل «ایجاد محیط مجازی» از کتاب آنلاین پایتون کدرزمطالعه بیشتراین سوال و جواب در stackoverflowمقاله «رمزبرداری از محیط‌های مجازی»این پست پیش از این در تیر ۹۷ در وبلاگ بینایی ماشین منتشر شده بود.</description>
                <category>محمدحسن ستاریان</category>
                <author>محمدحسن ستاریان</author>
                <pubDate>Fri, 08 Nov 2019 11:12:20 +0330</pubDate>
            </item>
                    <item>
                <title>آشنایی با ImageMagick برای کار با تصاویر</title>
                <link>https://virgool.io/@mh_sattarian/imagemagick-intro-z4cwoagg8eg3</link>
                <description>‏ImageMagick یک مجموعه فوق‌العاده از کتابخانه‌ها (Libraries) و برنامه‌های (Binaries) رایگان و متن‌باز و چندسکویی برای کار با تصاویر پیکسلی (Raster Graphics) و برداری (Vector Graphics) با امکانات فراوان برای نمایش، تبدیل، تغییر و ویرایش است که می‌تواند با بیش از ۲۰۰ فرمت مختلف تصویر کار کند. از این برنامه می‌توان از طریق رابط خط فرمان (CLI) و یا برنامه‌ های شخص ثالث (Third-party) نوشته شده توسط کتابخانه‌های آن در زبان‌های دیگر استفاده کرد. برای آشنایی با ImageMagick تنها به این پست اکتفا نکرده و حتما مستندات آن را بخوانید.برای مثال تمامی تغییرات زیر به راحتی با استفاده از خط فرمان قابل اعمال به تصاویر هستند:لیست دستورات و امکانات ImageMagickدر ادامه چندتا از دستورات پر استفاده را مرور می‌کنیم. اما قبل از آن روش نصب:نصب‏ImageMagick چندسکویی بوده و برای سیستم‌عامل‌های مختلف در دسترس است. برای استفاده از آن می‌توان از کتابخانه‌های آن برای زبان‌های مختلف مثل C و ++C، پایتون، PHP و غیره استفاده کرد و یا برنامه‌های آماده (Binaries) آن را نصب و استفاده کرد که در ادامه بررسی می‌کنیم:ویندوزبرای نصب ImageMgick در ویندوز می‌توان فایل نصب (exe) آن را دریافت کرد و یا با استفاده از پکیج منیجر Chocolatey (از اینجا دریافت کنید) با وارد کردن دستور زیر در CMD آن را نصب کرد:&gt; choco install imagemagickلینوکسبرای نصب ImageMagick روی لینوکس، از دستورات زیر استفاده می‌کنیم:$ sudo apt update #update package lists
$ sudo apt install imagemagickاطلاعات بیشترروش استفادهاستفاده از ImageMagick توسط رابط خط فرمان (CLI) بسیار ساده است (لیست مثال‌های استفاده). قالب اجرای دستورات با استفاده از دستور magick --help  و magick -usage  قابل مشاهده است:Usage: magick tool [ {option} | {image} ... ] {output_image}
Usage: magick [ {option} | {image} ... ] {output_image}
       magick [ {option} | {image} ... ] -script {filename} [ {script_args} ...]
       magick -help | -version | -usage | -list {option}قالب تمامی دستورات شامل، خواندن تصویر، اعمال تغیرات روی آن و مشخص کردن خروجی است.در ویندوز تمامی عملیات‌ها با دستور magick شروع می‌شوند و گاهی لازم است پس از دستور  magick  علمیات مربوطه نیز نوشته شود (لیست دستورات)، اما در لینوکس دستورات با اسم عملیات شروع می‌شوند.پس توجه داشته باشیم دستورات به فرم magick convert  در لینوکس به صورت convert  استفاده خواهند شد.همچنین ImageMagick از globbing برای آدرس‌دهی پشتیبانی می‌کند. بنابراین بجای تغییر چندین تصویر بجای دستور حلقه زیر:&gt; for %f in (*.‌png) do magick mogrify -resize 50% $fاز دستور زیر استفاده می‌کنیم:&gt; magick mogrify -resize 50% *.pngکه مزیت آن علاوه بر سادگی، قابلیت همزمان سازی ImageMagick است که سرعت کار را افزایش می‌دهد.دستوراتنمایش (Display)برای نمایش تصویر می‌توان در ویندوز از دستور imdisplay  و در لینوکس از magick display  استفاده کرد.# Windows
&gt; imdisplay image.png

# Linux
$ magick display image.pngتبدیل (Convert)برای تبدیل فرمت تصویر کافیست، مانند نمونه دستور زیر، فرمت خروجی را مشخص کنید:&gt; magick image.jpg image.pngهمچنین با استفاده از دستور convert نیز می‌توان فرمت تصویر را تغییر داد؛ به علاوه، این دستور می‌تواند تغییرات دیگری نیز انجام بدهد. برای مثال دستور زیر لبه‌های تصویر (که نتیجه اعمال فیلتر canny با سایز کرنل مشخص شده است) را در فایل خروجی ذخیره می‌کند:&gt; magick convert image.jpg -canny 0x1 -negate image-canny.jpgنتیجه لبه‌یابی با روش cannyکاهش کیفیت (Reduce Quality)از راه‌های فشرده‌سازی تصویر کاهش رزولوشن آن است، با دستور زیر می‌توان درصد کیفیت تصویر خروجی را مشخص کرد. اینکار نیز با دستور convert  قابل انجام است:&gt; convert image.jpg -quality 75 output_file.jpgتغییر سایز (Resize)برای تغییر سایز تصویر می‌توان از دستور resize استفاده کرد:# Using precentage
&gt; magick image.png -resize &#039;200%&#039; bigWiz.png

# Using percentage for one axis
&gt; magick image.png -resize &#039;200x50%&#039; longShortWiz.png

# Maximum values of height and width given, aspect ratio preserved.
&gt; magick image.png -resize &#039;100x200&#039; notThinWiz.png

# Minimum values of width and height given, aspect ratio preserved.
&gt; magick image.png -resize &#039;100x200^&#039; biggerNotThinWiz.png

# Width and height emphatically given, original aspect ratio ignored.
&gt; magick image.png -resize &#039;100x200!&#039; dochThinWiz.png

# Or, using mogrify
&gt; magick mogrify -resize 80% image.jpgبا استفاده از دستور mogrify نتیجه در خود تصویر ورودی ذخیره می‌شود بنابراین دیگر نیازی به مشخص کردن فایل خروجی نیست.روش‌های دیگر مشخص کردن سایز خروجی را ببینید.تصویر مختلط (Collage)به کمک magick به سرعت و تنها با یک دستور می‌توانیم یک تصویر مختلط از چندین تصویر درست کنیم:&gt; magick montage image1.jpg image2.jpg image3.jpg image4.jpg output_montage.jpg

# Using custom geometry and title
&gt; magick montage image1.jpg image2.jpg image3.jpg image4.jpg -geometry +10+10 -title &#039;Collage_title&#039; output_montage.jpgبا اجرای خط اول تصویر خروجی کیفیت خوبی نخواهد داشت، دلیل آن مقادیر پیش‌فرض برای geometry  است که برابر ۱۲۰ پیکسل تنظیم شده. در خط دوم با وارد کردن مقدار مورد نظر برای geometry  گفته‌ایم سایز تصاویر تغییر نکنند و سایز حاشیه تصاویر را برابر ۱۰ پیکسل تنظیم کرده‌ایم.بریدن تصویر (Cropping)برای بریدن تصویر از طرفین می‌توان از دستور shave استفاده کنیم:&gt; magick convert border.gif -shave 10x10 shave.gif
&gt; magick convert border.gif -shave 10x0  shave_sides.gif
&gt; magick convert border.gif -shave  0x20 shave_topbot.gif۱. دستور اول، ۱۰ پیکسل از محور x و ۱۰ پیکسل از y از دو سمت تصویر را برش می‌دهد. ۲. دستور دوم، تنها ۱۰ پیکسل از دوسمت محور x تصویر را برش می‌دهد.۳. دستور سوم، ۲۰ پیکسل از دو سمت محور y تصویر را برش می‌دهد.برای بریدن ناحیه دلخواه از تصویر باید از دستور crop استفاده کنیم:&gt; magick convert rose: -crop 40x30+10+10  crop.gif
&gt; magick convert rose: -crop 40x30+40+30  crop_br.gif
&gt; magick convert rose: -crop 40x30-10-10  crop_tl.gif
&gt; magick convert rose: -crop 90x60-10-10  crop_all.gif
&gt; magick convert rose: -crop 40x30+90+60  crop_miss.gifآدرس تصویر نوشته شده (:rose ) آدرس عکس‌های نمونه قابل استفاده، شناخته شده توسط magick است که برای تست می‌توان استفاده کرد. لیست این تصاویر در قسمت Built-in Images در این صفحه قابل مشاهده‌اند.۱. دستور اول، عکسی با سایز ۴۰ پیکسل در ۳۰ پیکسل تولید خواهد کرد که از مختصات ۱۰ در ۱۰ از تصویر برش زده خواهد شد. درواقع برشی به طول ۴۰ پیکسل و عرض ۳۰ پیکسل از مختصات ۱۰ در ۱۰ از تصویر گرفته می‌شود؛ بدیهی است اگر ناحیه برش از تصویر بزرگتر باشد، تنها قسمت برش خورده، تصویر خروجی خواهد بود.۲. دستور دوم، تصویری خروجی با حداکثر اندازه ۴۰ پیکسل در ۳۰ پیکسل خواهد داشت که نتیجه برش از مختصات ۴۰ در ۳۰ است.۳. دستور سوم نیز تصویری خروجی برابر نتیجه برشی با اندازه ۴۰ پیکسل در ۳۰ از مختصات ۱۰- در ۱۰- خواهد داشت.۴. دستور چهارم، تصویر خروجی ای از نتیجه برش تصویر به مساحت ۹۰ پیکسل در ۶۰ پیکسل از مختصات ۱۰- در ۱۰- خواهد داشت؛ چون که این ناحیه شامل کل تصویر است، تصویر خروجی با ورودی تفاوتی ندارد. ۵. دستور پنجم که عکس خروجی نتیجه برش تصویر از مختصات ۹۰ در ۶۰ با سایز ۴۰ پیکسل در ۳۰ پیکسل خواهد بود، ناحیه برش هیچ قسمتی از عکس را شامل نمی‌شود، بنابراین عکسی خالی خواهد ساخت.چرخاندن (Rotation)برای چرخواندن تصویر به صورت زیر عمل می‌کنیم (مقدار مثبت rotate  به معنی چرخش ساعتگرد و مقدار منفی آن به معنی چرخش پادساعتگرد است):&gt; magick convert koala.gif -rotate 30 rotate.jpg
&gt; magick convert koala.gif -background lightskyblue -rotate 30 rotate_color.png
&gt; magick convert koala.gif -alpha set -background none -rotate 30 rotate_trans.png۱. دستور اول تصویر را ۳۰ درجه ساعتگرد می‌چرخاند.۲.  دستور دوم تصویر را ۳۰ درجه ساعتگرد چرخانده و پس‌زمینه ناشی از حذف تصویر را با رنگ تعیین شده پر می‌کند. ۳. دستور سوم تصویر را چرخانده و پس‌زمینه آن را حذف می‌کند.توجه کنید که برای داشتن تصویر بدون پس‌زمینه، به فرمتی آن را ذخیره کنید که از کد رنگ آلفا پشتیبانی کند. برای مثال فرمت PNG تصویر بدون پس‌زمینه را به درستی ذخیره می‌کند اما فرمت JPG تصویر بدون پس‌زمینه را با پس‌زمینه سفید ذخیره می‌کند.موارد فوق تنها قسمت کوچکی از توانایی‌های ImageMagick است که شامل متحرک‌سازی، کار با متن‌ها، کار با تصاویر دیجیتال و تصاویر خام و ... می‌شود. بنابراین مطالعه مستندات و نمونه موارد استفاده در درک توانایی‌های آن موثرتر خواهد بود که البته سعی می‌کنم در پست‌های بعدی این امکانات ImageMagick را بررسی کنم.این پست در تیر ۹۷ در وبلاگ بینایی ماشین منتشر شده بوده است.</description>
                <category>محمدحسن ستاریان</category>
                <author>محمدحسن ستاریان</author>
                <pubDate>Thu, 07 Nov 2019 01:45:31 +0330</pubDate>
            </item>
                    <item>
                <title>اجرای تحت وب ژوپیتر نوت‌بوک‌ها توسط سرویس Azure Notebooks</title>
                <link>https://virgool.io/@mh_sattarian/jupyter-on-azure-notebooks-sdqonhv0gjrj</link>
                <description>سرویس Microsoft Azure Notebooks یک Interactive Notebook یا یک محیط آنلاین برنامه‌نویسی پویا (interactive) قدرت گرفته از Jupyter است که زبان‌های پایتون، R و F# را پشتیبانی‌می‌کند.مزیت این سرویس نسبت به سرویس‌های مشابه مثل mybinder و یا حتی repl.it؛ در دسترس بودن برای همه کاربران و از پیش نصب بودن تعداد زیادی از پکیج‌های مورد نیاز برای انواع زمینه‌های کاری است. سرویس Azure Notebooks مایکروسافت، مکان ساخت نوت‌بوک و یا کلون‌کردن یک ریپوزیتوری گیت که دارای نوت‌بوک است و اجرای آن تحت وب را به کاربران می‌دهد و یک ترمینال برای مدیریت و کار با فایل‌های نوت‌بوک در اختیار هر کاربر می‌گذارد. هر کاربر ۴ گیگابایت فضای مموری و ۱ گیگابایت فضای ذخیره‌سازی فایل برای هر نوت‌بوک به صورت رایگان در اختیار دارد. برای استفاده از این سرویس، تنها داشتن یک ایمیل در یکی از سرویس‌های مایکروسافت مانند outlook و یا Hotmail کافیست.در ادامه موارد زیر را بررسی می‌کنیم:نحوه ورود و اجرای نوت‌بوک‌ها در Azure notebook و ایجاد یا کلون کردن یک نوت‌بوکنصب پکیج‌ها داخل نوت‌بوک‌ها یا توسط ترمینالورود، ایجاد یا کلون و اجرای نوت‌بوک‌ها https://www.aparat.com/v/7yjXB پس از ورود -توسط ایمیل مایکروسافت (outlook یا Hotmail)- برای دسترسی به نوت‌بوک‌ها وارد قسمت Libraries (از گزینه‌های بالای صفحه) می‌شویم تا لیست نوت‌بوک‌ها فعلی نمایش داده شود.برای ساخت و یا کلون کردن نوت‌بوک روی گزینه New Library + کلیک می‌کنیم. حال برای کلون کردن ریپازیتوری وارد تب From GitHub شده و آدرس آن را در فیلد اول و نام دلخواه خود برای نوت‌بوکی که ساخته خواهد شد را در فیلد دوم و آدرس دلخواهمان برای دسترسی به این نوت‌بوک را در فیلد سوم وارد می‌کنیم.پس از پایان عملیات کلون کردن، وارد کتابخانه کلون شده می‌شویم و نوت‌بوک‌ها با انتخاب آنها در دسترس هستند.اگر سرور اجرای نوت‌بوک‌ها روشن نباشد با انتخاب یکی از نوت‌بوک‌ها روشن شده و پس از مدتی نوت‌بوک اجرا شده و همانند یک ژوپیتر نوت‌بوک که در کامپیوتر اجرا می‌کنیم، تحت وب قابل استفاده است.با باز کردن نوت‌بوک پنجره‌ای برای انتخاب کرنل مورد نیاز برای این نوت‌بوک باز خواهد شد. البته کرنل را می‌توان بعدا از طریق تب Kernel داخل خود نوت‌بوک هم تغییر داد.در ویدئو برای تست نوت‌بوک و کرنل آن سه کتابخانه openCV، Tensorflow و Keras در نوت‌بوک import شده و ورژن آنها بررسی و چاپ شد.نصب پکیجعلاوه بر تعداد زیاد پکیج‌های از پیش نصب شده برای هر نوت‌بوک امکان نصب پکیج‌های دلخواه هم از داخل نوت‌بوک و از هم طریق ترمینال وجود دارد.پکیج‌هایی که نصب می‌شوند تنها در طول مدت روشن بودن سرور نوت‌بوک‌ها فعال هستند و نوت‌بوک‌های غیرفعال پس از ۱ ساعت عدم فعالیت خاموش می‌شوند.نصب پکیج از داخل نوت‌بوک https://www.aparat.com/v/5eEUn در داخل نوت‌بوک‌ها با استفاده از اوپراتور «!» در ابتدای خط هر سلول می‌توان دستوری را از داخل نوت‌بوک روی کرنل سیستم‌عامل سرور اجرا کرد و خروجی آن را داخل نوت‌بوک مشاهده کرد. برای امتحان این موضوع دستور cal (کوتاه کلمه calendar) استفاده شد که یک تقویم کوتاه را نمایش می‌دهد.همانطور که دستور cal که یک دستور محیط ترمینال ([terminal [bash) است داخل نوت‌بوک قابل دسترسی است؛ دستورات pip یا conda که برای نصب پکیج‌ها استفاده می‌کنیم هم در دسترس هستند.برای امتحان در دسترس بودن این دو دستور لیست پکیج‌های نصب شده توسط هر دو برنامه با دستورهای pip list و conda list چاپ شد.همچنین برای مثال نصب پکیج، پکیج cowsay از داخل خود نوت‌بوک نصب و بلافاصله قابل import کردن و استفاده شد.نکته مهم در حذف پکیج از داخل نوت‌بوکاز آنجایی که از داخل نوت‌بوک‌ها امکان دادن ورودی (input) به برنامه در حال اجرا روی ترمینال (مثلا هنگام نصب پکیج توسط pip) وجود ندارد و اگر برای حذف یک پکیج نیاز به تایید یا ورود هر نوع اطلاعاتی از سمت کاربر باشد عملیات به صورت کامل انجام نمی‌شود باید از فلگ (flag) یا تنظیم q- به همراه دستور pip استفاده کرد.نصب پکیج توسط ترمینال https://www.aparat.com/v/ipXAc هر نوت‌بوک به صورت مجزا ۴ گیگ رم و ۱ گیگ فضای ذخیره‌سازی در اختیار دارد. به علاوه می‌توان به صورت مستقیم از طریق ترمینال به مدیریت و کنترل محیط اختصاصی (Environment / container) نوت‌بوک بر روی سرور پرداخت.ترمینال از طریق دکمه Terminal در صفحه کتابخانه نوت‌بوک‌ها (Libraries) در دسترس است.برای تست ترمینال مشخصات سرور چاپ شد.بر اساس کرنل انتخاب شده برای نوت‌بوک، کرنل پایتون در مسیر جداگانه‌ای مطابق زیر قرار دارد:کرنل python 2.7 :مسیر anaconda2_501/~کرنل python 3.5 :مسیر anaconda3_420/~کرنل python 3.6 :مسیر anaconda3_501/~باید به این نکته توجه کرد که پایتون داخل نوت‌بوک با پایتونی که داخل ترمینال نصب است متفاوت است و در نتیجه برنامه‌های آنها مخصوصا pip و conda که ما نیازشان داریم هم متفاوت هستند؛ چراکه پایتون محیط ترمینال آن پایتونی است که روی سرور نصب است اما پایتونی که به عنوان کرنل نوت‌بوک استفاده می‌شود در مسیری همانند آنچه بالا گفته شد قرار دارد.برای بررسی نکته بالا با استفاده از دستور which و اجرای which pip مسیر برنامه pip درون نوت‌بوک و درون ترمینال چاپ شد که مشاهده می‌شود درون نوت‌بوک، pip از درون دایرکتوری anaconda3_501 و درون ترمینال از دایرکتوری دیگری اجرا می‌شوند.برای دسترسی به برنامه pip یا conda ای که پکیج‌های نوت‌بوک را مدیریت می‌کند می‌توان به دو صورت عمل کرد:۱. اجرای مستقیم این برنامه‌ها از مسیر نصبشون به این صورت که بجای دستور pip یا conda می‌نویسیم: [anaconda3_501/bin/[pip or conda/~۲. همچنین می‌توان آدرس این برنامه‌ها را به متغیر (Environment Variable) ای به نام PATH اضافه کنیم تا به راحتی و تنها با نوشتن pip  یا conda در دسترس باشند. برای اینکار با وارد کردن دستور export PATH=~/anaconda3_501/bin:$PATH مسیر برنامه‌های نصب شده پایتون درون فولدر anaconda3_501 (این فولدر باید بر اساس کرنل انتخاب شده برای نوت‌بوک انتخاب شود) به متغیر PATH اضافه شده و برنامه‌های pip و conda (و دیگر برنامه‌ها را) تنها با اسمشان قابل اجرا می‌کند.در ادامه برای استفاده آتی ابتدا یک جمله از شکسپیر درون فایلی نوشته شد.سپس در ترمینال برای مثال نصب پکیج توسط ترمینال، پکیج lolcat نصب شد.همچنین با استفاده از lolcat جمله‌ای از شکسپیر که درون فایل ذخیره شده بود چاپ شد.آپدیتبا کلون کردن یک ریپازیتوری و اجرای نوت بوک‌های آن، تغییرات روی ریپازیتوری شامل اضافه شدن فایل‌ها، تغییر در آنها و ... درون کتابخانه سرویس Azure به صورت خودکار بروز رسانی نمی‌شود. به عبارت دیگر، با کلون کردن یک گیت ریپازیتوری، پروژه به ریپازیتوری سینک (sync) نمی‌شود. برای اینکار نیاز است تا با استفاده از git، خودمان این کتابخانه (که در واقع کلونی از کتابخانه روی GitHub) است را بروز رسانی کنیم.برای دریافت آخرین بروزرسانی‌ها به این صورت عمل می‌کنیم که ابتدا ترمینال ریپازیتوریمون رو باز می‌کنیم (بالاتر نحوه باز کردن توضیح داده شد) و سپس با دستور زیر به پوشه library که پوشه‌ای است که فایل‌های کلون شده از گیت‌هاب قرار داده شده اند میرویم:$ cd libraryدر ادامه مطابق تصویر با دستور زیر بررسی می‌کنیم آیا تغییری در فایل‌های نوت بوک داده‌شده است یا خیر:$ git statusنتیجه دستور git statusهمونطور که مشاهده می‌کنید تغییراتی درون فایل بعضی نوت‌بوک‌ها داده شده است که در قسمت Changes not staged for commit ذکر شده‌اند و فایل‌هایی جدید ایجاد شده‌اند که در قسمت Untracked files مشخص شده‌اند. لازم است قبل از بروزرسانی ریپازیتوری این تغییرات (فایل‌هایی که تغییر کرده‌اند) commit شده [در این مورد بخوانید] یا تغییرات stash شوند [در این مورد بخوانید]. دلیل این‌کار این است که در طی بروز رسانی ریپازیتوری از منبع آن در گیت‌هاب تغییرات داده شده از دست نرفته و حتی بعدا قابل انتقال به دیگر نسخه‌‌های این ریپازیتوری باشند. اگر که تغییرات داده شده روی نوت‌بوک‌ها مهم نیستند می‌توانید آن‌ها را حذف کنید.در اینجا تغییرات را پاک کرده و فایل نوت‌بوک‌ها را به حالت اول آنها برمی‌گردانیم. برای اینکار مطابق تصویر با دستور زیر تمامی فایل‌های تغییر کرده را به حالت اولشان برمی‌گردانیم:$ git checkout .نتیجه دستور git checkoutدر دستور بالا به دات (. یا نقطه) در انتهای دستور دقت کنید؛ این نقطه به معنی اعمال checkout -در نظر نگرفتن تغییرات- به همه فایل‌های modified (فایل‌های تغییر کرده یا همان فایل‌هایی که در تصویر قبلی لیست شده بودند) است.حال همانطور که از اجرای دوباره دستور git status مشخص است، تغییراتی که در فایل‌ نوت‌بوک‌ها داده بودیم حذف شده اند و نام هیچ فایلی در قسمت Changes not staged for commit ذکر نشده است.در ادامه برای گرفتن آخرین تغییرات از ریپازیتوری گیت‌هاب و اضافه کردن آنها به ریپازیتوری فعلیمون مطابق تصویر از دستور زیر استفاده می‌کنیم:$ git pullنتیجه دستور git pullپس از اجرای این دستور آخرین تغییرات به زیپازیتوری جاری در Azure اضافه می‌شود. همچنین همانطور که مشاهده می‌کنید لیست نوت‌بوک‌های تغییر کرده به همراه میزان تغییر آن‌ها نمایش داده شده است. حال با بازگشت به قسمت library سرویس azure، نوت‌بوک‌های اضافه شده قابل اجرا هستند.این پست در فروردین ۹۷ در وبلاگ بینایی ماشین قرار گرفته بوده است.</description>
                <category>محمدحسن ستاریان</category>
                <author>محمدحسن ستاریان</author>
                <pubDate>Thu, 07 Nov 2019 00:46:18 +0330</pubDate>
            </item>
                    <item>
                <title>تبدیل Jupyter Notebook به Rest API Endpoint</title>
                <link>https://virgool.io/@mh_sattarian/jupyter-notebook-into-rest-api-avirgywcok43</link>
                <description>ژوپیتر نوت‌بوک‌ها (Jupyter notebooks) فوق‌العاده هستن، به ما اجازه می‌دهند که به سرعت و راحتی شروع به بررسی و کد زدن (یا به اصطلاح prototype and experiment) کنیم؛ اما آیا می‌دونستید که می‌تونیم از اون‌ها به عنوان یک Backend استفاده کنیم؟در این آموزش، می‌بینیم که چطور از یک نوت‌بوک به عنوان یک API ساده استفاده کنیم -مشابه همون‌کاری که Runkit که ابزاری مشابه برای Javascript است انجام می‌دهد- و یک API ساده برای تبدیل درجه به رادیان می‌سازیم؛ فایل این نوت‌بوک در این آدرس در دسترس است.بنابراین، در نهایت ما Endpointای خواهیم داشت که می‌توانیم از اون به صورت زیر استفاده کنیم:$ curl &amp;quothttp://serverIp:8888/convert?angle=180&amp;quot
#{&amp;quotconvertedAngle&amp;quot: 3.141592653589793}نصب و راه‌اندازیبا فرض اینکه jupyter notebook  را نصب دارید، در ابتدا پکیج kernel_gateway را به صورت زیر نصب کنید:$ pip install jupyter_kernel_gatewayسپس با وارد کردن دستور زیر، تنظیمات kernel_gateway را بسازید:$ jupyter kernelgateway --generate-configبا وارد کردن این دستور فایل تنظیمات پیش‌فرض در آدرس ذکر شده ساخته می‌شوند. برای آنکه API شما بتواند توسط دیگر سیستم‌ها قابل دسترسی باشد این فایل را مطابق زیر تغییر دهید:# ~/.jupyter/jupyter_kernel_gateway_config.py

c.KernelGatewayApp.ip = &#039;*&#039;ساختن APIیک نوت‌بوک (برای مثال با نام JNB_endpoint.ipynb) ساخته و در یک سلول پکیج‌های مورد استفاده را import می‌کنیم:import math
import json

REQUEST = json.dumps({
&#039;path&#039; : {},
&#039;args&#039; : {}
})این سلول، در هنگام لود نوت‌بوک تنها یکبار اجرا می‌شود، می‌توانید تمامی initializationهای خود را در این سلول انجام دهید.*سپس سلولی دیگر مطابق زیر می‌سازیم:# GET /convert
req = json.loads(REQUEST)
args = req[&#039;args&#039;]

if &#039;angle&#039; not in args:
  print(json.dumps({&#039;convertedAngle&#039;: None}))
else:
  # Note the [0] when retrieving the argument.
  # This is because you could potentially pass multiple angles.
  angle = int(args[&#039;angle&#039;][0])
  converted = math.radians(angle)
  print(json.dumps({&#039;convertedAngle&#039;: converted}))به خط اول توجه کنید، در این خط با استفاده از یک کامنت، برای kernel gateway مشخص کردیم که در چه آدرسی (اینجا convert/) و به چه نوع ریکوئستی (اینجا GET) گوش کند. این سلول با هر ریکوئست GET در آدرس مشخص شده اجرا می‌شود.در نهایت به صورت زیر نوت‌بوک خود را اجرا کنید:$ jupyter kernelgateway --KernelGatewayApp.api=&#039;kernel_gateway.notebook_http&#039; --KernelGatewayApp.seed_uri=&#039;/home/username/JNB_endpoint.ipynb&#039; --port 8888به همین راحتی ما نوت‌بوک خودمون رو به یک Rest API تبدیل کردیم. حالا اگر یک ریکوئست به Endpointمون که آدرس سرور و پورتی است که در دستور بالا مشخص کردیم (پیش‌فرض 8888:) بفرستیم نتیجه مورد انتظارمون رو می‌بینیم:$ curl &amp;quothttp://serverIp:8888/convert?angle=180&amp;quot
#{&amp;quotconvertedAngle&amp;quot: 3.141592653589793}* هنگام ساخت نوت‌بوک به ساختن و initialize متغیر REQUEST دقت کنید، اینکار برای آن است که نوت‌بوک ما به صورت عادی قابل اجرا باشد، REQUEST متغیر خاصی است که Kernel Gateway به سلول‌های مربوط به API می‌فرستد. اگر این متغیر تعریف نشده باشد، با اجرای نوت‌بوک با استفاده از دستور بالا مشکلی ایجاد نمی‌کند اما اجرای عادی نوت‌بوک به دلیل تعریف نشدن این متغیر با خطا مواجه می‌شود؛ بنابراین این متغیر را initialize کردیم تا از این خطا جلوگیری کنیم.نوت‌بوکی که ساختیم در این آدرس قابل دسترسی است و می‌توانید اینجا مشاهده کنید: https://gist.github.com/mhsattarian/ca0cf359343d7b3894175660ac07be36 منابع+ وبلاگ OUseful.Info+ وبلاگ Eliot Andresاین پست در وبلاگ بینایی کامپیوتر نیز منتشر شده است.</description>
                <category>محمدحسن ستاریان</category>
                <author>محمدحسن ستاریان</author>
                <pubDate>Thu, 31 Oct 2019 17:47:38 +0330</pubDate>
            </item>
                    <item>
                <title>چگونه یک لوکال سرور ران کنیم؟ اند مور</title>
                <link>https://virgool.io/mappub/localserver-msyemxke540b</link>
                <description>مقدمهگاهی اوقات وقتی یک پروژه وب رو دانلود می‌کنیم و سعی میکنیم که اجراش کنیم بعضی عملکردها یا جزئیات سایت مثل عکس‌ها یا استایل‌ها درست نمایش داده نمی‌شوند و یا حتی فقط با یک صفحه سفید مواجه می‌شیم.اگر کنسول مرورگر (Browser console) را باز کنیم با خطاهایی مثل خطاهای زیر مواجه می‌شیم:هنگام پیدا نکردن فایل  دلیل این خطا این است که مرورگر فایل‌های درخواستی کاربر را پیدا نمی‌کند؛ یکی از دلایلی که باعث به وجود آمدن این مشکل می‌شود، این است که طراح/برنامه نویسی که پروژه را درست کرده در زمان آدرس دهی‌ها، بجای آدرس‌دهی نسبی (relative) -آدرس‌دهی از فایل جاری- از آدرس‌دهی مطلق (absolute) -آدرس‌دهی از root پروژه- استفاده کرده است؛ چراکه در فایل‌های جاوااسکریپت (JavaScript) نمی‌توان از آدرس دهی نسبی استفاده کرد و فایل‌های لازم باید از فایل index.html که در نهایت همه فایل‌های جاوااسکریپت در آن مشخص و استفاده می‌شوند آدرس‌دهی شوند؛ از آنجایی که این کار در اکثر مواقع بسیار سخت است آدرس‌دهی‌ها به صورت مطلق انجام می‌شوند.هنگام درخواست Ajax برای یک فایل
این خطا به این دلیل به وجود می‌آید که ارسال درخواست Ajax در پروتکل file -که برای دریافت فایل‌ها بدون وب سرور استفاده می‌شود- امکان پذیر نیست و فایل‌ها باید از طریق پروتلکل http/https که برای انتقال اطلاعات در بستر وب استفاده می‌شود دریافت شوند.لوکال سروررابطه وب‌سرور و مرورگر برای حل مشکلات فوق، باید از نرم‌افزارهای لوکال سرور استفاده کنیم. لوکال سرور یک برنامه وب‌سرور است که یک سرور برای تبادل داده طبق پروتکل HTTP روی سیستمی که اجرا می‌شود (معمولا در آدرس 127.0.0.1 سیستم که آدرس رزرو شده سیستم برای شبیه‌سازی اتصالات است و با آدرس زیر شناخته می‌شود) به وجود می‌آورد.http://localhostایجاد یک لوکال سروربرای ایجاد لوکال سرور روش‌های مختلفی وجود دارد که سعی می‌کنیم راحتترین‌های آنها را معرفی کنیم:JavaScript -&gt; lite-serverاگر روی سیستم خود Nodejs را نصب دارید، راحتترین روش برای ایجاد یک لوکال سرور استفاده از lite-server است. برای نصب دستور زیر را در ترمینال وارد می‌کنیم:[sudo] npm i lite-server --globalپس از نصب برنامه، کافیست به فولدر پروژه رفته و یک پنجره ترمینال بازکرده و دستور زیر را بزنیم:lite-serverحال -همانطور که در توضیحات چاپ شده پس از دستور نوشته شده- برنامه در آدرس زیر اجرا می‌شود:http://localhost:3000JavaScript -&gt; http-serverابزار جاوااسکریپتی دیگر برای ایجاد یک لوکال سرور http-server است که نحوه نصب و استفاده از آن مشابه قبل است: [sudo] npm i -g http-serverPython -&gt; SimpleHTTPServerیکی دیگر از روش‌های ساده ایجاد یک لوکال سرور استفاده از ماژول SimpleHTTPServer پایتون است. برای استفاده از آن لازم است پایتون روی سیستم نصب باشد.برای راه اندازی سرور کافیست در فولدر پروژه خود یکی از دستورهای زیر را با توجه به ورژن پایتون خود استفاده کنید: python 2
[sudo] python -m SimpleHTTPServer 133
# python 3
[sudo] python3 -m http.server 133PHP -&gt; Built-in web serverروش دیگر برای ساخت لوکال سرور استفاده از وب‌سرور داخلی PHP است. برای استفاده کافیست در مسیر پروژه دستور زیر را وارد کنید تا پروژه در آدرس وارد شده سرو شده و قابل دسترس شود.php -S localhost:8000FENIXنرم‌افزار سبک و سریع FENIX برای راه‌اندازی لوکال‌سرورهای مختلف با امکان اختصاص دادن آی‌پی پابلیک به پروژه‌ها برای دسترسی از خارج سیستم است؛ نحوه استفاده را اینجا بخوانید. Stacks -&gt; WAMP/XAMP/AMPPS/easyPHPروش دیگر استفاده از یکی از محیط‌های توسعه php مانند WAMP، XAMP، AMPPS و یا easyPHP است که وب‌سروری داخلی برای اجرا در اختیار می‌گذارند.انتشار صفحات وب پایا (Static web publishing)علاوه بر استفاده از یک لوکال سرور و اجرای پروژه وب به وسیله آن‌ها می‌توان از یکی از برنامه‌های انتشار صفحات پایا (Static pages) مانند surge.sh و یا netlify استفاده کرد. در ادامه نحوه استفاده از surge توضیح داده می‌شود:Surge.shدپلوی کردن یک پروژه وب با استفاده از این سرویس به سادگی وارد کردن یک دستور در ترمینال است؛ تنها کافیست از پیش Nodejs روی سیستم شما نصب باشد.برای نصب surge دستور زیر را در ترمینال وارد می‌کنیم:npm install --global surgeسپس برای دپلوی پروژه خود کافیست در مسیر پروژه دستور زیر را وارد کنیم:surgeبا وارد کردن این دستور از شما خواسته می‌شود که مسیر پروژه را تایید کنید. در صورت لزوم می‌توانید مسیر را تغییر داده و با فشردن دکمه enter آن‌را تایید کنید. سپس یک نام تصادفی برای پروژه انتخاب شده و برای تایید نمایش داده می‌شود که می‌توانید به آدرس دلخواه خود تغییر بدهید. فقط باید توجه کنید که نام دلخواه خود را قالب زیر وارد کنید:[your-chosen-name].surge.shسپس با فشردن کلید enter پروژه آپلود شده و در آدرس انتخابی قابل دسترسی است.دسترسی از خارجبا سرو شدن پروژه‌ها در سیستم،‌ امکان دسترسی به آن‌ها در خود سیستم فراهم می‌شود، همچنین کاربران شبکه می‌توانند با آی‌پی سیستم به پورت مربوطه متصل شده و به پروژه دسترسی داشته باشند. اما از خارج از شبکه دسترسی به پروژه امکان پذیر نیست. برای دسترسی از خارج شبکه به پروژه برای نمایش یا تست آن می‌توانیم از ابزارهای زیر استفاده کنیم:ngrokngrok ابزاری برای ایجاد یک تونل به یکی از پورت‌های سیستم از طریق پروتکل‌های مختلف است که از SSL هم پشتیبانی می‌کند. برای استفاده پس از ثبت‌نام در سرویس و لاگین کردن کافیست همانند دستور زیر پروتکل و پورت مربوطه که هنگام سرو کردن پروژه انتخاب می‌کنید را وارد کنید تا یک IP پابلیک برای دسترسی به پروژه در نظر گرفته شود:ngrok http 3000localtunnelابزاری دیگری است که برای اینکار می‌توان استفاده کرد. برا ی استفاده از آن باید Nodejs روی سیستم شما نصب باشد.برای نصب آن از دستور زیر استفاده می‌کنیم:npm install -g localtunnelپس از نصب کافیست پروژه را در پورت دلخواه سرو کرده و با استفاده از دستور زیر به آن یک آی‌پی پابلیک اختصاص دهیم:lt --port 3000</description>
                <category>محمدحسن ستاریان</category>
                <author>محمدحسن ستاریان</author>
                <pubDate>Sun, 02 Jun 2019 16:22:27 +0430</pubDate>
            </item>
            </channel>
</rss>