Back to Home
Gistify Snippet
kyiov
Aug 30ยท2 file(s)
Public

WhatsApp Channel Reaction Sender (Node.js & CLI Multi-File)

Full JavaScript / Node.js client & CLI suite to send automated emoji reactions to WhatsApp Channels with session handshake, cookiejar support, and multi-batch repeater.

#whatsapp#reaction#javascript#nodejs#cli#tools#channel
11
Raw
channelreact.jsjavascript
134 lines
1/**
2 * ChannelReact (JavaScript / Node.js Engine)
3 * Automated WhatsApp Channel Reaction Sender
4 *
5 * Features:
6 * - Session Handshake & Token Acquisition
7 * - Automatic retry with exponential backoff
8 * - Multi-reaction batch sender
9 */
10
11const BASE_URL = "https://satriareact.satriadeveloperz.workers.dev";
12const BROWSER_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36";
13
14const EMOJI_REGEX = /[\u{1F300}-\u{1FAFF}\u{2600}-\u{27BF}\u{FE0F}]/u;
15
16export function isEmoji(str) {
17 const val = String(str || "").trim();
18 if (!val) return false;
19 return val.length <= 8 && EMOJI_REGEX.test(val);
20}
21
22export class ChannelReactClient {
23 constructor(options = {}) {
24 this.baseUrl = options.baseUrl || BASE_URL;
25 this.userAgent = options.userAgent || BROWSER_USER_AGENT;
26 this.cookies = "";
27 }
28
29 getHeaders() {
30 const headers = {
31 "User-Agent": this.userAgent,
32 "Content-Type": "application/json",
33 "Origin": this.baseUrl,
34 "Referer": `${this.baseUrl}/`,
35 "Accept": "application/json, text/plain, */*",
36 };
37 if (this.cookies) {
38 headers["Cookie"] = this.cookies;
39 }
40 return headers;
41 }
42
43 async handshake(maxRetries = 5) {
44 let lastError = null;
45
46 for (let attempt = 1; attempt <= maxRetries; attempt++) {
47 if (attempt > 1) {
48 await new Promise((res) => setTimeout(res, 2000 * attempt));
49 }
50
51 try {
52 const response = await fetch(`${this.baseUrl}/api/handshake`, {
53 method: "POST",
54 headers: this.getHeaders(),
55 body: JSON.stringify({}),
56 });
57
58 // Store session cookies if returned
59 const setCookie = response.headers.get("set-cookie");
60 if (setCookie) {
61 this.cookies = setCookie.split(";")[0];
62 }
63
64 const data = await response.json();
65 if (response.ok && data?.success && data?.token) {
66 return {
67 token: data.token,
68 clientId: data.clientId,
69 username: data.step1?.username || data.username,
70 expiresInMs: data.expiresInMs || 60000,
71 };
72 }
73 lastError = `HTTP ${response.status}: ${JSON.stringify(data)}`;
74 } catch (err) {
75 lastError = err.message;
76 }
77 }
78
79 throw new Error(`Handshake failed after ${maxRetries} attempts (${lastError})`);
80 }
81
82 async react(url, reactions = ["๐Ÿ‘"], token = null, maxRetries = 3) {
83 let currentToken = token;
84 if (!currentToken) {
85 const hs = await this.handshake();
86 currentToken = hs.token;
87 }
88
89 const reactionList = Array.isArray(reactions) ? reactions : [reactions];
90
91 for (let attempt = 1; attempt <= maxRetries; attempt++) {
92 try {
93 const response = await fetch(`${this.baseUrl}/api/react`, {
94 method: "POST",
95 headers: this.getHeaders(),
96 body: JSON.stringify({
97 url,
98 reactions: reactionList,
99 token: currentToken,
100 }),
101 });
102
103 const data = await response.json();
104 if (response.ok && data?.success) {
105 return {
106 success: true,
107 count: data.count || reactionList.length,
108 message: data.message || "Terkirim",
109 task: data.task?.status || "pending",
110 vip: data.vip?.packageName || "VIP API",
111 };
112 }
113
114 // Token expired or handshake invalid -> re-handshake and retry
115 if ((response.status === 401 || response.status === 403 || response.status === 502) && attempt < maxRetries) {
116 await new Promise((res) => setTimeout(res, 2000 * attempt));
117 const newHs = await this.handshake();
118 currentToken = newHs.token;
119 continue;
120 }
121
122 throw new Error(`Reaction request failed (HTTP ${response.status}): ${JSON.stringify(data)}`);
123 } catch (err) {
124 if (attempt >= maxRetries) throw err;
125 await new Promise((res) => setTimeout(res, 1500 * attempt));
126 }
127 }
128
129 throw new Error(`Failed to send reactions after ${maxRetries} attempts`);
130 }
131}
132
133export default ChannelReactClient;
134
Discussion & Comments0

No comments yet โ€” be the first to leave feedback or start a code discussion.

Sign in with your GitHub account to write comments and join the discussion.