Initial code commit
This commit is contained in:
parent
88e75758af
commit
08f2127864
14 changed files with 1633 additions and 0 deletions
142
src/API.ts
Normal file
142
src/API.ts
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
import axios, { AxiosResponse } from 'axios';
|
||||
|
||||
/**
|
||||
* Type defining the credentials to use when authenticating with the Listmonk API.
|
||||
*/
|
||||
export type APICredentials = {
|
||||
host: string,
|
||||
username: string,
|
||||
password: string
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that provides an interface to the Listmonk API.
|
||||
*/
|
||||
export class API {
|
||||
/** The hostname of the Listmonk instance. */
|
||||
private host: string;
|
||||
/** The username to use when authenticating with Listmonk. */
|
||||
private username: string;
|
||||
/** The password to use when authenticating with Listmonk. */
|
||||
private password: string;
|
||||
|
||||
/**
|
||||
* Create a new Listmonk API instance.
|
||||
*
|
||||
* @param credentials The credentials to use when authenticating with Listmonk.
|
||||
*/
|
||||
constructor(credentials?: APICredentials) {
|
||||
if(typeof credentials === 'undefined') {
|
||||
credentials = this.getDefaultCredentialsFromEnvVars();
|
||||
}
|
||||
|
||||
this.host = credentials.host;
|
||||
this.username = credentials.username;
|
||||
this.password = credentials.password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default credentials for the Listmonk API (as found in the environment variables).
|
||||
*
|
||||
* The table below describes the environment variables that are used:
|
||||
*
|
||||
* | Environment Variable | Description |
|
||||
* | -------------------- | ------------------------------------------------------ |
|
||||
* | LISTMONK_HOST | The hostname of the Listmonk instance. |
|
||||
* | LISTMONK_USERNAME | The username to use when authenticating with Listmonk. |
|
||||
* | LISTMONK_PASSWORD | The password to use when authenticating with Listmonk. |
|
||||
*
|
||||
* @returns The default credentials for the Listmonk API (as found in the environment variables).
|
||||
*/
|
||||
private getDefaultCredentialsFromEnvVars(): APICredentials {
|
||||
if(typeof process.env.LISTMONK_HOST === 'undefined' || process.env.LISTMONK_HOST === null || process.env.LISTMONK_HOST === '') {
|
||||
throw new Error('LISTMONK_HOST is not defined');
|
||||
}
|
||||
|
||||
if(typeof process.env.LISTMONK_USERNAME === 'undefined' || process.env.LISTMONK_USERNAME === null || process.env.LISTMONK_USERNAME === '') {
|
||||
throw new Error('LISTMONK_USERNAME is not defined');
|
||||
}
|
||||
|
||||
if(typeof process.env.LISTMONK_PASSWORD === 'undefined' || process.env.LISTMONK_PASSWORD === null || process.env.LISTMONK_PASSWORD === '') {
|
||||
throw new Error('LISTMONK_PASSWORD is not defined');
|
||||
}
|
||||
|
||||
return {
|
||||
host: process.env.LISTMONK_HOST,
|
||||
username: process.env.LISTMONK_USERNAME,
|
||||
password: process.env.LISTMONK_PASSWORD
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The credentials for the Listmonk API.
|
||||
*/
|
||||
getCredentials(): APICredentials {
|
||||
return {
|
||||
host: this.host,
|
||||
username: this.username,
|
||||
password: this.password
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a GET request to the Listmonk API.
|
||||
*
|
||||
* T (type parameter 1) is the type of the data returned by the API call.
|
||||
*
|
||||
* @param endpoint The endpoint/path to send the GET request to (that is, the URL without the hostname).
|
||||
* @returns A promise that resolves to the results of the API call.
|
||||
*/
|
||||
async get<T = any>(endpoint: string): Promise<T> {
|
||||
const results = await axios.get<{ data: { results: T } }>(endpoint.startsWith('/') ? this.host + '/api' + endpoint : this.host + '/api/' + endpoint, { auth: { username: this.username, password: this.password } })
|
||||
.then(response => { /*console.log(`${endpoint}: ${JSON.stringify(response.data, null, 4)}`);*/ return typeof response.data.data.results !== 'undefined' ? response.data.data.results : (response.data.data as T); });
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a POST request to the Listmonk API.
|
||||
*
|
||||
* T (type parameter 1) is the type of the data returned by the API call.
|
||||
* D (type parameter 2) is the type of the data to send to the API.
|
||||
*
|
||||
* @param endpoint The endpoint/path to send the POST request to (that is, the URL without the hostname).
|
||||
* @param data The data to send to the API.
|
||||
* @returns A promise that resolves to the result of the API call.
|
||||
*/
|
||||
async post<D = any,T = any>(endpoint: string, data: D): Promise<T> {
|
||||
const results = await axios.post<T, AxiosResponse<T,any>,D>(endpoint.startsWith('/') ? this.host + '/api' + endpoint : this.host + '/api/' + endpoint, data, { auth: { username: this.username, password: this.password } }).then(response => response.data);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a PUT request to the Listmonk API.
|
||||
*
|
||||
* T (type parameter 1) is the type of the data returned by the API call.
|
||||
* D (type parameter 2) is the type of the data to send to the API.
|
||||
*
|
||||
* @param endpoint The endpoint/path to send the PUT request to (that is, the URL without the hostname).
|
||||
* @param data The data to send to the API.
|
||||
* @returns A promise that resolves to the result of the API call.
|
||||
*/
|
||||
async put<D = any,T = any>(endpoint: string, data: D): Promise<T> {
|
||||
const results = await axios.put<T, AxiosResponse<T,any>,D>(endpoint.startsWith('/') ? this.host + '/api' + endpoint : this.host + '/api/' + endpoint, data, { auth: { username: this.username, password: this.password } }).then(response => response.data);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a DELETE request to the Listmonk API.
|
||||
*
|
||||
* T (type parameter 1) is the type of the data returned by the API call.
|
||||
*
|
||||
* @param endpoint The endpoint/path to send the DELETE request to (that is, the URL without the hostname).
|
||||
* @returns A promise that resolves to the result of the API call.
|
||||
*/
|
||||
async delete<T = any>(endpoint: string): Promise<T> {
|
||||
const results = await axios.delete<T>(endpoint.startsWith('/') ? this.host + '/api' + endpoint : this.host + '/api/' + endpoint, { auth: { username: this.username, password: this.password } }).then(response => response.data);
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
29
src/APIObject.ts
Normal file
29
src/APIObject.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { API, APICredentials } from "./API";
|
||||
|
||||
export abstract class APIObject {
|
||||
/** The API instance to use for making requests. */
|
||||
protected api: API;
|
||||
|
||||
/**
|
||||
* Create a new API object.
|
||||
*
|
||||
* @param api The API instance to use for making requests. Defaults to a new API instance if not provided.
|
||||
*/
|
||||
protected constructor(credentials?: APICredentials) {
|
||||
this.api = new API(credentials);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the object to a JSON object.
|
||||
*
|
||||
* This is mostly used for API call. But also sometimes for code reuse purposes.
|
||||
*/
|
||||
abstract toJSON(): any;
|
||||
|
||||
/**
|
||||
* Allows the object to be created from data.
|
||||
*
|
||||
* @param data The data to create the object from.
|
||||
*/
|
||||
abstract fromData<T>(data: T): APIObject | Promise<APIObject>;
|
||||
}
|
||||
529
src/Campaign.ts
Normal file
529
src/Campaign.ts
Normal file
|
|
@ -0,0 +1,529 @@
|
|||
import { APIObject } from './APIObject';
|
||||
import { API, APICredentials } from './API';
|
||||
import { List, ListData } from './List';
|
||||
import { Template } from './Template';
|
||||
|
||||
type CampaignData = {
|
||||
id?: number,
|
||||
uuid?: string,
|
||||
name: string,
|
||||
subject: string,
|
||||
lists: number[] | ListData[],
|
||||
type: "regular" | "optin",
|
||||
content_type: 'richtext' | 'html' | 'markdown' | 'plain',
|
||||
body: string,
|
||||
from_email?: string,
|
||||
alt_body?: string,
|
||||
send_at?: string,
|
||||
messenger?: string,
|
||||
template_id?: number,
|
||||
tags?: string[],
|
||||
headers?: { [key: string]: string },
|
||||
status?: "draft" | "scheduled" | "running" | "paused" | "cancelled",
|
||||
created_at?: string,
|
||||
updated_at?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a campaign in Listmonk.
|
||||
*/
|
||||
export class Campaign extends APIObject {
|
||||
/** Identifier for the Campaign */
|
||||
private id?: number;
|
||||
/** Universal Unique Identifier (UUID) for the Campaign */
|
||||
private uuid?: string;
|
||||
/** Campaign name. */
|
||||
private name: string;
|
||||
/** Campaign email subject. */
|
||||
private subject: string;
|
||||
/** Lists to send campaign to. */
|
||||
private lists: List[];
|
||||
/** Campaign type: 'regular' or 'optin'. */
|
||||
private type?: 'regular' | 'optin';
|
||||
/** Content type: `richtext`, `html`, `markdown`, `plain` */
|
||||
private contentType?: 'richtext' | 'html' | 'markdown' | 'plain';
|
||||
/** Content body of campaign. */
|
||||
private campaignBody: string;
|
||||
/** 'From' email in campaign emails. Defaults to value from settings if not provided. */
|
||||
private fromEmail?: string;
|
||||
/** Alternate plain text body for HTML (and richtext) emails. */
|
||||
private altBody?: string;
|
||||
/** Timestamp to schedule campaign. Format: `YYYY-MM-DDTHH:MM:SSZ` */
|
||||
private sendAt?: string;
|
||||
/** `email` or a custom messenger defined in settings. Defaults to `email` if not provided. */
|
||||
private messenger?: string;
|
||||
/** Template to use */
|
||||
private template?: Template;
|
||||
/** Tags to mark campaign. */
|
||||
private tags?: string[];
|
||||
/** Key-value pairs to send as SMTP headers. Example: `[{"x-custom-header": "value"}]`. */
|
||||
private headers?: { [key: string]: string };
|
||||
/**
|
||||
* status for campaign: `draft`, `scheduled`, `running`, `paused`, `cancelled`.
|
||||
*
|
||||
* Note:
|
||||
* - Only `scheduled` campaigns can change status to `draft`.
|
||||
* - Only `draft` campaigns can change status to `scheduled`.
|
||||
* - Only `paused` and `draft` campaigns can start (`running` status).
|
||||
* - Only `running` campaigns can change status to `cancelled` and `paused`.
|
||||
*/
|
||||
private status?: 'draft' | 'scheduled' | 'running' | 'paused' | 'cancelled';
|
||||
/** Timestamp for when the campaign was created. Format: `YYYY-MM-DDTHH:MM:SSZ` */
|
||||
private createdAt?: string;
|
||||
/** Timestamp for when the campaign was last updated. Format: `YYYY-MM-DDTHH:MM:SSZ` */
|
||||
private updatedAt?: string;
|
||||
|
||||
constructor(credentials?: APICredentials, id?: number, uuid?: string, name?: string, subject?: string, lists?: List[], type?: 'regular' | 'optin', contentType?: 'richtext' | 'html' | 'markdown' | 'plain', campaignBody?: string, fromEmail?: string, altBody?: string, sendAt?: string, messenger?: string, template?: Template, tags?: string[], headers?: { [key: string]: string }, status?: 'draft' | 'scheduled' | 'running' | 'paused' | 'cancelled', createdAt?: string, updatedAt?: string) {
|
||||
super(credentials);
|
||||
|
||||
this.id = id;
|
||||
this.uuid = uuid;
|
||||
this.name = typeof name !== 'undefined' ? name : '';
|
||||
this.subject = typeof subject !== 'undefined' ? subject : '';
|
||||
this.lists = typeof lists !== 'undefined' ? lists : [];
|
||||
this.type = type;
|
||||
this.contentType = contentType;
|
||||
this.campaignBody = typeof campaignBody !== 'undefined' ? campaignBody : '';
|
||||
this.fromEmail = fromEmail;
|
||||
this.altBody = altBody;
|
||||
this.sendAt = sendAt;
|
||||
this.messenger = messenger;
|
||||
this.template = template;
|
||||
this.tags = typeof tags !== 'undefined' ? tags : [];
|
||||
this.headers = headers;
|
||||
this.status = status;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Identifier for the Campaign
|
||||
*/
|
||||
getID(): number | undefined {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Universal Unique Identifier (UUID) for the Campaign
|
||||
*/
|
||||
getUUID(): string | undefined {
|
||||
return this.uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Campaign name
|
||||
*/
|
||||
getName(): string {
|
||||
return this.name;
|
||||
}
|
||||
/**
|
||||
* Set the name of the campaign
|
||||
*
|
||||
* @param name The name to set the Campaign name to
|
||||
*/
|
||||
setName(name: string): void {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Campaign email subject
|
||||
*/
|
||||
getSubject(): string {
|
||||
return this.subject;
|
||||
}
|
||||
/**
|
||||
* Set the subject of the campaign
|
||||
*
|
||||
* @param subject The subject to set the Campaign email subject to
|
||||
*/
|
||||
setSubject(subject: string): void {
|
||||
this.subject = subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Lists to send the campaign to
|
||||
*/
|
||||
getLists(): List[] {
|
||||
return this.lists;
|
||||
}
|
||||
/**
|
||||
* Add a list for the campaign to send to
|
||||
*
|
||||
* @param list List to add to the campaign
|
||||
*/
|
||||
addList(list: List): void {
|
||||
this.lists.push(list);
|
||||
}
|
||||
/**
|
||||
* Remove a list from the campaign
|
||||
*
|
||||
* @param list List to remove from the campaign
|
||||
*/
|
||||
removeList(list: List): void {
|
||||
this.lists = this.lists.filter((l: List) => l.getID() !== list.getID());
|
||||
}
|
||||
/**
|
||||
* Check if the campaign includes a list
|
||||
*
|
||||
* @param list The list to check if the campaign includes
|
||||
* @returns A boolean indicating if the campaign includes the list
|
||||
*/
|
||||
includesList(list: List): boolean {
|
||||
return this.lists.some((l: List) => l.getID() === list.getID());
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Campaign type: 'regular' or 'optin'
|
||||
*/
|
||||
getType(): 'regular' | 'optin' | undefined {
|
||||
return this.type;
|
||||
}
|
||||
/**
|
||||
* Set the type of the campaign
|
||||
*
|
||||
* @param type Campaign type: 'regular' or 'optin'
|
||||
*/
|
||||
setType(type: 'regular' | 'optin'): void {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Content type: `richtext`, `html`, `markdown`, `plain`
|
||||
*/
|
||||
getContentType(): 'richtext' | 'html' | 'markdown' | 'plain' | undefined {
|
||||
return this.contentType;
|
||||
}
|
||||
/**
|
||||
* Set the content type of the campaign
|
||||
*
|
||||
* @param contentType Content type: `richtext`, `html`, `markdown`, `plain`
|
||||
*/
|
||||
setContentType(contentType: 'richtext' | 'html' | 'markdown' | 'plain'): void {
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Content body of the campaign
|
||||
*/
|
||||
getCampaignBody(): string {
|
||||
return this.campaignBody;
|
||||
}
|
||||
/**
|
||||
* Set the content body of the campaign
|
||||
*
|
||||
* @param campaignBody The content body to set the campaign body to
|
||||
*/
|
||||
setCampaignBody(campaignBody: string): void {
|
||||
this.campaignBody = campaignBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns 'From' email in campaign emails. Defaults to value from settings if not provided.
|
||||
*/
|
||||
getFromEmail(): string | undefined {
|
||||
return this.fromEmail;
|
||||
}
|
||||
/**
|
||||
* Set the 'From' email in campaign emails
|
||||
*
|
||||
* @param fromEmail The email to set the 'From' email in campaign emails to
|
||||
*/
|
||||
setFromEmail(fromEmail: string): void {
|
||||
this.fromEmail = fromEmail;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Alternate plain text body for HTML (and richtext) emails
|
||||
*/
|
||||
getAltBody(): string | undefined {
|
||||
return this.altBody;
|
||||
}
|
||||
/**
|
||||
* Set the alternate plain text body for HTML (and richtext) emails
|
||||
*
|
||||
* @param altBody The alternate plain text body to set for HTML (and richtext) emails
|
||||
*/
|
||||
setAltBody(altBody: string): void {
|
||||
this.altBody = altBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Timestamp to schedule the campaign. Format: `YYYY-MM-DDTHH:MM:SSZ`
|
||||
*/
|
||||
getSendAt(): string | undefined {
|
||||
return this.sendAt;
|
||||
}
|
||||
/**
|
||||
* Set the timestamp to schedule the campaign
|
||||
*
|
||||
* @param sendAt The timestamp to set to schedule the campaign
|
||||
*/
|
||||
setSendAt(sendAt: string): void {
|
||||
this.sendAt = sendAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns `email` or a custom messenger defined in settings. Defaults to `email` if not provided.
|
||||
*/
|
||||
getMessenger(): string | undefined {
|
||||
return this.messenger;
|
||||
}
|
||||
/**
|
||||
* Set the messenger for the campaign
|
||||
*
|
||||
* @param messenger The messenger to set for the campaign
|
||||
*/
|
||||
setMessenger(messenger: string): void {
|
||||
this.messenger = messenger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ID of the template used for the campaign
|
||||
*
|
||||
* @returns ID of the template used for the campaign or -1 if the template isn't set explicitly
|
||||
*/
|
||||
getTemplateID(): number {
|
||||
return this.getTemplate()?.getID() ?? -1;
|
||||
}
|
||||
/**
|
||||
* @returns The template used for the campaign
|
||||
*/
|
||||
getTemplate(): Template | undefined {
|
||||
return this.template;
|
||||
}
|
||||
/**
|
||||
* Set the template for the campaign
|
||||
*
|
||||
* @param template The template to set for the campaign
|
||||
*/
|
||||
setTemplate(template: Template): void {
|
||||
this.template = template;
|
||||
}
|
||||
/**
|
||||
* Set the template for the campaign by it's ID
|
||||
*
|
||||
* @param templateID The ID of the template to set for the campaign
|
||||
*/
|
||||
setTemplateByID(templateID: number): void {
|
||||
this.template = new Template(this.api.getCredentials(), templateID);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Tags to mark the campaign
|
||||
*/
|
||||
getTags(): string[] | undefined {
|
||||
return this.tags;
|
||||
}
|
||||
/**
|
||||
* Add a tag to the campaign
|
||||
*
|
||||
* @param tag The tag to add to the campaign
|
||||
*/
|
||||
addTag(tag: string): void {
|
||||
if(this.tags === undefined) {
|
||||
this.tags = [];
|
||||
}
|
||||
this.tags.push(tag);
|
||||
}
|
||||
/**
|
||||
* Remove a tag from the campaign
|
||||
*
|
||||
* @param tag The tag to remove from the campaign
|
||||
*/
|
||||
removeTag(tag: string): void {
|
||||
if(this.tags === undefined) {
|
||||
return;
|
||||
}
|
||||
this.tags = this.tags.filter((t: string) => t !== tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Key-value pairs to send as SMTP headers
|
||||
*/
|
||||
getHeaders(): { [key: string]: string } | undefined {
|
||||
return this.headers;
|
||||
}
|
||||
/**
|
||||
* Add a header to the campaign
|
||||
*
|
||||
* @param key The key of the header to add
|
||||
* @param value The value of the header to add
|
||||
*/
|
||||
addHeader(key: string, value: string): void {
|
||||
if(this.headers === undefined) {
|
||||
this.headers = {};
|
||||
}
|
||||
this.headers[key] = value;
|
||||
}
|
||||
/**
|
||||
* Remove a header from the campaign
|
||||
*
|
||||
* @param key The key of the header to remove
|
||||
*/
|
||||
removeHeader(key: string): void {
|
||||
if(this.headers === undefined) {
|
||||
return;
|
||||
}
|
||||
delete this.headers[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Status for the campaign: `draft`, `scheduled`, `running`, `paused`, `cancelled`
|
||||
*/
|
||||
getStatus(): 'draft' | 'scheduled' | 'running' | 'paused' | 'cancelled' | undefined {
|
||||
return this.status;
|
||||
}
|
||||
/**
|
||||
* Set the status for the campaign
|
||||
*
|
||||
* Note:
|
||||
* - Only `scheduled` campaigns can change status to `draft`.
|
||||
* - Only `draft` campaigns can change status to `scheduled`.
|
||||
* - Only `paused` and `draft` campaigns can start (`running` status).
|
||||
* - Only `running` campaigns can change status to `cancelled` and `paused`.
|
||||
*
|
||||
* @param status The status to set for the campaign: `draft`, `scheduled`, `running`, `paused`, `cancelled`
|
||||
*/
|
||||
setStatus(status: 'draft' | 'scheduled' | 'running' | 'paused' | 'cancelled'): void {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Timestamp for when the campaign was created. Format: `YYYY-MM-DDTHH:MM:SSZ`
|
||||
*/
|
||||
getCreatedAt(): string | undefined {
|
||||
return this.createdAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Timestamp for when the campaign was last updated. Format: `YYYY-MM-DDTHH:MM:SSZ`
|
||||
*/
|
||||
getUpdatedAt(): string | undefined {
|
||||
return this.updatedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or updates the campaign in Listmonk.
|
||||
*/
|
||||
async save() {
|
||||
const data = this.toJSON();
|
||||
|
||||
// If the UUID is not set, then we assume we need to create the subscriber in Listmonk
|
||||
if(typeof data.uuid === 'undefined') {
|
||||
// Technically, this might be unnecessary since the values should be set to undefined anyway, but it's good to be safe
|
||||
delete data.id;
|
||||
delete data.uuid;
|
||||
delete data.created_at;
|
||||
delete data.updated_at;
|
||||
delete data.status;
|
||||
|
||||
|
||||
// Make the API call to create the subscriber in Listmonk
|
||||
await this.api.post<CampaignData>('/campaigns', data);
|
||||
}
|
||||
else {
|
||||
await this.api.put<CampaignData>('/campaigns/' + data.id, data);
|
||||
}
|
||||
}
|
||||
|
||||
toJSON(): CampaignData {
|
||||
return {
|
||||
id: this.id,
|
||||
uuid: this.uuid,
|
||||
name: this.name,
|
||||
subject: this.subject,
|
||||
lists: this.lists.map((list: List) => list.getID() as number),
|
||||
type: typeof this.type !== 'undefined' ? this.type : 'regular',
|
||||
content_type: typeof this.contentType !== 'undefined' ? this.contentType : 'plain',
|
||||
body: this.campaignBody,
|
||||
from_email: this.fromEmail,
|
||||
alt_body: this.altBody,
|
||||
send_at: this.sendAt,
|
||||
messenger: this.messenger,
|
||||
template_id: this.template?.getID(),
|
||||
tags: this.tags,
|
||||
headers: this.headers,
|
||||
status: typeof this.status !== 'undefined' ? this.status : 'draft',
|
||||
created_at: this.createdAt,
|
||||
updated_at: this.updatedAt
|
||||
};
|
||||
}
|
||||
|
||||
async fromData<CampaignData>(data: CampaignData): Promise<Campaign> {
|
||||
const typedData = data as {id?: number, uuid?: string, name: string, subject: string, lists: number[] | ListData[], type: "regular" | "optin", content_type: 'richtext' | 'html' | 'markdown' | 'plain', body: string, from_email?: string, alt_body?: string, send_at?: string, messenger?: string, template_id?: number, tags?: string[], headers?: { [key: string]: string }, status: "draft" | "scheduled" | "running" | "paused" | "cancelled", created_at?: string, updated_at?: string };
|
||||
const lists = (
|
||||
await Promise.all(
|
||||
typedData.lists.map(
|
||||
async (listData: Number | ListData) => {
|
||||
if(typeof listData === 'number') {
|
||||
return await List.find((list: List) => list.getID() === listData);
|
||||
}
|
||||
else {
|
||||
return new List().fromData(listData);
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
).filter((list: List | undefined) => typeof list !== 'undefined');
|
||||
|
||||
const template = typeof typedData.template_id !== 'undefined' ? await Template.find((template: Template) => template.getID() === typedData.template_id) : undefined;
|
||||
|
||||
this.id = typedData.id;
|
||||
this.uuid = typedData.uuid;
|
||||
this.setName(typedData.name);
|
||||
this.setSubject(typedData.subject);
|
||||
this.lists = lists;
|
||||
this.setType(typedData.type);
|
||||
this.setContentType(typedData.content_type);
|
||||
this.setCampaignBody(typedData.body);
|
||||
this.fromEmail = typedData.from_email;
|
||||
this.altBody = typedData.alt_body;
|
||||
this.sendAt = typedData.send_at;
|
||||
this.messenger = typedData.messenger;
|
||||
this.template = template;
|
||||
this.tags = typedData.tags;
|
||||
this.headers = typedData.headers;
|
||||
this.setStatus(typedData.status);
|
||||
this.createdAt = typedData.created_at;
|
||||
this.updatedAt = typedData.updated_at;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Campaign object with the given data.
|
||||
*
|
||||
* Note, this doesn't automatically create the campaign in Listmonk. You need to call `save` to do that.
|
||||
*
|
||||
* @param name Name of the new campaign.
|
||||
* @param subject Subject of the new campaign.
|
||||
* @param lists Lists to send the campaign to.
|
||||
* @param type Type of the campaign: 'regular' or 'optin'.
|
||||
* @param contentType Content type: `richtext`, `html`, `markdown`, `plain`
|
||||
* @param body Raw HTML body of the campaign
|
||||
* @returns The new campaign object
|
||||
*/
|
||||
static async create(name: string, subject: string, lists: List[], type: "regular" | "optin", content_type: 'richtext' | 'html' | 'markdown' | 'plain', body: string, credentials?: APICredentials): Promise<Campaign> {
|
||||
return await new Campaign(credentials).fromData({ name, subject, lists: lists.map((list: List) => list.toJSON()), type, content_type, body });
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a campaign based on the given predicate.
|
||||
*
|
||||
* @param predicate A function that takes a campaign and returns true if it matches the desired template.
|
||||
* @param credentials Optional credentials to use to authenticate API requests
|
||||
* @returns The campaign that matches the predicate, or undefined if no campaign matches the predicate.
|
||||
*/
|
||||
static async find(predicate: (campaign: Campaign) => boolean, credentials?: APICredentials): Promise<Campaign | undefined> {
|
||||
return await new API(credentials).get<CampaignData[]>('/campaigns')
|
||||
.then(async (campaigns: CampaignData[]) => {
|
||||
// Convert each CampaignData JSON object to a Campaign object
|
||||
const campaignObjs: Campaign[] = await Promise.all(campaigns.map((campaign: CampaignData) => new Campaign(credentials).fromData(campaign)));
|
||||
|
||||
// Use the predicate function provided to find the desired campaign
|
||||
const campaign = campaignObjs.find(predicate);
|
||||
|
||||
return campaign;
|
||||
});
|
||||
}
|
||||
}
|
||||
131
src/List.ts
Normal file
131
src/List.ts
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
import { APIObject } from "./APIObject";
|
||||
import { API, APICredentials } from "./API";
|
||||
|
||||
export type ListData = {
|
||||
id?: number,
|
||||
uuid?: string,
|
||||
name: string,
|
||||
type: 'public' | 'private',
|
||||
optin?: 'single' | 'double',
|
||||
tags: string[],
|
||||
created_at?: string,
|
||||
updated_at?: string
|
||||
};
|
||||
|
||||
export class List extends APIObject {
|
||||
private id?: number;
|
||||
private uuid?: string;
|
||||
private name: string;
|
||||
private type?: 'public' | 'private';
|
||||
private optin?: 'single' | 'double';
|
||||
private tags: string[];
|
||||
private createdAt?: string;
|
||||
private updatedAt?: string;
|
||||
|
||||
constructor(credentials?: APICredentials, id?: number, uuid?: string, name?: string, type?: 'public' | 'private', optin?: 'single' | 'double', tags?: string[], createdAt?: string, updatedAt?: string) {
|
||||
super(credentials);
|
||||
|
||||
this.id = id;
|
||||
this.uuid = uuid;
|
||||
this.name = typeof name !== 'undefined' ? name : '';
|
||||
this.type = type;
|
||||
this.optin = optin;
|
||||
this.tags = typeof tags !== 'undefined' ? tags : [];
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
getID(): number | undefined {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
getName(): string {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or updates the list in Listmonk.
|
||||
*/
|
||||
async save() {
|
||||
const data = this.toJSON();
|
||||
|
||||
// If the UUID is not set, then we assume we need to create the list in Listmonk
|
||||
if(typeof data.uuid === 'undefined') {
|
||||
// Technically, this might be unnecessary since the values should be set to undefined anyway, but it's good to be safe
|
||||
delete data.id;
|
||||
delete data.created_at;
|
||||
delete data.updated_at;
|
||||
delete data.uuid;
|
||||
|
||||
// Make the API call to create the list in Listmonk
|
||||
await this.api.post<ListData>('/lists', data);
|
||||
} else {
|
||||
// Make the API call to update the list in Listmonk
|
||||
await this.api.post<ListData>('/lists/' + data.uuid, data);
|
||||
}
|
||||
}
|
||||
|
||||
toJSON(): ListData {
|
||||
return {
|
||||
id: this.id,
|
||||
uuid: this.uuid,
|
||||
name: this.name,
|
||||
type: typeof this.type !== 'undefined' ? this.type : 'private',
|
||||
optin: this.optin,
|
||||
tags: this.tags,
|
||||
created_at: this.createdAt,
|
||||
updated_at: this.updatedAt
|
||||
};
|
||||
}
|
||||
|
||||
fromData<ListData>(data: ListData): List {
|
||||
const typedData = data as { id?: number, uuid?: string, name: string, type: 'public' | 'private', optin?: 'single' | 'double', tags: string[], created_at?: string, updated_at?: string };
|
||||
|
||||
this.id = typedData.id;
|
||||
this.uuid = typedData.uuid;
|
||||
this.name = typedData.name;
|
||||
this.type = typedData.type;
|
||||
this.optin = typedData.optin;
|
||||
this.tags = typedData.tags;
|
||||
this.createdAt = typedData.created_at;
|
||||
this.updatedAt = typedData.updated_at;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new List object with the given data.
|
||||
*
|
||||
* Note, this doesn't automatically create the list in Listmonk. You need to call `save` to do that.
|
||||
*
|
||||
* @param name Name of the new list.
|
||||
* @param type Type of list. Options: private, public.
|
||||
* @param optin Opt-in type. Options: single, double.
|
||||
* @param tags Associated tags for a list.
|
||||
* @param credentials Optional credentials to use for the API call.
|
||||
* @returns A new List object with the given data.
|
||||
*/
|
||||
static async create(name: string, type: 'public' | 'private', optin: 'single' | 'double', tags?: string[], credentials?: APICredentials): Promise<List> {
|
||||
return await new List(credentials).fromData({ name, type, optin, tags });
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a list based on a predicate function.
|
||||
*
|
||||
* @param predicate The function to use to find the desired list.
|
||||
* @param credentials Optional The credentials to use for the API call.
|
||||
* @returns The list that matches the predicate, or undefined if no list matches the predicate.
|
||||
*/
|
||||
static async find(predicate: (list: List) => boolean, credentials?: APICredentials): Promise<List | undefined> {
|
||||
return await new API(credentials).get<ListData[]>('/lists')
|
||||
.then((lists: ListData[]) => {
|
||||
// Convert each LIstData JSOn object to a List object
|
||||
const listObjs: List[] = lists.map((list: ListData) => new List(credentials).fromData(list));
|
||||
|
||||
// Use the predicate function provided to find the desired list
|
||||
const list = listObjs.find(predicate);
|
||||
|
||||
return list;
|
||||
});
|
||||
}
|
||||
}
|
||||
47
src/ListMembership.ts
Normal file
47
src/ListMembership.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import { APIObject } from './APIObject';
|
||||
import { APICredentials } from './API';
|
||||
import { List, ListData } from './List';
|
||||
|
||||
export type ListMembershipData = {
|
||||
subscription_status: 'unconfirmed' | 'confirmed',
|
||||
[key: string]: any
|
||||
};
|
||||
|
||||
export class ListMembership extends APIObject {
|
||||
private subscriptionStatus?: 'unconfirmed' | 'confirmed';
|
||||
private list?: List;
|
||||
|
||||
constructor(credentials?: APICredentials, subscriptionStatus?: 'unconfirmed' | 'confirmed', list?: List) {
|
||||
super(credentials);
|
||||
|
||||
this.subscriptionStatus = subscriptionStatus;
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
getSubscriptionStatus(): 'unconfirmed' | 'confirmed' | undefined {
|
||||
return this.subscriptionStatus;
|
||||
}
|
||||
|
||||
getList(): List | undefined {
|
||||
return this.list;
|
||||
}
|
||||
|
||||
toJSON(): ListMembershipData {
|
||||
return {
|
||||
subscription_status: typeof this.subscriptionStatus !== 'undefined' ? this.subscriptionStatus : 'unconfirmed',
|
||||
...this.list?.toJSON()
|
||||
};
|
||||
}
|
||||
|
||||
fromData<ListMembershipData>(data: ListMembershipData): ListMembership {
|
||||
// NOTE types are weird here because TypeScript was complaining otherwise
|
||||
const subscriptionStatus = (data as { subscription_status: 'unconfirmed' | 'confirmed', [key: string]: any }).subscription_status;
|
||||
delete (data as { subscription_status?: 'unconfirmed' | 'confirmed', [key: string]: any }).subscription_status;
|
||||
const list = new List().fromData(data as ListData);
|
||||
|
||||
this.subscriptionStatus = subscriptionStatus;
|
||||
this.list = list;
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
216
src/Subscriber.ts
Normal file
216
src/Subscriber.ts
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
import { APIObject } from "./APIObject";
|
||||
import { API, APICredentials } from "./API";
|
||||
import { List } from "./List";
|
||||
import { ListMembership, ListMembershipData } from "./ListMembership";
|
||||
|
||||
type SubscriberData = {
|
||||
id?: number,
|
||||
uuid?: string,
|
||||
email: string,
|
||||
name: string,
|
||||
attribs: {
|
||||
[key: string]: any
|
||||
},
|
||||
status: "enabled" | "blocklisted",
|
||||
lists: number[] | ListMembershipData[],
|
||||
created_at?: string,
|
||||
updated_at?: string
|
||||
};
|
||||
|
||||
export class Subscriber extends APIObject {
|
||||
private id?: number;
|
||||
private uuid?: string;
|
||||
private email: string;
|
||||
private name: string;
|
||||
private attribs: { [key: string]: any };
|
||||
private status?: 'enabled' | 'blocklisted';
|
||||
private lists: ListMembership[];
|
||||
private createdAt?: string;
|
||||
private updatedAt?: string;
|
||||
|
||||
constructor(credentials?: APICredentials, id?: number, uuid?: string, email?: string, name?: string, attribs?: { [key: string]: any }, status?: 'enabled' | 'blocklisted', lists?: ListMembership[], createdAt?: string, updatedAt?: string) {
|
||||
super(credentials);
|
||||
|
||||
this.id = id;
|
||||
this.uuid = uuid;
|
||||
this.email = typeof email !== 'undefined' ? email : '';
|
||||
this.name = typeof name !== 'undefined' ? name : '';
|
||||
this.attribs = typeof attribs !== 'undefined' ? attribs : {};
|
||||
this.status = status;
|
||||
this.lists = typeof lists !== 'undefined' ? lists : [];
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
getID(): number | undefined {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
getUUID(): string | undefined {
|
||||
return this.uuid;
|
||||
}
|
||||
|
||||
getEmail(): string {
|
||||
return this.email;
|
||||
}
|
||||
|
||||
getName(): string {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
getAttributes(): { [key: string]: any } {
|
||||
return this.attribs;
|
||||
}
|
||||
hasAttribute(key: string): boolean {
|
||||
return typeof this.attribs[key] !== 'undefined';
|
||||
}
|
||||
getAttribute(key: string): any {
|
||||
return this.hasAttribute(key) ? this.attribs[key] : undefined;
|
||||
}
|
||||
setAttribute(key: string, value: any) {
|
||||
this.attribs[key] = value;
|
||||
}
|
||||
removeAttribute(key: string) {
|
||||
delete this.attribs[key];
|
||||
}
|
||||
|
||||
getStatus(): 'enabled' | 'blocklisted' | undefined {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
getLists(): ListMembership[] {
|
||||
return this.lists;
|
||||
}
|
||||
addList(list: List) {
|
||||
this.lists.push(new ListMembership(this.api.getCredentials()).fromData({ subscription_status: 'unconfirmed', ...list.toJSON() }));
|
||||
}
|
||||
removeList(list: ListMembership) {
|
||||
this.lists = this.lists.filter((listMembership: ListMembership) => listMembership !== list);
|
||||
}
|
||||
|
||||
getCreatedAt(): string | undefined {
|
||||
return this.createdAt;
|
||||
}
|
||||
|
||||
getUpdatedAt(): string | undefined {
|
||||
return this.updatedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or updates the subscriber in Listmonk.
|
||||
*/
|
||||
async save() {
|
||||
const data = this.toJSON();
|
||||
|
||||
// If the UUID is not set, then we assume we need to create the subscriber in Listmonk
|
||||
if(typeof data.uuid === 'undefined') {
|
||||
// Technically, this might be unnecessary since the values should be set to undefined anyway, but it's good to be safe
|
||||
delete data.id;
|
||||
delete data.created_at;
|
||||
delete data.updated_at;
|
||||
delete data.uuid;
|
||||
|
||||
// Make the API call to create the subscriber in Listmonk
|
||||
await this.api.post<SubscriberData>('/subscribers', data);
|
||||
}
|
||||
else {
|
||||
await this.api.put<SubscriberData>('/subscribers/' + data.id, data);
|
||||
}
|
||||
}
|
||||
|
||||
toJSON(): SubscriberData {
|
||||
return {
|
||||
id: this.id,
|
||||
created_at: this.createdAt,
|
||||
updated_at: this.updatedAt,
|
||||
uuid: this.uuid,
|
||||
email: this.email,
|
||||
name: this.name,
|
||||
attribs: this.attribs,
|
||||
status: this.status as 'enabled' | 'blocklisted',
|
||||
lists: this.lists.map((listMembership: ListMembership) => (listMembership.getList() as List).getID() as number)
|
||||
};
|
||||
}
|
||||
|
||||
async fromData<SubscriberData>(data: SubscriberData): Promise<Subscriber> {
|
||||
// NOTE types are weird here because TypeScript was complaining otherwise
|
||||
const typedData = (data as { id?: number, uuid?: string, email: string, name: string, attribs: { [key: string]: any }, status: "enabled" | "blocklisted", lists: number[] | ListMembershipData[], created_at?: string, updated_at?: string });
|
||||
|
||||
let listMemberships: ListMembership[] = [];
|
||||
// We only want to bother processing the lists if there are any
|
||||
if(typedData.lists.length > 0) {
|
||||
listMemberships = await Promise.all(
|
||||
typedData.lists.map(
|
||||
async (listMembershipData: number | ListMembershipData) => {
|
||||
if(typeof listMembershipData === 'number') {
|
||||
// Get the list object based on it's IDs from Listmonk
|
||||
const listObj = await List.find((list: List) => (list.getID() as number) === listMembershipData);
|
||||
|
||||
// Verify we found a list with the given ID
|
||||
if(typeof listObj === 'undefined') {
|
||||
throw new Error('List with ID ' + listMembershipData + ' not found');
|
||||
}
|
||||
|
||||
// Default the membership object to unconfirmed
|
||||
listMembershipData = {
|
||||
subscription_status: 'unconfirmed',
|
||||
...listObj.toJSON()
|
||||
}
|
||||
}
|
||||
|
||||
return new ListMembership(this.api.getCredentials()).fromData(listMembershipData);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
this.id = typedData.id;
|
||||
this.uuid = typedData.uuid;
|
||||
this.email = typedData.email;
|
||||
this.name = typedData.name;
|
||||
this.attribs = typedData.attribs;
|
||||
this.status = typedData.status;
|
||||
this.lists = listMemberships;
|
||||
this.createdAt = typedData.created_at;
|
||||
this.updatedAt = typedData.updated_at;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Subscriber object with the given data.
|
||||
*
|
||||
* Note, this doesn't automatically create the subscriber in Listmonk. You need to call `save` to do that.
|
||||
*
|
||||
* @param email Subscriber's email address |
|
||||
* @param name Subscriber's name. |
|
||||
* @param status Subscriber's status. |
|
||||
* @param lists List of list IDs to subscribe to. |
|
||||
* @param attribs Attributes of the new subscriber. |
|
||||
* @param preconfirm_subscriptions If true, subscriptions are marked as confirmed and no-optin emails are sent for double opt-in lists. |
|
||||
* @param credentials Optional The credentials to use for the API call.
|
||||
*/
|
||||
static async create(email: string, name: string, status: 'enabled' | 'blocklisted', lists?: List[], attribs?: { [key: string]: any }, preconfirm_subscriptions?: boolean, credentials?: APICredentials): Promise<Subscriber> {
|
||||
return await new Subscriber(credentials).fromData({ email, name, status, lists: typeof lists !== 'undefined' ? lists.map((list: List) => list.getID()) : [] , attribs });
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a subscriber based on the given predicate.
|
||||
*
|
||||
* @param predicate A function that takes a subscriber and returns true if it matches the desired subscriber.
|
||||
* @param credentials Optional The credentials to use for the API call.
|
||||
* @returns The subscriber that matches the predicate, or undefined if no subscriber matches the predicate.
|
||||
*/
|
||||
static async find(predicate: (subscriber: Subscriber) => boolean, credentials?: APICredentials): Promise<Subscriber | undefined> {
|
||||
return await new API(credentials).get<SubscriberData[]>('/subscribers?per_page=all')
|
||||
.then(async (subscribers: SubscriberData[]) => {
|
||||
// Convert each SubscriberData JSON object to a Subscriber object
|
||||
const subscriberObjs: Subscriber[] = await Promise.all(subscribers.map((subscriber: SubscriberData) => new Subscriber(credentials).fromData(subscriber)));
|
||||
|
||||
// Use the predicate function provided to find the desired list
|
||||
const subscriber = subscriberObjs.find(predicate);
|
||||
|
||||
return subscriber;
|
||||
});
|
||||
}
|
||||
}
|
||||
238
src/Template.ts
Normal file
238
src/Template.ts
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
import { APIObject } from './APIObject';
|
||||
import { API, APICredentials } from './API';
|
||||
|
||||
type TemplateData = {
|
||||
id?: number,
|
||||
name: string,
|
||||
type: 'campaign' | 'tx',
|
||||
body: string,
|
||||
subject?: string,
|
||||
is_default?: boolean,
|
||||
created_at?: string,
|
||||
updated_at?: string
|
||||
};
|
||||
|
||||
/**
|
||||
* A class that represents a template in Listmonk.
|
||||
*/
|
||||
export class Template extends APIObject {
|
||||
private id?: number;
|
||||
/** Name of the template */
|
||||
private name: string;
|
||||
/** Type of the template (`campaign` or `tx`) */
|
||||
private type?: 'campaign' | 'tx';
|
||||
/** Raw HTML body of the template */
|
||||
private body: string;
|
||||
/** Subject line for the template (only for tx) */
|
||||
private subject?: string;
|
||||
/** Whether the template is the default template */
|
||||
private isDefault?: boolean;
|
||||
/** Timestamp when the template was created. Format: `YYYY-MM-DDTHH:MM:SSZ` */
|
||||
private createdAt?: string;
|
||||
/** Timestamp when the template was last updated. Format: `YYYY-MM-DDTHH:MM:SSZ` */
|
||||
private updatedAt?: string;
|
||||
|
||||
/**
|
||||
* Create a new template object
|
||||
*
|
||||
* @param credentials Optional credentials to use to authenticate API requests
|
||||
* @param id The identifier for the template
|
||||
* @param name The name of the template
|
||||
* @param type The type of the template (`campaign` or `tx`)
|
||||
* @param body The raw HTML body of the template
|
||||
* @param isDefault Whether the template is the default template
|
||||
* @param createdAt When the template was created. Format: `YYYY-MM-DDTHH:MM:SSZ`
|
||||
* @param updatedAt When the template was last updated. Format: `YYYY-MM-DDTHH:MM:SSZ`
|
||||
*/
|
||||
constructor(credentials?: APICredentials, id?: number, name?: string, type?: "campaign" | "tx", body?: string, isDefault?: boolean, createdAt?: string, updatedAt?: string) {
|
||||
super(credentials);
|
||||
|
||||
this.id = id;
|
||||
this.name = typeof name !== 'undefined' ? name : '';
|
||||
this.body = typeof body !== 'undefined' ? body : '';
|
||||
this.type = type;
|
||||
this.isDefault = isDefault;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Identifier for the template
|
||||
*/
|
||||
getID(): number | undefined {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The name of the template
|
||||
*/
|
||||
getName(): string {
|
||||
return this.name;
|
||||
}
|
||||
/**
|
||||
* Set the name of the template
|
||||
*
|
||||
* @param name Name of the template
|
||||
*/
|
||||
setName(name: string): void {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Raw HTML body of the template
|
||||
*/
|
||||
getBody(): string {
|
||||
return this.body;
|
||||
}
|
||||
/**
|
||||
* Set the raw HTML body of the template
|
||||
*
|
||||
* @param body Raw HTML body of the template
|
||||
*/
|
||||
setBody(body: string): void {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Type of the template (`campaign` or `tx`)
|
||||
*/
|
||||
getType(): 'campaign' | 'tx' | undefined {
|
||||
return this.type;
|
||||
}
|
||||
/**
|
||||
* Set the type of the template
|
||||
*
|
||||
* @param type Type of the template (`campaign` or `tx`)
|
||||
*/
|
||||
setType(type: 'campaign' | 'tx'): void {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Subject line for the template (only for tx)
|
||||
*/
|
||||
getSubject(): string | undefined {
|
||||
return this.subject;
|
||||
}
|
||||
/**
|
||||
* Set the subject line for the template
|
||||
*
|
||||
* NOTE: ONLY relevant for `tx` type templates
|
||||
*
|
||||
* @param subject Subject line for the template
|
||||
*/
|
||||
setSubject(subject: string): void {
|
||||
this.subject = subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Whether the template is the default template
|
||||
*/
|
||||
getIsDefault(): boolean | undefined {
|
||||
return this.isDefault;
|
||||
}
|
||||
/**
|
||||
* Set whether the template is the default template
|
||||
*
|
||||
* @param isDefault Whether the template is the default template
|
||||
*/
|
||||
setIsDefault(isDefault: boolean): void {
|
||||
this.isDefault = isDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Timestamp when the template was created. Format: `YYYY-MM-DDTHH:MM:SSZ`
|
||||
*/
|
||||
getCreatedAt(): string | undefined {
|
||||
return this.createdAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Timestamp when the template was last updated. Format: `YYYY-MM-DDTHH:MM:SSZ`
|
||||
*/
|
||||
getUpdatedAt(): string | undefined {
|
||||
return this.updatedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or updates the template in Listmonk.
|
||||
*/
|
||||
async save() {
|
||||
const data = this.toJSON();
|
||||
|
||||
// If the UUID is not set, then we assume we need to create the subscriber in Listmonk
|
||||
if(typeof data.id === 'undefined') {
|
||||
// Technically, this might be unnecessary since the values should be set to undefined anyway, but it's good to be safe
|
||||
delete data.id;
|
||||
delete data.created_at;
|
||||
delete data.updated_at;
|
||||
|
||||
// Make the API call to create the subscriber in Listmonk
|
||||
await this.api.post<TemplateData>('/templates', data);
|
||||
}
|
||||
else {
|
||||
await this.api.put<TemplateData>('/templates/' + data.id, data);
|
||||
}
|
||||
}
|
||||
|
||||
toJSON(): TemplateData {
|
||||
return {
|
||||
id: this.id,
|
||||
name: this.name,
|
||||
body: this.body,
|
||||
type: typeof this.type !== 'undefined' ? this.type : 'campaign',
|
||||
is_default: this.isDefault,
|
||||
created_at: this.createdAt,
|
||||
updated_at: this.updatedAt
|
||||
};
|
||||
}
|
||||
|
||||
fromData<TemplateData>(data: TemplateData): Template {
|
||||
const typedData = data as { id?: number, name: string, type: 'campaign' | 'tx', body: string, subject?: string, is_default?: boolean, created_at?: string, updated_at?: string};
|
||||
|
||||
this.id = typedData.id;
|
||||
this.name = typedData.name
|
||||
this.type = typedData.type;
|
||||
this.body = typedData.body;
|
||||
this.isDefault = typedData.is_default;
|
||||
this.createdAt = typedData.created_at;
|
||||
this.updatedAt = typedData.updated_at;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Template object with the given data.
|
||||
*
|
||||
* Note, this doesn't automatically create the template in Listmonk. You need to call `save` to do that.
|
||||
*
|
||||
* @param name Name of the new template.
|
||||
* @param type Type of the template (`campaign` or `tx`)
|
||||
* @param body Raw HTML body of the template
|
||||
* @param credentials Optional credentials to use to authenticate API requests
|
||||
* @returns The new template object
|
||||
*/
|
||||
static async create(name: string, type: 'campaign' | 'tx', body: string, credentials?: APICredentials): Promise<Template> {
|
||||
return await new Template(credentials).fromData({ name, type, body });
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a template based on the given predicate.
|
||||
*
|
||||
* @param predicate A function that takes a template and returns true if it matches the desired template.
|
||||
* @param credentials Optional credentials to use to authenticate API requests
|
||||
* @returns The template that matches the predicate, or undefined if no template matches the predicate.
|
||||
*/
|
||||
static async find(predicate: (template: Template) => boolean, credentials?: APICredentials): Promise<Template | undefined> {
|
||||
return await new API(credentials).get<TemplateData[]>('/templates')
|
||||
.then(async (templates: TemplateData[]) => {
|
||||
// Convert each TemplateData JSON object to a Template object
|
||||
const templateObjs: Template[] = await Promise.all(templates.map((template: TemplateData) => new Template(credentials).fromData(template)));
|
||||
|
||||
// Use the predicate function provided to find the desired template
|
||||
const template = templateObjs.find(predicate);
|
||||
|
||||
return template;
|
||||
});
|
||||
}
|
||||
}
|
||||
53
src/demo.ts
Normal file
53
src/demo.ts
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import dotenv from 'dotenv';
|
||||
|
||||
import { List } from './List';
|
||||
import { Subscriber } from './Subscriber';
|
||||
import { Template } from './Template';
|
||||
import { Campaign } from './Campaign';
|
||||
|
||||
// Load environment variables from .env file (mostly Listmonki API credentials)
|
||||
dotenv.config();
|
||||
|
||||
async function main() {
|
||||
// Create a new list
|
||||
await (await List.create('API Generated List', 'private', 'single')).save();
|
||||
|
||||
// Get a list by it's name
|
||||
const list = await List.find((list: List) => list.getName() === 'API Generated List');
|
||||
if(typeof list === 'undefined') {
|
||||
throw new Error('List not found');
|
||||
}
|
||||
|
||||
// Create a subscriber
|
||||
await (await Subscriber.create('api@nodejs.listmonk', 'Listmonk Node Client Library', 'enabled', [/*list*/], { phone: '+12345678912' })).save();
|
||||
|
||||
// Get a subscriber by their email
|
||||
const subscriber = await Subscriber.find((subscriber: Subscriber) => subscriber.getEmail() === 'api@nodejs.listmonk');
|
||||
if(typeof subscriber === 'undefined') {
|
||||
throw new Error('Subscriber not found');
|
||||
}
|
||||
|
||||
// Add the subscriber to the list
|
||||
subscriber.addList(list);
|
||||
await subscriber.save();
|
||||
|
||||
// Create a template
|
||||
await (await Template.create('API Generated Template', 'campaign', 'Hello {{ .Subscriber.FirstName }},<br>{{ template "content" . }}<br>Your Phone number on record is: {{ .Subscriber.Attribs.phone }}')).save();
|
||||
|
||||
// Get a template by it's name
|
||||
const template = await Template.find((template: Template) => template.getName() === 'API Generated Template');
|
||||
if(typeof template === 'undefined') {
|
||||
throw new Error('Template not found');
|
||||
}
|
||||
|
||||
// Create a campaign
|
||||
const campaign = (await Campaign.create('API Generated Campaign', 'API Generated Campaign Subject', [list], 'regular', 'html', '<h1>API Generated Text</h1><br><p>A demo campaign generated using the Listmonk Node Client Library</p>'));
|
||||
|
||||
// Set the template for the campaign
|
||||
campaign.setTemplate(template);
|
||||
|
||||
// Save the campaign to Listmonk
|
||||
await campaign.save();
|
||||
}
|
||||
|
||||
main();
|
||||
Loading…
Add table
Add a link
Reference in a new issue