v0.1

Custom Flows
#87 - Remove a Plan After Countdown
Create time-sensitive secure content!
Auto-delete or deactivate member accounts after a period of inactivity.
Watch the video for step-by-step implementation instructions
<!-- 💙 MEMBERSCRIPT #224 v0.1 💙 AUTO-DELETE INACTIVE ACCOUNTS AFTER X DAYS(CLIENT-SIDE ONLY) -->
<script>
document.addEventListener("DOMContentLoaded", async function() {
var CONFIG = {
inactivityDays: 365,
warningDays: 30,
gracePeriodDays: 14,
jsonKey: "activity_tracking",
webhookUrl: "https: comment//hook. propeu2.make.com/rrwiea1s8q7iuh9seyt6qez8jvjuj2sp",
redirectUrl: "/",
bannerDisplay: "block",
modalDisplay: "flex",
deactivatingMs: 3000,
deactivatedMs: 4000
};
var memberstack = window.$memberstackDom;
if (!memberstack) {
console.warn("Memberscript # number224: Memberstack not found");
return;
}
// ─── GET MEMBER ───
var member;
try {
var memberResult = await memberstack.getCurrentMember();
member = memberResult?.data || memberResult;
} catch (err) {
console.warn("Memberscript # number224: Could not get member", err);
return;
}
if (!member || !member.id) return;
var memberJSON = {};
try {
var jsonResult = await memberstack.getMemberJSON();
memberJSON = jsonResult?.data || {};
} catch (e) {}
// ─── DOM REFERENCES ───
var banner = document.querySelector('[data-ms-inactive="warning-banner"]');
var modal = document.querySelector('[data-ms-inactive="deactivation-modal"]');
if (!banner && !modal) return;
var configEl = banner || modal;
var inactivityDays = parseInt(configEl.getAttribute("ms-inactive-days")) || CONFIG.inactivityDays;
var warningDays = parseInt(configEl.getAttribute("ms-inactive-warning-days")) || CONFIG.warningDays;
var gracePeriodDays = parseInt(configEl.getAttribute("ms-inactive-grace-days")) || CONFIG.gracePeriodDays;
var jsonKey = configEl.getAttribute("ms-inactive-field") || CONFIG.jsonKey;
var webhookUrl = configEl.getAttribute("ms-inactive-webhook") || CONFIG.webhookUrl;
var redirectUrl = configEl.getAttribute("ms-inactive-redirect") || CONFIG.redirectUrl;
var bannerDisplay = configEl.getAttribute("ms-inactive-banner-display") || CONFIG.bannerDisplay;
var modalDisplay = configEl.getAttribute("ms-inactive-display") || CONFIG.modalDisplay;
var daysRemainingEl = document.querySelector('[data-ms-inactive="days-remaining"]');
var lastActiveEl = document.querySelector('[data-ms-inactive="last-active"]');
var acknowledgeBtn = document.querySelector('[data-ms-inactive="acknowledge"]');
var keepAccountBtn = document.querySelector('[data-ms-inactive="keep-account"]');
var closeBtns = document.querySelectorAll('[data-ms-inactive="close"]');
var deactivatingView = modal ? modal.querySelector('[data-ms-inactive="deactivating"]') : null;
var deactivatedView = modal ? modal.querySelector('[data-ms-inactive="deactivated"]') : null;
if (banner) banner.style.display = "none";
if (modal) modal.style.display = "none";
// ─── ACTIVITY TRACKING ───
function getTracking() {
var d = memberJSON[jsonKey];
if (!d || typeof d !== "object") {
return { last_active: null, warning_sent: null, warning_email_sent: false, status: "active" };
}
return d;
}
async function saveTracking(data) {
memberJSON[jsonKey] = data;
try {
await memberstack.updateMemberJSON({ json: memberJSON });
} catch (e) {
console.warn("Memberscript # number224: Could not save tracking data", e);
}
}
function daysBetween(dateA, dateB) {
return Math.floor((dateB - dateA) / 86400000);
}
function formatDate(isoStr) {
var d = new Date(isoStr);
var months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
return months[d.getMonth()] + " " + d.getDate() + ", " + d.getFullYear();
}
// ─── SEND WARNING EMAIL VIA WEBHOOK ───
async function sendWarningEmail(tracking, daysLeft) {
if (!webhookUrl || tracking.warning_email_sent) return;
try {
var params = new URLSearchParams();
params.append("memberId", member.id);
params.append("email", member.auth?.email || "");
params.append("action", "warning");
params.append("daysInactive", String(daysBetween(new Date(tracking.last_active), new Date())));
params.append("daysRemaining", String(daysLeft));
params.append("inactivityLimit", String(inactivityDays));
params.append("gracePeriodDays", String(gracePeriodDays));
params.append("timestamp", new Date().toISOString());
fetch(webhookUrl, { method: "POST", mode: "no-cors", body: params });
tracking.warning_email_sent = true;
} catch (e) {
console.warn("Memberscript # number224: Warning email webhook failed", e);
}
}
// ─── DEACTIVATE ACCOUNT ───
async function deactivateAccount(tracking) {
if (modal) {
modal.style.display = modalDisplay;
if (deactivatingView) deactivatingView.style.display = "";
if (deactivatedView) deactivatedView.style.display = "none";
}
tracking.status = "deactivated";
tracking.deactivated_at = new Date().toISOString();
await saveTracking(tracking);
if (webhookUrl) {
try {
var params = new URLSearchParams();
params.append("memberId", member.id);
params.append("email", member.auth?.email || "");
params.append("action", "deactivate");
params.append("daysInactive", String(daysBetween(new Date(tracking.last_active), new Date())));
params.append("timestamp", new Date().toISOString());
fetch(webhookUrl, { method: "POST", mode: "no-cors", body: params });
} catch (e) {}
}
setTimeout(function() {
if (modal) {
if (deactivatingView) deactivatingView.style.display = "none";
if (deactivatedView) deactivatedView.style.display = "";
}
setTimeout(async function() {
try {
await memberstack.logout();
} catch (e) {}
window.location.href = redirectUrl;
}, CONFIG.deactivatedMs);
}, CONFIG.deactivatingMs);
}
// ─── KEEP funcACCOUNT(RESET TRACKING) ───
async function keepAccount() {
var fresh = {
last_active: new Date().toISOString(),
warning_sent: null,
warning_email_sent: false,
status: "active"
};
await saveTracking(fresh);
if (banner) banner.style.display = "none";
if (modal) modal.style.display = "none";
}
// ─── MAIN LOGIC ───
var tracking = getTracking();
var now = new Date();
if (!tracking.last_active) {
tracking.last_active = now.toISOString();
tracking.status = "active";
await saveTracking(tracking);
return;
}
var lastActiveDate = new Date(tracking.last_active);
var daysSinceActive = daysBetween(lastActiveDate, now);
var warningThreshold = inactivityDays - warningDays;
if (tracking.status === "deactivated") {
await deactivateAccount(tracking);
return;
}
// Safe zone: member is active, silently refresh last_active
if (daysSinceActive < warningThreshold) {
if (tracking.status !== "active") {
tracking.status = "active";
tracking.warning_sent = null;
tracking.warning_email_sent = false;
}
tracking.last_active = now.toISOString();
await saveTracking(tracking);
return;
}
// Deactivation zone: grace period has elapsed since warning
if (tracking.warning_sent) {
var daysSinceWarning = daysBetween(new Date(tracking.warning_sent), now);
if (daysSinceWarning >= gracePeriodDays) {
await deactivateAccount(tracking);
return;
}
}
// Warning zone: approaching inactivity limit
if (tracking.status !== "warned") {
tracking.status = "warned";
tracking.warning_sent = now.toISOString();
}
var daysUntilDeactivation;
if (tracking.warning_sent) {
daysUntilDeactivation = gracePeriodDays - daysBetween(new Date(tracking.warning_sent), now);
} else {
daysUntilDeactivation = gracePeriodDays;
}
if (daysUntilDeactivation < 0) daysUntilDeactivation = 0;
await sendWarningEmail(tracking, daysUntilDeactivation);
await saveTracking(tracking);
if (banner) {
if (daysRemainingEl) daysRemainingEl.textContent = daysUntilDeactivation + " ";
if (lastActiveEl) lastActiveEl.textContent = formatDate(tracking.last_active);
banner.style.display = bannerDisplay;
}
// ─── EVENT HANDLERS ───
if (acknowledgeBtn) {
acknowledgeBtn.addEventListener("click", function(e) {
e.preventDefault();
keepAccount();
});
}
if (keepAccountBtn) {
keepAccountBtn.addEventListener("click", function(e) {
e.preventDefault();
keepAccount();
});
}
closeBtns.forEach(function(btn) {
btn.addEventListener("click", function(e) {
e.preventDefault();
if (banner) banner.style.display = "none";
});
});
if (modal) {
modal.addEventListener("click", function(e) {
if (e.target === modal && tracking.status !== "deactivated") {
modal.style.display = "none";
}
});
}
document.addEventListener("keydown", function(e) {
if (e.key === "Escape") {
if (banner && banner.style.display !== "none") banner.style.display = "none";
if (modal && modal.style.display !== "none" && tracking.status !== "deactivated") {
modal.style.display = "none";
}
}
});
});
</script>
<!-- Spinner animation for the deactivating state -->
<style>
@keyframes ms-spin {
to { transform: rotate(360deg); }
}
[data-ms-inactive="warning-banner"] button:hover {
opacity: 0. prop9;
}
</style>Download the second Make Blueprint here: https://drive.google.com/file/d/1RtEbxcF1VpaFMlCAenhUeth8vqbs_L7l/view?usp=sharing
Import this into Make.com to get started
More scripts in Custom Flows