Keep AI Email Templates With Memento Design Pattern
Today we will talk about the memento design pattern, one of the behavioral design patterns that I use a lot. Its purpose is to undo the last work done, especially in games, text editor applications, or step-by-step applications.
Today, we will act forward and backward on multiple phishing emails we created with AI. (Memento) which stores a copy of all or part of the object whose state is to be preserved in another place. The real object, of which a copy of all or some of its features will be kept, is also the part (Originator) that creates the memento object and is responsible for its restoration. It is the (Caretaker) that stores the reference values of the memento object to be stored.
Now Let’s Create Console App for MementoAI application.
Firstly download “Betalgo.Open.AI” .Net Library. We will use the Betalgo library for OpenAI .Net Library.
program.cs(Part 1): First of all, let’s create a method to write phishing emails with OpenAI.
Using OpenAI For Creating Phishing Email
We will get tags, prompt, and assist roles (what you are) as a parameter.
- “var openAiService = new OpenAIService(new OpenAiOptions()”: We will create OpenAIService with the current API Key. We could get this key from the OpenAI Platform Page here
- “var completionResult = openAiService.ChatCompletion.CreateCompletion(new ChatCompletionCreateRequest()”: We will use ChatCompletion to use the latest Model and create phishing emails. We have four roles here. System, User, Assistant, and Tool. We will give our prompt and AI roles to the OpenAI with these roles.
- We will set our ChatCompletion parameters.
- If we get the result without any error, we will return Phishing Mail content to the client. If we get an error, we will return Error Message with its code.
You can’t change your situation, the only thing you can change is how you chose to deal with it.
program.cs(Part 1):
string tags= "Contains Credit and Car Tags.";
string prompt = "Write an email to Bora Kasmer offering the recipient a chance
to win a $50\\r\\nAmazon gift card when they click a link to complete
an employee satisfaction survey. Let the connection path be
https://borakasmer.com?id=5. Create a beautifully formatted output using
HTML and CSS.\", string whatyouare = \"You are a helpful assistant";
string whatyouare = "You are a helpful assistant.";
var result=GetPhishingMail(tags, prompt, whatyouare);
Console.WriteLine(result);
Console.ReadLine();
public string GetPhishingMail(string tags, string prompt,string whatyouare)
{
var openAiService = new OpenAIService(new OpenAiOptions()
{
ApiKey = "---YOUR PASSWORD---"
});
var completionResult = openAiService.ChatCompletion.CreateCompletion(new ChatCompletionCreateRequest()
{
Messages = new List<ChatMessage>
{
ChatMessage.FromSystem(whatyouare),
ChatMessage.FromUser(prompt),
ChatMessage.FromUser(tags),
},
Model = Models.Gpt_4_turbo,
MaxTokens = 2000,
FrequencyPenalty = 0,
Temperature = (float?)0.7,
PresencePenalty = 0,
});
if (completionResult.Result.Successful)
{
return completionResult.Result.Choices.FirstOrDefault().Message.Content;
}
else
{
if (completionResult.Result.Error == null)
{
throw new Exception("Unknown Error");
}
return $"{completionResult.Result.Error.Code}: {completionResult.Result.Error.Message}";
}
}
Result: If everything goes well, OpenAI returns the result as below phishing mail.
We Will Keep All Email Template States With a Memento Design Pattern To Navigate Between Them.
If we call GetPhisingMail() three times and want to move between them, how can we keep these states?
for (var i = 0; i < 3; i++)
{
var result = GetPhishingMail(tags, prompt, whatyouare);
}
MementoAIPhishing.cs: We will use this class to keep the Email Template’s State and move back and forth between these states.
- AIMailMemento: This class is used for keeping AI-Generated Email.
- MailUndoTaker & MailRedoTaker : We will use the MailUndoTaker class to keep the old states of AI Email Templates. We will use the MailRedoTake class to keep the next states of AI Email Templates. We will use a stack list in both classes. When we move back to the state, we will put the current state in the MailRedoTake class. When we move forward state, we will put the current state to the MailUndoTaker class.
- We used the “TryPop()” method for checking null state possibilities for the next and preview states on the get property.
For this scenario, I prefer to use the Stack List. I think it is much more suitable for memory and speed performance while taking and pushing data to the queue.
- MementoAIPhishing class is our main class. It has MailTemplate property for keeping email. MailRedoTaker and, MailUndoTaker variables are used for keeping email old and new states. The “Save()” method is used for keeping the current state of email in the MailUndoTaker class.
You can change anything you want. You just can’t change
everything you want. — Peter McWilliams
- The “Undo()” method is used for getting the previous state of EmailTemplate. When we back to the previous state, we put to current state to the MailRedoTaker.
- The “Redo()” method is used to get the next state of EmailTemplate. When we move to the next state, we put to current state to the MailUndoTaker.
MementoAIPhishing.cs (Full):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MementoAI
{
public class MementoAIPhishing
{
public string MailTemplate { get; set; }
MailRedoTaker RT;
MailUndoTaker UT;
public MementoAIPhishing()
{
RT = new();
UT = new();
}
public void Save()
{
UT.Mail = new AIMailMemento() { MailTemplate = MailTemplate };
}
public void Undo()
{
RT.Mail = new AIMailMemento() { MailTemplate = this.MailTemplate };
this.MailTemplate = UT.Mail.MailTemplate;
}
public void Redo()
{
UT.Mail = new AIMailMemento() { MailTemplate = this.MailTemplate };
this.MailTemplate = RT.Mail.MailTemplate;
}
public class MailUndoTaker
{
Stack<AIMailMemento> mementos = new Stack<AIMailMemento>();
public AIMailMemento Mail
{
get => mementos.TryPop(out AIMailMemento result) ? result : new AIMailMemento();
set
{
mementos.Push(value);
}
}
}
public class MailRedoTaker
{
Stack<AIMailMemento> mementos = new Stack<AIMailMemento>();
public AIMailMemento Mail
{
get => mementos.TryPop(out AIMailMemento result) ? result : new AIMailMemento();
set
{
mementos.Push(value);
}
}
}
public class AIMailMemento
{
public string MailTemplate { get; set; }
public DateTime CreatedDate { get; set; }
}
}
}
How To Use It
program.cs: Commented lines are dummy data created to test Undo and Redo situations without using OpenAI.
- We will get three phishing email templates in a loop by using OpenAI. Except for the last one, we will save every email state to the MailUndoTaker because every new state could be considered as a retrospective record.
if (i!=2) => But the last one can be treated as a valid current situation. So we won’t Save() it.
- Firstly we will write the current state of the email, which we will call 1.Undo(), and move to the previous state. And again we will call 2. Undo() and we will come to the first state. Finally, we will call 3. Redo(), Redo() method 2X times and we will come to the latest current email state.
program.cs(Part 2):
MementoAIPhishing memento = new();
//memento.MailTemplate = "1";
//memento.Save();
//memento.MailTemplate = "2";
//memento.Save();
//memento.MailTemplate = "3";
for (var i = 0; i < 3; i++)
{
var result = GetPhishingMail(tags, prompt, whatyouare);
memento.MailTemplate = result;
if (i != 2) memento.Save();
}
Console.WriteLine(memento.MailTemplate);
Console.WriteLine("");
Console.WriteLine("Undo".PadLeft(40, '*'));
memento.Undo();
Console.WriteLine(memento.MailTemplate);
Console.WriteLine("Undo".PadLeft(40, '*'));
memento.Undo();
Console.WriteLine(memento.MailTemplate);
Console.WriteLine("Redo, Redo".PadLeft(40, '*'));
memento.Redo();
memento.Redo();
Console.WriteLine(memento.MailTemplate);
IF YOU CAN’T CHANGE IT. CHANGE THE WAY YOU
THINK ABOUT IT. — MARY ENGELBREIT
RESULT
- This is latest Current AI Mail Template
2. After calling Undo() Method, this is Previous AI Mail Template
3. After calling the Undo() Method, this Previous is actually the first AI Mail Template.
4. After calling two Redo(), Redo() Methods, this is again latest current AI Mail Template
Conclusion:
In this article, we talked about how we can move between the before and after states of a situation. I use this topic in my current business life, exactly as in the article. We have allowed our customers to create phishing emails with AI within certain limits. If they create emails in different versions, they can navigate between them and save one. In this case, the two most logical solutions seemed to me. One is a memento design pattern and the other is a linked list. I continued my way, using the Memento Design Pattern as in the article.
If you want to store the different e-mail versions you create permanently rather than instantly so that they can be accessed later, it would be better to use an in-memory cache solution such as Redis instead of DB.
See you until the next article.
“If you have read so far, first of all, thank you for your patience and support. I welcome all of you to my blog for more!”
Source [Github]: https://github.com/borakasmer/OpenAIMemento/