Skip to content

Commit 46637f7

Browse files
committed
Tag colour support n stuff
1 parent 6f24e45 commit 46637f7

11 files changed

Lines changed: 164 additions & 37 deletions

File tree

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
ALTER TABLE "tags_tags"
22
ADD "attachments" TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[],
3-
ADD "container" BOOLEAN NOT NULL DEFAULT FALSE,
4-
ADD "containerColor" INT;
3+
ADD "color" INT NOT NULL DEFAULT -1;

backend/src/plugin/core/commandEngine/parsing/prefixParser.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ function readNamedArg(
146146
}
147147

148148
// maybe best not to make this immutable? it causes typing issues
149-
if (value instanceof Array) {
149+
if (option.array) {
150150
output.pushTo(key, ...(value as unknown[]));
151151
} else {
152152
output.set(key, value);

backend/src/plugin/core/public/helper/customOptionTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Color } from "#common/schema/general.ts";
12
import { isSnowflake } from "#common/snowflake.ts";
23
import type { StringReader } from "#common/stringReader.ts";
34
import {

backend/src/plugin/logging/config/tags.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,35 @@
1+
import { paddedHex } from "#common/general.ts";
12
import { messageTemplate } from "#common/schema/message.ts";
23
import { GuildView } from "#common/template/guild.ts";
34
import { UserView } from "#common/template/user.ts";
45
import { eventConfig } from "#plugin/logging/config/index.ts";
5-
import { m } from "mousetache";
6+
import type { Tag } from "#plugin/tags/public/tag.ts";
7+
import { m, type InferView } from "mousetache";
68

79
export const TagView = m.object({
810
name: m.terminal(),
911
content: m.terminal({ noEscape: true }),
12+
color: m.terminal({ noEscape: true }),
13+
attachments: m.array(m.terminal({ noEscape: true }))
1014
});
1115

16+
export type TagView = InferView<typeof TagView>;
17+
18+
export function makeTagView(tag: Tag): TagView {
19+
return {
20+
name: tag.name,
21+
content: tag.content,
22+
get color() {
23+
if (tag.color !== -1) {
24+
return paddedHex(tag.color, 3)
25+
} else {
26+
return "None";
27+
}
28+
},
29+
attachments: tag.attachments,
30+
};
31+
}
32+
1233
export const TagCreateEvent = eventConfig(
1334
messageTemplate(
1435
m.object({
@@ -50,6 +71,8 @@ export const TagEditEvent = eventConfig(
5071

5172
nameChanged: m.terminal(),
5273
contentChanged: m.terminal(),
74+
colorChanged: m.terminal(),
75+
attachmentsChanged: m.terminal(),
5376
}),
5477
),
5578
{
@@ -71,6 +94,18 @@ export const TagEditEvent = eventConfig(
7194
name: "New Content",
7295
value: "{{#contentChanged}}{{newTag.content}}{{/contentChanged}}",
7396
},
97+
{
98+
name: "Color",
99+
value: "{{#colorChanged}}{{oldTag.color}} → {{newTag.color}}{{/colorChanged}}",
100+
},
101+
{
102+
name: "Old Attachments",
103+
value: "{{#attachmentsChanged}}{{#oldTag.attachments}}{{.}}\n{{/oldTag.attachments}}{{/attachmentsChanged}}",
104+
},
105+
{
106+
name: "New Attachments",
107+
value: "{{#attachmentsChanged}}{{#newTag.attachments}}{{.}}\n{{/newTag.attachments}}{{/attachmentsChanged}}",
108+
},
74109
],
75110
color: "yellow",
76111
footer: { text: "Actor ID: {{actor.id}}" },

backend/src/plugin/logging/logger/tags.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Nullable } from "#common/general.ts";
22
import { makeGuildView } from "#common/template/guild.ts";
33
import { makeMemberUserView } from "#common/template/user.ts";
44
import type { SquirrelDiscordContext } from "#discord/index.ts";
5+
import { makeTagView } from "#plugin/logging/config/tags.ts";
56
import { logEvent } from "#plugin/logging/helper/logging.ts";
67
import {
78
onTagCreated,
@@ -30,7 +31,7 @@ async function handleCreate(
3031
return {
3132
guild: makeGuildView(guild),
3233
actor: makeMemberUserView(actor),
33-
tag,
34+
tag: makeTagView(tag),
3435
};
3536
},
3637
});
@@ -50,13 +51,17 @@ async function handleEdit(
5051
return {
5152
guild: makeGuildView(guild),
5253
actor: makeMemberUserView(actor),
53-
oldTag: oldTag,
54-
newTag: {
54+
oldTag: makeTagView(oldTag),
55+
newTag: makeTagView({
5556
name: changes.name ?? oldTag.name,
5657
content: changes.content ?? oldTag.content,
57-
},
58+
color: changes.color ?? oldTag.color,
59+
attachments: changes.attachments ?? oldTag.attachments,
60+
}),
5861
nameChanged: changes.name !== null,
5962
contentChanged: changes.content !== null,
63+
colorChanged: changes.color !== null,
64+
attachmentsChanged: changes.attachments !== null,
6065
};
6166
},
6267
});
@@ -75,7 +80,7 @@ async function handleDelete(
7580
return {
7681
guild: makeGuildView(guild),
7782
actor: makeMemberUserView(actor),
78-
tag,
83+
tag: makeTagView(tag),
7984
};
8085
},
8186
});

backend/src/plugin/tags/command/tag.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@ import { MAX_TAG_NAME_LENGTH } from "#plugin/tags/constants.ts";
66
import { autocompleteTags } from "#plugin/tags/helper/autocompletion.ts";
77
import { tagsConfigStore } from "#plugin/tags/index.ts";
88
import { getTag } from "#plugin/tags/storage/tags.ts";
9-
import { Gallery, GalleryItem, Text } from "oceanic-component-helper";
10-
import type { MessageComponent } from "oceanic.js";
9+
import {
10+
Container,
11+
Gallery,
12+
GalleryItem,
13+
Text,
14+
} from "oceanic-component-helper";
15+
import type { ContainerComponent, MessageComponent } from "oceanic.js";
1116

1217
export default defineCommand({
1318
name: ["tag", "tagsend", "sendtag"],
@@ -41,10 +46,14 @@ export default defineCommand({
4146
return;
4247
}
4348

44-
const components: MessageComponent[] = [Text(tag.content)];
49+
const container = Container([Text(tag.content)]);
50+
51+
if (tag.color !== -1) {
52+
container.accentColor = tag.color;
53+
}
4554

4655
if (tag.attachments.length !== 0) {
47-
components.push(
56+
container.components.push(
4857
Gallery(
4958
tag.attachments.map((attachment) =>
5059
GalleryItem(attachment),
@@ -53,6 +62,6 @@ export default defineCommand({
5362
);
5463
}
5564

56-
await ctx.respond({ components });
65+
await ctx.respond({ components: [container] });
5766
},
5867
});

backend/src/plugin/tags/command/tagCreate.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ import {
77
MAX_TAG_CONTENT_LENGTH,
88
MAX_TAG_NAME_LENGTH,
99
} from "#plugin/tags/constants.ts";
10+
import { attachments, optionalColor } from "#plugin/tags/helper/customOptionTypes.ts";
1011
import { tagsConfigStore } from "#plugin/tags/index.ts";
1112
import { onTagCreated } from "#plugin/tags/public/extensionPoints.ts";
13+
import type { Tag } from "#plugin/tags/public/tag.ts";
1214
import { createTag } from "#plugin/tags/storage/tags.ts";
1315

1416
const logger = moduleLogger();
@@ -24,20 +26,25 @@ export default defineCommand({
2426
description: "The name of the tag to create.",
2527
required: true,
2628
position: 0,
27-
maxLength: MAX_TAG_NAME_LENGTH,
29+
2830
greedy: false,
31+
maxLength: MAX_TAG_NAME_LENGTH,
2932
},
3033
content: {
3134
type: "string",
3235
name: ["content", "c"],
3336
required: true,
3437
position: 1,
38+
3539
maxLength: MAX_TAG_CONTENT_LENGTH,
3640
},
3741
attachments: {
38-
type: "string",
42+
type: attachments,
3943
name: ["attachments", "a", "attachment", "attach"],
40-
array: true,
44+
},
45+
color: {
46+
type: optionalColor,
47+
name: ["color", "c"],
4148
},
4249
},
4350

@@ -48,11 +55,13 @@ export default defineCommand({
4855
(permissions) => permissions.tagCreate,
4956
),
5057
async run(ctx, args) {
51-
const success = await createTag(ctx.squirrelCtx.db, ctx.guild.id, {
58+
const tag: Tag = {
5259
name: args.name,
5360
content: args.content,
5461
attachments: args.attachments ?? [],
55-
});
62+
color: args.color ?? -1,
63+
};
64+
const success = await createTag(ctx.squirrelCtx.db, ctx.guild.id, tag);
5665

5766
if (!success) {
5867
await ctx.respond(
@@ -62,7 +71,7 @@ export default defineCommand({
6271
}
6372

6473
onTagCreated
65-
.fire(ctx.squirrelCtx, ctx.guild, ctx.member, args)
74+
.fire(ctx.squirrelCtx, ctx.guild, ctx.member, tag)
6675
.catch((error) => logger.error?.("Error in onTagCreated", error));
6776

6877
await ctx.respond(

backend/src/plugin/tags/command/tagEdit.ts

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { escapeMarkdown } from "#common/discord/markdown.ts";
22
import { moduleLogger } from "#common/logger/index.ts";
3+
import { Color } from "#common/schema/general.ts";
4+
import type { StringReader } from "#common/stringReader.ts";
35
import { defineCommand } from "#plugin/core/public/extensionPoints.ts";
46
import { permissionsGuard } from "#plugin/core/public/helper/commandGuards.ts";
57
import { icons } from "#plugin/core/public/icons.ts";
@@ -8,6 +10,7 @@ import {
810
MAX_TAG_NAME_LENGTH,
911
} from "#plugin/tags/constants.ts";
1012
import { autocompleteTags } from "#plugin/tags/helper/autocompletion.ts";
13+
import { attachments, optionalColor } from "#plugin/tags/helper/customOptionTypes.ts";
1114
import { tagsConfigStore } from "#plugin/tags/index.ts";
1215
import { onTagEdited } from "#plugin/tags/public/extensionPoints.ts";
1316
import { updateTag } from "#plugin/tags/storage/tags.ts";
@@ -25,22 +28,36 @@ export default defineCommand({
2528
description: "The name of the tag to modify.",
2629
required: true,
2730
position: 0,
28-
maxLength: MAX_TAG_NAME_LENGTH,
31+
2932
greedy: false,
33+
maxLength: MAX_TAG_NAME_LENGTH,
3034

3135
autocomplete: (ctx, value) => autocompleteTags(ctx, value),
3236
},
37+
newName: {
38+
type: "string",
39+
name: ["new-name", "rename", "nn", "rn"],
40+
description: "Specify a new name to rename to.",
41+
42+
maxLength: MAX_TAG_NAME_LENGTH,
43+
},
3344
content: {
3445
type: "string",
3546
name: ["content", "c"],
47+
description: "Modify the content.",
3648
position: 1,
49+
3750
maxLength: MAX_TAG_CONTENT_LENGTH,
3851
},
39-
newName: {
40-
type: "string",
41-
name: ["new-name", "rename", "nn", "rn"],
42-
description: "Change the name of the tag to something else.",
43-
maxLength: MAX_TAG_NAME_LENGTH,
52+
attachments: {
53+
type: attachments,
54+
name: ["attachments", "attach", "a"],
55+
description: "Modify attachments, specified as links separated by spaces or simply 'clear' to remove them."
56+
},
57+
color: {
58+
type: optionalColor,
59+
name: ["color", "c"],
60+
description: "Modify the color of the tag. This will display "
4461
},
4562
},
4663

@@ -54,6 +71,8 @@ export default defineCommand({
5471
const changes = {
5572
name: args.newName,
5673
content: args.content,
74+
attachments: args.attachments,
75+
color: args.color,
5776
};
5877

5978
const oldTag = await updateTag(
@@ -71,7 +90,7 @@ export default defineCommand({
7190
}
7291

7392
// check after we know the tag exists
74-
if (args.newName === null && args.content === null) {
93+
if (changes.name === null && changes.content === null && changes.attachments === null && changes.color === null) {
7594
await ctx.respond(`${icons.error} No changes specified!`);
7695
return;
7796
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { Color } from "#common/schema/general.ts";
2+
import type { StringReader } from "#common/stringReader.ts";
3+
4+
const CLEAR_PATTERN = /clear\b/iy;
5+
6+
export const attachments = {
7+
name: "attachments",
8+
read(reader: StringReader) {
9+
if (reader.skipOver(CLEAR_PATTERN)) {
10+
return [];
11+
}
12+
13+
const result = [];
14+
15+
while (reader.canRead()) {
16+
const prevCursor = reader.cursor;
17+
const url = reader.readWord();
18+
19+
if (!(url.startsWith("https://") || url.startsWith("http://"))) {
20+
reader.cursor = prevCursor;
21+
break;
22+
}
23+
24+
result.push(url);
25+
}
26+
27+
return result;
28+
},
29+
};
30+
31+
/** Parses a color or returns -1 if none is supplied. */
32+
export const optionalColor = {
33+
name: "color",
34+
read(reader: StringReader) {
35+
const word = reader.readWord();
36+
37+
if (word.toLowerCase() === "none") {
38+
return -1;
39+
}
40+
41+
const result = Color.safeParse(word);
42+
43+
if (!result.success) {
44+
return null;
45+
}
46+
47+
return result.data;
48+
},
49+
};

backend/src/plugin/tags/public/tag.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@ export interface Tag {
22
name: string;
33
content: string;
44
attachments: readonly string[];
5+
/** The tag's color (-1 for transparent) */
6+
color: number;
57
}

0 commit comments

Comments
 (0)