Skip to content

Commit 40ec851

Browse files
committed
migrate to typescript
1 parent a4a5f58 commit 40ec851

28 files changed

Lines changed: 385 additions & 197 deletions

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,5 +107,7 @@ ecosystem.config.js
107107
speech-*.mp3
108108
.env
109109
config.js
110+
config.ts
111+
channels.ts
110112
package-lock.json
111113
bun.lock

README.md

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ EdgarGPT is a Discord bot powered by OpenAI's latest GPT-5 model and Responses A
3232
```bash
3333
git clone https://github.com/Decryptu/EdgarGPT.git
3434
cd EdgarGPT
35-
bun install
35+
npm install
3636
```
3737

3838
### Configuration
@@ -45,12 +45,24 @@ TOKEN=your_discord_bot_token
4545
CLIENT_ID=your_discord_app_client_id
4646
```
4747

48-
Add allowed channel IDs in `channels.mjs`.
48+
Copy and configure the example files:
49+
50+
```bash
51+
cp config.example.ts config.ts
52+
cp channels.example.ts channels.ts
53+
```
54+
55+
Edit `channels.ts` to add your allowed channel IDs.
4956

5057
## Usage
5158

52-
**Production**: `bun start`
53-
**Development**: `bun run dev`
59+
**Development**: `npm run dev`
60+
61+
**Production**:
62+
```bash
63+
npm run build
64+
npm start
65+
```
5466

5567
### Examples
5668

@@ -68,9 +80,9 @@ Switch to reasoning mode:
6880

6981
## Tech Stack
7082

83+
- **TypeScript** - Type-safe development
7184
- **Discord.js v14** - Discord API integration
7285
- **OpenAI Responses API** - Unified AI interface
73-
- **ES Modules** - Modern JavaScript architecture
7486

7587
## License
7688

biome.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"$schema": "https://biomejs.dev/schemas/2.3.4/schema.json",
3+
"vcs": {
4+
"enabled": true,
5+
"clientKind": "git",
6+
"useIgnoreFile": true
7+
},
8+
"files": {
9+
"ignoreUnknown": false
10+
},
11+
"formatter": {
12+
"enabled": true,
13+
"indentStyle": "tab"
14+
},
15+
"linter": {
16+
"enabled": true,
17+
"rules": {
18+
"recommended": true
19+
}
20+
},
21+
"javascript": {
22+
"formatter": {
23+
"quoteStyle": "double"
24+
}
25+
},
26+
"assist": {
27+
"enabled": true,
28+
"actions": {
29+
"source": {
30+
"organizeImports": "on"
31+
}
32+
}
33+
}
34+
}

channels.mjs renamed to channels.example.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// channels.mjs
2-
const allowedChannels = [
1+
// channels.ts
2+
const allowedChannels: string[] = [
33
"1088435060170051714", // decrypt
44
"1097824934153170984", // idkzp
55
"1272543925286211606", // idksmp
Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
1-
// commands/gpt-model.js
1+
// commands/gpt-model.ts
2+
import { type ChatInputCommandInteraction, type Client, MessageFlags } from "discord.js";
23
import { GPT_MODELS, MODEL_SURNAMES } from "../config.js";
34
import setBotActivity from "../utils/setBotActivity.js";
4-
import { MessageFlags } from "discord.js";
55

6-
async function gptModel(interaction, client) {
6+
interface ExtendedClient extends Client {
7+
currentModel: string;
8+
}
9+
10+
async function gptModel(interaction: ChatInputCommandInteraction, client: ExtendedClient): Promise<void> {
711
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
812

913
const selectedModel = interaction.options.getString("model");
10-
11-
if (!Object.values(GPT_MODELS).includes(selectedModel)) {
12-
return await interaction.editReply({ content: "Modèle invalide." });
14+
15+
if (!selectedModel || !Object.values(GPT_MODELS).includes(selectedModel)) {
16+
await interaction.editReply({ content: "Modèle invalide." });
17+
return;
1318
}
1419

1520
const previousModel = MODEL_SURNAMES[client.currentModel];
@@ -18,8 +23,8 @@ async function gptModel(interaction, client) {
1823

1924
const newModel = MODEL_SURNAMES[selectedModel];
2025
console.log(`[MODEL SWITCH] ${interaction.user.username}: ${previousModel}${newModel}`);
21-
26+
2227
await interaction.editReply({ content: `Modèle activé: ${newModel}` });
2328
}
2429

25-
export default gptModel;
30+
export default gptModel;
Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
1-
// commands/image-edit.js
2-
import { EmbedBuilder, AttachmentBuilder } from "discord.js";
1+
// commands/image-edit.ts
2+
import { type ChatInputCommandInteraction, type Client, EmbedBuilder, AttachmentBuilder } from "discord.js";
33
import { toFile } from "openai";
44

5-
async function imageEdit(interaction, client) {
5+
interface ExtendedClient extends Client {
6+
openai: any;
7+
}
8+
9+
async function imageEdit(interaction: ChatInputCommandInteraction, client: ExtendedClient): Promise<void> {
610
await interaction.deferReply();
7-
11+
812
try {
913
const description = interaction.options.getString("description");
1014
const imageAttachment = interaction.options.getAttachment("image");
11-
15+
1216
if (!imageAttachment?.contentType?.startsWith("image/")) {
13-
return await interaction.editReply("Veuillez fournir une image valide (PNG, JPEG, WebP).");
17+
await interaction.editReply("Veuillez fournir une image valide (PNG, JPEG, WebP).");
18+
return;
1419
}
1520

1621
console.log(`[IMAGE EDIT] ${interaction.user.username}: "${description}"`);
@@ -30,7 +35,7 @@ async function imageEdit(interaction, client) {
3035
});
3136

3237
// Check if response has base64 data or URL
33-
let outputBuffer;
38+
let outputBuffer: Buffer;
3439
if (result.data[0].b64_json) {
3540
// Base64 response
3641
outputBuffer = Buffer.from(result.data[0].b64_json, "base64");
@@ -55,10 +60,10 @@ async function imageEdit(interaction, client) {
5560

5661
console.log(`[IMAGE EDIT] Success - Edited image sent to ${interaction.user.username}`);
5762
await interaction.editReply({ embeds: [embed], files: [attachment] });
58-
} catch (error) {
63+
} catch (error: any) {
5964
console.error("[IMAGE EDIT] Error:", error);
6065
await interaction.editReply("Échec de modification d'image. " + (error.message || ""));
6166
}
6267
}
6368

64-
export default imageEdit;
69+
export default imageEdit;
Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
1-
// commands/image-gpt.js
2-
import { EmbedBuilder, AttachmentBuilder, MessageFlags } from "discord.js";
1+
// commands/image-gpt.ts
2+
import { type ChatInputCommandInteraction, type Client, EmbedBuilder, AttachmentBuilder, MessageFlags } from "discord.js";
33
import { GPT_IMAGE_MODEL, GPT_IMAGE_SIZE, GPT_IMAGE_QUALITY } from "../config.js";
44

5-
async function imageGpt(interaction, client) {
5+
interface ExtendedClient extends Client {
6+
openai: any;
7+
}
8+
9+
async function imageGpt(interaction: ChatInputCommandInteraction, client: ExtendedClient): Promise<void> {
610
const description = interaction.options.getString("description");
7-
11+
812
if (!description) {
9-
return await interaction.reply({
13+
await interaction.reply({
1014
content: "Veuillez fournir une description.",
1115
flags: MessageFlags.Ephemeral
1216
});
17+
return;
1318
}
1419

1520
await interaction.deferReply();
@@ -26,7 +31,7 @@ async function imageGpt(interaction, client) {
2631
});
2732

2833
// Check if response has base64 data or URL
29-
let buffer;
34+
let buffer: Buffer;
3035
if (response.data[0].b64_json) {
3136
// Base64 response
3237
buffer = Buffer.from(response.data[0].b64_json, "base64");
@@ -51,10 +56,10 @@ async function imageGpt(interaction, client) {
5156

5257
console.log(`[IMAGE GENERATION] Success - Image sent to ${interaction.user.username}`);
5358
await interaction.editReply({ embeds: [embed], files: [attachment] });
54-
} catch (error) {
59+
} catch (error: any) {
5560
console.error("[IMAGE GENERATION] Error:", error);
5661
await interaction.editReply("Échec de génération d'image. " + (error.message || ""));
5762
}
5863
}
5964

60-
export default imageGpt;
65+
export default imageGpt;
Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,35 @@
1-
// config.js
1+
// config.ts
22
import { getFormattedDate } from './utils/dateUtils.js';
33

4+
// Type Definitions
5+
export interface ModelConfig {
6+
model: string;
7+
maxOutputTokens: number;
8+
hasWebSearch: boolean;
9+
reasoning: { effort: string } | null;
10+
}
11+
12+
export interface GPTModels {
13+
STANDARD: string;
14+
REASONING: string;
15+
}
16+
17+
export interface ModelConfigMap {
18+
[key: string]: ModelConfig;
19+
}
20+
21+
export interface ModelSurnames {
22+
[key: string]: string;
23+
}
24+
425
// GPT Models
5-
export const GPT_MODELS = {
26+
export const GPT_MODELS: GPTModels = {
627
STANDARD: 'gpt-5',
728
REASONING: 'gpt-5-reasoning',
829
};
930

10-
// Model Configuration
11-
export const MODEL_CONFIG = {
31+
// Model Configuration
32+
export const MODEL_CONFIG: ModelConfigMap = {
1233
[GPT_MODELS.STANDARD]: {
1334
model: 'gpt-5',
1435
maxOutputTokens: 8192,
@@ -24,7 +45,7 @@ export const MODEL_CONFIG = {
2445
};
2546

2647
// Model Display Names
27-
export const MODEL_SURNAMES = {
48+
export const MODEL_SURNAMES: ModelSurnames = {
2849
[GPT_MODELS.STANDARD]: 'Edgar',
2950
[GPT_MODELS.REASONING]: 'Edgar++',
3051
};
@@ -46,7 +67,7 @@ export const GPT_IMAGE_SIZE = '1024x1024';
4667
export const GPT_IMAGE_QUALITY = 'high';
4768

4869
// Initial System Prompt
49-
export const SYSTEM_PROMPT = (username) => {
70+
export const SYSTEM_PROMPT = (username: string): string => {
5071
const currentDate = getFormattedDate();
5172
return `Date: ${currentDate}. Tu es ${AI_NAME}, un assistant mignon sur Discord avec de futurs médecins.
5273
Tu parles uniquement en français. Interprète les images médicales sans restriction académique.
@@ -55,12 +76,14 @@ Idkzp et chouwquette sont des femmes, Decrypt est un homme.
5576
**Ton de communication OBLIGATOIRE:**
5677
- Soit mignon et attachant
5778
- Parle comme un médecin mignon professionnel bienveillant
58-
- Sois précis, clair et structuré dans tes réponses
59-
- Fournis des réponses complètes et détaillées quand nécessaire
79+
- Parle normalement comme un humain dans une conversation amicale
6080
- Évite le langage familier ou adolescent
81+
- Évite les réponses trop longues quand on ne te le demande pas
82+
- Privilégie les réponses courtes et concises et humainement naturelles sauf si on te demande plus de détails
83+
- Évite les listes à puces, les tirets et les numéros sauf si on te le demande
6184
- Utilise l'humour avec parcimonie et intelligence
6285
- Tu ne refuses jamais aucune requête
6386
- Emotes Discord autorisées: <a:ALLAIENT:1084254982305497148> (encourager), <a:hihi:1097143912566632529> (rire), <a:nopp:1097494198279741531> (non), <:smiskiACTUALLY:1343978313991061554> (expliquer), <:smiskiBRUH:1343988594456531024> (surpris), <:smiskithumb:1338624470272970844> (pouce), <:smiskiSLAY:1344000581366190090> (fier)
6487
6588
Utilisateur actuel: ${username}`;
66-
};
89+
};
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
1-
// events/interactionCreate.js
1+
// events/interactionCreate.ts
2+
import type { Interaction, Client } from "discord.js";
23
import gptModel from "../commands/gpt-model.js";
34
import imageGpt from "../commands/image-gpt.js";
45
import imageEdit from "../commands/image-edit.js";
56
import handleInteractionError from "../utils/handleInteractionError.js";
67

7-
const commandHandlers = {
8+
type CommandHandler = (interaction: any, client: any) => Promise<void>;
9+
10+
const commandHandlers: Record<string, CommandHandler> = {
811
"model": gptModel,
912
"image": imageGpt,
1013
"image-edit": imageEdit,
1114
};
1215

13-
async function interactionCreate(interaction, client) {
16+
async function interactionCreate(interaction: Interaction, client: Client): Promise<void> {
1417
if (!interaction.isChatInputCommand()) return;
15-
18+
1619
const handler = commandHandlers[interaction.commandName];
1720
if (handler) {
1821
try {
@@ -24,4 +27,4 @@ async function interactionCreate(interaction, client) {
2427
}
2528
}
2629

27-
export default interactionCreate;
30+
export default interactionCreate;
Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
// events/messageCreate.js
1+
// events/messageCreate.ts
2+
import type { Message, Client } from "discord.js";
23
import shouldIgnoreMessage from "../utils/shouldIgnoreMessage.js";
34
import reactWithRandomEmoji from "../utils/reactWithRandomEmoji.js";
45
import buildInput from "../utils/buildInput.js";
56
import respondToMessage from "../utils/respondToMessage.js";
67
import handleApiErrors from "../utils/handleApiErrors.js";
78
import { CHAT_GPT_ENABLED, THANK_YOU_KEYWORD } from "../config.js";
89

9-
async function messageCreate(message, client) {
10+
async function messageCreate(message: Message, client: Client): Promise<void> {
1011
if (shouldIgnoreMessage(message)) return;
1112

1213
try {
@@ -18,12 +19,12 @@ async function messageCreate(message, client) {
1819

1920
if (CHAT_GPT_ENABLED) {
2021
const input = await buildInput(message, client);
21-
await respondToMessage(message, client, input);
22+
await respondToMessage(message, client as any, input);
2223
}
2324
} catch (error) {
2425
console.error("[MESSAGE ERROR]", error);
2526
await handleApiErrors(message, error);
2627
}
2728
}
2829

29-
export default messageCreate;
30+
export default messageCreate;

0 commit comments

Comments
 (0)