ckptw
Create powerful WhatsApp bots easily.
- ✨ Effortless
- 🧱 Builder
- 🛒 Built in Collector
- ⏰ Built in Cooldown
- 🔑 Built in Command handler
- 🎉 And more!
Table Of Contents
Installation
npm install @mengkodingan/ckptw
yarn add @mengkodingan/ckptw
pnpm add @mengkodingan/ckptw
Example
import { Client, Events, MessageType } from "@mengkodingan/ckptw";
const bot = new Client({
prefix: "!",
printQRInTerminal: true,
readIncommingMsg: true
});
bot.ev.once(Events.ClientReady, (m) => {
console.log(`ready at ${m.user.id}`);
});
bot.command('ping', async(ctx) => ctx.reply({ text: 'pong!' }));
bot.command('hi', async(ctx) => ctx.reply('hello! you can use string as a first parameter in reply function too!'));
bot.hears('test', async(ctx) => ctx.reply('test 1 2 3 beep boop...'));
bot.hears(MessageType.stickerMessage, async(ctx) => ctx.reply('wow, cool sticker'));
bot.hears(['help', 'menu'], async(ctx) => ctx.reply('hears can be use with array too!'));
bot.hears(/(using\s?)?regex/, async(ctx) => ctx.reply('or using regex!'));
bot.launch();
Or using the Events
import { Client, Events } from "@mengkodingan/ckptw";
const bot = new Client({
prefix: "!",
printQRInTerminal: true,
readIncommingMsg: true
});
bot.ev.once(Events.ClientReady, (m) => {
console.log(`ready at ${m.user.id}`);
});
bot.ev.on(Events.MessagesUpsert, (m, ctx) => {
if(m.key.fromMe) return;
if(m.content === "hello") {
ctx.reply("hi 👋");
}
})
bot.launch();
Client Configuration
export interface ClientOptions {
prefix: Array<string> | string | RegExp;
readIncommingMsg?: boolean;
authDir?: string;
printQRInTerminal?: boolean;
qrTimeout?: number;
markOnlineOnConnect?: boolean;
phoneNumber?: string;
usePairingCode?: boolean;
selfReply?: boolean;
}
Command Options
bot.command(opts: CommandOptions | string, code?: (ctx: Ctx) => Promise<any>)
bot.command('ping', async(ctx) => ctx.reply('pong!'))
export interface CommandOptions {
name: string;
aliases?: Array<string>;
code: (ctx: Ctx) => Promise<any>;
}
bot.command({
name: 'ping',
code: async(ctx) => ctx.reply('pong!');
})
Command Handler
With command handler you dont need all your command is located in one file.
-
in your main file
import { CommandHandler } from "@mengkodingan/ckptw";
import path from "path";
const cmd = new CommandHandler(bot, path.resolve() + '/CommandsPath');
cmd.load();
-
in your command file
module.exports = {
name: "ping",
code: async (ctx) => {
ctx.reply("pong!");
},
};
You can add a type
property to define the handler type... For now there are only command
and hears
types.
module.exports = {
name: "hears with command handler",
type: "hears",
code: async (ctx) => {
ctx.reply("hello world!");
},
};
Command Cooldown
Cooldown can give a delay on the command. This can be done to prevent users from spamming your bot commands.
import { Cooldown } from "@mengkodingan/ckptw";
bot.command('ping', async(ctx) => {
const cd = new Cooldown(ctx, 8000);
if(cd.onCooldown) return ctx.reply(`slow down... wait ${cd.timeleft}ms`);
ctx.reply('pong!')
})
if you want to trigger some function when the cooldown end, you can use the "end" events in the cooldown:
⚠
Will always be triggered when the cooldown is over (even though the users only runs the command once)
cd.on("end", () => {
ctx.reply({ text: "cd timeout" });
})
Cooldown getter:
cd.onCooldown;
cd.timeleft;
Builder
-
Button
Make a button message with Button Builder.
export type ButtonType = 'cta_url' | 'cta_call' | 'cta_copy' | 'cta_reminder' | 'cta_cancel_reminder' | 'address_message' | 'send_location' | 'quick_reply';
import { ButtonBuilder } from "@mengkodingan/ckptw";
let button = new ButtonBuilder()
.setId('!ping')
.setDisplayText('command Ping')
.setType('quick_reply')
.build();
let button2 = new ButtonBuilder()
.setId('id2')
.setDisplayText('copy code')
.setType('cta_copy')
.setCopyCode('hello world')
.build();
let button3 = new ButtonBuilder()
.setId('id3')
.setDisplayText('@mengkodingan/ckptw')
.setType('cta_url')
.setURL('https://github.com/mengkodingan/ckptw')
.setMerchantURL('https://github.com/mengkodingan')
.build();
ctx.replyInteractiveMessage({
body: 'this is body',
footer: 'this is footer',
nativeFlowMessage: { buttons: [button, button2, button3] }
})
-
Sections
Sections message is like a list.
import { SectionsBuilder } from "@mengkodingan/ckptw";
let section1 = new SectionsBuilder()
.setDisplayText("Click me")
.addSection({
title: 'Title 1',
rows: [
{ header: "Row Header 1", title: "Row Title 1", description: "Row Description 1", id: "Row Id 1" },
{ header: "Row Header 2", title: "Row Title 2", description: "Row Description 2", id: "Row Id 2" }
]
})
.addSection({
title: 'This is title 2',
rows: [
{ title: "Ping", id: "!ping" },
{ title: "Hello world", id: "hello world" },
]
})
.build();
ctx.sendInteractiveMessage(ctx.id!, {
body: 'this is body',
footer: 'this is footer',
nativeFlowMessage: { buttons: [section1] }
})
-
Carousel
A carousel message is a type of message that slides like a carousel.
import { ButtonBuilder, CarouselBuilder } from "@mengkodingan/ckptw";
let button = new ButtonBuilder()
.setId('!ping')
.setDisplayText('command Ping')
.setType('quick_reply')
.build();
let exampleMediaAttachment = await ctx.prepareWAMessageMedia({ image: { url: "https://github.com/mengkodingan.png" } }, { upload: ctx._client.waUploadToServer })
let cards = new CarouselBuilder()
.addCard({
body: "BODY 1",
footer: "FOOTER 1",
header: {
title: "HEADER TITLE 1",
hasMediaAttachment: true,
...exampleMediaAttachment
},
nativeFlowMessage: { buttons: [button] }
})
.addCard({
body: "BODY 2",
footer: "FOOTER 2",
header: {
title: "HEADER TITLE 2",
hasMediaAttachment: true,
...exampleMediaAttachment
},
nativeFlowMessage: { buttons: [button] }
})
.build();
ctx.replyInteractiveMessage({
body: "this is body",
footer: "this is footer",
carouselMessage: {
cards,
},
});
-
Contact
Send a contact.
import { VCardBuilder } from "@mengkodingan/ckptw";
const vcard = new VCardBuilder()
.setFullName("John Doe")
.setOrg("PT Mencari Cinta Sejati")
.setNumber("621234567890")
.build();
ctx.reply({ contacts: { displayName: "John D", contacts: [{ vcard }] }});
-
Template Buttons (⚠ DEPRCATED! Use button builder instead.)
Send a button with "attachment".
import { TemplateButtonsBuilder } from "@mengkodingan/ckptw";
const templateButtons = new TemplateButtonsBuilder()
.addURL({ displayText: 'ckptw at Github', url: 'https://github.com/mengkodingan/ckptw' })
.addCall({ displayText: 'call me', phoneNumber: '+1234567890' })
.addQuickReply({ displayText: 'just a normal button', id: 'btn1' })
.build();
ctx.sendMessage(ctx.id, { text: "template buttons", templateButtons });
Collector
There are several options that can be used in the collector:
export interface CollectorArgs {
time?: number;
max?: number;
endReason?: string[];
maxProcessed?: number;
filter?: () => boolean;
}
-
Message Collector
let col = ctx.MessageCollector({ time: 10000 });
ctx.reply({ text: "say something... Timeout: 10s" });
col.on("collect", (m) => {
console.log("COLLECTED", m);
ctx.sendMessage(ctx.id, {
text: `Collected: ${m.content}\nFrom: ${m.sender}`,
});
});
col.on("end", (collector, r) => {
console.log("ended", r);
ctx.sendMessage(ctx.id, { text: `Collector ended` });
});
-
Awaited Messages
ctx.awaitMessages({ time: 10000 }).then((m) => ctx.reply(`got ${m.length} array length`)).catch(() => ctx.reply('end'))
Downloading Media
the code below will save the received image to ./saved.jpeg
import { MessageType } from "@mengkodingan/ckptw";
import fs from "node:fs";
bot.ev.on(Events.MessagesUpsert, async(m, ctx) => {
if(ctx.getMessageType() === MessageType.imageMessage) {
const buffer = await ctx.getMediaMessage(ctx.msg, 'buffer')
fs.writeFileSync('./saved.jpeg', buffer);
}
})
Events
Firstly you must import the Events Constant like this:
import { Events } from "@mengkodingan/ckptw";
-
Available Events
- ClientReady - Emitted when the bot client is ready.
- MessagesUpsert - Received an messages.
- QR - The bot QR is ready to scan. Return the QR Codes.
- GroupsJoin - Emitted when bot joining groups.
- UserJoin - Emitted when someone joins a group where bots are also in that group.
- UserLeave - Same with UserJoin but this is when the user leaves the group.
- Poll - Emitted when someone create a poll message.
- PollVote - Emitted when someone votes for one/more options in a poll.
- Reactions - Emitted when someone reacts to a message.
- Call - Emitted when someone calling, call was accepted or rejected.
Sending Message
ctx.sendMessage(ctx.id, { text: "hello" });
ctx.reply("hello");
ctx.reply({ text: "hello" });
ctx.sendMessage(ctx.id, { image: { url: 'https://example.com/image.jpeg' }, caption: "image caption" });
ctx.reply({ image: { url: 'https://example.com/image.jpeg' }, caption: "image caption" });
ctx.reply({ audio: { url: './audio.mp3' }, mimetype: 'audio/mp4', ptt: false });
ctx.reply({ sticker: { url: './tmp/generatedsticker.webp' }});
import fs from "node:fs";
ctx.reply({ video: fs.readFileSync("./video.mp4"), caption: "video caption", gifPlayback: false });
Formatter
WhatsApp allows you to format text inside your messages. Like bolding your message, etc. This function formats strings into several markdown styles supported by WhatsApp.
⚠ Some new text formatting is only available on Web and Mac desktop.
You can see the Whatsapp FAQ about formatting messages here.
import { bold, inlineCode, italic, monospace, quote, strikethrough } from "@mengkodingan/ckptw";
const str = "Hello World";
const boldString = bold(str);
const italicString = italic(str);
const strikethroughString = strikethrough(str);
const quoteString = quote(str);
const inlineCodeString = inlineCode(str);
const monospaceString = monospace(str);
Editing Message
let res = await ctx.reply("old text");
ctx.editMessage(res.key, "new text");
Deleting Message
let res = await ctx.reply("testing");
ctx.deleteMessage(res.key);
Poll Message
singleSelect
means you can only select one of the multiple options in the poll. Default to be false
ctx.sendPoll(ctx.id, { name: "ini polling", values: ["abc", "def"], singleSelect: true })
Get Mentions
You can use the function from ctx
to get the jid array mentioned in the message. For example, a message containing hello @jstn @person
where @jstn
& @person
is a mention, then you can get an array containing the jid of the two mentioned users.
ctx.getMentioned()
Group Stuff
ctx.groups.create(subject: string, members: string[]);
ctx.groups.inviteCodeInfo(code: string);
ctx.groups.acceptInvite(code: string);
ctx.groups.acceptInviteV4(key: string | proto.IMessageKeinviteMessage: proto.Message.IGroupInviteMessage);
ctx.group(jid?: string);
ctx.group().members();
ctx.group().inviteCode();
ctx.group().revokeInviteCode();
ctx.group().joinApproval(mode: "on" | "off");
ctx.group().leave();
ctx.group().membersCanAddMemberMode(mode: "on" | "off");
ctx.group().metadata();
ctx.group().toggleEphemeral(expiration: number);
ctx.group().updateDescription(description: number);
ctx.group().updateSubject(subject: number);
ctx.group().membersUpdate(members: string[], action: ParticipantAction);
ctx.group().kick(members: string[]);
ctx.group().add(members: string[]);
ctx.group().promote(members: string[]);
ctx.group().demote(members: string[]);
ctx.group().pendingMembers();
ctx.group().pendingMembersUpdate(members: string[], action: 'reject' | 'approve');
ctx.group().approvePendingMembers(members: string[]);
ctx.group().rejectPendingMembers(members: string[]);
ctx.group().updateSetting(setting: 'announcement' | 'not_announcement' | 'locked' | 'unlocked')
ctx.group().open()
ctx.group().close()
ctx.group().lock()
ctx.group().unlock()
Misc
ctx.reply({ text: "test" });
ctx.reply("you can use string as a first parameter too!");
ctx.sendInteractiveMessage(jid: string, content: IInteractiveMessageContent, options: MessageGenerationOptionsFromContent | {} = {});
ctx.replyInteractiveMessage(content: IInteractiveMessageContent, options: MessageGenerationOptionsFromContent | {} = {});
bot.hears('test', async(ctx) => ctx.reply('test 1 2 3 beep boop...'));
import { MessageType } from "@mengkodingan/ckptw";
bot.hears(MessageType.stickerMessage, async(ctx) => ctx.reply('wow, cool sticker'));
ctx.react(jid: string, emoji: string, key?: WAProto.IMessageKey);
ctx.react(ctx.id, "👀");
bot.readyAt;
ctx.id
ctx.decodedId
ctx.args
ctx.sender
ctx.quoted
ctx.getMessageType()
ctx.getContentType(content: WAProto.IMessage | undefined)
ctx.downloadContentFromMessage(downloadable: DownloadableMessage, type: MediaType, opts?: MediaDownloadOptions)
ctx.read()
ctx.simulateTyping()
ctx.simulateRecording()
bot.bio("Hi there!");
await bot.fetchBio("1234@s.whatsapp.net");
await bot.block("1234@s.whatsapp.net");
await bot.unblock("1234@s.whatsapp.net");
ctx.getDevice(id)
ctx.getDevice()
ctx.isGroup()
bot.core
ctx._client