Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
snowfluke committed Mar 14, 2024
0 parents commit fdbf04b
Show file tree
Hide file tree
Showing 26 changed files with 3,899 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
DISPLAY_NAME="YOUR BOT DISPLAY NAME"
ADMIN_NUMBER="628XXX"
NODE_ENV=production
PORT=4321
18 changes: 18 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
"overrides": [],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": ["@typescript-eslint", "simple-import-sort"],
"rules": {
"simple-import-sort/imports": "error",
"simple-import-sort/exports": "error"
}
}
22 changes: 22 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# build output
dist/

# dependencies
node_modules/

# logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*

# auth
.wwebjs_auth

# environment variables
.env
.env.production

# macOS-specific files
.DS_Store
56 changes: 56 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
FROM node:20

RUN apt-get update && apt-get install -y \
gconf-service \
libgbm-dev \
libasound2 \
libatk1.0-0 \
libc6 \
libcairo2 \
libcups2 \
libdbus-1-3 \
libexpat1 \
libfontconfig1 \
libgcc1 \
libgconf-2-4 \
libgdk-pixbuf2.0-0 \
libglib2.0-0 \
libgtk-3-0 \
libnspr4 \
libpango-1.0-0 \
libpangocairo-1.0-0 \
libstdc++6 \
libx11-6 \
libx11-xcb1 \
libxcb1 \
libxcomposite1 \
libxcursor1 \
libxdamage1 \
libxext6 \
libxfixes3 \
libxi6 \
libxrandr2 \
libxrender1 \
libxss1 \
libxtst6 \
ca-certificates \
fonts-liberation \
libappindicator1 \
libnss3 \
lsb-release \
xdg-utils \
wget

RUN npm install -g pnpm

WORKDIR /app

COPY . .

RUN pnpm install

RUN pnpm run build

EXPOSE 3000

CMD ["pnpm", "start"]
156 changes: 156 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# WA-GATE

A very simple typescript whatsapp gateway via expressjs REST API using whatsapp-web.js

## Contribution

Any features request and contribution are welcome! ^\_^

## Installation & Configuration

### Configuration

1. Make sure you clone this repo first
2. Copy `.env.example` and rename it to `.env`
3. Change the configuration there
4. Don't forget to change the `logo.jpg` in the root directory

### Installation

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
2. pnpm package manager, because why not
3. You can go for docker for an easy setups, or if you deploy it manually you will need to install Google Chrome
4. On an Ubuntu server, you will need to run this command:

```bash
sudo apt-get update && sudo apt-get install -y \
gconf-service \
libgbm-dev \
libasound2 \
libatk1.0-0 \
libc6 \
libcairo2 \
libcups2 \
libdbus-1-3 \
libexpat1 \
libfontconfig1 \
libgcc1 \
libgconf-2-4 \
libgdk-pixbuf2.0-0 \
libglib2.0-0 \
libgtk-3-0 \
libnspr4 \
libpango-1.0-0 \
libpangocairo-1.0-0 \
libstdc++6 \
libx11-6 \
libx11-xcb1 \
libxcb1 \
libxcomposite1 \
libxcursor1 \
libxdamage1 \
libxext6 \
libxfixes3 \
libxi6 \
libxrandr2 \
libxrender1 \
libxss1 \
libxtst6 \
ca-certificates \
fonts-liberation \
libappindicator1 \
libnss3 \
lsb-release \
xdg-utils \
wget
```

5. `cd` into the project directory
6. run `pnpm install`
7. run `pnpm build`
8. run `pnpm start`
9. After that you will need to scan the QR that is printed to the terminal
10. You're basically done, or if you want to be more robust, you can use `pm2` for a better process management

## Endpoints

- [GET] /api/v1/

Response:

```json
{
"message": "REST API is working"
}
```

- [POST][Multipart/form-data] /api/v1/send/

| name | value |
| ------- | ------------ |
| number | 628XXX... |
| content | your message |

Response:

```json
{
"status": "success",
"code": 200,
"message": "Message sucessfully sent",
"data": {
"number": "628XXX...",
"content": "Hi, mom!",
"type": "text"
}
}
```

- [POST][Multipart/form-data] /api/v1/send/media

| name | value |
| ------- | ------------ |
| number | 628XXX... |
| content | your message |
| file | binary file |

Response:

```json
{
"status": "success",
"code": 200,
"message": "Message sucessfully sent",
"data": {
"number": "628XXX...",
"content": "this is your media caption",
"type": "media"
}
}
```

### Error response

```json
{
"status": "error",
"code": 400,
"message": "Bad Image"
}
```

#### Error code

| Code | Status |
| ---- | --------------------- |
| 200 | SUCCESS |
| 201 | CREATED |
| 204 | NO CONTENT |
| 400 | BAD REQUEST |
| 401 | UNAUTHORIZED |
| 403 | FORBIDDEN |
| 404 | NOT FOUND |
| 408 | TIME OUT |
| 429 | TOO MANY REQUEST |
| 500 | INTERNAL SERVER ERROR |
| 503 | SERVICE UNAVAILABLE |
24 changes: 24 additions & 0 deletions app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import compression from "compression";
import express from "express";
import helmet from "helmet";
import { CacheMiddleware } from "./middlewares";
import router from "./routes";
import { JSON_OPTIONS, URL_ENCODE_OPTIONS } from "./utils/constant.util";

const app = express();

// Middlewares
app.disable("x-powered-by");
app.set("trust proxy", 1);

app.use(compression());
app.use(express.urlencoded(URL_ENCODE_OPTIONS));
app.use(helmet());

app.use(express.json(JSON_OPTIONS));
app.use(CacheMiddleware);

// Routes
app.use("/api/v1", router);

export { app };
79 changes: 79 additions & 0 deletions controllers/sender.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { unlinkSync, writeFileSync } from "fs";
import { tmpdir } from "os";
import { join } from "path";

import { client } from "..";

import { NextFunction, Request, Response } from "../interfaces/base.interface";
import { STATUS } from "../utils/constant.util";
import { BadRequestError } from "../utils/error.util";
import { Helper } from "../utils/helper.util";

export const sendMsg = async (
req: Request,
res: Response,
next: NextFunction
) => {
try {
const { content, number } = req.body;
const helper = new Helper();

if (!content) throw new BadRequestError("No message content provided!");
if (!helper.isValidPhoneNumber(number))
throw new BadRequestError("Phone number is not valid! Format: 62...");

client.sendMsg(content, number);

const result = {
status: "success",
code: 200,
message: "Message sucessfully sent",
data: {
number,
content,
type: "text",
},
};
res.status(STATUS.SUCCESS).json(result);
} catch (error) {
return next(error);
}
};

export const sendMedia = async (
req: Request,
res: Response,
next: NextFunction
) => {
try {
const { content, number } = req.body;
const helper = new Helper();

if (!req.file) throw new BadRequestError("No file were provided!");
if (!helper.isValidPhoneNumber(number))
throw new BadRequestError("Phone number is not valid! Format: 62...");

const tempFilePath = join(tmpdir(), req.file.originalname);
writeFileSync(tempFilePath, req.file.buffer);

client.sendFile(content, number, tempFilePath).then(() => {
unlinkSync(tempFilePath);
});

const result = {
status: "success",
code: 200,
message: "Message sucessfully sent",
data: {
number,
content,
type: "media",
},
};

res.status(STATUS.SUCCESS).json(result);
} catch (error) {
console.log(error);
return next(error);
}
};
30 changes: 30 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import dotenv from "dotenv";
import { app } from "./app";
import { WagateClient } from "./lib/wagate-client";
import logger from "./utils/log.util";

dotenv.config();
const PORT = process.env.PORT || 4321;

logger.info("Starting the server...");
let client: WagateClient;

// Init
app.listen(PORT, async () => {
logger.info("REST API is running on port " + PORT);
client = new WagateClient();

logger.info("Starting the bot...");
await client.init();
});

// Global error catch
process.once("unhandledRejection", async function (reason) {
logger.error(reason);
});

process.once("uncaughtException", async function (err) {
logger.error(err.message);
});

export { client };
Loading

0 comments on commit fdbf04b

Please sign in to comment.