understanding commited on
Commit
656c481
·
verified ·
1 Parent(s): 609d77a

Upload 25 files

Browse files
.env ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ PORT=8080
2
+ API_KEY=api
3
+ MONGO_URL=mongodb+srv://loda:[email protected]/?retryWrites=true&w=majority
package.json ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "whatsapp-ping",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "vite-node ./src/test/index.ts",
8
+ "dev": "concurrently \"vite-node --watch src/index.ts\" \"npx tailwindcss -i ./public/input.css -o ./public/global.css --watch\"",
9
+ "start": "pm2 start ./dist/index.js",
10
+ "build": "tsc && tailwindcss -i ./public/input.css -o ./public/global.css"
11
+ },
12
+ "keywords": [
13
+ "whatsapp ping"
14
+ ],
15
+ "author": "Zain-ul-din",
16
+ "license": "MIT",
17
+ "devDependencies": {
18
+ "@types/body-parser": "^1.19.5",
19
+ "@types/cors": "^2.8.17",
20
+ "@types/dotenv": "^8.2.0",
21
+ "@types/express": "^4.17.21",
22
+ "concurrently": "^8.2.2",
23
+ "husky": "^9.1.5",
24
+ "tailwindcss": "^3.4.10",
25
+ "typescript": "^5.6.3",
26
+ "vite-node": "^2.0.5"
27
+ },
28
+ "dependencies": {
29
+ "@hapi/boom": "^10.0.1",
30
+ "@whiskeysockets/baileys": "^6.7.9",
31
+ "@whiskeysockets/libsignal-node": "github:WhiskeySockets/libsignal-node",
32
+ "body-parser": "^1.20.2",
33
+ "cors": "^2.8.5",
34
+ "dotenv": "^16.4.5",
35
+ "ejs": "^3.1.10",
36
+ "express": "^4.19.2",
37
+ "express-rate-limit": "^7.4.0",
38
+ "mongo-baileys": "^1.0.1",
39
+ "mongodb": "^6.8.1",
40
+ "platformsh-config": "^2.4.1",
41
+ "qrcode-terminal": "^0.12.0",
42
+ "whatsapp-ping": "file:",
43
+ "zod": "^3.23.8"
44
+ }
45
+ }
public/global.css ADDED
@@ -0,0 +1,817 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *, ::before, ::after {
2
+ --tw-border-spacing-x: 0;
3
+ --tw-border-spacing-y: 0;
4
+ --tw-translate-x: 0;
5
+ --tw-translate-y: 0;
6
+ --tw-rotate: 0;
7
+ --tw-skew-x: 0;
8
+ --tw-skew-y: 0;
9
+ --tw-scale-x: 1;
10
+ --tw-scale-y: 1;
11
+ --tw-pan-x: ;
12
+ --tw-pan-y: ;
13
+ --tw-pinch-zoom: ;
14
+ --tw-scroll-snap-strictness: proximity;
15
+ --tw-gradient-from-position: ;
16
+ --tw-gradient-via-position: ;
17
+ --tw-gradient-to-position: ;
18
+ --tw-ordinal: ;
19
+ --tw-slashed-zero: ;
20
+ --tw-numeric-figure: ;
21
+ --tw-numeric-spacing: ;
22
+ --tw-numeric-fraction: ;
23
+ --tw-ring-inset: ;
24
+ --tw-ring-offset-width: 0px;
25
+ --tw-ring-offset-color: #fff;
26
+ --tw-ring-color: rgb(59 130 246 / 0.5);
27
+ --tw-ring-offset-shadow: 0 0 #0000;
28
+ --tw-ring-shadow: 0 0 #0000;
29
+ --tw-shadow: 0 0 #0000;
30
+ --tw-shadow-colored: 0 0 #0000;
31
+ --tw-blur: ;
32
+ --tw-brightness: ;
33
+ --tw-contrast: ;
34
+ --tw-grayscale: ;
35
+ --tw-hue-rotate: ;
36
+ --tw-invert: ;
37
+ --tw-saturate: ;
38
+ --tw-sepia: ;
39
+ --tw-drop-shadow: ;
40
+ --tw-backdrop-blur: ;
41
+ --tw-backdrop-brightness: ;
42
+ --tw-backdrop-contrast: ;
43
+ --tw-backdrop-grayscale: ;
44
+ --tw-backdrop-hue-rotate: ;
45
+ --tw-backdrop-invert: ;
46
+ --tw-backdrop-opacity: ;
47
+ --tw-backdrop-saturate: ;
48
+ --tw-backdrop-sepia: ;
49
+ --tw-contain-size: ;
50
+ --tw-contain-layout: ;
51
+ --tw-contain-paint: ;
52
+ --tw-contain-style: ;
53
+ }
54
+
55
+ ::backdrop {
56
+ --tw-border-spacing-x: 0;
57
+ --tw-border-spacing-y: 0;
58
+ --tw-translate-x: 0;
59
+ --tw-translate-y: 0;
60
+ --tw-rotate: 0;
61
+ --tw-skew-x: 0;
62
+ --tw-skew-y: 0;
63
+ --tw-scale-x: 1;
64
+ --tw-scale-y: 1;
65
+ --tw-pan-x: ;
66
+ --tw-pan-y: ;
67
+ --tw-pinch-zoom: ;
68
+ --tw-scroll-snap-strictness: proximity;
69
+ --tw-gradient-from-position: ;
70
+ --tw-gradient-via-position: ;
71
+ --tw-gradient-to-position: ;
72
+ --tw-ordinal: ;
73
+ --tw-slashed-zero: ;
74
+ --tw-numeric-figure: ;
75
+ --tw-numeric-spacing: ;
76
+ --tw-numeric-fraction: ;
77
+ --tw-ring-inset: ;
78
+ --tw-ring-offset-width: 0px;
79
+ --tw-ring-offset-color: #fff;
80
+ --tw-ring-color: rgb(59 130 246 / 0.5);
81
+ --tw-ring-offset-shadow: 0 0 #0000;
82
+ --tw-ring-shadow: 0 0 #0000;
83
+ --tw-shadow: 0 0 #0000;
84
+ --tw-shadow-colored: 0 0 #0000;
85
+ --tw-blur: ;
86
+ --tw-brightness: ;
87
+ --tw-contrast: ;
88
+ --tw-grayscale: ;
89
+ --tw-hue-rotate: ;
90
+ --tw-invert: ;
91
+ --tw-saturate: ;
92
+ --tw-sepia: ;
93
+ --tw-drop-shadow: ;
94
+ --tw-backdrop-blur: ;
95
+ --tw-backdrop-brightness: ;
96
+ --tw-backdrop-contrast: ;
97
+ --tw-backdrop-grayscale: ;
98
+ --tw-backdrop-hue-rotate: ;
99
+ --tw-backdrop-invert: ;
100
+ --tw-backdrop-opacity: ;
101
+ --tw-backdrop-saturate: ;
102
+ --tw-backdrop-sepia: ;
103
+ --tw-contain-size: ;
104
+ --tw-contain-layout: ;
105
+ --tw-contain-paint: ;
106
+ --tw-contain-style: ;
107
+ }
108
+
109
+ /*
110
+ ! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com
111
+ */
112
+
113
+ /*
114
+ 1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
115
+ 2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
116
+ */
117
+
118
+ *,
119
+ ::before,
120
+ ::after {
121
+ box-sizing: border-box;
122
+ /* 1 */
123
+ border-width: 0;
124
+ /* 2 */
125
+ border-style: solid;
126
+ /* 2 */
127
+ border-color: #e5e7eb;
128
+ /* 2 */
129
+ }
130
+
131
+ ::before,
132
+ ::after {
133
+ --tw-content: '';
134
+ }
135
+
136
+ /*
137
+ 1. Use a consistent sensible line-height in all browsers.
138
+ 2. Prevent adjustments of font size after orientation changes in iOS.
139
+ 3. Use a more readable tab size.
140
+ 4. Use the user's configured `sans` font-family by default.
141
+ 5. Use the user's configured `sans` font-feature-settings by default.
142
+ 6. Use the user's configured `sans` font-variation-settings by default.
143
+ 7. Disable tap highlights on iOS
144
+ */
145
+
146
+ html,
147
+ :host {
148
+ line-height: 1.5;
149
+ /* 1 */
150
+ -webkit-text-size-adjust: 100%;
151
+ /* 2 */
152
+ -moz-tab-size: 4;
153
+ /* 3 */
154
+ -o-tab-size: 4;
155
+ tab-size: 4;
156
+ /* 3 */
157
+ font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
158
+ /* 4 */
159
+ font-feature-settings: normal;
160
+ /* 5 */
161
+ font-variation-settings: normal;
162
+ /* 6 */
163
+ -webkit-tap-highlight-color: transparent;
164
+ /* 7 */
165
+ }
166
+
167
+ /*
168
+ 1. Remove the margin in all browsers.
169
+ 2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.
170
+ */
171
+
172
+ body {
173
+ margin: 0;
174
+ /* 1 */
175
+ line-height: inherit;
176
+ /* 2 */
177
+ }
178
+
179
+ /*
180
+ 1. Add the correct height in Firefox.
181
+ 2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
182
+ 3. Ensure horizontal rules are visible by default.
183
+ */
184
+
185
+ hr {
186
+ height: 0;
187
+ /* 1 */
188
+ color: inherit;
189
+ /* 2 */
190
+ border-top-width: 1px;
191
+ /* 3 */
192
+ }
193
+
194
+ /*
195
+ Add the correct text decoration in Chrome, Edge, and Safari.
196
+ */
197
+
198
+ abbr:where([title]) {
199
+ -webkit-text-decoration: underline dotted;
200
+ text-decoration: underline dotted;
201
+ }
202
+
203
+ /*
204
+ Remove the default font size and weight for headings.
205
+ */
206
+
207
+ h1,
208
+ h2,
209
+ h3,
210
+ h4,
211
+ h5,
212
+ h6 {
213
+ font-size: inherit;
214
+ font-weight: inherit;
215
+ }
216
+
217
+ /*
218
+ Reset links to optimize for opt-in styling instead of opt-out.
219
+ */
220
+
221
+ a {
222
+ color: inherit;
223
+ text-decoration: inherit;
224
+ }
225
+
226
+ /*
227
+ Add the correct font weight in Edge and Safari.
228
+ */
229
+
230
+ b,
231
+ strong {
232
+ font-weight: bolder;
233
+ }
234
+
235
+ /*
236
+ 1. Use the user's configured `mono` font-family by default.
237
+ 2. Use the user's configured `mono` font-feature-settings by default.
238
+ 3. Use the user's configured `mono` font-variation-settings by default.
239
+ 4. Correct the odd `em` font sizing in all browsers.
240
+ */
241
+
242
+ code,
243
+ kbd,
244
+ samp,
245
+ pre {
246
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
247
+ /* 1 */
248
+ font-feature-settings: normal;
249
+ /* 2 */
250
+ font-variation-settings: normal;
251
+ /* 3 */
252
+ font-size: 1em;
253
+ /* 4 */
254
+ }
255
+
256
+ /*
257
+ Add the correct font size in all browsers.
258
+ */
259
+
260
+ small {
261
+ font-size: 80%;
262
+ }
263
+
264
+ /*
265
+ Prevent `sub` and `sup` elements from affecting the line height in all browsers.
266
+ */
267
+
268
+ sub,
269
+ sup {
270
+ font-size: 75%;
271
+ line-height: 0;
272
+ position: relative;
273
+ vertical-align: baseline;
274
+ }
275
+
276
+ sub {
277
+ bottom: -0.25em;
278
+ }
279
+
280
+ sup {
281
+ top: -0.5em;
282
+ }
283
+
284
+ /*
285
+ 1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
286
+ 2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
287
+ 3. Remove gaps between table borders by default.
288
+ */
289
+
290
+ table {
291
+ text-indent: 0;
292
+ /* 1 */
293
+ border-color: inherit;
294
+ /* 2 */
295
+ border-collapse: collapse;
296
+ /* 3 */
297
+ }
298
+
299
+ /*
300
+ 1. Change the font styles in all browsers.
301
+ 2. Remove the margin in Firefox and Safari.
302
+ 3. Remove default padding in all browsers.
303
+ */
304
+
305
+ button,
306
+ input,
307
+ optgroup,
308
+ select,
309
+ textarea {
310
+ font-family: inherit;
311
+ /* 1 */
312
+ font-feature-settings: inherit;
313
+ /* 1 */
314
+ font-variation-settings: inherit;
315
+ /* 1 */
316
+ font-size: 100%;
317
+ /* 1 */
318
+ font-weight: inherit;
319
+ /* 1 */
320
+ line-height: inherit;
321
+ /* 1 */
322
+ letter-spacing: inherit;
323
+ /* 1 */
324
+ color: inherit;
325
+ /* 1 */
326
+ margin: 0;
327
+ /* 2 */
328
+ padding: 0;
329
+ /* 3 */
330
+ }
331
+
332
+ /*
333
+ Remove the inheritance of text transform in Edge and Firefox.
334
+ */
335
+
336
+ button,
337
+ select {
338
+ text-transform: none;
339
+ }
340
+
341
+ /*
342
+ 1. Correct the inability to style clickable types in iOS and Safari.
343
+ 2. Remove default button styles.
344
+ */
345
+
346
+ button,
347
+ input:where([type='button']),
348
+ input:where([type='reset']),
349
+ input:where([type='submit']) {
350
+ -webkit-appearance: button;
351
+ /* 1 */
352
+ background-color: transparent;
353
+ /* 2 */
354
+ background-image: none;
355
+ /* 2 */
356
+ }
357
+
358
+ /*
359
+ Use the modern Firefox focus style for all focusable elements.
360
+ */
361
+
362
+ :-moz-focusring {
363
+ outline: auto;
364
+ }
365
+
366
+ /*
367
+ Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
368
+ */
369
+
370
+ :-moz-ui-invalid {
371
+ box-shadow: none;
372
+ }
373
+
374
+ /*
375
+ Add the correct vertical alignment in Chrome and Firefox.
376
+ */
377
+
378
+ progress {
379
+ vertical-align: baseline;
380
+ }
381
+
382
+ /*
383
+ Correct the cursor style of increment and decrement buttons in Safari.
384
+ */
385
+
386
+ ::-webkit-inner-spin-button,
387
+ ::-webkit-outer-spin-button {
388
+ height: auto;
389
+ }
390
+
391
+ /*
392
+ 1. Correct the odd appearance in Chrome and Safari.
393
+ 2. Correct the outline style in Safari.
394
+ */
395
+
396
+ [type='search'] {
397
+ -webkit-appearance: textfield;
398
+ /* 1 */
399
+ outline-offset: -2px;
400
+ /* 2 */
401
+ }
402
+
403
+ /*
404
+ Remove the inner padding in Chrome and Safari on macOS.
405
+ */
406
+
407
+ ::-webkit-search-decoration {
408
+ -webkit-appearance: none;
409
+ }
410
+
411
+ /*
412
+ 1. Correct the inability to style clickable types in iOS and Safari.
413
+ 2. Change font properties to `inherit` in Safari.
414
+ */
415
+
416
+ ::-webkit-file-upload-button {
417
+ -webkit-appearance: button;
418
+ /* 1 */
419
+ font: inherit;
420
+ /* 2 */
421
+ }
422
+
423
+ /*
424
+ Add the correct display in Chrome and Safari.
425
+ */
426
+
427
+ summary {
428
+ display: list-item;
429
+ }
430
+
431
+ /*
432
+ Removes the default spacing and border for appropriate elements.
433
+ */
434
+
435
+ blockquote,
436
+ dl,
437
+ dd,
438
+ h1,
439
+ h2,
440
+ h3,
441
+ h4,
442
+ h5,
443
+ h6,
444
+ hr,
445
+ figure,
446
+ p,
447
+ pre {
448
+ margin: 0;
449
+ }
450
+
451
+ fieldset {
452
+ margin: 0;
453
+ padding: 0;
454
+ }
455
+
456
+ legend {
457
+ padding: 0;
458
+ }
459
+
460
+ ol,
461
+ ul,
462
+ menu {
463
+ list-style: none;
464
+ margin: 0;
465
+ padding: 0;
466
+ }
467
+
468
+ /*
469
+ Reset default styling for dialogs.
470
+ */
471
+
472
+ dialog {
473
+ padding: 0;
474
+ }
475
+
476
+ /*
477
+ Prevent resizing textareas horizontally by default.
478
+ */
479
+
480
+ textarea {
481
+ resize: vertical;
482
+ }
483
+
484
+ /*
485
+ 1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
486
+ 2. Set the default placeholder color to the user's configured gray 400 color.
487
+ */
488
+
489
+ input::-moz-placeholder, textarea::-moz-placeholder {
490
+ opacity: 1;
491
+ /* 1 */
492
+ color: #9ca3af;
493
+ /* 2 */
494
+ }
495
+
496
+ input::placeholder,
497
+ textarea::placeholder {
498
+ opacity: 1;
499
+ /* 1 */
500
+ color: #9ca3af;
501
+ /* 2 */
502
+ }
503
+
504
+ /*
505
+ Set the default cursor for buttons.
506
+ */
507
+
508
+ button,
509
+ [role="button"] {
510
+ cursor: pointer;
511
+ }
512
+
513
+ /*
514
+ Make sure disabled buttons don't get the pointer cursor.
515
+ */
516
+
517
+ :disabled {
518
+ cursor: default;
519
+ }
520
+
521
+ /*
522
+ 1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
523
+ 2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
524
+ This can trigger a poorly considered lint error in some tools but is included by design.
525
+ */
526
+
527
+ img,
528
+ svg,
529
+ video,
530
+ canvas,
531
+ audio,
532
+ iframe,
533
+ embed,
534
+ object {
535
+ display: block;
536
+ /* 1 */
537
+ vertical-align: middle;
538
+ /* 2 */
539
+ }
540
+
541
+ /*
542
+ Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
543
+ */
544
+
545
+ img,
546
+ video {
547
+ max-width: 100%;
548
+ height: auto;
549
+ }
550
+
551
+ /* Make elements with the HTML hidden attribute stay hidden by default */
552
+
553
+ [hidden]:where(:not([hidden="until-found"])) {
554
+ display: none;
555
+ }
556
+
557
+ :root {
558
+ --background: 0 0% 100%;
559
+ --foreground: 240 10% 3.9%;
560
+ --card: 0 0% 100%;
561
+ --card-foreground: 240 10% 3.9%;
562
+ --popover: 0 0% 100%;
563
+ --popover-foreground: 240 10% 3.9%;
564
+ --primary: 240 5.9% 10%;
565
+ --primary-foreground: 0 0% 98%;
566
+ --secondary: 240 4.8% 95.9%;
567
+ --secondary-foreground: 240 5.9% 10%;
568
+ --muted: 240 4.8% 95.9%;
569
+ --muted-foreground: 240 3.8% 45%;
570
+ --accent: 240 4.8% 95.9%;
571
+ --accent-foreground: 240 5.9% 10%;
572
+ --destructive: 0 72% 51%;
573
+ --destructive-foreground: 0 0% 98%;
574
+ --border: 240 5.9% 90%;
575
+ --input: 240 5.9% 90%;
576
+ --ring: 240 5.9% 10%;
577
+ --radius: 0.5rem;
578
+ }
579
+
580
+ * {
581
+ border-color: hsl(var(--border));
582
+ }
583
+
584
+ body {
585
+ /* Your custom styles here */
586
+ font-family: "Inter", sans-serif;
587
+ background-color: hsl(var(--background));
588
+ color: hsl(var(--foreground));
589
+ }
590
+
591
+ h1,
592
+ h2,
593
+ h3,
594
+ h4,
595
+ h5,
596
+ h6 {
597
+ /* Your custom styles here */
598
+ font-family: "Inter", sans-serif;
599
+ }
600
+
601
+ .mx-auto {
602
+ margin-left: auto;
603
+ margin-right: auto;
604
+ }
605
+
606
+ .my-auto {
607
+ margin-top: auto;
608
+ margin-bottom: auto;
609
+ }
610
+
611
+ .mb-4 {
612
+ margin-bottom: 1rem;
613
+ }
614
+
615
+ .ml-auto {
616
+ margin-left: auto;
617
+ }
618
+
619
+ .mr-1 {
620
+ margin-right: 0.25rem;
621
+ }
622
+
623
+ .mt-2 {
624
+ margin-top: 0.5rem;
625
+ }
626
+
627
+ .mt-4 {
628
+ margin-top: 1rem;
629
+ }
630
+
631
+ .flex {
632
+ display: flex;
633
+ }
634
+
635
+ .hidden {
636
+ display: none;
637
+ }
638
+
639
+ .h-\[302px\] {
640
+ height: 302px;
641
+ }
642
+
643
+ .h-screen {
644
+ height: 100vh;
645
+ }
646
+
647
+ .w-\[302px\] {
648
+ width: 302px;
649
+ }
650
+
651
+ .w-full {
652
+ width: 100%;
653
+ }
654
+
655
+ .max-w-md {
656
+ max-width: 28rem;
657
+ }
658
+
659
+ .cursor-pointer {
660
+ cursor: pointer;
661
+ }
662
+
663
+ .flex-col {
664
+ flex-direction: column;
665
+ }
666
+
667
+ .items-center {
668
+ align-items: center;
669
+ }
670
+
671
+ .justify-center {
672
+ justify-content: center;
673
+ }
674
+
675
+ .text-balance {
676
+ text-wrap: balance;
677
+ }
678
+
679
+ .rounded-md {
680
+ border-radius: calc(var(--radius) - 2px);
681
+ }
682
+
683
+ .rounded-b-lg {
684
+ border-bottom-right-radius: var(--radius);
685
+ border-bottom-left-radius: var(--radius);
686
+ }
687
+
688
+ .border {
689
+ border-width: 1px;
690
+ }
691
+
692
+ .border-t {
693
+ border-top-width: 1px;
694
+ }
695
+
696
+ .border-gray-300 {
697
+ --tw-border-opacity: 1;
698
+ border-color: rgb(209 213 219 / var(--tw-border-opacity, 1));
699
+ }
700
+
701
+ .bg-blue-600 {
702
+ --tw-bg-opacity: 1;
703
+ background-color: rgb(37 99 235 / var(--tw-bg-opacity, 1));
704
+ }
705
+
706
+ .p-2 {
707
+ padding: 0.5rem;
708
+ }
709
+
710
+ .p-6 {
711
+ padding: 1.5rem;
712
+ }
713
+
714
+ .py-12 {
715
+ padding-top: 3rem;
716
+ padding-bottom: 3rem;
717
+ }
718
+
719
+ .py-2 {
720
+ padding-top: 0.5rem;
721
+ padding-bottom: 0.5rem;
722
+ }
723
+
724
+ .pt-2 {
725
+ padding-top: 0.5rem;
726
+ }
727
+
728
+ .text-center {
729
+ text-align: center;
730
+ }
731
+
732
+ .text-2xl {
733
+ font-size: 1.5rem;
734
+ line-height: 2rem;
735
+ }
736
+
737
+ .text-lg {
738
+ font-size: 1.125rem;
739
+ line-height: 1.75rem;
740
+ }
741
+
742
+ .font-medium {
743
+ font-weight: 500;
744
+ }
745
+
746
+ .font-semibold {
747
+ font-weight: 600;
748
+ }
749
+
750
+ .text-blue-400 {
751
+ --tw-text-opacity: 1;
752
+ color: rgb(96 165 250 / var(--tw-text-opacity, 1));
753
+ }
754
+
755
+ .text-gray-800 {
756
+ --tw-text-opacity: 1;
757
+ color: rgb(31 41 55 / var(--tw-text-opacity, 1));
758
+ }
759
+
760
+ .text-green-400 {
761
+ --tw-text-opacity: 1;
762
+ color: rgb(74 222 128 / var(--tw-text-opacity, 1));
763
+ }
764
+
765
+ .text-red-400 {
766
+ --tw-text-opacity: 1;
767
+ color: rgb(248 113 113 / var(--tw-text-opacity, 1));
768
+ }
769
+
770
+ .text-white {
771
+ --tw-text-opacity: 1;
772
+ color: rgb(255 255 255 / var(--tw-text-opacity, 1));
773
+ }
774
+
775
+ .shadow-sm {
776
+ --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
777
+ --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);
778
+ box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
779
+ }
780
+
781
+ .transition {
782
+ transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter;
783
+ transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;
784
+ transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter;
785
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
786
+ transition-duration: 150ms;
787
+ }
788
+
789
+ .duration-200 {
790
+ transition-duration: 200ms;
791
+ }
792
+
793
+ .hover\:bg-blue-700:hover {
794
+ --tw-bg-opacity: 1;
795
+ background-color: rgb(29 78 216 / var(--tw-bg-opacity, 1));
796
+ }
797
+
798
+ .hover\:text-blue-600:hover {
799
+ --tw-text-opacity: 1;
800
+ color: rgb(37 99 235 / var(--tw-text-opacity, 1));
801
+ }
802
+
803
+ .focus\:outline-none:focus {
804
+ outline: 2px solid transparent;
805
+ outline-offset: 2px;
806
+ }
807
+
808
+ .focus\:ring-2:focus {
809
+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
810
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
811
+ box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
812
+ }
813
+
814
+ .focus\:ring-blue-500:focus {
815
+ --tw-ring-opacity: 1;
816
+ --tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1));
817
+ }
public/input.css ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ @layer base {
6
+ .font-body,
7
+ .font-heading {
8
+ /* Your custom styles here */
9
+ font-family: "Inter", sans-serif;
10
+ }
11
+
12
+ :root {
13
+ --background: 0 0% 100%;
14
+ --foreground: 240 10% 3.9%;
15
+ --card: 0 0% 100%;
16
+ --card-foreground: 240 10% 3.9%;
17
+ --popover: 0 0% 100%;
18
+ --popover-foreground: 240 10% 3.9%;
19
+ --primary: 240 5.9% 10%;
20
+ --primary-foreground: 0 0% 98%;
21
+ --secondary: 240 4.8% 95.9%;
22
+ --secondary-foreground: 240 5.9% 10%;
23
+ --muted: 240 4.8% 95.9%;
24
+ --muted-foreground: 240 3.8% 45%;
25
+ --accent: 240 4.8% 95.9%;
26
+ --accent-foreground: 240 5.9% 10%;
27
+ --destructive: 0 72% 51%;
28
+ --destructive-foreground: 0 0% 98%;
29
+ --border: 240 5.9% 90%;
30
+ --input: 240 5.9% 90%;
31
+ --ring: 240 5.9% 10%;
32
+ --radius: 0.5rem;
33
+ }
34
+ }
35
+
36
+ @layer base {
37
+ * {
38
+ @apply border-border;
39
+ }
40
+
41
+ body {
42
+ @apply bg-background text-foreground font-body;
43
+ }
44
+
45
+ h1,
46
+ h2,
47
+ h3,
48
+ h4,
49
+ h5,
50
+ h6 {
51
+ @apply font-heading;
52
+ }
53
+ }
setup.bash ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ npm install
2
+ npm run build
src/baileys/db.ts ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { MongoClient } from "mongodb";
2
+ import { AuthenticationCreds } from "@whiskeysockets/baileys";
3
+
4
+ const dbName = "whatsapp";
5
+ const collectionName = "authState";
6
+
7
+ interface AuthDocument extends Document {
8
+ _id: string;
9
+ creds?: AuthenticationCreds;
10
+ }
11
+
12
+ async function connectDB() {
13
+ const client = new MongoClient(process.env.MONGO_URL || "");
14
+ await client.connect();
15
+ const db = client.db(dbName);
16
+ const collection = db.collection<AuthDocument>(collectionName);
17
+ return { client, collection };
18
+ }
19
+
20
+ export { connectDB };
src/baileys/index.ts ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import makeWASocket, {
2
+ DisconnectReason,
3
+ useMultiFileAuthState
4
+ } from "@whiskeysockets/baileys";
5
+ import { Boom } from "@hapi/boom";
6
+ import * as fs from "fs";
7
+ import { connectDB } from "./db";
8
+ import { useMongoDBAuthState } from "mongo-baileys";
9
+
10
+ async function connectToWhatsApp(onStart?: () => void) {
11
+ const { state, saveCreds } = process.env.MONGO_URL
12
+ ? await useMongoDBAuthState((await connectDB()).collection as any)
13
+ : await useMultiFileAuthState("auth_info_baileys");
14
+
15
+ const sock = makeWASocket({
16
+ // can provide additional config here
17
+ printQRInTerminal: true,
18
+ mobile: false,
19
+ keepAliveIntervalMs: 10000,
20
+ syncFullHistory: false,
21
+ markOnlineOnConnect: true,
22
+ defaultQueryTimeoutMs: undefined,
23
+ auth: state
24
+ });
25
+
26
+ sock.ev.on("creds.update", saveCreds);
27
+
28
+ const setupAuth = new Promise(async (resolve, rej) => {
29
+ sock.ev.on("connection.update", (update) => {
30
+ const { connection, lastDisconnect, qr } = update;
31
+ global.waQrCode = qr || null;
32
+ try {
33
+ if (connection === "close" && lastDisconnect) {
34
+ const statusCode = (lastDisconnect.error as Boom)?.output?.statusCode;
35
+ const shouldReconnect =
36
+ (lastDisconnect.error as Boom)?.output?.statusCode !==
37
+ DisconnectReason.loggedOut;
38
+
39
+ console.log(
40
+ "connection closed due to ",
41
+ lastDisconnect.error,
42
+ ", status code: ",
43
+ statusCode,
44
+ ", reconnecting ",
45
+ shouldReconnect
46
+ );
47
+ // reconnect if not logged out
48
+ if (shouldReconnect) {
49
+ connectToWhatsApp();
50
+ } else {
51
+ // clear credentials
52
+ if (lastDisconnect.error) {
53
+ if (fs.existsSync("./auth_info_baileys")) {
54
+ fs.rmSync("./auth_info_baileys", {
55
+ force: true,
56
+ recursive: true
57
+ });
58
+ }
59
+ }
60
+ }
61
+ } else if (connection === "open") {
62
+ console.log("\n ✔ opened connection \n");
63
+ resolve(null);
64
+ }
65
+ } catch (err) {
66
+ console.log(err);
67
+ }
68
+ });
69
+ });
70
+
71
+ const FIVE_MIN_IN_MS = 1000 * 60 * 5;
72
+
73
+ await Promise.race([
74
+ setupAuth,
75
+ new Promise((_, rej) =>
76
+ setTimeout(
77
+ () => rej("Timeout while setting up connection to whatsapp"),
78
+ FIVE_MIN_IN_MS
79
+ )
80
+ )
81
+ ]);
82
+
83
+ global.waSock = sock;
84
+ }
85
+
86
+ // run in main file
87
+ export { connectToWhatsApp };
src/controllers/home.ts ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Request, Response } from "express";
2
+
3
+ export const homeController = (req: Request, res: Response) => {
4
+ res.render("index");
5
+ };
6
+
7
+ export const getQrCodeController = (req: Request, res: Response) => {
8
+ const apiKey = req.body.api_key;
9
+
10
+ if (apiKey != process.env.API_KEY) {
11
+ res.render("error", { message: "Invalid Credentials" });
12
+ return;
13
+ }
14
+
15
+ if (global.waQrCode) {
16
+ res.render("qr", { qr: global.waQrCode });
17
+ } else {
18
+ res.render("qr", { qr: "" });
19
+ }
20
+ };
src/controllers/index.ts ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ import pingController from "./ping";
2
+ import { getQrCodeController, homeController } from "./home";
3
+
4
+ export { pingController, homeController, getQrCodeController };
src/controllers/ping.ts ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Request, Response } from "express";
2
+ import { delay } from "../lib/delay";
3
+
4
+ const pingController = async (req: Request, res: Response): Promise<void> => {
5
+ if (global.waSock == null) {
6
+ res.status(500).send({ error: "WA is not connected" });
7
+ return;
8
+ }
9
+ const { message, numbers, image } = req.body;
10
+ for (let number of numbers) {
11
+ const id = `${number}@s.whatsapp.net`;
12
+ if (image) {
13
+ const imgToBase64 = Buffer.from(image, "base64");
14
+ await global.waSock.sendMessage(id, {
15
+ image: imgToBase64,
16
+ caption: message
17
+ });
18
+ } else {
19
+ await global.waSock.sendMessage(id, { text: message });
20
+ }
21
+ await delay(100 * Math.random());
22
+ }
23
+ res.status(200).send({ message: "success" });
24
+ };
25
+
26
+ export default pingController;
src/index.ts ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import express from "express";
2
+ import { homeRoute, pingRoute } from "./routes";
3
+ import bodyParser from "body-parser";
4
+ import dotenv from "dotenv";
5
+ import cors from "cors";
6
+ import { connectToWhatsApp } from "./baileys";
7
+
8
+ const app = express();
9
+ const PORT = process.env.PORT || 8080;
10
+
11
+ dotenv.config();
12
+
13
+ app.set("views", "./views");
14
+ app.set("view engine", "ejs");
15
+
16
+ app.use(cors({ origin: "*" }));
17
+ app.use(express.static("public"));
18
+
19
+ app.use(bodyParser.json({ limit: "20mb" }));
20
+ app.use(express.urlencoded({ extended: true }));
21
+
22
+ app.use(homeRoute);
23
+ app.use(pingRoute);
24
+
25
+ app.use((_, res) => {
26
+ res.status(404).json({ message: "Resource not found" });
27
+ });
28
+
29
+ (async () => {
30
+ await connectToWhatsApp();
31
+ })();
32
+
33
+ app.listen(PORT, () => {
34
+ console.log(`server is listening on localhost:${PORT}`);
35
+ });
src/lib/delay.ts ADDED
@@ -0,0 +1 @@
 
 
1
+ export const delay = (ms: number) => new Promise((r) => setTimeout(r, ms));
src/middlewares/api-key-authentication.ts ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Request, Response, NextFunction } from "express";
2
+
3
+ const authentication = (req: Request, res: Response, next: NextFunction) => {
4
+ const authHeader = req.headers["authorization"];
5
+ if (!authHeader) {
6
+ return res.status(401).json({ message: "Missing authorization header" });
7
+ }
8
+
9
+ const apiKey = authHeader.split("Bearer ").at(-1);
10
+ if (!apiKey) {
11
+ return res.status(401).json({ message: "Invalid authorization format" });
12
+ }
13
+
14
+ if (process.env.API_KEY && apiKey === process.env.API_KEY) {
15
+ next();
16
+ } else {
17
+ res.status(401).json({ message: "Unauthorized" });
18
+ }
19
+ };
20
+
21
+ export default authentication;
src/middlewares/ping-message-validator.ts ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Request, Response, NextFunction } from "express";
2
+ import { z } from "zod";
3
+
4
+ const pingMessageSchema = z.object({
5
+ message: z
6
+ .string()
7
+ .min(1, "Message is required and must be a non-empty string"),
8
+ numbers: z
9
+ .array(
10
+ z
11
+ .string()
12
+ .min(12, "Each number must be at least 12 characters long")
13
+ .regex(
14
+ /^\d{12}$/,
15
+ "Invalid phone number format. Correct example: 123456789012"
16
+ )
17
+ )
18
+ .max(5, "You can provide a maximum of 5 phone numbers"),
19
+ image: z.string().optional()
20
+ });
21
+
22
+ const validatePingMessage = (
23
+ req: Request,
24
+ res: Response,
25
+ next: NextFunction
26
+ ): void => {
27
+ try {
28
+ pingMessageSchema.parse(req.body);
29
+ next();
30
+ } catch (error) {
31
+ if (error instanceof z.ZodError) {
32
+ res.status(400).json({ errors: error.errors });
33
+ } else {
34
+ res.status(500).json({ message: "Internal Server Error" });
35
+ }
36
+ }
37
+ };
38
+
39
+ export default validatePingMessage;
src/routes/home.ts ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import express from "express";
2
+ import { getQrCodeController, homeController } from "../controllers";
3
+
4
+ const router = express.Router();
5
+
6
+ router.get("/", homeController);
7
+ router.post("/", getQrCodeController);
8
+
9
+ export default router;
src/routes/index.ts ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ import pingRoute from "./ping";
2
+ import homeRoute from "./home";
3
+
4
+ export { pingRoute, homeRoute };
src/routes/ping.ts ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import express from "express";
2
+ import { pingController } from "../controllers";
3
+ import validatePingMessage from "../middlewares/ping-message-validator";
4
+ import { rateLimit } from "express-rate-limit";
5
+ import authentication from "../middlewares/api-key-authentication";
6
+
7
+ const limiter = rateLimit({
8
+ windowMs: 1000,
9
+ limit: 5,
10
+ standardHeaders: "draft-7",
11
+ legacyHeaders: false
12
+ });
13
+
14
+ const route = express.Router();
15
+
16
+ route.use(limiter);
17
+ route.use(authentication);
18
+ route.post("/ping", validatePingMessage, pingController);
19
+
20
+ export default route;
src/test/index.ts ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ import dotenv from "dotenv";
2
+ dotenv.config();
3
+ console.log("✔ All test has been passed");
src/types/AsyncReturnType.d.ts ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ type AsyncReturnType<T extends (...args: any) => Promise<any>> = T extends (
2
+ ...args: any
3
+ ) => Promise<infer R>
4
+ ? R
5
+ : any;
6
+
7
+ export default AsyncReturnType;
src/types/globals.d.ts ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ import { WASocket } from "@whiskeysockets/baileys"; // Import the type for your socket
2
+
3
+ declare global {
4
+ var waSock: WASocket | null;
5
+ var waQrCode: string | null;
6
+ }
7
+
8
+ export {};
tailwind.config.js ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /** @type {import('tailwindcss').Config} */
2
+ module.exports = {
3
+ content: ["./views/**/*.{html,js,ejs}", "./public/**/*.{html,js}"],
4
+ theme: {
5
+ extend: {
6
+ colors: {
7
+ border: "hsl(var(--border))",
8
+ input: "hsl(var(--input))",
9
+ ring: "hsl(var(--ring))",
10
+ background: "hsl(var(--background))",
11
+ foreground: "hsl(var(--foreground))",
12
+ primary: {
13
+ DEFAULT: "hsl(var(--primary))",
14
+ foreground: "hsl(var(--primary-foreground))"
15
+ },
16
+ secondary: {
17
+ DEFAULT: "hsl(var(--secondary))",
18
+ foreground: "hsl(var(--secondary-foreground))"
19
+ },
20
+ destructive: {
21
+ DEFAULT: "hsl(var(--destructive))",
22
+ foreground: "hsl(var(--destructive-foreground))"
23
+ },
24
+ muted: {
25
+ DEFAULT: "hsl(var(--muted))",
26
+ foreground: "hsl(var(--muted-foreground))"
27
+ },
28
+ accent: {
29
+ DEFAULT: "hsl(var(--accent))",
30
+ foreground: "hsl(var(--accent-foreground))"
31
+ },
32
+ popover: {
33
+ DEFAULT: "hsl(var(--popover))",
34
+ foreground: "hsl(var(--popover-foreground))"
35
+ },
36
+ card: {
37
+ DEFAULT: "hsl(var(--card))",
38
+ foreground: "hsl(var(--card-foreground))"
39
+ }
40
+ },
41
+ borderRadius: {
42
+ xl: `calc(var(--radius) + 4px)`,
43
+ lg: `var(--radius)`,
44
+ md: `calc(var(--radius) - 2px)`,
45
+ sm: `calc(var(--radius) - 4px)`
46
+ },
47
+ keyframes: {
48
+ "accordion-down": {
49
+ from: { height: 0 },
50
+ to: { height: "var(--radix-accordion-content-height)" }
51
+ },
52
+ "accordion-up": {
53
+ from: { height: "var(--radix-accordion-content-height)" },
54
+ to: { height: 0 }
55
+ }
56
+ },
57
+ animation: {
58
+ "accordion-down": "accordion-down 0.2s ease-out",
59
+ "accordion-up": "accordion-up 0.2s ease-out"
60
+ }
61
+ }
62
+ },
63
+ plugins: []
64
+ };
tsconfig.json ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES6",
4
+ "module": "commonjs",
5
+ "outDir": "./dist",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true
9
+ },
10
+ "include": ["src/**/*.ts"],
11
+ "exclude": ["node_modules"]
12
+ }
views/error.ejs ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Error - Whats App Ping</title>
7
+ <link href="/global.css" rel="stylesheet" />
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ </head>
10
+ <body>
11
+ <main class="h-screen flex flex-col items-center justify-center">
12
+ <div class="border rounded-b-lg shadow-sm w-full max-w-md p-6">
13
+ <h1 class="text-red-400 text-lg font-medium text-center py-12">
14
+ Error: <%= message %>
15
+ </h1>
16
+ </div>
17
+ </main>
18
+ </body>
19
+ </html>
views/index.ejs ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Whats App Ping</title>
7
+ <link href="/global.css" rel="stylesheet" />
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ </head>
10
+ <body>
11
+ <main class="h-screen flex flex-col items-center justify-center">
12
+ <div
13
+ class="charming-header border rounded-b-lg shadow-sm w-full max-w-md p-6"
14
+ >
15
+ <h1 class="text-2xl font-semibold text-center mb-4">
16
+ 🔔 Whats App Ping
17
+ </h1>
18
+ <form method="post" action="/">
19
+ <label class="flex flex-col mb-4">
20
+ <span class="text-lg">API Key</span>
21
+ <input
22
+ type="text"
23
+ placeholder="Enter your API key"
24
+ class="mt-2 p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
25
+ name="api_key"
26
+ required
27
+ />
28
+ </label>
29
+ <button
30
+ type="submit"
31
+ class="w-full bg-blue-600 text-white py-2 rounded-md hover:bg-blue-700 transition duration-200"
32
+ >
33
+ Submit
34
+ </button>
35
+ </form>
36
+
37
+ <div class="mt-4 flex">
38
+ <a
39
+ class="ml-auto flex cursor-pointer items-center text-gray-800 hover:text-blue-600 transition duration-200"
40
+ href="https://github.com/Zain-ul-din/whatsapp-ping"
41
+ target="_blank"
42
+ >
43
+ <!-- GitHub SVG Icon -->
44
+ <svg
45
+ xmlns="http://www.w3.org/2000/svg"
46
+ width="20"
47
+ height="20"
48
+ viewBox="0 0 24 24"
49
+ fill="none"
50
+ stroke="currentColor"
51
+ stroke-width="2"
52
+ stroke-linecap="round"
53
+ stroke-linejoin="round"
54
+ class="lucide lucide-github mr-1"
55
+ >
56
+ <path
57
+ d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"
58
+ />
59
+ <path d="M9 18c-4.51 2-5-2-7-2" />
60
+ </svg>
61
+ View On GitHub
62
+ </a>
63
+ </div>
64
+ </div>
65
+ </main>
66
+ </body>
67
+ </html>
views/qr.ejs ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Scan Qr - Whats App Ping</title>
7
+ <link href="/global.css" rel="stylesheet" />
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <script src="https://cdn.rawgit.com/davidshimjs/qrcodejs/gh-pages/qrcode.min.js"></script>
10
+ </head>
11
+ <body>
12
+ <main class="h-screen flex flex-col items-center justify-center">
13
+ <div
14
+ class="charming-header border rounded-b-lg flex flex-col justify-center items-center shadow-sm w-full max-w-md p-6"
15
+ >
16
+ <input id="qr_code" value="<%= qr %>" hidden />
17
+ <div id="qrcode" class="w-[302px] h-[302px] my-auto mx-auto"></div>
18
+
19
+ <% if (qr.length == 0) { %>
20
+ <p class="text-blue-400 text-balance font-medium border-t pt-2">
21
+ QR code generation is still in progress; please wait a minute and try
22
+ again.
23
+ </p>
24
+ <% } else { %>
25
+ <p class="text-green-400 text-balance font-medium border-t pt-2">
26
+ Use your phone to scan the QR code.
27
+ </p>
28
+ <% } %>
29
+ </div>
30
+ </main>
31
+
32
+ <script>
33
+ var qrcode = new QRCode("qrcode");
34
+
35
+ function makeCode() {
36
+ var elText = document.getElementById("qr_code");
37
+
38
+ if (!elText.value) return;
39
+
40
+ qrcode.makeCode(elText.value);
41
+ }
42
+
43
+ makeCode();
44
+ </script>
45
+ </body>
46
+ </html>