← back to kevinlanovwork-coder__dashboard

Function bodies 131 total

All specs Real LLM only Function bodies
scrapeCross function · javascript · L209-L227 (19 LOC)
scraper/run-thb.js
async function scrapeCross(browser) {
  const page = await browser.newPage();
  try {
    await page.goto('https://crossenf.com/remittance', { waitUntil: 'load', timeout: 30000 });
    await page.waitForTimeout(2000);
    // THB is already the default receiving currency — no country change needed
    const receiveInput = page.locator('input[inputmode="numeric"]').nth(1);
    await receiveInput.click({ clickCount: 3 });
    await receiveInput.fill(String(AMOUNT));
    await receiveInput.press('Tab');
    await page.waitForTimeout(3000);
    const totalRaw = await page.locator('input[inputmode="numeric"]').nth(0).inputValue();
    const total = extractNumber(totalRaw);
    if (!total) throw new Error('총 송금액 추출 실패');
    const fee = 5000;
    return { operator: 'Cross', receiving_country: COUNTRY, receive_amount: AMOUNT,
      send_amount_krw: total, service_fee: fee, total_sending_amount: total + fee };
  } finally { await page.close(); }
}
scrapeCoinshot function · javascript · L230-L253 (24 LOC)
scraper/run-thb.js
async function scrapeCoinshot(browser) {
  const page = await browser.newPage();
  try {
    await page.goto('https://coinshot.org/main', { waitUntil: 'load', timeout: 30000 });
    await page.waitForTimeout(2000);
    await page.waitForSelector('button.lang-btn[value="ko"]', { timeout: 10000 });
    await page.click('button.lang-btn[value="ko"]');
    await page.waitForTimeout(1000);
    await page.click('#current-receiving-currency');
    await page.waitForTimeout(500);
    await page.click('#select-receiving-currency a[data-currency="THB"]');
    await page.waitForTimeout(1000);
    await page.click('#receiving-input', { clickCount: 3 });
    await page.fill('#receiving-input', String(AMOUNT));
    await page.press('#receiving-input', 'Enter');
    await page.waitForTimeout(3000);
    const sendAmtRaw = await page.inputValue('#sending-input');
    const sendAmt = extractNumber(sendAmtRaw);
    if (!sendAmt) throw new Error('총 송금액 추출 실패');
    const fee = 5000; // Thailand: 5,000원 고정
scrapeJrf function · javascript · L256-L278 (23 LOC)
scraper/run-thb.js
async function scrapeJrf(browser) {
  const context = await browser.newContext({ ignoreHTTPSErrors: true });
  const page = await context.newPage();
  try {
    await page.goto('https://www.jpremit.co.kr/', { waitUntil: 'domcontentloaded', timeout: 30000 });
    await page.waitForTimeout(3000);
    await page.click('#div_curr', { force: true });
    await page.waitForSelector('li#THB', { state: 'visible', timeout: 10000 });
    await page.click('li#THB');
    await page.waitForTimeout(1500);
    await page.click('#rec_money', { clickCount: 3 });
    await page.fill('#rec_money', String(AMOUNT));
    await page.dispatchEvent('#rec_money', 'keyup');
    await page.waitForTimeout(3000);
    const sendAmtRaw = await page.inputValue('#send_money');
    const sendAmt = extractNumber(sendAmtRaw);
    if (!sendAmt) throw new Error('총 송금액 추출 실패');
    const feeRaw = await page.textContent('#servicefee').catch(() => null);
    const fee = extractNumber(feeRaw) ?? 5000;
    return { operator: 'JR
scrapeE9pay function · javascript · L281-L310 (30 LOC)
scraper/run-thb.js
async function scrapeE9pay(browser) {
  const page = await browser.newPage();
  try {
    await page.goto('https://www.e9pay.co.kr/', { waitUntil: 'domcontentloaded', timeout: 30000 });
    await page.waitForTimeout(3000);
    await page.waitForSelector('#TH_THB', { state: 'attached', timeout: 10000 });
    await page.evaluate(() => {
      const radio = document.querySelector('#TH_THB');
      radio.checked = true;
      radio.dispatchEvent(new Event('change', { bubbles: true }));
      radio.dispatchEvent(new Event('click', { bubbles: true }));
    });
    await page.waitForTimeout(1000);
    await page.waitForSelector('#reverse', { timeout: 5000 });
    await page.click('#reverse');
    await page.waitForTimeout(500);
    await page.waitForSelector('#receive-money', { timeout: 5000 });
    await page.click('#receive-money', { clickCount: 3 });
    await page.fill('#receive-money', String(AMOUNT));
    await page.dispatchEvent('#receive-money', 'blur');
    await page.waitForTimeout(
main function · javascript · L327-L411 (85 LOC)
scraper/run-thb.js
async function main() {
  const runHour = getRunHour();
  console.log(`\n[${new Date().toISOString()}] Thailand THB 스크래핑 시작 — run_hour: ${runHour}\n`);

  const browser = await chromium.launch({
    headless: true,
    args: ['--no-sandbox', '--disable-setuid-sandbox'],
  });

  const results = [];
  const errors  = [];

  console.log(`  모든 스크래퍼 병렬 실행 중... (${SCRAPERS.length}개)\n`);
  const settled = await Promise.allSettled(
    SCRAPERS.map(({ fn, needsBrowser }) => (needsBrowser ? fn(browser) : fn()))
  );

  for (let i = 0; i < settled.length; i++) {
    const { name } = SCRAPERS[i];
    const result = settled[i];
    if (result.status === 'fulfilled') {
      results.push(result.value);
      console.log(`  ✓ ${name}: 송금액 ${result.value.send_amount_krw?.toLocaleString()}원  수수료 ${result.value.service_fee?.toLocaleString()}원  합계 ${result.value.total_sending_amount?.toLocaleString()}원`);
    } else {
      console.error(`  ✗ ${name} 실패: ${result.reason?.message}`);
      errors.push(
scrapeGme function · javascript · L15-L34 (20 LOC)
scraper/run-vnd.js
async function scrapeGme(browser) {
  const page = await browser.newPage();
  try {
    await page.goto('https://online.gmeremit.com/', { waitUntil: 'domcontentloaded', timeout: 30000 });
    await page.waitForSelector('#nCountry', { timeout: 10000 });
    await page.click('#nCountry'); await page.waitForTimeout(500);
    await page.fill('#CountryValue', 'Vietnam'); await page.waitForTimeout(300);
    await page.click('#toCurrUl li[data-countrycode="VND"]');
    await page.waitForTimeout(1000);
    await page.click('#recAmt', { clickCount: 3 });
    await page.fill('#recAmt', String(AMOUNT));
    await page.dispatchEvent('#recAmt', 'change');
    await page.waitForTimeout(3000);
    const raw = await page.$eval('#numAmount', el => el.value || el.textContent).catch(() => null);
    const total = extractNumber(raw);
    if (!total) throw new Error('총 송금액 추출 실패');
    return { operator: 'GME', receiving_country: COUNTRY, receive_amount: AMOUNT,
      send_amount_krw: total, service_fee: 0
scrapeSentbe function · javascript · L37-L64 (28 LOC)
scraper/run-vnd.js
async function scrapeSentbe(browser) {
  const context = await browser.newContext({
    userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
    locale: 'ko-KR',
  });
  const page = await context.newPage();
  try {
    await page.goto('https://www.sentbe.com/ko', { waitUntil: 'networkidle', timeout: 30000 });
    await page.click('button.close').catch(() => null);
    await page.waitForTimeout(300);
    await page.click('article.app-download-popup .dim').catch(() => null);
    await page.waitForTimeout(500);
    await page.waitForSelector('.receiveAmountInput .el-input-group__append', { timeout: 10000 });
    await page.click('.receiveAmountInput .el-input-group__append'); await page.waitForTimeout(500);
    // "베트남 / 동 - VND" 선택 (USD 제외)
    await page.click('.receiveAmountInput .el-select-dropdown__item:has-text("베트남 / 동")');
    await page.waitForTimeout(1000);
    await page.click('#receiveAmount', { clickCou
Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
scrapeSbi function · javascript · L67-L89 (23 LOC)
scraper/run-vnd.js
async function scrapeSbi(browser) {
  const context = await browser.newContext({
    userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
    locale: 'ko-KR',
  });
  const page = await context.newPage();
  try {
    await page.goto('https://www.sbicosmoney.com/', { waitUntil: 'load', timeout: 30000 });
    await page.waitForTimeout(2000);
    await page.click('button:has-text("Close")').catch(() => null); await page.waitForTimeout(500);
    await page.click('.dest-country'); await page.waitForTimeout(500);
    await page.click('a[data-currency="VND"]'); await page.waitForTimeout(1500);
    await page.click('#targetAmount', { clickCount: 3 });
    await page.fill('#targetAmount', String(AMOUNT));
    await page.dispatchEvent('#targetAmount', 'input'); await page.waitForTimeout(2000);
    const raw = await page.inputValue('#krwAmount');
    const sendAmt = extractNumber(raw);
    if (!sendAmt) throw new Error('총 송금
scrapeGmoneytrans function · javascript · L92-L108 (17 LOC)
scraper/run-vnd.js
async function scrapeGmoneytrans() {
  const url = 'https://mapi.gmoneytrans.net/exratenew1/ajx_calcRate.asp'
    + `?receive_amount=${AMOUNT}`
    + '&payout_country=Viet+Nam'
    + '&total_collected=0'
    + '&payment_type=Bank+Account'
    + '&currencyType=VND';
  const res = await fetch(url, { signal: AbortSignal.timeout(15000) });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const text = await res.text();
  const serviceCharge = parseField(text, 'serviceCharge') ?? 2500;
  const sendAmount    = parseField(text, 'sendAmount');
  if (!sendAmount) throw new Error(`파싱 실패: ${text.slice(0, 200)}`);
  return { operator: 'GMoneyTrans', receiving_country: COUNTRY, receive_amount: AMOUNT,
    send_amount_krw: sendAmount, service_fee: serviceCharge,
    total_sending_amount: sendAmount + serviceCharge };
}
parseField function · javascript · L109-L112 (4 LOC)
scraper/run-vnd.js
function parseField(text, field) {
  const m = text.match(new RegExp(`${field}--td_clm--([\\d.]+)--td_end--`));
  return m ? parseFloat(m[1]) : null;
}
scrapeE9pay function · javascript · L115-L141 (27 LOC)
scraper/run-vnd.js
async function scrapeE9pay(browser) {
  const page = await browser.newPage();
  try {
    await page.goto('https://www.e9pay.co.kr/', { waitUntil: 'domcontentloaded', timeout: 30000 });
    await page.waitForTimeout(3000);
    await page.waitForSelector('#VN_VND', { state: 'attached', timeout: 10000 });
    await page.evaluate(() => {
      const radio = document.querySelector('#VN_VND');
      radio.checked = true;
      radio.dispatchEvent(new Event('change', { bubbles: true }));
      radio.dispatchEvent(new Event('click', { bubbles: true }));
    });
    await page.waitForTimeout(1000);
    await page.click('#reverse'); await page.waitForTimeout(500);
    await page.waitForSelector('#receive-money', { timeout: 5000 });
    await page.click('#receive-money', { clickCount: 3 });
    await page.fill('#receive-money', String(AMOUNT));
    await page.dispatchEvent('#receive-money', 'blur'); await page.waitForTimeout(3000);
    const raw = await page.$eval('#send-money', el => el.value).
scrapeHanpass function · javascript · L144-L161 (18 LOC)
scraper/run-vnd.js
async function scrapeHanpass() {
  const res = await fetch('https://app.hanpass.com/app/v1/remittance/get-cost', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ inputAmount: String(AMOUNT), inputCurrencyCode: 'VND',
      fromCurrencyCode: 'KRW', toCurrencyCode: 'VND', toCountryCode: 'VN',
      memberSeq: '1', lang: 'en' }),
    signal: AbortSignal.timeout(15000),
  });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const data = await res.json();
  if (data.resultCode !== '0') throw new Error(`Hanpass API 오류: ${data.resultMessage}`);
  const total = data.depositAmountIncludingFee;
  const fee   = data.transferFee ?? 0;
  if (!total) throw new Error('총 송금액 추출 실패');
  return { operator: 'Hanpass', receiving_country: COUNTRY, receive_amount: AMOUNT,
    send_amount_krw: total - fee, service_fee: fee, total_sending_amount: total };
}
scrapeCross function · javascript · L164-L184 (21 LOC)
scraper/run-vnd.js
async function scrapeCross(browser) {
  const page = await browser.newPage();
  try {
    await page.goto('https://crossenf.com/remittance', { waitUntil: 'load', timeout: 30000 });
    await page.waitForTimeout(2000);
    await page.locator('div.relative:has(span:text("THB"))').click();
    await page.waitForSelector('#aside-root ul', { timeout: 10000 });
    await page.locator('#aside-root li:has(img[alt="VN flag"])').click();
    await page.waitForTimeout(1000);
    const receiveInput = page.locator('input[inputmode="numeric"]').nth(1);
    await receiveInput.click({ clickCount: 3 });
    await receiveInput.fill(String(AMOUNT));
    await receiveInput.press('Tab'); await page.waitForTimeout(3000);
    const totalRaw = await page.locator('input[inputmode="numeric"]').nth(0).inputValue();
    const total = extractNumber(totalRaw);
    if (!total) throw new Error('총 송금액 추출 실패');
    const fee = 5000;
    return { operator: 'Cross', receiving_country: COUNTRY, receive_amount: AMOUNT,
   
scrapeJrf function · javascript · L187-L206 (20 LOC)
scraper/run-vnd.js
async function scrapeJrf(browser) {
  const context = await browser.newContext({ ignoreHTTPSErrors: true });
  const page = await context.newPage();
  try {
    await page.goto('https://www.jpremit.co.kr/', { waitUntil: 'domcontentloaded', timeout: 30000 });
    await page.waitForTimeout(3000);
    await page.click('#div_curr', { force: true });
    await page.waitForSelector('li#VND', { state: 'visible', timeout: 10000 });
    await page.click('li#VND'); await page.waitForTimeout(1500);
    await page.click('#rec_money', { clickCount: 3 });
    await page.fill('#rec_money', String(AMOUNT));
    await page.dispatchEvent('#rec_money', 'keyup'); await page.waitForTimeout(3000);
    const raw = await page.inputValue('#send_money');
    const sendAmt = extractNumber(raw);
    if (!sendAmt) throw new Error('총 송금액 추출 실패');
    const fee = 4500;
    return { operator: 'JRF', receiving_country: COUNTRY, receive_amount: AMOUNT,
      send_amount_krw: sendAmt, service_fee: fee, total_sending_amo
main function · javascript · L221-L302 (82 LOC)
scraper/run-vnd.js
async function main() {
  const runHour = getRunHour();
  console.log(`\n[${new Date().toISOString()}] Vietnam VND 스크래핑 시작 — run_hour: ${runHour}\n`);

  const browser = await chromium.launch({
    headless: true,
    args: ['--no-sandbox', '--disable-setuid-sandbox'],
  });

  const results = [];
  const errors  = [];

  console.log(`  모든 스크래퍼 병렬 실행 중... (${SCRAPERS.length}개)\n`);
  const settled = await Promise.allSettled(
    SCRAPERS.map(({ fn, needsBrowser }) => (needsBrowser ? fn(browser) : fn()))
  );

  for (let i = 0; i < settled.length; i++) {
    const { name } = SCRAPERS[i];
    const result = settled[i];
    if (result.status === 'fulfilled') {
      results.push(result.value);
      console.log(`  ✓ ${name}: 송금액 ${result.value.send_amount_krw?.toLocaleString()}원  수수료 ${result.value.service_fee?.toLocaleString()}원  합계 ${result.value.total_sending_amount?.toLocaleString()}원`);
    } else {
      console.error(`  ✗ ${name} 실패: ${result.reason?.message}`);
      errors.push({
Repobility · code-quality intelligence platform · https://repobility.com
scrapeGme function · javascript · L15-L33 (19 LOC)
scraper/run-xaf.js
async function scrapeGme() {
  const body = new URLSearchParams({
    method: 'GetExRate', pCurr: 'XAF', pCountryName: 'Cameroon',
    collCurr: 'KRW', deliveryMethod: '2', cAmt: '', pAmt: String(AMOUNT),
    cardOnline: 'false', calBy: 'P',
  }).toString();
  const res = await fetch('https://online.gmeremit.com/Default.aspx', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body, signal: AbortSignal.timeout(15000),
  });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const data = await res.json();
  if (data.errorCode !== '0') throw new Error(`GME API 오류: ${data.msg}`);
  const total = parseFloat(data.collAmt?.toString().replace(/,/g, '') ?? '');
  if (!total) throw new Error('총 송금액 추출 실패');
  return { operator: 'GME', receiving_country: COUNTRY, receive_amount: AMOUNT,
    send_amount_krw: total, service_fee: 0, total_sending_amount: total };
}
scrapeGmoneytrans function · javascript · L36-L52 (17 LOC)
scraper/run-xaf.js
async function scrapeGmoneytrans() {
  const url = 'https://mapi.gmoneytrans.net/exratenew1/ajx_calcRate.asp'
    + `?receive_amount=${AMOUNT}`
    + '&payout_country=Cameroon'
    + '&total_collected=0'
    + '&payment_type=MTN'
    + '&currencyType=XAF';
  const res = await fetch(url, { signal: AbortSignal.timeout(15000) });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const text = await res.text();
  const serviceCharge = parseField(text, 'serviceCharge') ?? 5000;
  const sendAmount    = parseField(text, 'sendAmount');
  if (!sendAmount) throw new Error(`파싱 실패: ${text.slice(0, 200)}`);
  return { operator: 'GMoneyTrans', receiving_country: COUNTRY, receive_amount: AMOUNT,
    send_amount_krw: sendAmount, service_fee: serviceCharge,
    total_sending_amount: sendAmount + serviceCharge };
}
parseField function · javascript · L53-L56 (4 LOC)
scraper/run-xaf.js
function parseField(text, field) {
  const m = text.match(new RegExp(`${field}--td_clm--([\\d.]+)--td_end--`));
  return m ? parseFloat(m[1]) : null;
}
main function · javascript · L65-L137 (73 LOC)
scraper/run-xaf.js
async function main() {
  const runHour = getRunHour();
  console.log(`\n[${new Date().toISOString()}] Cameroon XAF 스크래핑 시작 — run_hour: ${runHour}\n`);

  const results = [];
  const errors  = [];

  console.log(`  모든 스크래퍼 병렬 실행 중... (${SCRAPERS.length}개)\n`);
  const settled = await Promise.allSettled(SCRAPERS.map(({ fn }) => fn()));

  for (let i = 0; i < settled.length; i++) {
    const { name } = SCRAPERS[i];
    const result = settled[i];
    if (result.status === 'fulfilled') {
      results.push(result.value);
      console.log(`  ✓ ${name}: 송금액 ${result.value.send_amount_krw?.toLocaleString()}원  수수료 ${result.value.service_fee?.toLocaleString()}원  합계 ${result.value.total_sending_amount?.toLocaleString()}원`);
    } else {
      console.error(`  ✗ ${name} 실패: ${result.reason?.message}`);
      errors.push({ name, error: result.reason?.message });
    }
  }

  if (results.length === 0) {
    console.error('\n모든 스크래퍼 실패. 종료합니다.');
    process.exit(1);
  }

  const gmeRecord   = resu
scrape function · javascript · L9-L55 (47 LOC)
scraper/scrapers/coinshot.js
export async function scrape(browser) {
  const page = await browser.newPage();
  try {
    await page.goto('https://coinshot.org/main', {
      waitUntil: 'load',
      timeout: 30000,
    });
    await page.waitForTimeout(2000);

    // ── 언어 선택 모달 닫기 ────────────────────────────────────────────
    await page.waitForSelector('button.lang-btn[value="ko"]', { timeout: 10000 });
    await page.click('button.lang-btn[value="ko"]');
    await page.waitForTimeout(1000);

    // ── 수신 국가: Indonesia (IDR) 선택 ────────────────────────────────
    await page.click('#current-receiving-currency');
    await page.waitForTimeout(500);
    await page.click('#select-receiving-currency a[data-currency="IDR"]');
    await page.waitForTimeout(1000);

    // ── 수령액 입력: 13,000,000 IDR ────────────────────────────────────
    await page.click('#receiving-input', { clickCount: 3 });
    await page.fill('#receiving-input', '13000000');
    await page.press('#receiving-input', 'Enter');
    await page.waitFo
scrape function · javascript · L9-L53 (45 LOC)
scraper/scrapers/cross.js
export async function scrape(browser) {
  const page = await browser.newPage();
  try {
    // networkidle 미달성 사이트 — load 사용
    await page.goto('https://crossenf.com/remittance', {
      waitUntil: 'load',
      timeout: 30000,
    });
    await page.waitForTimeout(2000);

    // ── 수신 국가: Indonesia (IDR) 선택 ────────────────────────────────
    // 수신 통화 드롭다운 열기 (기본값 THB)
    await page.locator('div.relative:has(span:text("THB"))').click();
    await page.waitForSelector('#aside-root ul', { timeout: 10000 });

    // Indonesia 국기(ID flag) 이미지로 li 선택
    await page.locator('#aside-root li:has(img[alt="ID flag"])').click();
    await page.waitForTimeout(1000);

    // ── 수령액 입력: 13,000,000 IDR ────────────────────────────────────
    const receiveInput = page.locator('input[inputmode="numeric"]').nth(1);
    await receiveInput.click({ clickCount: 3 });
    await receiveInput.fill('13000000');
    await receiveInput.press('Tab');
    await page.waitForTimeout(3000);

    // ── 총 송금액(KRW) 
scrape function · javascript · L9-L60 (52 LOC)
scraper/scrapers/e9pay.js
export async function scrape(browser) {
  const page = await browser.newPage();
  try {
    await page.goto('https://www.e9pay.co.kr/', {
      waitUntil: 'domcontentloaded',
      timeout: 30000,
    });
    await page.waitForTimeout(3000);

    // ── 수신 국가: Indonesia (IDR) 선택 (라디오가 CSS로 숨겨져 있어 JS로 트리거) ──
    await page.waitForSelector('#ID_IDR', { state: 'attached', timeout: 10000 });
    await page.evaluate(() => {
      const radio = document.querySelector('#ID_IDR');
      radio.checked = true;
      radio.dispatchEvent(new Event('change', { bubbles: true }));
      radio.dispatchEvent(new Event('click', { bubbles: true }));
    });
    await page.waitForTimeout(1000);

    // ── reverse 버튼 클릭 → 수령액 입력 모드로 전환 ────────────────────
    await page.waitForSelector('#reverse', { timeout: 5000 });
    await page.click('#reverse');
    await page.waitForTimeout(500);

    // ── 수령액 입력: 13,000,000 IDR ────────────────────────────────────
    await page.waitForSelector('#receive-money', {
scrape function · javascript · L9-L62 (54 LOC)
scraper/scrapers/gme.js
export async function scrape(browser) {
  const page = await browser.newPage();
  try {
    await page.goto('https://online.gmeremit.com/', {
      waitUntil: 'domcontentloaded',
      timeout: 30000,
    });

    // ── 수신 국가: Indonesia (IDR) 선택 ────────────────────────────────
    // #nCountry 클릭 → 드롭다운 열기
    await page.waitForSelector('#nCountry', { timeout: 10000 });
    await page.click('#nCountry');
    await page.waitForTimeout(500);

    // 검색창에 "Indonesia" 입력해 필터링
    await page.waitForSelector('#CountryValue', { timeout: 5000 });
    await page.fill('#CountryValue', 'Indonesia');
    await page.waitForTimeout(300);

    // Indonesia (IDR) 항목 클릭
    await page.click('#toCurrUl li[data-countrycode="IDR"]');
    await page.waitForTimeout(1000);

    // ── 수령액 입력: 13,000,000 IDR ────────────────────────────────────
    await page.waitForSelector('#recAmt', { timeout: 10000 });
    await page.click('#recAmt', { clickCount: 3 });
    await page.fill('#recAmt', '13000000');

    // 
Repobility — same analyzer, your code, free for public repos · /scan/
scrape function · javascript · L9-L36 (28 LOC)
scraper/scrapers/gmoneytrans.js
export async function scrape() {
  const url =
    'https://mapi.gmoneytrans.net/exratenew1/ajx_calcRate.asp' +
    '?receive_amount=13000000' +
    '&payout_country=Indonesia' +
    '&total_collected=0' +
    '&payment_type=Bank+Account' +
    '&currencyType=IDR';

  const res = await fetch(url, { signal: AbortSignal.timeout(15000) });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);

  const text = await res.text();

  const serviceCharge = parseField(text, 'serviceCharge') ?? 2500;
  const sendAmount    = parseField(text, 'sendAmount');

  if (!sendAmount) throw new Error(`파싱 실패: ${text.slice(0, 200)}`);

  return {
    operator: OPERATOR,
    receiving_country: 'Indonesia',
    receive_amount: 13_000_000,
    send_amount_krw: sendAmount,
    service_fee: serviceCharge,
    total_sending_amount: sendAmount + serviceCharge,
  };
}
parseField function · javascript · L38-L41 (4 LOC)
scraper/scrapers/gmoneytrans.js
function parseField(text, field) {
  const m = text.match(new RegExp(`${field}--td_clm--([\\d.]+)--td_end--`));
  return m ? parseFloat(m[1]) : null;
}
scrape function · javascript · L8-L37 (30 LOC)
scraper/scrapers/hanpass.js
export async function scrape() {
  const res = await fetch('https://app.hanpass.com/app/v1/remittance/get-cost', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      inputAmount: '13000000',
      inputCurrencyCode: 'IDR',
      fromCurrencyCode: 'KRW',
      toCurrencyCode: 'IDR',
      toCountryCode: 'ID',
      memberSeq: '1',
      lang: 'en',
    }),
    signal: AbortSignal.timeout(15000),
  });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const data = await res.json();
  if (data.resultCode !== '0') throw new Error(`Hanpass API 오류: ${data.resultMessage}`);
  const total = data.depositAmountIncludingFee;
  const fee   = data.transferFee ?? 0;
  if (!total) throw new Error('총 송금액 추출 실패');
  return {
    operator: OPERATOR,
    receiving_country: 'Indonesia',
    receive_amount: 13_000_000,
    send_amount_krw: total - fee,
    service_fee: fee,
    total_sending_amount: total,
  };
}
scrape function · javascript · L9-L52 (44 LOC)
scraper/scrapers/jrf.js
export async function scrape(browser) {
  const context = await browser.newContext({ ignoreHTTPSErrors: true });
  const page = await context.newPage();
  try {
    await page.goto('https://www.jpremit.co.kr/', {
      waitUntil: 'domcontentloaded',
      timeout: 30000,
    });
    await page.waitForTimeout(3000);

    // ── 수신 통화: IDR 선택 ─────────────────────────────────────────────
    await page.click('#div_curr', { force: true });
    await page.waitForSelector('li#IDR', { state: 'visible', timeout: 10000 });
    await page.click('li#IDR');
    await page.waitForTimeout(1500);

    // ── 수령액 입력: 13,000,000 IDR ────────────────────────────────────
    await page.click('#rec_money', { clickCount: 3 });
    await page.fill('#rec_money', '13000000');
    await page.dispatchEvent('#rec_money', 'keyup');
    await page.waitForTimeout(3000);

    // ── 총 송금액(KRW) 추출 — fee 미포함 ──────────────────────────────
    const sendAmtRaw = await page.inputValue('#send_money');
    const sendAmt = e
scrape function · javascript · L12-L61 (50 LOC)
scraper/scrapers/sbi.js
export async function scrape(browser) {
  const context = await browser.newContext({
    userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
    locale: 'ko-KR',
  });
  const page = await context.newPage();
  try {
    await page.goto('https://www.sbicosmoney.com/', {
      waitUntil: 'load',
      timeout: 30000,
    });
    await page.waitForTimeout(2000);

    // ── 팝업 닫기 ──────────────────────────────────────────────────────
    await page.click('button:has-text("Close")').catch(() => null);
    await page.waitForTimeout(500);

    // ── 수신 국가: Indonesia (IDR) 선택 ────────────────────────────────
    await page.click('.dest-country');
    await page.waitForTimeout(500);
    await page.click('a[data-currency="IDR"]');
    await page.waitForTimeout(1500);

    // ── 수령액 입력: 13,000,000 IDR ────────────────────────────────────
    await page.click('#targetAmount', { clickCount: 3 });
    await page.fill('#targetA
scrape function · javascript · L9-L60 (52 LOC)
scraper/scrapers/sentbe.js
export async function scrape(browser) {
  const context = await browser.newContext({
    userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
    locale: 'ko-KR',
  });
  const page = await context.newPage();
  try {
    await page.goto('https://www.sentbe.com/ko', {
      waitUntil: 'networkidle',
      timeout: 30000,
    });

    // ── 팝업 닫기 ──────────────────────────────────────────────────────
    await page.click('button.close').catch(() => null);
    await page.waitForTimeout(300);
    // app-download 팝업이 오버레이를 가리는 경우 dim 클릭으로 닫기
    await page.click('article.app-download-popup .dim').catch(() => null);
    await page.waitForTimeout(500);

    // ── 수신 국가: 인도네시아 / 루피아 - IDR 선택 ──────────────────────
    await page.waitForSelector('.receiveAmountInput .el-input-group__append', { timeout: 10000 });
    await page.click('.receiveAmountInput .el-input-group__append');
    await page.waitForTimeout(500);
    awa
scrape function · javascript · L9-L48 (40 LOC)
scraper/scrapers/utransfer.js
export async function scrape(browser) {
  const page = await browser.newPage();
  try {
    await page.goto('https://www.utransfer.com', {
      waitUntil: 'networkidle',
      timeout: 30000,
    });
    await page.waitForTimeout(2000);

    // ── 수신 통화: IDR 선택 (두 번째 select) ───────────────────────────
    await page.locator('select').nth(1).selectOption('IDR');
    await page.waitForTimeout(1000);

    // ── 수령액 입력: 13,000,000 IDR ────────────────────────────────────
    await page.click('input[name="toAmount"]', { clickCount: 3 });
    await page.fill('input[name="toAmount"]', '13000000');
    await page.dispatchEvent('input[name="toAmount"]', 'change');
    await page.waitForTimeout(3000);

    // ── 총 송금액(KRW) 추출 ────────────────────────────────────────────
    const sendAmtRaw = await page.inputValue('input[name="fromAmount"]').catch(() => null);
    const sendAmt = extractNumber(sendAmtRaw);
    if (!sendAmt) throw new Error('총 송금액을 추출할 수 없습니다.');

    // ── 수수료 추출 ─────────────
scrape function · javascript · L9-L74 (66 LOC)
scraper/scrapers/wirebarley.js
export async function scrape(browser) {
  const context = await browser.newContext({
    userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
    locale: 'ko-KR',
  });
  const page = await context.newPage();
  try {
    await page.goto('https://www.wirebarley.com/ko', {
      waitUntil: 'networkidle',
      timeout: 30000,
    });
    await page.waitForTimeout(3000);

    // ── 광고 팝업 닫기 ─────────────────────────────────────────────────
    await page.locator('#lafc-popup button').click().catch(() => null);
    await page.waitForTimeout(1000);

    // ── 수신 통화 드롭다운 열기 (기본값: USD) ──────────────────────────
    await page.locator('[data-title="currencyToMoneyBox"]').nth(1)
      .locator('img[alt="드롭 다운"]').click();
    await page.waitForTimeout(2000);

    // ── 수신 국가: Indonesia (IDR) 선택 ────────────────────────────────
    await page.locator('button:has(img[alt="ID"])').click();
    await page.waitForTimeout(2000)
‹ prevpage 3 / 3