پنل مدیریت وان سیگنال از مدتها پیش تنها با فیلتر شکن باز میشد اما کاربرهایی که IP ایران رو داشتند بدون مشکل در OneSignal
رجیستر میشدن و در نتیجه مشکلی برای ارسال Push Notification وجود نداشت.
چندماه پیش بود که خبر اومد که APIهای وان سیگنال به روی کاربران ایرانی بسته شده و در نتیجه کاربرهامون دیگه تو سرویس وان سیگنال رجیستر نمیشن و عملاً این سرویس برای توسعهدهندهگان ایرانی که هدفشون بازار ایران هست کاربردی نداره.
همون زمانها خاطرم هست که مطلبی رو خوندم که اشاره شده بود چون مشکل فقط در مرحلهی اول و دریافت Player ID
هست، اگر این API رو از طریق سرور خودمون ارسال کنیم مشکل حل میشه.
چند هفته پیش لازم شد تا در پروژهی جدید از سرویس Push Notification استفاده کنم. تو این پروژه اپ Android و iOS داریم و خب باید از سرویسی استفاده کنیم که هردو رو ساپورت کنه.
با توجه به ماجرای OneSignal
اول از همه رفتم سمت سرویسهای ایرانی و البته سرویس FCM
خود گوگل. در تست من، مشکلی در سرویس FCM وجود نداشت و Device token
به درستی دریافت میشد، البته وقتی تلاش کردم به همهی کاربران از طریق پنل FCM پوش ارسال کنم، ناموفق بود اما اگر تنها یک Device رو هدف قرار میدادم، پوش به درستی دریافت میشد. برای من محرز شد که FCM مشکلی در رجیستر کردن کاربران (حداقل در اندروید) نداره و نهایتاً باید پنلی براش بنویسم یا اینکه از گزینههای اوپن سورس موجود (پنل هایی که برای ارسال پوش از FCM استفاده میکنن) که تعدادشون کم هم نبود استفاده کنم. تنها مشکل این بود که دولوپر iOS نتیجهی دیگری گرفته بود که در iOS بدون فیلتر شکن کاربران در سرویس FCM رجیستر نمیشن. که البته برای من عجیب بود این اختلاف در دو پلتفرم (که البته راهی برای رد یا تاییدش درحال حاضر ندارم)
پس از اون سرویسهای ایرانی رو بررسی کردیم، پوشه اولین گزینه بود اما ظاهراً SDK
مختص iOS
نداره هنوز و به همین دلیل حذف شد. سایر سرویس ها هم یا قیمت بالایی داشتن و یا پیچیدگی زیادی در پیاده سازی داشتن (که خب باعث شد اطمینان کمتری به کیفیت سرویس در من ایجاد بشه).
نهایتن تصمیم گرفتم مشکل OneSignal رو حل کنم. چون قبلن هم در چندین پروژه ازش استفاده کرده بودم و کلی متد کمکی برای شرایط متفاوت براش نوشته بودم که اینجا هم با دردسری کمتری میتونستم ازشون بهره ببرم، اما چون مطلبی که چندماه پیش خونده بودم رو پیدا نکردم، خودم شروع به پیادهسازیش کردم و دلیل اصلی نوشتن این پست هم همین هست، شاید در شرایطی به کمک دیگری بیاد.
با بررسی SDK وان سیگنال برای اندروید و iOS متوجه شدم که رفع مشکل خیلی ساده هست و و کافیه چیزی شبیه پروکسی سمت سرور ایجاد بشه تا درخواست ها از اون طریق ارسال بشه و در SDK هم تغییرات اندکی ایجاد بشه.
Android:
iOS:
اگه لینکهای بالا رو بررسی کنید میبینید که خیلی راحت میشه درخواست ها رو به Url
دیگری ارسال کرد. کافیه که سروری در خارج کشور داشته باشیم و یک API ساده که همهی درخواست ها رو دریافت کنه، به سمت سرور OneSignal ارسال کنه و پاسخ رو برگردونه.
کد زیر به زیان PHP
و فریموورک Slim
هست. البته مشخصه که مفهوم کلی بسیار ساده هست و با هر زبان دیگه و یا هر فریموورکی قابل پیاده سازی هست:
<?php require_once __DIR__ . '/vendor/autoload.php'; use GuzzleHttp\Client; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; $app = new Slim\App(); //region Helper Functions function startsWith($haystack, $needle) { // search backwards starting from haystack length characters from the end if (is_array($needle)) { foreach ($needle as $str) { if (strrpos($haystack, $str, -strlen($haystack)) !== FALSE) { return TRUE; } } return FALSE; } return $needle === "" || strrpos($haystack, $needle, -strlen($haystack)) !== FALSE; } /** * @param Response $response * @param string|array $body * @param int $status * * @return \Psr\Http\Message\ResponseInterface */ function postResponse(Response $response, $body, $status = 200) { $response->getBody()->write(is_array($body) ? json_encode($body, JSON_UNESCAPED_UNICODE) : $body); return $response->withStatus($status > 0 ? $status : 200); } /** * @param Request $request * * @return array */ function getInputData(Request $request) { $data = $request->getParsedBody(); if (empty($data)) { $data = $request->getQueryParams(); if (empty($data)) { $data = $_REQUEST; } } if (startsWith($data, "{") || startsWith($data, "[")) { return json_decode($data, TRUE); } parse_str($data, $array); return $array; } //endregion $app->any('/one_signal[/{params:.*}]', function (Request $request, Response $response, $args) { $method = $request->getMethod(); $sentHeaders = $request->getHeaders(); $uri = $args['params']; $body = getInputData($request); $headers = []; foreach ($sentHeaders as $header => $value) { $normalizedHeader = str_replace('HTTP_', '', $header); if (startsWith($normalizedHeader, 'HOST')) { continue; } $headers[$normalizedHeader] = $value[0]; } foreach ($body as $key => $value) { $correctType = $value; if (in_array($key, ['device_type', 'timezone', 'session_count', 'created_at', 'playtime', 'badge_count', 'last_active', 'notification_types', 'test_type', 'net_type', 'game_version'])) { $correctType = (int)$value; } else { if (in_array($key, ['lat', 'long'])) { $correctType = (double)$value; } else { if ($key == 'rooted') { $correctType = filter_var($value, FILTER_VALIDATE_BOOLEAN); } } } $body[$key] = $correctType; } $client = new Client(); $options = [ 'headers' => $headers ]; if (strtolower($method) != 'get') { $options['json'] = $body; } $result = $client->request($method, "https://onesignal.com/api/v1/{$uri}", $options); $responseHeaders = $result->getHeaders(); foreach ($responseHeaders as $header => $value) { $response->withAddedHeader($header, is_array($value) ? $value[0] : $value); } return postResponse($response, (string)$result->getBody()); }); $app->run();
اینجا از پکیج Slim Framework
برای ساخت API استفاده شده که با Composer
به پروژه اضافهش کنید:
composer require slim/slim "^3.0"
برای ارسال درخواستهای HTTP
هم از کتابخانهی Guzzle
استفاده شده:
composer require guzzlehttp/guzzle:~6.0
البته باید توجه داشته باشید که در این کدها، هیچ عامل محدودکننده گزاشته نشده، یعنی اگه فرد دیگری به این API درخواست رو ارسال کنه میتونه تو اپ خودش ازش استفاده کنه، که خب میتونه سربار زیادی برای سرورتون ایجاد کنه. منطقی هست که مقدار app_id
در دیتای ورودی رو چک کنید و اگر متفاوت از app_id
مختص خودتون بود، پیغام خطا بدید و درخواست رو رد کنید.
if ($body['app_id'] != "YOUR_APP_ID") { return postResponse($response, ['error_code' => -100, 'error_message' => 'Request rejected'], 400); }
حالا کافیه که SDK رو به پروژهی اندروید یا iOS اضافه کنید، و مقادیری که در ابتدای پست بهش اشاره شد رو به آدرس سرور خودتون تغییر بدید.
مثلاً به جای https://onesignal.com/api/v1/
از آدرس https://your-domain.com/one_signal/
استفاده کنید
برای اندروید سورس SDK رو دانلود کنید و بعنوان ماژول به پروژه اضافه کنید و اون خط رو تغییر بدید. برای iOS هم به همین شکل.
البته ناگفته نماند که در انتهای تحقیقات برای یافتن جایگزین OneSignal به سایت https://yeksignal.com رسیدم، اما چون راهحل سادهتر از چیزی بود که فکر میکردم و سرور خارج از ایران هم داشتم نیازی به استفاده از این سرویس ندیدم. اگر سرور خارج از ایران ندارید و مجبورید سرور جداگانه تهیه کنید، شاید برای شما گزینهی معقولی باشه.
- ۰ نظر
- ۰۸ آذر ۹۷ ، ۲۱:۵۴
- ۳۸۱۶ نمایش