I’ve enabled the administrative speech protocols for Mr. Good bot allowing us to control his speech in real time outside of the database and I built out a nifty admin interface!

Screenshot of the Mr. Good Bot Admin Interface
Screenshot of the Mr. Good Bot Admin Interface

It works well enough but it makes a terrible wallpaper so here’s the featured image as a wallpaper:

Administrative Speech Protocols Enabled Wallpaper
Administrative Speech Protocols Enabled Wallpaper

And, for those of you who prefer more vibrant colors in their wallpapers, here’s the full color alternative (real 😉 ) version:

Administrative Speech Protocols Enabled Wallpaper Alternate
Administrative Speech Protocols Enabled Wallpaper Alternate

Now, if you’d like to know a little about how the admin system works and get the code (don’t worry it’s free), keep reading…

With Mr. Good Bot Mostly Assembled,  it was time to build a system that let me control what Mr. Good Bot says without sitting at my desk manually modifying the database.

I understand the code I’m presenting here isn’t the “cleanest” solution but that has never been my goal with this project. So… I don’t really care! 😛

There is a PHP file (Admin.php) that generates the HTML for the interface with an external JavaScript file (adminfunctions.js) that holds the JS functions needed after the page has loaded.

I also made some minor changes to Speech.php and added a function to Functions.php so you’ll want to grab an updated copy/clone or do a pull if you are using Git.

How It Works

When the admin page loads the server includes the standard Good Bot Functions.php file to get access to all the Good Bot functions.

The bot will try to “POST” a field called “statement” and if it is present it will add it to the database for the bot to say (currently hard-coded to be Mr. Good Bot).

Next, the code will perform a $_REQUEST for fields called ShowIncomplete and ShowComplete which are served via $_GET URL parameters.

Parameters are updated using JavaScript which toggles the URL parameters via “Bit Shifting” and reload the page for the server to regenerate the page and reflect all changes.

If BOTH fields are not present then the adminfunctions.js script is included early and the JS function NewPageLoad(); is called when the page loads which will add the ShowIncomplete and ShowComplete  (e.g: ?ShowIncomplete=1&ShowComplete=1) to the URL and reload the page.

Otherwise, depending on the values (zero or one) specified for the show parameters, the complete and incomplete sentences (statements) are loaded as rows into separate tables.

If a statement type is set to show but there are no statements in the database for that type a message will inform you there are no statements for that type.

If there are statements to show each has a “Say It” button that you can use to hear the bot say the sentences completely independent of the front end bots.

There is a form with a text input field named “statement” that when submitted is posted to Admin.php for AddSentence() with the show parameters sent along as $_GET variables.

Admin.php

<!DOCTYPE html>
<?php

include 'Functions.php'; // Include Mr. Good Bot functions


$conn = ConnectToMySQL();
AddSentence($conn, 'Mr. Good Bot'); // Try to add statement to database for Mr. Good Bot  
DisconnectFromMySQL($conn);

// Check for ShowIncomplete and ShowComplete, set them if they are not set
if(!isset($_REQUEST['ShowIncomplete']) || !isset($_REQUEST['ShowComplete'])){
?>
<script src="adminfunctions.js"></script> 
<script>
// On first load add ShowIncomplete & ShowComplete URL GET parameters
NewPageLoad(); // from adminfunctions.js
</script>
<?php
}
else{ //  ShowIncomplete and ShowComplete parameters are set
	$conn = ConnectToMySQL();
	
	// Load Incomplete sentences?
	if($_REQUEST['ShowIncomplete'] == '1' ){
        $Incomplete_Sentences = LoadInCompleteSentences($conn);
	}
	
	// Load Complete sentences?
	if($_REQUEST['ShowComplete'] == '1' ){
        $Complete_Sentences = LoadCompleteSentences($conn);
	}
	
	DisconnectFromMySQL($conn);
}
?>
<html lang="en">
<title>Mr Good Bot</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>

table{
	border-collapse:collapse;
	border-spacing:0;
}
td{
	font-family:Arial, sans-serif;
	font-size:14px;
	padding:10px 5px;
	border-style:solid;
	border-width:1px;
	overflow:hidden;
	word-break:normal;
	border-color:black;
	text-align:left;
	vertical-align:top;
}
th{
	font-family:Arial, sans-serif;
	font-size:14px;
	font-weight:strong;
	padding:10px 5px;
	border-style:solid;
	border-width:1px;
	overflow:hidden;
	word-break:normal;
	border-color:black;
	text-align:left;
	vertical-align:top;
}
</style>

<body>
<h1>Mr Good Bot Admin Interface</h1>
<p>Welcome to the Admin Interface for Mr Good Bot</p>

<audio id="Admin-Test-Audio"><source src="AdminTest.wav" type="audio/wav">Your browser doesn't support the audio element.</audio>

<input type="checkbox" name="toggleIncomplete" onclick='ToggleViewStatments("incomplete")' <?php if($_REQUEST['ShowIncomplete'] === '1'){echo 'checked ';} ?>>Show Incomplete 
&nbsp;
<input type="checkbox" name="toggleComplete" onclick='ToggleViewStatments("complete")' <?php if($_REQUEST['ShowComplete'] === '1'){echo 'checked ';} ?>>Show Complete<br> 


<h3>Add New Statement</h3>

<?php
// To preserve show parameters during post as $_GET params
$post_action = $_SERVER['PHP_SELF'];
$post_action .= '?ShowIncomplete=' . $_REQUEST['ShowIncomplete'];
$post_action .= '&ShowComplete=' . $_REQUEST['ShowComplete'];
?>

<form method="post" action="<?php echo $post_action; ?>">
   <input type="text" name="statement">
   <input type="submit" name="submit" value="Add"><br>
</form>


<?php
// If there are Incomplete Sentences
if(!empty($Incomplete_Sentences)){
?>
<div id='incomplete'>
    <h3>Incomplete</h3>
    <table>
        <tr>
        <th nowrap>ID</th>
        <th nowrap>Status</th>
        <th nowrap>Audio Test</th>
        <th nowrap>Statement</th>
    </tr>
<?php
	$table = '';
	foreach($Incomplete_Sentences as $key=>$sentence){
		$table .= '    <tr>' . PHP_EOL;
		$table .= '        <td>' . $key . '</td>' . PHP_EOL;
		$table .= '        <td>Incomplete</td>' . PHP_EOL;
		$table .= '        <td><button href="#" onclick="AdminTestAudio(\''.$sentence['Statement'].'\')">Say It</button></td>' . PHP_EOL;
		$table .= '        <td>' . $sentence['Statement'] . '</td>' . PHP_EOL;
		$table .= '    </tr>';
	}
	$table .= '    </table>' . PHP_EOL;
	$table .= '</div>' . PHP_EOL;
	echo $table;
}
else{
	if($_REQUEST['ShowIncomplete'] === '1'){
?>
		<h3>Incomplete</h3>
		There are no incomplete statements.
<?php
	}
}
?>


<?php
// If there are Complete Sentences
if(!empty($Complete_Sentences)){
?>
<div id='complete'>
    <h3>Complete</h3>
    <table>
    <tr>
        <th nowrap>ID</th>
        <th nowrap>Status</th>
	    <th nowrap>Audio Test</th>
        <th nowrap>Statement</th>
    </tr>
<?php
	$table = '';
	foreach($Complete_Sentences as $key=>$sentence){
		$table .= '    <tr>' . PHP_EOL;
		$table .= '        <td>' . $key . '</td>' . PHP_EOL;
		$table .= '        <td>Complete</td>' . PHP_EOL;
		$table .= '        <td><button href="#" onclick="AdminTestAudio(\''.$sentence['Statement'].'\')">Say It</button></td>' . PHP_EOL;
		$table .= '        <td>' . $sentence['Statement'] . '</td>' . PHP_EOL;
		$table .= '    </tr>' . PHP_EOL;
	}
	$table .= '    </table>' . PHP_EOL;
	$table .= '</div>' . PHP_EOL;
	echo $table;
}
else{
	if($_REQUEST['ShowComplete'] === '1'){
?>
		<h3>Complete</h3>
		There are no complete statements.
<?php
	}
}
?>
<script src="adminfunctions.js"></script> 
</body>
</html>

 

adminfunctions.js

function AdminTestAudio(stmnt){       
   var httpRequest = new XMLHttpRequest();
   var params = 'admin=1&' + 'statement="' + stmnt +'"';
   
   httpRequest.onreadystatechange = function(){
      if(httpRequest.readyState == 4 && httpRequest.status == 200 && httpRequest.responseText == '1'){
		   
		 document.getElementById("Admin-Test-Audio").src = 'AdminTest.wav?' + Math.random();
         document.getElementById("Admin-Test-Audio").play();
      }
   }
   
   httpRequest.open('POST', 'Speech.php', true);
   httpRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
   httpRequest.send(params); 
}


// Setup the URL GET variables when the page is loaded for the first time
function NewPageLoad(){       
	var url = location.href;    // Get current URL string
	url += '?ShowIncomplete=1'; // Append the get parameter ShowIncomplete
				    // Set its value to 1
	url += '&ShowComplete=1';   // Append the get parameter ShowComplete
				    // Set its value to 1
	location.href = url;        // Reload the page
				    // This code will not run again because
				    // ShowIncomplete and ShowComplete will be set
}


// Bit Shift 0 to 1 & 1 to 0
function BitShiftToggle(value){
  return 1 >> value;
}

// Toggle if statement type is shown
// Example 1:
// URL: http://localhost/MrGoodBot/Admin.php?ShowIncomplete=0&ShowComplete=0
//
// ToggleViewStatments('incomplete')
//
// New URL: http://localhost/MrGoodBot/Admin.php?ShowIncomplete=1&ShowComplete=0
//
// Example 2:
// URL: http://localhost/MrGoodBot/Admin.php?ShowIncomplete=1&ShowComplete=1
//
// ToggleViewStatments('complete')
//
// New URL: http://localhost/MrGoodBot/Admin.php?ShowIncomplete=1&ShowComplete=0
function ToggleViewStatments(type){
	// Get params from URL
	var params = new URLSearchParams(location.search);
	
	// Toggle params
	if(type == 'incomplete'){
		params.set('ShowIncomplete', BitShiftToggle(params.get('ShowIncomplete')));
	}
	else if(type == 'complete'){
		params.set('ShowComplete', BitShiftToggle(params.get('ShowComplete')));
	}
    
	// Reload page with new params
    location.href = location.pathname + '?' + params.toString();  
}

 

GitHub

All of the code for Mr. Good Bot is available for download on GitHub for free.

GitHub: MrGoodBot

 

This administrative interface works reasonably well but leave it to my toddler to be the ultimate pre-alpha prototype tester and find a way to break it (I’m so proud)!

But… we’ll talk about that next time, thanks for reading!


If you enjoy building bots or just like my content then please like, share, comment and subscribe!

And if you want to contribute financially to my efforts, I have a Patreon where you can pledge $1 or more a month for one or more months, cancel any time.

Much Love,

~Joy