Skip to content

Commit fdbf04b

Browse files
committed
init
0 parents  commit fdbf04b

26 files changed

+3899
-0
lines changed

.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
DISPLAY_NAME="YOUR BOT DISPLAY NAME"
2+
ADMIN_NUMBER="628XXX"
3+
NODE_ENV=production
4+
PORT=4321

.eslintrc.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"env": {
3+
"browser": true,
4+
"es2021": true
5+
},
6+
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
7+
"overrides": [],
8+
"parser": "@typescript-eslint/parser",
9+
"parserOptions": {
10+
"ecmaVersion": "latest",
11+
"sourceType": "module"
12+
},
13+
"plugins": ["@typescript-eslint", "simple-import-sort"],
14+
"rules": {
15+
"simple-import-sort/imports": "error",
16+
"simple-import-sort/exports": "error"
17+
}
18+
}

.gitignore

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# build output
2+
dist/
3+
4+
# dependencies
5+
node_modules/
6+
7+
# logs
8+
*.log
9+
npm-debug.log*
10+
yarn-debug.log*
11+
yarn-error.log*
12+
pnpm-debug.log*
13+
14+
# auth
15+
.wwebjs_auth
16+
17+
# environment variables
18+
.env
19+
.env.production
20+
21+
# macOS-specific files
22+
.DS_Store

Dockerfile

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
FROM node:20
2+
3+
RUN apt-get update && apt-get install -y \
4+
gconf-service \
5+
libgbm-dev \
6+
libasound2 \
7+
libatk1.0-0 \
8+
libc6 \
9+
libcairo2 \
10+
libcups2 \
11+
libdbus-1-3 \
12+
libexpat1 \
13+
libfontconfig1 \
14+
libgcc1 \
15+
libgconf-2-4 \
16+
libgdk-pixbuf2.0-0 \
17+
libglib2.0-0 \
18+
libgtk-3-0 \
19+
libnspr4 \
20+
libpango-1.0-0 \
21+
libpangocairo-1.0-0 \
22+
libstdc++6 \
23+
libx11-6 \
24+
libx11-xcb1 \
25+
libxcb1 \
26+
libxcomposite1 \
27+
libxcursor1 \
28+
libxdamage1 \
29+
libxext6 \
30+
libxfixes3 \
31+
libxi6 \
32+
libxrandr2 \
33+
libxrender1 \
34+
libxss1 \
35+
libxtst6 \
36+
ca-certificates \
37+
fonts-liberation \
38+
libappindicator1 \
39+
libnss3 \
40+
lsb-release \
41+
xdg-utils \
42+
wget
43+
44+
RUN npm install -g pnpm
45+
46+
WORKDIR /app
47+
48+
COPY . .
49+
50+
RUN pnpm install
51+
52+
RUN pnpm run build
53+
54+
EXPOSE 3000
55+
56+
CMD ["pnpm", "start"]

README.md

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
# WA-GATE
2+
3+
A very simple typescript whatsapp gateway via expressjs REST API using whatsapp-web.js
4+
5+
## Contribution
6+
7+
Any features request and contribution are welcome! ^\_^
8+
9+
## Installation & Configuration
10+
11+
### Configuration
12+
13+
1. Make sure you clone this repo first
14+
2. Copy `.env.example` and rename it to `.env`
15+
3. Change the configuration there
16+
4. Don't forget to change the `logo.jpg` in the root directory
17+
18+
### Installation
19+
20+
1. Node.js v20+ , I've setups to 20 in package.json, you can change it but generally it works in Node.js v12 higher
21+
2. pnpm package manager, because why not
22+
3. You can go for docker for an easy setups, or if you deploy it manually you will need to install Google Chrome
23+
4. On an Ubuntu server, you will need to run this command:
24+
25+
```bash
26+
sudo apt-get update && sudo apt-get install -y \
27+
gconf-service \
28+
libgbm-dev \
29+
libasound2 \
30+
libatk1.0-0 \
31+
libc6 \
32+
libcairo2 \
33+
libcups2 \
34+
libdbus-1-3 \
35+
libexpat1 \
36+
libfontconfig1 \
37+
libgcc1 \
38+
libgconf-2-4 \
39+
libgdk-pixbuf2.0-0 \
40+
libglib2.0-0 \
41+
libgtk-3-0 \
42+
libnspr4 \
43+
libpango-1.0-0 \
44+
libpangocairo-1.0-0 \
45+
libstdc++6 \
46+
libx11-6 \
47+
libx11-xcb1 \
48+
libxcb1 \
49+
libxcomposite1 \
50+
libxcursor1 \
51+
libxdamage1 \
52+
libxext6 \
53+
libxfixes3 \
54+
libxi6 \
55+
libxrandr2 \
56+
libxrender1 \
57+
libxss1 \
58+
libxtst6 \
59+
ca-certificates \
60+
fonts-liberation \
61+
libappindicator1 \
62+
libnss3 \
63+
lsb-release \
64+
xdg-utils \
65+
wget
66+
```
67+
68+
5. `cd` into the project directory
69+
6. run `pnpm install`
70+
7. run `pnpm build`
71+
8. run `pnpm start`
72+
9. After that you will need to scan the QR that is printed to the terminal
73+
10. You're basically done, or if you want to be more robust, you can use `pm2` for a better process management
74+
75+
## Endpoints
76+
77+
- [GET] /api/v1/
78+
79+
Response:
80+
81+
```json
82+
{
83+
"message": "REST API is working"
84+
}
85+
```
86+
87+
- [POST][Multipart/form-data] /api/v1/send/
88+
89+
| name | value |
90+
| ------- | ------------ |
91+
| number | 628XXX... |
92+
| content | your message |
93+
94+
Response:
95+
96+
```json
97+
{
98+
"status": "success",
99+
"code": 200,
100+
"message": "Message sucessfully sent",
101+
"data": {
102+
"number": "628XXX...",
103+
"content": "Hi, mom!",
104+
"type": "text"
105+
}
106+
}
107+
```
108+
109+
- [POST][Multipart/form-data] /api/v1/send/media
110+
111+
| name | value |
112+
| ------- | ------------ |
113+
| number | 628XXX... |
114+
| content | your message |
115+
| file | binary file |
116+
117+
Response:
118+
119+
```json
120+
{
121+
"status": "success",
122+
"code": 200,
123+
"message": "Message sucessfully sent",
124+
"data": {
125+
"number": "628XXX...",
126+
"content": "this is your media caption",
127+
"type": "media"
128+
}
129+
}
130+
```
131+
132+
### Error response
133+
134+
```json
135+
{
136+
"status": "error",
137+
"code": 400,
138+
"message": "Bad Image"
139+
}
140+
```
141+
142+
#### Error code
143+
144+
| Code | Status |
145+
| ---- | --------------------- |
146+
| 200 | SUCCESS |
147+
| 201 | CREATED |
148+
| 204 | NO CONTENT |
149+
| 400 | BAD REQUEST |
150+
| 401 | UNAUTHORIZED |
151+
| 403 | FORBIDDEN |
152+
| 404 | NOT FOUND |
153+
| 408 | TIME OUT |
154+
| 429 | TOO MANY REQUEST |
155+
| 500 | INTERNAL SERVER ERROR |
156+
| 503 | SERVICE UNAVAILABLE |

app.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import compression from "compression";
2+
import express from "express";
3+
import helmet from "helmet";
4+
import { CacheMiddleware } from "./middlewares";
5+
import router from "./routes";
6+
import { JSON_OPTIONS, URL_ENCODE_OPTIONS } from "./utils/constant.util";
7+
8+
const app = express();
9+
10+
// Middlewares
11+
app.disable("x-powered-by");
12+
app.set("trust proxy", 1);
13+
14+
app.use(compression());
15+
app.use(express.urlencoded(URL_ENCODE_OPTIONS));
16+
app.use(helmet());
17+
18+
app.use(express.json(JSON_OPTIONS));
19+
app.use(CacheMiddleware);
20+
21+
// Routes
22+
app.use("/api/v1", router);
23+
24+
export { app };

controllers/sender.controller.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { unlinkSync, writeFileSync } from "fs";
2+
import { tmpdir } from "os";
3+
import { join } from "path";
4+
5+
import { client } from "..";
6+
7+
import { NextFunction, Request, Response } from "../interfaces/base.interface";
8+
import { STATUS } from "../utils/constant.util";
9+
import { BadRequestError } from "../utils/error.util";
10+
import { Helper } from "../utils/helper.util";
11+
12+
export const sendMsg = async (
13+
req: Request,
14+
res: Response,
15+
next: NextFunction
16+
) => {
17+
try {
18+
const { content, number } = req.body;
19+
const helper = new Helper();
20+
21+
if (!content) throw new BadRequestError("No message content provided!");
22+
if (!helper.isValidPhoneNumber(number))
23+
throw new BadRequestError("Phone number is not valid! Format: 62...");
24+
25+
client.sendMsg(content, number);
26+
27+
const result = {
28+
status: "success",
29+
code: 200,
30+
message: "Message sucessfully sent",
31+
data: {
32+
number,
33+
content,
34+
type: "text",
35+
},
36+
};
37+
res.status(STATUS.SUCCESS).json(result);
38+
} catch (error) {
39+
return next(error);
40+
}
41+
};
42+
43+
export const sendMedia = async (
44+
req: Request,
45+
res: Response,
46+
next: NextFunction
47+
) => {
48+
try {
49+
const { content, number } = req.body;
50+
const helper = new Helper();
51+
52+
if (!req.file) throw new BadRequestError("No file were provided!");
53+
if (!helper.isValidPhoneNumber(number))
54+
throw new BadRequestError("Phone number is not valid! Format: 62...");
55+
56+
const tempFilePath = join(tmpdir(), req.file.originalname);
57+
writeFileSync(tempFilePath, req.file.buffer);
58+
59+
client.sendFile(content, number, tempFilePath).then(() => {
60+
unlinkSync(tempFilePath);
61+
});
62+
63+
const result = {
64+
status: "success",
65+
code: 200,
66+
message: "Message sucessfully sent",
67+
data: {
68+
number,
69+
content,
70+
type: "media",
71+
},
72+
};
73+
74+
res.status(STATUS.SUCCESS).json(result);
75+
} catch (error) {
76+
console.log(error);
77+
return next(error);
78+
}
79+
};

index.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import dotenv from "dotenv";
2+
import { app } from "./app";
3+
import { WagateClient } from "./lib/wagate-client";
4+
import logger from "./utils/log.util";
5+
6+
dotenv.config();
7+
const PORT = process.env.PORT || 4321;
8+
9+
logger.info("Starting the server...");
10+
let client: WagateClient;
11+
12+
// Init
13+
app.listen(PORT, async () => {
14+
logger.info("REST API is running on port " + PORT);
15+
client = new WagateClient();
16+
17+
logger.info("Starting the bot...");
18+
await client.init();
19+
});
20+
21+
// Global error catch
22+
process.once("unhandledRejection", async function (reason) {
23+
logger.error(reason);
24+
});
25+
26+
process.once("uncaughtException", async function (err) {
27+
logger.error(err.message);
28+
});
29+
30+
export { client };

0 commit comments

Comments
 (0)