");
-
- data.forEach(function(rule, idx) {
- let badge = $(`
${rule.name}`)
- if (idx > 0) { badge.addClass("ms-2") }
- badges.append(badge);
- });
-
- return badges.html();
- }
- },
- {
- title: "Created",
- data: "creation_datetime",
- render: function(data, type) {
- let dateTime = new Date(data);
- return $(`
-
%H:%M:%S")}">
- ${formatStringDate(dateTime, "%D, %b %Y")}
-
- `).popover()[0];
- }
- },
- {
- title: "Notes",
- data: "extra_notes",
- orderable: false,
- className: "text-center",
- render: function(data, type) {
- if (!data) { return "" }
- const extraNotes = sanitise(data);
- return $(`
-
-
- `).popover()[0];
- }
- },
- {
- title: "Active",
- data: "active",
- orderable: false,
- className: "text-center form-switch",
- render: function(data, type) {
- return `
`
- }
- },
- {
- orderable: false,
- className: "p-0",
- render: function(data, type, row) {
- const embedColour = sanitise(row.embed_colour);
- return `
`
- }
- }
- ]
- });
-
- bindTableCheckboxes("#subTable", subTable, "#subscriptionsTabPane .table-del-btn");
-}
-
-async function updateSubFromObject(sub, handleErrorMsg=true) {
- let data = {
- "name": sub.name,
- "url": sub.url,
- "guild_id": sub.guild_id,
- "extra_notes": sub.extra_notes,
- "embed_colour": sub.embed_colour,
- "article_fetch_image": sub.article_fetch_image,
- "published_threshold": sub.published_threshold,
- "active": sub.active
- };
-
- let formData = new FormData();
-
- for (key in data) {
- formData.append(key, data[key]);
- }
-
- sub.article_title_mutators.forEach(mutator => formData.append("article_title_mutators", mutator.id));
- sub.article_desc_mutators.forEach(mutator => formData.append("article_desc_mutators", mutator.id));
- sub.filters.forEach(filter => formData.append("filters", filter));
-
- return await saveSubscription(sub.id, formData, handleErrorMsg=handleErrorMsg);
-}
-
-$("#subscriptionsTabPane").on("change", ".sub-toggle-active", async function () {
-
- /*
- Lock all toggles to soft-prevent spam.
- There is a rate limit, but allowing the user to
- reach it from this toggle would be bad.
- */
- $(".sub-toggle-active").prop("disabled", true);
-
- try {
- const active = $(this).prop("checked");
- const sub = subTable.row($(this).closest("tr")).data();
-
- // Update the table row
- sub.active = active;
- subTable.data(sub).draw();
-
- // Update the database
- const subId = await updateSubFromObject(sub, handleErrorMsg=false);
-
- if (!subId) {
- throw Error("This subscription no longer exists.");
- }
-
- showToast(
- active ? "success" : "danger",
- "Subscription " + (active ? "Activated" : "Deactivated"),
- "Subscription ID: " + subId
- );
- }
- catch (error) {
- console.error(error);
- showToast(
- "danger",
- "Error Updating Subscription",
- `Tried to toggle activeness, but encountered a problem.
${error}
`
- );
- }
- finally {
- // Re-enable toggles after 500ms
- setTimeout(() => {
- $(".sub-toggle-active").prop("disabled", false); },
- 500
- );
- }
-});
-
-// Open new subscription modal
-$("#addSubscriptionBtn").on("click", async function() {
- await showEditSubModal(-1);
-});
-
-function clearPreviousValidation() {
- $("#subFormModal, #subAdvancedModal").removeClass("was-validated");
- $("#subFormModal .invalid-feedback, #subAdvancedModal .invalid-feedback").remove();
- $("#subFormModal .is-invalid, #subAdvancedModal .is-invalid").removeClass("is-invalid");
-}
-
-async function showEditSubModal(subId) {
- clearPreviousValidation();
-
- if (subId === -1) {
- $("#subFormModal .form-create, #subAdvancedModal .form-create").show();
- $("#subFormModal .form-edit, #subAdvancedModal .form-edit").hide();
-
- $("#subFormModal input, #subFormModal textarea").val("");
- $("#subChannels").val("").change();
- $("#subFilters").val("").change();
- $("#subTitleMutators").val("").change();
- $("#subDescMutators").val("").change();
- $("#subActive").prop("checked", true);
- $("#subUniqueRules").val(1).change(); // GUID option selected by default
-
- $("#subEmbedColour .colour-reset").click();
- $("#subArticleFetchImage").prop("checked", true);
-
- $("#subPubThreshold").val(getCurrentDateTime());
- }
- else {
- $("#subFormModal .form-create, #subAdvancedModal .form-create").hide();
- $("#subFormModal .form-edit, #subAdvancedModal .form-edit").show();
-
- const subscription = subTable.row(function(idx, data, node) {
- return data.id === subId;
- }).data();
-
- $("#subName").val(subscription.name);
- $("#subUrl").val(subscription.url);
- $("#subExtraNotes").val(subscription.extra_notes);
- $("#subActive").prop("checked", subscription.active);
-
- $("#subTitleMutators").val("").change();
- $("#subTitleMutators").val(subscription.article_title_mutators.map(mutator => mutator.id)).change();
-
- $("#subDescMutators").val("").change();
- $("#subDescMutators").val(subscription.article_desc_mutators.map(mutator => mutator.id)).change();
-
- const channels = await getSubChannels(subscription.id);
- $("#subChannels").val("").change();
- $("#subChannels").val(channels.results.map(channel => channel.channel_id)).change();
-
- $("#subFilters").val("").change();
- $("#subFilters").val(subscription.filters).change();
-
- $("#subUniqueRules").val("").change();
- $("#subUniqueRules").val(subscription.unique_content_rules.map(rule => rule.id)).change();
-
- updateColourInput("subEmbedColour", `#${subscription.embed_colour}`);
- $("#subArticleFetchImage").prop("checked", subscription.article_fetch_image);
-
- $("#subPubThreshold").val(subscription.published_threshold.split('+')[0]);
- }
-
- $("#subId").val(subId);
- $("#subFormModal").modal("show");
-}
-
-function getValueFromField(elem) {
- const tagName = elem.tagName.toLowerCase();
- const $elem = $(elem);
-
- if (tagName) { return $elem.val() }
-
- switch ($elem.attr("type")) {
- case "checkbox":
- return $elem.prop("checked");
-
- default:
- return $elem.val();
- }
-}
-
-$("#subForm").on("submit", async function(event) {
- event.preventDefault();
-
- let subId = $("#subId").val();
- let guildId = selectedServer.id;
-
- // TODO: move this into a function, so I can fix the active toggle switches which are broken due to this change
-
- let formData = new FormData();
- formData.append("guild_id", guildId);
-
- // Populate formdata with [data-field] control values
- $('#subForm [data-field], #subAdvancedModal [data-field]').each(function() {
- const value = getValueFromField(this);
- if (Array.isArray(value) && !value.length) { return };
- formData.append($(this).data("field"), value);
- });
-
- // Add title mutators to formdata
- $("#subTitleMutators option:selected").toArray().map(mutator => parseInt(mutator.value)).forEach(
- mutator => formData.append("article_title_mutators", mutator)
- );
-
- // Add description mutator to formdata
- $("#subDescMutators option:selected").toArray().map(mutator => parseInt(mutator.value)).forEach(
- mutator => formData.append("article_desc_mutators", mutator)
- );
-
- // Add Filters to formdata
- $("#subFilters option:selected").toArray().forEach(
- filter => formData.append("filters", parseInt(filter.value))
- );
-
- for (const [key, value] of formData.entries()) {
- console.log(`${key}: ${value}`);
- }
-
- // // Unique Content Rules
- // $("#subUniqueRules option:selected").toArray().forEach(
- // rule => formData.append("unique_content_rules", parseInt(rule.value))
- // );
-
- for (const [key, value] of formData.entries()) {
- console.log(`${key}: ${value}`);
- }
-
- // This field is constructed differently, so needs to be specifically added
- formData.append("embed_colour", getColourInputVal("subEmbedColour", false));
-
-
- subId = await saveSubscription(subId, formData);
-
- if (subId) {
- showToast("success", "Subscription Saved", `Subscription ID ${subId}`);
- }
- else {
- showToast("danger", "Error Saving Subscription", "");
- return;
- }
-
- await deleteSubChannels(subId);
- $("#subChannels option:selected").each(async function() {
- let $channel = $(this);
- let channelFormData = new FormData();
- channelFormData.append("channel_id", $channel.val());
- channelFormData.append("channel_name", $channel.data("name"));
- channelFormData.append("subscription", subId);
- await newSubChannel(channelFormData);
- });
-
- await loadSubscriptions(guildId);
- $("#subFormModal").modal("hide");
-});
-
-async function saveSubscription(id, formData, handleErrorMsg=true) {
- let response
-
- try {
- response = id === "-1" ? await newSubscription(formData) : await editSubscription(id, formData);
- }
- catch (err) {
- if (typeof err !== "object" && err.responseJSON) {
- return false
- }
-
- clearPreviousValidation();
- for (const [fieldKey, message] of Object.entries(err.responseJSON)) {
- const $field = $(`#subFormModal [data-field="${fieldKey}"], #subAdvancedModal [data-field="${fieldKey}"]`);
- const $helpText = $field.closest("div").find(".form-text");
- const $feedback = $(`
${message}
`);
-
- $field.addClass("is-invalid").prop("invalid", true);
- $feedback.insertAfter($helpText.length ? $helpText : $field);
- }
-
- $("#subFormModal, #subAdvancedModal").addClass("was-validated");
-
- if (handleErrorMsg) {
- showToast("danger", "Subscription Error", err.responseText, 18000);
- }
-
- return false;
- }
-
- return response.id;
-}
-
-async function saveSubChannel(formData) {
- var response
-
- try {
- response = await newSubChannel(formData);
- }
- catch (error) {
- console.log(error);
- showToast("danger", "Failed to save subchannel", error, 18000);
- return false
- }
-
- return response.id
-}
-
-function clearExistingSubRows() {
- $("#subTable thead .table-select-all").prop("checked", false).prop("indeterminate", false);
- subTable.clear().draw(false);
-}
-
-$("#subscriptionsTabPane").on("click", ".table-refresh-btn", async function() {
- loadSubscriptions(selectedServer.id);
-});
-
-async function loadSubscriptions(guildId) {
- if (!guildId)
- return;
-
- setTableFilter("subTable", "guild_id", guildId);
- ensureTablePagination("subTable");
-
- $("#subscriptionsTabPane .table-del-btn").prop("disabled", true);
- clearExistingSubRows();
-
- try {
- var subs = await getSubscriptions(tableFilters["subTable"], tableSorts["subTable"]);
- subTable.rows.add(subs.results).draw(false);
- }
- catch (err) {
- console.error(err)
- showToast("danger", `Error Loading Subscriptions: HTTP ${err.status}`, err, 15000);
- return;
- }
-
- updateTableContainer(
- "subscriptionsTabPane",
- tableFilters["subTable"]["page"],
- tableFilters["subTable"]["page_size"],
- subs.results.length,
- subs.count,
- subs.next,
- subs.previous
- );
-
- $("#subTable thead .table-select-all").prop("disabled", subs.results.length === 0);
- console.debug("loading subs, " + subs.results.length + " found");
-}
-
-// #region Server Change Event Handler
-
-$(document).on("selectedServerChange", async function() {
- let guildId = selectedServer.id;
-
- // await updateDefaultSubEmbedColour();
-
- await loadSubscriptions(guildId);
- await loadChannelOptions(guildId);
- await loadFilterOptions(guildId);
- await loadMutatorOptions();
- await loadUniqueContentRuleOptions();
-})
-
-async function updateDefaultSubEmbedColour(settings=null) {
- if (!settings){
- settings = (await getGuildSettings(selectedServer.id)).results[0]
- }
- $("#subEmbedColour .colour-reset").attr("data-defaultcolour", "#" + settings.default_embed_colour);
-}
-
-// #endregion
-
-
-// #region Delete Subscriptions
-
-// Delete button on the 'edit subscription' modal
-$("#deleteEditSub").on("click", async function() {
- const subId = parseInt($("#subId").val());
- const sub = subTable.row(function(idx, row) { return row.id === subId }).data();
- const subName = sanitise(sub.name);
-
- $("#subFormModal").modal("hide");
-
- await confirmationModal(
- "Delete a Subscription",
- `Do you wish to permanently delete
${subName}?`,
- "danger",
- async () => {
- await deleteSubscription(subId);
- await loadSubscriptions(selectedServer.id);
-
- showToast(
- "danger",
- "Deleted a Subscription",
- subName,
- 12000
- );
- },
- async () => {
- $("#subFormModal").modal("show");
- }
- );
-});
-
-async function deleteSelectedSubscriptions() {
- const rows = subTable.rows(".selected").data().toArray();
- const names = rows.map(row => row.name);
- const namesString = arrayToHtmlList(names, true).prop("outerHTML");
- const isMany = names.length > 1;
-
- await confirmationModal(
- `Delete ${isMany ? "Many Subscriptions" : "a Subscription"}`,
- `Do you wish to permanently delete ${isMany ? "these" : "this"}
${names.length} subscription${isMany ? "s" : ""}?
${namesString}`,
- "danger",
- async () => {
- rows.forEach(async row => { await deleteSubscription(row.id) });
-
- showToast(
- "danger",
- `Deleted ${names.length} Subscription${isMany ? "s" : ""}`,
- `${arrayToHtmlList(names, false).prop("outerHTML")}`,
- 12000
- );
-
- // Multi-deletion can take time, this timeout ensures the refresh is accurate
- setTimeout(async () => {
- await loadSubscriptions(selectedServer.id);
- }, 600);
- },
- null
-
- );
-}
-
-// #endregion
-
-
-// #region Load Modal Options
-
-async function loadChannelOptions(guildId) {
-
- // Disable input while options are loading
- $("#subChannels").prop("disabled", true);
-
- // Delete existing options
- $("#subChannels option").each(function() {
- if ($(this).val())
- $(this).remove();
- });
-
- // Clear select2 input
- $("#subChannels").val("").change();
- try {
- const channels = await loadChannels(guildId);
-
- // If we have reached the discord API rate limit
- if (channels.message && channels.message.includes("rate limit")) {
- throw new Error(
- `${channels.message} Retry after ${channels.retry_after} seconds.`
- )
- }
-
- // If we can't fetch channels due to error
- if (channels.code === 50001) {
- let server = getServerFromSnowflake(guildId);
-
- // Also check that the user hasn't changed the currently active guild, otherwise
- // the alert will show under the wrong server.
- if (selectedServer.id === guildId)
- $("#serverJoinAlert").show();
-
- // Warning icon on sidebar server select
- let sidebarItem = $(`#serverList .server-item[data-guild-id="${guildId}"]`);
- if (!sidebarItem.find(".badge.text-warning").length) {
- let alertTemplate = $($("#serverItemIconTemplate").html());
- alertTemplate.attr("data-bs-title", `The Bot isn't a member of ${sanitise(server.name)}`).tooltip();
- sidebarItem.find(".server-item-selector").append(alertTemplate);
- }
-
- const guildName = sanitise(getServerFromSnowflake(guildId).name);
-
- throw new Error(
- `Unable to retrieve channels from Guild
${guildName}.
- Ensure that @PYRSS is a member with permissions
- to view channels.`
- );
- }
-
- // Sort by the specified position of each channel object
- channels.sort((a, b) => a.position - b.position);
-
- discordChannels = [];
- channels.forEach(channel => {
-
- // We only want TextChannels, which have a type of 0
- if (channel.type !== 0)
- return;
-
- let channelObj = {text: `#${channel.name}`, value: channel.id, "data-name": channel.name}
- $("#subChannels").append($("