| | import express from 'express'; |
| | import { chromium } from 'playwright'; |
| | import cors from 'cors'; |
| | import dotenv from 'dotenv'; |
| | import os from 'os'; |
| | import axios from 'axios'; |
| | import fetch from 'node-fetch'; |
| | import sharp from 'sharp'; |
| | import { spawn } from 'child_process'; |
| | import path from 'path'; |
| | import { NodeVM } from "vm2"; |
| | import { createRequire } from "module"; |
| | const require = createRequire(import.meta.url); |
| |
|
| | import EventEmitter from 'events'; |
| | import WebSocket from 'ws'; |
| |
|
| | import pkg from 'axios-cookiejar-support'; |
| | const { wrapper } = pkg; |
| |
|
| | import { CookieJar } from 'tough-cookie'; |
| | import * as cheerio from 'cheerio'; |
| | import TurndownService from 'turndown'; |
| | import { Readable } from 'stream'; |
| |
|
| | dotenv.config(); |
| |
|
| | const config = { |
| | maxTextLength: 100, |
| | viewport: { width: 1920, height: 1080 }, |
| | userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' |
| | }; |
| |
|
| | let browser, page; |
| |
|
| | const utils = { |
| | async initialize() { |
| | if (!browser) { |
| | browser = await chromium.launch({ headless: true }); |
| | const context = await browser.newContext({ |
| | viewport: config.viewport, |
| | userAgent: config.userAgent |
| | }); |
| |
|
| | await context.route('**/*', (route) => { |
| | const url = route.request().url(); |
| | if (url.endsWith('.png') || url.endsWith('.jpg') || url.includes('google-analytics')) { |
| | return route.abort(); |
| | } |
| | route.continue(); |
| | }); |
| |
|
| | page = await context.newPage(); |
| | await page.goto('https://www.bratgenerator.com/', { waitUntil: 'domcontentloaded', timeout: 10000 }); |
| |
|
| | try { |
| | await page.click('#onetrust-accept-btn-handler', { timeout: 2000 }); |
| | } catch { } |
| |
|
| | await page.evaluate(() => setupTheme('white')); |
| | } |
| | }, |
| |
|
| | async generateBrat(text) { |
| | await page.fill('#textInput', text); |
| | const overlay = page.locator('#textOverlay'); |
| | return overlay.screenshot({ timeout: 3000 }); |
| | }, |
| |
|
| | async close() { |
| | if (browser) await browser.close(); |
| | } |
| | }; |
| |
|
| | const app = express(); |
| | app.use(express.json()); |
| | app.use(cors()); |
| |
|
| | app.get('/', (req, res) => { |
| | res.send('<h1>Welcome to the Web Scraping API</h1>'); |
| | }); |
| |
|
| | app.get('/brat', async (req, res) => { |
| | try { |
| | const { text: q } = req.query; |
| | if (!q) { |
| | return res.json({ |
| | name: 'HD Bart Generator API', |
| | message: 'Parameter text di perlukan', |
| | version: '2.1.0', |
| | runtime: { |
| | os: os.type(), |
| | platform: os.platform(), |
| | architecture: os.arch(), |
| | cpuCount: os.cpus().length, |
| | uptime: `${os.uptime()} seconds`, |
| | memoryUsage: `${Math.round((os.totalmem() - os.freemem()) / 1024 / 1024)} MB used of ${Math.round(os.totalmem() / 1024 / 1024)} MB` |
| | } |
| | }); |
| | } |
| | const imageBuffer = await utils.generateBrat(q); |
| | res.set('Content-Type', 'image/png'); |
| | res.send(imageBuffer); |
| | } catch (error) { |
| | console.error(error); |
| | res.status(500).json({ |
| | status: false, |
| | message: 'Error generating image', |
| | error: process.env.NODE_ENV === 'development' ? error.message : undefined |
| | }); |
| | } |
| | }); |
| |
|
| | app.get('/screenshot', async (req, res) => { |
| | const { url } = req.query; |
| |
|
| | if (!url) { |
| | return res.status(400).send('URL is required'); |
| | } |
| |
|
| | try { |
| | const browser = await chromium.launch(); |
| | const page = await browser.newPage(); |
| | await page.goto(url); |
| | |
| | const screenshotBuffer = await page.screenshot(); |
| | await browser.close(); |
| | |
| | res.type('image/png').send(screenshotBuffer); |
| | } catch (error) { |
| | res.status(500).send('Internal Server Error'); |
| | } |
| | }); |
| |
|
| |
|
| | function generateRandomString(length) { |
| | const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; |
| | let result = ''; |
| | for (let i = 0; i < length; i++) { |
| | result += characters.charAt(Math.floor(Math.random() * characters.length)); |
| | } |
| | return result; |
| | } |
| |
|
| | class MicrosoftBingAutoLogin { |
| | constructor(bing_account, bing_password) { |
| | console.log('Initializing auto login for Microsoft Bing ...'); |
| | this.bing_account = bing_account; |
| | this.bing_password = bing_password; |
| | this.init(); |
| | } |
| |
|
| | async init() { |
| | this.browser = await chromium.launch(); |
| | } |
| |
|
| | |
| | async login() { |
| | const sig = generateRandomString(32); |
| | const CSRFToken = generateRandomString(8) + '-' + generateRandomString(4) + '-' + generateRandomString(4) + '-' + generateRandomString(4) + '-' + generateRandomString(12); |
| | const loginUrl = `https://login.live.com/login.srf?wa=wsignin1.0&rpsnv=13&id=264960&wreply=https%3a%2f%2fwww.bing.com%2fsecure%2fPassport.aspx%3fedge_suppress_profile_switch%3d1%26requrl%3dhttps%253a%252f%252fwww.bing.com%252fsearch%253ftoWww%253d1%2526redig%253d9220EACAFFCA40508E4E7BD52023921B%2526q%253dBing%252bAI%2526showconv%253d1%2526wlexpsignin%253d1%26sig=${sig}&wp=MBI_SSL&lc=1028&CSRFToken=${CSRFToken}&aadredir=1`; |
| |
|
| | |
| | const page = await this.browser.newPage(); |
| | await page.goto(loginUrl); |
| |
|
| | |
| | const accountInput = await page.$('input[name="loginfmt"]'); |
| | await accountInput.type(this.bing_account); |
| | const passwordInput = await page.$('input[name="passwd"]'); |
| | await passwordInput.type(this.bing_password); |
| |
|
| | |
| | const submitButton = await page.$('input[type="submit"]'); |
| | await submitButton.click(); |
| |
|
| | |
| | await page.waitForNavigation(); |
| | } |
| |
|
| | async getCookies() { |
| | const page = await this.browser.newPage(); |
| | await page.goto('https://bing.com/chat'); |
| | const cookies = await page.cookies(); |
| | return cookies; |
| | } |
| | } |
| |
|
| | |
| | app.get('/login', async (req, res) => { |
| | const { user, pass } = req.query; |
| |
|
| | if (!user || !pass) { |
| | return res.status(400).send('Missing username or password.'); |
| | } |
| |
|
| | try { |
| | const bingLogin = new MicrosoftBingAutoLogin(user, pass); |
| | await bingLogin.login(); |
| | const cookies = await bingLogin.getCookies(); |
| | res.json(cookies); |
| | } catch (error) { |
| | res.status(500).send('Error during login: ' + error.message); |
| | } |
| | }); |
| |
|
| | app.get('/cookie', async (req, res) => { |
| | const { url } = req.query; |
| |
|
| | if (!url) { |
| | return res.status(400).send('URL is required'); |
| | } |
| |
|
| | try { |
| | const browser = await chromium.launch(); |
| | const page = await browser.newPage(); |
| | await page.goto(url); |
| | const cookies = await page.context().cookies(); |
| | await browser.close(); |
| | res.json(cookies); |
| | } catch (error) { |
| | res.status(500).send('Internal Server Error'); |
| | } |
| | }); |
| |
|
| |
|
| | |
| |
|
| | app.get('/cookie/v2', async (req, res) => { |
| | const { url } = req.query; |
| |
|
| | if (!url) { |
| | return res.status(400).send('URL is required'); |
| | } |
| |
|
| | try { |
| | const browser = await chromium.launch(); |
| | const page = await browser.newPage(); |
| | await page.goto(url); |
| |
|
| | |
| | const cookies = await page.context().cookies(); |
| |
|
| | |
| | const response = await page.waitForResponse(url); |
| | const headers = response.headers(); |
| |
|
| | await browser.close(); |
| |
|
| | |
| | res.json({ cookies, headers }); |
| | } catch (error) { |
| | console.error(error); |
| | res.status(500).send('Internal Server Error'); |
| | } |
| | }); |
| |
|
| | app.get('/welcome', async (req, res) => { |
| | const { name, info, desc } = req.query; |
| |
|
| | |
| | if (!name || !info) { |
| | return res.status(400).json({ error: 'Missing required parameters' }); |
| | } |
| |
|
| | |
| | const html = ` |
| | <!DOCTYPE html> |
| | <html lang="en" > |
| | <head> |
| | <meta charset="UTF-8"> |
| | <title>Course Card UI Design - #094 of #100Days100Projects</title> |
| | <link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.10.2/css/all.min.css'> |
| | <style> |
| | @import url('https://fonts.googleapis.com/css?family=Muli&display=swap'); |
| | |
| | * { |
| | box-sizing: border-box; |
| | } |
| | |
| | |
| | body { |
| | background-image: linear-gradient(45deg, #7175da, #9790F2); |
| | font-family: 'Muli', sans-serif; |
| | display: flex; |
| | align-items: center; |
| | justify-content: center; |
| | flex-direction: column; |
| | min-height: 100vh; |
| | margin: 0; |
| | } |
| | |
| | .courses-container { |
| | |
| | } |
| | |
| | .course { |
| | background-color: #fff; |
| | border-radius: 10px; |
| | box-shadow: 0 10px 10px rgba(0, 0, 0, 0.2); |
| | display: flex; |
| | max-width: 100%; |
| | margin: 20px; |
| | overflow: hidden; |
| | width: 700px; |
| | } |
| | |
| | .course h6 { |
| | opacity: 0.6; |
| | margin: 0; |
| | letter-spacing: 1px; |
| | text-transform: uppercase; |
| | } |
| | |
| | .course h2 { |
| | letter-spacing: 1px; |
| | margin: 10px 0; |
| | } |
| | |
| | .course-preview { |
| | background-color: #2A265F; |
| | color: #fff; |
| | padding: 30px; |
| | max-width: 250px; |
| | } |
| | |
| | .course-preview a { |
| | color: #fff; |
| | display: inline-block; |
| | font-size: 12px; |
| | opacity: 0.6; |
| | margin-top: 30px; |
| | text-decoration: none; |
| | } |
| | |
| | .course-info { |
| | padding: 30px; |
| | position: relative; |
| | width: 100%; |
| | } |
| | |
| | .progress-container { |
| | position: absolute; |
| | top: 30px; |
| | right: 30px; |
| | text-align: right; |
| | width: 150px; |
| | } |
| | |
| | .progress { |
| | background-color: #ddd; |
| | border-radius: 3px; |
| | height: 5px; |
| | width: 100%; |
| | } |
| | |
| | .progress::after { |
| | border-radius: 3px; |
| | background-color: #2A265F; |
| | content: ''; |
| | position: absolute; |
| | top: 0; |
| | left: 0; |
| | height: 5px; |
| | width: 66%; |
| | } |
| | |
| | .progress-text { |
| | font-size: 10px; |
| | opacity: 0.6; |
| | letter-spacing: 1px; |
| | } |
| | |
| | .btn { |
| | background-color: #2A265F; |
| | border: 0; |
| | border-radius: 50px; |
| | box-shadow: 0 10px 10px rgba(0, 0, 0, 0.2); |
| | color: #fff; |
| | font-size: 16px; |
| | padding: 12px 25px; |
| | position: absolute; |
| | bottom: 30px; |
| | right: 30px; |
| | letter-spacing: 1px; |
| | } |
| | |
| | /* SOCIAL PANEL CSS */ |
| | .social-panel-container { |
| | position: fixed; |
| | right: 0; |
| | bottom: 80px; |
| | transform: translateX(100%); |
| | transition: transform 0.4s ease-in-out; |
| | } |
| | |
| | .social-panel-container.visible { |
| | transform: translateX(-10px); |
| | } |
| | |
| | .social-panel { |
| | background-color: #fff; |
| | border-radius: 16px; |
| | box-shadow: 0 16px 31px -17px rgba(0,31,97,0.6); |
| | border: 5px solid #001F61; |
| | display: flex; |
| | flex-direction: column; |
| | justify-content: center; |
| | align-items: center; |
| | font-family: 'Muli'; |
| | position: relative; |
| | height: 169px; |
| | width: 370px; |
| | max-width: calc(100% - 10px); |
| | } |
| | |
| | .social-panel button.close-btn { |
| | border: 0; |
| | color: #97A5CE; |
| | cursor: pointer; |
| | font-size: 20px; |
| | position: absolute; |
| | top: 5px; |
| | right: 5px; |
| | } |
| | |
| | .social-panel button.close-btn:focus { |
| | outline: none; |
| | } |
| | |
| | .social-panel p { |
| | background-color: #001F61; |
| | border-radius: 0 0 10px 10px; |
| | color: #fff; |
| | font-size: 14px; |
| | line-height: 18px; |
| | padding: 2px 17px 6px; |
| | position: absolute; |
| | top: 0; |
| | left: 50%; |
| | margin: 0; |
| | transform: translateX(-50%); |
| | text-align: center; |
| | width: 235px; |
| | } |
| | |
| | .social-panel p i { |
| | margin: 0 5px; |
| | } |
| | |
| | .social-panel p a { |
| | color: #FF7500; |
| | text-decoration: none; |
| | } |
| | |
| | .social-panel h4 { |
| | margin: 20px 0; |
| | color: #97A5CE; |
| | font-family: 'Muli'; |
| | font-size: 14px; |
| | line-height: 18px; |
| | text-transform: uppercase; |
| | } |
| | |
| | .social-panel ul { |
| | display: flex; |
| | list-style-type: none; |
| | padding: 0; |
| | margin: 0; |
| | } |
| | |
| | .social-panel ul li { |
| | margin: 0 10px; |
| | } |
| | |
| | .social-panel ul li a { |
| | border: 1px solid #DCE1F2; |
| | border-radius: 50%; |
| | color: #001F61; |
| | font-size: 20px; |
| | display: flex; |
| | justify-content: center; |
| | align-items: center; |
| | height: 50px; |
| | width: 50px; |
| | text-decoration: none; |
| | } |
| | |
| | .social-panel ul li a:hover { |
| | border-color: #FF6A00; |
| | box-shadow: 0 9px 12px -9px #FF6A00; |
| | } |
| | |
| | .floating-btn { |
| | border-radius: 26.5px; |
| | background-color: #001F61; |
| | border: 1px solid #001F61; |
| | box-shadow: 0 16px 22px -17px #03153B; |
| | color: #fff; |
| | cursor: pointer; |
| | font-size: 16px; |
| | line-height: 20px; |
| | padding: 12px 20px; |
| | position: fixed; |
| | bottom: 20px; |
| | right: 20px; |
| | z-index: 999; |
| | } |
| | |
| | .floating-btn:hover { |
| | background-color: #ffffff; |
| | color: #001F61; |
| | } |
| | |
| | .floating-btn:focus { |
| | outline: none; |
| | } |
| | |
| | .floating-text { |
| | background-color: #001F61; |
| | border-radius: 10px 10px 0 0; |
| | color: #fff; |
| | font-family: 'Muli'; |
| | padding: 7px 15px; |
| | position: fixed; |
| | bottom: 0; |
| | left: 50%; |
| | transform: translateX(-50%); |
| | text-align: center; |
| | z-index: 998; |
| | } |
| | |
| | .floating-text a { |
| | color: #FF7500; |
| | text-decoration: none; |
| | } |
| | |
| | @media screen and (max-width: 480px) { |
| | |
| | .social-panel-container.visible { |
| | transform: translateX(0px); |
| | } |
| | |
| | .floating-btn { |
| | right: 10px; |
| | } |
| | } |
| | </style> |
| | |
| | </head> |
| | <body> |
| | <!-- partial:index.partial.html --> |
| | <div class="courses-container"> |
| | <div class="course"> |
| | <div class="course-preview"> |
| | <h6>Course</h6> |
| | <h2>${name}</h2> |
| | <a href="#">View all chapters <i class="fas fa-chevron-right"></i></a> |
| | </div> |
| | <div class="course-info"> |
| | <div class="progress-container"> |
| | <div class="progress"></div> |
| | <span class="progress-text"> |
| | 6/9 Challenges |
| | </span> |
| | </div> |
| | <h6>${info}</h6> |
| | <h2>${desc}</h2> |
| | <button class="btn">Continue</button> |
| | </div> |
| | </div> |
| | </div> |
| | |
| | <!-- SOCIAL PANEL HTML --> |
| | <div class="social-panel-container"> |
| | <div class="social-panel"> |
| | <p>Created with <i class="fa fa-heart"></i> by |
| | <a target="_blank" href="https://florin-pop.com">Florin Pop</a></p> |
| | <button class="close-btn"><i class="fas fa-times"></i></button> |
| | <h4>Get in touch on</h4> |
| | <ul> |
| | <li> |
| | <a href="https://www.patreon.com/florinpop17" target="_blank"> |
| | <i class="fab fa-discord"></i> |
| | </a> |
| | </li> |
| | <li> |
| | <a href="https://twitter.com/florinpop1705" target="_blank"> |
| | <i class="fab fa-twitter"></i> |
| | </a> |
| | </li> |
| | <li> |
| | <a href="https://linkedin.com/in/florinpop17" target="_blank"> |
| | <i class="fab fa-linkedin"></i> |
| | </a> |
| | </li> |
| | <li> |
| | <a href="https://facebook.com/florinpop17" target="_blank"> |
| | <i class="fab fa-facebook"></i> |
| | </a> |
| | </li> |
| | <li> |
| | <a href="https://instagram.com/florinpop17" target="_blank"> |
| | <i class="fab fa-instagram"></i> |
| | </a> |
| | </li> |
| | </ul> |
| | </div> |
| | </div> |
| | <button class="floating-btn"> |
| | Get in Touch |
| | </button> |
| | |
| | <div class="floating-text"> |
| | Part of <a href="https://florin-pop.com/blog/2019/09/100-days-100-projects" target="_blank">#100Days100Projects</a> |
| | </div> |
| | <!-- partial --> |
| | <script> |
| | // INSERT JS HERE |
| | |
| | |
| | // SOCIAL PANEL JS |
| | const floating_btn = document.querySelector('.floating-btn'); |
| | const close_btn = document.querySelector('.close-btn'); |
| | const social_panel_container = document.querySelector('.social-panel-container'); |
| | |
| | floating_btn.addEventListener('click', () => { |
| | social_panel_container.classList.toggle('visible') |
| | }); |
| | |
| | close_btn.addEventListener('click', () => { |
| | social_panel_container.classList.remove('visible') |
| | }); |
| | </script> |
| | |
| | </body> |
| | </html>`; |
| |
|
| | try { |
| | const browser = await chromium.launch(); |
| | const page = await browser.newPage(); |
| | await page.setContent(html); |
| | const buffer = await page.screenshot({ type: 'png' }); |
| | await browser.close(); |
| |
|
| | res.set('Content-Type', 'image/png'); |
| | return res.send(buffer); |
| | } catch (error) { |
| | console.error('Error generating PNG:', error); |
| | res.status(500).json({ error: 'Failed to convert HTML to PNG' }); |
| | } |
| | }); |
| |
|
| | app.get('/youtube-videos', async (req, res) => { |
| | const channelName = req.query.name || 'YasoobKhalid'; |
| | const url = `https://www.youtube.com/@${channelName}/videos`; |
| |
|
| | const browser = await chromium.launch({ headless: true }); |
| | const page = await browser.newPage(); |
| | await page.goto(url); |
| |
|
| | const channelTitle = "Title" || await page.locator('yt-formatted-string[class*="ytd-channel-name"]').textContent(); |
| | const handle = await page.locator('yt-formatted-string#channel-handle').textContent(); |
| | const subscriberCount = await page.locator('yt-formatted-string#subscriber-count').textContent(); |
| |
|
| | let lastHeight = await page.evaluate(() => { |
| | return document.documentElement.scrollHeight; |
| | }); |
| |
|
| | const WAIT_IN_SECONDS = 5; |
| | while (true) { |
| | await page.evaluate('window.scrollTo(0, document.documentElement.scrollHeight)'); |
| | await page.waitForTimeout(WAIT_IN_SECONDS * 1000); |
| |
|
| | const newHeight = await page.evaluate(() => { |
| | return document.documentElement.scrollHeight; |
| | }); |
| |
|
| | if (newHeight === lastHeight) { |
| | break; |
| | } |
| |
|
| | lastHeight = newHeight; |
| | } |
| |
|
| | const videoData = await page.evaluate(() => { |
| | const thumbnails = Array.from(document.querySelectorAll('a#thumbnail yt-image img')); |
| | const views = Array.from(document.querySelectorAll('div#metadata-line span:nth-child(1)')); |
| | const titles = Array.from(document.querySelectorAll('#video-title')); |
| | const links = Array.from(document.querySelectorAll('#video-title-link')); |
| |
|
| | return titles.map((title, index) => ({ |
| | title: title.textContent.trim(), |
| | views: views[index]?.textContent.trim(), |
| | thumbnail: thumbnails[index]?.src, |
| | link: links[index]?.href, |
| | })); |
| | }); |
| |
|
| | await browser.close(); |
| |
|
| | res.json({ |
| | channelTitle, |
| | handle, |
| | subscriberCount, |
| | videos: videoData, |
| | }); |
| | }); |
| |
|
| | |
| | const Patterns = { |
| | channel: { |
| | name: /channelMetadataRenderer\":{\"title\":\"(.*?)\"/, |
| | id: /channelId\":\"(.*?)\"/, |
| | verified: /"label":"Verified"/, |
| | check_live: /{"text":"LIVE"}/, |
| | live: /thumbnailOverlays\":\[(.*?)]/, |
| | video_id: /videoId\":\"(.*?)\"/, |
| | uploads: /gridVideoRenderer\":{\"videoId\":\"(.*?)\"/, |
| | subscribers: /\"subscriberCountText\":{\"accessibility\":(.*?),/, |
| | views: /viewCountText\":{\"simpleText\":\"(.*?)\"}/, |
| | creation: /{\"text\":\"Joined \"},{\"text\":\"(.*?)\"}/, |
| | country: /country\":{\"simpleText\":\"(.*?)\"}/, |
| | custom_url: /canonicalChannelUrl\":\"(.*?)\"/, |
| | description: /{\"description\":{\"simpleText\":\"(.*?)\"}/, |
| | avatar: /height\":88},{\"url\":\"(.*?)\"/, |
| | banner: /width\":1280,\"height\":351},{\"url\":\"(.*?)\"/, |
| | playlists: /{\"url\":\"\/playlist\?list=(.*?)\"/, |
| | video_count: /videoCountText\":{\"runs\":\[{\"text\":(.*?)}\]/, |
| | socials: /q=https%3A%2F%2F(.*?)\"/, |
| | upload_ids: /videoId\":\"(.*?)\"/, |
| | stream_ids: /videoId\":\"(.*?)\"/, |
| | upload_chunk: /gridVideoRenderer\":{(.*?)\"navigationEndpoint/, |
| | upload_chunk_fl_1: /simpleText\":\"Streamed/, |
| | upload_chunk_fl_2: /default_live./, |
| | upcoming_check: /\"title\":\"Upcoming live streams\"/, |
| | upcoming: /gridVideoRenderer\":{\"videoId\":\"(.*?)\"/, |
| | }, |
| | video: { |
| | video_id: /videoId\":\"(.*?)\"/, |
| | title: /title\":\"(.*?)\"/, |
| | duration: /approxDurationMs\":\"(.*?)\"/, |
| | upload_date: /uploadDate\":\"(.*?)\"/, |
| | author_id: /channelIds\":\[\"(.*?)\"/, |
| | description: /shortDescription\":\"(.*)\",\"isCrawlable/, |
| | tags: /<meta name=\"keywords\" content=\"(.*?)\">/, |
| | is_streamed: /simpleText\":\"Streamed live/, |
| | is_premiered: /dateText\":{\"simpleText\":\"Premiered/, |
| | views: /videoViewCountRenderer\":{\"viewCount\":{\"simpleText\":\"(.*?)\"/, |
| | likes: /toggledText\":{\"accessibility\":{\"accessibilityData\":{\"label\":\"(.*?) /, |
| | thumbnail: /playerMicroformatRenderer\":{\"thumbnail\":{\"thumbnails\":\[{\"url\":\"(.*?)\"/, |
| | }, |
| | playlist: { |
| | name: /{\"title\":\"(.*?)\"/, |
| | video_count: /stats\":\[{\"runs\":\[{\"text\":\"(.*?)\"/, |
| | video_id: /videoId\":\"(.*?)\"/, |
| | thumbnail: /og:image\" content=\"(.*?)\?"/, |
| | }, |
| | extra: { |
| | video_id: /videoId\":\"(.*?)\"/, |
| | }, |
| | query: { |
| | channel_id: /channelId\":\"(.*?)\"/, |
| | video_id: /videoId\":\"(.*?)\"/, |
| | playlist_id: /playlistId\":\"(.*?)\"/, |
| | }, |
| | }; |
| |
|
| | |
| | const matchPattern = (pattern, data) => { |
| | const matches = []; |
| | let match; |
| | while ((match = pattern.exec(data)) !== null) { |
| | matches.push(match[1] || match[0]); |
| | } |
| | return matches; |
| | }; |
| |
|
| | |
| | const fetchChannelData = async (channelName) => { |
| | try { |
| | const url = `https://www.youtube.com/@${channelName}/videos`; |
| | const response = await axios.get(url); |
| | return response.data; |
| | } catch (error) { |
| | throw new Error('Error fetching YouTube channel data: ' + error.message); |
| | } |
| | }; |
| |
|
| | |
| | app.get('/youtube-info', async (req, res) => { |
| | const { name } = req.query; |
| |
|
| | if (!name) { |
| | return res.status(400).json({ error: 'Channel name is required' }); |
| | } |
| |
|
| | try { |
| | const data = await fetchChannelData(name); |
| | const results = {}; |
| |
|
| | |
| | Object.keys(Patterns.channel).forEach((key) => { |
| | const pattern = Patterns.channel[key]; |
| | results[key] = matchPattern(pattern, data); |
| | }); |
| |
|
| | res.json({ channelName: name, data: results }); |
| | } catch (error) { |
| | res.status(500).json({ error: error.message }); |
| | } |
| | }); |
| |
|
| | const playwright = { |
| | avLang: ['javascript', 'python', 'java', 'csharp'], |
| | |
| | request: async function(language = 'javascript', code) { |
| | if (!this.avLang.includes(language.toLowerCase())) { |
| | throw new Error(`Language "${language}" is not supported. Choose from available languages: ${this.avLang.join(', ')}`); |
| | } |
| |
|
| | const url = 'https://try.playwright.tech/service/control/run'; |
| | const headers = { |
| | 'authority': 'try.playwright.tech', |
| | 'accept': '*/*', |
| | 'content-type': 'application/json', |
| | 'origin': 'https://try.playwright.tech', |
| | 'referer': 'https://try.playwright.tech/?l=playwright-test', |
| | 'user-agent': 'Postify/1.0.0', |
| | }; |
| |
|
| | const data = { |
| | code: code, |
| | language: language |
| | }; |
| |
|
| | try { |
| | const response = await axios.post(url, data, { headers }); |
| | const { success, error, version, duration, output, files } = response.data; |
| | return { success, error, version, duration, output, files }; |
| | } catch (error) { |
| | if (error.response) { |
| | const { success, error: errMsg, version, duration, output, files } = error.response.data; |
| | return { success, error: errMsg, version, duration, output, files }; |
| | } else { |
| | throw new Error(error.message); |
| | } |
| | } |
| | } |
| | }; |
| |
|
| | app.get('/view', async (req, res) => { |
| | const { url, count } = req.query; |
| |
|
| | if (!url || !count) { |
| | return res.status(400).json({ error: 'Missing required parameters: url and count' }); |
| | } |
| |
|
| | const language = 'javascript'; |
| | const code = ` |
| | const { chromium } = require('playwright'); |
| | (async () => { |
| | const browser = await chromium.launch({ headless: true }); |
| | const page = await browser.newPage(); |
| | const targetUrl = '${url}'; |
| | |
| | for (let i = 0; i < ${count}; i++) { |
| | await page.goto(targetUrl); |
| | console.log(\`View \${i + 1}: \${targetUrl}\`); |
| | await page.waitForTimeout(3000); // Delay for 3 seconds before next view |
| | } |
| | |
| | await browser.close(); |
| | })(); |
| | `; |
| |
|
| | try { |
| | const data = await playwright.request(language, code); |
| | return res.status(200).json(data); |
| | } catch (error) { |
| | console.error('Error:', error); |
| | return res.status(500).json({ error: error.message }); |
| | } |
| | }) |
| |
|
| | const font = 'BlueArchive'; |
| |
|
| | const crossBuffer = Buffer.from('<svg width="480" height="200" version="1.1"><polygon points="252,0 159,190 165,190 287,0" style="fill:#fff" /></svg>'); |
| | const haloBuffer = Buffer.from('<svg width="480" height="200" version="1.1"><polygon points="252,0 159,190 165,190 287,0" style="fill:#fff" /></svg>'); |
| |
|
| | const splitText = (text) => { |
| | if (Array.isArray(text)) return text; |
| | if (text.includes(' ')) { |
| | return text.split(' ').filter(i => i.trim()); |
| | } else if (text.match(/^[A-Z][a-z]*[A-Z][a-z]*$/)) { |
| | return text.replace(/[A-Z]/g, t => ' ' + t).trim().split(' '); |
| | } else { |
| | const h = Math.floor(text.length / 2); |
| | return [text.substring(0, h), text.substring(h)]; |
| | } |
| | }; |
| |
|
| | const baLogo = async (text, left = 0) => { |
| | const [head, tail] = splitText(text); |
| | if (!head || !tail) throw new Error('Invalid input text'); |
| | |
| | let width = 32, height = 260; |
| | const top = 208; |
| | const matrix = [[1, -0.35], [0, 1]]; |
| |
|
| | const comps = []; |
| | |
| | const headPart = sharp({ |
| | text: { |
| | font, |
| | text: `<span color="#208ef7" size="144pt">${head}</span>`, |
| | dpi: 72, |
| | rgba: true, |
| | } |
| | }).affine(matrix, { |
| | background: '#fff0', |
| | interpolator: sharp.interpolators.nohalo |
| | }).png(); |
| | const headMeta = await headPart.metadata(); |
| | |
| | const w = width + headMeta.width - 162; |
| | const dl = w < 0 ? 0 : w + left; |
| |
|
| | const tailPart = sharp({ |
| | text: { |
| | font, |
| | text: `<span color="#208ef7" size="144pt">${head}<span color="#2b2b2b" size="144pt">${tail}</span></span>`, |
| | dpi: 72, |
| | rgba: true, |
| | } |
| | }).affine(matrix, { |
| | background: '#fff0', |
| | interpolator: sharp.interpolators.nohalo |
| | }).png(); |
| | const tailMeta = await tailPart.metadata(); |
| | |
| | comps.push({ |
| | input: await tailPart.toBuffer(), |
| | left: width, |
| | top, |
| | }); |
| |
|
| | comps.push({ |
| | input: crossBuffer, |
| | left: dl + 4, |
| | top: 4, |
| | }); |
| |
|
| | width += (tailMeta.width < 256 ? 256 : tailMeta.width ) + 64; |
| | height += 144; |
| | if (width < 500) width = 500; |
| | |
| | return sharp({ |
| | create: { |
| | width, height, |
| | channels: 4, |
| | background: '#fff', |
| | }, |
| | }).composite(comps).png(); |
| | }; |
| |
|
| | app.get('/balogo', async (req, res) => { |
| | try { |
| | const { text, text2 } = req.query; |
| | if (!text) return res.status(400).send('Text is required'); |
| | |
| | const logoImage = await baLogo(text); |
| | res.set('Content-Type', 'image/png'); |
| | res.send(await logoImage.toBuffer()); |
| | } catch (error) { |
| | res.status(500).send('Error generating logo: ' + error.message); |
| | } |
| | }); |
| |
|
| | async function baLogov2(textL = "蔚蓝", textR = "档案", x = "-15", y = "0", tp = false) { |
| | const browser = await chromium.launch({ headless: true }); |
| | const context = await browser.newContext({ |
| | viewport: { width: 1920, height: 1080 }, |
| | userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' |
| | }); |
| | const page = await context.newPage(); |
| | await page.goto('https://symbolon.pages.dev/'); |
| | await page.waitForSelector('#canvas'); |
| | await page.fill('#textL', textL); |
| | await page.fill('#textR', textR); |
| | await page.fill('#graphX', x); |
| | await page.fill('#graphY', y); |
| | await page.waitForTimeout(500); |
| | const imageBuffer = await page.$eval('#canvas', (canvas) => { |
| | return canvas.toDataURL('image/png').split(',')[1]; |
| | }); |
| | const buffer = Buffer.from(imageBuffer, 'base64'); |
| | await browser.close(); |
| | return buffer; |
| | } |
| |
|
| | app.get('/balogo/v2', async (req, res) => { |
| | try { |
| | const { textL = "蔚蓝", textR = "档案", x = "-15", y = "0", tp = "false" } = req.query; |
| | const tpBool = tp === "true"; |
| | const imageBuffer = await baLogov2(textL, textR, x, y, tpBool); |
| | res.setHeader('Content-Type', 'image/png'); |
| | res.send(imageBuffer); |
| | } catch (error) { |
| | console.error(error); |
| | res.status(500).json({ error: "Error generating logo" }); |
| | } |
| | }); |
| |
|
| | async function baLogov3(textL = "蔚蓝", textR = "档案") { |
| | const browser = await chromium.launch({ headless: true }); |
| | const context = await browser.newContext({ |
| | viewport: { width: 1920, height: 1080 }, |
| | userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' |
| | }); |
| | const page = await context.newPage(); |
| | await page.goto('https://appleneko2001-bluearchive-logo.vercel.app/'); |
| | |
| | try { |
| | await page.waitForSelector('#canvas', { timeout: 30000 }); |
| | } catch (err) { |
| | console.error('Canvas tidak ditemukan:', err); |
| | } |
| |
|
| | try { |
| | await page.waitForSelector('#textL', { state: 'visible', timeout: 10000 }); |
| | await page.fill('#textL', textL); |
| | } catch (err) { |
| | console.error('Elemen #textL gagal diisi:', err); |
| | } |
| |
|
| | try { |
| | await page.waitForSelector('#textR', { state: 'visible', timeout: 10000 }); |
| | await page.fill('#textR', textR); |
| | } catch (err) { |
| | console.error('Elemen #textR gagal diisi:', err); |
| | } |
| |
|
| | await page.waitForTimeout(2000); |
| |
|
| | const imageBuffer = await page.$eval('#canvas', (canvas) => { |
| | return canvas.toDataURL('image/png').split(',')[1]; |
| | }); |
| |
|
| | const buffer = Buffer.from(imageBuffer, 'base64'); |
| | |
| | await browser.close(); |
| | return buffer; |
| | } |
| |
|
| | app.get('/balogo/v3', async (req, res) => { |
| | try { |
| | const { textL = "蔚蓝", textR = "档案" } = req.query; |
| | const imageBuffer = await baLogov3(textL, textR); |
| | res.setHeader('Content-Type', 'image/png'); |
| | res.send(imageBuffer); |
| | } catch (error) { |
| | console.error(error); |
| | res.status(500).json({ error: "Error generating logo" }); |
| | } |
| | }); |
| |
|
| | async function baLogov4(textL = "蔚蓝", textR = "档案") { |
| | const browser = await chromium.launch({ headless: true }); |
| | const context = await browser.newContext({ |
| | viewport: { width: 1920, height: 1080 }, |
| | userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' |
| | }); |
| | const page = await context.newPage(); |
| | await page.goto('https://ba.api.hli.icu/'); |
| | |
| | try { |
| | await page.waitForSelector('#canvas', { timeout: 30000 }); |
| | } catch (err) { |
| | console.error('Canvas tidak ditemukan:', err); |
| | } |
| |
|
| | try { |
| | await page.waitForSelector('#textL', { state: 'visible', timeout: 10000 }); |
| | await page.fill('#textL', textL); |
| | } catch (err) { |
| | console.error('Elemen #textL gagal diisi:', err); |
| | } |
| |
|
| | try { |
| | await page.waitForSelector('#textR', { state: 'visible', timeout: 10000 }); |
| | await page.fill('#textR', textR); |
| | } catch (err) { |
| | console.error('Elemen #textR gagal diisi:', err); |
| | } |
| |
|
| | await page.waitForTimeout(500); |
| |
|
| | const imageBuffer = await page.$eval('#canvas', (canvas) => { |
| | return canvas.toDataURL('image/png').split(',')[1]; |
| | }); |
| |
|
| | const buffer = Buffer.from(imageBuffer, 'base64'); |
| | |
| | await browser.close(); |
| | return buffer; |
| | } |
| |
|
| | app.get('/balogo/v4', async (req, res) => { |
| | try { |
| | const { textL = "蔚蓝", textR = "档案" } = req.query; |
| | const imageBuffer = await baLogov4(textL, textR); |
| | res.setHeader('Content-Type', 'image/png'); |
| | res.send(imageBuffer); |
| | } catch (error) { |
| | console.error(error); |
| | res.status(500).json({ error: "Error generating logo" }); |
| | } |
| | }); |
| |
|
| | app.get('/rayso', async (req, res) => { |
| | const { code, title = 'app.js', theme = 'supabase', language = '', darkMode = 'true', background = 'false' } = req.query; |
| |
|
| | if (!code) |
| | return res.status(400).json({ error: 'Parameter "code" (Base64 encoded) diperlukan' }); |
| |
|
| | try { |
| | const encodedCode = Buffer.from(code, 'base64'); |
| | const url = `https://www.ray.so/#code=${encodeURIComponent(encodedCode)}&title=${encodeURIComponent(title)}&theme=${theme}&language=${language}&background=${background}&darkMode=${darkMode}`; |
| |
|
| | const browser = await chromium.launch({ headless: true }); |
| | const context = await browser.newContext({ |
| | viewport: { width: 1920, height: 1080 }, |
| | userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' |
| | }); |
| | const page = await context.newPage(); |
| |
|
| | await page.goto(url); |
| | await page.waitForSelector('.Frame_frame__rcr69'); |
| |
|
| | const elementHandle = await page.$('.Frame_frame__rcr69'); |
| | const screenshotBuffer = await elementHandle.screenshot({ fullPage: true }); |
| |
|
| | await browser.close(); |
| |
|
| | res.setHeader('Content-Type', 'image/png'); |
| | return res.status(200).end(screenshotBuffer); |
| | } catch (error) { |
| | return res.status(500).json({ error: 'Gagal mengambil screenshot', details: error.message }); |
| | } |
| | }); |
| |
|
| | app.get('/carbon', async (req, res) => { |
| | const { code, theme } = req.query; |
| |
|
| | if (!code) return res.status(400).json({ error: 'Parameter "code" diperlukan.' }); |
| | if (!theme) return res.status(400).json({ error: 'Parameter "theme" diperlukan.' }); |
| |
|
| | try { |
| | const browser = await chromium.launch({ headless: true }); |
| | const context = await browser.newContext({ |
| | viewport: { width: 1920, height: 1080 }, |
| | userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', |
| | acceptDownloads: true |
| | }); |
| | const page = await context.newPage(); |
| |
|
| | await page.goto('https://carbon.now.sh/'); |
| | await page.click('input[aria-labelledby="theme-dropdown"]'); |
| | await page.keyboard.type(theme); |
| | await page.keyboard.press('Enter'); |
| | await page.click('pre.CodeMirror-line span[role="presentation"]'); |
| | await page.keyboard.press('Control+A'); |
| | await page.keyboard.press('Backspace'); |
| | await page.keyboard.type(code); |
| | await page.waitForSelector('button[data-cy="quick-export-button"]'); |
| | const [download] = await Promise.all([ |
| | page.waitForEvent('download'), |
| | page.click('button[data-cy="quick-export-button"]') |
| | ]); |
| | const downloadPath = await download.path(); |
| |
|
| | res.setHeader('Content-Type', 'application/octet-stream'); |
| | return res.status(200).sendFile(downloadPath); |
| | } catch (error) { |
| | return res.status(500).json({ error: 'Gagal memproses permintaan.', details: error.message }); |
| | } finally { |
| | await browser.close(); |
| | } |
| | }); |
| |
|
| | app.get('/recoded', async (req, res) => { |
| | const { code, colors, font, lang, bg } = req.query; |
| |
|
| | if (!code) return res.status(400).json({ error: 'Parameter "code" diperlukan.' }); |
| | if (!colors) return res.status(400).json({ error: 'Parameter "colors" diperlukan.' }); |
| | if (!font) return res.status(400).json({ error: 'Parameter "font" diperlukan.' }); |
| | if (!lang) return res.status(400).json({ error: 'Parameter "lang" diperlukan.' }); |
| | if (!bg) return res.status(400).json({ error: 'Parameter "bg" diperlukan.' }); |
| |
|
| | try { |
| | const browser = await chromium.launch({ headless: true }); |
| | const context = await browser.newContext({ |
| | viewport: { width: 1920, height: 1080 }, |
| | userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', |
| | acceptDownloads: true, |
| | }); |
| | const page = await context.newPage(); |
| |
|
| | await page.goto('https://recoded.netlify.app/'); |
| | await page.fill('textarea.npm__react-simple-code-editor__textarea', code); |
| |
|
| | await page.click('div.input-box[style*="120px"]:has-text("VS Code Dark")'); |
| | await page.keyboard.type(colors); |
| | await page.keyboard.press('Enter'); |
| |
|
| | await page.click('div[data-testid="font-select"]'); |
| | await page.keyboard.type(font); |
| | await page.keyboard.press('Enter'); |
| |
|
| | await page.click('div[data-testid="language-select"]'); |
| | await page.keyboard.type(lang); |
| | await page.keyboard.press('Enter'); |
| |
|
| | await page.click('div[data-testid="background-color-select"]'); |
| | await page.keyboard.type(bg); |
| | await page.keyboard.press('Enter'); |
| |
|
| | const [download] = await Promise.all([ |
| | page.waitForEvent('download'), |
| | page.click('button.button.export') |
| | ]); |
| | const downloadPath = await download.path(); |
| |
|
| | res.setHeader('Content-Type', 'image/png'); |
| | return res.status(200).send(downloadPath); |
| | } catch (error) { |
| | return res.status(500).json({ error: 'Gagal memproses permintaan.', details: error.message }); |
| | } finally { |
| | await browser.close(); |
| | } |
| | }); |
| |
|
| | app.get('/chalkist', async (req, res) => { |
| | const { code, title } = req.query; |
| |
|
| | if (!code) return res.status(400).json({ error: 'Parameter "code" diperlukan.' }); |
| | if (!title) return res.status(400).json({ error: 'Parameter "title" diperlukan.' }); |
| |
|
| | let browser; |
| | try { |
| | browser = await chromium.launch({ headless: true }); |
| | const context = await browser.newContext({ |
| | viewport: { width: 1920, height: 1080 }, |
| | userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', |
| | }); |
| | const page = await context.newPage(); |
| |
|
| | await page.goto('https://chalk.ist/'); |
| |
|
| | |
| | const titleSelector = 'div[data-placeholder="Untitled..."]'; |
| | await page.focus(titleSelector); |
| | await page.keyboard.type(title); |
| |
|
| | |
| | const codeSelector = 'textarea.editor.font-config'; |
| | await page.fill(codeSelector, code); |
| |
|
| | |
| | const screenshotSelector = 'div[data-editor-frame]'; |
| | await page.waitForSelector(screenshotSelector); |
| |
|
| | |
| | const element = await page.$(screenshotSelector); |
| | const screenshotBuffer = await element.screenshot(); |
| |
|
| | res.setHeader('Content-Type', 'image/png'); |
| | res.status(200).send(screenshotBuffer); |
| | } catch (error) { |
| | console.error('Error:', error); |
| | res.status(500).json({ error: 'Gagal memproses permintaan.', details: error.message }); |
| | } finally { |
| | if (browser) await browser.close(); |
| | } |
| | }); |
| |
|
| | app.get('/brat/v2', async (req, res) => { |
| | const { text } = req.query; |
| |
|
| | if (!text) return res.status(400).json({ error: 'Parameter "text" diperlukan.' }); |
| |
|
| | try { |
| | const browser = await chromium.launch({ headless: true }); |
| | const context = await browser.newContext({ |
| | viewport: { width: 1920, height: 1080 }, |
| | userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', |
| | acceptDownloads: true, |
| | }); |
| |
|
| | const page = await context.newPage(); |
| |
|
| | await page.goto('https://brat-generator.net/'); |
| | await page.fill('input[class="w-full p-3 text-base border border-gray-200 rounded-lg mb-5 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 transition-colors"]', text); |
| |
|
| | try { |
| | await page.waitForSelector('button[class="px-5 py-3 text-sm font-medium rounded-lg flex items-center justify-between flex-1 transition-all bg-white border-2 border-blue-500 text-blue-600"]', { visible: true }); |
| | await page.click('button[class="px-5 py-3 text-sm font-medium rounded-lg flex items-center justify-between flex-1 transition-all bg-white border-2 border-blue-500 text-blue-600"]'); |
| | } catch (error) { |
| | console.log("Tombol 'White Background' tidak ditemukan atau tidak terlihat, melewati langkah ini."); |
| | } |
| |
|
| | try { |
| | await page.waitForSelector('button[class="w-full mt-5 px-5 py-3 text-sm font-medium rounded-lg transition-all bg-blue-500 text-white hover:bg-blue-600"]', { visible: true }); |
| | await page.click('button[class="w-full mt-5 px-5 py-3 text-sm font-medium rounded-lg transition-all bg-blue-500 text-white hover:bg-blue-600"]'); |
| | } catch (error) { |
| | console.log("Tombol 'Download Image' tidak ditemukan atau tidak terlihat, melewati langkah ini."); |
| | } |
| |
|
| | await page.waitForSelector('canvas[class="absolute top-0 left-0 w-full h-full bg-white shadow-inner"]', { visible: true }); |
| | await page.waitForTimeout(800); |
| |
|
| | const canvas = await page.$('canvas[class="absolute top-0 left-0 w-full h-full bg-white shadow-inner"]'); |
| | const screenshot = await canvas.screenshot(); |
| |
|
| | res.setHeader('Content-Type', 'image/png'); |
| | res.status(200).send(screenshot); |
| |
|
| | } catch (error) { |
| | return res.status(500).json({ error: 'Gagal memproses permintaan.', details: error.message }); |
| | } finally { |
| | await browser.close(); |
| | } |
| | }); |
| |
|
| | const getRandomName = () => { |
| | const names = ['wudy', 'andi', 'budi', 'citra', 'dina']; |
| | return names[Math.floor(Math.random() * names.length)]; |
| | }; |
| |
|
| | const getRandomClass = () => { |
| | const classes = ['A', 'B', 'C', 'D']; |
| | return classes[Math.floor(Math.random() * classes.length)]; |
| | }; |
| |
|
| | const getRandomText = () => { |
| | const texts = ['halo', 'selamat pagi', 'apa kabar?', 'siang', 'malam']; |
| | return texts[Math.floor(Math.random() * texts.length)]; |
| | }; |
| |
|
| | const getRandomType = () => { |
| | return Math.floor(Math.random() * 14) + 1; |
| | }; |
| |
|
| | const getRandomDay = () => { |
| | const days = ['senin', 'selasa', 'rabu', 'kamis', 'jumat', 'sabtu', 'minggu']; |
| | return days[Math.floor(Math.random() * days.length)]; |
| | }; |
| |
|
| | const getRandomYear = () => { |
| | const currentYear = new Date().getFullYear(); |
| | return Math.floor(Math.random() * (currentYear - 2000 + 1)) + 2000; |
| | }; |
| | app.get('/nulis', async (req, res) => { |
| | try { |
| | |
| | const { |
| | waktu = (getRandomYear()).toString(), |
| | hari = (getRandomDay()).toString(), |
| | nama = (getRandomName()).toString(), |
| | kelas = (getRandomClass()).toString(), |
| | text = (getRandomText()).toString(), |
| | type = (getRandomType()).toString() |
| | } = req.query; |
| | |
| | const diNama3 = nama; |
| | const diKelas3 = kelas; |
| | const diTulis9 = text; |
| |
|
| | const panjangKalimat9 = diTulis9.replace(/(\S+\s*){1,10}/g, '$&\n'); |
| | const panjangNama3 = diNama3.replace(/(\S+\s*){1,10}/g, '$&\n'); |
| | const panjangKelas3 = diKelas3.replace(/(\S+\s*){1,10}/g, '$&\n'); |
| |
|
| | const panjangBaris9 = panjangKalimat9.split('\n').slice(0, 30).join('\n'); |
| | const panjangBarisNama3 = panjangNama3.split('\n').slice(0, 30).join('\n'); |
| | const panjangBarisKelas3 = panjangKelas3.split('\n').slice(0, 30).join('\n'); |
| |
|
| | const months = ['- 1 -', '- 2 -', '- 3 -', '- 4 -', '- 5 -', '- 6 -', '- 7 -', '- 8 -', '- 9 -', '- 10 -', '- 11 -', '- 12 -']; |
| | const myDays = ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu']; |
| | const date = new Date(); |
| | const day = date.getDate(); |
| | const month = date.getMonth(); |
| | const thisDay = myDays[date.getDay()]; |
| | const year = date.getFullYear(); |
| |
|
| | const waktu6 = `${day} ${months[month]} ${year}`; |
| | const hari6 = thisDay; |
| |
|
| | const inputImagePath = path.join(process.cwd(), 'magernulis1.jpg'); |
| | const fontPath = path.join(process.cwd(), 'Zahraaa.ttf'); |
| | |
| | let theme = null; |
| | if (type === '1') { |
| | theme = [ |
| | inputImagePath, |
| | '-font', fontPath, |
| | '-fill', '#8c1a00', |
| | '-size', '1024x784', |
| | '-pointsize', '20', |
| | '-interline-spacing', '1', |
| | '-annotate', '+806+78', hari6, |
| | '-annotate', '+806+102', waktu6, |
| | '-annotate', '+360+100', panjangBarisNama3, |
| | '-annotate', '+360+120', panjangBarisKelas3, |
| | '-annotate', '+344+142', panjangBaris9, |
| | 'png:-' |
| | ]; |
| | } |
| | if (type === '2') { |
| | theme = [ |
| | inputImagePath, |
| | '-font', fontPath, |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '20', |
| | '-interline-spacing', |
| | '-7.5', |
| | '-annotate', |
| | '+344+142', |
| | panjangBaris9, |
| | 'png:-' |
| | ] |
| | } |
| | if (type === '3') { |
| | theme = [ |
| | inputImagePath, |
| | '-fill', |
| | '#001675', |
| | '-font', |
| | fontPath, |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '20', |
| | '-interline-spacing', |
| | '-7.5', |
| | '-annotate', |
| | '+344+142', |
| | panjangKalimat9, |
| | 'png:-' |
| | ] |
| | } |
| | if (type === '4') { |
| | theme = [ |
| | inputImagePath, |
| | '-fill', |
| | '#8c1a00', |
| | '-font', |
| | fontPath, |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '20', |
| | '-interline-spacing', |
| | '-7.5', |
| | '-annotate', |
| | '+344+142', |
| | panjangKalimat9, |
| | 'png:-' |
| | ] |
| | } |
| | if (type === '5') { |
| | theme = [ |
| | inputImagePath, |
| | '-font', |
| | fontPath, |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '20', |
| | '-interline-spacing', |
| | '1', |
| | '-annotate', |
| | '+806+78', |
| | hari, |
| | '-font', |
| | fontPath, |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '18', |
| | '-interline-spacing', |
| | '1', |
| | '-annotate', |
| | '+806+102', |
| | waktu, |
| | '-font', |
| | fontPath, |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '20', |
| | '-interline-spacing', |
| | '-7.5', |
| | '-annotate', |
| | '+344+142', |
| | panjangKalimat9, |
| | 'png:-' |
| | ] |
| | } |
| | if (type === '6') { |
| | theme = [ |
| | inputImagePath, |
| | '-fill', |
| | '#001675', |
| | '-font', |
| | fontPath, |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '20', |
| | '-interline-spacing', |
| | '1', |
| | '-annotate', |
| | '+806+78', |
| | hari, |
| | '-fill', |
| | '#001675', |
| | '-font', |
| | fontPath, |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '18', |
| | '-interline-spacing', |
| | '1', |
| | '-annotate', |
| | '+806+102', |
| | waktu, |
| | '-fill', |
| | '#001675', |
| | '-font', |
| | fontPath, |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '20', |
| | '-interline-spacing', |
| | '-7.5', |
| | '-annotate', |
| | '+344+142', |
| | panjangKalimat9, |
| | 'png:-' |
| | ] |
| | } |
| | if (type === '7') { |
| | theme = [ |
| | inputImagePath, |
| | '-fill', |
| | '#8c1a00', |
| | '-font', |
| | fontPath, |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '20', |
| | '-interline-spacing', |
| | '1', |
| | '-annotate', |
| | '+806+78', |
| | hari, |
| | '-fill', |
| | '#8c1a00', |
| | '-font', |
| | fontPath, |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '18', |
| | '-interline-spacing', |
| | '1', |
| | '-annotate', |
| | '+806+102', |
| | waktu, |
| | '-fill', |
| | '#8c1a00', |
| | '-font', |
| | fontPath, |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '20', |
| | '-interline-spacing', |
| | '-7.5', |
| | '-annotate', |
| | '+344+142', |
| | panjangKalimat9, |
| | 'png:-' |
| | ] |
| | } |
| | if (type === '8') { |
| | theme = [ |
| | inputImagePath, |
| | '-font', |
| | fontPath, |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '20', |
| | '-interline-spacing', |
| | '1', |
| | '-annotate', |
| | '+806+78', |
| | hari, |
| | '-font', |
| | fontPath, |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '18', |
| | '-interline-spacing', |
| | '1', |
| | '-annotate', |
| | '+806+102', |
| | waktu, |
| | '-font', |
| | fontPath, |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '18', |
| | '-interline-spacing', |
| | '1', |
| | '-annotate', |
| | '+360+100', |
| | nama, |
| | '-font', |
| | fontPath, |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '18', |
| | '-interline-spacing', |
| | '1', |
| | '-annotate', |
| | '+360+120', |
| | kelas, |
| | '-font', |
| | fontPath, |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '20', |
| | '-interline-spacing', |
| | '-7.5', |
| | '-annotate', |
| | '+344+142', |
| | panjangKalimat9, |
| | 'png:-' |
| | ] |
| | } |
| | if (type === '9') { |
| | theme = [ |
| | inputImagePath, |
| | '-font', |
| | fontPath, |
| | '-fill', |
| | '#001675', |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '20', |
| | '-interline-spacing', |
| | '1', |
| | '-annotate', |
| | '+806+78', |
| | hari, |
| | '-font', |
| | fontPath, |
| | '-fill', |
| | '#001675', |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '18', |
| | '-interline-spacing', |
| | '1', |
| | '-annotate', |
| | '+806+102', |
| | waktu, |
| | '-font', |
| | fontPath, |
| | '-fill', |
| | '#001675', |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '18', |
| | '-interline-spacing', |
| | '1', |
| | '-annotate', |
| | '+360+100', |
| | nama, |
| | '-font', |
| | fontPath, |
| | '-fill', |
| | '#001675', |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '18', |
| | '-interline-spacing', |
| | '1', |
| | '-annotate', |
| | '+360+120', |
| | kelas, |
| | '-font', |
| | fontPath, |
| | '-fill', |
| | '#001675', |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '20', |
| | '-interline-spacing', |
| | '-7.5', |
| | '-annotate', |
| | '+344+142', |
| | panjangKalimat9, |
| | 'png:-' |
| | ]; |
| | } |
| | if (type === '10') { |
| | theme = [ |
| | inputImagePath, |
| | '-font', |
| | fontPath, |
| | '-fill', |
| | '#8c1a00', |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '20', |
| | '-interline-spacing', |
| | '1', |
| | '-annotate', |
| | '+806+78', |
| | hari, |
| | '-font', |
| | fontPath, |
| | '-fill', |
| | '#8c1a00', |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '18', |
| | '-interline-spacing', |
| | '1', |
| | '-annotate', |
| | '+806+102', |
| | waktu, |
| | '-font', |
| | fontPath, |
| | '-fill', |
| | '#8c1a00', |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '18', |
| | '-interline-spacing', |
| | '1', |
| | '-annotate', |
| | '+360+100', |
| | nama, |
| | '-font', |
| | fontPath, |
| | '-fill', |
| | '#8c1a00', |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '18', |
| | '-interline-spacing', |
| | '1', |
| | '-annotate', |
| | '+360+120', |
| | kelas, |
| | '-font', |
| | fontPath, |
| | '-fill', |
| | '#8c1a00', |
| | '-size', |
| | '1024x784', |
| | '-pointsize', |
| | '20', |
| | '-interline-spacing', |
| | '-7.5', |
| | '-annotate', |
| | '+344+142', |
| | panjangKalimat9, |
| | 'png:-' |
| | ] |
| | } |
| | |
| | const splitText = text.replace(/(\S+\s*){1,9}/g, '$&\n') |
| | const fixHeight = splitText.split('\n').slice(0, 31).join('\n') |
| | const sebelumkiri = path.join(process.cwd(), 'sebelumkiri.jpg'); |
| | const sebelumkanan = path.join(process.cwd(), 'sebelumkanan.jpg'); |
| | const foliokanan = path.join(process.cwd(), 'foliokanan.jpg'); |
| | const foliokiri = path.join(process.cwd(), 'foliokiri.jpg'); |
| | const fontPathB = path.join(process.cwd(), 'Indie-Flower.ttf'); |
| |
|
| | if (type === '11') { |
| | theme = [ |
| | sebelumkiri, |
| | '-font', |
| | fontPathB, |
| | '-size', |
| | '960x1280', |
| | '-pointsize', |
| | '22', |
| | '-interline-spacing', |
| | '2', |
| | '-annotate', |
| | '+140+153', |
| | fixHeight, |
| | 'png:-' |
| | ]; |
| | } |
| | |
| | if (type === '12') { |
| | theme = [ |
| | sebelumkanan, |
| | '-font', |
| | fontPathB, |
| | '-size', |
| | '960x1280', |
| | '-pointsize', |
| | '23', |
| | '-interline-spacing', |
| | '2', |
| | '-annotate', |
| | '+128+129', |
| | fixHeight, |
| | 'png:-' |
| | ]; |
| | } |
| | |
| | if (type === '13') { |
| | theme = [ |
| | foliokanan, |
| | '-font', |
| | fontPathB, |
| | '-size', |
| | '960x1280', |
| | '-pointsize', |
| | '23', |
| | '-interline-spacing', |
| | '3', |
| | '-annotate', |
| | '+89+190', |
| | fixHeight, |
| | 'png:-' |
| | ]; |
| | } |
| | if (type === '14') { |
| | theme = [ |
| | foliokiri, |
| | '-font', |
| | fontPathB, |
| | '-size', |
| | '1720x1280', |
| | '-pointsize', |
| | '23', |
| | '-interline-spacing', |
| | '4', |
| | '-annotate', |
| | '+48+185', |
| | fixHeight, |
| | 'png:-' |
| | ]; |
| | } |
| | const convert = spawn('convert', theme); |
| | const collectImageData = () => { |
| | return new Promise((resolve, reject) => { |
| | let imageData = Buffer.alloc(0); |
| |
|
| | convert.stdout.on('data', (data) => { |
| | imageData = Buffer.concat([imageData, data]); |
| | }); |
| |
|
| | convert.on('close', (code) => { |
| | if (code !== 0) { |
| | return reject(new Error(`ImageMagick exited with code ${code}`)); |
| | } |
| | resolve(imageData); |
| | }); |
| |
|
| | convert.on('error', (err) => { |
| | reject(err); |
| | }); |
| | }); |
| | }; |
| |
|
| | const imageData = await collectImageData(); |
| | res.set('Content-Type', 'image/png'); |
| | res.send(imageData); |
| | convert.kill('SIGTERM'); |
| | } catch (err) { |
| | console.error(err); |
| | res.status(500).json({ error: 'Terjadi kesalahan server.' }); |
| | } |
| | }); |
| |
|
| | Readable.fromEventEmitter = function (emitter, events) { |
| | const readable = new Readable({ |
| | read() {} |
| | }); |
| |
|
| | const onData = (data) => { |
| | readable.push(data); |
| | }; |
| | const onEnd = () => { |
| | readable.push(null); |
| | }; |
| | const onError = (err) => { |
| | readable.emit('error', err); |
| | }; |
| |
|
| | emitter.on(events[0], onData); |
| | emitter.on(events[1], onEnd); |
| | emitter.on(events[2], onError); |
| |
|
| | return readable; |
| | }; |
| |
|
| | const iask = { |
| | turndownService: new TurndownService(), |
| |
|
| | request(query, mode, options) { |
| | if (typeof query === 'object') { |
| | return query; |
| | } |
| | return { q: query, mode, ...options }; |
| | }, |
| |
|
| | eventx(query) { |
| | const pipe = new EventEmitter(); |
| | const getQueryString = () => { |
| | const params = new URLSearchParams(query); |
| | return params.toString(); |
| | }; |
| | return { query, pipe, getQueryString }; |
| | }, |
| |
|
| | parseChunk(message) { |
| | try { |
| | const data = JSON.parse(message); |
| | const diff = data.pop(); |
| | let content = ''; |
| | let stop = false; |
| |
|
| | if (diff?.e?.[0]?.[1]?.data) { |
| | content = diff.e[0][1].data.replace(/<br\/>/g, '\n'); |
| | console.log(content); |
| | } |
| |
|
| | if (diff?.response?.rendered?.[2]?.[1]?.[4]?.[4]) { |
| | content = this.turndownService.turndown(diff.response.rendered[2][1][4][4]); |
| | stop = true; |
| | console.log(content); |
| | } |
| |
|
| | return { content, stop }; |
| | } catch (error) { |
| | console.error(error); |
| | return { content: '', stop: true }; |
| | } |
| | }, |
| |
|
| | inspect(response) { |
| | const $ = cheerio.load(response.data); |
| | const phxElement = $('[id^="phx-"]').first(); |
| | const joinMessage = JSON.stringify([ |
| | null, |
| | null, |
| | `lv:${phxElement.attr('id')}`, |
| | 'phx_join', |
| | { |
| | url: response.request.res.responseUrl, |
| | session: phxElement.attr('data-phx-session'), |
| | }, |
| | ]); |
| | const csrfToken = $('meta[name="csrf-token"]').attr('content'); |
| | return { joinMessage, csrfToken }; |
| | }, |
| |
|
| | async cws(queryString, jar) { |
| | const client = wrapper(axios.create({ jar })); |
| | try { |
| | const response = await client.get(`https://iask.ai?${queryString}`); |
| | const { joinMessage, csrfToken } = this.inspect(response); |
| | const cookies = await jar.getCookieString('https://iask.ai'); |
| | console.log(cookies); |
| |
|
| | const ws = new WebSocket(`wss://iask.ai/live/websocket?_csrf_token=${csrfToken}&vsn=2.0.0`, { |
| | headers: { |
| | 'Cookie': cookies |
| | } |
| | }); |
| |
|
| | await new Promise((resolve, reject) => { |
| | ws.on('open', () => { |
| | console.log('Websocket berhasil terhubung...'); |
| | ws.send(joinMessage); |
| | resolve(); |
| | }); |
| | ws.on('error', (err) => { |
| | console.error('Tidak dapat terhubung ke WebSocket:', err); |
| | reject(err); |
| | }); |
| | }); |
| |
|
| | return ws; |
| | } catch (error) { |
| | console.error(error); |
| | throw error; |
| | } |
| | }, |
| |
|
| | async handleJoin(event) { |
| | try { |
| | const jar = new CookieJar(); |
| | const ws = await this.cws(event.getQueryString(), jar); |
| | const pipe = event.pipe; |
| |
|
| | ws.on('message', (message) => { |
| | console.log(message.toString()); |
| | const { content, stop } = this.parseChunk(message.toString()); |
| | if (content !== '') { |
| | pipe.emit('data', content); |
| | } |
| | if (stop) { |
| | pipe.emit('end'); |
| | ws.close(); |
| | } |
| | }); |
| |
|
| | ws.on('close', () => { |
| | console.log('Websocket terputus...'); |
| | pipe.emit('end'); |
| | }); |
| |
|
| | ws.on('error', (err) => { |
| | console.error(err); |
| | pipe.emit('error', err); |
| | }); |
| | } catch (error) { |
| | console.error(error); |
| | event.pipe.emit('error', error); |
| | } |
| | }, |
| |
|
| | dispatcher: new EventEmitter(), |
| |
|
| | setupDispatcher() { |
| | this.dispatcher.on('JoinEvent', (event) => this.handleJoin(event)); |
| | }, |
| |
|
| | ask: async (query, mode = 'question', options = {}) => { |
| | const summon = iask.request(query, mode, options); |
| | const event = iask.eventx(summon); |
| | iask.dispatcher.emit('JoinEvent', event); |
| | return Readable.fromEventEmitter(event.pipe, ['data', 'end', 'error']); |
| | }, |
| |
|
| | init() { |
| | this.setupDispatcher(); |
| | } |
| | }; |
| |
|
| | async function Ask(query, mode = 'question', options = { detail_level: 'detailed' }) { |
| | try { |
| | iask.init(); |
| | const stream = await iask.ask(query, mode, options); |
| |
|
| | |
| | return new Promise((resolve, reject) => { |
| | let result = ''; |
| |
|
| | stream.on('data', (chunk) => { |
| | result += chunk; |
| | }); |
| |
|
| | stream.on('end', () => { |
| | console.log('Permintaan ke Websocket iASK berhasil..'); |
| | console.log('Result:', result); |
| | resolve({ result }); |
| | }); |
| |
|
| | stream.on('error', (err) => { |
| | console.error(err); |
| | reject({ err }); |
| | }); |
| | }); |
| |
|
| | } catch (error) { |
| | console.error(error); |
| | throw error; |
| | } |
| | } |
| |
|
| | app.post('/ask', async (req, res) => { |
| | const { query, mode = 'question', ...options } = req.body; |
| |
|
| | if (!query) { |
| | return res.status(400).json({ error: 'Query parameter is required' }); |
| | } |
| |
|
| | try { |
| | const result = await Ask(query, mode, options); |
| | |
| | return res.status(200).json(result); |
| | } catch (error) { |
| | console.error(error); |
| | return res.status(500).json({ error: 'Failed to handle the query' }); |
| | } |
| | }); |
| |
|
| | app.get('/ask', async (req, res) => { |
| | const { query, mode = 'question', ...options } = req.query; |
| |
|
| | if (!query) { |
| | return res.status(400).json({ error: 'Query parameter is required' }); |
| | } |
| |
|
| | try { |
| | const result = await Ask(query, mode, options); |
| | |
| | return res.status(200).json(result); |
| | } catch (error) { |
| | console.error(error); |
| | return res.status(500).json({ error: 'Failed to handle the query' }); |
| | } |
| | }); |
| |
|
| | let expressLogs = []; |
| | const originalLog = console.log; |
| | console.log = (...args) => { |
| | expressLogs.push(args.map(arg => (typeof arg === "object" ? JSON.stringify(arg) : arg)).join(" ")); |
| | originalLog.apply(console, args); |
| | }; |
| |
|
| | app.post("/playwright", async (req, res) => { |
| | const { code } = req.body; |
| | if (!code) return res.status(400).json({ error: "Kode tidak boleh kosong" }); |
| |
|
| | try { |
| | let vmLogs = []; |
| | const vm = new NodeVM({ |
| | console: "redirect", |
| | sandbox: {}, |
| | require: { |
| | external: true, |
| | builtin: ["fs", "path"], |
| | root: "./", |
| | mock: { playwright: { chromium } }, |
| | }, |
| | }); |
| |
|
| | vm.on("console.log", (msg) => vmLogs.push(msg)); |
| |
|
| | const startIndex = expressLogs.length; |
| | const runCode = vm.run(`module.exports = async () => { ${code} };`, "sandbox.js"); |
| | await runCode(); |
| |
|
| | const logsAfterRun = [...expressLogs.slice(startIndex), ...vmLogs]; |
| | res.json({ log: logsAfterRun.pop() || "" }); |
| | } catch (error) { |
| | res.status(500).json({ error: error.message, log: [...expressLogs, ...vmLogs].pop() || "" }); |
| | } |
| | }); |
| |
|
| | const PORT = process.env.PORT || 7860; |
| |
|
| | app.listen(PORT, async () => { |
| | console.log(`Server running on port ${PORT}`); |
| | await utils.initialize(); |
| | }); |
| |
|
| | process.on('SIGINT', async () => { |
| | await utils.close(); |
| | process.exit(0); |
| | }); |