TS3AudioBot Control Panel Documentation

TS3AudioBot Panel


Introduction

This product need a certain level of technical expertise. If you don't know what you are doing, you might break the System and cause bad things to happen.


First of all, Thank you so much for purchasing this template and for being my loyal customer. You are awesome!
You are entitled to get free lifetime updates to this product + exceptional support from the author directly.

This documentation is to help you regarding each step of customization. Please go through the documentation carefully to understand how this product is working and how you can work with it.

Requirements

You will need the following sofwares to install and use this product:

  1. Web Browser for testing (eg: Google Chrome or Mozilla Firefox)
  2. A Server with the following tools installed:
    1. nano (text editor)
    2. Apache2 (rewrite module active, AllowOverride for directory)
    3. PHP7.3+ and PHP modules for Apache2
    4. MariaDB 10.1.37
    5. Composer PHP dependeny tool (https://getcomposer.org/download)

Getting Started #back to top

Let's get started installing the Interface on your Server. For demonstration purposes, imagine I use a Debian 9.8 Server during this installation process.

In theory the process is pretty simple:

  1. Download Code & unpack
  2. Import Databasev
  3. Edit config
  4. Install Dependencys & run

Download Code & unpack

                                    root@server:/var/www/html# wget https://bennetgallein.de/download-key/...
                                

Once downloaded, rename the file:

                                    root@server:/var/www/html# mv <file> cp.zip
                                

And unpack it:

                                    root@server:/var/www/html# unzip cp.zip .
                                

Import Database

As said, I assume that you already have a MySQL Database installed on your System (prefferable a MariaDB 10.1.37 Server).

Let's enter the Database over the command line tool.

                                    root@server:/# mysql -u root
                                

If you have a password for the root user, use this command:

                                    root@server:/# mysql -u root -p
                                

After you logged in to the Database, you should see something like this:

                                    MariaDB [(none)]>
                                

You see it? Great! Let's create the Database:

                                    MariaDB [(none)]> CREATE DATABASE TS3ABCP;
                                    Query OK, 1 row affected (0.01 sec)

                                    MariaDB [(none)]>
                                

Now you want to exit the Database. Use exit; to do that.
Now we want to import the Database. The easiest thing is to use the command line or a tool like phpmyadmin if that is installed:

                                    root@server:/# mysql -u root -p TS3ABCP < /var/www/html/database.sql
                                

Install Dependencies and run

Now, we are basically finished. We just need to install the Dependencies, create an Account and give it Admin rights.

                                    root@server:/var/www/html/# chmod +x ./bin/install.sh && sudo ./bin/install.sh
                                

This will install all the dependencies the Interface needs, if errors accour during the installation, try fixing them by googleing.

Edit config

Now onto the important point on this list to make everythink work just the way you expect it to. If you do not have an config.json by this point (should be created by the script above)

                                    root@server:/var/www/html# cp config.json.example config.json && nano config.json
                                
Key Example Value
APP_URL "/" This is the URL of your application. If you want it to run on yourdomain.com/ enter /, otherwise the path like /TS3ABCP/
DB_HOST "localhost" The Host of your Database. Should be localhost, 127.0.0.1 or your IP.
DB_USER "root" The User with which to access the Database.
DB_PASSWORD "root" The passwort of the Database user specified in DB_USER. Enter "" if you dont want to enter a password
DB_NAME "TS3ABCP" The name of the Database which you'll have create with the steps above. If you followed the tutorial, enter TS3ABCP
MB_HOST "localhost" The host of the server where the Bot is running. If it is running, localhost should be fine. Remember to expose the IP in the config file of the Bot!
MB_PORT "8180" The port from the API of the Bot. Default is 8180
MB_TOKEN "j+W41OpXcHv8In9vt/Q2x+UmUPs=:ts3ab:GVFdH..." This is the response from !bot api token which you will need to authenticate as an admin, so the Interface can control the Bots
MB_LIMIT "3" This is the default limit of Bots a normal User can create. If you don't want to set a limit, set it to -1 to enable infinite Bots per user
REGISTER_ACTIVE true With this you are able to disable registrations for new users. Set to false to disable registrations.
TERMS_AND_CONDITIONS "http://example/tos.html" A link to your Terms and Conditions. This will be linked on the registration site.
CUSTOM_BOX_TITLE "Website" This is the box on the dashboard where you can enter a custom thing.
CUSTOM_BOX_TEXT "bennetgallein.de" This is the content of the box on the dashboard.
QUICKPLAY { "ILoveRadion": "http://stream01.iloveradio.de/iloveradio1.mp3", "ReyFM": "https://stream01.reyfm.de/original_192kbps.mp3" } This is a JSON Array for custom quicklplay links. On the left side (key) is the Name (can be anything) while on the right site you need to paste the direct play link to the stream!
THEME "default" This is the theme folder, inside the _views folder. If you didn't know what to write here, just enter the example value
ANALYTICS_ENABLED true If you want to have analytics reports enabled
ANALYTICS_KEY 6254e81ade14be8908cd0d9f05da90f6 your personal analytics key. If you dont have one, you can register your site here: bennetgallein.de/cis
MODULES "MODULES": { "TicketModule": false, "AntiRobotModule": true } This is an array of your activated modules. If you want to have the module enabled, set true otherwise false.You need to purchase and install the Module in order to use it!
optional: API_ENABLED false Wether you want to have the API enabled. This feature is only in the commercial edition!
optional: API_AUTH super-secure-token This is the Authentication Token you need to communicate with the REST-API.This feature is only in the commercial edition!

Save everything once you are done with editing and exit.

Permission System

Starting with the 2.1.0 release, the Panel features a detailed permission system, which allows you to create roles for different groups of users and allow and disallow certain actions.

The permission files are all stored in the _permissions folder, the default.json is from the core of the Panel, all other files there are from Modules.

If you don't have any permission files in the folder, run the following command from the folder of the CP to download the default permission: chmod +x bin -R && sudo ./bin/download_permission_files.sh

                                    {
                                        "0": [

                                        ],
                                        "50":[
                                            "admin.users",
                                            "admin.user",
                                            "admin.user.changelimit",
                                            "admin.bots",
                                            "admin.bot"
                                        ],
                                        "100": [
                                            "admin.users",
                                            "admin.user",
                                            "admin.user.delete",
                                            "admin.user.changelimit",
                                            "admin.user.makeadmin",
                                            "admin.user.addbalance",
                                            "admin.user.removebalance",
                                            "admin.bots",
                                            "admin.bot",
                                            "admin.internal.apiunresponsive",
                                            "admin.internal.startallbots",
                                            "admin.internal.stopallbots",
                                            "admin.internal.update",
                                            "admin.internal.migrate",
                                            "admin.ignoredeactivatedregister"
                                        ]
                                    }
                                

What you can see here is pretty similliar to the content in _permissions/default.json. "0","50","100" are the permissions that you need to set in the Database in order to assign a user to a group.

value explanation
admin.users Permission to see the list of users
admin.user Pemrission to see individual user
admin.user.delete Permission to delete a user
admin.user.changelimit Permission to change the individual botlimit for a user
admin.user.makeadmin Pemrission to make a user an admin
admin.user.addbalance Permission to add balance to the users account
admin.user.removebalance Permission to remove balance from the users account
admin.bots Permission to see the list of bots
admin.bot Permission to take full control over bots the user doesn't own, usefull for support-purposes
admin.internal.apiunresponsive Permission to click the "API crashed" button in the Admin view
admin.internal.startallbots Permission to click the "Start all Bots" button in the Admin view
admin.internal.stopallbots Permission to click the "Stop all Bots" button in the Admin view
admin.internal.update Permission to update the Software
admin.internal.migrate Permission to visit the migrate to url, only if your version is older than 1.1.8
admin.ignoredeactivatedregister Permission to register even if the REGISTER_ACTIVE is false.
admin.setpermission Permission to update the permission level of a user

For Module Permissions, view the Modules Section and teir respective Documentation.


Now, you are finished. Open your Browser and browse to your IP. Create a new User, enter the Database and set the permission to your Admin Permission level. Now you can login and enjoy your product!

All Modules need a config-entry in order to get loaded:

                              "MODULES": {
                                "TicketModule": true,
                                "AntiRobotModule": true
                              }
                          

Ticket Module#back to top

Installing a Module is straight forward. Download the files over the download-link you received via E-Mail. Them unzip the folder TicketModule inside the _Modules folder. Reload the Page and if no errors accour, browse to <ip>/module/ticket/install in your Browser to install the required Database folders and the additional pages to the _views folder.

If you get any errors and the logs say something about Class Not Found, run composer dump-autoload -o in the directory of the Panel to load the new classes.

Permissions

value explanation
ticket.tickets Permission to see the list of users
ticket.admin.setupPermission to visit the setup page
ticket.admin.ticketsPermission to see all tickets
ticket.admin.ticketPermission to see a ticket that doesn't belong to the logged in user
ticket.admin.answerPermission to answer a ticket that doesn't belong to the logged in user
ticket.admin.closePermission to close a ticket that doesn't belong to the logged in user
ticket.createnewPermission to create a new ticket

AntiRobot Module#back to top

Installing a Module is straight forward. Download the files over the download-link you received via E-Mail. Them unzip the folder AntiRobotModule inside the _Modules folder.

There are also some new config entries you need to make. CAPTCHA_PUBLIC (your public google recaptcha code), CAPTCHA_PRIVATE (your private recaptcha code) and CAPTCHA_LIMIT(a number between 0 and 1, the level of trust google at least needs to have in the user (example value: "0.7"))


MultiHost Module#back to top

Installing a Module is straight forward. Download the files over the download-link you received via E-Mail. Them unzip the folder MultiHostModule inside the _Modules folder.

Once done with the unzipping, make sure you have the latest permission files by running the script: chmod +x ./bin/download_permission_files.sh && sudo ./bin/download_permission_files.sh. After that, visit /module/multihost/install and confirm that there are no errors. The module will take the Bot which is currently in the config.json as the first node. After that, you should be good to go setting up your new nodes and bots.

Subscription Module#back to top

Installing a Module is straight forward. Download the files over the download-link you received via E-Mail. Them unzip the folder SubscriptionModule inside the _Modules folder.

After that, go to your browser and visit this url: <ip>/module/subscription/install to import the database changes.

There is also a new config entry that you need to make. PAYMENT_INTERVAL is the number of days between each payment. So if you set this to 30 days, the module will try to subtract the BOT_PRICE from the users account and if it fails, will stop the bot and delete it.

It is also recommended to setup a cronjob to execute the job on a daily basis, you can decide how often you want to check if a payment is required. Use this tool to get an valid crontab format and then enter the following in your crontab file (can be accessed by executing crontab -e in the console): * * * * * /usr/bin/wget --spider "http(s)://your-ip.de/module/subscription/execute" >/dev/null 2>&1 which will execute the script every minute.

Developers#back to top

If you want to develop your own modules read this page: https://docs.bennetgallein.de/ts3abcp/modules.html

If you want to develop your own themes for the Dashboard, that's cool. In this part of the guide, I'll give my best to explain how everything works and how you can get started creating custom themes and how to install them.

Of course there are conditions to this option: You have to leave a copyright to my Website in the footer and to all other Authors you used. If you want to remove the copyright, email me: me@bennetgallein.de and against a small fee this should be no problem. Just don't do it without my permission, my ToS permit it and I realy don't want to revoke your license!

With that out of the way, let's get started: Basically you have to create a folder inside the _views/ folder with the name of your template (I use test in this tutorial) and change the THEME property in the config to the name of the folder (in my example test).

In order for the Panel to continue working as expected, you need to follow the file structure I provide below. Also note that I use a Templating Engine, which allows you to include files easily (I use it to include styles, scripts, sidebars, etc.). A full List of the Syntax can be found in this file.

File Structure

                                    _views/
                                        test/
                                            admin_bots.html
                                            admin_list.html
                                            admin_user.html
                                            bot.html
                                            error.html
                                            index.html
                                            new_bot.html
                                            settings.html
                                            sign-in.html
                                            sign-up.html
                                            _includes/
                                                optional files
                                

This is the file structure. You need to have the same file names at the same locations, otherwise the Software won't find them.

sign-in.html

This file is responsible to display the login form. there is only one parameter register which you can use, which shows if the REGISTER_ACTIVE option in the config is enabled or disabled. You can check this by using the following Syntax in your code:

                                    { if :register }
                                        // register is active, display link to register form
                                    { endif }
                                

You also need to add a little bit of JavaScript to make the login work, because it's interactive and the Document wont get reloaded until the user is being redirected to the Dashboard.

                                    $("#login").on("click", (e) => {
                                        e.preventDefault();

                                        let emailInput = $("#email").val();
                                        let passwordInput = $("#password").val();

                                        $.post("{ :app_url }login",
                                        {
                                            email: emailInput,
                                            password: passwordInput
                                        }, (data) => {
                                            if (data.error === true) {
                                                // there is a error. The Error message is in data.error_msg
                                            } else {
                                                setTimeout(() => {
                                                    window.location = "{ :app_url }dashboard";
                                                }, 2000);
                                            }
                                        });
                                    });
                                

sign-up.html

This file is responsible to display the sign up form. This wont render if REGISTER_ACTIVE is set to false in the config. The only parameter here is tac, which will be populated with the content of the TERMS_AND_CONDITIONS variable.

Again, we need some JavaScript to make this work:

                                        $("#register-btn").on('click', function (e) {
                                            e.preventDefault();

                                            var name = $("#name").val();
                                            var email = $("#email").val();
                                            var password = $("#password").val();
                                            var confirmpassword = $("#cpassword").val();

                                            if (password !== confirmpassword || password === "") {
                                                // checks are also made in the backend
                                                // but it saves some time to do so of them here
                                            }
                                            $.post("{ :app_url }register",
                                                {
                                                    name: name,
                                                    email: email,
                                                    password: password
                                                }, function (data) {
                                                    if (data.error === true) {
                                                        // error, see data.error_msg for more information.
                                                    } else {
                                                        // no error. Redirect to login
                                                        setTimeout(function() {
                                                            window.location = "{ :app_url }login";
                                                        }, 2000);
                                                    }
                                                });
                                        });
                                

settings.html

This page is to render the settings for the user account. Here we'll also introduct our first object, which is the user. You can access it's properties by using the { :user.property } Syntax.

User object:

property explanation
id The ID of the user in the database.
name The name the user registed under
email The email address of the user
permission The permission level the user has
limit the limit of Bots a user can have

For example, if you want to display the user's email on the settings page, just enter { :user.email } and it will render the email of the current logged in user.

To change the passwort, use this as a template for further development:

                                        function changePass() {
                                            $.post("{ :app_url }api/changepass", {
                                                old: $("#old").val(),
                                                new: $("#new").val()
                                            }, function(data) {
                                               if (data.error) {
                                                   Swal({
                                                       type: 'error',
                                                       title: 'Failed',
                                                       text: data.error_msg,
                                                   })
                                               } else {
                                                   Swal({
                                                       type: 'success',
                                                       title: 'Done',
                                                       text: data.error_msg,
                                                   })
                                               }
                                            });
                                        }
                                

index.html

Okay, this is for the main Dashboard a user lands on, once he logged in successfully. This table will give you an overview which parameters are here and how to use them.

property explanation
name The name of the logged in user
email The email of the logged in user
num_bots The amount of Bots the user currently has
last_login A Timestamp of the last login
bots An Array of all Bots and their information.
total_bots The Amount of total Bots created by all users.
custom_box_title The custom box title value from the config
custom_box_text The content of the box, according to the config
filter If the User has a filter active
showfilter a boolean to show the filter.

Displaying the Bots on the Dashoard looks something like this:

                                        { foreach :bot in :bots }
                                        <tr>
                                            <td>{ :bot.id }</td>
                                            <td>
                                                <a class="text-dark" href="{ :app_url }bot/{ :bot.id }">{ :bot.nickname }</a>
                                            </td>
                                            <td class="d-none d-md-table-cell">{ :bot.created }</td>
                                            <td>
                                                <?php if ($bot['status'] == "Online"): ?>
                                                <span class="badge badge-success">{ :bot.status }</span>
                                                { else }
                                                <span class="badge badge-danger">{ :bot.status }</span>
                                                { endif }
                                            </td>
                                        </tr>
                                        { endforeach }
                                

Basically you are looping over the Array to display the values, pretty simpel huh?

new-bot.html

This page displays the dialog to create a new Bot. There are no parameters, but I'll drop you a hint on howto create a new Bot.

                                        $("#submit-create").on("click", function(e) {
                                            e.preventDefault();
                                            let nickname = $("#nickname").val();
                                            let ip = $("#ip").val();

                                            $.post("{ :app_url }bot/new", {
                                                nickname: nickname,
                                                ip: ip
                                            }, function(data) {
                                                console.log(data);
                                                if (data.error) {
                                                    Swal("Done", "" + data.error_msg, "error");
                                                } else {
                                                    Swal("Done", "" + data.error_msg, "success");
                                                }
                                            });
                                        });
                                

bot.html

This is the page which display the specific Bot and it's properties. Parameters are id which is the Bot ID, bot which is the Bot Object and quickplay which will be false if there are no quicklplay links and a key-value array if there are some.

Bot object:

property explanation
id Bot ID in Database, same as id which comes by default
nickname the nickname set for the Bot
connected the IP where the Bot is connected to
userid The ID of the User who created this Bot
last_id The last ID the Bot received from the TS3AudioBot API
active If the Bot is deleted (0 if it is deleted)
created A timestamp when the Bot was created
status Status of the Bot (Online or Offline)
commander Wether the Bot is a channel-commander or not.

To update settings, volume and get current songs, look at the examples below:

                                        var slider = document.getElementById("volume");
                                        var output = document.getElementById("volume_display");
                                        output.innerHTML = slider.value; // Display the default slider value

                                        // Update the current slider value (each time you drag the slider handle)
                                        slider.oninput = function() {
                                            output.innerHTML = this.value;
                                        }

                                        function disconnect(id) {
                                            $.get("{ :app_url }api/bot/disconnect/" + id, function (data) {
                                                if (data.error === true) {
                                                    Swal({
                                                        type: 'error',
                                                        title: 'Failed',
                                                        text: data.error_msg,
                                                    })
                                                } else {
                                                    Swal({
                                                        type: 'success',
                                                        title: 'Done',
                                                        text: data.error_msg
                                                    })
                                                }

                                                setTimeout(function () {
                                                    location.reload();
                                                }, 2000)
                                            });
                                        }

                                        function start(id) {
                                            $.get("{ :app_url }api/bot/start/" + id, function (data) {
                                                if (data.error === true) {
                                                    Swal({
                                                        type: 'error',
                                                        title: 'Failed',
                                                        text: data.error_msg,
                                                    })
                                                } else {
                                                    Swal({
                                                        type: 'success',
                                                        title: 'Done',
                                                        text: data.error_msg
                                                    })
                                                }
                                                setTimeout(function () {
                                                    location.reload();
                                                }, 2000);
                                            });
                                        }

                                        function deleteBot(id) {
                                            $.get("{ :app_url }api/delete/" + id, function (data) {
                                                if (data.error === true) {
                                                    Swal({
                                                        type: 'error',
                                                        title: 'Failed',
                                                        text: data.error_msg,
                                                    })
                                                } else {
                                                    Swal({
                                                        type: 'success',
                                                        title: 'Done',
                                                        text: data.error_msg
                                                    })
                                                }
                                                setTimeout(function () {
                                                    location.href = "{ :app_url }";
                                                }, 2000);
                                            });
                                        }

                                        function updateConnection(id) {
                                            $.post("{ :app_url }api/bot/update/" + id, {
                                                nickname: $("#nickname").val(),
                                                ip: $("#ip").val(),
                                                commander: document.getElementById("checkbox-commander").checked,
                                                volume: slider.value,
                                                default: $("#default").val(),
                                                default_password: $("#channel_password").val(),
                                                server_password: $("#server_pw").val()
                                            }, function (data) {
                                                if (data.error === true) {
                                                    Swal({
                                                        type: 'error',
                                                        title: 'Failed',
                                                        text: data.error_msg,
                                                    })
                                                } else {
                                                    Swal({
                                                        type: 'success',
                                                        title: 'Done',
                                                        text: data.error_msg
                                                    })
                                                }
                                            });
                                        }

                                        function updateIdentity(id) {
                                            $.post("{ :app_url }api/bot/identity/" + id, {
                                                identity: $("#identity_key").val()
                                            }, function(data) {

                                                if (data.error === true) {
                                                    Swal({
                                                        type: 'error',
                                                        title: 'Failed',
                                                        text: data.error_msg,
                                                    })
                                                } else {
                                                    Swal({
                                                        type: 'success',
                                                        title: 'Done',
                                                        text: data.error_msg
                                                    })
                                                }
                                            });
                                        }

                                        function playSong(id) {
                                            $.post("{ :app_url }api/bot/play/" + id, {
                                                song: $("#song_input").val()
                                            }, function (data) {
                                                if (data.error === true) {
                                                    Swal({
                                                        type: 'error',
                                                        title: 'Failed',
                                                        text: data.error_msg,
                                                    })
                                                } else {
                                                    Swal({
                                                        type: 'success',
                                                        title: 'Done',
                                                        text: data.error_msg
                                                    })
                                                }
                                            });
                                        }

                                        function playQuick(url, id) {
                                            $.post("{ :app_url }api/bot/play/" + id, {
                                                song: url
                                            }, function (data) {
                                                if (data.error === true) {
                                                    Swal({
                                                        type: 'error',
                                                        title: 'Failed',
                                                        text: data.error_msg,
                                                    })
                                                } else {
                                                    Swal({
                                                        type: 'success',
                                                        title: 'Done',
                                                        text: data.error_msg
                                                    })
                                                }
                                            });
                                        }

                                        function stopSong(id) {
                                            $.get("{ :app_url }api/bot/stop/" + id,
                                                function (data) {
                                                    if (data.error === true) {
                                                        Swal({
                                                            type: 'error',
                                                            title: 'Failed',
                                                            text: data.error_msg,
                                                        })
                                                    } else {
                                                        Swal({
                                                            type: 'success',
                                                            title: 'Done',
                                                            text: data.error_msg
                                                        })
                                                    }
                                                });
                                        }
                                        function getCurrent(id) {
                                            $.get("{ :app_url }api/bot/current/" + id,
                                                function(data) {
                                                    if (data.error === true) {
                                                        Swal({
                                                            type: "error",
                                                            title: "Failed",
                                                            text: data.error_msg
                                                        });
                                                    } else {
                                                        current = JSON.parse(data.current);
                                                        if (typeof current.ErrorCode !== "undefined") {
                                                            $("#current_song").text("Nothing playing");
                                                        } else {
                                                            $("#current_song").html("" + current.Value + "");
                                                        }
                                                        $("#volume_display").text(data.volume);
                                                        $("#volume").val(data.volume);
                                                        $("#default").val(data.default_channel);
                                                        $("#channel_password").val(data.default_password);
                                                        $("#server_pw").val(data.server_password);
                                                        $("#identity_key").val(data.identity);
                                                    }
                                            });
                                        }
                                        getCurrent({ :bot.id });
                                

admin_list.html

This renders a list of Users to the Admin. Only parameter is users

property explanation
id ID of the user
username Username of the user
email email of the user
password Hashed password of the user
register registration timestamp
last_login Timestamp of the last login
permission Permission level of the user
mb_limit the bot limit of the user.

admin_user.html

This page displays information about one specific user. Parameters are id, user and bots

All parameters are explained already above.

To change the limit, delete a Bot or delete the User, look at the following examples:

                                        function deleteUser(id) {
                                            $.get("{ :app_url }api/users/delete/" + id, function (data) {
                                                if (data.error === true) {
                                                    Swal({
                                                        type: 'error',
                                                        title: 'Failed',
                                                        text: data.error_msg,
                                                    })
                                                } else {
                                                    Swal({
                                                        type: 'success',
                                                        title: 'Done',
                                                        text: data.error_msg
                                                    })
                                                }
                                            });
                                        }

                                        function deleteBot(id) {
                                            $.get("{ :app_url }api/delete/" + id, function (data) {
                                                if (data.error === true) {
                                                    Swal({
                                                        type: 'error',
                                                        title: 'Failed',
                                                        text: data.error_msg,
                                                    })
                                                } else {
                                                    Swal({
                                                        type: 'success',
                                                        title: 'Done',
                                                        text: data.error_msg
                                                    })
                                                }
                                                setTimeout(function() {
                                                   location.reload();
                                                }, 2000);
                                            });
                                        }

                                        function changeLimit(id) {
                                            Swal.fire({
                                                title: "Change Bot Limit",
                                                input: 'text',
                                                showCancelButton: true,
                                                confirmButtonText: 'Update',
                                                showLoaderOnConfirm: true,
                                                preConfirm: (limit) => {
                                                    return fetch(`{ :app_url }api/botlimit/${id}/${limit}`, { credentials: "same-origin" })
                                                        .then(response => {
                                                            if (!response.ok) {
                                                                throw new Error(response.statusText)
                                                            }
                                                            return response.json()
                                                        })
                                                        .catch(error => {
                                                            Swal.showValidationMessage(
                                                                `Request failed: ${error}`
                                                            )
                                                        })
                                                },
                                                allowOutsideClick: () => !Swal.isLoading()
                                            }).then((result) => {
                                                console.log(result);
                                                if (result.value.error === false) {
                                                    Swal({
                                                        type: 'success',
                                                        title: "Update complete",
                                                        text: result.value.error_msg
                                                    })
                                                } else {
                                                    Swal({
                                                        type: 'error',
                                                        title: "Update complete",
                                                        text: result.value.error_msg
                                                    })
                                                }
                                            })
                                        }
                                

admin_bots.html

This display all Bots created. The only parameter is bots, which is explained above already, but with an additional username attribute to get the username of the Bots owner.