Spaces:
Building
Building
Upload 25 files
Browse files- .env +3 -0
- package.json +45 -0
- public/global.css +817 -0
- public/input.css +53 -0
- setup.bash +2 -0
- src/baileys/db.ts +20 -0
- src/baileys/index.ts +87 -0
- src/controllers/home.ts +20 -0
- src/controllers/index.ts +4 -0
- src/controllers/ping.ts +26 -0
- src/index.ts +35 -0
- src/lib/delay.ts +1 -0
- src/middlewares/api-key-authentication.ts +21 -0
- src/middlewares/ping-message-validator.ts +39 -0
- src/routes/home.ts +9 -0
- src/routes/index.ts +4 -0
- src/routes/ping.ts +20 -0
- src/test/index.ts +3 -0
- src/types/AsyncReturnType.d.ts +7 -0
- src/types/globals.d.ts +8 -0
- tailwind.config.js +64 -0
- tsconfig.json +12 -0
- views/error.ejs +19 -0
- views/index.ejs +67 -0
- views/qr.ejs +46 -0
.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>
|