@drptbl/mailsac
Version:
Mailsac API Library
490 lines (449 loc) • 19.3 kB
text/typescript
import axios, { AxiosInstance } from "axios";
import { ErrorCodes, IMailsacError } from "./JsonError";
import { IAddress, IForwardOptions, IOwned } from "./models/Address";
import { IAttachment } from "./models/Attachment";
import {
FolderTypes, IFolderResponse, IHeadersResponse, IInboxOptions,
IInboxResponse, ILabelsResponse,
IMessage, IReadResponse, ISearchResponse, ISendMessageOptions,
} from "./models/Message";
import { ISignOutResult, IUser, IUserStats } from "./models/User";
export interface ICountResponse {
_id: string;
n: number;
}
/**
* Querystring Param Format Description
* startDate date (UTC) Required - Limit results to inboxes that received messages after this date.
* endDate date (UTC) Required - Limit results to inboxes that received messages before this date.
* limit integer Optional - Limit results to this many, default 20, max 1000.
* skip integer Optional - Skip this many, default 0.
*/
export interface IQueryOptions {
startDate: Date;
endDate: Date;
limit?: number;
skip?: number;
}
export class Client {
public apiKey?: string;
private axios: AxiosInstance;
constructor(apiKey?: string) {
this.apiKey = apiKey;
this.axios = axios.create({
baseURL: "https://mailsac.com/api/",
// headers: {
// "Mailsac-Key": this.apiKey ? this.apiKey : "",
// },
timeout: 30000,
});
}
/**
* Get an array of private inbox address objects for the account.
* @return {Promise<IAddress[]>} address list
*/
public getPrivateAddresses(): Promise<IAddress[]> {
return this.request<IAddress[]>("/addresses");
}
/**
* Get a single address object.
* Returns an object if owned by the user or not owned.
* Returns 401 if owned by other user.
* @param {string} email email
* @return {Promise<IAddress>} Address
*/
public getAddress(email: string): Promise<IAddress> {
return this.request<IAddress>(`/addresses/${email}`);
}
/**
* Check if an address is private (AKA owned).
* @param {string} email email
* @return {Promise<IOwned>} owned
*/
public checkAddressOwnership(email: string): Promise<IOwned> {
return this.request<IOwned>(`/addresses/${email}/availability`);
}
/**
* Reserve ownership of a private email address.
* No POST body is required.
* Returns 200 if successfully reserves the address.
* Returns 401 if owned by other user.
* Returns 400 if it is already owned by the user.
* @param {string} email email
* @return {Promise<IAddress>} address
*/
public reserveAddress(email: string): Promise<IAddress> {
return this.request<IAddress>(`/addresses/${email}`, "post");
}
/**
* Release ownership of a private address.
* Returns 200 if successfully releases the address.
* Returns 401 if owned by other user.
* Returns 400 if it is not owned.
* @param {string} email email
* @return {Promise<void>} void
*/
public releaseAddress(email: string): Promise<void> {
return this.request(`/addresses/${email}`, "delete");
}
/**
* For a privately owned address email, set it to forward to another email.
* @param {string} email email
* @return {Promise<void>} void
*/
public forwardAddress(email: string, options: IForwardOptions): Promise<void> {
return this.request(`/private-address-forwarding/${email}`, "put", { data: options });
}
/**
* This is how to check the mail. Get a list of messages for the :email address.
* The objects are abbreviated to provide basic metadata.
* @param {string} email email
* @return {Promise<IMessage[]>} message list
*/
public getMessages(email: string): Promise<IMessage[]> {
return this.request<IMessage[]>(`/addresses/${email}/messages`);
}
/**
* Get a list of messages that have been saved and made private for the user.
* @return {Promise<IMessage[]>} message list
*/
public getSavedMessages(): Promise<IMessage[]> {
return this.request<IMessage[]>("/addresses/starred/messages");
}
/**
* Get an individual message.
* @param {string} email email address
* @param {string} messageId the Mailsac Message._id
* @return {Promise<IMessage>} message
*/
public getMessage(email: string, messageId: string): Promise<IMessage> {
return this.request<IMessage>(`/addresses/${email}/messages/${messageId}`);
}
/**
* This is how to check the mail across all of an account's private email addresses.
* @param {IInboxOptions} options IInboxOptions
* @return {Promise<IInboxResponse>} response
*/
public getPrivateMessages(options?: IInboxOptions): Promise<IInboxResponse> {
return this.request<IInboxResponse>("/inbox");
}
/**
* Search for messages in private address. query matches the to, from, and subject.
* @param {string} query query
* @return {Promise<ISearchResponse>} response
*/
public searchPrivateMessages(query: string): Promise<ISearchResponse> {
return this.request<ISearchResponse>("/inbox-search", "get", { params: { query } });
}
/**
* Toggle starred status so it will not be automatically removed.
* There is no PUT body.
* It returns only the message metadata.
* @param {string} email email
* @param {string} messageId messageId
* @return {Promise<IMessage>} message
*/
public saveMessage(email: string, messageId: string): Promise<IMessage> {
return this.request<IMessage>(`/addresses/${email}/messages/${messageId}/star`, "put");
}
/**
* Set labels on a message.
* Any existing labels will be replaced with the new array of string labels.
* Maximum 3 labels per message.
* @param {string} email email
* @param {string} messageId messageId
* @param {string[]} labels labels list
* @return {Promise<ILabelsResponse>} response
*/
public setMessageLabels(email: string, messageId: string, labels: string[]): Promise<ILabelsResponse> {
return this.request<ILabelsResponse>(`/addresses/${email}/messages/${messageId}/labels`,
"put", { data: { labels } });
}
/**
* Move the message to a different mail folder.
* No PUT body is needed.
* No other folders can be added. To organize mail, use labels.
* @param {string} email email
* @param {string} messageId messageId
* @param {FolderTypes} folder folder
* @return {Promise<IFolderResponse>} response
*/
public changeMessageFolder(email: string, messageId: string, folder: FolderTypes): Promise<IFolderResponse> {
return this.request<IFolderResponse>(`/addresses/${email}/messages/${messageId}/folder/${folder}`, "put");
}
/**
* Change the read state of a message. No PUT body is needed.
* Pass read as true to mark the message as read, and false to mark it as unread. The default is false - unread.
* @param {string} email email
* @param {string} messageId messageId
* @param {boolean} read read
* @return {Promise<IReadResponse>} response
*/
public setMessageRead(email: string, messageId: string, read: boolean = false): Promise<IReadResponse> {
return this.request<IReadResponse>(`/addresses/${email}/messages/${messageId}/read/${read}`, "put");
}
/**
* Permanently removes a message. There is no history or trash bin.
* @param {string} email email
* @param {string} messageId messageId
* @return {Promise<void>} void
*/
public deleteMessage(email: string, messageId: string): Promise<void> {
return this.request(`/addresses/${email}/messages/${messageId}`, "delete");
}
/**
* Get the SMTP headers from an email message.
* Use the querystring param ?download=1 to trigger file download in browser.
* @param {string} email email
* @param {string} messageId messageId
* @param {[type]} download=false download
* @return {Promise<IHeadersResponse>} void
*/
public getMessageHeaders(email: string, messageId: string, download = false): Promise<IHeadersResponse> {
return this.request<IHeadersResponse>(`/headers/${email}/${messageId}`,
"get", download ? { params: { download: 1 } } : undefined);
}
/**
* Get safe HTML from an email message. Scripts, images and links are stripped out.
* Use the querystring param ?download=1 to trigger file download in browser.
* @param {string} email email
* @param {string} messageId messageId
* @param {[type]} download=false download
* @return {Promise<string>} response
*/
public getSanitizedMessage(email: string, messageId: string, download = false): Promise<string> {
return this.request<string>(`/body/${email}/${messageId}`, "get",
download ? { params: { download: 1 } } : undefined);
}
/**
* Get a message's HTML content.
* Attached images are inlined and nothing has been stripped.
* Use the querystring param ?download=1 to trigger file download in browser.
* @param {string} email email
* @param {string} messageId messageId
* @param {[type]} download=false download
* @return {Promise<string>} response
*/
public getHTMLMessage(email: string, messageId: string, download = false): Promise<string> {
return this.request<string>(`/dirty/${email}/${messageId}`, "get",
download ? { params: { download: 1 } } : undefined);
}
/**
* Get a message's text content.
* Use the querystring param ?download=1 to trigger file download in browser.
* @param {string} email email
* @param {string} messageId messageId
* @param {[type]} download=false download
* @return {Promise<string>} response
*/
public getMessageText(email: string, messageId: string, download = false): Promise<string> {
return this.request<string>(`/text/${email}/${messageId}`, "get",
download ? { params: { download: 1 } } : undefined);
}
/**
* The entire original SMTP message transport message.
* Use the querystring param ?download=1 to trigger file download in browser.
* @param {string} email email
* @param {string} messageId messageId
* @param {[type]} download=false download
* @return {Promise<string>} response
*/
public getRawMessage(email: string, messageId: string, download = false): Promise<string> {
return this.request<string>(`/raw/${email}/${messageId}`, "get",
download ? { params: { download: 1 } } : undefined);
}
/**
* Send transactional text-only email messages.
* Outgoing message credits must be purchased first.
* One credit will be used per recipient (as opposed to per email).
* Either text or html is required to send a message.
* When passing a raw SMTP package it should be a full SMTP message.
* All required fields below must be included in the raw package
* @param {ISendMessageOptions} options options
* @return {Promise<ISendMessageOptions>} response
*/
public sendMessage(options: ISendMessageOptions): Promise<ISendMessageOptions> {
return this.request<ISendMessageOptions>(`/outgoing-messages`, "post", { data: options });
}
/**
* Get Current User
* @return {Promise<IUser>} user
*/
public getCurrentUser(): Promise<IUser> {
return new Promise(async (resolve, reject) => {
const res = await this.request<IUser>(`/me`);
if ((res as any) === "") {
return reject("You have to be authenticated");
}
return resolve(res);
});
}
/**
* Get information about non-owned addresses with starred
* messages and total starred messages, and list of owned addresses.
* @return {Promise<IUserStats>} stats
*/
public getCurrentUserStats(): Promise<IUserStats> {
return this.request<IUserStats>(`/me/stats`);
}
/**
* Destroy the logged in user's session.
* No POST body.
* For cookie auth which works on the website only.
* @return {Promise<ISignOutResult>} result
*/
public signOut(): Promise<ISignOutResult> {
return this.request<ISignOutResult>(`/auth/logout`, "post");
}
/**
* List the metadata for all attachments on a message.
* @param {string} email email
* @param {string} messageId messageId
* @return {Promise<IAttachment[]>} attachments list
*/
public getAttachments(email: string, messageId: string): Promise<IAttachment[]> {
return this.request<IAttachment[]>(`/addresses/${email}/messages/${messageId}/attachments`);
}
/**
* Download an attachment on a message.
* The Content-Type will be set to the contentType field from the attachment metadata.
* @param {string} email email
* @param {string} messageId messageId
* @param {string} attachmentId attachmentId
* @return {Promise<void>} void
*/
public downloadAttachment(email: string, messageId: string, attachmentId: string): Promise<void> {
return this.request<void>(`/addresses/${email}/messages/${messageId}/attachments/${attachmentId}`);
}
/**
* Search for attachments that were received during the requested time period.
* Limited to non-private inboxes.
* @param {IQueryOptions} options options
* Field Description
* id MD5 hash of the attachment file
* n count of messages with this attachment
* @return {Promise<ICountResponse[]>} common attachments list
*/
public getCommonAttachments(options: IQueryOptions): Promise<ICountResponse[]> {
return this.request<ICountResponse[]>(`/mailstats/common-attachments`, "get", { params: options });
}
/**
* Count the number of email messages that have attachments with this MD5 sum.
* Limited to non-private inboxes.
* @param {string} md5 md5
* @return {Promise<number>} number
*/
public countMD5Attachments(md5: string): Promise<number> {
return new Promise((resolve, reject) => {
this.request<any>(`/mailstats/common-attachments/${md5}/count`)
.then((response) => resolve(response.n))
.catch((err) => reject(err));
});
}
/**
* List the email messages that have attachments with the requested MD5 sum.
* Limited to non-private inboxes.
* @param {string} md5 MD5
* @return {Promise<IMessage[]>} messages
*/
public getMessagesWitchAttachment(md5: string): Promise<IMessage[]> {
return this.request<IMessage[]>(`/mailstats/common-attachments/${md5}`);
}
/**
* Download an attachment with the MD5 sum requested.
* Warning: attachments may contain viruses!
* @param {string} md5 MD5
* @return {Promise<void>} void
*/
public downloadCommonAttachment(md5: string): Promise<void> {
return this.request<void>(`/mailstats/common-attachments/${md5}/download`);
}
/**
* Search for the top non-private addresses that have been receiving mail.
* @param {IQueryOptions} options options
* @return {Promise<ICountResponse>} response
*/
public getTopAddresses(options: IQueryOptions): Promise<ICountResponse> {
return this.request<ICountResponse>(`/mailstats/top-addresses`);
}
/**
* Get an array of domains and IP addresses that have been
* blacklisted for violating the terms of service and/or degrading the service experience for other customers.
* @return {Promise<string[]>} blacklisted
*/
public getBlacklistedDomainsAndIps(): Promise<string[]> {
return this.request<string[]>(`/mailstats/blacklist`);
}
/**
* Check if a domain or IP is on the blacklist.
* Returns 404 if not blacklisted.
* Returns 200 if blacklisted.
* @param {string} domainOrIP domainOrIP
* @return {Promise<boolean>} blacklisted
*/
public checkBlacklist(domainOrIP: string): Promise<boolean> { // TODO: finish this
return new Promise((resolve, reject) => {
this.request(`/mailstats/blacklist/${domainOrIP}`)
.then((r) => console.log(r))
.catch((r) => console.log(r));
return resolve(true);
});
}
private request<T>(url: string, method = "get", config?: object): Promise<T> {
return new Promise((resolve, reject) => {
const request: any = { ...{ method, url }, ...config };
if (method === "get") {
request.params = { ...request.params, ...{ _mailsacKey: this.apiKey ? this.apiKey : "" } };
} else {
request.data = { ...request.data, ...{ _mailsacKey: this.apiKey ? this.apiKey : "" } };
}
this.axios.request(request)
.then((response) => resolve(response.data as T))
.catch((error) => {
if (error.response) {
let details;
switch (error.response.status) {
case ErrorCodes.BAD_REQUEST:
details = "Fix the validation error and try the request again";
break;
case ErrorCodes.UNAUTHORIZED:
details = "Your API key is wrong or you are requesting something that belongs to someone else";
break;
case ErrorCodes.FORBIDDEN:
details = "No matter how you repeat the request, it will not succeed with those credentials";
break;
case ErrorCodes.NOT_FOUND:
details = "The requested resource does not exist, or no longer exists, or the URL route is wrong";
break;
case ErrorCodes.TEAPOT:
details = "Short and stout";
break;
case ErrorCodes.RELAX:
details = "Chill";
break;
case ErrorCodes.TOO_MANY_REQUESTS:
details = "Please slow down your requests";
break;
case ErrorCodes.INTERNAL_SERVER_ERROR:
details =
"We had a problem with our server and have been notified via our monitoring - try again later";
break;
case ErrorCodes.SERVICE_UNAVAILABLE:
details = "We're temporarially offline for maintanance - try again later";
break;
default:
details = "Default error message";
break;
}
return reject({
details,
message: error.response.data.message ? error.response.data.message : error.response.data.error,
status: error.response.status,
} as IMailsacError);
} else {
return reject(error.message);
}
});
});
}
}