رفتن به محتوای اصلی
Price App API

راهنمای ربات قیمت تلگرام

ساخت ربات قیمت ارز و طلا با API سرویکس و Telegram

یک ربات قیمت تلگرام را با Node.js، API سرویکس، کنترل تازگی، نگهداری امن کلید، مدیریت خطا و retry محدود برای ارز، طلا یا سکه بسازید.

این راهنما یک نمونه اجرایی برای اتصال بک اند شما به API سرویکس و Telegram Bot API است. سرویکس SDK رسمی تلگرام ارائه نمی کند و ربات مشتری را میزبانی یا اداره نمی کند؛ کد روی سرور یا worker خودتان اجرا می شود.

جریان داده ربات

  1. worker شما کاتالوگ نمادهای سرویکس را با API Key می خواند؛
  2. نماد انتخابی، مقدار، businessTime و وضعیت stale بررسی می شود؛
  3. پیام فارسی فقط وقتی مقدار قابل نمایش وجود دارد ساخته می شود؛
  4. پیام با متد sendMessage به Telegram Bot API فرستاده می شود؛
  5. خطاهای دائمی متوقف و خطاهای شبکه یا 5xx با retry محدود مدیریت می شوند.

برای دریافت چند قیمت در یک اجرا، کاتالوگ را یک بار بخوانید و پیام را از همان snapshot بسازید. برای هر کاربر یا هر نماد درخواست جداگانه نفرستید.

پیش نیاز و متغیرهای محیطی

  • Node.js 20 یا جدیدتر با fetch داخلی؛
  • حساب فعال سرویکس و API Key ساخته شده در Profile؛
  • توکن ربات Telegram و chat_id مقصد؛
  • اجرا در سرور، worker یا job زمان بندی شده، نه JavaScript مرورگر.
export SERVIX_API_KEY="YOUR_SERVIX_API_KEY"
export TELEGRAM_BOT_TOKEN="YOUR_TELEGRAM_BOT_TOKEN"
export TELEGRAM_CHAT_ID="@YOUR_CHANNEL_OR_CHAT_ID"
export SERVIX_ASSET_CODE="USD_RLS"

node telegram-price-bot.mjs

توکن Telegram طبق قرارداد Bot API بخشی از URL درخواست است؛ URL کامل را در لاگ، analytics یا پیام خطا ثبت نکنید. API Key سرویکس فقط در هدر X-API-Key فرستاده می شود.

نمونه کامل Node.js

نمونه زیر کاتالوگ GET /api/v1/assets/supported را مصرف می کند تا علاوه بر مقدار، پشتیبانی تامین کننده، موجود بودن آخرین قیمت و stale را نیز بررسی کند. کد پیش فرض USD_RLS است و می توانید آن را با GOLD_18_RLS یا SEKKEH_RLS جایگزین کنید.

const REQUIRED_ENVIRONMENT = [
  "SERVIX_API_KEY",
  "TELEGRAM_BOT_TOKEN",
  "TELEGRAM_CHAT_ID",
];
const RETRYABLE_STATUS = new Set([500, 502, 503, 504]);

function requireEnvironment(name) {
  const value = process.env[name]?.trim();
  if (!value) throw new Error("Missing environment variable: " + name);
  return value;
}

const delay = (milliseconds) =>
  new Promise((resolve) => setTimeout(resolve, milliseconds));
const retryDelay = (attempt) =>
  500 * (2 ** (attempt - 1)) + Math.floor(Math.random() * 250);

async function requestJson(service, url, options, retryTelegram429 = false) {
  const maximumAttempts = 3;
  for (let attempt = 1; attempt <= maximumAttempts; attempt += 1) {
    let response;
    try {
      response = await fetch(url, options);
    } catch (error) {
      if (attempt === maximumAttempts) {
        throw new Error(service + " network request failed.", { cause: error });
      }
      await delay(retryDelay(attempt));
      continue;
    }

    const body = await response.json().catch(() => ({}));
    if (response.ok) return body;

    if (response.status === 429 && retryTelegram429) {
      const retryAfter = Number(body.parameters?.retry_after);
      if (Number.isFinite(retryAfter) && attempt < maximumAttempts) {
        await delay(retryAfter * 1000);
        continue;
      }
    }

    const requestId = response.headers.get("x-request-id")
      ?? body.requestId
      ?? "unknown";
    const detail = body.code ?? body.description ?? "request failed";
    if (!RETRYABLE_STATUS.has(response.status)
        || attempt === maximumAttempts) {
      throw new Error(
        service + " HTTP " + response.status + ": " + detail + "; requestId=" + requestId,
      );
    }
    await delay(retryDelay(attempt));
  }
  throw new Error(service + " request failed unexpectedly.");
}

async function main() {
  for (const name of REQUIRED_ENVIRONMENT) requireEnvironment(name);
  const servixApiKey = requireEnvironment("SERVIX_API_KEY");
  const telegramBotToken = requireEnvironment("TELEGRAM_BOT_TOKEN");
  const telegramChatId = requireEnvironment("TELEGRAM_CHAT_ID");
  const assetCode = process.env.SERVIX_ASSET_CODE?.trim() || "USD_RLS";

  const catalog = await requestJson(
    "Servix",
    "https://servix.cc/api/v1/assets/supported",
    { headers: { "X-API-Key": servixApiKey } },
  );
  const asset = Array.isArray(catalog.assets)
    ? catalog.assets.find((item) => item.code === assetCode)
    : undefined;

  if (!asset?.providerSupported || !asset.latestValueAvailable) {
    throw new Error("No displayable value for " + assetCode + ".");
  }
  const numericValue = Number(asset.latestValue);
  if (!Number.isFinite(numericValue)) {
    throw new Error("Non-numeric value for " + assetCode + ".");
  }

  const unit = assetCode.endsWith("_RLS") ? "ریال" : "دلار";
  const freshness = asset.stale
    ? "⚠️ داده قدیمی است"
    : "✅ داده تازه است";
  const message = [
    "قیمت " + (asset.labelFa || assetCode),
    new Intl.NumberFormat("fa-IR").format(numericValue) + " " + unit,
    freshness,
    "زمان داده: " + (asset.businessTime || "نامشخص"),
  ].join("\n");

  await requestJson(
    "Telegram",
    "https://api.telegram.org/bot" + telegramBotToken + "/sendMessage",
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ chat_id: telegramChatId, text: message }),
    },
    true,
  );
}

main().catch((error) => {
  console.error(error instanceof Error ? error.message : "Price bot failed.");
  process.exitCode = 1;
});

رفتار خطا و retry

وضعیترفتار ربات
400 یا 417 سرویکسورودی یا بازه اشتباه است؛ بدون اصلاح درخواست retry نکنید.
401 یا 403 سرویکسکلید، حساب تست یا اشتراک را بررسی کنید؛ کلید را در لاگ ننویسید.
404 سرویکسکد نماد یا موجود بودن داده را با کاتالوگ کنترل کنید.
429 سرویکسسهمیه روزانه تمام شده است؛ polling را متوقف کنید و در حلقه سریع retry نکنید.
شبکه یا 5xxحداکثر سه تلاش با تاخیر نمایی و jitter؛ سپس هشدار عملیاتی ثبت کنید.
429 تلگراماگر parameters.retry_after موجود بود، همان تعداد ثانیه صبر کنید و تعداد تلاش را محدود نگه دارید.

کد نمونه فقط خطاهای شبکه و وضعیت های 500، 502، 503 و 504 را خودکار تکرار می کند. برای جزئیات پاسخ سرویکس، راهنمای خطاها و سهمیه را بخوانید.

زمان بندی، cache و داده مفقود

  • تناوب job را از نیاز واقعی محصول و سهمیه روزانه محاسبه کنید؛
  • کاتالوگ یا snapshot موفق را در بک اند cache کنید و میان کاربران به اشتراک بگذارید؛
  • اگر latestValueAvailable نادرست است، قیمت صفر یا مقدار قبلی بدون هشدار نسازید؛
  • وقتی stale درست است، businessTime و هشدار داده قدیمی را در پیام نگه دارید؛
  • برای نمودار یا گزارش گذشته از API داده تاریخی استفاده کنید.

امنیت و دسترسی Telegram

توکن ها را در secret manager یا متغیر محیطی نگه دارید، دسترسی فایل و CI را محدود کنید و هنگام احتمال افشا کلید یا توکن را تعویض کنید. برای پیام خصوصی، کاربر باید ابتدا با ربات تعامل کرده باشد؛ برای کانال یا گروه نیز ربات باید مجوز مناسب داشته باشد. قرارداد sendMessage و ResponseParameters را در مستندات رسمی Telegram Bot API بررسی کنید.

صفحه های مرتبط و شروع کار

برای انتخاب داده، مرکز نرخ ارز، طلا و سکه را ببینید. سپس راهنمای API Key را مرور کنید. حساب تست برای اعتبارسنجی این جریان مناسب است و پلن تولیدی باید با تناوب job و تعداد درخواست روزانه هماهنگ باشد.

ساخت حساب و دریافت API Key · مقایسه پلن ها و سهمیه ها