Created tiny dynamic flow for my bots
Well, “no-code” is in our life. With this blog, I will try mine. Also, I will try to explain my journey without diving into implementation details. I believe anyone can do this better or in their own way.
What will it do?
What I want to do is just a basic platform but a totally generic way to create or update my discord or telegram bot without touching the code or I just hoping to. Of course, will define the main blocks of the bot and I have to update my code if any more blocks are needed. Just imagine one block and you can drag and drop any other block inside/upside/downside. When an event triggers blocks will execute if they are in the conditionally provided block. For example, one block is checking for the message event and the beginning of the text is equal to the given string -which is a common pattern in the bot world something like “!!play URL”- then this blocks children will be executed.
Before I begin I must create a structure with data points. The basic system components exist with four main parts.
- UI: Where is the place I hope to do not code
- API: Simply accepts UI flow and writes to my lovely DB
- DB: You know DB. It can be a text file either. #textfilesdbtoo
- Bot: Bot is a general description here. It can be any bot even your custom bot like a bot getting ss from the given website URL.
After I defined the components I need to pass data between components. But how?
Well, let’s talk about data. I want a flow such as one block can be able to run multiple inner -or children- blocks with a given generic condition. So at the end of the day, as a user, I want a block in my UI like this
So I need a block definition that is tree-shaped or we can say hierarchical. I can strict attributes like “type”, “name”, “condition”, or “data”. Here, I used strictly “type” instead of using every possible attribute because I can extend these blocks in any direction, and some of them may not need that attribute. Other attributes like “condition” or “data” or anything they restricted by child type. For example “When” block can be extended from the base block and includes the “triggered” attribute with the value “any time”.
What about running?
When I want to run this data I need a generic way to do this because all I want it is. Assume I get a piece of JSON data that includes this kind of tree-shaped block data and I have event message only. In this situation, I did some hard code but finally come up with converting JSON to a tree with the help of composite pattern. I could do converting while processing but in the beginning, I have only one small flow and it wasn’t an issue. So, both ways are okay but you should think about what could be better for performance, and the first thing for me while this post is “it should run”
The greatest benefit of this approach is that you don’t need to care about the concrete classes of objects that compose the tree. You don’t need to know whether an object is a simple product or a sophisticated box. You can treat them all the same via the common interface. When you call a method, the objects themselves pass the request down the tree.
I created a Command interface to represent all blocks and with the power of composite pattern, I became able to execute all commands. Or am I?
There is an issue to solve and it is “command needs its data to work”. If we think with OOP we can handle with some abstraction let's say “Context” and its implementations like XCommandContext, YCommandContext or we can simply create only one context and share it with all blocks but we should define the boundaries of that blocks for what variables it can use or not. After that passing the context to its owner or children command via the execute function or constructor can solve this also we can add blockwise variables. Cool right? And also might confusing. Let’s demonstrate.
Imagine there are four blocks and they all will be executed. One of them is the root block has two of them as children and the first child also have one child. In the beginning, we have a context for the root and it is blank.
- After we execute the root block we got variable A and pass it to the first child.
- B2 is now executed and it already has a context with variable A which means it can use variable A in any functionality for itself. Passing context to its child B3. B3 is executed and it also has variable A and variable X. Generated variable K. It has no child so it will not pass the context.
The tricky part is every block will delete its variables before it has been done. So B2 will remove variable X and we will return to B1 to execute the next block.
- B4 will be executed and it has only variable A to use inside the context. The remaining process is the same.
It is like we are noting the variables to a letter and we are deleting when we will not use them anymore.
I created a flow-based bot platform that I can change the behavior of the bot from the web app with a basic composite pattern. It was fun to develop. Here are some pictures from the platform and the bot.
I didn’t come up here with the implementation here because I believe the basic idea behind the implementation -which is one of the design patterns- is more important than how you implement this. There could be other approaches that are more efficient and maintainable. If you know any please share with me and with the readers. Also, if there is any part hard to understand you can share it with me.
- Composite Pattern, https://refactoring.guru/design-patterns/composite