Autoclose settings
This commit is contained in:
parent
02ec5c8a90
commit
1bbd4a785b
@ -1,20 +1,67 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/TicketsBot/GoPanel/database"
|
dbclient "github.com/TicketsBot/GoPanel/database"
|
||||||
|
"github.com/TicketsBot/database"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// time.Duration marshals to nanoseconds, custom impl to marshal to seconds
|
||||||
|
type autoCloseBody struct {
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
SinceOpenWithNoResponse int64 `json:"since_open_with_no_response"`
|
||||||
|
SinceLastMessage int64 `json:"since_last_message"`
|
||||||
|
OnUserLeave bool `json:"on_user_leave"`
|
||||||
|
}
|
||||||
|
|
||||||
func GetAutoClose(ctx *gin.Context) {
|
func GetAutoClose(ctx *gin.Context) {
|
||||||
guildId := ctx.Keys["guildid"].(uint64)
|
guildId := ctx.Keys["guildid"].(uint64)
|
||||||
|
|
||||||
settings, err := database.Client.AutoClose.Get(guildId); if err != nil {
|
settings, err := dbclient.Client.AutoClose.Get(guildId)
|
||||||
|
if err != nil {
|
||||||
ctx.AbortWithStatusJSON(500, gin.H{
|
ctx.AbortWithStatusJSON(500, gin.H{
|
||||||
"success": false,
|
"success": false,
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.JSON(200, settings)
|
ctx.JSON(200, convertToAutoCloseBody(settings))
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertToAutoCloseBody(settings database.AutoCloseSettings) (body autoCloseBody) {
|
||||||
|
body.Enabled = settings.Enabled
|
||||||
|
|
||||||
|
if settings.SinceOpenWithNoResponse != nil {
|
||||||
|
body.SinceOpenWithNoResponse = int64(*settings.SinceOpenWithNoResponse / time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.SinceLastMessage != nil {
|
||||||
|
body.SinceLastMessage = int64(*settings.SinceLastMessage / time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.OnUserLeave != nil {
|
||||||
|
body.OnUserLeave = *settings.OnUserLeave
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertFromAutoCloseBody(body autoCloseBody) (settings database.AutoCloseSettings) {
|
||||||
|
settings.Enabled = body.Enabled
|
||||||
|
|
||||||
|
if body.SinceOpenWithNoResponse > 0 {
|
||||||
|
duration := time.Second * time.Duration(body.SinceOpenWithNoResponse)
|
||||||
|
settings.SinceOpenWithNoResponse = &duration
|
||||||
|
}
|
||||||
|
|
||||||
|
if body.SinceLastMessage > 0 {
|
||||||
|
duration := time.Second * time.Duration(body.SinceLastMessage)
|
||||||
|
settings.SinceLastMessage = &duration
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.OnUserLeave = &body.OnUserLeave
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
@ -1,36 +1,48 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/TicketsBot/GoPanel/botcontext"
|
||||||
dbclient "github.com/TicketsBot/GoPanel/database"
|
dbclient "github.com/TicketsBot/GoPanel/database"
|
||||||
"github.com/TicketsBot/database"
|
"github.com/TicketsBot/GoPanel/rpc"
|
||||||
|
"github.com/TicketsBot/GoPanel/utils"
|
||||||
|
"github.com/TicketsBot/common/premium"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var maxDays = 90
|
||||||
|
var maxLength = time.Hour * 24 * time.Duration(maxDays)
|
||||||
|
|
||||||
func PostAutoClose(ctx *gin.Context) {
|
func PostAutoClose(ctx *gin.Context) {
|
||||||
guildId := ctx.Keys["guildid"].(uint64)
|
guildId := ctx.Keys["guildid"].(uint64)
|
||||||
|
|
||||||
var settings database.AutoCloseSettings
|
var body autoCloseBody
|
||||||
if err := ctx.BindJSON(&settings); err != nil {
|
if err := ctx.BindJSON(&body); err != nil {
|
||||||
ctx.AbortWithStatusJSON(400, gin.H{
|
ctx.JSON(400, utils.ErrorJson(err))
|
||||||
"success": false,
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if settings.Enabled && (settings.SinceLastMessage == nil || settings.SinceOpenWithNoResponse == nil || settings.OnUserLeave == nil) {
|
settings := convertFromAutoCloseBody(body)
|
||||||
ctx.AbortWithStatusJSON(400, gin.H{
|
|
||||||
"success": false,
|
// get premium
|
||||||
"error": "No time period provided",
|
botContext, err := botcontext.ContextForGuild(guildId)
|
||||||
})
|
if err != nil {
|
||||||
|
ctx.JSON(500, utils.ErrorJson(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settings.SinceOpenWithNoResponse != nil && *settings.SinceOpenWithNoResponse < 0) || (settings.SinceLastMessage != nil && *settings.SinceLastMessage < 0) {
|
premiumTier := rpc.PremiumClient.GetTierByGuildId(guildId, true, botContext.Token, botContext.RateLimiter)
|
||||||
ctx.AbortWithStatusJSON(400, gin.H{
|
|
||||||
"success": false,
|
if premiumTier < premium.Premium {
|
||||||
"error": "Negative time period provided",
|
settings.SinceOpenWithNoResponse = nil
|
||||||
})
|
settings.SinceLastMessage = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time period cannot be negative, convertFromAutoCloseBody will not allow
|
||||||
|
|
||||||
|
if (settings.SinceOpenWithNoResponse != nil && *settings.SinceOpenWithNoResponse > maxLength) ||
|
||||||
|
(settings.SinceLastMessage != nil && *settings.SinceLastMessage > maxLength) {
|
||||||
|
ctx.JSON(400, utils.ErrorStr("Time period cannot be longer than %d days", maxDays))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,14 +53,9 @@ func PostAutoClose(ctx *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := dbclient.Client.AutoClose.Set(guildId, settings); err != nil {
|
if err := dbclient.Client.AutoClose.Set(guildId, settings); err != nil {
|
||||||
ctx.AbortWithStatusJSON(500, gin.H{
|
ctx.JSON(500, utils.ErrorJson(err))
|
||||||
"success": false,
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.JSON(200, gin.H{
|
ctx.JSON(200, utils.SuccessResponse)
|
||||||
"success": true,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
117
frontend/src/components/form/Duration.svelte
Normal file
117
frontend/src/components/form/Duration.svelte
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
<div class="col">
|
||||||
|
<div class="row label">
|
||||||
|
<div class="parent">
|
||||||
|
<label class="form-label">{label}</label>
|
||||||
|
{#if badge !== undefined}
|
||||||
|
<div class="badge" style="margin-left: 4px">{badge}</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row fields">
|
||||||
|
<div class="parent">
|
||||||
|
<input class="form-input" type="number" min=0 {disabled} bind:value={days}/>
|
||||||
|
<div class="period" class:disabled>D</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="parent">
|
||||||
|
<input class="form-input" type="number" min=0 {disabled} bind:value={hours}/>
|
||||||
|
<div class="period" class:disabled>H</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="parent">
|
||||||
|
<input class="form-input" type="number" min=0 {disabled} bind:value={minutes}/>
|
||||||
|
<div class="period" class:disabled>M</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export let label;
|
||||||
|
export let badge;
|
||||||
|
export let disabled = false; // note: bind:disabled isn't valid
|
||||||
|
|
||||||
|
export let days = 0;
|
||||||
|
export let hours = 0;
|
||||||
|
export let minutes = 0;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.col {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fields > .parent:not(:first-child) {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
border-top-right-radius: 0 !important;
|
||||||
|
border-bottom-right-radius: 0 !important;
|
||||||
|
width: 100px;
|
||||||
|
-moz-appearance: textfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.period.disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
input::-webkit-outer-spin-button,
|
||||||
|
input::-webkit-inner-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.parent {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.period {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-color: #2e3136 !important;
|
||||||
|
background-color: #2e3136 !important;
|
||||||
|
color: white !important;
|
||||||
|
outline: none;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
padding: 0 10px;
|
||||||
|
margin: 0 0 0.5em 0;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.badge) {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
background-color: #3472f7;
|
||||||
|
border-radius: 2px;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
</style>
|
@ -46,6 +46,7 @@
|
|||||||
border-color: #2e3136 !important;
|
border-color: #2e3136 !important;
|
||||||
border-left: none;
|
border-left: none;
|
||||||
color: white;
|
color: white;
|
||||||
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.svelte-emoji-picker__trigger:active) {
|
:global(.svelte-emoji-picker__trigger:active) {
|
||||||
|
@ -4,39 +4,117 @@
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div slot="body" class="body-wrapper">
|
<div slot="body" class="body-wrapper">
|
||||||
<div class="alert danger">
|
<form class="form-wrapper" on:submit|preventDefault={submit}>
|
||||||
<span class="alert-text">
|
<div class="row do-margin">
|
||||||
<span>
|
<Checkbox col4={true} label="Enabled" bind:value={data.enabled}/>
|
||||||
This feature is currently disabled. Discord will soon be releasing message threads,
|
<Checkbox col4={true} label="Close On User Leave" bind:value={data.on_user_leave}/>
|
||||||
which will incorporate their own auto-close behaviour. Thank you for your patience.
|
</div>
|
||||||
</span>
|
<div class="row">
|
||||||
</span>
|
<div class="col-2" style="flex-direction: row">
|
||||||
</div>
|
<Duration label="Since Open With No Response" badge="Premium" disabled={!isPremium}
|
||||||
|
bind:days={sinceOpenDays} bind:hours={sinceOpenHours} bind:minutes={sinceOpenMinutes}/>
|
||||||
|
</div>
|
||||||
|
<div class="col-2" style="flex-direction: row">
|
||||||
|
<Duration label="Since Last Message" badge="Premium" disabled={!isPremium}
|
||||||
|
bind:days={sinceLastDays} bind:hours={sinceLastHours} bind:minutes={sinceLastMinutes}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-1">
|
||||||
|
<Button icon="fas fa-paper-plane" fullWidth=true>Submit</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.body-wrapper {
|
.form-wrapper {
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-radius: 4px;
|
height: 100%;
|
||||||
padding: 10px 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.danger {
|
.row {
|
||||||
background-color: #fc727a;
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.alert-text {
|
.form-wrapper > .row:not(:last-child) {
|
||||||
width: 95%
|
margin-bottom: 1%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Card from "../Card.svelte";
|
import Card from "../Card.svelte";
|
||||||
|
import Checkbox from "../form/Checkbox.svelte";
|
||||||
|
import {notifyError, notifySuccess, withLoadingScreen} from "../../js/util";
|
||||||
|
import axios from "axios";
|
||||||
|
import {API_URL} from "../../js/constants";
|
||||||
|
import Duration from "../form/Duration.svelte";
|
||||||
|
import {toDays, toHours, toMinutes} from "../../js/timeutil";
|
||||||
|
import Button from "../Button.svelte";
|
||||||
|
|
||||||
|
export let guildId;
|
||||||
|
|
||||||
|
let data = {};
|
||||||
|
let isPremium = false;
|
||||||
|
|
||||||
|
let sinceOpenDays = 0, sinceOpenHours = 0, sinceOpenMinutes = 0;
|
||||||
|
let sinceLastDays = 0, sinceLastHours = 0, sinceLastMinutes = 0;
|
||||||
|
|
||||||
|
async function submit() {
|
||||||
|
data.since_open_with_no_response = sinceOpenDays * 86400 + sinceOpenHours * 3600 + sinceOpenMinutes * 60;
|
||||||
|
data.since_last_message = sinceLastDays * 86400 + sinceLastHours * 3600 + sinceLastMinutes * 60;
|
||||||
|
|
||||||
|
const res = await axios.post(`${API_URL}/api/${guildId}/autoclose`, data);
|
||||||
|
if (res.status !== 200) {
|
||||||
|
notifyError(res.data.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
notifySuccess('Auto close settings updated successfully');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadPremium() {
|
||||||
|
const res = await axios.get(`${API_URL}/api/${guildId}/premium`);
|
||||||
|
if (res.status !== 200) {
|
||||||
|
notifyError(res.data.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isPremium = res.data.premium;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadSettings() {
|
||||||
|
const res = await axios.get(`${API_URL}/api/${guildId}/autoclose`);
|
||||||
|
if (res.status !== 200) {
|
||||||
|
notifyError(res.data.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = res.data
|
||||||
|
update(res.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(data) {
|
||||||
|
if (data.since_open_with_no_response) {
|
||||||
|
sinceOpenDays = toDays(data.since_open_with_no_response);
|
||||||
|
sinceOpenHours = toHours(data.since_open_with_no_response);
|
||||||
|
sinceOpenMinutes = toMinutes(data.since_open_with_no_response);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.since_last_message) {
|
||||||
|
sinceLastDays = toDays(data.since_last_message);
|
||||||
|
sinceLastHours = toHours(data.since_last_message);
|
||||||
|
sinceLastMinutes = toMinutes(data.since_last_message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
withLoadingScreen(async () => {
|
||||||
|
await loadPremium();
|
||||||
|
await loadSettings();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
11
frontend/src/js/timeutil.js
Normal file
11
frontend/src/js/timeutil.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export function toDays(value) {
|
||||||
|
return Math.floor(value / 86400);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toHours(value) {
|
||||||
|
return Math.floor((value % 86400) / 3600);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toMinutes(value) {
|
||||||
|
return Math.floor((value % 86400 % 3600) / 60);
|
||||||
|
}
|
@ -258,7 +258,8 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
panels = res.data;
|
// convert button_style to string
|
||||||
|
panels = res.data.map((p) => Object.assign({}, p, {button_style: p.button_style.toString()}));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadMultiPanels() {
|
async function loadMultiPanels() {
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="main-col">
|
<div class="main-col">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<SettingsCard guildId={guildId}/>
|
<SettingsCard {guildId}/>
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<AutoCloseCard/>
|
<AutoCloseCard {guildId}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="right-col">
|
<div class="right-col">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<ClaimsCard guildId={guildId}/>
|
<ClaimsCard {guildId}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user