Logo OmniDevvKANA Web
Kembali ke katalog plugin

search

kurs

Konversi kurs mata uang, tanpa arg untuk ringkasan ke IDR

Currency conversion, no args for an IDR summary

plugins/search/kurs.js

Lokasi file di repo kana-md

2

Perintah termasuk alias

3.4 KB

Ukuran file sumber

4/11/2026

Update terakhir, commit 5e7e0e7

Daftar perintah

Ketuk tombol salin untuk menyalin perintah, lalu tempel di chat WhatsApp bot KANA.

.kurs.currency

Cara pakai

.kurs <jumlah> <DARI> <KE>

Contoh

.kurs 100 USD IDR

Kode sumber plugin

Unduh .js

Cara pasang: simpan file ini ke folder plugins/search/ di bot KANA kamu, lalu restart bot. Pemilik source code lain bisa mengambil update plugin terbaru dari halaman ini kapan saja.

import { getRates, convertAmount } from '../../src/scrapers/search/kurs.js'

export const config = {
  name: 'kurs',
  alias: ['currency'],
  category: 'search',
  description: { id: 'Konversi kurs mata uang, tanpa arg untuk ringkasan ke IDR', en: 'Currency conversion, no args for an IDR summary' },
  usage: '.kurs <jumlah> <DARI> <KE>',
  example: '.kurs 100 USD IDR',
  cooldown: 8,
  limit: 5,
}

export const i18n = {
  id: {
    kurs: {
      failed: 'Mouu, data kurs lagi nggak bisa diambil: {message}. Coba lagi nanti ya~',
      invalid: 'Formatnya: .kurs <jumlah> <DARI> <KE>, contoh: .kurs 100 USD IDR. Kode mata uang 3 huruf yaa~',
      unknownCurrency: 'Mouu, kode mata uang "{code}" nggak dikenal. Coba USD, EUR, SGD, JPY, IDR, dan sejenisnya ya~',
      result: '{amount} {from} = {result} {to}\nKurs: 1 {from} = {rate} {to}',
      summaryHeader: 'Kurs ke IDR hari ini:',
      summaryItem: '1 {code} = {value} IDR',
    },
  },
  en: {
    kurs: {
      failed: 'Mouu, exchange rate data cannot be fetched: {message}. Try again later~',
      invalid: 'Format: .kurs <amount> <FROM> <TO>, example: .kurs 100 USD IDR. Currency codes are 3 letters~',
      unknownCurrency: 'Mouu, the currency code "{code}" is not recognized. Try USD, EUR, SGD, JPY, IDR, and similar~',
      result: '{amount} {from} = {result} {to}\nRate: 1 {from} = {rate} {to}',
      summaryHeader: "Today's rates to IDR:",
      summaryItem: '1 {code} = {value} IDR',
    },
  },
}

const SUMMARY_CODES = ['USD', 'EUR', 'SGD', 'JPY']

export const test = { input: '.kurs', expect: /kurs/i }

export async function handler(ctx) {
  const locale = ctx.lang === 'id' ? 'id-ID' : 'en-US'
  const num = (value, digits = 2) =>
    new Intl.NumberFormat(locale, { minimumFractionDigits: 0, maximumFractionDigits: digits }).format(value)
  const args = ctx.args.map((a) => a.trim()).filter(Boolean)
  try {
    if (!args.length) {
      const usd = await getRates('USD')
      const idrPerUsd = usd.rates.IDR
      if (!idrPerUsd) return ctx.reply(ctx.t('kurs.failed', { message: 'IDR rate missing' }))
      const lines = [ctx.t('kurs.summaryHeader')]
      for (const code of SUMMARY_CODES) {
        const perUsd = code === 'USD' ? 1 : usd.rates[code]
        if (!perUsd) continue
        lines.push(ctx.t('kurs.summaryItem', { code, value: num(idrPerUsd / perUsd, code === 'JPY' ? 2 : 0) }))
      }
      return ctx.reply(lines.join('\n'))
    }
    if (args.length < 3) return ctx.reply(ctx.t('kurs.invalid'))
    const amount = Number(String(args[0]).replace(/\./g, '').replace(',', '.'))
    const from = args[1].toUpperCase()
    const to = args[2].toUpperCase()
    if (!Number.isFinite(amount) || !/^[A-Z]{3}$/.test(from) || !/^[A-Z]{3}$/.test(to)) {
      return ctx.reply(ctx.t('kurs.invalid'))
    }
    const rates = await getRates(from)
    const converted = convertAmount(rates, amount, to)
    if (!converted) return ctx.reply(ctx.t('kurs.unknownCurrency', { code: to }))
    return ctx.reply(
      ctx.t('kurs.result', {
        amount: num(amount),
        from,
        result: num(converted.result),
        to,
        rate: num(converted.rate, converted.rate < 1 ? 6 : 2),
      })
    )
  } catch (err) {
    const code = (err.message.match(/[A-Z]{3}/) || [])[0]
    if (/tidak valid|not valid/i.test(err.message) && code) return ctx.reply(ctx.t('kurs.unknownCurrency', { code }))
    return ctx.reply(ctx.t('kurs.failed', { message: err.message }))
  }
}