Custom naming schemes

This commit is contained in:
rxdn 2022-06-18 20:00:44 +01:00
parent b8d2bf03ff
commit d5f8e58b3a
11 changed files with 233 additions and 23 deletions

View File

@ -23,6 +23,11 @@ import (
const freePanelLimit = 3
var (
placeholderPattern = regexp.MustCompile(`%(\w+)%`)
channelNamePattern = regexp.MustCompile(`^[\w\d-_]+$`)
)
type panelBody struct {
ChannelId uint64 `json:"channel_id,string"`
MessageId uint64 `json:"message_id,string"`
@ -40,6 +45,7 @@ type panelBody struct {
ButtonStyle component.ButtonStyle `json:"button_style,string"`
ButtonLabel string `json:"button_label"`
FormId *int `json:"form_id"`
NamingScheme *string `json:"naming_scheme"`
}
func (p *panelBody) IntoPanelMessageData(customId string, isPremium bool) panelMessageData {
@ -153,6 +159,7 @@ func CreatePanel(ctx *gin.Context) {
ButtonStyle: int(data.ButtonStyle),
ButtonLabel: data.ButtonLabel,
FormId: data.FormId,
NamingScheme: data.NamingScheme,
}
panelId, err := dbclient.Client.Panel.Create(panel)
@ -305,6 +312,11 @@ func (p *panelBody) doValidations(ctx *gin.Context, guildId uint64) bool {
}
}
if !p.verifyNamingScheme() {
ctx.JSON(400, utils.ErrorStr("Invalid naming scheme: ensure that the naming scheme is less than 100 characters and the placeholders you have used are valid"))
return false
}
return true
}
@ -459,6 +471,47 @@ func (p *panelBody) verifyTeams(guildId uint64) (bool, error) {
return dbclient.Client.SupportTeam.AllTeamsExistForGuild(guildId, p.Teams)
}
func (p *panelBody) verifyNamingScheme() bool {
if p.NamingScheme == nil {
return true
}
if len(*p.NamingScheme) == 0 {
p.NamingScheme = nil
return true
}
// Substitute out {} users may use by mistake, spaces for dashes and convert to lowercase
p.NamingScheme = utils.Ptr(strings.ReplaceAll(*p.NamingScheme, "{", "%"))
p.NamingScheme = utils.Ptr(strings.ReplaceAll(*p.NamingScheme, "}", "%"))
p.NamingScheme = utils.Ptr(strings.ReplaceAll(*p.NamingScheme, " ", "-"))
p.NamingScheme = utils.Ptr(strings.ToLower(*p.NamingScheme))
if len(*p.NamingScheme) > 100 {
return false
}
// We must remove all placeholders from the string to check whether the rest of the string is legal
noPlaceholders := *p.NamingScheme
// Validate placeholders used
validPlaceholders := []string{"id", "username"}
for _, match := range placeholderPattern.FindAllStringSubmatch(*p.NamingScheme, -1) {
if len(match) < 2 { // Infallible
return false
}
placeholder := match[1]
if !utils.Contains(validPlaceholders, placeholder) {
return false
}
noPlaceholders = strings.Replace(noPlaceholders, match[0], "", -1) // match[0] = "%placeholder%"
}
return channelNamePattern.MatchString(noPlaceholders)
}
func getRoleHashSet(guildId uint64) (*collections.Set[uint64], error) {
ctx, err := botcontext.ContextForGuild(guildId)
if err != nil {

View File

@ -17,6 +17,7 @@ func ListPanels(ctx *gin.Context) {
Emoji types.Emoji `json:"emote"`
Mentions []string `json:"mentions"`
Teams []int `json:"teams"`
UseServerDefaultNamingScheme bool `json:"use_server_default_naming_scheme"`
}
guildId := ctx.Keys["guildid"].(uint64)
@ -79,6 +80,7 @@ func ListPanels(ctx *gin.Context) {
Emoji: types.NewEmoji(p.EmojiName, p.EmojiId),
Mentions: mentions,
Teams: teamIds,
UseServerDefaultNamingScheme: p.NamingScheme == nil,
}
return nil

View File

@ -125,6 +125,7 @@ func UpdatePanel(ctx *gin.Context) {
ButtonStyle: int(data.ButtonStyle),
ButtonLabel: data.ButtonLabel,
FormId: data.FormId,
NamingScheme: data.NamingScheme,
}
if err = dbclient.Client.Panel.Update(panel); err != nil {

View File

@ -12,7 +12,8 @@
"sirv-cli": "^1.0.0",
"svelte-emoji-selector": "^1.0.1",
"svelte-router-spa": "^6.0.2",
"svelte-select": "^3.17.0"
"svelte-select": "^3.17.0",
"svelte-tooltip": "^1.2.0"
},
"devDependencies": {
"@babel/core": "^7.14.6",
@ -3176,6 +3177,51 @@
"integrity": "sha512-2gzDDMDhM+ImDaLEZVlnlHVY1340Y368tT4Qk5IwLnCeRJ4zV3cVwliVGacoHy7iCDukcGXzKwDzG/hTTcaljg==",
"dev": true
},
"node_modules/svelte-tooltip": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/svelte-tooltip/-/svelte-tooltip-1.2.0.tgz",
"integrity": "sha512-FmaiNoCGkXBqF5AAscv+NdLtVIhywpqcaKw7mP1IksLJPMW3QaOPB8G23CHPQWde0J3A0NdCDQ3DI2isWGsDbg==",
"dependencies": {
"sirv-cli": "^0.4.4"
}
},
"node_modules/svelte-tooltip/node_modules/@polka/url": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@polka/url/-/url-0.5.0.tgz",
"integrity": "sha512-oZLYFEAzUKyi3SKnXvj32ZCEGH6RDnao7COuCVhDydMS9NrCSVXhM79VaKyP5+Zc33m0QXEd2DN3UkU7OsHcfw=="
},
"node_modules/svelte-tooltip/node_modules/sirv": {
"version": "0.4.6",
"resolved": "https://registry.npmjs.org/sirv/-/sirv-0.4.6.tgz",
"integrity": "sha512-rYpOXlNbpHiY4nVXxuDf4mXPvKz1reZGap/LkWp9TvcZ84qD/nPBjjH/6GZsgIjVMbOslnY8YYULAyP8jMn1GQ==",
"dependencies": {
"@polka/url": "^0.5.0",
"mime": "^2.3.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/svelte-tooltip/node_modules/sirv-cli": {
"version": "0.4.6",
"resolved": "https://registry.npmjs.org/sirv-cli/-/sirv-cli-0.4.6.tgz",
"integrity": "sha512-/Vj85/kBvPL+n9ibgX6FicLE8VjidC1BhlX67PYPBfbBAphzR6i0k0HtU5c2arejfU3uzq8l3SYPCwl1x7z6Ww==",
"dependencies": {
"console-clear": "^1.1.0",
"get-port": "^3.2.0",
"kleur": "^3.0.0",
"local-access": "^1.0.1",
"sade": "^1.4.0",
"sirv": "^0.4.6",
"tinydate": "^1.0.0"
},
"bin": {
"sirv": "index.js"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/terser": {
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.7.0.tgz",
@ -5606,6 +5652,44 @@
"integrity": "sha512-2gzDDMDhM+ImDaLEZVlnlHVY1340Y368tT4Qk5IwLnCeRJ4zV3cVwliVGacoHy7iCDukcGXzKwDzG/hTTcaljg==",
"dev": true
},
"svelte-tooltip": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/svelte-tooltip/-/svelte-tooltip-1.2.0.tgz",
"integrity": "sha512-FmaiNoCGkXBqF5AAscv+NdLtVIhywpqcaKw7mP1IksLJPMW3QaOPB8G23CHPQWde0J3A0NdCDQ3DI2isWGsDbg==",
"requires": {
"sirv-cli": "^0.4.4"
},
"dependencies": {
"@polka/url": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@polka/url/-/url-0.5.0.tgz",
"integrity": "sha512-oZLYFEAzUKyi3SKnXvj32ZCEGH6RDnao7COuCVhDydMS9NrCSVXhM79VaKyP5+Zc33m0QXEd2DN3UkU7OsHcfw=="
},
"sirv": {
"version": "0.4.6",
"resolved": "https://registry.npmjs.org/sirv/-/sirv-0.4.6.tgz",
"integrity": "sha512-rYpOXlNbpHiY4nVXxuDf4mXPvKz1reZGap/LkWp9TvcZ84qD/nPBjjH/6GZsgIjVMbOslnY8YYULAyP8jMn1GQ==",
"requires": {
"@polka/url": "^0.5.0",
"mime": "^2.3.1"
}
},
"sirv-cli": {
"version": "0.4.6",
"resolved": "https://registry.npmjs.org/sirv-cli/-/sirv-cli-0.4.6.tgz",
"integrity": "sha512-/Vj85/kBvPL+n9ibgX6FicLE8VjidC1BhlX67PYPBfbBAphzR6i0k0HtU5c2arejfU3uzq8l3SYPCwl1x7z6Ww==",
"requires": {
"console-clear": "^1.1.0",
"get-port": "^3.2.0",
"kleur": "^3.0.0",
"local-access": "^1.0.1",
"sade": "^1.4.0",
"sirv": "^0.4.6",
"tinydate": "^1.0.0"
}
}
}
},
"terser": {
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.7.0.tgz",

View File

@ -28,6 +28,7 @@
"sirv-cli": "^1.0.0",
"svelte-emoji-selector": "^1.0.1",
"svelte-router-spa": "^6.0.2",
"svelte-select": "^3.17.0"
"svelte-select": "^3.17.0",
"svelte-tooltip": "^1.2.0"
}
}

View File

@ -1,17 +1,39 @@
<div class:col-1={col1} class:col-2={col2} class:col-3={col3} class:col-4={col4}>
{#if label !== undefined}
<label for="input" class="form-label">{label}</label>
<div class="label-wrapper">
<label for="input" class="form-label">
{label}
</label>
{#if tooltipText !== undefined}
<div style="margin-bottom: 5px">
<Tooltip tip={tooltipText} top color="#121212">
{#if tooltipLink !== undefined}
<a href={tooltipLink} target="_blank">
<i class="fas fa-circle-info form-label tooltip-icon"></i>
</a>
{:else}
<i class="fas fa-circle-info form-label tooltip-icon"></i>
{/if}
</Tooltip>
</div>
{/if}
</div>
{/if}
<input id="input" class="form-input" placeholder="{placeholder}" disabled="{disabled}" on:input on:change
bind:value={value}>
</div>
<script>
import Tooltip from 'svelte-tooltip';
export let value;
export let label;
export let placeholder;
export let disabled = false;
export let tooltipText = undefined;
export let tooltipLink = undefined;
export let col1 = false;
export let col2 = false;
export let col3 = false;
@ -22,4 +44,15 @@
input {
width: 100%;
}
.label-wrapper {
display: flex;
flex-direction: row;
align-items: center;
gap: 5px;
}
.tooltip-icon {
cursor: pointer;
}
</style>

View File

@ -41,8 +41,6 @@
on:toggle={handleEmojiTypeChange} />
</div>
{#if data.use_custom_emoji}
<!--bind:selectedValue={selectedMentions}
on:select={updateMentions}-->
<div class="multiselect-super">
<Select items={emojis}
Item={EmojiItem}
@ -106,6 +104,29 @@
<Input col2={true} label="Large Image URL" bind:value={data.image_url} placeholder="https://example.com/image.png" />
<Input col2={true} label="Small Image URL" bind:value={data.thumbnail_url} placeholder="https://example.com/image.png" />
</div>
<div class="row">
<div class="col-2">
<label for="naming-scheme-wrapper" class="form-label">Naming Scheme</label>
<div class="row" id="naming-scheme-wrapper">
<div>
<label class="form-label">Use Server Default</label>
<Toggle hideLabel
toggledColor="#66bb6a"
untoggledColor="#ccc"
bind:toggled={data.use_server_default_naming_scheme} />
</div>
<div class="col-fill">
{#if !data.use_server_default_naming_scheme}
<Input label="Naming Scheme"
bind:value={data.naming_scheme}
placeholder="ticket-%id%"
tooltipText="Click here for the full placeholder list"
tooltipLink="https://docs.ticketsbot.net" />
{/if}
</div>
</div>
</div>
</div>
</div>
</div>
</form>
@ -126,6 +147,7 @@
import Dropdown from "../form/Dropdown.svelte";
import Checkbox from "../form/Checkbox.svelte";
import Toggle from "svelte-toggle";
import Slider from "../form/Slider.svelte";
export let guildId;
export let seedDefault = true;
@ -247,8 +269,7 @@
.forEach((mention) => selectedMentions.push(mention));
}
$: data.emote = data.emote;
console.log(data.emote)
data.emote = data.emote;
tempColour = intToColour(data.colour);
}
@ -271,7 +292,8 @@
button_style: "1",
form_id: "null",
channel_id: channels.find((c) => c.type === 0).id,
category_id: channels.find((c) => c.type === 4).id
category_id: channels.find((c) => c.type === 4).id,
use_server_default_naming_scheme: true,
};
} else {
applyOverrides();
@ -295,6 +317,12 @@
height: 100%;
}
.col-fill {
display: flex;
flex-direction: column;
flex-grow: 1;
}
:global(.col-1-3) {
display: flex;
flex-direction: column;
@ -359,6 +387,10 @@
position: absolute;
}
#naming-scheme-wrapper {
gap: 10px;
}
:global(.multiselect-super) {
display: flex;
width: 100%;

View File

@ -13,7 +13,7 @@
<!--<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700" rel="stylesheet">-->
<!-- Icons -->
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v6.1.1/css/all.css">
<!-- GA -->
<script async src='https://www.google-analytics.com/analytics.js'

View File

@ -38,7 +38,7 @@
export let currentRoute;
export let params = {};
let guildId = currentRoute.namedParams.id
let guildId = currentRoute.namedParams.id;
import Head from '../includes/Head.svelte'
import LoadingScreen from '../includes/LoadingScreen.svelte'

4
go.mod
View File

@ -81,3 +81,7 @@ require (
gopkg.in/yaml.v2 v2.4.0 // indirect
nhooyr.io/websocket v1.8.4 // indirect
)
replace (
github.com/TicketsBot/database => "../database"
)

View File

@ -4,9 +4,9 @@ import (
"github.com/rxdn/gdl/objects/channel/message"
)
func ContainsString(slice []string, target string) bool {
func Contains[T comparable](slice []T, value T) bool {
for _, elem := range slice {
if elem == target {
if elem == value {
return true
}
}
@ -15,8 +15,8 @@ func ContainsString(slice []string, target string) bool {
}
func Reverse(slice []message.Message) []message.Message {
for i := len(slice)/2-1; i >= 0; i-- {
opp := len(slice)-1-i
for i := len(slice)/2 - 1; i >= 0; i-- {
opp := len(slice) - 1 - i
slice[i], slice[opp] = slice[opp], slice[i]
}
return slice