@spatulox/simplediscordbot 3.0.2 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +1 -1
- package/CHANGELOG.md +6 -0
- package/LICENSE.md +21 -21
- package/README.md +75 -65
- package/dist/index.d.mts +96 -2
- package/dist/index.d.ts +96 -2
- package/dist/index.js +120 -13
- package/dist/index.mjs +108 -2
- package/package.json +1 -1
- package/prompt-message-for-wiki.txt +17 -17
package/.env.example
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
DISCORD_BOT_TOKEN=example
|
|
1
|
+
DISCORD_BOT_TOKEN=example
|
|
2
2
|
DISCORD_BOT_DEV # Define this one with anything you want, the value is not important as long as the env var is defined
|
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
Date format : dd/mm/yyyy
|
|
3
3
|
|
|
4
|
+
### 21/09/2026 - 3.1.0
|
|
5
|
+
- Add :
|
|
6
|
+
- `ChartManager` : text based charts for Components V2, since Discord has no chart component. `progressBar()` / `progressBars()` draw unicode gauges (`CPU : ███░░░░░░░ 32.7 %`), `sparkline()` / `sparklines()` draw one line curves (`▁▂▃▅▇█▆▄▃▂▁`). Each method returns a `TextDisplayBuilder` to drop into a `ContainerBuilder`, and the grouped forms render every row in a single one so a dashboard costs 1 component instead of N against the 40 components budget of a message
|
|
7
|
+
- `sparklines()` takes an opt-in `sharedScale` so stacked curves are scaled against the same bounds and stay comparable, instead of each one filling the whole height
|
|
8
|
+
- Values are clamped (`value > max`, negative values, `max = 0`), a constant serie renders as a straight line instead of dividing by zero, an empty serie or an empty row list renders `—` (`TextDisplayBuilder.setContent()` rejects an empty string), and non finite points are dropped instead of flattening the whole curve
|
|
9
|
+
|
|
4
10
|
### 28/04/2026 - 2.2.1
|
|
5
11
|
- Changes :
|
|
6
12
|
- Add dependency to @spatulox/utils
|
package/LICENSE.md
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 Spatulox
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Spatulox
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,65 +1,75 @@
|
|
|
1
|
-
|
|
2
|
-
# Simple Discord Bot 
|
|
3
|
-
|
|
4
|
-
[](https://npmjs.com/package/@spatulox/simplediscordbot)
|
|
5
|
-
[](https://npmjs.com/package/@spatulox/simplediscordbot)
|
|
6
|
-
[](LICENSE)
|
|
7
|
-
|
|
8
|
-
> **TypeScript Discord.js framework** - Simple, powerful, framework-ready. Built for developers who want clean bot architecture.
|
|
9
|
-
|
|
10
|
-
## ✨ Features :
|
|
11
|
-
> - Fully compatible with discordjs
|
|
12
|
-
> - Include the [DiscordInterationManager](https://github.com/Spatulox/DiscordInteractionManager) package
|
|
13
|
-
> - Simple Log package
|
|
14
|
-
> - Provides easy Managers to avoid repetitive code everywhere
|
|
15
|
-
> - Simple yet powerful builders (Embeds, Modals, SelectMenus, Components, Buttons) that rely on discord.js for full compatibility
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
|
|
1
|
+
|
|
2
|
+
# Simple Discord Bot 
|
|
3
|
+
|
|
4
|
+
[](https://npmjs.com/package/@spatulox/simplediscordbot)
|
|
5
|
+
[](https://npmjs.com/package/@spatulox/simplediscordbot)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
> **TypeScript Discord.js framework** - Simple, powerful, framework-ready. Built for developers who want clean bot architecture.
|
|
9
|
+
|
|
10
|
+
## ✨ Features :
|
|
11
|
+
> - Fully compatible with discordjs
|
|
12
|
+
> - Include the [DiscordInterationManager](https://github.com/Spatulox/DiscordInteractionManager) package
|
|
13
|
+
> - Simple Log package
|
|
14
|
+
> - Provides easy Managers to avoid repetitive code everywhere
|
|
15
|
+
> - Simple yet powerful builders (Embeds, Modals, SelectMenus, Components, Buttons) that rely on discord.js for full compatibility
|
|
16
|
+
> - Text based charts (progress bars, sparklines) for Components V2, since Discord has no chart component
|
|
17
|
+
|
|
18
|
+
# Don't forget to check the [wiki](https://github.com/spatulox-discord/SimpleDiscordBot/wiki)
|
|
19
|
+
|
|
20
|
+
## 🧩 Starting a new bot ?
|
|
21
|
+
|
|
22
|
+
> Don't start from an empty folder : the [**DiscordBotTemplate**](https://github.com/Spatulox/DiscordBotTemplate)
|
|
23
|
+
> is a ready-to-use project built on this framework. Login, logging, modules, interaction
|
|
24
|
+
> routing and slash command deployment are already wired, you only fill in your ids.
|
|
25
|
+
>
|
|
26
|
+
> It has [its own wiki](https://github.com/Spatulox/DiscordBotTemplate/wiki) too, and a
|
|
27
|
+
> `feat/multi-bot` branch for several bots living in one repository.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
```bash
|
|
31
|
+
npm i @spatulox/simplediscordbot
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
Env Variables:
|
|
37
|
+
```
|
|
38
|
+
DISCORD_BOT_TOKEN="" // Your bot Token
|
|
39
|
+
```
|
|
40
|
+
<details>
|
|
41
|
+
<summary>Note for v2.1.2 and older</summary>
|
|
42
|
+
|
|
43
|
+
> You also need to set up this env var :
|
|
44
|
+
> ```
|
|
45
|
+
> DISCORD_BOT_CLIENTID="" // You bot ClientID
|
|
46
|
+
> ```
|
|
47
|
+
|
|
48
|
+
</details>
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import {Bot, BotConfig, SimpleColor, Time} from "@spatulox/simplediscordbot";
|
|
52
|
+
import {Client, Events, GatewayIntentBits} from "discord.js";
|
|
53
|
+
|
|
54
|
+
const client = new Client({
|
|
55
|
+
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages]
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const config: BotConfig = {
|
|
59
|
+
defaultSimpleColor: SimpleColor.blue,
|
|
60
|
+
botName: "Simple Discord Bot",
|
|
61
|
+
log: {
|
|
62
|
+
info: {channelId: "YOUR_LOG_CHANNEL_ID", console: true, discord: true},
|
|
63
|
+
error: {channelId: "YOUR_LOG_CHANNEL_ID", console: true, discord: true},
|
|
64
|
+
warn: {channelId: "YOUR_LOG_CHANNEL_ID", console: true, discord: true},
|
|
65
|
+
debug: {channelId: "YOUR_LOG_CHANNEL_ID", console: true, discord: false},
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const bot = new Bot(client, config);
|
|
70
|
+
|
|
71
|
+
bot.client.on(Events.ClientReady, async () => {
|
|
72
|
+
Bot.setRandomActivity(randomActivityList, Time.minute.MIN_10.toMilliseconds());
|
|
73
|
+
console.log("Bot ready! ✨");
|
|
74
|
+
});
|
|
75
|
+
```
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EmbedBuilder, ContainerBuilder, ActionRowBuilder, MessageActionRowComponentBuilder, Message, TextChannel, DMChannel, ThreadChannel, MessageCreateOptions, User, GuildMember, BaseInteraction, InteractionResponse, Client, ActivityType, InteractionDeferReplyOptions, InteractionReplyOptions, InteractionEditReplyOptions, Snowflake, WebhookMessageCreateOptions, EmojiResolvable, Guild, BanOptions, GuildBasedChannel, GuildChannelCreateOptions, ForumChannel, NewsChannel, StageChannel, StartThreadOptions, VoiceChannel, Invite, Channel, Collection, GuildBan, ModalBuilder, StringSelectMenuBuilder, UserSelectMenuBuilder, RoleSelectMenuBuilder, MentionableSelectMenuBuilder, ChannelSelectMenuBuilder, ChannelType, SeparatorSpacingSize, ButtonBuilder, AttachmentBuilder, ButtonStyle } from 'discord.js';
|
|
1
|
+
import { EmbedBuilder, ContainerBuilder, ActionRowBuilder, MessageActionRowComponentBuilder, Message, TextChannel, DMChannel, ThreadChannel, MessageCreateOptions, User, GuildMember, BaseInteraction, InteractionResponse, Client, ActivityType, InteractionDeferReplyOptions, InteractionReplyOptions, InteractionEditReplyOptions, Snowflake, WebhookMessageCreateOptions, EmojiResolvable, Guild, BanOptions, GuildBasedChannel, GuildChannelCreateOptions, ForumChannel, NewsChannel, StageChannel, StartThreadOptions, VoiceChannel, Invite, Channel, Collection, GuildBan, ModalBuilder, StringSelectMenuBuilder, UserSelectMenuBuilder, RoleSelectMenuBuilder, MentionableSelectMenuBuilder, ChannelSelectMenuBuilder, ChannelType, SeparatorSpacingSize, ButtonBuilder, AttachmentBuilder, TextDisplayBuilder, ButtonStyle } from 'discord.js';
|
|
2
2
|
import { BaseSelectMenuBuilder } from '@discordjs/builders';
|
|
3
3
|
export { CacheManager, FileManager, Log, SimpleMutex, Time } from '@spatulox/utils';
|
|
4
4
|
|
|
@@ -737,6 +737,100 @@ declare class ComponentManager {
|
|
|
737
737
|
static toInteractionEdit(container: ContainerBuilder, file?: AttachmentBuilder | AttachmentBuilder[] | null, footer?: boolean): InteractionEditReplyOptions;
|
|
738
738
|
}
|
|
739
739
|
|
|
740
|
+
interface ProgressBarOptions {
|
|
741
|
+
/** Number of characters of the bar itself, default 10 */
|
|
742
|
+
width?: number;
|
|
743
|
+
/** Character of the filled part, default "█" */
|
|
744
|
+
filled?: string;
|
|
745
|
+
/** Character of the empty part, default "░" */
|
|
746
|
+
empty?: string;
|
|
747
|
+
/** Append the numeric value after the bar, default true */
|
|
748
|
+
showValue?: boolean;
|
|
749
|
+
/** Unit printed after the value, default "%" */
|
|
750
|
+
unit?: string;
|
|
751
|
+
/** Decimals of the printed value, default 1 */
|
|
752
|
+
decimals?: number;
|
|
753
|
+
/**
|
|
754
|
+
* Wrap the whole render in a ``` block. Discord renders TextDisplay with a proportional
|
|
755
|
+
* font, so labels of different widths never line up : only a code block gives a real
|
|
756
|
+
* monospace alignment. Default false.
|
|
757
|
+
*/
|
|
758
|
+
codeBlock?: boolean;
|
|
759
|
+
}
|
|
760
|
+
interface SparklineOptions {
|
|
761
|
+
/** Fixed low bound of the scale, default min(values) */
|
|
762
|
+
min?: number;
|
|
763
|
+
/** Fixed high bound of the scale, default max(values) */
|
|
764
|
+
max?: number;
|
|
765
|
+
/** Only keep the N most recent points, default 50 */
|
|
766
|
+
maxPoints?: number;
|
|
767
|
+
/** Append the last value after the curve, default true */
|
|
768
|
+
showValue?: boolean;
|
|
769
|
+
/** Unit printed after the value, default "" */
|
|
770
|
+
unit?: string;
|
|
771
|
+
/** Decimals of the printed value, default 1 */
|
|
772
|
+
decimals?: number;
|
|
773
|
+
/** See ProgressBarOptions.codeBlock. Default false */
|
|
774
|
+
codeBlock?: boolean;
|
|
775
|
+
}
|
|
776
|
+
interface ProgressBarRow {
|
|
777
|
+
label: string;
|
|
778
|
+
value: number;
|
|
779
|
+
/** Default 100 */
|
|
780
|
+
max?: number;
|
|
781
|
+
/** Overrides the options given to progressBars() for this row only */
|
|
782
|
+
options?: ProgressBarOptions;
|
|
783
|
+
}
|
|
784
|
+
interface SparklineRow {
|
|
785
|
+
label: string;
|
|
786
|
+
values: number[];
|
|
787
|
+
/** Overrides the options given to sparklines() for this row only */
|
|
788
|
+
options?: SparklineOptions;
|
|
789
|
+
}
|
|
790
|
+
interface SparklinesOptions extends SparklineOptions {
|
|
791
|
+
/**
|
|
792
|
+
* Scale every curve against the same bounds, deduced from all the series at once.
|
|
793
|
+
* Off by default : each curve then uses its own min/max and fills the whole height,
|
|
794
|
+
* which reads better alone but makes two stacked curves impossible to compare.
|
|
795
|
+
*/
|
|
796
|
+
sharedScale?: boolean;
|
|
797
|
+
}
|
|
798
|
+
declare class ChartManager {
|
|
799
|
+
/**
|
|
800
|
+
* A single gauge : `CPU : ███░░░░░░░ 32.7 %`
|
|
801
|
+
*/
|
|
802
|
+
static progressBar(label: string, value: number, max?: number, options?: ProgressBarOptions): TextDisplayBuilder;
|
|
803
|
+
/**
|
|
804
|
+
* Several gauges in ONE TextDisplay, so a dashboard of 6 metrics still costs 1 component.
|
|
805
|
+
* Labels are padded to the longest one so the bars start at the same column.
|
|
806
|
+
*/
|
|
807
|
+
static progressBars(rows: ProgressBarRow[], options?: ProgressBarOptions): TextDisplayBuilder;
|
|
808
|
+
/**
|
|
809
|
+
* A one line curve : `RAM : ▁▂▃▅▇█▆▄▃▂▁ 62 %`. One block character per point, the last
|
|
810
|
+
* value is printed at the end because a sparkline alone carries no scale.
|
|
811
|
+
*/
|
|
812
|
+
static sparkline(label: string, values: number[], options?: SparklineOptions): TextDisplayBuilder;
|
|
813
|
+
/**
|
|
814
|
+
* Several curves in ONE TextDisplay, the sparkline counterpart of progressBars().
|
|
815
|
+
* Labels are padded to the longest one so every curve starts at the same column.
|
|
816
|
+
*/
|
|
817
|
+
static sparklines(rows: SparklineRow[], options?: SparklinesOptions): TextDisplayBuilder;
|
|
818
|
+
/**
|
|
819
|
+
* Only fills the bounds that were not given explicitly, and only when asked : a row
|
|
820
|
+
* setting its own min/max still wins, since row options are spread after these.
|
|
821
|
+
*/
|
|
822
|
+
private static withSharedScale;
|
|
823
|
+
private static renderBar;
|
|
824
|
+
private static renderSpark;
|
|
825
|
+
private static formatValue;
|
|
826
|
+
/**
|
|
827
|
+
* TextDisplayBuilder.setContent() rejects an empty string, so an empty row list must
|
|
828
|
+
* still render something rather than throw at build time.
|
|
829
|
+
*/
|
|
830
|
+
private static wrap;
|
|
831
|
+
private static clamp01;
|
|
832
|
+
}
|
|
833
|
+
|
|
740
834
|
interface ButtonOptions {
|
|
741
835
|
label?: string;
|
|
742
836
|
emoji?: string;
|
|
@@ -839,4 +933,4 @@ declare const SimpleDiscordBotInfo: {
|
|
|
839
933
|
license: string;
|
|
840
934
|
};
|
|
841
935
|
|
|
842
|
-
export { Bot, type BotConfig, BotEnv, ButtonManager, type ButtonOptions, ComponentManager, type ComponentManagerCreate, type ComponentManagerField, type ComponentManagerFileInput, DiscordRegex, EmbedManager, GuildManager, type ModalField, ModalFieldType, ModalManager, type RandomBotActivity, ReactionManager, type SelectMenuCreateOption, type SelectMenuList, SelectMenuManager, SimpleColor, SimpleDiscordBotInfo, UserManager, WebhookManager };
|
|
936
|
+
export { Bot, type BotConfig, BotEnv, ButtonManager, type ButtonOptions, ChartManager, ComponentManager, type ComponentManagerCreate, type ComponentManagerField, type ComponentManagerFileInput, DiscordRegex, EmbedManager, GuildManager, type ModalField, ModalFieldType, ModalManager, type ProgressBarOptions, type ProgressBarRow, type RandomBotActivity, ReactionManager, type SelectMenuCreateOption, type SelectMenuList, SelectMenuManager, SimpleColor, SimpleDiscordBotInfo, type SparklineOptions, type SparklineRow, type SparklinesOptions, UserManager, WebhookManager };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EmbedBuilder, ContainerBuilder, ActionRowBuilder, MessageActionRowComponentBuilder, Message, TextChannel, DMChannel, ThreadChannel, MessageCreateOptions, User, GuildMember, BaseInteraction, InteractionResponse, Client, ActivityType, InteractionDeferReplyOptions, InteractionReplyOptions, InteractionEditReplyOptions, Snowflake, WebhookMessageCreateOptions, EmojiResolvable, Guild, BanOptions, GuildBasedChannel, GuildChannelCreateOptions, ForumChannel, NewsChannel, StageChannel, StartThreadOptions, VoiceChannel, Invite, Channel, Collection, GuildBan, ModalBuilder, StringSelectMenuBuilder, UserSelectMenuBuilder, RoleSelectMenuBuilder, MentionableSelectMenuBuilder, ChannelSelectMenuBuilder, ChannelType, SeparatorSpacingSize, ButtonBuilder, AttachmentBuilder, ButtonStyle } from 'discord.js';
|
|
1
|
+
import { EmbedBuilder, ContainerBuilder, ActionRowBuilder, MessageActionRowComponentBuilder, Message, TextChannel, DMChannel, ThreadChannel, MessageCreateOptions, User, GuildMember, BaseInteraction, InteractionResponse, Client, ActivityType, InteractionDeferReplyOptions, InteractionReplyOptions, InteractionEditReplyOptions, Snowflake, WebhookMessageCreateOptions, EmojiResolvable, Guild, BanOptions, GuildBasedChannel, GuildChannelCreateOptions, ForumChannel, NewsChannel, StageChannel, StartThreadOptions, VoiceChannel, Invite, Channel, Collection, GuildBan, ModalBuilder, StringSelectMenuBuilder, UserSelectMenuBuilder, RoleSelectMenuBuilder, MentionableSelectMenuBuilder, ChannelSelectMenuBuilder, ChannelType, SeparatorSpacingSize, ButtonBuilder, AttachmentBuilder, TextDisplayBuilder, ButtonStyle } from 'discord.js';
|
|
2
2
|
import { BaseSelectMenuBuilder } from '@discordjs/builders';
|
|
3
3
|
export { CacheManager, FileManager, Log, SimpleMutex, Time } from '@spatulox/utils';
|
|
4
4
|
|
|
@@ -737,6 +737,100 @@ declare class ComponentManager {
|
|
|
737
737
|
static toInteractionEdit(container: ContainerBuilder, file?: AttachmentBuilder | AttachmentBuilder[] | null, footer?: boolean): InteractionEditReplyOptions;
|
|
738
738
|
}
|
|
739
739
|
|
|
740
|
+
interface ProgressBarOptions {
|
|
741
|
+
/** Number of characters of the bar itself, default 10 */
|
|
742
|
+
width?: number;
|
|
743
|
+
/** Character of the filled part, default "█" */
|
|
744
|
+
filled?: string;
|
|
745
|
+
/** Character of the empty part, default "░" */
|
|
746
|
+
empty?: string;
|
|
747
|
+
/** Append the numeric value after the bar, default true */
|
|
748
|
+
showValue?: boolean;
|
|
749
|
+
/** Unit printed after the value, default "%" */
|
|
750
|
+
unit?: string;
|
|
751
|
+
/** Decimals of the printed value, default 1 */
|
|
752
|
+
decimals?: number;
|
|
753
|
+
/**
|
|
754
|
+
* Wrap the whole render in a ``` block. Discord renders TextDisplay with a proportional
|
|
755
|
+
* font, so labels of different widths never line up : only a code block gives a real
|
|
756
|
+
* monospace alignment. Default false.
|
|
757
|
+
*/
|
|
758
|
+
codeBlock?: boolean;
|
|
759
|
+
}
|
|
760
|
+
interface SparklineOptions {
|
|
761
|
+
/** Fixed low bound of the scale, default min(values) */
|
|
762
|
+
min?: number;
|
|
763
|
+
/** Fixed high bound of the scale, default max(values) */
|
|
764
|
+
max?: number;
|
|
765
|
+
/** Only keep the N most recent points, default 50 */
|
|
766
|
+
maxPoints?: number;
|
|
767
|
+
/** Append the last value after the curve, default true */
|
|
768
|
+
showValue?: boolean;
|
|
769
|
+
/** Unit printed after the value, default "" */
|
|
770
|
+
unit?: string;
|
|
771
|
+
/** Decimals of the printed value, default 1 */
|
|
772
|
+
decimals?: number;
|
|
773
|
+
/** See ProgressBarOptions.codeBlock. Default false */
|
|
774
|
+
codeBlock?: boolean;
|
|
775
|
+
}
|
|
776
|
+
interface ProgressBarRow {
|
|
777
|
+
label: string;
|
|
778
|
+
value: number;
|
|
779
|
+
/** Default 100 */
|
|
780
|
+
max?: number;
|
|
781
|
+
/** Overrides the options given to progressBars() for this row only */
|
|
782
|
+
options?: ProgressBarOptions;
|
|
783
|
+
}
|
|
784
|
+
interface SparklineRow {
|
|
785
|
+
label: string;
|
|
786
|
+
values: number[];
|
|
787
|
+
/** Overrides the options given to sparklines() for this row only */
|
|
788
|
+
options?: SparklineOptions;
|
|
789
|
+
}
|
|
790
|
+
interface SparklinesOptions extends SparklineOptions {
|
|
791
|
+
/**
|
|
792
|
+
* Scale every curve against the same bounds, deduced from all the series at once.
|
|
793
|
+
* Off by default : each curve then uses its own min/max and fills the whole height,
|
|
794
|
+
* which reads better alone but makes two stacked curves impossible to compare.
|
|
795
|
+
*/
|
|
796
|
+
sharedScale?: boolean;
|
|
797
|
+
}
|
|
798
|
+
declare class ChartManager {
|
|
799
|
+
/**
|
|
800
|
+
* A single gauge : `CPU : ███░░░░░░░ 32.7 %`
|
|
801
|
+
*/
|
|
802
|
+
static progressBar(label: string, value: number, max?: number, options?: ProgressBarOptions): TextDisplayBuilder;
|
|
803
|
+
/**
|
|
804
|
+
* Several gauges in ONE TextDisplay, so a dashboard of 6 metrics still costs 1 component.
|
|
805
|
+
* Labels are padded to the longest one so the bars start at the same column.
|
|
806
|
+
*/
|
|
807
|
+
static progressBars(rows: ProgressBarRow[], options?: ProgressBarOptions): TextDisplayBuilder;
|
|
808
|
+
/**
|
|
809
|
+
* A one line curve : `RAM : ▁▂▃▅▇█▆▄▃▂▁ 62 %`. One block character per point, the last
|
|
810
|
+
* value is printed at the end because a sparkline alone carries no scale.
|
|
811
|
+
*/
|
|
812
|
+
static sparkline(label: string, values: number[], options?: SparklineOptions): TextDisplayBuilder;
|
|
813
|
+
/**
|
|
814
|
+
* Several curves in ONE TextDisplay, the sparkline counterpart of progressBars().
|
|
815
|
+
* Labels are padded to the longest one so every curve starts at the same column.
|
|
816
|
+
*/
|
|
817
|
+
static sparklines(rows: SparklineRow[], options?: SparklinesOptions): TextDisplayBuilder;
|
|
818
|
+
/**
|
|
819
|
+
* Only fills the bounds that were not given explicitly, and only when asked : a row
|
|
820
|
+
* setting its own min/max still wins, since row options are spread after these.
|
|
821
|
+
*/
|
|
822
|
+
private static withSharedScale;
|
|
823
|
+
private static renderBar;
|
|
824
|
+
private static renderSpark;
|
|
825
|
+
private static formatValue;
|
|
826
|
+
/**
|
|
827
|
+
* TextDisplayBuilder.setContent() rejects an empty string, so an empty row list must
|
|
828
|
+
* still render something rather than throw at build time.
|
|
829
|
+
*/
|
|
830
|
+
private static wrap;
|
|
831
|
+
private static clamp01;
|
|
832
|
+
}
|
|
833
|
+
|
|
740
834
|
interface ButtonOptions {
|
|
741
835
|
label?: string;
|
|
742
836
|
emoji?: string;
|
|
@@ -839,4 +933,4 @@ declare const SimpleDiscordBotInfo: {
|
|
|
839
933
|
license: string;
|
|
840
934
|
};
|
|
841
935
|
|
|
842
|
-
export { Bot, type BotConfig, BotEnv, ButtonManager, type ButtonOptions, ComponentManager, type ComponentManagerCreate, type ComponentManagerField, type ComponentManagerFileInput, DiscordRegex, EmbedManager, GuildManager, type ModalField, ModalFieldType, ModalManager, type RandomBotActivity, ReactionManager, type SelectMenuCreateOption, type SelectMenuList, SelectMenuManager, SimpleColor, SimpleDiscordBotInfo, UserManager, WebhookManager };
|
|
936
|
+
export { Bot, type BotConfig, BotEnv, ButtonManager, type ButtonOptions, ChartManager, ComponentManager, type ComponentManagerCreate, type ComponentManagerField, type ComponentManagerFileInput, DiscordRegex, EmbedManager, GuildManager, type ModalField, ModalFieldType, ModalManager, type ProgressBarOptions, type ProgressBarRow, type RandomBotActivity, ReactionManager, type SelectMenuCreateOption, type SelectMenuList, SelectMenuManager, SimpleColor, SimpleDiscordBotInfo, type SparklineOptions, type SparklineRow, type SparklinesOptions, UserManager, WebhookManager };
|
package/dist/index.js
CHANGED
|
@@ -24,6 +24,7 @@ __export(index_exports, {
|
|
|
24
24
|
BotEnv: () => BotEnv,
|
|
25
25
|
ButtonManager: () => ButtonManager,
|
|
26
26
|
CacheManager: () => import_utils22.CacheManager,
|
|
27
|
+
ChartManager: () => ChartManager,
|
|
27
28
|
ComponentManager: () => ComponentManager,
|
|
28
29
|
DiscordRegex: () => DiscordRegex,
|
|
29
30
|
EmbedManager: () => EmbedManager,
|
|
@@ -763,7 +764,7 @@ var BotInteraction = class {
|
|
|
763
764
|
// package.json
|
|
764
765
|
var package_default = {
|
|
765
766
|
name: "@spatulox/simplediscordbot",
|
|
766
|
-
version: "3.0.
|
|
767
|
+
version: "3.0.3",
|
|
767
768
|
author: "Spatulox",
|
|
768
769
|
description: "Simple discord bot framework to set up a bot under 30 secondes",
|
|
769
770
|
exports: {
|
|
@@ -2465,30 +2466,135 @@ var ComponentManager = class {
|
|
|
2465
2466
|
}
|
|
2466
2467
|
};
|
|
2467
2468
|
|
|
2468
|
-
// src/manager/
|
|
2469
|
+
// src/manager/messages/ChartManager.ts
|
|
2469
2470
|
var import_discord17 = require("discord.js");
|
|
2471
|
+
var SPARK_BLOCKS = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
|
|
2472
|
+
var DEFAULT_MAX_POINTS = 50;
|
|
2473
|
+
var EMPTY = "\u2014";
|
|
2474
|
+
var ChartManager = class _ChartManager {
|
|
2475
|
+
/**
|
|
2476
|
+
* A single gauge : `CPU : ███░░░░░░░ 32.7 %`
|
|
2477
|
+
*/
|
|
2478
|
+
static progressBar(label, value, max = 100, options = {}) {
|
|
2479
|
+
const line = `${label} : ${_ChartManager.renderBar(value, max, options)}`;
|
|
2480
|
+
return new import_discord17.TextDisplayBuilder().setContent(_ChartManager.wrap(line, options.codeBlock));
|
|
2481
|
+
}
|
|
2482
|
+
/**
|
|
2483
|
+
* Several gauges in ONE TextDisplay, so a dashboard of 6 metrics still costs 1 component.
|
|
2484
|
+
* Labels are padded to the longest one so the bars start at the same column.
|
|
2485
|
+
*/
|
|
2486
|
+
static progressBars(rows, options = {}) {
|
|
2487
|
+
const width = rows.reduce((longest, row) => Math.max(longest, row.label.length), 0);
|
|
2488
|
+
const lines = rows.map((row) => {
|
|
2489
|
+
const opts = { ...options, ...row.options };
|
|
2490
|
+
return `${row.label.padEnd(width, " ")} : ${_ChartManager.renderBar(row.value, row.max ?? 100, opts)}`;
|
|
2491
|
+
});
|
|
2492
|
+
return new import_discord17.TextDisplayBuilder().setContent(_ChartManager.wrap(lines.join("\n"), options.codeBlock));
|
|
2493
|
+
}
|
|
2494
|
+
/**
|
|
2495
|
+
* A one line curve : `RAM : ▁▂▃▅▇█▆▄▃▂▁ 62 %`. One block character per point, the last
|
|
2496
|
+
* value is printed at the end because a sparkline alone carries no scale.
|
|
2497
|
+
*/
|
|
2498
|
+
static sparkline(label, values, options = {}) {
|
|
2499
|
+
const line = `${label} : ${_ChartManager.renderSpark(values, options)}`;
|
|
2500
|
+
return new import_discord17.TextDisplayBuilder().setContent(_ChartManager.wrap(line, options.codeBlock));
|
|
2501
|
+
}
|
|
2502
|
+
/**
|
|
2503
|
+
* Several curves in ONE TextDisplay, the sparkline counterpart of progressBars().
|
|
2504
|
+
* Labels are padded to the longest one so every curve starts at the same column.
|
|
2505
|
+
*/
|
|
2506
|
+
static sparklines(rows, options = {}) {
|
|
2507
|
+
const width = rows.reduce((longest, row) => Math.max(longest, row.label.length), 0);
|
|
2508
|
+
const base = _ChartManager.withSharedScale(rows, options);
|
|
2509
|
+
const lines = rows.map((row) => {
|
|
2510
|
+
const opts = { ...base, ...row.options };
|
|
2511
|
+
return `${row.label.padEnd(width, " ")} : ${_ChartManager.renderSpark(row.values, opts)}`;
|
|
2512
|
+
});
|
|
2513
|
+
return new import_discord17.TextDisplayBuilder().setContent(_ChartManager.wrap(lines.join("\n"), options.codeBlock));
|
|
2514
|
+
}
|
|
2515
|
+
/**
|
|
2516
|
+
* Only fills the bounds that were not given explicitly, and only when asked : a row
|
|
2517
|
+
* setting its own min/max still wins, since row options are spread after these.
|
|
2518
|
+
*/
|
|
2519
|
+
static withSharedScale(rows, options) {
|
|
2520
|
+
if (!options.sharedScale) return options;
|
|
2521
|
+
if (options.min !== void 0 && options.max !== void 0) return options;
|
|
2522
|
+
const points = rows.flatMap((row) => row.values).filter((v) => Number.isFinite(v));
|
|
2523
|
+
if (points.length === 0) return options;
|
|
2524
|
+
return {
|
|
2525
|
+
...options,
|
|
2526
|
+
min: options.min ?? Math.min(...points),
|
|
2527
|
+
max: options.max ?? Math.max(...points)
|
|
2528
|
+
};
|
|
2529
|
+
}
|
|
2530
|
+
static renderBar(value, max, options) {
|
|
2531
|
+
const width = Math.max(1, Math.trunc(options.width ?? 10));
|
|
2532
|
+
const filled = options.filled ?? "\u2588";
|
|
2533
|
+
const empty = options.empty ?? "\u2591";
|
|
2534
|
+
const ratio = _ChartManager.clamp01(value / max);
|
|
2535
|
+
const filledCount = Math.round(ratio * width);
|
|
2536
|
+
const bar = filled.repeat(filledCount) + empty.repeat(width - filledCount);
|
|
2537
|
+
return bar + _ChartManager.formatValue(value, options);
|
|
2538
|
+
}
|
|
2539
|
+
static renderSpark(values, options) {
|
|
2540
|
+
const maxPoints = Math.max(1, Math.trunc(options.maxPoints ?? DEFAULT_MAX_POINTS));
|
|
2541
|
+
const points = values.filter((v) => Number.isFinite(v)).slice(-maxPoints);
|
|
2542
|
+
if (points.length === 0) return EMPTY;
|
|
2543
|
+
const lo = options.min ?? Math.min(...points);
|
|
2544
|
+
const hi = options.max ?? Math.max(...points);
|
|
2545
|
+
const middle = SPARK_BLOCKS[Math.floor(SPARK_BLOCKS.length / 2) - 1];
|
|
2546
|
+
const curve = hi === lo ? middle.repeat(points.length) : points.map((v) => {
|
|
2547
|
+
const level = Math.round(_ChartManager.clamp01((v - lo) / (hi - lo)) * (SPARK_BLOCKS.length - 1));
|
|
2548
|
+
return SPARK_BLOCKS[level];
|
|
2549
|
+
}).join("");
|
|
2550
|
+
const last = points[points.length - 1];
|
|
2551
|
+
return curve + _ChartManager.formatValue(last, { ...options, unit: options.unit ?? "" });
|
|
2552
|
+
}
|
|
2553
|
+
static formatValue(value, options) {
|
|
2554
|
+
if (options.showValue === false) return "";
|
|
2555
|
+
const decimals = Math.max(0, Math.trunc(options.decimals ?? 1));
|
|
2556
|
+
const printable = Number.isFinite(value) ? value.toFixed(decimals) : "?";
|
|
2557
|
+
const unit = options.unit ?? "%";
|
|
2558
|
+
return unit ? ` ${printable} ${unit}` : ` ${printable}`;
|
|
2559
|
+
}
|
|
2560
|
+
/**
|
|
2561
|
+
* TextDisplayBuilder.setContent() rejects an empty string, so an empty row list must
|
|
2562
|
+
* still render something rather than throw at build time.
|
|
2563
|
+
*/
|
|
2564
|
+
static wrap(content, codeBlock) {
|
|
2565
|
+
const body = content.length > 0 ? content : EMPTY;
|
|
2566
|
+
return codeBlock ? "```\n" + body + "\n```" : body;
|
|
2567
|
+
}
|
|
2568
|
+
static clamp01(ratio) {
|
|
2569
|
+
if (!Number.isFinite(ratio)) return 0;
|
|
2570
|
+
return Math.min(1, Math.max(0, ratio));
|
|
2571
|
+
}
|
|
2572
|
+
};
|
|
2573
|
+
|
|
2574
|
+
// src/manager/interactible/ButtonManager.ts
|
|
2575
|
+
var import_discord18 = require("discord.js");
|
|
2470
2576
|
var ButtonManager = class _ButtonManager {
|
|
2471
2577
|
static create(options) {
|
|
2472
|
-
const btn = new
|
|
2578
|
+
const btn = new import_discord18.ButtonBuilder().setCustomId(options.customId).setLabel(options.label ?? "Button").setStyle(options.style).setDisabled(options.disabled ?? false);
|
|
2473
2579
|
if (options.emoji) {
|
|
2474
2580
|
btn.setEmoji(options.emoji);
|
|
2475
2581
|
}
|
|
2476
2582
|
return btn;
|
|
2477
2583
|
}
|
|
2478
2584
|
static primary(options) {
|
|
2479
|
-
return this.create({ ...options, style:
|
|
2585
|
+
return this.create({ ...options, style: import_discord18.ButtonStyle.Primary });
|
|
2480
2586
|
}
|
|
2481
2587
|
static success(options) {
|
|
2482
|
-
return this.create({ ...options, style:
|
|
2588
|
+
return this.create({ ...options, style: import_discord18.ButtonStyle.Success });
|
|
2483
2589
|
}
|
|
2484
2590
|
static secondary(options) {
|
|
2485
|
-
return this.create({ ...options, style:
|
|
2591
|
+
return this.create({ ...options, style: import_discord18.ButtonStyle.Secondary });
|
|
2486
2592
|
}
|
|
2487
2593
|
static danger(options) {
|
|
2488
|
-
return this.create({ ...options, style:
|
|
2594
|
+
return this.create({ ...options, style: import_discord18.ButtonStyle.Danger });
|
|
2489
2595
|
}
|
|
2490
2596
|
static link(options) {
|
|
2491
|
-
const btn = new
|
|
2597
|
+
const btn = new import_discord18.ButtonBuilder().setLabel(options.label).setStyle(import_discord18.ButtonStyle.Link).setURL(options.url);
|
|
2492
2598
|
if (options.emoji) btn.setEmoji(options.emoji);
|
|
2493
2599
|
return btn;
|
|
2494
2600
|
}
|
|
@@ -2500,7 +2606,7 @@ var ButtonManager = class _ButtonManager {
|
|
|
2500
2606
|
}
|
|
2501
2607
|
static row(but) {
|
|
2502
2608
|
const buttons = Array.isArray(but) ? but.slice(0, 5) : [but];
|
|
2503
|
-
return new
|
|
2609
|
+
return new import_discord18.ActionRowBuilder().addComponents(buttons);
|
|
2504
2610
|
}
|
|
2505
2611
|
static toMessage(button) {
|
|
2506
2612
|
return {
|
|
@@ -2510,7 +2616,7 @@ var ButtonManager = class _ButtonManager {
|
|
|
2510
2616
|
static toInteraction(button, ephemeral = false) {
|
|
2511
2617
|
return {
|
|
2512
2618
|
components: this.createRowsToReturn(button),
|
|
2513
|
-
flags: ephemeral ? [
|
|
2619
|
+
flags: ephemeral ? [import_discord18.MessageFlags.Ephemeral] : []
|
|
2514
2620
|
};
|
|
2515
2621
|
}
|
|
2516
2622
|
static toInteractionEdit(button) {
|
|
@@ -2521,10 +2627,10 @@ var ButtonManager = class _ButtonManager {
|
|
|
2521
2627
|
static createRowsToReturn(button) {
|
|
2522
2628
|
if (Array.isArray(button)) {
|
|
2523
2629
|
return button.map(
|
|
2524
|
-
(btn) => btn instanceof
|
|
2630
|
+
(btn) => btn instanceof import_discord18.ActionRowBuilder ? btn : _ButtonManager.row(btn)
|
|
2525
2631
|
);
|
|
2526
2632
|
}
|
|
2527
|
-
return button instanceof
|
|
2633
|
+
return button instanceof import_discord18.ActionRowBuilder ? [button] : [_ButtonManager.row(button)];
|
|
2528
2634
|
}
|
|
2529
2635
|
};
|
|
2530
2636
|
|
|
@@ -2538,7 +2644,7 @@ var DiscordRegex = class {
|
|
|
2538
2644
|
// Caractères spéciaux
|
|
2539
2645
|
static SPACE = "\u200B";
|
|
2540
2646
|
// URLs basiques
|
|
2541
|
-
static URL_REGEX = /(https?:\/\/[
|
|
2647
|
+
static URL_REGEX = /(https?:\/\/[^\s<>]+)/i;
|
|
2542
2648
|
/* DISCORD REGEX */
|
|
2543
2649
|
static USER_REGEX = /<@\d{18}>/;
|
|
2544
2650
|
static BOT_REGEX = /<@\d{19}>/;
|
|
@@ -2652,6 +2758,7 @@ var DiscordRegex = class {
|
|
|
2652
2758
|
BotEnv,
|
|
2653
2759
|
ButtonManager,
|
|
2654
2760
|
CacheManager,
|
|
2761
|
+
ChartManager,
|
|
2655
2762
|
ComponentManager,
|
|
2656
2763
|
DiscordRegex,
|
|
2657
2764
|
EmbedManager,
|
package/dist/index.mjs
CHANGED
|
@@ -743,7 +743,7 @@ var BotInteraction = class {
|
|
|
743
743
|
// package.json
|
|
744
744
|
var package_default = {
|
|
745
745
|
name: "@spatulox/simplediscordbot",
|
|
746
|
-
version: "3.0.
|
|
746
|
+
version: "3.0.3",
|
|
747
747
|
author: "Spatulox",
|
|
748
748
|
description: "Simple discord bot framework to set up a bot under 30 secondes",
|
|
749
749
|
exports: {
|
|
@@ -2468,6 +2468,111 @@ var ComponentManager = class {
|
|
|
2468
2468
|
}
|
|
2469
2469
|
};
|
|
2470
2470
|
|
|
2471
|
+
// src/manager/messages/ChartManager.ts
|
|
2472
|
+
import { TextDisplayBuilder as TextDisplayBuilder2 } from "discord.js";
|
|
2473
|
+
var SPARK_BLOCKS = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
|
|
2474
|
+
var DEFAULT_MAX_POINTS = 50;
|
|
2475
|
+
var EMPTY = "\u2014";
|
|
2476
|
+
var ChartManager = class _ChartManager {
|
|
2477
|
+
/**
|
|
2478
|
+
* A single gauge : `CPU : ███░░░░░░░ 32.7 %`
|
|
2479
|
+
*/
|
|
2480
|
+
static progressBar(label, value, max = 100, options = {}) {
|
|
2481
|
+
const line = `${label} : ${_ChartManager.renderBar(value, max, options)}`;
|
|
2482
|
+
return new TextDisplayBuilder2().setContent(_ChartManager.wrap(line, options.codeBlock));
|
|
2483
|
+
}
|
|
2484
|
+
/**
|
|
2485
|
+
* Several gauges in ONE TextDisplay, so a dashboard of 6 metrics still costs 1 component.
|
|
2486
|
+
* Labels are padded to the longest one so the bars start at the same column.
|
|
2487
|
+
*/
|
|
2488
|
+
static progressBars(rows, options = {}) {
|
|
2489
|
+
const width = rows.reduce((longest, row) => Math.max(longest, row.label.length), 0);
|
|
2490
|
+
const lines = rows.map((row) => {
|
|
2491
|
+
const opts = { ...options, ...row.options };
|
|
2492
|
+
return `${row.label.padEnd(width, " ")} : ${_ChartManager.renderBar(row.value, row.max ?? 100, opts)}`;
|
|
2493
|
+
});
|
|
2494
|
+
return new TextDisplayBuilder2().setContent(_ChartManager.wrap(lines.join("\n"), options.codeBlock));
|
|
2495
|
+
}
|
|
2496
|
+
/**
|
|
2497
|
+
* A one line curve : `RAM : ▁▂▃▅▇█▆▄▃▂▁ 62 %`. One block character per point, the last
|
|
2498
|
+
* value is printed at the end because a sparkline alone carries no scale.
|
|
2499
|
+
*/
|
|
2500
|
+
static sparkline(label, values, options = {}) {
|
|
2501
|
+
const line = `${label} : ${_ChartManager.renderSpark(values, options)}`;
|
|
2502
|
+
return new TextDisplayBuilder2().setContent(_ChartManager.wrap(line, options.codeBlock));
|
|
2503
|
+
}
|
|
2504
|
+
/**
|
|
2505
|
+
* Several curves in ONE TextDisplay, the sparkline counterpart of progressBars().
|
|
2506
|
+
* Labels are padded to the longest one so every curve starts at the same column.
|
|
2507
|
+
*/
|
|
2508
|
+
static sparklines(rows, options = {}) {
|
|
2509
|
+
const width = rows.reduce((longest, row) => Math.max(longest, row.label.length), 0);
|
|
2510
|
+
const base = _ChartManager.withSharedScale(rows, options);
|
|
2511
|
+
const lines = rows.map((row) => {
|
|
2512
|
+
const opts = { ...base, ...row.options };
|
|
2513
|
+
return `${row.label.padEnd(width, " ")} : ${_ChartManager.renderSpark(row.values, opts)}`;
|
|
2514
|
+
});
|
|
2515
|
+
return new TextDisplayBuilder2().setContent(_ChartManager.wrap(lines.join("\n"), options.codeBlock));
|
|
2516
|
+
}
|
|
2517
|
+
/**
|
|
2518
|
+
* Only fills the bounds that were not given explicitly, and only when asked : a row
|
|
2519
|
+
* setting its own min/max still wins, since row options are spread after these.
|
|
2520
|
+
*/
|
|
2521
|
+
static withSharedScale(rows, options) {
|
|
2522
|
+
if (!options.sharedScale) return options;
|
|
2523
|
+
if (options.min !== void 0 && options.max !== void 0) return options;
|
|
2524
|
+
const points = rows.flatMap((row) => row.values).filter((v) => Number.isFinite(v));
|
|
2525
|
+
if (points.length === 0) return options;
|
|
2526
|
+
return {
|
|
2527
|
+
...options,
|
|
2528
|
+
min: options.min ?? Math.min(...points),
|
|
2529
|
+
max: options.max ?? Math.max(...points)
|
|
2530
|
+
};
|
|
2531
|
+
}
|
|
2532
|
+
static renderBar(value, max, options) {
|
|
2533
|
+
const width = Math.max(1, Math.trunc(options.width ?? 10));
|
|
2534
|
+
const filled = options.filled ?? "\u2588";
|
|
2535
|
+
const empty = options.empty ?? "\u2591";
|
|
2536
|
+
const ratio = _ChartManager.clamp01(value / max);
|
|
2537
|
+
const filledCount = Math.round(ratio * width);
|
|
2538
|
+
const bar = filled.repeat(filledCount) + empty.repeat(width - filledCount);
|
|
2539
|
+
return bar + _ChartManager.formatValue(value, options);
|
|
2540
|
+
}
|
|
2541
|
+
static renderSpark(values, options) {
|
|
2542
|
+
const maxPoints = Math.max(1, Math.trunc(options.maxPoints ?? DEFAULT_MAX_POINTS));
|
|
2543
|
+
const points = values.filter((v) => Number.isFinite(v)).slice(-maxPoints);
|
|
2544
|
+
if (points.length === 0) return EMPTY;
|
|
2545
|
+
const lo = options.min ?? Math.min(...points);
|
|
2546
|
+
const hi = options.max ?? Math.max(...points);
|
|
2547
|
+
const middle = SPARK_BLOCKS[Math.floor(SPARK_BLOCKS.length / 2) - 1];
|
|
2548
|
+
const curve = hi === lo ? middle.repeat(points.length) : points.map((v) => {
|
|
2549
|
+
const level = Math.round(_ChartManager.clamp01((v - lo) / (hi - lo)) * (SPARK_BLOCKS.length - 1));
|
|
2550
|
+
return SPARK_BLOCKS[level];
|
|
2551
|
+
}).join("");
|
|
2552
|
+
const last = points[points.length - 1];
|
|
2553
|
+
return curve + _ChartManager.formatValue(last, { ...options, unit: options.unit ?? "" });
|
|
2554
|
+
}
|
|
2555
|
+
static formatValue(value, options) {
|
|
2556
|
+
if (options.showValue === false) return "";
|
|
2557
|
+
const decimals = Math.max(0, Math.trunc(options.decimals ?? 1));
|
|
2558
|
+
const printable = Number.isFinite(value) ? value.toFixed(decimals) : "?";
|
|
2559
|
+
const unit = options.unit ?? "%";
|
|
2560
|
+
return unit ? ` ${printable} ${unit}` : ` ${printable}`;
|
|
2561
|
+
}
|
|
2562
|
+
/**
|
|
2563
|
+
* TextDisplayBuilder.setContent() rejects an empty string, so an empty row list must
|
|
2564
|
+
* still render something rather than throw at build time.
|
|
2565
|
+
*/
|
|
2566
|
+
static wrap(content, codeBlock) {
|
|
2567
|
+
const body = content.length > 0 ? content : EMPTY;
|
|
2568
|
+
return codeBlock ? "```\n" + body + "\n```" : body;
|
|
2569
|
+
}
|
|
2570
|
+
static clamp01(ratio) {
|
|
2571
|
+
if (!Number.isFinite(ratio)) return 0;
|
|
2572
|
+
return Math.min(1, Math.max(0, ratio));
|
|
2573
|
+
}
|
|
2574
|
+
};
|
|
2575
|
+
|
|
2471
2576
|
// src/manager/interactible/ButtonManager.ts
|
|
2472
2577
|
import {
|
|
2473
2578
|
ButtonBuilder as ButtonBuilder2,
|
|
@@ -2546,7 +2651,7 @@ var DiscordRegex = class {
|
|
|
2546
2651
|
// Caractères spéciaux
|
|
2547
2652
|
static SPACE = "\u200B";
|
|
2548
2653
|
// URLs basiques
|
|
2549
|
-
static URL_REGEX = /(https?:\/\/[
|
|
2654
|
+
static URL_REGEX = /(https?:\/\/[^\s<>]+)/i;
|
|
2550
2655
|
/* DISCORD REGEX */
|
|
2551
2656
|
static USER_REGEX = /<@\d{18}>/;
|
|
2552
2657
|
static BOT_REGEX = /<@\d{19}>/;
|
|
@@ -2659,6 +2764,7 @@ export {
|
|
|
2659
2764
|
BotEnv,
|
|
2660
2765
|
ButtonManager,
|
|
2661
2766
|
CacheManager,
|
|
2767
|
+
ChartManager,
|
|
2662
2768
|
ComponentManager,
|
|
2663
2769
|
DiscordRegex,
|
|
2664
2770
|
EmbedManager,
|
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
Rédige une page de wiki au format suivant :
|
|
2
|
-
|
|
3
|
-
- Utilise le langage Markdown.
|
|
4
|
-
- Commence par un en‑tête `## [NomDeLaClasse / NomDuModule]`.
|
|
5
|
-
- Ajoute un bref paragraphe introductif qui explique en 1–2 phrases ce que cette classe / ce module fait.
|
|
6
|
-
- Ensuite, utilise des sous‑titres courtes (au maximum 6 mots) comme :
|
|
7
|
-
- `### Core behavior`
|
|
8
|
-
- `### Main methods`
|
|
9
|
-
- `### Exemple d’utilisation`
|
|
10
|
-
- `### Typique usage`
|
|
11
|
-
- Pour chaque section, rédige 2–3 phrases max, claires et concises.
|
|
12
|
-
- Évite les résumés / conclusions à la fin; limite le contenu exactement aux sections utiles.
|
|
13
|
-
- Si la classe est un helper ou un utils, explique aussi comment l’utilisateur doit l’instancier / l’utiliser dans son code.
|
|
14
|
-
- Si la classe fait partie d’un système plus large (ex. module system, manager, interaction router), place un petit paragraphe qui montre où elle s’insère dans l’architecture globale.
|
|
15
|
-
- Ne fais pas de liste de toutes les méthodes une par une; regroupe‑les par rôle ou par type (ex. “methods for reading files”, “methods for sending messages”).
|
|
16
|
-
- Termine par une petite section `### Typical usage` avec 1–2 exemples de code courts et concrets en TypeScript, montrant comment l’utiliser dans un contexte de bot Discord.
|
|
17
|
-
|
|
1
|
+
Rédige une page de wiki en anglais au format suivant :
|
|
2
|
+
|
|
3
|
+
- Utilise le langage Markdown.
|
|
4
|
+
- Commence par un en‑tête `## [NomDeLaClasse / NomDuModule]`.
|
|
5
|
+
- Ajoute un bref paragraphe introductif qui explique en 1–2 phrases ce que cette classe / ce module fait.
|
|
6
|
+
- Ensuite, utilise des sous‑titres courtes (au maximum 6 mots) comme :
|
|
7
|
+
- `### Core behavior`
|
|
8
|
+
- `### Main methods`
|
|
9
|
+
- `### Exemple d’utilisation`
|
|
10
|
+
- `### Typique usage`
|
|
11
|
+
- Pour chaque section, rédige 2–3 phrases max, claires et concises.
|
|
12
|
+
- Évite les résumés / conclusions à la fin; limite le contenu exactement aux sections utiles.
|
|
13
|
+
- Si la classe est un helper ou un utils, explique aussi comment l’utilisateur doit l’instancier / l’utiliser dans son code.
|
|
14
|
+
- Si la classe fait partie d’un système plus large (ex. module system, manager, interaction router), place un petit paragraphe qui montre où elle s’insère dans l’architecture globale.
|
|
15
|
+
- Ne fais pas de liste de toutes les méthodes une par une; regroupe‑les par rôle ou par type (ex. “methods for reading files”, “methods for sending messages”).
|
|
16
|
+
- Termine par une petite section `### Typical usage` avec 1–2 exemples de code courts et concrets en TypeScript, montrant comment l’utiliser dans un contexte de bot Discord.
|
|
17
|
+
|
|
18
18
|
Tout doit rester lisible, orienté développeur, sans entrer dans des détails implémentatoires trop bas‑niveau.
|