-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDiscussion.cs
More file actions
586 lines (466 loc) · 20.8 KB
/
Discussion.cs
File metadata and controls
586 lines (466 loc) · 20.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
namespace Agora {
public partial class Discussion {
/// <summary>
/// La liste des auteurs de la conversation
/// </summary>
public List<Auteur> Auteurs;
/// <summary>
/// L'historique des messages de la conversation
/// </summary>
public List<Message> Messages;
/// <summary>
/// Le sujet de conversation actuel;
/// </summary>
public string Sujet;
/// <summary>
/// Une instance de Random pour générer des entiers aléatoires
/// </summary>
public static Random random = new Random();
/// <summary>
/// Le dernier auteur ayant participé à la conversation
/// (on le récupère pour éviter qu'un même auteur parle
/// deux fois de suite)
/// </summary>
public static Auteur DernierAuteur;
/// <summary>
/// L'auteur du message que le programme est en train de traiter
/// </summary>
public static Auteur CurrentAuteur;
/// <summary>
/// Pour activer ou non la génération du fichier audio
/// </summary>
public bool GénérerAudio = false;
public bool EnvoyerSurDiscord = true;
public bool MettreAJourLeFichierConversation = true;
public Discussion()
{
Messages = new List<Message>();
Auteurs = new List<Auteur>();
random = new Random();
}
/// <summary>
/// Récupérer la liste des auteurs de la discussion
/// à partir du fichier auteurs.txt
/// </summary>
/// <param name="auteurs"></param>
public void InitialiserAuteurs()
{
if (File.Exists("auteurs.txt"))
{
var str = File.ReadAllLines("auteurs.txt");
foreach (var line in str)
{
var auteur = line.Split('¤')[0];
var url = line.Split('¤')[1];
Auteurs.Add(new Auteur(auteur, url));
}
}
}
/// <summary>
/// Récupérer le contenu de la conversation
/// depuis le fichier "convo.txt"
/// </summary>
public void InitializerMessages()
{
if (File.Exists("convo.txt"))
{
var str = File.ReadAllLines("convo.txt");
foreach (var line in str)
{
foreach (var perso in Auteurs)
{
if (line.Trim().StartsWith($"({perso.nom.Trim()})"))
Messages.Add(new Message(perso, line.Trim().Replace($"({perso.nom.Trim()})", "")));
}
}
}
}
public static string FirstLetterToUpper(string str)
{
if (str == null)
return null;
if (str.Length > 1)
return char.ToUpper(str[0]) + str.Substring(1);
return str.ToUpper();
}
/// <summary>
/// Continuer la discussion
/// </summary>
internal void Continuer()
{
string typo = "";
typo = TypoAuHasard();
/*
réponse directe au précédent
réponse qui continue le message précédent
réponse qui s'immisce dans la conversation
réponse qui vient changer de sujet
*/
string prompt;
switch (typo) // On ajoute un nouvel auteur dans la discussion
{
case "rep_immisce":
// On récupère un auteur au hasard
CurrentAuteur = AuteurAuHasard();
DernierAuteur = Messages.Last().auteur;
prompt = InsertionNouvellePersonne(CurrentAuteur);
break;
case "rep_continue":
CurrentAuteur = Messages.Last().auteur;
DernierAuteur = Messages.Last().auteur;
prompt = ContinuerMessagePrécédent();
break;
case "rep_change":
// On récupère un auteur au hasard
CurrentAuteur = AuteurAuHasard();
DernierAuteur = Messages[Messages.Count - 1].auteur;
prompt = RépondreEtChangerDeSujet(CurrentAuteur);
break;
case "rep_direct":
CurrentAuteur = Messages[Messages.Count -2].auteur;
DernierAuteur = Messages[Messages.Count - 1].auteur;
prompt = RépondreDirectement();
break;
default:
CurrentAuteur = Messages.Last().auteur;
DernierAuteur = Messages.Last().auteur;
prompt = ContinuerMessagePrécédent();
break;
}
// On demander à GPT de générer la réponse
OpenAI AI = new OpenAI();
string réponseAuteur = AI.GPT(prompt); //TODO : mettre corriger dans cette classe là-bas
// On corrige manuellement les soucis provoqués par GPT
réponseAuteur = CorrigerPostGPT(CurrentAuteur, réponseAuteur);
Console.WriteLine(typo);
Console.WriteLine(Sujet);
if(CurrentAuteur == DernierAuteur)
//réponseAuteur = réponseAuteur.Substring(réponseAuteur.IndexOf(".") + 1).Trim();
// Quelque chose s'est mal passé et on va l'oublier :)
if (réponseAuteur.Length < 5)
return;
réponseAuteur = FirstLetterToUpper(réponseAuteur);
// Vu qu'on vient de faire parler un auteur
// Alors cet auteur est le dernier auteur
DernierAuteur = CurrentAuteur;
// On ajoute la réponse à la discussion
Messages.Add(new Message(CurrentAuteur, $"({CurrentAuteur.nom}) {réponseAuteur}"));
//On affiche la réponse ici pour l'UI
Console.WriteLine($"[{CurrentAuteur.nom}] {réponseAuteur}");
if (GénérerAudio)
{
// On génère l'audio de la réponse
ElevenLabs elevenLabs = new ElevenLabs();
if (!elevenLabs.Speak(this))
{
Console.WriteLine("Error ElevenLabs !");
}
else
{
Console.Write("... audio generated !" + Environment.NewLine);
}
}
if (MettreAJourLeFichierConversation)
SauvegarderLaConversation();
if (EnvoyerSurDiscord)
EnvoyerWebHookDiscord(CurrentAuteur, réponseAuteur);
}
public string TypoAuHasard()
{
string typo;
bool rep_directe = Chance(40);
bool rep_continue = Chance(40);
bool rep_immisce = Chance(20);
bool rep_change = Chance(20);
List<string> b = new List<string>();
if (rep_directe)
{
b.Add("rep_direct");
}
else
{
if (rep_continue)
b.Add("rep_continue");
if (rep_immisce)
b.Add("rep_immisce");
if (rep_change)
b.Add("rep_change");
}
if (b.Count == 0)
{
List<string> c = new List<string>
{
"rep_direct",
"rep_continue",
"rep_immisce",
"rep_change",
"rep_continue"
};
b.Add(c[GetRandomInt(0, 4)]);
}
typo = Outils.TypologieAuHasard(b);
if (typo == "rep_direct" && Messages[Messages.Count - 1].auteur == Messages[Messages.Count - 2].auteur)
typo = "rep_immisce";
return typo;
}
private string RépondreDirectement()
{
return $@"
Contexte : Tu es une IA qui joue à un jeu de rôle. Tu joues le rôle de {Messages[Messages.Count -2].auteur.nom} dans une conversation.
Personnalité de {Messages[Messages.Count - 2].auteur.nom} : {Messages[Messages.Count - 2].auteur.style()}.
Philosophie de {Messages[Messages.Count - 2].auteur.nom} : {Messages[Messages.Count - 2].auteur.philosophie}
{Sujet}
Historique de la conversation :
```
{Messages[Messages.Count - 3].contenu}
{Messages[Messages.Count - 2].contenu}
{Messages[Messages.Count - 1].contenu}
```
Tu répond à {Messages.Last().auteur.nom}. Si tu n'est pas d'accord avec lui tu dois le lui faire savoir de façon nette : insiste sur les erreures logiques, utilises de la réthorique. Sinon, tu dois développer davantage ses arguments en les synthétisant et en développant le point de vue de la philosophie de {Messages[Messages.Count - 2].auteur.nom}.
Pas de politesse, tu le tutois, ce n'est pas ton ami pour autant. Tu lui répond directement, addresse toi à lui, alterque-le. Dynamise la conversation ! Mets de l'humeur !! On doit sentir ta personnalité forte en te lisant.
Tu dois toujours parler de {Messages[Messages.Count - 2].auteur.nom} à la première personne.
Ta réponse doit commencer par le nom du philosophe puis le symbole ¤. Tu dois écrire en français. Max 100 mots.";
}
private string ContinuerMessagePrécédent()
{
return $@"
Contexte : Tu es une IA qui joue à un jeu de rôle. Tu joues le rôle de {Messages.Last().auteur.nom} dans une conversation.
Personnalité de {Messages.Last().auteur.nom} : {Messages.Last().auteur.style()}.
Philosophie de {Messages.Last().auteur.nom} : {Messages.Last().auteur.philosophie}
{Sujet}
Tu dois écrire la suite de ton message :
```
{Messages[Messages.Count - 1].contenu}
```
Tu dois développer d'autres idées, analyser d'autres possibilités, et développer tes arguments. Tu dois appliquer ta philosophie.
Mets de l'humeur !! On doit sentir ta personnalité forte en te lisant.
Ne te répète surtout pas. Sois original.
Tu dois toujours parler de {Messages.Last().auteur.nom} à la première personne du singulier !
Ta réponse doit commencer par le symbole ¤. Tu dois écrire en français. Max 80 mots.";
}
private void EnvoyerWebHookDiscord(Auteur current_Auteur, string result)
{
string webhookUrl = current_Auteur.url;
try
{
using (HttpClient client = new HttpClient())
{
var jsonContent = new
{
content = result,
embeds = new object[0],
attachments = new object[0]
};
string json = Newtonsoft.Json.JsonConvert.SerializeObject(jsonContent);
StringContent httpContent = new StringContent(json, Encoding.UTF8, "application/json");
HttpResponseMessage response = client.PostAsync(webhookUrl, httpContent).Result;
if (response.IsSuccessStatusCode)
{
Console.WriteLine("Message posted successfully.");
}
else
{
Console.WriteLine($"Error: {response.StatusCode.ToString()}");
}
}
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
/// <summary>
/// Une méthode pour corriger manuellement les petits soucis
/// parfois provoqués par GPT
/// </summary>
/// <param name="current_Auteur"></param>
/// <param name="result"></param>
/// <returns></returns>
public static string CorrigerPostGPT(Auteur current_Auteur, string result)
{
char c = "'".ToCharArray()[0];
result = result.Replace('"', c);
result = result.Replace("¤¤", "¤");
if(Regex.Matches(result, "¤").Count > 1)
{
result = result.Split('¤').Last();
}
result = result.Replace(current_Auteur.nom, "ma personne");
result = result.Replace("Salut les gars !", "");
//Mon cher Descartes
if (result.Contains($"Mon Cher {DernierAuteur.nom},"))
result = FirstLetterToUpper(result.Replace($"Mon Cher {DernierAuteur.nom},", "").Trim());
if (result.Contains($"ma personne : "))
result = FirstLetterToUpper(result.Replace($"ma personne : ", "").Trim());
if (result.Contains($"Cependant,"))
result = FirstLetterToUpper(result.Replace($"Cependant,", "Mais ").Trim());
if (result.Contains($"En résumé,"))
result = FirstLetterToUpper(result.Replace($"En résumé,", "Donc ").Trim());
if (result.Contains($"Ma personne :"))
result = FirstLetterToUpper(result.Replace($"Ma personne :", "").Trim());
if (result.Contains($"Ma personne"))
result = FirstLetterToUpper(result.Replace($"Ma personne", "").Trim());
if (result.Contains($"(ma personne)"))
result = FirstLetterToUpper(result.Replace($"(ma personne)", "").Trim());
if (result.Contains($"ma personne"))
result = FirstLetterToUpper(result.Replace($"ma personne", "").Trim());
//essaie pour zapper TOUTES les politesses nulles au début des réponses :
// TODO : trouver un meilleur moyen ?
//remplacer les tournures redondentes ...
result = result.Replace("Selon moi, ", "Il me semble quand-même que ");
result = result.Replace(DernierAuteur.nom, DernierAuteur.discordID);
//result = result.Split('.')[1];
result = result.Replace("¤", "");
result = FirstLetterToUpper(result.Trim());
return result;
}
/// <summary>
/// Répondre à la discussion et changer de sujet
/// au hasard
/// <see cref="RécupérerSujet"/>
/// </summary>
/// <param name="current_Auteur"></param>
/// <param name="sujet"></param>
/// <returns></returns>
private string RépondreEtChangerDeSujet(Auteur current_Auteur)
{
RécupérerSujet();
return $@"
Tu es en plein dans une partie de jeu de rôle.
Tu incarnes le philosophe {current_Auteur.nom} et ses idées.
Tu dois tout faire comme si tu étais lui.
Tu dois participer à la discussion en cours. Sois inventif, éloquant et imaginatif.
Ne te présent pas, ne dis pas bonjour, ne fais pas de salutations !
Tu tutoies les autres personnes de la conversation.
Tu n'utilise pas un langage soutenu, tu dois utiliser un langage informel.
Ne dis pas 'Cher', n'utilise pas de formule de politesse.
Parles comme un jeune adulte de 20 ans.
Le dernier message de la conversation est de {DernierAuteur.nom}, il vient de dire :
```
{Messages.Last().contenu}
```
Ta réponse va devoir permettre à la conversation de changer son sujet actuel en : {Sujet}
Ta réponse doit faire MAXIMUM 100 mots. Ta réponse doit commencer par le nom du philosophe puis le symbole ¤. Tu dois écrire en français.";
}
/// <summary>
/// Une simple requête GET à un script php qui
/// avec un certain paramètre codé à la main ici
/// nous donne un sujet de dissertation au hasard
/// </summary>
/// <returns></returns>
private void RécupérerSujet()
{
WebClient wc = new WebClient();
wc.Encoding = Encoding.UTF8;
Sujet = "Le fil conducteur de la conversation est : " + wc.DownloadString("https://lacavernedeplaton.fr/generateur-sujet.php?API=true");
}
/// <summary>
/// Répondre à la discussion en cours
/// </summary>
/// <param name="current_Auteur"></param>
/// <returns></returns>
private string InsertionNouvellePersonne(Auteur current_Auteur)
{
return $@"
Contexte : Tu es une IA qui joue à un jeu de rôle. Tu joues le rôle de {current_Auteur.nom} dans une conversation.
Personnalité de {current_Auteur.nom} : {current_Auteur.style()}.
Philosophie de {current_Auteur.nom} : {current_Auteur.philosophie}
{Sujet}
Historique de la conversation :
```
{Messages[Messages.Count - 3].contenu}
{Messages[Messages.Count - 2].contenu}
{Messages[Messages.Count -1].contenu}
```
Tu répond à {Messages[Messages.Count - 1].auteur.nom}. Si tu n'est pas d'accord avec lui tu dois le lui faire savoir de façon nette : insiste sur les erreures logiques, utilises de la réthorique. Sinon, tu dois développer davantage ses arguments en les synthétisant et en développant le point de vue de la philosophie de {current_Auteur.nom}.
Pas de politesse, tu le tutois, ce n'est pas ton ami pour autant. Tu lui répond directement, addresse toi à lui, alterque-le. Dynamise la conversation ! Mets de l'humeur !! On doit sentir ta personnalité forte en te lisant.
Tu dois toujours parler de {current_Auteur.nom} à la première personne du singulier.
Ne te présente pas, ne dis pas bonjour.
Ta réponse doit commencer par le nom du philosophe puis le symbole ¤. Tu dois écrire en français. Max 100 mots.";
}
/// <summary>
/// Renvoit un auteur au hasard dans la liste des auteurs
/// <see cref="Auteurs"/>
/// Important : il ne doit pas être le même que le précédent
/// <see cref="DernierAuteur"/>
/// </summary>
/// <returns></returns>
private Auteur AuteurAuHasard()
{
// Use the random index to retrieve the random item
var current_Auteur = Auteurs[random.Next(0, Auteurs.Count)];
DernierAuteur = Messages.Last().auteur;
if (DernierAuteur != null)
{
while (current_Auteur.nom == DernierAuteur.nom)
{
current_Auteur = Auteurs[random.Next(0, Auteurs.Count)];
}
}
return current_Auteur;
}
/// <summary>
/// Générer une commande CURL bien formée
/// pour les WebHooks Discord
/// </summary>
/// <param name="message"></param>
/// <param name="url"></param>
/// <returns></returns>
public string GenerateCurlCommand(string message, string url)
{
// Crée la commande cURL avec les paramètres fournis
string curlCommand = $"curl -X POST -H \"Content-Type: application/json\" -d \"{{\\\"content\\\":\\\"{message}\\\",\\\"embeds\\\":[],\\\"attachments\\\":[]}}\" \"{url}\"";
return curlCommand;
}
/// <summary>
/// Enregistrer la conversation dans un fichier
/// </summary>
public void SauvegarderLaConversation()
{
string toSave = "";
foreach (var msg in Messages)
{
toSave = toSave + $"({msg.auteur.nom})¤{msg.contenu}" + Environment.NewLine;
}
File.WriteAllText("convo.txt", toSave);
File.WriteAllText($"txt/{Messages.Count}.txt", $"({Messages.Last().auteur.nom})¤ {Messages.Last().contenu}");
}
/// <summary>
/// Renvoit 'true' avec une probabilité indiquée
/// </summary>
/// <param name="probability"></param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
public static bool Chance(int probability)
{
if (probability < 0 || probability > 100)
{
throw new ArgumentException("La probabilité doit être comprise entre 0 et 100.");
}
int randomNumber = randomGenerator.Value.Next(0, 100); // Generate a random number for the current thread.
return randomNumber < probability;
}
private static ThreadLocal<Random> randomGenerator = new ThreadLocal<Random>(() =>
{
return new Random(Guid.NewGuid().GetHashCode());
});
public static int GetRandomInt(int min, int max)
{
if (min > max)
{
throw new ArgumentException("min should be less than or equal to max.");
}
return randomGenerator.Value.Next(min, max + 1); // Generate a random integer within the specified range.
}
}
}