Skip to content

Commit 2f23da2

Browse files
fix: return number from embed parser (#120)
* fix: return number from embed parser * docs: add dev notes deny parser * docs: update badge * test: add test for resolveColor
1 parent 8c92f9f commit 2f23da2

7 files changed

Lines changed: 118 additions & 12 deletions

File tree

packages/tagscript-plugin-discord/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
**A tagscript plugin to work with discord.js**
66

7-
[![npm](https://img.shields.io/npm/dw/tagscript)](https://www.npmjs.com/package/tagscript)
7+
[![npm](https://img.shields.io/npm/dw/tagscript-plugin-discord)](https://www.npmjs.com/package/tagscript-plugin-discord)
88
[![codecov](https://codecov.io/gh/imranbarbhuiya/tagscript/branch/main/graph/badge.svg?precision=2&flag=tagscript-plugin-discord)](https://codecov.io/gh/imranbarbhuiya/tagscript)
9-
[![npm](https://img.shields.io/npm/v/tagscript?color=crimson&logo=npm&style=flat-square)](https://www.npmjs.com/package/tagscript)
9+
[![npm](https://img.shields.io/npm/v/tagscript-plugin-discord?color=crimson&logo=npm&style=flat-square)](https://www.npmjs.com/package/tagscript-plugin-discord)
1010

1111
</div>
1212

packages/tagscript-plugin-discord/src/lib/Parsers/AllowDeny.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,35 @@ import { BaseParser, type IParser, type Context } from 'tagscript';
66
* it will send the response if one is given. Multiple role, user or channel
77
* requirements can be given, and should be split by a `,`.
88
*
9+
* Aliases: allowlist, whitelist
10+
*
911
* @example
1012
* ```yaml
1113
* {require(user,role,channel):response}
1214
* ```
13-
* Aliases: allowlist, whitelist
1415
* @example
1516
* ```yaml
1617
* {require(Moderator)}
1718
* {require(#general, #bot-commands):This tag can only be run in #general and #bot-cmds.}
1819
* {require(757425366209134764, 668713062186090506, 737961895356792882):You aren't allowed to use this tag.}
1920
* ```
21+
*
22+
* Developers need to add the check themselves.
23+
* @example
24+
* ```ts
25+
* const { Interpreter } = require("tagscript")
26+
* const { RequiredParser } = require("tagscript-plugin-discord")
27+
*
28+
* const ts = new Interpreter(new RequiredParser())
29+
*
30+
* const result = await ts.run("{require(id1, id2):You aren't allowed to use this tag.}")
31+
*
32+
* if (!result.actions.require.ids.includes(interaction.user.id)) {
33+
* // add channel, role check here or check using name instead of id
34+
* return interaction.reply(result.actions.require.message)
35+
* }
36+
*
37+
* ```
2038
*/
2139
export class RequiredParser extends BaseParser implements IParser {
2240
public constructor() {
@@ -49,6 +67,22 @@ export class RequiredParser extends BaseParser implements IParser {
4967
* {deny(#general, #chat):This tag can't be run in #general and #chat.}
5068
* {deny(757425366209134764, 668713062186090506, 737961895356792882):You aren't allowed to use this tag.}
5169
* ```
70+
*
71+
* Developers need to add the check themselves.
72+
* @example
73+
* ```ts
74+
* const { Interpreter } = require("tagscript")
75+
* const { DenyParser } = require("tagscript-plugin-discord")
76+
*
77+
* const ts = new Interpreter(new DenyParser())
78+
*
79+
* const result = await ts.run("{require(id1, id2):You aren't allowed to use this tag.}")
80+
*
81+
* if (result.actions.deny.ids.includes(interaction.user.id)) {
82+
* // add channel, role check here or check using name instead of id
83+
* return interaction.reply(result.actions.deny.message)
84+
* }
85+
* ```
5286
*/
5387

5488
export class DenyParser extends BaseParser implements IParser {

packages/tagscript-plugin-discord/src/lib/Parsers/Embed.ts

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { BaseParser, split, type Context, type IParser, type Awaitable } from 'tagscript';
22

3+
import { resolveColor } from '../Utils';
4+
35
import type { EmbedData, APIEmbed } from 'discord.js';
46

57
/**
@@ -13,12 +15,12 @@ import type { EmbedData, APIEmbed } from 'discord.js';
1315
* @example
1416
* Using JSON
1517
* ```yaml
16-
* { embed: json }
18+
* {embed: json}
1719
* ```
1820
* @example
1921
* ```yaml
20-
* { embed: { "title": "Hello!", "description": "This is a test embed." } }
21-
* { embed: {
22+
* {embed: { "title": "Hello!", "description": "This is a test embed." } }
23+
* {embed: {
2224
* "title": "Here's a random duck!",
2325
* "image": { "url": "https://random-d.uk/api/randomimg" },
2426
* "color": 15194415
@@ -27,14 +29,26 @@ import type { EmbedData, APIEmbed } from 'discord.js';
2729
* @example
2830
* Using properties
2931
* ```yaml
30-
* { embed(property): value }
32+
* {embed(property):value}
3133
* ```
3234
* @example
3335
* ```yaml
34-
* { embed(color): 0x37b2cb }
35-
* { embed(title): Rules }
36-
* { embed(description): Follow these rules to ensure a good experience in our server! }
37-
* { embed(field): Rule 1|Respect everyone you speak to.|false }
36+
* {embed(color): 0x37b2cb}
37+
* {embed(title): Rules}
38+
* {embed(description): Follow these rules to ensure a good experience in our server!}
39+
* {embed(field): Rule 1|Respect everyone you speak to.|false}
40+
* ```
41+
* Developers need to construct the embed builder themselves with the output of the tag.
42+
* @example
43+
* ```ts
44+
* const { Interpreter } = require("tagscript")
45+
* const { EmbedParser } = require("tagscript-plugin-discord")
46+
*
47+
* const ts = new Interpreter(new EmbedParser())
48+
* const result = await ts.run('{embed: { "title": "Hello!", "description": "This is a test embed." }}')
49+
*
50+
* // You might need to change the embed object before passing to `EmbedBuilder`. Changes such as change thumbnail and image value from string to object.
51+
* const embed = new EmbedBuilder(response.actions.embed);
3852
* ```
3953
* @remarks
4054
* The return type depends on user's input. So it might not be `EmbedData | APIEmbed`. So use a typeguard to check.
@@ -63,6 +77,13 @@ export class EmbedParser extends BaseParser implements IParser {
6377
});
6478
}
6579

80+
if (ctx.tag.parameter === 'color') {
81+
return this.returnEmbed(ctx, {
82+
// This can return number but it should be handled by the dev
83+
color: resolveColor(ctx.tag.payload!) as number
84+
});
85+
}
86+
6687
return this.returnEmbed(ctx, { [ctx.tag.parameter]: ctx.tag.payload });
6788
}
6889

@@ -73,7 +94,9 @@ export class EmbedParser extends BaseParser implements IParser {
7394
* @returns
7495
*/
7596
protected parseEmbedJSON(payload: string): Awaitable<APIEmbed | EmbedData> {
76-
return JSON.parse(payload);
97+
const parsedResult = JSON.parse(payload);
98+
if (parsedResult.color) parsedResult.color = resolveColor(parsedResult.color);
99+
return parsedResult;
77100
}
78101

79102
private returnEmbed(ctx: Context, data: APIEmbed | EmbedData): string {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export * from './CommandInteraction';
2+
export * from './resolveColor';
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { resolveColor as DJSResolveColor, type ColorResolvable } from 'discord.js';
2+
3+
/**
4+
* Resolves a color to a number. This function doesn't throw for invalid colors but returns the input
5+
*
6+
* @param color - The color to resolve
7+
* @returns
8+
*/
9+
export const resolveColor = (color: string): number | string => {
10+
try {
11+
return Number(color) || DJSResolveColor(color as ColorResolvable);
12+
} catch {
13+
return color;
14+
}
15+
};

packages/tagscript-plugin-discord/tests/Parsers/Embed.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,18 @@ describe('EmbedParser', () => {
9494
author: { name: 'Mahir', icon_url: 'https://example.com/image.png' }
9595
});
9696
});
97+
98+
test('GIVEN color in JSON THEN resolve it to hex color', async () => {
99+
const text = '{embed:{"color":"0x00ff00"}}';
100+
101+
expect((await ts.run(text)).actions.embed).toStrictEqual({
102+
color: 65_280
103+
});
104+
});
105+
106+
test.each(['Red', '#ed4245', 0xed4245])('GIVEN color %j in property THEN resolve it to hex color', async (color) => {
107+
expect((await ts.run(`{embed(color):${color}}`)).actions.embed).toStrictEqual({
108+
color: 0xed4245
109+
});
110+
});
97111
});
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { resolveColor } from '../../src';
2+
3+
describe('ResolveColor', () => {
4+
test('GIVEN a color name THEN return valid hex code', () => {
5+
expect(resolveColor('Red')).toBe(0xed4245);
6+
});
7+
8+
test('GIVEN a hex code starts with # THEN return valid hex code', () => {
9+
expect(resolveColor('#FF0000')).toBe(0xff0000);
10+
});
11+
12+
test('GIVEN a hex code starts with 0x THEN return valid hex code', () => {
13+
expect(resolveColor('0xFF0000')).toBe(0xff0000);
14+
});
15+
16+
test('GIVEN an invalid color THEN return the input', () => {
17+
expect(resolveColor('invalid')).toBe('invalid');
18+
});
19+
});

0 commit comments

Comments
 (0)