├── src ├── temp │ └── .nomedia ├── images │ └── logo.jpg ├── -.env ├── misc │ ├── cilok.sticker.exif │ ├── loader.txt │ └── languages.json ├── install.bat ├── install.sh └── config.json ├── Procfile ├── .gitignore ├── docs ├── assets │ ├── icons.png │ ├── icons@2x.png │ ├── widgets.png │ ├── widgets@2x.png │ └── highlight.css ├── .nojekyll ├── modules │ ├── client.html │ ├── command.html │ ├── database.html │ ├── typings.ICommandHandler.html │ ├── utilities.promisify.html │ ├── metadata.html │ ├── event.html │ ├── utilities.chalk.html │ ├── utilities.inspect.html │ └── typings.html ├── modules.html ├── interfaces │ ├── utilities.chalk.Options.html │ ├── utilities.DebugLogger.html │ ├── utilities.EncodeIntoResult.html │ ├── typings.EventParser.html │ ├── utilities.chalk.ChalkFunction.html │ └── utilities.chalk.ColorSupport.html └── index.html ├── lib ├── test │ └── test.ts ├── typings │ ├── index.ts │ ├── client.ts │ ├── metadata.ts │ ├── global.d.ts │ └── command.ts ├── utilities │ ├── index.ts │ ├── measure.ts │ └── utilities.ts ├── addons │ └── webp.ts ├── commands │ ├── owner.ts │ └── menu.ts ├── index.ts ├── event.ts ├── database.ts ├── metadata.ts ├── command.ts └── client.ts ├── Dockerfile ├── LICENSE ├── package.json ├── README.md └── tsconfig.json /src/temp/.nomedia: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: npm start 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /dist/ 3 | /src/database -------------------------------------------------------------------------------- /src/images/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xevalous/cilok-bot/HEAD/src/images/logo.jpg -------------------------------------------------------------------------------- /docs/assets/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xevalous/cilok-bot/HEAD/docs/assets/icons.png -------------------------------------------------------------------------------- /docs/assets/icons@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xevalous/cilok-bot/HEAD/docs/assets/icons@2x.png -------------------------------------------------------------------------------- /docs/assets/widgets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xevalous/cilok-bot/HEAD/docs/assets/widgets.png -------------------------------------------------------------------------------- /docs/assets/widgets@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xevalous/cilok-bot/HEAD/docs/assets/widgets@2x.png -------------------------------------------------------------------------------- /lib/test/test.ts: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | 3 | async function main() {} 4 | main().then(console.log); 5 | -------------------------------------------------------------------------------- /src/-.env: -------------------------------------------------------------------------------- 1 | PG_HOST= 2 | PG_PORT=0 3 | PG_DATABASE= 4 | PG_USER= 5 | PG_PASSWORD= 6 | PG_SSL_REJECT_UNAUTH=false -------------------------------------------------------------------------------- /lib/typings/index.ts: -------------------------------------------------------------------------------- 1 | export * from './global'; 2 | export * from './client'; 3 | export * from './metadata'; 4 | export * from './command'; 5 | -------------------------------------------------------------------------------- /src/misc/cilok.sticker.exif: -------------------------------------------------------------------------------- 1 | II*AWb{"sticker-pack-id":"CilokStick","sticker-pack-name":"CilokBot","sticker-pack-publisher":"sticker"} -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. -------------------------------------------------------------------------------- /src/install.bat: -------------------------------------------------------------------------------- 1 | echo "CILOK | Starting Installation..." 2 | npm i pnpm -g 3 | pnpm i yarn -g 4 | pnpm install 5 | pnpm i ts-node -g 6 | echo "CILOK | Installation complete" -------------------------------------------------------------------------------- /src/misc/loader.txt: -------------------------------------------------------------------------------- 1 | ______ __ __ ___ __ 2 | / ___(_) /__ / /______/ _ )___ / /_ 3 | / /__/ / / _ \/ '_/___/ _ / _ \/ __/ 4 | \___/_/_/\___/_/\_\ /____/\___/\__/ 5 | -------------------------------------------------------------------------------- /lib/utilities/index.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import Measure from './measure'; 3 | 4 | export * from './utilities'; 5 | export * from 'fastest-levenshtein'; 6 | export * from 'util'; 7 | export { Measure, chalk }; 8 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14.19.1 2 | WORKDIR /app/ 3 | RUN apt-get update && apt-get install ffmpeg webp -y 4 | COPY package*.json ./ 5 | RUN npm install pnpm 6 | RUN pnpm install -P 7 | RUN pnpm build 8 | COPY . . 9 | CMD pnpm start 10 | -------------------------------------------------------------------------------- /src/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | sudo apt-get update 3 | sudo apt-get upgrade 4 | echo "CILOK | Starting Installation..." 5 | sudo apt-get install libwebp ffmpeg nodejs npm 6 | npm i pnpm -g 7 | pnpm i yarn -g 8 | pnpm install 9 | pnpm i ts-node -g 10 | echo "CILOK | Installation complete" -------------------------------------------------------------------------------- /lib/typings/client.ts: -------------------------------------------------------------------------------- 1 | import { Readable } from 'stream'; 2 | import { AnyMessageContent, MiscMessageGenerationOptions } from '@adiwajshing/baileys'; 3 | 4 | export type Content = 5 | | (AnyMessageContent & MiscMessageGenerationOptions) 6 | | { image: string } 7 | | { filename: string } 8 | | { video: string } 9 | | { image: string } 10 | | { audio: string }; 11 | 12 | export type ButtonConfig = { value: string } & ( 13 | | { 14 | reply: string; 15 | } 16 | | { 17 | url: string; 18 | } 19 | | { 20 | call: string; 21 | } 22 | | { 23 | title?: string; 24 | description?: string; 25 | listTitle?: string; 26 | } 27 | ); 28 | 29 | export type GetBuffer = string | number | Readable | readonly number[] | { valueOf(): string | Uint8Array | readonly number[] } | URL; 30 | -------------------------------------------------------------------------------- /lib/typings/metadata.ts: -------------------------------------------------------------------------------- 1 | import { GroupMetadata, proto } from '@adiwajshing/baileys'; 2 | 3 | export interface Metadata extends proto.IWebMessageInfo { 4 | sender: { 5 | jid: string | null | undefined; 6 | name: string | null | undefined; 7 | }; 8 | client: { 9 | name: string | undefined; 10 | jid: string; 11 | }; 12 | validator: { 13 | message: { 14 | isText: boolean; 15 | isMedia: boolean; 16 | }; 17 | isOwner: boolean; 18 | isGroup: boolean; 19 | }; 20 | utilities: { 21 | downloadMess: (filename?: string) => ReturnType; 22 | deleteMess: (forAll?: boolean) => Promise; 23 | }; 24 | type: [string, string | undefined]; 25 | string: string; 26 | from: string | null | undefined; 27 | fromMe: boolean; 28 | mentionedJid: string[] | undefined; 29 | message: proto.IMessage | null | undefined; 30 | quoted?: Metadata; 31 | body: proto.IMessage[keyof proto.IMessage]; 32 | data: string[]; 33 | groupMetadata?: GroupMetadata; 34 | } 35 | -------------------------------------------------------------------------------- /lib/typings/global.d.ts: -------------------------------------------------------------------------------- 1 | import Client from '../client'; 2 | import Command from '../command'; 3 | import Database from '../database'; 4 | import NodeCache from 'node-cache'; 5 | import { Pool } from 'pg'; 6 | import { Metadata } from './metadata'; 7 | import { AuthenticationState, GroupMetadata } from '@adiwajshing/baileys'; 8 | 9 | export type ProcessMode = 'dev' | 'production'; 10 | 11 | declare global { 12 | namespace NodeJS { 13 | interface ProcessEnv { 14 | NODE_ENV: ProcessMode; 15 | PG_HOST: string; 16 | PG_PORT: string; 17 | PG_DATABASE: string; 18 | PG_USER: string; 19 | PG_PASSWORD: string; 20 | PG_SSL_REJECT_UNAUTHORIZED: boolean; 21 | } 22 | } 23 | interface String { 24 | normalizeJid: () => string; 25 | compress: () => string; 26 | decompress: () => string; 27 | } 28 | var statusMessage: string; 29 | var config: typeof import('../../src/config.json'); 30 | var utilities: typeof import('../utilities'); 31 | var nodeCache: NodeCache; 32 | var database: Database; 33 | var client: Client; 34 | var command: Command; 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 I'am VEXG 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/addons/webp.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import FormData from 'form-data'; 3 | import { load } from 'cheerio'; 4 | import { append, headers } from '../utilities'; 5 | import { createReadStream, PathLike } from 'fs'; 6 | 7 | export default async function webp2mp4(file: PathLike): Promise<{ status: number; author: string; result: string }> { 8 | try { 9 | async function request(form: FormData, file = '') { 10 | return axios.post( 11 | file ? `https://ezgif.com/webp-to-mp4/${file}` : 'https://s6.ezgif.com/webp-to-mp4', 12 | form, 13 | headers(undefined, { 14 | 'Content-Type': `multipart/form-data; boundary=${form.getBoundary()}`, 15 | }), 16 | ); 17 | } 18 | let form = append.form({ 19 | 'new-image-url': '', 20 | 'new-image': createReadStream(file), 21 | }); 22 | let $ = load((await request(form)).data); 23 | const filename = $('input[name="file"]').attr('value'); 24 | form = append.form({ 25 | file: filename, 26 | convert: 'Convert WebP to MP4!', 27 | }); 28 | const response = await request(form, filename); 29 | $ = load(response.data); 30 | return { 31 | status: response.status, 32 | author: 'VEXG', 33 | result: `https:${$('div#output > p.outfile > video > source').attr('src')}`, 34 | }; 35 | } catch (e) { 36 | throw e; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/misc/languages.json: -------------------------------------------------------------------------------- 1 | { 2 | "af": "Afrikaans", 3 | "sq": "Albanian", 4 | "ar": "Arabic", 5 | "hy": "Armenian", 6 | "ca": "Catalan", 7 | "zh": "Chinese", 8 | "zh-cn": "Chinese (Mandarin/China)", 9 | "zh-tw": "Chinese (Mandarin/Taiwan)", 10 | "zh-yue": "Chinese (Cantonese)", 11 | "hr": "Croatian", 12 | "cs": "Czech", 13 | "da": "Danish", 14 | "nl": "Dutch", 15 | "en": "English", 16 | "en-au": "English (Australia)", 17 | "en-uk": "English (United Kingdom)", 18 | "en-us": "English (United States)", 19 | "eo": "Esperanto", 20 | "fi": "Finnish", 21 | "fr": "French", 22 | "de": "German", 23 | "el": "Greek", 24 | "ht": "Haitian Creole", 25 | "hi": "Hindi", 26 | "hu": "Hungarian", 27 | "is": "Icelandic", 28 | "id": "Indonesian", 29 | "it": "Italian", 30 | "ja": "Japanese", 31 | "ko": "Korean", 32 | "la": "Latin", 33 | "lv": "Latvian", 34 | "mk": "Macedonian", 35 | "no": "Norwegian", 36 | "pl": "Polish", 37 | "pt": "Portuguese", 38 | "pt-br": "Portuguese (Brazil)", 39 | "ro": "Romanian", 40 | "ru": "Russian", 41 | "sr": "Serbian", 42 | "sk": "Slovak", 43 | "es": "Spanish", 44 | "es-es": "Spanish (Spain)", 45 | "es-us": "Spanish (United States)", 46 | "sw": "Swahili", 47 | "sv": "Swedish", 48 | "ta": "Tamil", 49 | "th": "Thai", 50 | "tr": "Turkish", 51 | "vi": "Vietnamese", 52 | "cy": "Welsh" 53 | } 54 | -------------------------------------------------------------------------------- /lib/typings/command.ts: -------------------------------------------------------------------------------- 1 | import { Metadata } from './metadata'; 2 | 3 | export namespace ICommandHandler { 4 | export interface Event extends AdditonalEvent { 5 | name: string; 6 | command: { 7 | string: string[]; 8 | regExp: RegExp[]; 9 | }; 10 | tag: string[]; 11 | help?: string; 12 | index: number | null; 13 | callback: (mess: Metadata, property: CommandProperty) => Promise | any; 14 | prefix: boolean; 15 | enable: boolean; 16 | } 17 | export interface AdditonalEvent { 18 | /** Alternative commands will not appear in the menu, but it still can be executed */ 19 | alternativeCommand?: (string | RegExp)[]; 20 | mediaCustomReply?: string[] | string; 21 | quotedCustom?: string[] | string; 22 | quoted?: boolean; 23 | url?: boolean; 24 | admin?: boolean; 25 | clientAdminRequired?: boolean; 26 | query?: string; 27 | wait?: boolean | string; 28 | owner?: boolean | string; 29 | group?: boolean | string; 30 | media?: boolean | string; 31 | } 32 | export interface CommandProperty { 33 | instance: Event; 34 | text: string; 35 | query: string; 36 | command: string; 37 | commandWithQuery: string; 38 | prefix: string; 39 | modify: (event: Event) => Event; 40 | } 41 | } 42 | 43 | export interface EventParser { 44 | prefix: RegExp; 45 | index: number; 46 | commandWithQuery: string; 47 | command: string; 48 | } 49 | -------------------------------------------------------------------------------- /lib/commands/owner.ts: -------------------------------------------------------------------------------- 1 | import { exec } from 'child_process'; 2 | 3 | command.on( 4 | ['>', '->'], 5 | ['owner'], 6 | async (mess, { text, command }) => { 7 | const parse = command.includes('->') ? text.replace('->', 'return ') : text.replace('>', ''); 8 | try { 9 | const evaluate = await eval(`;(async () => {${parse} })()`).catch((err: unknown) => { 10 | client.reply(mess, err as string); 11 | }); 12 | return client.reply(mess, evaluate); 13 | } catch (err) { 14 | return client.reply(mess, err as string); 15 | } 16 | }, 17 | { 18 | owner: '--noresp', 19 | wait: false, 20 | prefix: false, 21 | }, 22 | ); 23 | 24 | command.on( 25 | ['$'], 26 | ['owner'], 27 | async (mess, { query }) => { 28 | try { 29 | exec(`${query}`, (e, a) => { 30 | if (e) return client.reply(mess, `${e}`); 31 | client.reply(mess, a); 32 | }); 33 | return void 0; 34 | } catch (err) { 35 | return client.reply(mess, err as string); 36 | } 37 | }, 38 | { 39 | owner: '--noresp', 40 | wait: false, 41 | prefix: false, 42 | }, 43 | ); 44 | 45 | command.on( 46 | ['setstatus'], 47 | ['owner'], 48 | (mess, { query }) => { 49 | try { 50 | query = query.trim(); 51 | if (query === '--reset') global.statusMessage = ''; 52 | else global.statusMessage = query; 53 | return client.reply(mess, '*Success* | Berhasil mengubah status message'); 54 | } catch (err) { 55 | return client.throw(mess, err, 'setstatus'); 56 | } 57 | }, 58 | { 59 | owner: '--noresp', 60 | }, 61 | ); 62 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cilok-v2-md", 3 | "version": "3.0.0", 4 | "author": "VEXG", 5 | "description": "A whatsApp bot for whatsApp multi-device", 6 | "main": "lib/index.ts", 7 | "engines": { 8 | "node": "14.19.1", 9 | "yarn": "1.22.18" 10 | }, 11 | "scripts": { 12 | "start": "node ./lib/index.js", 13 | "build": "tsc --p .", 14 | "dev": "ts-node --files lib/index.ts --dev", 15 | "test": "ts-node --files lib/test/test.ts --dev" 16 | }, 17 | "dependencies": { 18 | "@adiwajshing/baileys": "github:adiwajshing/baileys", 19 | "@adiwajshing/keyed-db": "^0.2.4", 20 | "@hapi/boom": "^9.1.4", 21 | "awesome-phonenumber": "^3.1.0", 22 | "axios": "^0.26.1", 23 | "axios-retry": "^3.2.5", 24 | "chalk": "4.1.2", 25 | "cheerio": "1.0.0-rc.10", 26 | "dayjs": "^1.11.3", 27 | "domhandler": "^4.3.1", 28 | "dotenv": "^16.0.1", 29 | "fastest-levenshtein": "^1.0.12", 30 | "file-type": "16.5.3", 31 | "fluent-ffmpeg": "^2.1.2", 32 | "form-data": "^4.0.0", 33 | "googlethis": "github:LuanRT/google-this", 34 | "jimp": "^0.16.1", 35 | "lodash": "^4.17.21", 36 | "node-cache": "^5.1.2", 37 | "node-cron": "^3.0.1", 38 | "pg": "^8.7.3", 39 | "pino": "^7.11.0", 40 | "qrcode-terminal": "^0.12.0", 41 | "sharp": "^0.30.7", 42 | "uuid": "^8.3.2", 43 | "youtubei": "^0.0.1-rc.36" 44 | }, 45 | "devDependencies": { 46 | "@types/fluent-ffmpeg": "^2.1.20", 47 | "@types/lodash": "^4.14.182", 48 | "@types/node": "^17.0.45", 49 | "@types/node-cron": "^3.0.1", 50 | "@types/pg": "^8.6.5", 51 | "typescript": "^4.7.4" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ownerNumber": ["6289679070459", "4365066999977", "436508448617777"], 3 | "prefix": ["!", "#"], 4 | "database": { 5 | "postgresql": true 6 | }, 7 | "path": { 8 | "database": "./src/database/", 9 | "temp": "./src/temp/", 10 | "exif": "./src/misc/cilok.sticker.exif" 11 | }, 12 | "unicode": { 13 | "splitter": ";", 14 | "bullet": "◈", 15 | "dash": "━", 16 | "wings": ["*❮* ", " *❯*"] 17 | }, 18 | "response": { 19 | "owner": "*REJECTED* | Perintah ini khusus owner", 20 | "admin": "*REJECTED* | Perintah ini hanya bisa diakses oleh Admin", 21 | "group": "*REJECTED* | Perintah ini hanya bisa di gunakan dalam grup", 22 | "wait": "*WAIT* | Sedang di proses...", 23 | "private": "*REJECTED* | Perintah ini hanya bisa di gunakan dalam private chat", 24 | "text": "*REJECTED* | Pesan harus berupa text", 25 | "url": "*REJECTED* | Link Invalid", 26 | "media": "*REJECTED* | Kirim / reply pesan media", 27 | "mediaCustom": "*REJECTED* | Pesan harus berupa", 28 | "mediaCustomReply": "*REJECTED* | Kirim / reply pesan yang berupa", 29 | "mention": "*REJECTED* | Tag orang yang ingin di target", 30 | "quoted": "*REJECTED* | Reply pesan yang ingin di target", 31 | "quotedCustom": "*REJECTED* | Reply pesan yang berupa", 32 | "error": "*ERROR* | Terjadi kesalahan pada saat melakukan perintah", 33 | "clientAdminRequired": "*PERMISSION* | Bot harus menjadi admin untuk melakukan perintah ini", 34 | "spam": "*SPAM DETECTED* | Tunggu 5 detik agar bisa menggunakan command lagi", 35 | "limit": "*REJECTED* | Limit download kamu sudah habis dan akan di reset pada jam 24.00, untuk selengkapnya silahkan ketik *limit*", 36 | "help": ", ketik *helpcommand* untuk mengetahui cara menggunakan fitur di setiap command" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/utilities/measure.ts: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import chalk from 'chalk'; 3 | import dayjs from 'dayjs'; 4 | import duration from 'dayjs/plugin/duration'; 5 | import { logger } from './utilities'; 6 | import { performance } from 'perf_hooks'; 7 | 8 | export default class Measure { 9 | private DB: Record; 10 | private static instance: Measure; 11 | private constructor() { 12 | this.DB = {} as typeof this.DB; 13 | } 14 | 15 | public static memoryUsage(): number { 16 | return Math.round((process.memoryUsage().heapUsed / 1024 / 1024) * 100) / 100; 17 | } 18 | 19 | public static start(tag: string): void { 20 | if (!Measure.instance) Measure.instance = new Measure(); 21 | this.instance.DB[tag] = { 22 | tag, 23 | heapMemory: { 24 | before: `${Measure.memoryUsage()} MB`, 25 | }, 26 | time: performance.now(), 27 | }; 28 | return void 0; 29 | } 30 | 31 | public static end = (tag: string) => 32 | new Promise((resolve, __) => { 33 | if (!this.instance.DB[tag]) return; //throw new Error(`${tag} does not exist.`); 34 | dayjs.extend(duration); 35 | this.instance.DB[tag].heapMemory.after = `${Measure.memoryUsage()} MB`; 36 | this.instance.DB[tag].time = `${dayjs 37 | .duration(performance.now() - (this.instance.DB[tag].time as number)) 38 | .asSeconds() 39 | .toFixed(3)} MS`; 40 | logger( 41 | 'measure', 42 | `${chalk.bold(_.upperFirst(tag))} took ${chalk.bold.underline(this.instance.DB[tag].time)} to complete with ${chalk.bold.underline( 43 | this.instance.DB[tag].heapMemory.before, 44 | )} to ${chalk.bold.underline(this.instance.DB[tag].heapMemory.after)} heap memory usage`, 45 | ); 46 | resolve(this.instance.DB[tag]); 47 | return delete this.instance.DB[tag]; 48 | }); 49 | } 50 | -------------------------------------------------------------------------------- /lib/index.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import chalk from 'chalk'; 3 | import config from '../src/config.json'; 4 | import Client from './client'; 5 | import Database from './database'; 6 | import NodeCache from 'node-cache'; 7 | import CommandHandler from './command'; 8 | import { ProcessMode } from './typings'; 9 | import { config as envConfig } from 'dotenv'; 10 | import * as fs from 'fs'; 11 | import * as utilities from './utilities'; 12 | 13 | utilities.Measure.start('run'); 14 | 15 | console.log(chalk.cyan.bold(fs.readFileSync('./src/misc/loader.txt', 'utf8'))); 16 | 17 | envConfig({ 18 | path: './src/.env', 19 | }); 20 | 21 | (async function () { 22 | try { 23 | const args: Array = process.argv 24 | .slice(2) 25 | .filter((v) => v.startsWith('--')) 26 | .map((v) => v.replace('--', '')); 27 | if (args.some((v) => v === 'dev')) process.env.NODE_ENV = 'dev'; 28 | else process.env.NODE_ENV = 'production'; 29 | 30 | globalThis.statusMessage = ''; 31 | globalThis.config = config; 32 | globalThis.utilities = utilities; 33 | globalThis.nodeCache = new NodeCache({ useClones: false, stdTTL: 43200 }); 34 | 35 | utilities.Measure.start('database'); 36 | globalThis.database = await Database.connect(true); 37 | utilities.Measure.end('database'); 38 | 39 | utilities.Measure.start('client'); 40 | globalThis.client = await Client.connect(); 41 | utilities.Measure.end('client'); 42 | 43 | utilities.Measure.start('commands'); 44 | globalThis.command = new CommandHandler(); 45 | for (const fn of fs.readdirSync(path.join('./lib/commands'))) 46 | if (!fn.endsWith('d.ts') && fn.endsWith(process.env.NODE_ENV === 'production' ? 'js' : 'ts')) await import(`./commands/${fn}`); 47 | utilities.Measure.end('commands'); 48 | 49 | utilities.Measure.end('run'); 50 | return void 0; 51 | } catch (err) { 52 | throw err; 53 | } 54 | })(); 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cilok-Bot - MD 2 | 3 | ## Documentation 4 | https://vexg.github.io/cilok-v3/ 5 | 6 | 7 | 8 | 9 | ## Installation 10 | #### Note 11 | - This source code using [pnpm](https://pnpm.io/) as primary dependencies manager. [Learn more](https://pnpm.io/motivation) 12 | - You can skip this section if you using heroku. 13 | 14 | ### Requirements (Skip this section if you are using linux) 15 | - [Node.js](https://nodejs.org/en/) 16 | - [Ffmpeg](https://github.com/BtbN/FFmpeg-Builds/releases) 17 | - [Libwebp](https://storage.googleapis.com/downloads.webmproject.org/releases/webp/index.html) 18 | 19 | ### For Linux 20 | ``` 21 | bash ./src/install.sh 22 | ``` 23 | ### For Windows 24 | ``` 25 | start ./src/install.bat 26 | ``` 27 | ## Heroku 28 | ### Buildpacks 29 | - heroku/nodejs 30 | - https://github.com/jonathanong/heroku-buildpack-ffmpeg-latest.git 31 | - https://github.com/clhuang/heroku-buildpack-webp-binaries.git 32 | 33 | ## Starting the BOT 34 | ### Config 35 | - From "-.env" rename to ".env" in ```src``` and fill the value in that file. 36 | - Fill your client number (Optional) & your other / primary number in [```config.json```](https://github.com/VEXG/cilok-v2-md/blob/f93ce0ec32b83ccc1f99f552346632808a6a33ae/src/cilok.config.json#L4) to validate that you're the owner of the bot & you can change the response message, unicode, etc if you want to. 37 | 38 | ### Run the BOT 39 | - run this command in your terminal 40 | - production 41 | ``` 42 | pnpm build && pnpm start 43 | ``` 44 | - development 45 | ``` 46 | pnpm dev 47 | ``` 48 | - scan the QR to make a session. session, chats will be stored in ```src/database``` 49 | - there will be some logs spamming because the logger level is set to ```info```, you can change the value to ```silent``` in [here](https://github.com/VEXG/cilok-v2-md/blob/f93ce0ec32b83ccc1f99f552346632808a6a33ae/lib/connection.ts#L19) to hide the spamming logs. [Learn more](https://github.com/pinojs/pino/blob/master/docs/api.md#loggerlevels-object) -------------------------------------------------------------------------------- /lib/commands/menu.ts: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | 3 | command.on( 4 | ['menu', 'help'], 5 | ['hidden'], 6 | (mess) => { 7 | try { 8 | const menuList: ButtonConfig[] = []; 9 | for (const a of Object.keys(command.tag)) 10 | if (!['owner', 'hidden'].includes(a)) 11 | menuList.push({ 12 | title: config.unicode.dash.repeat(30), 13 | listTitle: `${a[0].toUpperCase() + a.slice(1).toLowerCase()} menu`, 14 | value: `!${a}menu`, 15 | }); 16 | return client.sendButton( 17 | mess, 18 | { 19 | text: 'Berikut adalah list menu yang tersedia', 20 | footer: 'Made with ♡ by VEXG', 21 | buttonText: 'Klik untuk melihat', 22 | quoted: mess, 23 | }, 24 | menuList, 25 | ); 26 | } catch (err) { 27 | return client.throw(mess, err, 'menuList'); 28 | } 29 | }, 30 | { 31 | wait: false, 32 | }, 33 | ); 34 | 35 | command.on( 36 | [/^(?!menu)[a-z]{0,20}(menu)/i], 37 | ['hidden'], 38 | (mess, { prefix, command }) => { 39 | try { 40 | const tagKey: string = command.split('menu')[0].toLowerCase(); 41 | if (!global.command.tag[tagKey]) return; 42 | if (['owner', 'hidden'].includes(tagKey) && !mess.validator.isOwner) return client.reply(mess, config.response.owner); // only owner can access this menu 43 | let getCommand: string[] = [], 44 | commandList = `${utilities.wings(`${tagKey[0].toUpperCase() + tagKey.slice(1).toLowerCase()} menu`)}`; 45 | for (const a in global.command.tag[tagKey]) 46 | if (global.command.tag[tagKey][a].enable) 47 | getCommand = getCommand.concat( 48 | (global.command.tag[tagKey][a].prefix ? prefix : '') + 49 | _.difference(global.command.tag[tagKey][a].command.string, global.command.tag[tagKey][a].alternativeCommand ?? []), 50 | ); 51 | for (const a of getCommand.sort()) 52 | if (a.includes(',')) { 53 | for (const b of a.split(',')) commandList += `\n${config.unicode.bullet} ${prefix}${b.replace(prefix, '')}`; 54 | } else { 55 | commandList += `\n${config.unicode.bullet} ${a}`; 56 | } 57 | return client.sendButton( 58 | mess, 59 | { 60 | text: commandList.trim(), 61 | footer: 'Made with ♡ by VEXG', 62 | quoted: mess, 63 | }, 64 | [], 65 | ); 66 | } catch (err) { 67 | return client.throw(mess, err, 'section menu'); 68 | } 69 | }, 70 | { 71 | wait: false, 72 | }, 73 | ); 74 | 75 | command.on(['bugreport'], ['hidden'], async (mess, { query }) => { 76 | try { 77 | await client.sendMessage(`${config.ownerNumber[0]}@s.whatsapp.net`, { 78 | text: `${utilities.wings('Bug report')}\n${utilities.parseJSON({ 79 | dari: `wa.me/${mess.sender.jid?.replace(/:/g, '').split('@')[0]}`, 80 | pesan: query.trim(), 81 | })}` as string, 82 | }); 83 | await client.reply(mess, '*SENDED* | Terimakasih, pesan sudah terkirim ke owner'); 84 | return client.reply(mess, '*ALERT* | Jika mengirim pesan bugreport dengan tujuan iseng maka akan di block'); 85 | } catch (err) { 86 | return client.throw(mess, err, 'bugreport'); 87 | } 88 | }); 89 | -------------------------------------------------------------------------------- /lib/event.ts: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import path from 'path'; 3 | import Client from './client'; 4 | import metadata from './metadata'; 5 | import { Boom } from '@hapi/boom'; 6 | import { schedule } from 'node-cron'; 7 | import { BinaryNode } from '@adiwajshing/baileys'; 8 | import * as fs from 'fs'; 9 | 10 | export async function eventHandler(client: Client) { 11 | client.socket.ev.on('creds.update', () => client.auth.saveState()); 12 | 13 | client.socket.ev.on('connection.update', (condition) => { 14 | switch (condition.connection) { 15 | case 'open': 16 | utilities.logger('info', 'Connected to whatsapp'); 17 | break; 18 | case 'close': 19 | const statusCode = (condition.lastDisconnect?.error as Boom).output.statusCode; 20 | if (statusCode !== client.baileys.DisconnectReason.loggedOut) Client.connect(); 21 | else process.exit(); 22 | break; 23 | } 24 | }); 25 | 26 | client.socket.ev.on('messages.upsert', async (ev) => { 27 | if (!Object.keys(ev.messages[0]).includes('message') || !Object.keys(ev.messages[0]).includes('key')) return; 28 | 29 | if (!database.storage?.chats) database.storage.chats = {}; 30 | if (!database.storage?.chatsId) database.storage.chatsId = []; 31 | 32 | const mess = await metadata(ev.messages[0]); 33 | if (mess.type[0] === 'protocolMessage') client.socket.ev.emit('messages.delete', { keys: [mess.message?.protocolMessage?.key!] }); 34 | if (mess.key.id!.length < 20 || mess.from === 'status@broadcast') return; 35 | 36 | nodeCache.set(mess.key.id!, mess); 37 | // TODO: Remove isGroup validator 38 | if (!_.has(database.storage.chats, mess.from!) && mess.validator.isGroup) database.storage.chats[mess.from!] = {}; 39 | if (!database.storage.chatsId.includes(mess.from)) database.storage.chatsId.push(mess.from); 40 | 41 | return Promise.all([client.socket.readMessages([mess.key]), command.emit(mess)]); 42 | }); 43 | 44 | client.socket.ev.on('groups.update', async (ev) => { 45 | if (database.storage.chats[ev[0].id!]) database.storage.chats[`${ev[0].id}`].groupMetadata = await client.socket.groupMetadata(ev[0].id!); 46 | }); 47 | 48 | client.socket.ev.on('group-participants.update', async (ev) => { 49 | if (database.storage.chats[ev.id]) database.storage.chats[`${ev.id}`].groupMetadata = await client.socket.groupMetadata(ev.id); 50 | }); 51 | 52 | client.socket.ws.on('CB:call,,offer', async (ev: BinaryNode) => { 53 | const caller = ev.attrs.from; 54 | if (config.ownerNumber.includes(caller.split('@')[0])) return; 55 | await client.sendMessage(caller, { 56 | text: '*BLOCKED* | Anda telah di block karena menelepon cilok', 57 | }); 58 | await utilities.delay(550); 59 | return client.socket.updateBlockStatus(caller, 'block'); 60 | }); 61 | 62 | return void 0; 63 | } 64 | 65 | export function scheduleHandler() { 66 | const tempSchedule = schedule('*/1.2 * * * *', () => { 67 | return fs.readdir(config.path.temp, (err, f) => { 68 | if (err) throw err; 69 | if (f.length > 1) 70 | f.forEach((v) => { 71 | if (v !== '.nomedia') fs.unlinkSync(path.join(config.path.temp, v)); 72 | }); 73 | }); 74 | }); 75 | fs.watch(config.path.temp, (e) => { 76 | if (e === 'rename') { 77 | tempSchedule.stop(); 78 | tempSchedule.start(); 79 | } 80 | }); 81 | 82 | schedule('*/15 * * * *', () => Promise.all([database.save(), utilities.Measure.memoryUsage() > 100 ? nodeCache.flushAll() : void 0])); 83 | } 84 | -------------------------------------------------------------------------------- /docs/assets/highlight.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --light-hl-0: #001080; 3 | --dark-hl-0: #9CDCFE; 4 | --light-hl-1: #000000; 5 | --dark-hl-1: #D4D4D4; 6 | --light-hl-2: #0000FF; 7 | --dark-hl-2: #569CD6; 8 | --light-hl-3: #0070C1; 9 | --dark-hl-3: #4FC1FF; 10 | --light-hl-4: #795E26; 11 | --dark-hl-4: #DCDCAA; 12 | --light-hl-5: #A31515; 13 | --dark-hl-5: #CE9178; 14 | --light-hl-6: #AF00DB; 15 | --dark-hl-6: #C586C0; 16 | --light-hl-7: #267F99; 17 | --dark-hl-7: #4EC9B0; 18 | --light-hl-8: #008000; 19 | --dark-hl-8: #6A9955; 20 | --light-hl-9: #098658; 21 | --dark-hl-9: #B5CEA8; 22 | --light-hl-10: #EE0000; 23 | --dark-hl-10: #D7BA7D; 24 | --light-hl-11: #811F3F; 25 | --dark-hl-11: #D16969; 26 | --light-hl-12: #EE0000; 27 | --dark-hl-12: #DCDCAA; 28 | --light-hl-13: #000000FF; 29 | --dark-hl-13: #D4D4D4; 30 | --light-code-background: #F5F5F5; 31 | --dark-code-background: #1E1E1E; 32 | } 33 | 34 | @media (prefers-color-scheme: light) { :root { 35 | --hl-0: var(--light-hl-0); 36 | --hl-1: var(--light-hl-1); 37 | --hl-2: var(--light-hl-2); 38 | --hl-3: var(--light-hl-3); 39 | --hl-4: var(--light-hl-4); 40 | --hl-5: var(--light-hl-5); 41 | --hl-6: var(--light-hl-6); 42 | --hl-7: var(--light-hl-7); 43 | --hl-8: var(--light-hl-8); 44 | --hl-9: var(--light-hl-9); 45 | --hl-10: var(--light-hl-10); 46 | --hl-11: var(--light-hl-11); 47 | --hl-12: var(--light-hl-12); 48 | --hl-13: var(--light-hl-13); 49 | --code-background: var(--light-code-background); 50 | } } 51 | 52 | @media (prefers-color-scheme: dark) { :root { 53 | --hl-0: var(--dark-hl-0); 54 | --hl-1: var(--dark-hl-1); 55 | --hl-2: var(--dark-hl-2); 56 | --hl-3: var(--dark-hl-3); 57 | --hl-4: var(--dark-hl-4); 58 | --hl-5: var(--dark-hl-5); 59 | --hl-6: var(--dark-hl-6); 60 | --hl-7: var(--dark-hl-7); 61 | --hl-8: var(--dark-hl-8); 62 | --hl-9: var(--dark-hl-9); 63 | --hl-10: var(--dark-hl-10); 64 | --hl-11: var(--dark-hl-11); 65 | --hl-12: var(--dark-hl-12); 66 | --hl-13: var(--dark-hl-13); 67 | --code-background: var(--dark-code-background); 68 | } } 69 | 70 | body.light { 71 | --hl-0: var(--light-hl-0); 72 | --hl-1: var(--light-hl-1); 73 | --hl-2: var(--light-hl-2); 74 | --hl-3: var(--light-hl-3); 75 | --hl-4: var(--light-hl-4); 76 | --hl-5: var(--light-hl-5); 77 | --hl-6: var(--light-hl-6); 78 | --hl-7: var(--light-hl-7); 79 | --hl-8: var(--light-hl-8); 80 | --hl-9: var(--light-hl-9); 81 | --hl-10: var(--light-hl-10); 82 | --hl-11: var(--light-hl-11); 83 | --hl-12: var(--light-hl-12); 84 | --hl-13: var(--light-hl-13); 85 | --code-background: var(--light-code-background); 86 | } 87 | 88 | body.dark { 89 | --hl-0: var(--dark-hl-0); 90 | --hl-1: var(--dark-hl-1); 91 | --hl-2: var(--dark-hl-2); 92 | --hl-3: var(--dark-hl-3); 93 | --hl-4: var(--dark-hl-4); 94 | --hl-5: var(--dark-hl-5); 95 | --hl-6: var(--dark-hl-6); 96 | --hl-7: var(--dark-hl-7); 97 | --hl-8: var(--dark-hl-8); 98 | --hl-9: var(--dark-hl-9); 99 | --hl-10: var(--dark-hl-10); 100 | --hl-11: var(--dark-hl-11); 101 | --hl-12: var(--dark-hl-12); 102 | --hl-13: var(--dark-hl-13); 103 | --code-background: var(--dark-code-background); 104 | } 105 | 106 | .hl-0 { color: var(--hl-0); } 107 | .hl-1 { color: var(--hl-1); } 108 | .hl-2 { color: var(--hl-2); } 109 | .hl-3 { color: var(--hl-3); } 110 | .hl-4 { color: var(--hl-4); } 111 | .hl-5 { color: var(--hl-5); } 112 | .hl-6 { color: var(--hl-6); } 113 | .hl-7 { color: var(--hl-7); } 114 | .hl-8 { color: var(--hl-8); } 115 | .hl-9 { color: var(--hl-9); } 116 | .hl-10 { color: var(--hl-10); } 117 | .hl-11 { color: var(--hl-11); } 118 | .hl-12 { color: var(--hl-12); } 119 | .hl-13 { color: var(--hl-13); } 120 | pre, code { background: var(--code-background); } 121 | -------------------------------------------------------------------------------- /docs/modules/client.html: -------------------------------------------------------------------------------- 1 | client | cilok-v2-md
Options
All
  • Public
  • Public/Protected
  • All
Menu

Module client

Legend

  • Namespace
  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Interface
  • Class

Settings

Theme

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/modules/command.html: -------------------------------------------------------------------------------- 1 | command | cilok-v2-md
Options
All
  • Public
  • Public/Protected
  • All
Menu

Module command

Legend

  • Namespace
  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Interface
  • Class

Settings

Theme

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/modules/database.html: -------------------------------------------------------------------------------- 1 | database | cilok-v2-md
Options
All
  • Public
  • Public/Protected
  • All
Menu

Module database

Legend

  • Namespace
  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Interface
  • Class

Settings

Theme

Generated using TypeDoc

-------------------------------------------------------------------------------- /lib/database.ts: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import path from 'path'; 3 | import { Pool } from 'pg'; 4 | import * as fs from 'fs'; 5 | 6 | export default class Database { 7 | private static instance: Database; 8 | private static PG: Pool; 9 | public storage: Partial>; 10 | private constructor() { 11 | this.storage = {}; 12 | } 13 | 14 | public static async connect(usePgsql: boolean): Promise { 15 | try { 16 | if (!Database.instance) Database.instance = new Database(); 17 | if (!fs.existsSync(config.path.database)) fs.mkdirSync(config.path.database); 18 | if (usePgsql) { 19 | utilities.logger('database', 'Using postgreSQL'); 20 | await this.instance.PGHandler(); 21 | } else { 22 | utilities.logger('database', 'Using local database'); 23 | for (const v of fs.readdirSync(config.path.database)) 24 | Database.instance.storage[v.split('.')[0]] = (await import(path.join(path.resolve('.'), './src/database', v))).default; 25 | } 26 | utilities.logger('database', `Loaded ${Object.keys(Database.instance.storage).length} database(s)`); 27 | return Database.instance; 28 | } catch (err) { 29 | throw err; 30 | } 31 | } 32 | 33 | private async PGHandler(): Promise { 34 | const config = { 35 | host: process.env.PG_HOST, 36 | port: parseInt(process.env.PG_PORT!), 37 | database: process.env.PG_DATABASE, 38 | user: process.env.PG_USER, 39 | password: process.env.PG_PASSWORD, 40 | ...(process.env.PG_SSL_REJECT_UNAUTH !== 'null' ? { ssl: { rejectUnauthorized: process.env.PG_SSL_REJECT_UNAUTH === 'true' } } : {}), 41 | }; 42 | if (Object.values(config).some((v) => v === undefined || (typeof v === 'number' && isNaN(v as number)))) 43 | throw new Error('There is a invalid value in PGsql config, please check your .env file'); 44 | Database.PG = new Pool(config); 45 | Database.PG.on('error', (err) => { 46 | throw err; 47 | }); 48 | await Database.PG.query( 49 | 'CREATE TABLE IF NOT EXISTS clk_database(tag varchar(100) PRIMARY KEY, data text); DELETE FROM clk_database WHERE data IS NULL;', 50 | ); 51 | const data = await Database.PG.query<{ tag: string; data: string }>('SELECT * FROM clk_database'); 52 | if (data.rowCount > 0) 53 | for (const v of data.rows) { 54 | if (!v) continue; 55 | if (v.tag === 'session') this.writeSession(v.data); 56 | else 57 | try { 58 | this.storage[v.tag] = JSON.parse(v.data); 59 | } catch { 60 | this.storage[v.tag] = v.data; 61 | } 62 | } 63 | return void 0; 64 | } 65 | 66 | public save(fromLocalToPG?: boolean) { 67 | try { 68 | utilities.logger('database', 'Saving database...'); 69 | const data: [string, string | object | any[]][] = fromLocalToPG 70 | ? _.map(fs.readdirSync(config.path.database), (v) => [v.split('.')[0], fs.readFileSync(path.join(config.path.database, v), 'utf8')]) 71 | : Object.entries( 72 | _.merge( 73 | _.pickBy(this.storage, (v) => v), 74 | fs.existsSync(`${config.path.database}session.json`) && Database.PG 75 | ? { 76 | session: fs.readFileSync(`${config.path.database}session.json`, 'utf8'), 77 | } 78 | : {}, 79 | ), 80 | ); 81 | data.forEach(async (v) => { 82 | if (v[1]) { 83 | v[1] = typeof v[1] === 'string' ? v[1] : JSON.stringify(v[1]); 84 | if (Database.PG) 85 | await Database.PG.query('INSERT INTO clk_database(tag, data) VALUES($1, $2) ON CONFLICT (tag) DO UPDATE SET data = $2', [v[0], v[1]]); 86 | else fs.writeFileSync(`${config.path.database}${v[0]}.json`, v[1]); 87 | } 88 | }); 89 | utilities.logger('database', 'Successfully saved database'); 90 | return void 0; 91 | } catch (err) { 92 | throw err; 93 | } 94 | } 95 | 96 | private async writeSession(data: string): Promise { 97 | fs.writeFileSync(`${config.path.database}session.json`, data); 98 | return void 0; 99 | } 100 | 101 | public async writeDatabaseToLocal(): Promise { 102 | for (const k of Object.entries(this.storage)) 103 | fs.writeFileSync(`${config.path.database}${k[0]}.${typeof k[1] === 'string' ? 'txt' : 'json'}`, JSON.stringify(k[1])); 104 | return void 0; 105 | } 106 | 107 | public async deleteRow(...tag: T): Promise { 108 | if (!Database.PG) throw new Error('Must use PostgreSQL database'); 109 | for (const t of tag) await Database.PG.query('DELETE FROM clk_database WHERE tag = $1', [t]); 110 | return void 0; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /docs/modules.html: -------------------------------------------------------------------------------- 1 | cilok-v2-md
Options
All
  • Public
  • Public/Protected
  • All
Menu

cilok-v2-md

Legend

  • Namespace
  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Interface
  • Class

Settings

Theme

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/modules/typings.ICommandHandler.html: -------------------------------------------------------------------------------- 1 | ICommandHandler | cilok-v2-md
Options
All
  • Public
  • Public/Protected
  • All
Menu

Namespace ICommandHandler

Legend

  • Namespace
  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Interface
  • Class

Settings

Theme

Generated using TypeDoc

-------------------------------------------------------------------------------- /lib/metadata.ts: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import { Metadata } from './typings/metadata'; 3 | import * as baileys from '@adiwajshing/baileys'; 4 | 5 | export default async function metadata(mess: baileys.proto.IWebMessageInfo) { 6 | const proto = _.assign({} as Metadata, mess); 7 | proto.type = [ 8 | Object.keys(mess.message!)[0], 9 | Object.keys(mess.message!).find((a) => { 10 | const b = a.toString().toLowerCase(); 11 | return !b.includes('senderkey') && !b.includes('context'); 12 | }), 13 | ]; // [0] for realType else [1] 14 | proto.message = proto.type[1] === 'ephemeralMessage' ? mess.message?.ephemeralMessage?.message : mess.message; 15 | proto.data = 16 | typeof mess.message![proto.type[1] as keyof baileys.proto.IMessage] === 'object' 17 | ? Object.keys(mess.message![proto.type[1] as keyof baileys.proto.IMessage]!).includes('contextInfo') 18 | ? Object.keys(mess.message![proto.type[1] as keyof baileys.proto.IMessage]!).concat( 19 | Object.keys((mess.message![proto.type[1] as keyof baileys.proto.IMessage]! as Record).contextInfo), 20 | ) 21 | : Object.keys(mess.message![proto.type[1] as keyof baileys.proto.IMessage]!) 22 | : Object.keys(mess.message!); 23 | proto.string = 24 | proto.type[1] === 'conversation' 25 | ? mess.message?.conversation 26 | : proto.data.includes('caption') 27 | ? (mess.message as Record)[proto.type[1] as keyof baileys.proto.IMessage]!.caption 28 | : proto.type[1] === 'extendedTextMessage' 29 | ? (mess.message as Record)[proto.type[1] as keyof baileys.proto.IMessage].text 30 | : proto.type[1] === 'templateButtonReplyMessage' 31 | ? (mess.message as Record)[proto.type[1] as keyof baileys.proto.IMessage].selectedId 32 | : proto.type[1] === 'listResponseMessage' 33 | ? (mess.message as Record)[proto.type[1] as keyof baileys.proto.IMessage].singleSelectReply.selectedRowId 34 | : ''; 35 | proto.body = mess.message![proto.type[1] as keyof baileys.proto.IMessage]; 36 | proto.from = mess.key.remoteJid; 37 | proto.validator = { 38 | message: { 39 | isText: proto.type[1] === 'conversation' || proto.type[1] === 'extendedTextMessage', 40 | isMedia: 41 | proto.type[1] === 'stickerMessage' || 42 | proto.type[1] === 'imageMessage' || 43 | proto.type[1] === 'audioMessage' || 44 | proto.type[1] === 'videoMessage' || 45 | proto.type[1] === 'documentMessage', 46 | }, 47 | isOwner: false, 48 | isGroup: proto.from!.includes('@g.us'), 49 | }; 50 | proto.sender = { 51 | name: mess.pushName, 52 | jid: proto.validator.isGroup ? (mess.key.participant ? mess.key.participant : client.socket.user?.id) : mess.key.remoteJid, 53 | }; 54 | proto.validator.isOwner = 55 | config.ownerNumber.includes(proto.sender.jid ? proto.sender.jid.replace(/\:[0-9]{2}/, '').split('@')[0] : '') || (mess.key.fromMe ?? false); 56 | proto.client = { 57 | name: client.socket.user?.name, 58 | jid: client.socket.user!.id, 59 | }; 60 | proto.mentionedJid = 61 | proto.data.includes('contextInfo') && proto.data.includes('mentionedJid') 62 | ? (mess.message as Record)[proto.type[1] as keyof baileys.proto.IMessage].contextInfo.mentionedJid 63 | : undefined; 64 | proto.quoted = 65 | proto.data.includes('contextInfo') && proto.data.includes('quotedMessage') 66 | ? ({ 67 | key: { 68 | remoteJid: proto.from, 69 | fromMe: 70 | (mess.message as Record)[proto.type[1] as keyof baileys.proto.IMessage].contextInfo.participant === 71 | client.socket.user?.id, 72 | id: (mess.message as Record)[proto.type[1] as keyof baileys.proto.IMessage].contextInfo.stanzaId, 73 | participant: (mess.message as Record)[proto.type[1] as keyof baileys.proto.IMessage].contextInfo 74 | .participant, 75 | }, 76 | message: (mess.message as Record)[proto.type[1] as keyof baileys.proto.IMessage].contextInfo 77 | .quotedMessage, 78 | } as { 79 | key: baileys.proto.IMessageKey; 80 | message: baileys.proto.IMessage; 81 | } as Metadata) 82 | : undefined; 83 | proto.groupMetadata = proto.validator.isGroup ? database.storage.chats[proto.from!]?.groupMetadata ?? undefined : undefined; 84 | if (proto.validator.isGroup && !proto.groupMetadata) { 85 | proto.groupMetadata = await client.socket.groupMetadata(proto.from!); 86 | if (_.has(database.storage.chats, proto.from!)) database.storage.chats[`${proto.from!}`].groupMetadata = proto.groupMetadata; 87 | } 88 | proto.utilities = { 89 | downloadMess: async (filename) => await client.downloadContent(proto.message! as Metadata, filename!), 90 | deleteMess: (forAll = true) => { 91 | if (forAll) 92 | return client.socket.sendMessage(proto.from!, { 93 | delete: mess.key!, 94 | }); 95 | else 96 | return client.socket.chatModify( 97 | { clear: { messages: [{ id: mess.key.id!, fromMe: true, timestamp: mess.messageTimestamp as number }] } }, 98 | proto.from!, 99 | ); 100 | }, 101 | }; 102 | proto.quoted = proto.quoted ? (await nodeCache.get(proto.quoted.key.id!)) || (await metadata(proto.quoted! as Metadata)) : undefined; 103 | return proto; 104 | } 105 | -------------------------------------------------------------------------------- /docs/modules/utilities.promisify.html: -------------------------------------------------------------------------------- 1 | promisify | cilok-v2-md
Options
All
  • Public
  • Public/Protected
  • All
Menu

Namespace promisify

Index

Variables

Variables

custom: unique symbol
2 |

That can be used to declare custom promisified variants of functions.

3 |

Legend

  • Namespace
  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Interface
  • Class

Settings

Theme

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/modules/metadata.html: -------------------------------------------------------------------------------- 1 | metadata | cilok-v2-md
Options
All
  • Public
  • Public/Protected
  • All
Menu

Module metadata

Index

Functions

Functions

  • default(mess: IWebMessageInfo): Promise<Metadata & IWebMessageInfo>

Legend

  • Namespace
  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Interface
  • Class

Settings

Theme

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/interfaces/utilities.chalk.Options.html: -------------------------------------------------------------------------------- 1 | Options | cilok-v2-md
Options
All
  • Public
  • Public/Protected
  • All
Menu

Hierarchy

  • Options

Index

Properties

Properties

level?: Level
2 |

Specify the color support for Chalk.

3 |

By default, color support is automatically detected based on the environment.

4 |

Levels:

5 |
    6 |
  • 0 - All colors disabled.
  • 7 |
  • 1 - Basic 16 colors support.
  • 8 |
  • 2 - ANSI 256 colors support.
  • 9 |
  • 3 - Truecolor 16 million colors support.
  • 10 |
11 |

Legend

  • Namespace
  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Interface
  • Class

Settings

Theme

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/modules/event.html: -------------------------------------------------------------------------------- 1 | event | cilok-v2-md
Options
All
  • Public
  • Public/Protected
  • All
Menu

Module event

Index

Functions

  • eventHandler(client: default): Promise<undefined>
  • scheduleHandler(): void

Legend

  • Namespace
  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Interface
  • Class

Settings

Theme

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/interfaces/utilities.DebugLogger.html: -------------------------------------------------------------------------------- 1 | DebugLogger | cilok-v2-md
Options
All
  • Public
  • Public/Protected
  • All
Menu

Interface DebugLogger

Hierarchy

Callable

  • DebugLogger(msg: string, ...param: unknown[]): void
  • Parameters

    • msg: string
    • Rest ...param: unknown[]

    Returns void

Index

Properties

Properties

enabled: boolean

Legend

  • Namespace
  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Interface
  • Class

Settings

Theme

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/interfaces/utilities.EncodeIntoResult.html: -------------------------------------------------------------------------------- 1 | EncodeIntoResult | cilok-v2-md
Options
All
  • Public
  • Public/Protected
  • All
Menu

Interface EncodeIntoResult

Hierarchy

  • EncodeIntoResult

Index

Properties

Properties

read: number
2 |

The read Unicode code units of input.

3 |
written: number
4 |

The written UTF-8 bytes of output.

5 |

Legend

  • Namespace
  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Interface
  • Class

Settings

Theme

Generated using TypeDoc

-------------------------------------------------------------------------------- /lib/command.ts: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import { proto } from '@adiwajshing/baileys'; 3 | import { EventParser, ICommandHandler, Metadata } from './typings'; 4 | 5 | export default class CommandHandler { 6 | commandList: ICommandHandler.Event[]; 7 | tag: Record; 8 | prefix: RegExp[]; 9 | constructor() { 10 | this.commandList = []; 11 | this.tag = {}; 12 | this.prefix = config.prefix.map((v) => (typeof v === 'string' ? new RegExp(`^(${utilities.parseRegex(v)})`, 'i') : v)); 13 | } 14 | 15 | public on = ( 16 | command: (string | RegExp)[], 17 | tag: string[], 18 | callback: (mess: Metadata, property: ICommandHandler.CommandProperty) => Promise | any, 19 | commandConfig?: ICommandHandler.AdditonalEvent | ICommandHandler.Event, 20 | ): void => { 21 | const event: ICommandHandler.Event = { 22 | name: command.map((v) => (typeof v === 'string' ? v : v.toString())).join('/'), 23 | command: { 24 | string: _.concat(command, commandConfig?.alternativeCommand ?? []).map((v) => (typeof v === 'string' ? v : v.toString())), 25 | regExp: _.concat(command, commandConfig?.alternativeCommand ?? []).map((v) => 26 | typeof v === 'string' ? new RegExp(`^(${utilities.parseRegex(v)})`, 'i') : v, 27 | ), 28 | }, 29 | tag, 30 | help: undefined, 31 | wait: true, 32 | prefix: true, 33 | enable: true, 34 | index: null, 35 | callback, 36 | ...commandConfig, 37 | }; 38 | 39 | for (const k of tag) { 40 | this.tag[k] = this.tag[k] ? this.tag[k] : []; 41 | this.tag[k].push(event); 42 | } 43 | 44 | event.index = this.commandList.length; 45 | this.commandList.push(event); 46 | }; 47 | 48 | public emit = (mess: Metadata) => { 49 | const event = this.getCommand(_.deburr(mess.string)); 50 | try { 51 | if (!event) return; 52 | const access = this.getAccess(mess, event); 53 | if (access === 200) { 54 | event.instance.callback(mess, { 55 | ...event, 56 | }); 57 | return event; 58 | } 59 | return void 0; 60 | } catch (err) { 61 | throw err; 62 | } 63 | }; 64 | 65 | private action = ( 66 | mess: Metadata, 67 | event: string | string[] | boolean, 68 | responseKey: string, 69 | additonal?: { 70 | prefix?: string; 71 | command?: string; 72 | }, 73 | ): Promise | void => { 74 | const resultResponse = 75 | typeof event === 'boolean' 76 | ? config.response[responseKey as keyof typeof config.response] 77 | : event.includes('?>') && !(event instanceof Array) 78 | ? `${config.response[responseKey as keyof typeof config.response]} ${event.split('?>')[1]}` 79 | : event; 80 | if (resultResponse === '--noresp') return; 81 | return client.reply( 82 | mess, 83 | (() => { 84 | const possiblyArray = ['mediaCustomReply', 'quotedCustom']; 85 | let result = ''; 86 | if (possiblyArray.includes(responseKey)) { 87 | const length = resultResponse.length; 88 | result = config.response[responseKey as keyof typeof config.response]; 89 | if (Array.isArray(resultResponse) && length > 1) 90 | resultResponse.forEach((v, i) => { 91 | if (i === length - 1) result += `dan ${v}`; 92 | else result += ` ${v}, `; 93 | }); 94 | else result += ` ${resultResponse}`; 95 | } else if (!Array.isArray(resultResponse)) { 96 | result = 97 | resultResponse.trim() + 98 | (() => { 99 | if (!['owner', 'wait'].includes(responseKey)) 100 | if (['query'].includes(responseKey) || resultResponse.includes('')) return config.response.help; 101 | return ''; 102 | })(); 103 | } 104 | return result; 105 | })()! 106 | .replace('', additonal?.prefix ?? '') 107 | .replace('', additonal?.command ?? ''), 108 | ); 109 | }; 110 | 111 | private getAccess = (mess: Metadata, event: ICommandHandler.CommandProperty): void | Promise | 200 => { 112 | const eventType = event.instance; 113 | let CONFIG!: [string | string[] | boolean, string]; 114 | 115 | if (eventType.query && event.query.trim().length < 1) CONFIG = [eventType.query, 'query']; 116 | if (eventType.group && !mess.validator.isGroup) CONFIG = [eventType.group, 'group']; 117 | if (eventType.owner && !mess.validator.isOwner) CONFIG = [eventType.owner, 'owner']; 118 | if (eventType.url && !utilities.url(event.query).isValid) CONFIG = [eventType.url, 'url']; 119 | if (eventType.quoted && !mess.quoted) CONFIG = [eventType.quoted, 'quoted']; 120 | if (eventType.media && !(mess.quoted?.validator.message.isMedia ?? mess.validator.message.isMedia)) CONFIG = [eventType.media, 'media']; 121 | if ( 122 | eventType.group && 123 | typeof mess.groupMetadata?.participants.find((v) => v.id.normalizeJid() === mess.sender.jid?.normalizeJid())?.admin !== 'string' 124 | ) 125 | CONFIG = [eventType.admin!, 'admin']; 126 | if ( 127 | eventType.group && 128 | typeof mess.groupMetadata?.participants.find((v) => v.id.normalizeJid() === mess.client.jid.normalizeJid())?.admin !== 'string' 129 | ) 130 | CONFIG = [eventType.admin!, 'clientAdminRequired']; 131 | 132 | if ( 133 | eventType.mediaCustomReply && 134 | !(eventType.mediaCustomReply instanceof Array 135 | ? eventType.mediaCustomReply.some((v) => v in client.messageType && client.messageType[v] === (mess.quoted?.type[1] ?? mess.type[1])) 136 | : eventType.mediaCustomReply in client.messageType && 137 | client.messageType[eventType.mediaCustomReply] === (mess.quoted?.type[1] ?? mess.type[1])) 138 | ) 139 | CONFIG = [eventType.mediaCustomReply, 'mediaCustomReply']; 140 | if ( 141 | eventType.quotedCustom && 142 | !(eventType.quotedCustom instanceof Array 143 | ? eventType.quotedCustom.some((v) => v in client.messageType && client.messageType[v] === mess.quoted?.type[1]) 144 | : eventType.quotedCustom in client.messageType && client.messageType[eventType.quotedCustom] === mess.quoted?.type[1]) 145 | ) 146 | CONFIG = [eventType.quotedCustom, 'quotedCustom']; 147 | 148 | if (typeof CONFIG === 'object' && CONFIG.length > 0) { 149 | return this.action(mess, CONFIG[0], CONFIG[1], { 150 | prefix: event.prefix, 151 | command: event.command, 152 | }); 153 | } 154 | 155 | if (eventType?.wait) this.action(mess, eventType.wait, 'wait'); 156 | return 200; 157 | }; 158 | 159 | private getCommand = (text: string) => { 160 | const eventParser = (event: ICommandHandler.Event): EventParser | undefined => { 161 | const prefix: RegExp | undefined = event.prefix 162 | ? this.prefix.filter((v) => v.test(text)).sort((a, b) => b.toString().length - a.toString().length)[0] 163 | : /^()/i, 164 | index = event.index!, 165 | commandWithQuery = text.replace(prefix, ''), 166 | command = event.command.regExp.find((v) => v.test(commandWithQuery))?.exec(commandWithQuery); 167 | if (!prefix || !command) return undefined; 168 | return { prefix, index, commandWithQuery, command: (command as RegExpExecArray)[0] }; 169 | }; 170 | 171 | const parser: EventParser[] = []; 172 | let instance: ICommandHandler.Event | ICommandHandler.Event[] = this.commandList.filter((v) => { 173 | const validEvent = eventParser(v); 174 | if (validEvent) parser.push(validEvent); 175 | return v.enable && validEvent; 176 | }); 177 | const parsedEvent = parser.find( 178 | (v) => 179 | v.command === 180 | utilities.closest( 181 | text, 182 | parser.map((v) => v.command), 183 | ), 184 | ); 185 | if (!parsedEvent || !instance) return undefined; 186 | instance = instance.find((v) => v.index === parsedEvent.index)!; 187 | return { 188 | instance, 189 | text, 190 | command: parsedEvent.command, 191 | commandWithQuery: parsedEvent.commandWithQuery, 192 | query: parsedEvent.commandWithQuery.replace(parsedEvent.command, '').trim(), 193 | prefix: parsedEvent.prefix.exec(text)![0], 194 | modify: (property: ICommandHandler.Event) => { 195 | _.assign(this.commandList[(instance as typeof property).index!], property); 196 | return this.commandList[(instance as typeof property).index!]; 197 | }, 198 | }; 199 | }; 200 | } 201 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | cilok-v2-md
Options
All
  • Public
  • Public/Protected
  • All
Menu

cilok-v2-md

2 | 3 |

Cilok-Bot - MD

4 |
5 | 6 | 7 |

Documentation

8 |
9 |

https://vexg.github.io/cilok-v3/

10 | 11 | 12 |

Installation

13 |
14 | 15 | 16 |

Note

17 |
18 |
    19 |
  • This source code using pnpm as primary dependencies manager. Learn more
  • 20 |
  • You can skip this section if you using heroku.
  • 21 |
22 | 23 | 24 |

Requirements (Skip this section if you are using linux)

25 |
26 | 31 | 32 | 33 |

For Linux

34 |
35 |
bash ./src/install.sh
36 | 
37 | 38 | 39 |

For Windows

40 |
41 |
start ./src/install.bat
42 | 
43 | 44 | 45 |

Heroku

46 |
47 | 48 | 49 |

Buildpacks

50 |
51 | 56 | 57 | 58 |

Starting the BOT

59 |
60 | 61 | 62 |

Config

63 |
64 |
    65 |
  • From "-.env" rename to ".env" in src and fill the value in that file.
  • 66 |
  • Fill your client number (Optional) & your other / primary number in config.json to validate that you're the owner of the bot & you can change the response message, unicode, etc if you want to.
  • 67 |
68 | 69 | 70 |

Run the BOT

71 |
72 |
    73 |
  • run this command in your terminal
      74 |
    • production
      pnpm build && pnpm start
      75 | 
      76 |
    • 77 |
    • development
      pnpm dev
      78 | 
      79 |
    • 80 |
    81 |
  • 82 |
  • scan the QR to make a session. session, chats will be stored in src/database
  • 83 |
  • there will be some logs spamming because the logger level is set to info, you can change the value to silent in here to hide the spamming logs. Learn more
  • 84 |
85 |

Legend

  • Namespace
  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Interface
  • Class

Settings

Theme

Generated using TypeDoc

-------------------------------------------------------------------------------- /lib/utilities/utilities.ts: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import dayjs from 'dayjs'; 3 | import chalk from 'chalk'; 4 | import config from '../../src/config.json'; 5 | import FormData from 'form-data'; 6 | import { format } from 'util'; 7 | import { AxiosRequestConfig, AxiosRequestHeaders } from 'axios'; 8 | 9 | export async function delay(ms: number): Promise { 10 | return new Promise((resolve) => setTimeout(resolve, ms)); 11 | } 12 | 13 | export function wings(text: string): string { 14 | return `${config.unicode.wings[0]}*${text.trim()}*${config.unicode.wings[1]}`; 15 | } 16 | 17 | export function url(text: string) { 18 | const regex = 19 | /^(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})/i; 20 | return { 21 | isValid: regex.test(text), 22 | parse: text.match(regex)?.pop(), 23 | }; 24 | } 25 | 26 | export function parseRegex(text: string): string { 27 | return text.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&'); 28 | } 29 | 30 | export function isRegex(text: string) { 31 | return toString.call(text) === '[object RegExp]'; 32 | } 33 | 34 | export function randomNumber(num1: number, num2?: number): number { 35 | return !num2 ? Math.floor(Math.random() * num1) : Math.floor(Math.random() * (num2 - num1)) + num1; 36 | } 37 | 38 | export function randomize(data: T[], propertyPath?: string): T { 39 | let randomData = data.filter((v) => (propertyPath ? _.result(v, propertyPath) : v))[randomNumber(data.length)]; 40 | if (propertyPath) randomData = _.result(randomData, propertyPath); 41 | return randomData; 42 | } 43 | 44 | export function autoPath(format?: string, filename?: string, useTemp = true): string { 45 | const basePath = useTemp ? config.path.temp : ''; 46 | const baseName = 'clk' + (Math.random() + '').slice(10, 14); 47 | return `${basePath}${ 48 | filename 49 | ? filename.includes('?>') 50 | ? baseName + filename.split('?>')[1] 51 | : filename.includes('?<') 52 | ? filename.split('?<')[1] + Date.now() 53 | : filename 54 | : baseName 55 | }${format ? (!format.includes('.') ? `.${format}` : format) : ''}`; 56 | } 57 | 58 | export function headers(additional?: AxiosRequestConfig, additionalHeaders?: AxiosRequestHeaders): AxiosRequestConfig { 59 | return { 60 | headers: { 61 | 'user-agent': 62 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36 Edg/99.0.1150.30', 63 | 'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="99", "Microsoft Edge";v="99"', 64 | dnt: '1', 65 | ...additionalHeaders, 66 | }, 67 | ...additional, 68 | }; 69 | } 70 | 71 | export const append = { 72 | params: (data: object): URLSearchParams => new URLSearchParams(Object.entries(data)), 73 | form: (data: object): FormData => { 74 | const form = new FormData(); 75 | for (const a of Object.entries(data)) { 76 | form.append(a[0], a[1]); 77 | } 78 | return form; 79 | }, 80 | }; 81 | 82 | export function logger( 83 | tag: 'info' | 'warn' | 'error' | 'database' | 'measure' | { name: string; color: chalk.Chalk; loggerType?: 'info' | 'error' | 'warn' | 'debug' }, 84 | message: any, 85 | ...args: any[] 86 | ) { 87 | message = format(message, ...args); 88 | const defaultTag: Record = { 89 | info: [chalk.blueBright, 'info'], 90 | warn: [chalk.yellowBright, 'warn'], 91 | error: [chalk.redBright, 'error'], 92 | database: [chalk.greenBright, 'log'], 93 | measure: [chalk.magentaBright, 'log'], 94 | }; 95 | const date = chalk.bgBlackBright(` ${dayjs().format().toString()} `); 96 | if (typeof tag === 'object') { 97 | console[tag.loggerType ?? 'log'](date, tag.color.bold(`[ ${tag.name.toUpperCase()} ]`), message); 98 | } else { 99 | (console as Record)[defaultTag[tag][1]](date, defaultTag[tag][0].bold(`[ ${tag.toUpperCase()} ]`), message); 100 | } 101 | } 102 | 103 | export function parseJSON( 104 | json: object, 105 | options?: { 106 | ignoreValue?: any[]; 107 | ignoreKey?: string[]; 108 | header?: string; 109 | body?: string; 110 | footer?: string; 111 | preResult?: boolean; 112 | }, 113 | ): string | string[][] { 114 | if (Object.entries(json).length === 0) throw new Error('No json input provided'); 115 | const opt = { 116 | ignoreValue: [null, undefined], 117 | ignoreKey: [], 118 | header: '', 119 | body: `${config.unicode.bullet} *%key :* %value`, 120 | footer: config.unicode.dash.repeat(30), 121 | preResult: false, 122 | ...options, 123 | }; 124 | const content: string[][] = []; 125 | for (const [a, b] of Object.entries(json)) { 126 | if (opt.ignoreValue.indexOf(b) !== -1) continue; 127 | const key = a.replace(/[A-Z_]/g, (a) => a.replace(a, ` ${a !== '_' ? a.toLowerCase() : ''}`)).replace(/^\w/, (c) => c.toUpperCase()); 128 | const type = typeof b; 129 | if (opt.ignoreKey && (opt.ignoreKey as string[]).includes(a)) continue; 130 | switch (type) { 131 | case 'boolean': 132 | content.push([key, b ? 'Ya' : 'Tidak']); 133 | break; 134 | case 'object': 135 | if (Array.isArray(b)) { 136 | content.push([key, b.join(', ')]); 137 | } else { 138 | content.push([ 139 | key, 140 | parseJSON(b, { 141 | ignoreKey: opt.ignoreKey, 142 | preResult: true, 143 | }) as string, 144 | ]); 145 | } 146 | break; 147 | default: 148 | content.push([key, b]); 149 | break; 150 | } 151 | } 152 | if (opt.preResult) return content; 153 | const compile: string[] = [ 154 | opt.header === '' ? '' + '\n' : `${config.unicode.wings[0]}*${opt.header}*${config.unicode.wings[1]}\n`, 155 | content 156 | .map((a) => { 157 | return opt.body 158 | .replace(/%key/g, a[0]) 159 | .replace(/%value/g, a[1]) 160 | .trim(); 161 | }) 162 | .join('\n'), 163 | Array.isArray(json) ? `\n\n${opt.footer}\n` : '', 164 | ]; 165 | return compile.join(''); 166 | } 167 | 168 | export function searchJSON(obj: any, key: string | Array, output?: any): T | undefined { 169 | if (typeof obj === 'object') { 170 | if (Array.isArray(key)) { 171 | if (!output) output = {}; 172 | key.forEach((subKey) => { 173 | if (obj.hasOwnProperty(subKey)) { 174 | output[subKey] = obj[subKey] as T; 175 | } 176 | for (const k in obj) { 177 | if (obj.hasOwnProperty(k)) { 178 | let result = searchJSON( 179 | obj[k] as { 180 | [key: string]: unknown; 181 | }, 182 | subKey, 183 | output, 184 | ); 185 | if (result) { 186 | output[subKey] = result; 187 | } 188 | } 189 | } 190 | }); 191 | return output; 192 | } else { 193 | if (obj.hasOwnProperty(key)) { 194 | return obj[key] as T; 195 | } 196 | for (const k in obj) { 197 | if (obj.hasOwnProperty(k)) { 198 | let result = searchJSON(obj[k] as { [key: string]: unknown }, key); 199 | if (result) { 200 | return result as T; 201 | } 202 | } 203 | } 204 | } 205 | } 206 | } 207 | 208 | String.prototype.normalizeJid = function (): string { 209 | return this.replace(/\:[0-9]{1,2}/, ''); 210 | }; 211 | 212 | String.prototype.compress = function () { 213 | const str = decodeURIComponent(encodeURIComponent(String(this))); 214 | let result = '', 215 | char: number, 216 | nextChar: number, 217 | combinedCharCode: string; 218 | for (let i = 0; i < str.length; i += 2) { 219 | char = str.charCodeAt(i); 220 | if (i + 1 < str.length) { 221 | // You need to make sure that you don't have 3 digits second character else you might go over 65536. 222 | // But in UTF-16 the 32 characters aren't in your basic character set. But it's a limitation, anything 223 | // under charCode 32 will cause an error 224 | nextChar = str.charCodeAt(i + 1) - 31; 225 | // this is to pad the result, because you could have a code that is single digit, which would make 226 | // decompression a bit harder 227 | combinedCharCode = 228 | char + 229 | '' + 230 | nextChar.toLocaleString('en', { 231 | minimumIntegerDigits: 2, 232 | }); 233 | // You take the concanated code string and convert it back to a number, then a character 234 | result += String.fromCharCode(parseInt(combinedCharCode, 10)); 235 | } else result += str.charAt(i); // Here because you won't always have pair number length 236 | } 237 | return result; 238 | }; 239 | 240 | String.prototype.decompress = function () { 241 | const str = String(this); 242 | let result = '', 243 | char, 244 | codeStr, 245 | firstCharCode, 246 | lastCharCode; 247 | for (let i = 0; i < str.length; i++) { 248 | char = str.charCodeAt(i); 249 | if (char > 132) { 250 | codeStr = char.toString(10); 251 | // You take the first part of the compressed char code, it's your first letter 252 | firstCharCode = parseInt(codeStr.substring(0, codeStr.length - 2), 10); 253 | // For the second one you need to add 31 back. 254 | lastCharCode = parseInt(codeStr.substring(codeStr.length - 2, codeStr.length), 10) + 31; 255 | // You put back the 2 characters you had originally 256 | result += String.fromCharCode(firstCharCode) + String.fromCharCode(lastCharCode); 257 | } else result += str.charAt(i); 258 | } 259 | return result; 260 | }; 261 | -------------------------------------------------------------------------------- /docs/interfaces/typings.EventParser.html: -------------------------------------------------------------------------------- 1 | EventParser | cilok-v2-md
Options
All
  • Public
  • Public/Protected
  • All
Menu

Interface EventParser

Hierarchy

  • EventParser

Index

Properties

command: string
commandWithQuery: string
index: number
prefix: RegExp

Legend

  • Namespace
  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Interface
  • Property
  • Class

Settings

Theme

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/interfaces/utilities.chalk.ChalkFunction.html: -------------------------------------------------------------------------------- 1 | ChalkFunction | cilok-v2-md
Options
All
  • Public
  • Public/Protected
  • All
Menu

Hierarchy

Callable

  • ChalkFunction(text: TemplateStringsArray, ...placeholders: unknown[]): string
  • ChalkFunction(...text: unknown[]): string
  • 2 |

    Use a template string.

    3 |
    remarks

    Template literals are unsupported for nested calls (see issue #341)

    4 |
    example
    import chalk = require('chalk');

    log(chalk`
    CPU: {red ${cpu.totalPercent}%}
    RAM: {green ${ram.used / ram.total * 100}%}
    DISK: {rgb(255,131,0) ${disk.used / disk.total * 100}%}
    `); 5 |
    6 |
    example
    import chalk = require('chalk');

    log(chalk.red.bgBlack`2 + 3 = {bold ${2 + 3}}`) 7 |
    8 |

    Parameters

    • text: TemplateStringsArray
    • Rest ...placeholders: unknown[]

    Returns string

  • Parameters

    • Rest ...text: unknown[]

    Returns string

Legend

  • Namespace
  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Interface
  • Class

Settings

Theme

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/modules/utilities.chalk.html: -------------------------------------------------------------------------------- 1 | chalk | cilok-v2-md
Options
All
  • Public
  • Public/Protected
  • All
Menu

Index

Type aliases

Instance: new (options?: Options) => Chalk

Type declaration

Level: 0 | 1 | 2 | 3
4 |

Levels:

5 |
    6 |
  • 0 - All colors disabled.
  • 7 |
  • 1 - Basic 16 colors support.
  • 8 |
  • 2 - ANSI 256 colors support.
  • 9 |
  • 3 - Truecolor 16 million colors support.
  • 10 |
11 |

Legend

  • Namespace
  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Interface
  • Class

Settings

Theme

Generated using TypeDoc

-------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "ESNEXT" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, 15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ 22 | // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | 26 | /* Modules */ 27 | "module": "commonjs" /* Specify what module code is generated. */, 28 | // "rootDir": "./", /* Specify the root folder within your source files. */ 29 | //"moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */, 30 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 31 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 32 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 33 | // "typeRoots": [] /* Specify multiple folders that act like `./node_modules/@types`. */, 34 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 35 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 36 | "resolveJsonModule": true /* Enable importing .json files */, 37 | // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ 38 | 39 | /* JavaScript Support */ 40 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ 41 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 42 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ 43 | 44 | /* Emit */ 45 | "declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */, 46 | // "declarationMap": true /* Create sourcemaps for d.ts files. */, 47 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 48 | "sourceMap": false /* Create source map files for emitted JavaScript files. */, 49 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ 50 | // "outDir": "./dist" /* Specify an output folder for all emitted files. */, 51 | "removeComments": true /* Disable emitting comments. */, 52 | // "noEmit": true, /* Disable emitting files from a compilation. */ 53 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 54 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ 55 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 56 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 57 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 58 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 59 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 60 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 61 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 62 | // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ 63 | // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ 64 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 65 | // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ 66 | // "declarationDir": "" /* Specify the output directory for generated declaration files. */, 67 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 68 | 69 | /* Interop Constraints */ 70 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 71 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 72 | "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */, 73 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 74 | "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, 75 | 76 | /* Type Checking */ 77 | "strict": true /* Enable all strict type-checking options. */, 78 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ 79 | // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ 80 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 81 | // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ 82 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 83 | // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ 84 | // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ 85 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 86 | // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ 87 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ 88 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 89 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 90 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 91 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 92 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 93 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ 94 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 95 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 96 | 97 | /* Completeness */ 98 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 99 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 100 | }, 101 | "typedocOptions": { 102 | "entryPoints": [ 103 | "./lib/index.ts", 104 | "./lib/database.ts", 105 | "./lib/client.ts", 106 | "./lib/command.ts", 107 | "./lib/event.ts", 108 | "./lib/metadata.ts", 109 | "./lib/utilities/index.ts", 110 | "./lib/typings/index.ts" 111 | ], 112 | "out": "docs" 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /docs/interfaces/utilities.chalk.ColorSupport.html: -------------------------------------------------------------------------------- 1 | ColorSupport | cilok-v2-md
Options
All
  • Public
  • Public/Protected
  • All
Menu
2 |

Detect whether the terminal supports color.

3 |

Hierarchy

  • ColorSupport

Index

Properties

has16m: boolean
4 |

Return whether Chalk supports Truecolor 16 million colors.

5 |
has256: boolean
6 |

Return whether Chalk supports ANSI 256 colors.

7 |
hasBasic: boolean
8 |

Return whether Chalk supports basic 16 colors.

9 |
level: Level
10 |

The color level used by Chalk.

11 |

Legend

  • Namespace
  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Interface
  • Class

Settings

Theme

Generated using TypeDoc

-------------------------------------------------------------------------------- /lib/client.ts: -------------------------------------------------------------------------------- 1 | import P from 'pino'; 2 | import path from 'path'; 3 | import core from 'file-type/core'; 4 | import axios from 'axios'; 5 | import Ffmpeg from 'fluent-ffmpeg'; 6 | import { exec } from 'child_process'; 7 | import { Agent } from 'https'; 8 | import { Readable } from 'form-data'; 9 | import { fromBuffer } from 'file-type'; 10 | import { eventHandler, scheduleHandler } from './event'; 11 | import { ButtonConfig, Content, GetBuffer, Metadata } from './typings'; 12 | import * as fs from 'fs'; 13 | import makeWASocket, * as baileys from '@adiwajshing/baileys'; 14 | import axiosRetry from 'axios-retry'; 15 | 16 | export default class Client { 17 | private static instance: Client; 18 | private static socket: baileys.WASocket; 19 | public readonly auth: ReturnType; 20 | public readonly messageType: Record; 21 | private constructor() { 22 | this.auth = baileys.useSingleFileAuthState(path.join(config.path.database, 'session.json')); 23 | this.messageType = Object.fromEntries( 24 | Object.keys(baileys.proto) 25 | .filter((a) => a.endsWith('Message') || a.includes('Conversation')) 26 | .map((a) => { 27 | const type = a[0].toLowerCase() + a.slice(1); 28 | return [type.replace('conversation', 'text').replace('Message', ''), type]; 29 | }), 30 | ); 31 | } 32 | 33 | public static async connect() { 34 | try { 35 | if (!Client.instance) Client.instance = new Client(); 36 | utilities.logger('info', 'Connecting to whatsapp...'); 37 | Client.socket = makeWASocket({ 38 | auth: Client.instance.auth.state, 39 | printQRInTerminal: true, 40 | version: (await baileys.fetchLatestBaileysVersion()).version, 41 | browser: ['cilok-bot', 'Desktop', '3.1.0'], 42 | logger: P({ 43 | level: 'info', //process.env.NODE_ENV === 'dev' ? 'info' : 'silent', 44 | }), 45 | }); 46 | Promise.all([eventHandler(Client.instance), scheduleHandler()]); 47 | return Client.instance; 48 | } catch (err) { 49 | throw err; 50 | } 51 | } 52 | 53 | public get baileys() { 54 | return baileys; 55 | } 56 | public get socket(): baileys.WASocket { 57 | return Client.socket; 58 | } 59 | 60 | public async sendMessageFromContent(mess: Metadata, content: baileys.WAProto.IMessage & baileys.MessageGenerationOptionsFromContent) { 61 | try { 62 | const generatedContent = this.baileys.generateWAMessageFromContent(typeof mess === 'object' ? mess.from! : mess, content, content); 63 | await this.socket.relayMessage(generatedContent.key.remoteJid!, generatedContent.message!, { 64 | messageId: generatedContent.key.id!, 65 | }); 66 | return generatedContent; 67 | } catch (err) { 68 | throw err; 69 | } 70 | } 71 | 72 | public async sendMessage(mess: Metadata | string, content: Content): Promise { 73 | try { 74 | let property: Record = content; 75 | 76 | const mediaType = Object.keys(property).find((v) => ['document', 'video', 'audio', 'image', 'sticker'].includes(v)); 77 | if (!(typeof property[mediaType as keyof baileys.AnyMessageContent] === 'object') && mediaType) { 78 | const bufferData = await this.getBuffer(property[mediaType as keyof baileys.AnyMessageContent]); 79 | 80 | if (mediaType === 'image') { 81 | (property.caption as string) = property?.text ? property.text : property?.caption ? property.caption : ''; 82 | delete property?.text; 83 | } 84 | 85 | property = { 86 | mimetype: (property?.mimetype ? property.mimetype : mediaType === 'audio' ? 'audio/mpeg' : bufferData.mime) as string, 87 | fileName: (!property?.filename 88 | ? `${Date.now()}.${bufferData.ext}` 89 | : property?.filename.includes('.') 90 | ? property.filename 91 | : `${property.filename}.${bufferData.ext}`) as string, 92 | ...property, 93 | [mediaType]: bufferData.buffer, 94 | }; 95 | } 96 | 97 | return this.socket.sendMessage( 98 | typeof mess === 'object' ? mess.from! : mess, 99 | property as baileys.AnyMessageContent, 100 | property as baileys.MiscMessageGenerationOptions, 101 | ); 102 | } catch (err) { 103 | throw err; 104 | } 105 | } 106 | 107 | public async sendButton(mess: Metadata, content: Content, buttons: ButtonConfig[]): Promise { 108 | try { 109 | function parseButton(type: string, object: ButtonConfig) { 110 | return 'title' in object 111 | ? ({ 112 | ...object, 113 | title: object.listTitle ?? undefined, 114 | rowId: object.value ?? undefined, 115 | } as { 116 | title?: string | null; 117 | description?: string | null; 118 | rowId?: string | null; 119 | }) 120 | : ({ 121 | [type.includes('reply') ? 'quickReplyButton' : type + 'Button']: { 122 | displayText: object[type as keyof ButtonConfig], 123 | [type.includes('reply') ? 'id' : type.includes('call') ? 'phoneNumber' : type]: object.value ?? '', 124 | }, 125 | } as baileys.proto.IHydratedTemplateButton); 126 | } 127 | 128 | let hasList = false; 129 | let buttonData: baileys.proto.IHydratedTemplateButton[] | baileys.proto.ISection[] = []; 130 | 131 | for (const bc of buttons) { 132 | const type = Object.keys(bc) 133 | .find((v) => v !== 'value') 134 | ?.toLowerCase(); 135 | const parse = type ? parseButton(type, bc) : undefined; 136 | 137 | if ('title' in bc) { 138 | hasList = true; 139 | const rows: baileys.proto.IRow[] = []; 140 | rows.push(parse as baileys.proto.IRow); 141 | buttonData.push({ 142 | rows, 143 | title: bc.title, 144 | }); 145 | } else buttonData = (buttonData as baileys.proto.IHydratedTemplateButton[]).concat(parse as baileys.proto.IHydratedTemplateButton[]); 146 | } 147 | 148 | return this.sendMessage(mess, { 149 | ...content, 150 | ...{ [hasList ? 'sections' : 'templateButtons']: buttonData }, 151 | }); 152 | } catch (err) { 153 | throw err; 154 | } 155 | } 156 | 157 | public reply = async (mess: Metadata, text: string): Promise => 158 | this.sendMessage(mess, { text: utilities.format(text).trim(), quoted: mess }); 159 | 160 | public throw = async (mess: Metadata, error: any, command: string, additonal?: object): Promise => { 161 | await this.sendMessage(`${config.ownerNumber[0]}@s.whatsapp.net`, { 162 | text: `${utilities.wings('ERROR')}\n${utilities.parseJSON({ from: mess.from, command, ...additonal })}\n\n${utilities.format(error)}`, 163 | }); 164 | return this.reply( 165 | mess, 166 | config.response.error + 167 | (statusMessage 168 | ? `\n\nTerjadi masalah pada Cilok-Bot yang sedang berlangsung. silahkan ketik ${mess.string[0]}menu untuk melihat masalah yang terjadi.` 169 | : ''), 170 | ); 171 | }; 172 | 173 | public async downloadContent(mess: Metadata, filename?: string) { 174 | try { 175 | const values = Object.values(this.messageType); 176 | const type = Object.keys(mess).find((a) => values.includes(a) && !a.includes('senderKey') && !a.includes('context')); 177 | if (!type) throw new Error('Message type not found'); 178 | return this.getBuffer( 179 | await this.baileys.downloadContentFromMessage( 180 | mess[type as keyof Metadata] as baileys.DownloadableMessage, 181 | type.replace(/Message/i, '').trim() as baileys.MediaType, 182 | ), 183 | filename, 184 | ); 185 | } catch (err) { 186 | throw err; 187 | } 188 | } 189 | 190 | public getBuffer = async ( 191 | content: GetBuffer, 192 | filename?: string, 193 | autoFormat = true, 194 | ): Promise<{ 195 | filename?: string; 196 | buffer: Buffer; 197 | ext: core.FileExtension | 'bin'; 198 | mime: core.MimeType | 'application/octet-stream'; 199 | }> => { 200 | try { 201 | let buffer: Buffer; 202 | if (Buffer.isBuffer(content)) buffer = content; 203 | else if (/^data:.?\/.?;base64,/i.test(content as string)) buffer = Buffer.from((content as string).split(',')[1], 'base64'); 204 | else if (/^https?:\/\//.test(content as string)) { 205 | axiosRetry(axios, { 206 | retries: 3, 207 | retryDelay(retryCount) { 208 | return retryCount * 5000; 209 | }, 210 | onRetry(_, error, requestConfig) { 211 | if (error.message.includes('first certificate')) 212 | requestConfig.httpsAgent = new Agent({ 213 | rejectUnauthorized: false, 214 | }); 215 | }, 216 | }); 217 | buffer = ( 218 | await axios.get( 219 | content as string, 220 | utilities.headers({ 221 | responseType: 'arraybuffer', 222 | }), 223 | ) 224 | )?.data; 225 | } else if (fs.existsSync(content as string)) buffer = fs.readFileSync(content as string); 226 | else if ((content as unknown as { _readableState: any })?._readableState) buffer = await this.baileys.toBuffer(content as Readable); 227 | else if (typeof content === 'string') buffer = Buffer.from(content); 228 | else buffer = Buffer.alloc(0); 229 | const template = (await fromBuffer(buffer)) || { 230 | ext: 'bin', 231 | mime: 'application/octet-stream', 232 | }; 233 | 234 | if (filename) { 235 | filename = autoFormat ? `${filename}.${template.ext}` : filename; 236 | fs.writeFileSync(filename.includes(config.path.temp) ? filename : `${config.path.temp}${filename}`, buffer); 237 | return { 238 | filename, 239 | buffer, 240 | ...template, 241 | }; 242 | } 243 | return { 244 | buffer, 245 | ...template, 246 | }; 247 | } catch (err) { 248 | throw err; 249 | } 250 | }; 251 | 252 | public prepareSticker = async (content: GetBuffer, exifPath?: string): Promise => { 253 | try { 254 | const bufferData = await this.getBuffer(content, utilities.autoPath(undefined, undefined, false)), 255 | input = utilities.autoPath(undefined, bufferData.filename!), 256 | output = utilities.autoPath('webp', bufferData.filename?.split('.')[0]!); 257 | if (!fs.existsSync(config.path.temp)) fs.mkdirSync(config.path.temp.split('/')[0]); 258 | if (bufferData.ext === 'webp') 259 | if (exifPath) { 260 | return new Promise((resolve) => 261 | exec(`webpmux -set exif ${exifPath} ${input} -o ${input}`, (e) => { 262 | if (e) throw e; 263 | const saver = fs.readFileSync(input); 264 | if (fs.existsSync(input)) fs.unlinkSync(input); 265 | return resolve(saver); 266 | }), 267 | ); 268 | } else { 269 | const saver = fs.readFileSync(input); 270 | if (fs.existsSync(input)) fs.unlinkSync(input); 271 | return saver; 272 | } 273 | 274 | return new Promise((resolve) => 275 | Ffmpeg(input) 276 | .on('error', (e) => { 277 | if (fs.existsSync(input)) fs.unlinkSync(input); 278 | throw e; 279 | }) 280 | .videoCodec('libwebp') 281 | .videoFilter( 282 | "scale='min(320,iw)':min'(320,ih)':force_original_aspect_ratio=decrease,fps=15, pad=320:320:-1:-1:color=white@0.0, split [a][b]; [a] palettegen=reserve_transparent=on:transparency_color=ffffff [p]; [b][p] paletteuse", 283 | ) 284 | .toFormat('webp') 285 | .save(output) 286 | .on('end', () => { 287 | if (fs.existsSync(input)) fs.unlinkSync(input); 288 | if (exifPath) { 289 | return exec(`webpmux -set exif ${exifPath} ${output} -o ${output}`, (e) => { 290 | if (e) throw e; 291 | const saver = fs.readFileSync(output); 292 | if (fs.existsSync(output)) fs.unlinkSync(output); 293 | return resolve(saver); 294 | }); 295 | } else { 296 | const saver = fs.readFileSync(output); 297 | if (fs.existsSync(output)) fs.unlinkSync(output); 298 | return resolve(saver); 299 | } 300 | }), 301 | ); 302 | } catch (err) { 303 | throw err; 304 | } 305 | }; 306 | } 307 | -------------------------------------------------------------------------------- /docs/modules/utilities.inspect.html: -------------------------------------------------------------------------------- 1 | inspect | cilok-v2-md
Options
All
  • Public
  • Public/Protected
  • All
Menu

Namespace inspect

Index

Variables

colors: NodeJS.Dict<[number, number]>
custom: unique symbol
2 |

That can be used to declare custom inspect functions.

3 |
defaultOptions: InspectOptions
replDefaults: InspectOptions
4 |

Allows changing inspect settings from the repl.

5 |
styles: { [ K in Style]: string }

Legend

  • Namespace
  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Interface
  • Class

Settings

Theme

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/modules/typings.html: -------------------------------------------------------------------------------- 1 | typings | cilok-v2-md
Options
All
  • Public
  • Public/Protected
  • All
Menu

Module typings

Index

Type aliases

ButtonConfig: { value: string } & ({ reply: string } | { url: string } | { call: string } | { description?: string; listTitle?: string; title?: string })
Content: (AnyMessageContent & MiscMessageGenerationOptions) | { image: string } | { filename: string } | { video: string } | { image: string } | { audio: string }
GetBuffer: string | number | Readable | readonly number[] | { valueOf: any } | URL
ProcessMode: "dev" | "production"

Legend

  • Namespace
  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Interface
  • Class

Settings

Theme

Generated using TypeDoc

--------------------------------------------------------------------------------