@0xtld/tair-node
Version:
A Node.js package for Tair functionality with configuration, core, and helper modules.
301 lines (253 loc) • 6.9 kB
text/typescript
import axios, { AxiosInstance } from "axios";
import { Utils } from "../utils";
import { logger } from "../config";
interface IMailRestAccount {
address: string;
password: string;
}
enum EMailRestGateway {
MAIL_TM = "https://api.mail.tm",
MAIL_GW = "https://api.mail.gw",
}
interface IMailRestConfig {
gateway: EMailRestGateway;
account: IMailRestAccount;
maximumRetries: number;
}
interface IMailRestArgsGetOtp {
receiptEmail: string;
extractOtp: (message: IMailRestMessage) => Promise<string>;
configs: { retry: number; isGetDetail?: boolean; isDelete?: boolean };
}
interface IMailRestMessage {
"@id": string;
"@type": string;
"@context": string;
id: string;
accountId: string;
msgid: string;
from: {
name: string;
address: string;
};
to: {
name: string;
address: string;
}[];
subject: string;
intro: string;
seen: boolean;
isDeleted: boolean;
hasAttachments: boolean;
size: number;
downloadUrl: string;
createdAt: string;
updatedAt: string;
// for attributes of message detail
text: string;
html: [string];
}
interface IMailRestDomain {
id: string;
domain: string;
isActive: boolean;
createdAt: string;
updatedAt: string;
}
/**
* @description MailRest class
*/
class MailRest {
/**
* @description jwt token of the account
*/
token: string | null = null;
axios: AxiosInstance;
configs: IMailRestConfig;
domains: IMailRestDomain[] = [];
constructor(configs: IMailRestConfig) {
this.configs = {
...configs,
maximumRetries: configs.maximumRetries || 20,
};
this.axios = axios.create({
baseURL: this.configs.gateway,
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
},
});
this.axios.interceptors.response.use(
(response) => {
const data = response.data || {};
if (data["hydra:member"]) {
return data["hydra:member"];
}
return data;
},
(error) => {
return Promise.reject(error);
}
);
}
setAccount(account: IMailRestAccount) {
this.configs.account = account;
}
async setAccountAndLogin(account: IMailRestAccount) {
this.token = null;
this.setAccount(account);
await this.login();
}
async getDomains(): Promise<IMailRestDomain[]> {
const data = await this.axios.get("/domains");
this.domains = data as unknown as IMailRestDomain[];
return this.domains;
}
/**
* @description Create an account
* @param account IMailRestAccount
* @param configs { createAndSet?: boolean }
* @returns Promise<IMailRestAccount>
*/
async createAccount(
account: IMailRestAccount,
configs: { createAndSet?: boolean } = { createAndSet: false }
) {
if (this.domains.length === 0) {
await this.getDomains();
}
const domain = Utils.shuffleArray(this.domains)[0];
if (domain === undefined) {
throw new Error("No domain available");
}
if (Utils.isEmail(account.address)) {
account.address = account.address.split("@")[0] + "@" + domain.domain;
}
logger.debug(`[${MailRest.name}] Creating account with email: ${account.address}`);
account = await this.axios.post("/accounts", account);
logger.debug(`[${MailRest.name}] Account created successfully`);
if (configs.createAndSet) {
this.setAccount(account);
}
return account;
}
async beforeRequest() {
if (this.token === null) {
await this.login();
}
}
async login() {
const body = {
address: this.configs.account.address,
password: this.configs.account.password,
};
const data = await this.axios.post("/token", body, {
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
});
this.token = (data as any).token;
}
/**
*
* @returns Promise<IMailRestMessage[]>
*/
async messages(): Promise<IMailRestMessage[]> {
await this.beforeRequest();
const url = "messages?page=1";
return await this.axios.get(url, {
headers: {
"Content-Type": "application/json",
Accept: "application/json",
Authorization: `Bearer ${this.token}`,
},
});
}
async deleteMessage(id: string) {
await this.beforeRequest();
const url = `messages/${id}`;
return await this.axios.delete(url, {
headers: {
"Content-Type": "application/json",
Accept: "application/json",
Authorization: `Bearer ${this.token}`,
},
});
}
async getMessage(id: string): Promise<IMailRestMessage> {
await this.beforeRequest();
const url = `messages/${id}`;
return await this.axios.get(url, {
headers: {
"Content-Type": "application/json",
Accept: "application/json",
Authorization: `Bearer ${this.token}`,
},
});
}
/**
* @description Truncate all messages
*/
async truncateAllMessages() {
const messages = await this.messages();
for (const message of messages) {
await this.deleteMessage(message.id);
logger.debug("Message deleted successfully", message.id);
}
}
/**
* @description Get OTP from email
* @param args IMailRestArgsGetOtp
* @returns Promise<string | null>
* */
async getOTP(args: IMailRestArgsGetOtp): Promise<string | null> {
const messages = await this.messages();
const { receiptEmail, extractOtp, configs } = args;
let message = messages.find((message) => {
if (Array.isArray(message.to)) {
return message.to.find((to) => to.address === receiptEmail);
}
});
const { isGetDetail, isDelete } = configs;
let retry = configs.retry;
if (!message) {
if (retry < this.configs.maximumRetries) {
logger.debug(`[${MailRest.name}] Retrying getOTP ..... in ${retry + 1} seconds`);
await Utils.sleep(retry + 1);
return await this.getOTP({
receiptEmail,
extractOtp,
configs: { retry: retry + 1, isGetDetail, isDelete },
});
}
return null;
}
if (isGetDetail) {
try {
message = await this.getMessage(message.id);
} catch (e) {
console.error(`[${MailRest.name}] Error getting message`, e);
}
}
if (isDelete) {
try {
await this.deleteMessage(message.id);
logger.debug("Message deleted successfully", message.id);
} catch (e) {
console.error(`[${MailRest.name}] Error deleting message`, e);
}
}
return extractOtp(message);
}
}
export {
MailRest,
IMailRestConfig,
IMailRestMessage,
EMailRestGateway,
IMailRestAccount,
IMailRestArgsGetOtp,
};