Logo OmniDevvKANA Web
Kembali ke katalog plugin

search

resep

Cari resep masakan dari TheMealDB

Search recipes from TheMealDB

plugins/search/resep.js

Lokasi file di repo kana-md

2

Perintah termasuk alias

3.4 KB

Ukuran file sumber

4/11/2026

Update terakhir, commit 44f4010

Daftar perintah

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

.resep.recipe

Cara pakai

.resep <kata kunci>

Contoh

.resep chicken

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 { downloadBuffer } from '../../src/scrapers/runner.js'
import { sendButtons } from '../../src/ui/buttons.js'

export const config = {
  name: 'resep',
  alias: ['recipe'],
  category: 'search',
  description: { id: 'Cari resep masakan dari TheMealDB', en: 'Search recipes from TheMealDB' },
  usage: '.resep <kata kunci>',
  example: '.resep chicken',
  cooldown: 5,
  limit: 3,
}

export const i18n = {
  id: {
    resep: {
      needQuery: 'Ketik nama makanannya dulu yaa, contoh: .resep chicken~ 🍳',
      notFound: 'Mouu, resep "{query}" nggak ketemu di TheMealDB. Coba kata bahasa Inggris lain ya~',
      failed: 'Mouu, pencarian resep gagal: {message}. Coba lagi nanti ya~',
      header: '🍳 {name}',
      meta: 'Kategori: {category} | Asal: {area}',
      ingredientsTitle: 'Bahan utama:',
      stepsTitle: 'Cara masak (ringkas):',
      others: 'Hasil lain: {names}',
      btnOther: 'Resep {name}',
      btnMenu: 'Menu Pencarian',
    },
  },
  en: {
    resep: {
      needQuery: 'Type a food name first, example: .resep chicken~ 🍳',
      notFound: 'Mouu, recipe "{query}" was not found on TheMealDB. Try another English keyword~',
      failed: 'Mouu, recipe search failed: {message}. Try again later~',
      header: '🍳 {name}',
      meta: 'Category: {category} | Origin: {area}',
      ingredientsTitle: 'Main ingredients:',
      stepsTitle: 'Steps (short version):',
      others: 'Other results: {names}',
      btnOther: '{name} recipe',
      btnMenu: 'Search Menu',
    },
  },
}

function mealIngredients(meal) {
  const list = []
  for (let i = 1; i <= 20; i++) {
    const ing = String(meal[`strIngredient${i}`] ?? '').trim()
    if (!ing) continue
    const measure = String(meal[`strMeasure${i}`] ?? '').trim()
    list.push(measure ? `${ing} (${measure})` : ing)
  }
  return list
}

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

export async function handler(ctx) {
  const query = ctx.text.trim()
  if (!query) return ctx.reply(ctx.t('resep.needQuery'))
  let meals
  try {
    const url = `https://www.themealdb.com/api/json/v1/1/search.php?s=${encodeURIComponent(query)}`
    const { buffer } = await downloadBuffer(url, { timeout: 20000 })
    meals = JSON.parse(buffer.toString('utf8'))?.meals
  } catch (err) {
    return ctx.reply(ctx.t('resep.failed', { message: err.message }))
  }
  if (!Array.isArray(meals) || !meals.length) return ctx.reply(ctx.t('resep.notFound', { query }))
  const meal = meals[0]
  const ingredients = mealIngredients(meal).slice(0, 10)
  const steps = String(meal.strInstructions ?? '').replace(/\s+/g, ' ').trim().slice(0, 1200)
  const others = meals.slice(1, 6)
  const lines = [
    ctx.t('resep.header', { name: meal.strMeal }),
    ctx.t('resep.meta', { category: meal.strCategory || '-', area: meal.strArea || '-' }),
    '',
    ctx.t('resep.ingredientsTitle'),
    ...ingredients.map((ing) => `• ${ing}`),
    '',
    ctx.t('resep.stepsTitle'),
    steps,
  ]
  if (others.length) lines.push('', ctx.t('resep.others', { names: others.map((m) => m.strMeal).join(', ') }))
  const body = lines.join('\n')
  const replies = others.slice(0, 3).map((m) => ({
    text: ctx.t('resep.btnOther', { name: String(m.strMeal).slice(0, 14) }),
    id: `.resep ${m.strMeal}`,
  }))
  replies.push({ text: ctx.t('resep.btnMenu'), id: '.menu search' })
  return sendButtons(ctx.sock, ctx.chat, {
    title: 'KANA',
    body,
    footer: ctx.t('menu.footer'),
    replies,
    textFallback: body,
  })
}