From 3572d08e0e4efb77cd1740934fe6961a3ab52c5a Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Sat, 25 Feb 2012 18:54:06 -0800 Subject: [PATCH 001/123] Synchronized this branch with the changes on my local system There were a lot of bug fixes and improvements in the local files that needed to be incorporated into this branch. --- admin/{bots.php => botpersonality.php} | 676 ++++++++++--------- admin/bugs.php | 2 +- admin/default.page.htm | 5 +- admin/error.log | 0 admin/index.php | 109 +-- admin/main.php | 28 +- admin/select_bots.php | 11 +- admin/style.css | 9 +- chatbot/debug/uhig8fjr9q2bq6ucso4l0qn6b7.txt | 12 - config/error.log | 0 config/global_config.php | 60 +- config/global_config.tpl | 9 + install/config.tpl.htm | 18 +- install/help.tpl.htm | 19 +- install/install_programo.php | 80 ++- 15 files changed, 585 insertions(+), 453 deletions(-) rename admin/{bots.php => botpersonality.php} (77%) delete mode 100644 admin/error.log delete mode 100644 chatbot/debug/uhig8fjr9q2bq6ucso4l0qn6b7.txt delete mode 100644 config/error.log diff --git a/admin/bots.php b/admin/botpersonality.php similarity index 77% rename from admin/bots.php rename to admin/botpersonality.php index f0903a9..682f687 100644 --- a/admin/bots.php +++ b/admin/botpersonality.php @@ -1,305 +1,373 @@ -getSection('TopNav'); - $leftNav = $template->getSection('LeftNav'); - $main = $template->getSection('Main'); - $topNavLinks = makeLinks('top', $topLinks, 12); - $navHeader = $template->getSection('NavHeader'); - $leftNavLinks = makeLinks('left', $leftLinks, 12); - $FooterInfo = getFooter(); - $errMsgClass = (!empty($msg)) ? "ShowError" : "HideError"; - $errMsgStyle = $template->getSection($errMsgClass); - $noLeftNav = ''; - $noTopNav = ''; - $noRightNav = $template->getSection('NoRightNav'); - $headerTitle = 'Actions:'; - $pageTitle = 'My-Program O - Bot Personality'; - $mainContent = ($func != 'updateBot') ? $func() : ''; - #$mainContent = "test... func = $func"; - $mainTitle = 'Bot Personality Settings for '.$bot_name; - if ($func == 'updateBot' or $func == 'addBotPersonaity') { - $msg = updateBot(); - include('main.php'); - } -function getBot() { - #die('entered function.'); - global $dbn; - $dbconn = db_open(); - $formCell = ' -'; - $blankCell ='   -'; - $startDiv = ' ' . "\n "; - $endDiv = "\n \n
\n"; - $inputs=""; - $row_class = 'row fm-opt'; - $bot_name = $_SESSION['poadmin']['bot_name']; - $bot_id = (isset($_SESSION['poadmin']['bot_id'])) ? $_SESSION['poadmin']['bot_id'] : 0; - //get the current bot's personality table from the db - $sql = "SELECT * FROM `botpersonality` where bot = $bot_id"; - #die ("SQL = $sql
db name = $dbn\n"); - $result = mysql_query($sql,$dbconn)or die('You have a SQL error on line '. __LINE__ . ' of ' . __FILE__ . '. Error message is: ' . mysql_error() . '.'); - $rowCount = mysql_num_rows($result); - if ($rowCount > 0) { - $left = true; - $colCount = 0; - while($row = mysql_fetch_assoc($result)) { - $rid = $row['id']; - $label = $row['name']; - $value = stripslashes_deep($row['value']); - $tmpRow = str_replace('[row_class]', $row_class, $formCell); - $tmpRow = str_replace('[row_id]', $rid, $tmpRow); - $tmpRow = str_replace('[row_label]', $label, $tmpRow); - $tmpRow = str_replace('[row_value]', $value, $tmpRow); - $inputs .= $tmpRow; - $colCount++; - if ($colCount >=3) { - $inputs .= ' - '; - $colCount = 0; - } - } - $inputs .= "\n"; - if (($colCount > 0) and ($colCount < 3)) { - for ($n = 0; $n < (3 - $colCount); $n++) { - $addCell = str_replace('[cid]',"[$n]", $blankCell); - $inputs .= $addCell; - } - } - mysql_close($dbconn); - $action = 'Update Data'; - $func = 'updateBot'; - } - else { - $inputs = newForm(); - $action = 'Add New Data'; - $func = 'addBotPersonality'; - } - if (empty($func)) $func = 'getBot'; - $form = << - - -$inputs - - - - -
- - - -
- - -endForm2; - return $form; -} - -function stripslashes_deep($value) { - $newValue = stripslashes($value); - while ($newValue != $value) { - $value = $newValue; - $newValue = stripslashes($value); - } - return $newValue; -} - - -function updateBot() { - global $bot_id, $bot_name; - $botId = (isset($_POST['bot_id'])) ? $_POST['bot_id'] : $bot_id; - $dbconn = db_open(); - $msg = ""; -/* -$sql = "UPDATE categories SET display_order = CASE id "; -foreach ($display_order as $id => $ordinal) { - $sql .= sprintf("WHEN %d THEN %d ", $id, $ordinal); -} -$sql .= "END WHERE id IN ($ids)"; -echo $sql; -*/ - $updateSQL = "UPDATE `botpersonality` SET `value` = CASE `name` \n"; - $sql = "select `id`, `name`, `value` from `botpersonality` where `bot` = $botId;"; - $changes = array(); - $additions = array(); - $result = mysql_query($sql, $dbconn) or die('You have a SQL error on line '. __LINE__ . ' of ' . __FILE__ . '. Error message is: ' . mysql_error() . ".
SQL:
\n$sql\n
\n
\n"); - while ($row = mysql_fetch_assoc($result)) { - $id = $row['id']; - $name = $row['name']; - $value = $row['value']; - $postVal = (isset($_POST[$name])) ? $_POST[$name] : ''; - if (!empty($postVal)) { - if ($postVal != $value){ - $changes[$id] = mysql_escape_string(stripslashes_deep($postVal)); - $additions[$id] = $name; - } - } - } - $changesText = implode(',', array_keys($changes)); - foreach ($changes as $id => $value) { - $name = $additions[$id]; - $updateSQL .= sprintf("WHEN '%s' THEN '%s' \n", $name, $value); - } - $updateSQL .= "END WHERE `id` IN ($changesText);"; - #die ("
\nupdate SQL = \n$updateSQL\n

\n"); - $saveSQL = str_replace("\n", "\r\n", $updateSQL); - $x = file_put_contents('sql.txt', "$saveSQL\r\n\r\n", FILE_APPEND); - $result = mysql_query($updateSQL, $dbconn) ;#or die('You have a SQL error on line '. __LINE__ . ' of ' . __FILE__ . '. Error message is: ' . mysql_error() . ".
SQL:
\n$updateSQL\n
\n
\n"); - #die("result = $result
\n error = |" . mysql_error($link) . "|\nSQL = $updateSQL
\n"); - if (!$result) $msg = 'Error updating bot.'; - $msg = (empty($msg)) ? 'Bot personality updated.' : $msg; - mysql_close($dbconn); - return $msg; -} -function addBotPersonality() { - $dbconn = db_open(); - $bot_id = $_POST['bot_id']; - $sql = "Insert into `botpersonality` (`id`, `bot`, `name`, `value`) values\n"; - $sql2 = "(null, $bot_id, '[key]', '[value]'),\n"; - $msg = ""; - $skipKeys = array('bot_id', 'action', 'func'); - foreach($_POST as $key => $value) { - if(!in_array($key, $skipKeys)) { - if($value=="") continue; - $value = mysql_escape_string(trim($value)); - $tmpSQL = str_replace('[key]', $key, $sql2); - $tmpSQL = str_replace('[value]', $value, $tmpSQL); - $sql .= $tmpSQL; - } - } - $sql = rtrim($sql,",\n"); - $result = mysql_query($sql,$dbconn) or die('You have a SQL error on line '. __LINE__ . ' of ' . __FILE__ . '. Error message is: ' . mysql_error() . ".
SQL:
\n$sql\n
\n
\n"); - if(!$result) { - $msg = 'Error updating bot personality.'; - } - elseif($msg == "") { - $msg = 'Bot personality added.'; - } - mysql_close($dbconn); - return $msg; -} - - function newForm() { - return << - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -endForm; - } +getSection('TopNav'); + $leftNav = $template->getSection('LeftNav'); + $main = $template->getSection('Main'); + $topNavLinks = makeLinks('top', $topLinks, 12); + $navHeader = $template->getSection('NavHeader'); + $leftNavLinks = makeLinks('left', $leftLinks, 12); + $FooterInfo = getFooter(); + $errMsgClass = (!empty($msg)) ? "ShowError" : "HideError"; + $errMsgStyle = $template->getSection($errMsgClass); + $noLeftNav = ''; + $noTopNav = ''; + $noRightNav = $template->getSection('NoRightNav'); + $headerTitle = 'Actions:'; + $pageTitle = 'My-Program O - Bot Personality'; + #$mainContent = ($func != 'updateBot') ? $func() : ''; + $mainContent = "main content"; + #$msg = "function = $func"; + switch ($func) { + case 'updateBot': + $msg = $func(); + $mainContent = getBot(); + break; + default: + $mainContent = $func(); + #$msg = "function = $func"; + } +/* +*/ + #$mainContent = "test... func = $func"; + $mainTitle = 'Bot Personality Settings for '.$bot_name; + if ($func == 'updateBot' or $func == 'addBotPersonaity') { + $msg = updateBot(); + include('main.php'); + } +function getBot() { + #die('entered function.'); + global $dbn; + $dbconn = db_open(); + $formCell = ' +'; + $blankCell ='   +'; + $startDiv = ' ' . "\n "; + $endDiv = "\n \n
\n"; + $inputs=""; + $row_class = 'row fm-opt'; + $bot_name = $_SESSION['poadmin']['bot_name']; + $bot_id = (isset($_SESSION['poadmin']['bot_id'])) ? $_SESSION['poadmin']['bot_id'] : 0; + //get the current bot's personality table from the db + $sql = "SELECT * FROM `botpersonality` where bot = $bot_id"; + #die ("SQL = $sql
db name = $dbn\n"); + $result = mysql_query($sql,$dbconn)or $msg .= SQL_Error(mysql_errno()); + if ($result) { + $rowCount = mysql_num_rows($result); + if ($rowCount > 0) { + $left = true; + $colCount = 0; + while($row = mysql_fetch_assoc($result)) { + $rid = $row['id']; + $label = $row['name']; + $value = stripslashes_deep($row['value']); + $tmpRow = str_replace('[row_class]', $row_class, $formCell); + $tmpRow = str_replace('[row_id]', $rid, $tmpRow); + $tmpRow = str_replace('[row_label]', $label, $tmpRow); + $tmpRow = str_replace('[row_value]', $value, $tmpRow); + $inputs .= $tmpRow; + $colCount++; + if ($colCount >=3) { + $inputs .= ' + '; + $colCount = 0; + } + } + $inputs .= "\n"; + if (($colCount > 0) and ($colCount < 3)) { + for ($n = 0; $n < (3 - $colCount); $n++) { + $addCell = str_replace('[cid]',"[$n]", $blankCell); + $inputs .= $addCell; + } + } + mysql_close($dbconn); + $action = 'Update Data'; + $func = 'updateBot'; + } + else { + $inputs = newForm(); + $action = 'Add New Data'; + $func = 'addBotPersonality'; + } + } + if (empty($func)) $func = 'getBot'; + $form = << + + +$inputs + + + + +
+ + + +
+ + +endForm2; + return $form; +} + +function stripslashes_deep($value) { + $newValue = stripslashes($value); + while ($newValue != $value) { + $value = $newValue; + $newValue = stripslashes($value); + } + return $newValue; +} + + +function updateBot() { + global $bot_id, $bot_name; + $botId = (isset($_POST['bot_id'])) ? $_POST['bot_id'] : $bot_id; + $dbconn = db_open(); + $msg = ""; + if (!empty($_POST['newEntryName'])) { + $newEntryNames = $_POST['newEntryName']; + $newEntryValues = $_POST['newEntryValue']; + $addSQL = "Insert into `botpersonality` (`id`, `bot`, `name`, `value`) values\n"; + $addSQLTemplate = "(null, $bot_id, '[key]', '[value]'),\n"; + foreach ($newEntryNames as $index => $key) { + $value = $newEntryValues[$index]; + if (empty($value)) continue; + $tmpSQL = str_replace('[key]', $key, $addSQLTemplate); + $tmpSQL = str_replace('[value]', $value, $tmpSQL); + $addSQL .= $tmpSQL; + } + $addSQL = rtrim($addSQL,",\n"); + $result = mysql_query($addSQL,$dbconn) or die('You have a SQL error on line '. __LINE__ . ' of ' . __FILE__ . '. Error message is: ' . mysql_error() . ".
SQL:
\n$addSQL\n
\n
\n"); + if(!$result) { + $msg = 'Error updating bot personality.'; + } + elseif($msg == "") { + $msg = 'Bot personality added.'; + } + } + + $updateSQL = "UPDATE `botpersonality` SET `value` = CASE `name` \n"; + $sql = "SELECT * FROM `botpersonality` where bot = $botId;"; + $changes = array(); + $additions = array(); + $result = mysql_query($sql, $dbconn) or $msg .= SQL_Error(mysql_errno()); + if ($result) { + while ($row = mysql_fetch_assoc($result)) { + $id = $row['id']; + $name = $row['name']; + $value = $row['value']; + $postVal = (isset($_POST[$name])) ? $_POST[$name] : ''; + if (!empty($postVal)) { + if ($postVal != $value){ + $changes[$id] = mysql_escape_string(stripslashes_deep($postVal)); + $additions[$id] = $name; + } + } + } + } + if (!empty($additions)) { + $changesText = implode(',', array_keys($changes)); + foreach ($changes as $id => $value) { + $name = $additions[$id]; + $updateSQL .= sprintf("WHEN '%s' THEN '%s' \n", $name, $value); + } + $updateSQL .= "END WHERE `id` IN ($changesText);"; + #die ("
\nupdate SQL = \n$updateSQL\n

\n"); + $saveSQL = str_replace("\n", "\r\n", $updateSQL); + $x = file_put_contents('sql.txt', "$saveSQL\r\n\r\n", FILE_APPEND); + $result = mysql_query($updateSQL, $dbconn) or die('You have a SQL error on line '. __LINE__ . ' of ' . __FILE__ . '. Error message is: ' . mysql_error() . ".
SQL:
\n$updateSQL\n
\n
\n"); + #die("result = $result
\n error = |" . mysql_error($link) . "|\nSQL = $updateSQL
\n"); + if (!$result) $msg = 'Error updating bot.'; + $msg = (empty($msg)) ? 'Bot personality updated.' : $msg; + } + else $msg = 'Something'; + mysql_close($dbconn); + return $msg; +} + +function addBotPersonality() { + $dbconn = db_open(); +/* + $postVars = print_r($_POST, true); + die ("Post vars:
\n$postVars\n
"); +*/ + $bot_id = $_POST['bot_id']; + $sql = "Insert into `botpersonality` (`id`, `bot`, `name`, `value`) values\n"; + $sql2 = "(null, $bot_id, '[key]', '[value]'),\n"; + $msg = ""; + $newEntryNames = (isset($_POST['newEntryName'])) ? $_POST['newEntryName'] : ''; + $newEntryValues = (isset($_POST['newEntryValue'])) ? $_POST['newEntryValue'] : ''; + if (!empty($newEntryNames)) { + foreach ($newEntryNames as $index => $key) { + $value = $newEntryValues[$index]; + if (!empty($value)) { + $tmpSQL = str_replace('[key]', $key, $sql2); + $tmpSQL = str_replace('[value]', $value, $tmpSQL); + $sql .= $tmpSQL; + } + } + } + $skipKeys = array('bot_id', 'action', 'func', 'newEntryName', 'newEntryValue'); + foreach($_POST as $key => $value) { + if(!in_array($key, $skipKeys)) { + if($value=="") continue; + if (is_array($value)) { + foreach ($value as $index => $fieldValue) { + $field = $key[$fieldValue]; + $fieldValue = mysql_escape_string(trim($fieldValue)); + $tmpSQL = str_replace('[key]', $field, $sql2); + $tmpSQL = str_replace('[value]', $fieldValue, $tmpSQL); + $sql .= $tmpSQL; + } + continue; + } + else { + $value = mysql_escape_string(trim($value)); + $tmpSQL = str_replace('[key]', $key, $sql2); + $tmpSQL = str_replace('[value]', $value, $tmpSQL); + $sql .= $tmpSQL; + } + } + } + $sql = rtrim($sql,",\n"); + $result = mysql_query($sql,$dbconn) or die('You have a SQL error on line '. __LINE__ . ' of ' . __FILE__ . '. Error message is: ' . mysql_error() . ".
SQL:
\n$sql\n
\n
\n"); + if(!$result) { + $msg = 'Error updating bot personality.'; + } + elseif($msg == "") { + $msg = 'Bot personality added!'; + } + mysql_close($dbconn); + return $msg; +} + + function newForm() { + return << + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +   +   +   + + +endForm; + } ?> \ No newline at end of file diff --git a/admin/bugs.php b/admin/bugs.php index eb7ee4f..dc3c27a 100644 --- a/admin/bugs.php +++ b/admin/bugs.php @@ -144,7 +144,7 @@ function sendMail() { $cba = checkBadAddress($email); $cbip = checkBadIP(); if ($email != "" and $name != "" and $subject != "" and $cba == 0 and $cbip == 0 and $message != "" and ($captcha == $capKey)) { - $toAddr = "dmorton@geekcavecreations.com, bugs@program-o.com"; + $toAddr = "dmorton@geekcavecreations.com, " . BUGS_EMAIL; $fromAddr = "$email"; $header = "From: $name <$email>"; $result = mail ($toAddr, $subject, $message, $header); diff --git a/admin/default.page.htm b/admin/default.page.htm index b21768e..03035e7 100644 --- a/admin/default.page.htm +++ b/admin/default.page.htm @@ -222,10 +222,11 @@ - + - + Yes + No diff --git a/admin/error.log b/admin/error.log deleted file mode 100644 index e69de29..0000000 diff --git a/admin/index.php b/admin/index.php index 7982ad3..26aa167 100644 --- a/admin/index.php +++ b/admin/index.php @@ -14,6 +14,7 @@ ini_set('error_log', _ADMIN_PATH_ . 'error.log'); ini_set('html_errors', false); ini_set('display_errors', false); + $msg = ''; # Show errors on the dev server. Comment out or remove to disable. if($server==$dev_host or ((!empty($alternate_local_server_name) and $server == $alternate_local_server_name))) { @@ -26,19 +27,20 @@ session_start(); $myPage = (isset($_GET['myPage'])) ? $_GET['myPage'] : ''; $hide_logo = (isset($_SESSION['display'])) ? $_SESSION['display'] : ''; - if((!isset($_SESSION['poadmin']['uid'])) || ($_SESSION['poadmin']['uid']=="")) { - $msg = 'Session timed out'; - $_GET['page'] = 'logout'; - } - else { - $name = $_SESSION['poadmin']['name']; - $ip = $_SESSION['poadmin']['ip']; - $last = $_SESSION['poadmin']['lastlogin']; - $lip = $_SESSION['poadmin']['lip']; - $llast = $_SESSION['poadmin']['llastlogin']; - $bot_name = $_SESSION['poadmin']['bot_name']; - $bot_id = $_SESSION['poadmin']['bot_id']; - #$hide_logo = (isset($_SESSION['poadmin']['display'])) ? $_SESSION['poadmin']['display'] : $hide_logo; + if (!empty($_SESSION)) { + if((!isset($_SESSION['poadmin']['uid'])) || ($_SESSION['poadmin']['uid']=="")) { + $msg .= "Session timed out
\n"; + $_GET['page'] = 'logout'; + } + else { + $name = $_SESSION['poadmin']['name']; + $ip = $_SESSION['poadmin']['ip']; + $last = $_SESSION['poadmin']['lastlogin']; + $lip = $_SESSION['poadmin']['lip']; + $llast = $_SESSION['poadmin']['llastlogin']; + $bot_name = $_SESSION['poadmin']['bot_name']; + $bot_id = $_SESSION['poadmin']['bot_id']; + } } //load shared files require_once(_LIB_PATH_ . 'db_functions.php'); @@ -47,7 +49,6 @@ # Load the template file $thisPath = dirname(__FILE__); $template = new Template("$thisPath/default.page.htm"); - $msg = ''; $leftLinks = makeLeftLinks(); $topLinks = makeTopLinks(); # set template section defaults @@ -74,7 +75,7 @@ $leftNavLinks = ''; $mediaType = ' media="screen"'; $mainTitle = 'Program O Login'; - $FooterInfo = '

© 2011 My Program-O
www.program-o.com

'; + $FooterInfo = '

© 2011-2012 My Program-O
www.program-o.com

'; $headerTitle = ''; $pageTitle = 'My-Program O - Login'; $upperScripts = ''; @@ -85,54 +86,53 @@ $pw = mysql_escape_string(strip_tags(trim($_POST['pw']))); $dbconn = db_open(); $sql = "SELECT * FROM `myprogramo` WHERE uname = '".$uname."' AND pword = '".MD5($pw)."'"; - $result = mysql_query($sql,$dbconn) or die ("mySQL error:" . mysql_error($dbconn)); - $count = mysql_num_rows($result); - $msg =""; - if($count>0) { - $row=mysql_fetch_array($result); - $_SESSION['poadmin']['uid']=$row['id']; - $_SESSION['poadmin']['name']=$row['uname']; - $_SESSION['poadmin']['lip']=$row['lastip']; - $_SESSION['poadmin']['llastlogin']=date('l jS \of F Y h:i:s A', strtotime($row['lastlogin'])); - if(!empty($_SERVER['HTTP_CLIENT_IP'])) { //check ip from share internet - $ip=$_SERVER['HTTP_CLIENT_IP']; - } - elseif(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { //to check ip is pass from proxy - $ip=$_SERVER['HTTP_X_FORWARDED_FOR']; - } - else { - $ip=$_SERVER['REMOTE_ADDR']; - } - $sqlupdate = "UPDATE `myprogramo` SET `lastip` = '$ip', `lastlogin` = CURRENT_TIMESTAMP WHERE uname = '$uname' limit 1"; - //echo $sql; - $result = mysql_query($sqlupdate,$dbconn); - $transact = mysql_affected_rows($dbconn); - $_SESSION['poadmin']['ip']=$ip; - $_SESSION['poadmin']['lastlogin']=date('l jS \of F Y h:i:s A'); - $sql = "SELECT * FROM `bots` WHERE bot_active = '1' ORDER BY bot_id ASC LIMIT 1"; - $result = mysql_query($sql,$dbconn); + $result = mysql_query($sql,$dbconn) or $msg .= SQL_Error(mysql_errno()); + if ($result) { $count = mysql_num_rows($result); - $msg =""; - if($count>0) { + if($count > 0) { $row=mysql_fetch_array($result); - $_SESSION['poadmin']['bot_id']=$row['bot_id']; - $_SESSION['poadmin']['bot_name']=$row['bot_name']; + $_SESSION['poadmin']['uid']=$row['id']; + $_SESSION['poadmin']['name']=$row['uname']; + $_SESSION['poadmin']['lip']=$row['lastip']; + $_SESSION['poadmin']['llastlogin']=date('l jS \of F Y h:i:s A', strtotime($row['lastlogin'])); + if(!empty($_SERVER['HTTP_CLIENT_IP'])) { //check ip from share internet + $ip=$_SERVER['HTTP_CLIENT_IP']; + } + elseif(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { //to check ip is pass from proxy + $ip=$_SERVER['HTTP_X_FORWARDED_FOR']; + } + else { + $ip=$_SERVER['REMOTE_ADDR']; + } + $sqlupdate = "UPDATE `myprogramo` SET `lastip` = '$ip', `lastlogin` = CURRENT_TIMESTAMP WHERE uname = '$uname' limit 1"; + $result = mysql_query($sqlupdate,$dbconn); + $transact = mysql_affected_rows($dbconn); + $_SESSION['poadmin']['ip']=$ip; + $_SESSION['poadmin']['lastlogin']=date('l jS \of F Y h:i:s A'); + $sql = "SELECT * FROM `bots` WHERE bot_active = '1' ORDER BY bot_id ASC LIMIT 1"; + $result = mysql_query($sql,$dbconn); + $count = mysql_num_rows($result); + if($count > 0) { + $row=mysql_fetch_array($result); + $_SESSION['poadmin']['bot_id']=$row['bot_id']; + $_SESSION['poadmin']['bot_name']=$row['bot_name']; + } + else { + $_SESSION['poadmin']['bot_id']=-1; + $_SESSION['poadmin']['bot_name']="unknown"; + } } else { - $_SESSION['poadmin']['bot_id']=-1; - $_SESSION['poadmin']['bot_name']="unknown"; + $msg .= "incorrect username/password
\n"; } } - else { - $msg = "incorrect username/password"; - } mysql_close($dbconn); if($msg == "") { include ('main.php'); } } elseif(isset($_GET['msg'])) { - $msg = htmlentities($_GET['msg']); + $msg .= htmlentities($_GET['msg']); } elseif(isset($_GET['page'])) { $curPage = $_GET['page']; @@ -274,7 +274,7 @@ function makeTopLinks() { ), array( '[linkClass]' => ' class="[curClass]"', - '[linkHref]' => ' href="http://www.program-o.com/ns/faq/"', + '[linkHref]' => ' href="'.FAQ_URL.'"', '[linkOnclick]' => '', '[linkAlt]' => ' alt="The Program O User\'s Guide"', '[linkTitle]' => ' title="The Program O User\'s Guide"', @@ -298,7 +298,7 @@ function makeTopLinks() { ), array( '[linkClass]' => ' class="[curClass]"', - '[linkHref]' => ' href="http://www.program-o.com/support/"', + '[linkHref]' => ' href="'.SUP_URL.'"', '[linkOnclick]' => '', '[linkAlt]' => ' alt="Get support for Program O"', '[linkTitle]' => ' title="Get support for Program O"', @@ -327,7 +327,7 @@ function makeLeftLinks() { ), array( '[linkClass]' => ' class="[curClass]"', - '[linkHref]' => ' href="http://github.com/?page=bots"', + '[linkHref]' => ' href="http://github.com/?page=botpersonality"', '[linkOnclick]' => '', '[linkAlt]' => ' alt="Edit your bot\'s personality"', '[linkTitle]' => ' title="Edit your bot\'s personality"', @@ -433,4 +433,5 @@ function makeLeftLinks() { return $out; } + ?> diff --git a/admin/main.php b/admin/main.php index 796aa83..70555d8 100644 --- a/admin/main.php +++ b/admin/main.php @@ -50,21 +50,23 @@ function getRSS() { global $template; $out = ''; $itemTemplate = $template->getSection('RSSItemTemplate'); - $url = 'http://www.program-o.com/ns/feed/rss/'; - $xml = simplexml_load_file($url); //loading the document - $title = $xml->channel->title; //gets the title of the document. - $rss = simplexml_load_file($url); - if($rss) { - $items = $rss->channel->item; - foreach($items as $item) { - $title = $item->title; - $link = $item->link; - $published_on = $item->pubDate; - $description = $item->description; - $out .= "

$title

\n"; - $out .= "

$description

"; + $xml = @simplexml_load_file(RSS_URL); //loading the document + if ($xml) { + $title = $xml->channel->title; //gets the title of the document. + $rss = simplexml_load_file(RSS_URL); + if($rss) { + $items = $rss->channel->item; + foreach($items as $item) { + $title = $item->title; + $link = $item->link; + $published_on = $item->pubDate; + $description = $item->description; + $out .= "

$title

\n"; + $out .= "

$description

"; + } } } + else $out = 'RSS Feed not available'; return $out; } diff --git a/admin/select_bots.php b/admin/select_bots.php index d05b68b..7f1f89c 100644 --- a/admin/select_bots.php +++ b/admin/select_bots.php @@ -165,6 +165,14 @@ function getSelectedBot() { } $action = "update"; } + $debugemail_0 = $debugemail_1 = ''; + switch ($bot_debugemail) { + case 0: + $debugemail_0 = 'checked="checked"'; + break; + case 1: + $debugemail_1 = 'checked="checked"'; + } mysql_close($dbconn); } else { @@ -187,7 +195,8 @@ function getSelectedBot() { '[bot_id]','[bot_name]','[bot_desc]','[parent_options]','[sel_yes]','[sel_no]', '[sel_html]','[sel_xml]','[sel_json]','[sel_session]','[sel_db]','[sel_fyes]', '[sel_fno]','[bot_conversation_lines]','[bot_remember_up_to]','[bot_debugemail]', - '[dm_]','[dm_i]','[dm_ii]','[dm_iii]','[ds_]','[ds_i]','[ds_ii]','[ds_iii]','[action]' + '[dm_]','[dm_i]','[dm_ii]','[dm_iii]','[ds_]','[ds_i]','[ds_ii]','[ds_iii]','[action]', + '[debugemail_0]','[debugemail_1]' ); foreach ($searches as $search) { $replace = str_replace('[', '', $search); diff --git a/admin/style.css b/admin/style.css index 40cbea6..1b52a49 100644 --- a/admin/style.css +++ b/admin/style.css @@ -63,11 +63,12 @@ span.eightyPercent input.wide { width: 67%; margin: 0; float: right; } .narrow { padding-left: 0; float: left;width: 25%;text-align: left; border: 1px dashed blue;} .errMsg { position: absolute; - top: 25px; - left: 25px; - width: 250px; + top: 10px; + left: 10px; + min-width: 250px; + width: 30%; background-color: #FFF45A; - background-color: transparent; + background-color: #FCC; /*transparent;*/ color: black; vertical-align: middle; text-align: center; diff --git a/chatbot/debug/uhig8fjr9q2bq6ucso4l0qn6b7.txt b/chatbot/debug/uhig8fjr9q2bq6ucso4l0qn6b7.txt deleted file mode 100644 index ff75b66..0000000 --- a/chatbot/debug/uhig8fjr9q2bq6ucso4l0qn6b7.txt +++ /dev/null @@ -1,12 +0,0 @@ -Array -( - [29-11-2011 16:10:13.53978400] => Array - ( - [fileName] => see above - [functionName] => see above - [line] => see above - [info] => MYSQL ERROR 1062 - Duplicate entry 'Dave' for key 'uname' when excuting insert into `myprogramo` (`id`, `uname`, `pword`, `lastip`) values(null, 'Dave', '986fb4494b455629e27ba1d1ad8cfdc8', '192.168.1.100'); - [srai_iteration] => 0 - ) - -) diff --git a/config/error.log b/config/error.log deleted file mode 100644 index e69de29..0000000 diff --git a/config/global_config.php b/config/global_config.php index 2b4a05e..0aadc0f 100644 --- a/config/global_config.php +++ b/config/global_config.php @@ -49,7 +49,7 @@ //------------------------------------------------------------------------ $server = strtolower($_SERVER['HTTP_HOST']); //leave this to auto detect $dev_host = "localhost"; //the name of your dev server - $alternate_local_server_name = ""; // Use if you test on a local network - the network name of the server computer. + $alternate_local_server_name = "programo.local"; // Use if you test on a local network - the network name of the server computer. //------------------------------------------------------------------------ // parent bot // the parent bot is used to find aiml matches if no match is found for the current bot @@ -73,17 +73,17 @@ $time_zone_locale = "America/Los_Angeles"; // a full list can be found at http://uk.php.net/manual/en/timezones.php $host = ""; //the localhost name - $dbh = ""; # dev remote server location + $dbh = "localhost"; # dev remote server location $dbPort = "3306"; # dev database name/prefix - $dbn = ""; # dev database name/prefix - $dbu = ""; # dev database username - $dbp = ""; # dev database password + $dbn = "morgaine"; # dev database name/prefix + $dbu = "Dave"; # dev database username + $dbp = "411693055"; # dev database password //these are the admin DB settings in case you want make the admin a different db user with more privs - $adm_dbh = ""; - $adm_dbn = ""; - $adm_dbu = ""; - $adm_dbp = ""; + $adm_dbh = "localhost"; + $adm_dbn = "morgaine"; + $adm_dbu = "Dave"; + $adm_dbp = "411693055"; //------------------------------------------------------------------------ // Default bot settings @@ -99,9 +99,9 @@ $default_format = "html"; $default_pattern = "*"; $default_update_aiml_code = 1; - $default_conversation_lines = 10; + $default_conversation_lines = 30; $default_remember_up_to = 10; - $default_debugemail = ""; + $default_debugemail = "dmorton@geekcavecreations.com"; /* * $default_debugshow - The level of messages to show the user * 0=none, @@ -109,7 +109,7 @@ * 2=error+general+sql, * 3=everything */ - $default_debugshow = 0; + $default_debugshow = 3; /* * $default_debugmode - How to show the debug data @@ -156,18 +156,18 @@ //------------------------------------------------------------------------ $time_zone_locale = "America/Los_Angeles"; // a full list can be found at http://uk.php.net/manual/en/timezones.php $host = ""; //the localhost name - $dbh = ""; + $dbh = "h50mysql91.secureserver.net"; $dbPort = "3306"; - $dbn = ""; - $dbu = ""; - $dbp = ""; + $dbn = "gccpchat"; + $dbu = "gccpchat"; + $dbp = "GCC6114djm"; //these are the admin DB settings in case you want make the admin a different db user with more privs - $adm_dbh = ""; - $adm_dbn = ""; - $adm_dbu = ""; - $adm_dbp = ""; + $adm_dbh = "h50mysql91.secureserver.net"; + $adm_dbn = "gccpchat"; + $adm_dbu = "Dave"; + $adm_dbp = "411693055"; //------------------------------------------------------------------------ // Default bot settings @@ -183,9 +183,9 @@ $default_format = "html"; $default_pattern = "*"; $default_update_aiml_code = 1; - $default_conversation_lines = 10; + $default_conversation_lines = 30; $default_remember_up_to = 10; - $default_debugemail = ""; + $default_debugemail = "[debugemail]"; /* * $default_debugshow - The level of messages to show the user * 0=none, @@ -193,7 +193,7 @@ * 2=error+general+sql, * 3=everything */ - $default_debugshow = 0; + $default_debugshow = 3; /* * $default_debugmode - How to show the debug data @@ -203,7 +203,7 @@ * 3 = email each conversation line (not recommended) */ $default_debugmode = 1; - $default_save_state = "session"; + $default_save_state = "[save_state]"; $error_response = "Internal error detected. Please inform my botmaster."; //------------------------------------------------------------------------ @@ -235,7 +235,16 @@ //------------------------------------------------------------------------ // Set Misc Data //------------------------------------------------------------------------ - $botmaster_name = ""; + $botmaster_name = "Dave Morton"; + +//------------------------------------------------------------------------ +// Set Program O Website URLs +//------------------------------------------------------------------------ + + define('RSS_URL', 'http://www.program-o.com/ns/feed/rss/'); + define('FAQ_URL', 'http://www.program-o.com/ns/faq/'); + define('SUP_URL', 'http://www.program-o.com/ns/feed/Support/'); + define('BUGS_EMAIL', 'bugs@program-o.com'); //------------------------------------------------------------------------ // @@ -291,4 +300,5 @@ // Set Script Installation as completed //------------------------------------------------------------------------ + define('SCRIPT_INSTALLED', true); ?> \ No newline at end of file diff --git a/config/global_config.tpl b/config/global_config.tpl index 8e5b134..6aa122a 100644 --- a/config/global_config.tpl +++ b/config/global_config.tpl @@ -237,6 +237,15 @@ //------------------------------------------------------------------------ $botmaster_name = "[botmaster_name]"; +//------------------------------------------------------------------------ +// Set Program O Website URLs +//------------------------------------------------------------------------ + + define('RSS_URL', 'http://www.program-o.com/ns/feed/rss/'); + define('FAQ_URL', 'http://www.program-o.com/ns/faq/'); + define('SUP_URL', 'http://www.program-o.com/ns/feed/Support/'); + define('BUGS_EMAIL', 'bugs@program-o.com'); + //------------------------------------------------------------------------ // // THERE SHOULD BE NO NEED TO EDIT ANYTHING BELOW THIS LINE diff --git a/install/config.tpl.htm b/install/config.tpl.htm index 40f107f..b2277b0 100644 --- a/install/config.tpl.htm +++ b/install/config.tpl.htm @@ -474,7 +474,7 @@ Conversation Lines: - +
@@ -490,6 +490,10 @@
Installation Complete!
[notes] + +
Installation Failed!
+ [errorMessage] +
WARNING! @@ -502,15 +506,3 @@
- - - - - - - - - - - - diff --git a/install/help.tpl.htm b/install/help.tpl.htm index 963cc45..734ff81 100644 --- a/install/help.tpl.htm +++ b/install/help.tpl.htm @@ -100,7 +100,7 @@ Number of bots
The default is one (1) bot, and in most cases the script will automatically set this to the correct value. - If, for some reason, it doesn't, then you can set it manually, after you set the names of your bot(s). + If, for some reason, it doesn't, then you can set it manually.
@@ -110,7 +110,8 @@
Name of the default bot
- This is the name of your primary (default) bot. Simply select the name you want to use from the drop-down list. + This is the name of your primary (default) bot. Simply select the name you want to use from the drop-down list, + after you set the names of your bot(s).
@@ -137,7 +138,10 @@
Email Address -
Required: Used internally to set up contact details,and to send debugging data.
+
+ Required: Used internally to set up contact details, and to (optionally) send debugging data. + See below for debugging options. +
Debugging Options @@ -156,8 +160,8 @@ Stored in the /chatbot/debug/ folder.
  • - Display Prints debugging data directly to the screen. Note - only works when Response - Format is set to HTML(See below) + Display Prints debugging data directly to the screen. Note - only works when Response + Format is set to HTML(See below)
  • email Sends debugging data via email - WARNING! can create a LOT of email @@ -181,7 +185,8 @@ Everything The results from a vast number of operations, calculations or function calls (sometimes several times within certain functions) is generated. WARNING! HUGE amounts of data is generated with this setting, to the point where script performance will be affected, - especially if the debug output method is set to "Show in Source". + especially if the debug output method is set to "Show in Source". And for God's sake, DON'T + use this if you're going to have debugging data sent to your email!
  • For the most part, this should be set to "Nothing", unless you're trying to trace a problem with your AIML @@ -248,7 +253,7 @@
    This is usually best kept as the default pattern, unless you're upgrading from Program O version 1.0, which used "RANDOM PICKUP LINE" as the pattern. Even if you are upgrading, you can also leave - this value as * and modify your AIML piclup line category to use * for it's pattern. + this value as * and modify your AIML pickup line category to use * for it's pattern.
    diff --git a/install/install_programo.php b/install/install_programo.php index 28df874..49d5a64 100644 --- a/install/install_programo.php +++ b/install/install_programo.php @@ -12,6 +12,7 @@ If ($myPHP_Version < 5) die ("I'm sorry, but Program O requires PHP version 5.0 or greater to function. Please ask your hosting provider to upgrade."); session_name('PGO_install'); session_start(); +$_SESSION['errorMessage'] = (!empty($_SESSION['errorMessage'])) ? $_SESSION['errorMessage'] : ''; require_once('../library/buildSelect.php'); require_once('../config/global_config.php'); define ('SECTION_START', ''); # search params for start and end of sections @@ -19,7 +20,8 @@ define ('PHP_SELF', $_SERVER['PHP_SELF']); # search params for start and end of sections ini_set("display_errors", 1); ini_set("log_errors",true); -ini_set("error_log","../logs/error.log"); +#ini_set("error_log","../logs/error.log"); +ini_set("error_log","error.log"); $currentDir = getcwd(); $defaultBaseDir = rtrim(str_ireplace('admin', '', $currentDir), '\\/'); @@ -88,6 +90,7 @@ $content = str_replace('[local_dbPort]', $dbPort, $content); $content = str_replace('[remote_dbPort]', $dbPort, $content); $content = str_replace('[PHP_SELF]', PHP_SELF, $content); +$content = str_replace('[errorMessage]', $_SESSION['errorMessage'], $content); foreach ($replVarsArray as $search => $replace) { $replace = trim($replace); if (!isset($$replace)) $$replace = 'crap'; @@ -136,6 +139,8 @@ function getSection($sectionName, $page_template, $notFoundReturn = true) { function save($page) { global $replVarsArray, $quickdebug, $writetotemp; $curSession = print_r($_SESSION, true); + #if (!checkDBContents($dbn)) fillDB(); + checkDBContents($_SESSION['local_dbn']); switch ($page) { case 1: $out = 2; @@ -197,41 +202,82 @@ function getMain($page, $page_template) { $conn = db_open(); for ($loop = 1; $loop <= $_SESSION['bot_count']; $loop++) { $sql = str_replace('[cur_bot_ID]', $loop, $sql_template); - $sql = str_replace('[cur_bot_name]',$_SESSION["bot_name_$loop"], $sql); - $sql = str_replace('[cur_bot_desc]',$_SESSION["bot_desc_$loop"], $sql); - $sql = str_replace('[cur_bot_active]',$_SESSION["bot_active_$loop"], $sql); - $sql = str_replace('[cur_bot_parent_id]',$_SESSION['default_bot_id'], $sql); - $sql = str_replace('[cur_format]',$_SESSION["format_$loop"], $sql); - $sql = str_replace('[cur_save_state]',$_SESSION["save_state_$loop"], $sql); - $sql = str_replace('[cur_conversation_lines]',$_SESSION["conversation_lines_$loop"], $sql); - $sql = str_replace('[cur_remember_up_to]',$_SESSION["remember_up_to_$loop"], $sql); + $sql = @str_replace('[cur_bot_name]',$_SESSION["bot_name_$loop"], $sql); + $sql = @str_replace('[cur_bot_desc]',$_SESSION["bot_desc_$loop"], $sql); + $sql = @str_replace('[cur_bot_active]',$_SESSION["bot_active_$loop"], $sql); + $sql = @str_replace('[cur_bot_parent_id]',$_SESSION['default_bot_id'], $sql); + $sql = @str_replace('[cur_format]',$_SESSION["format_$loop"], $sql); + $sql = @str_replace('[cur_save_state]',$_SESSION["save_state_$loop"], $sql); + $sql = @str_replace('[cur_conversation_lines]',$_SESSION["conversation_lines_$loop"], $sql); + $sql = @str_replace('[cur_remember_up_to]',$_SESSION["remember_up_to_$loop"], $sql); $sql = str_replace('[cur_debugemail]',$_SESSION["default_debugemail"], $sql); $sql = str_replace('[cur_debugshow]',$_SESSION["default_debugshow"], $sql); $sql = str_replace('[cur_debugmode]',$_SESSION["default_debugmode"], $sql); $sql = str_replace('[cur_default_aiml_pattern]',$_SESSION["default_pattern"], $sql); if (!isset($_SESSION["update_aiml_code_$loop"])) $_SESSION["update_aiml_code_$loop"] = 0; $sql = str_replace('[cur_update_aiml_code]',$_SESSION["update_aiml_code_$loop"], $sql); - $x = db_query($sql, $conn); + $x = db_query($sql, $conn) or $_SESSION['errorMessage'] = 'Could not add admin account! Error = ' . mysql_error(); } global $adm_dbu, $adm_dbp; $encrypted_adm_dbp = md5($adm_dbp); $cur_ip = $_SERVER['REMOTE_ADDR']; $adminSQL = "insert into `myprogramo` (`id`, `uname`, `pword`, `lastip`) values(null, '$adm_dbu', '$encrypted_adm_dbp', '$cur_ip');"; - $x = db_query($adminSQL, $conn); - return getSection('InstallComplete', $page_template); + $result = db_query($adminSQL, $conn) or $_SESSION['errorMessage'] = 'Could not add admin account! Error = ' . mysql_error(); + return ($result) ? getSection('InstallComplete', $page_template) : getSection('InstallError', $page_template); } function page2form() { global $page_template; - $out = ''; - $namesArray = explode("\n", $_SESSION['botNames']); - for ($loop = 1; $loop <= $_SESSION['bot_count']; $loop++) { + $out = ''; + $namesArray = explode("\n", $_SESSION['botNames']); + for ($loop = 1; $loop <= $_SESSION['bot_count']; $loop++) { + $curConvoLines = (!empty($_SESSION["conversation_lines_$loop"])) ? $_SESSION["conversation_lines_$loop"] : $_SESSION['default_conversation_lines']; $tmpSection = getSection('BotsTableForm', $page_template); $tmpSection = str_replace('[bot_ID]', $loop, $tmpSection); $tmpSection = str_replace('[current_bot_name]', rtrim($namesArray[$loop-1]), $tmpSection); + $tmpSection = str_replace('[cl_value]', $curConvoLines, $tmpSection); $out .= $tmpSection; - } + } return $out; } -?> \ No newline at end of file +function checkDBContents($dbn) { + $local_dbh = $_SESSION['local_dbh']; + $local_dbn = $_SESSION['local_dbn']; + $local_dbu = $_SESSION['local_dbu']; + $local_dbp = $_SESSION['local_dbp']; + $local_dbPort = $_SESSION['local_dbPort']; + $death = << +Variables: +local_dbh: $local_dbh +local_dbu: $local_dbu +local_dbp: $local_dbp +local_dbn: $local_dbn +local_dbPort: $local_dbPort + + +endDeath; + $host = ($local_dbh == 'Required') ? 'localhost' : $local_dbh; + $conn = mysql_connect($host, $local_dbu, $local_dbp) or die( "mysql_connect error:". mysql_errno() . ', ' . mysql_error() . $death); + mysql_select_db($local_dbn,$conn); + $sql = "show tables;"; + $result = mysql_query($sql,$conn) or die ("Houston, we have a problem! " . mysql_error() . ", sql = $sql"); + $out = mysql_fetch_assoc($result); + if (empty($out)) { + $sql = file_get_contents('new.sql'); + $queries = preg_split("/;/", $sql); + foreach ($queries as $query){ + $death .= "sql:\n$query\n\n"; +#die("This is where I died: " . __FILE__ . ', line ' . __LINE__ . "\nDeath = $death"); + if (strlen(trim($query)) > 0) { + $result = mysql_query($query,$conn) or die ("Houston, we have a problem! " . mysql_error() . ", sql:\n
    $wuery\n
    \n"); + $success = mysql_affected_rows($result); + } + } + } + mysql_close($conn); +} + +?> From 7fd44005f6cd64342c0d3a9b523582512af9f94a Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Sat, 25 Feb 2012 18:55:34 -0800 Subject: [PATCH 002/123] removed the outdated .htaccess file This file is no longer used, so is no longer needed. --- admin/.htaccess | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 admin/.htaccess diff --git a/admin/.htaccess b/admin/.htaccess deleted file mode 100644 index 10eda69..0000000 --- a/admin/.htaccess +++ /dev/null @@ -1,25 +0,0 @@ -ErrorDocument 400 /Program_O/test/myError.php -ErrorDocument 401 /Program_O/test/myError.php -ErrorDocument 402 /Program_O/test/myError.php -ErrorDocument 403 /Program_O/test/myError.php -ErrorDocument 404 /Program_O/test/myError.php -ErrorDocument 405 /Program_O/test/myError.php -ErrorDocument 406 /Program_O/test/myError.php -ErrorDocument 407 /Program_O/test/myError.php -ErrorDocument 408 /Program_O/test/myError.php -ErrorDocument 409 /Program_O/test/myError.php -ErrorDocument 410 /Program_O/test/myError.php -ErrorDocument 411 /Program_O/test/myError.php -ErrorDocument 412 /Program_O/test/myError.php -ErrorDocument 413 /Program_O/test/myError.php -ErrorDocument 414 /Program_O/test/myError.php -ErrorDocument 415 /Program_O/test/myError.php -ErrorDocument 416 /Program_O/test/myError.php -ErrorDocument 417 /Program_O/test/myError.php -ErrorDocument 500 /Program_O/test/myError.php -ErrorDocument 501 /Program_O/test/myError.php -ErrorDocument 502 /Program_O/test/myError.php -ErrorDocument 503 /Program_O/test/myError.php -ErrorDocument 504 /Program_O/test/myError.php -ErrorDocument 505 /Program_O/test/myError.php - From 4c0f87b6a2209b9135c9412bc9fd4c8fccf1a09a Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Sun, 26 Feb 2012 23:12:32 -0800 Subject: [PATCH 003/123] Further bug fixes Corrected issues in the GUI sample files that prevented them from operating, and removed several unnecessary files. --- admin/botpersonality.php | 3 - admin/bugs.php | 2 +- admin/clear.php | 11 +- admin/default.page.htm | 2 +- admin/members.php | 44 +- chatbot/conversation_start.php | 2 +- chatbot/core/aiml/find_aiml.php | 3 +- .../conversation/intialise_conversation.php | 14 +- config/date.php | 9 - config/global_config.php | 3 +- gui/twitter/twitlib/tmhOAuth.php | 656 ++++++++++++++++++ gui/xml/index.php | 28 +- install/install_programo.php | 1 - library/error_functions.php | 14 + 14 files changed, 740 insertions(+), 52 deletions(-) delete mode 100644 config/date.php create mode 100644 gui/twitter/twitlib/tmhOAuth.php diff --git a/admin/botpersonality.php b/admin/botpersonality.php index 682f687..2e0d488 100644 --- a/admin/botpersonality.php +++ b/admin/botpersonality.php @@ -190,11 +190,8 @@ function updateBot() { $updateSQL .= sprintf("WHEN '%s' THEN '%s' \n", $name, $value); } $updateSQL .= "END WHERE `id` IN ($changesText);"; - #die ("
    \nupdate SQL = \n$updateSQL\n

    \n"); $saveSQL = str_replace("\n", "\r\n", $updateSQL); - $x = file_put_contents('sql.txt', "$saveSQL\r\n\r\n", FILE_APPEND); $result = mysql_query($updateSQL, $dbconn) or die('You have a SQL error on line '. __LINE__ . ' of ' . __FILE__ . '. Error message is: ' . mysql_error() . ".
    SQL:
    \n$updateSQL\n
    \n
    \n"); - #die("result = $result
    \n error = |" . mysql_error($link) . "|\nSQL = $updateSQL
    \n"); if (!$result) $msg = 'Error updating bot.'; $msg = (empty($msg)) ? 'Bot personality updated.' : $msg; } diff --git a/admin/bugs.php b/admin/bugs.php index dc3c27a..ad72fff 100644 --- a/admin/bugs.php +++ b/admin/bugs.php @@ -154,7 +154,7 @@ function sendMail() { With your help, we can make Program O even better than ever!

    - Home + Home

    endThanx; } diff --git a/admin/clear.php b/admin/clear.php index 22701e3..4c334fd 100644 --- a/admin/clear.php +++ b/admin/clear.php @@ -101,7 +101,7 @@ function renderMain() { $content = <<permanent!
    This action CANNOT be undone!
    -
    +
    @@ -126,6 +126,15 @@ function renderMain() {
    + endForm; return $content; diff --git a/admin/default.page.htm b/admin/default.page.htm index 03035e7..2dd0e76 100644 --- a/admin/default.page.htm +++ b/admin/default.page.htm @@ -644,7 +644,7 @@

    Help...

    [adminList] - + diff --git a/admin/members.php b/admin/members.php index d35ad20..2703ce8 100644 --- a/admin/members.php +++ b/admin/members.php @@ -10,16 +10,17 @@ ini_set('memory_limit','128M'); ini_set('max_execution_time','0'); $myPost = print_r($_POST, true); - #$msg = "
    $myPost
    "; + #$msg = "
    $myPost

    \n"; #if (!empty($_POST)) die ("
    \n Post Vars:\n$myPost\n
    \n"); $uname = ''; $action = (isset($_POST['action'])) ? ucfirst(strtolower($_POST['action'])) : 'Add'; if (!empty($_POST)) { $msg = save($action); - $action = 'Add'; + #$action = ($action == 'editfromlist') ? 'Edit' : $action; } - $id = (isset($_POST['id'])) ? $_POST['id'] : getNextID(); + + $id = (isset($_POST['id']) and $action != 'Add') ? $_POST['id'] : getNextID(); $id = ($id <= 0) ? getNextID() : $id; if (isset($_POST['memberSelect'])) { $id = $_POST['memberSelect']; @@ -108,27 +109,42 @@ function updateDB($sql) { } function save($action) { - if (!isset($_POST['uname']) or !isset($_POST['pword']) or !isset($_POST['pwordConfirm'])) return 'You left something out!'; - $id = $_POST['id']; - $uname = $_POST['uname']; - $pword1 = $_POST['pword']; - $pword2 = $_POST['pwordConfirm']; - if ($action != 'Delete' and ($pword1 != $pword2)) return 'The passwords don\'t match!'; - $pword = md5($pword1); + global $dbn, $action; + #return 'action = ' . $action; + if (isset($_POST['memberSelect'])) { + $id = $_POST['memberSelect']; + } + else { + if (!isset($_POST['uname']) or !isset($_POST['pword']) or !isset($_POST['pwordConfirm'])) return 'You left something out!'; + $id = $_POST['id']; + $uname = $_POST['uname']; + $pword1 = $_POST['pword']; + $pword2 = $_POST['pwordConfirm']; + $pword = md5($pword1); + if ($action != 'Delete' and ($pword1 != $pword2)) return 'The passwords don\'t match!'; + } switch ($action) { case 'Add': - $sql = "insert into myprogramo (id, uname, pword, lastip, lastlogin) values (null, '$uname', '$pword','', CURRENT_TIMESTAMP);"; + $ip = $_SERVER['REMOTE_HOST']; + $sql = "insert into myprogramo (id, uname, pword, lastip, lastlogin) values (null, '$uname', '$pword','$ip', CURRENT_TIMESTAMP);"; $out = "Account for $uname successfully added!"; break; case 'Delete': - $sql = "DELETE FROM `pgov2_db1`.`myprogramo` WHERE `myprogramo`.`id` = $id LIMIT 1"; + $action = 'Add'; + $sql = "DELETE FROM `$dbn`.`myprogramo` WHERE `myprogramo`.`id` = $id LIMIT 1"; $out = "Account for $uname successfully deleted!"; break; - default: + case 'Edit': + $action = 'Add'; $sql = "update myprogramo set uname = '$uname', pword = '$pword' where id = $id;"; $out = "Account for $uname successfully updated!"; + break; + default: + $action = 'Edit'; + $sql = ''; + $out = ''; } - $x = updateDB($sql); + $x = (!empty($sql)) ? updateDB($sql) : ''; #return "action = $action
    \n SQL = $sql"; return $out; } diff --git a/chatbot/conversation_start.php b/chatbot/conversation_start.php index cae01f7..47895e9 100644 --- a/chatbot/conversation_start.php +++ b/chatbot/conversation_start.php @@ -131,7 +131,7 @@ return $convoArr['send_to_user']; } else { - #echo $convoArr['send_to_user']; + echo $convoArr['send_to_user']; } } else { diff --git a/chatbot/core/aiml/find_aiml.php b/chatbot/core/aiml/find_aiml.php index 30fe57b..3bd1fb1 100644 --- a/chatbot/core/aiml/find_aiml.php +++ b/chatbot/core/aiml/find_aiml.php @@ -572,8 +572,7 @@ function find_aiml_matches($convoArr){ runDebug( __FILE__, __FUNCTION__, __LINE__, "Match AIML sql: $sql",2); - $x = file_put_contents('sql.txt', $sql); - $result = db_query($sql,$con); + $result = db_query($sql,$con); if(($result)&&(mysql_num_rows($result)>0)){ runDebug( __FILE__, __FUNCTION__, __LINE__, "FOUND: '".mysql_num_rows($result)."' potential AIML matches",1); diff --git a/chatbot/core/conversation/intialise_conversation.php b/chatbot/core/conversation/intialise_conversation.php index e05c7ca..c4ce158 100644 --- a/chatbot/core/conversation/intialise_conversation.php +++ b/chatbot/core/conversation/intialise_conversation.php @@ -13,7 +13,7 @@ /** * function intialise_convoArray() * A function to intialise the conversation array - * This is the array that is built throught the conversation + * This is the array that is built throught the conversation * @param string $convo_id - unique session id * @param int $bot_id - the id of the bot * @param string $format - the return format of the response (html,json,xml) @@ -506,19 +506,17 @@ function check_set_user($convoArr) $bot_id = $convoArr['conversation']['bot_id']; $ip = $_SERVER['REMOTE_ADDR']; $sql = "select `name`, `id` from `users` where `session_id` = '$convo_id' limit 1;"; - $result = mysql_query($sql, $con); - if ($numRows = mysql_num_rows($result) == 0) { - $sqlAdd = "insert into `users` (`id`, `name`, `session_id`, `chatlines`, `ip`) "; - $sqlAdd .= "values (NULL, 'User', '$convo_id', 0, '$ip');"; - $addResult = mysql_query($sqlAdd,$con) or die("You have a SQL error in file " . __FILE__ . " at line " . __LINE__ . ". Error: " . mysql_error() . " SQL =
    $sqlAdd
    \n"); - $x = mysql_affected_rows($con); + $result = mysql_query($sql, $con) or $msg = SQL_error(mysql_errno(), __FILE__, __FUNCTION__, __LINE__); + $numRows = mysql_num_rows($result); + if ($numRows == 0) { + $user_id = intisaliseUser($convo_id); } else { $row = mysql_fetch_assoc($result); $user_id = (!empty($row['id'])) ? $row['id'] : 0; $user_name = (!empty($row['name'])) ? $row['name'] : 'User'; } - $convoArr['conversation']['user_name'] = (!empty($user_name)) ? $user_name : 'User'; + $convoArr['conversation']['user_name'] = (!empty($user_name)) ? $user_name : UNKNOWN_USER; #die("User name = $user_name
    \n"); return $convoArr; } diff --git a/config/date.php b/config/date.php deleted file mode 100644 index 297f5fc..0000000 --- a/config/date.php +++ /dev/null @@ -1,9 +0,0 @@ -\n"; -print "The formatted hour is " . strftime('%H') . "

    \n"; -$locale = setlocale(LC_ALL, '0'); -$x = setlocale(LC_TIME, $locale); -print "Locale is $locale.
    \n"; -print "The current time is " . date('h:i:s') . "
    \n"; -print "The formatted hour is " . strftime('%H') . "
    \n"; -?> \ No newline at end of file diff --git a/config/global_config.php b/config/global_config.php index 0aadc0f..e934fdb 100644 --- a/config/global_config.php +++ b/config/global_config.php @@ -35,7 +35,6 @@ define("_INC_PATH_",_BASE_DIR_.$path_separator); define("_ADMIN_PATH_",_BASE_DIR_."admin".$path_separator); - define("_GLOBAL_PATH_",_BASE_DIR_."global".$path_separator); define("_BOTCORE_PATH_",_BASE_DIR_."chatbot".$path_separator."core".$path_separator); define("_AIMLPHP_PATH_",_BASE_DIR_."chatbot".$path_separator."aiml_to_php".$path_separator); define("_LIB_PATH_",_BASE_DIR_."library".$path_separator); @@ -44,6 +43,8 @@ define("_DEBUG_PATH_",_BASE_DIR_."chatbot".$path_separator."debug".$path_separator); define("_INSTALL_PATH_",_BASE_DIR_.$path_separator."install".$path_separator); + define("UNKNOWN_USER",'Stranger'); // This is what the script uses if the user's name is unknown. + //------------------------------------------------------------------------ // server name //------------------------------------------------------------------------ diff --git a/gui/twitter/twitlib/tmhOAuth.php b/gui/twitter/twitlib/tmhOAuth.php new file mode 100644 index 0000000..4785f54 --- /dev/null +++ b/gui/twitter/twitlib/tmhOAuth.php @@ -0,0 +1,656 @@ +params = array(); + $this->headers = array(); + $this->auto_fixed_time = false; + $this->buffer = null; + + // default configuration options + $this->config = array_merge( + array( + // leave 'user_agent' blank for default, otherwise set this to + // something that clearly identifies your app + 'user_agent' => '', + + 'use_ssl' => true, + 'host' => 'api.twitter.com', + + 'consumer_key' => '', + 'consumer_secret' => '', + 'user_token' => '', + 'user_secret' => '', + 'force_nonce' => false, + 'nonce' => false, // used for checking signatures. leave as false for auto + 'force_timestamp' => false, + 'timestamp' => false, // used for checking signatures. leave as false for auto + + // oauth signing variables that are not dynamic + 'oauth_version' => '1.0', + 'oauth_signature_method' => 'HMAC-SHA1', + + // you probably don't want to change any of these curl values + 'curl_connecttimeout' => 30, + 'curl_timeout' => 10, + + // for security this should always be set to 2. + 'curl_ssl_verifyhost' => 2, + // for security this should always be set to true. + 'curl_ssl_verifypeer' => true, + + // you can get the latest cacert.pem from here http://curl.haxx.se/ca/cacert.pem + 'curl_cainfo' => dirname(__FILE__) . '/cacert.pem', + 'curl_capath' => dirname(__FILE__), + + 'curl_followlocation' => false, // whether to follow redirects or not + + // support for proxy servers + 'curl_proxy' => false, // really you don't want to use this if you are using streaming + 'curl_proxyuserpwd' => false, // format username:password for proxy, if required + 'curl_encoding' => '', // leave blank for all supported formats, else use gzip, deflate, identity + + // streaming API + 'is_streaming' => false, + 'streaming_eol' => "\r\n", + 'streaming_metrics_interval' => 60, + + // header or querystring. You should always use header! + // this is just to help me debug other developers implementations + 'as_header' => true, + 'debug' => false, + ), + $config + ); + $this->set_user_agent(); + } + + function set_user_agent() { + if (!empty($this->config['user_agent'])) + return; + + if ($this->config['curl_ssl_verifyhost'] && $this->config['curl_ssl_verifypeer']) { + $ssl = '+SSL'; + } else { + $ssl = '-SSL'; + } + + $ua = 'tmhOAuth ' . self::VERSION . $ssl . ' - //github.com/themattharris/tmhOAuth'; + $this->config['user_agent'] = $ua; + } + + /** +* Generates a random OAuth nonce. +* If 'force_nonce' is true a nonce is not generated and the value in the configuration will be retained. +* +* @param string $length how many characters the nonce should be before MD5 hashing. default 12 +* @param string $include_time whether to include time at the beginning of the nonce. default true +* @return void +*/ + private function create_nonce($length=12, $include_time=true) { + if ($this->config['force_nonce'] == false) { + $sequence = array_merge(range(0,9), range('A','Z'), range('a','z')); + $length = $length > count($sequence) ? count($sequence) : $length; + shuffle($sequence); + + $prefix = $include_time ? microtime() : ''; + $this->config['nonce'] = md5(substr($prefix . implode('', $sequence), 0, $length)); + } + } + + /** +* Generates a timestamp. +* If 'force_timestamp' is true a nonce is not generated and the value in the configuration will be retained. +* +* @return void +*/ + private function create_timestamp() { + $this->config['timestamp'] = ($this->config['force_timestamp'] == false ? time() : $this->config['timestamp']); + } + + /** +* Encodes the string or array passed in a way compatible with OAuth. +* If an array is passed each array value will will be encoded. +* +* @param mixed $data the scalar or array to encode +* @return $data encoded in a way compatible with OAuth +*/ + private function safe_encode($data) { + if (is_array($data)) { + return array_map(array($this, 'safe_encode'), $data); + } else if (is_scalar($data)) { + return str_ireplace( + array('+', '%7E'), + array(' ', '~'), + rawurlencode($data) + ); + } else { + return ''; + } + } + + /** +* Decodes the string or array from it's URL encoded form +* If an array is passed each array value will will be decoded. +* +* @param mixed $data the scalar or array to decode +* @return $data decoded from the URL encoded form +*/ + private function safe_decode($data) { + if (is_array($data)) { + return array_map(array($this, 'safe_decode'), $data); + } else if (is_scalar($data)) { + return rawurldecode($data); + } else { + return ''; + } + } + + /** +* Returns an array of the standard OAuth parameters. +* +* @return array all required OAuth parameters, safely encoded +*/ + private function get_defaults() { + $defaults = array( + 'oauth_version' => $this->config['oauth_version'], + 'oauth_nonce' => $this->config['nonce'], + 'oauth_timestamp' => $this->config['timestamp'], + 'oauth_consumer_key' => $this->config['consumer_key'], + 'oauth_signature_method' => $this->config['oauth_signature_method'], + ); + + // include the user token if it exists + if ( $this->config['user_token'] ) + $defaults['oauth_token'] = $this->config['user_token']; + + // safely encode + foreach ($defaults as $k => $v) { + $_defaults[$this->safe_encode($k)] = $this->safe_encode($v); + } + + return $_defaults; + } + + /** +* Extracts and decodes OAuth parameters from the passed string +* +* @param string $body the response body from an OAuth flow method +* @return array the response body safely decoded to an array of key => values +*/ + function extract_params($body) { + $kvs = explode('&', $body); + $decoded = array(); + foreach ($kvs as $kv) { + $kv = explode('=', $kv, 2); + $kv[0] = $this->safe_decode($kv[0]); + $kv[1] = $this->safe_decode($kv[1]); + $decoded[$kv[0]] = $kv[1]; + } + return $decoded; + } + + /** +* Prepares the HTTP method for use in the base string by converting it to +* uppercase. +* +* @param string $method an HTTP method such as GET or POST +* @return void value is stored to a class variable +* @author themattharris +*/ + private function prepare_method($method) { + $this->method = strtoupper($method); + } + + /** +* Prepares the URL for use in the base string by ripping it apart and +* reconstructing it. +* +* Ref: 3.4.1.2 +* +* @param string $url the request URL +* @return void value is stored to a class variable +* @author themattharris +*/ + private function prepare_url(http://github.com/$url) { + $parts = parse_url(http://github.com/$url); + + $port = isset($parts['port']) ? $parts['port'] : false; + $scheme = $parts['scheme']; + $host = $parts['host']; + $path = isset($parts['path']) ? $parts['path'] : false; + + $port or $port = ($scheme == 'https') ? '443' : '80'; + + if (($scheme == 'https' && $port != '443') + || ($scheme == 'http' && $port != '80')) { + $host = "$host:$port"; + } + $this->url = strtolower("$scheme://$host$path"); + } + + /** +* Prepares all parameters for the base string and request. +* Multipart parameters are ignored as they are not defined in the specification, +* all other types of parameter are encoded for compatibility with OAuth. +* +* @param array $params the parameters for the request +* @return void prepared values are stored in class variables +*/ + private function prepare_params($params) { + // do not encode multipart parameters, leave them alone + if ($this->config['multipart']) { + $this->request_params = $params; + $params = array(); + } + + // signing parameters are request parameters + OAuth default parameters + $this->signing_params = array_merge($this->get_defaults(), (array)$params); + + // Remove oauth_signature if present + // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.") + if (isset($this->signing_params['oauth_signature'])) { + unset($this->signing_params['oauth_signature']); + } + + // Parameters are sorted by name, using lexicographical byte value ordering. + // Ref: Spec: 9.1.1 (1) + uksort($this->signing_params, 'strcmp'); + + // encode. Also sort the signed parameters from the POST parameters + foreach ($this->signing_params as $k => $v) { + $k = $this->safe_encode($k); + $v = $this->safe_encode($v); + $_signing_params[$k] = $v; + $kv[] = "{$k}={$v}"; + } + + // auth params = the default oauth params which are present in our collection of signing params + $this->auth_params = array_intersect_key($this->get_defaults(), $_signing_params); + if (isset($_signing_params['oauth_callback'])) { + $this->auth_params['oauth_callback'] = $_signing_params['oauth_callback']; + unset($_signing_params['oauth_callback']); + } + + if (isset($_signing_params['oauth_verifier'])) { + $this->auth_params['oauth_verifier'] = $_signing_params['oauth_verifier']; + unset($_signing_params['oauth_verifier']); + } + + // request_params is already set if we're doing multipart, if not we need to set them now + if ( ! $this->config['multipart']) + $this->request_params = array_diff_key($_signing_params, $this->get_defaults()); + + // create the parameter part of the base string + $this->signing_params = implode('&', $kv); + } + + /** +* Prepares the OAuth signing key +* +* @return void prepared signing key is stored in a class variables +*/ + private function prepare_signing_key() { + $this->signing_key = $this->safe_encode($this->config['consumer_secret']) . '&' . $this->safe_encode($this->config['user_secret']); + } + + /** +* Prepare the base string. +* Ref: Spec: 9.1.3 ("Concatenate Request Elements") +* +* @return void prepared base string is stored in a class variables +*/ + private function prepare_base_string() { + $base = array( + $this->method, + $this->url, + $this->signing_params + ); + $this->base_string = implode('&', $this->safe_encode($base)); + } + + /** +* Prepares the Authorization header +* +* @return void prepared authorization header is stored in a class variables +*/ + private function prepare_auth_header() { + $this->headers = array(); + uksort($this->auth_params, 'strcmp'); + if (!$this->config['as_header']) : + $this->request_params = array_merge($this->request_params, $this->auth_params); + return; + endif; + + foreach ($this->auth_params as $k => $v) { + $kv[] = "{$k}=\"{$v}\""; + } + $this->auth_header = 'OAuth ' . implode(', ', $kv); + $this->headers['Authorization'] = $this->auth_header; + } + + /** +* Signs the request and adds the OAuth signature. This runs all the request +* parameter preparation methods. +* +* @param string $method the HTTP method being used. e.g. POST, GET, HEAD etc +* @param string $url the request URL without query string parameters +* @param array $params the request parameters as an array of key=value pairs +* @param string $useauth whether to use authentication when making the request. +*/ + private function sign($method, $url, $params, $useauth) { + $this->prepare_method($method); + $this->prepare_url(http://github.com/$url); + $this->prepare_params($params); + + // we don't sign anything is we're not using auth + if ($useauth) { + $this->prepare_base_string(); + $this->prepare_signing_key(); + + $this->auth_params['oauth_signature'] = $this->safe_encode( + base64_encode( + hash_hmac( + 'sha1', $this->base_string, $this->signing_key, true + ))); + + $this->prepare_auth_header(); + } + } + + /** +* Make an HTTP request using this library. This method doesn't return anything. +* Instead the response should be inspected directly. +* +* @param string $method the HTTP method being used. e.g. POST, GET, HEAD etc +* @param string $url the request URL without query string parameters +* @param array $params the request parameters as an array of key=value pairs +* @param string $useauth whether to use authentication when making the request. Default true. +* @param string $multipart whether this request contains multipart data. Default false +*/ + function request($method, $url, $params=array(), $useauth=true, $multipart=false) { + $this->config['multipart'] = $multipart; + + $this->create_nonce(); + $this->create_timestamp(); + + $this->sign($method, $url, $params, $useauth); + return $this->curlit(); + } + + /** +* Make a long poll HTTP request using this library. This method is +* different to the other request methods as it isn't supposed to disconnect +* +* Using this method expects a callback which will receive the streaming +* responses. +* +* @param string $method the HTTP method being used. e.g. POST, GET, HEAD etc +* @param string $url the request URL without query string parameters +* @param array $params the request parameters as an array of key=value pairs +* @param string $callback the callback function to stream the buffer to. +*/ + function streaming_request($method, $url, $params=array(), $callback='') { + if ( ! empty($callback) ) { + if ( ! function_exists($callback) ) { + return false; + } + $this->config['streaming_callback'] = $callback; + } + $this->metrics['start'] = time(); + $this->metrics['interval_start'] = $this->metrics['start']; + $this->metrics['tweets'] = 0; + $this->metrics['last_tweets'] = 0; + $this->metrics['bytes'] = 0; + $this->metrics['last_bytes'] = 0; + $this->config['is_streaming'] = true; + $this->request($method, $url, $params); + } + + /** +* Handles the updating of the current Streaming API metrics. +*/ + function update_metrics() { + $now = time(); + if (($this->metrics['interval_start'] + $this->config['streaming_metrics_interval']) > $now) + return false; + + $this->metrics['tps'] = round( ($this->metrics['tweets'] - $this->metrics['last_tweets']) / $this->config['streaming_metrics_interval'], 2); + $this->metrics['bps'] = round( ($this->metrics['bytes'] - $this->metrics['last_bytes']) / $this->config['streaming_metrics_interval'], 2); + + $this->metrics['last_bytes'] = $this->metrics['bytes']; + $this->metrics['last_tweets'] = $this->metrics['tweets']; + $this->metrics['interval_start'] = $now; + return $this->metrics; + } + + /** +* Utility function to create the request URL in the requested format +* +* @param string $request the API method without extension +* @param string $format the format of the response. Default json. Set to an empty string to exclude the format +* @return string the concatenation of the host, API version, API method and format +*/ + function url(http://github.com/$request,%20$format='json') { + $format = strlen($format) > 0 ? ".$format" : ''; + $proto = $this->config['use_ssl'] ? 'https:/' : 'http:/'; + + // backwards compatibility with v0.1 + if (isset($this->config['v'])) + $this->config['host'] = $this->config['host'] . '/' . $this->config['v']; + + return implode('/', array( + $proto, + $this->config['host'], + $request . $format + )); + } + + /** +* Public access to the private safe decode/encode methods +* +* @param string $text the text to transform +* @param string $mode the transformation mode. either encode or decode +* @return the string as transformed by the given mode +*/ + function transformText($text, $mode='encode') { + return $this->{"safe_$mode"}($text); + } + + /** +* Utility function to parse the returned curl headers and store them in the +* class array variable. +* +* @param object $ch curl handle +* @param string $header the response headers +* @return the string length of the header +*/ + private function curlHeader($ch, $header) { + $i = strpos($header, ':'); + if ( ! empty($i) ) { + $key = str_replace('-', '_', strtolower(substr($header, 0, $i))); + $value = trim(substr($header, $i + 2)); + $this->response['headers'][$key] = $value; + } + return strlen($header); + } + + /** +* Utility function to parse the returned curl buffer and store them until +* an EOL is found. The buffer for curl is an undefined size so we need +* to collect the content until an EOL is found. +* +* This function calls the previously defined streaming callback method. +* +* @param object $ch curl handle +* @param string $data the current curl buffer +*/ + private function curlWrite($ch, $data) { + $l = strlen($data); + if (strpos($data, $this->config['streaming_eol']) === false) { + $this->buffer .= $data; + return $l; + } + + $buffered = explode($this->config['streaming_eol'], $data); + $content = $this->buffer . $buffered[0]; + + $this->metrics['tweets']++; + $this->metrics['bytes'] += strlen($content); + + if ( ! function_exists($this->config['streaming_callback'])) + return 0; + + $metrics = $this->update_metrics(); + $stop = call_user_func( + $this->config['streaming_callback'], + $content, + strlen($content), + $metrics + ); + $this->buffer = $buffered[1]; + if ($stop) + return 0; + + return $l; + } + + /** +* Makes a curl request. Takes no parameters as all should have been prepared +* by the request method +* +* @return void response data is stored in the class variable 'response' +*/ + private function curlit() { + // method handling + switch ($this->method) { + case 'POST': + break; + default: + // GET, DELETE request so convert the parameters to a querystring + if ( ! empty($this->request_params)) { + foreach ($this->request_params as $k => $v) { + // Multipart params haven't been encoded yet. + // Not sure why you would do a multipart GET but anyway, here's the support for it + if ($this->config['multipart']) { + $params[] = $this->safe_encode($k) . '=' . $this->safe_encode($v); + } else { + $params[] = $k . '=' . $v; + } + } + $qs = implode('&', $params); + $this->url = strlen($qs) > 0 ? $this->url . '?' . $qs : $this->url; + $this->request_params = array(); + } + break; + } + + // configure curl + $c = curl_init(); + curl_setopt_array($c, array( + CURLOPT_USERAGENT => $this->config['user_agent'], + CURLOPT_CONNECTTIMEOUT => $this->config['curl_connecttimeout'], + CURLOPT_TIMEOUT => $this->config['curl_timeout'], + CURLOPT_RETURNTRANSFER => true, + CURLOPT_SSL_VERIFYPEER => $this->config['curl_ssl_verifypeer'], + CURLOPT_SSL_VERIFYHOST => $this->config['curl_ssl_verifyhost'], + + CURLOPT_FOLLOWLOCATION => $this->config['curl_followlocation'], + CURLOPT_PROXY => $this->config['curl_proxy'], + CURLOPT_ENCODING => $this->config['curl_encoding'], + CURLOPT_URL => $this->url, + // process the headers + CURLOPT_HEADERFUNCTION => array($this, 'curlHeader'), + CURLOPT_HEADER => false, + CURLINFO_HEADER_OUT => true, + )); + + if ($this->config['curl_cainfo'] !== false) + curl_setopt($c, CURLOPT_CAINFO, $this->config['curl_cainfo']); + + if ($this->config['curl_capath'] !== false) + curl_setopt($c, CURLOPT_CAPATH, $this->config['curl_capath']); + + if ($this->config['curl_proxyuserpwd'] !== false) + curl_setopt($c, CURLOPT_PROXYUSERPWD, $this->config['curl_proxyuserpwd']); + + if ($this->config['is_streaming']) { + // process the body + $this->response['content-length'] = 0; + curl_setopt($c, CURLOPT_TIMEOUT, 0); + curl_setopt($c, CURLOPT_WRITEFUNCTION, array($this, 'curlWrite')); + } + + switch ($this->method) { + case 'GET': + break; + case 'POST': + curl_setopt($c, CURLOPT_POST, true); + break; + default: + curl_setopt($c, CURLOPT_CUSTOMREQUEST, $this->method); + } + + if ( ! empty($this->request_params) ) { + // if not doing multipart we need to implode the parameters + if ( ! $this->config['multipart'] ) { + foreach ($this->request_params as $k => $v) { + $ps[] = "{$k}={$v}"; + } + $this->request_params = implode('&', $ps); + } + curl_setopt($c, CURLOPT_POSTFIELDS, $this->request_params); + } else { + // CURL will set length to -1 when there is no data, which breaks Twitter + $this->headers['Content-Type'] = ''; + $this->headers['Content-Length'] = ''; + } + + // CURL defaults to setting this to Expect: 100-Continue which Twitter rejects + $this->headers['Expect'] = ''; + + if ( ! empty($this->headers)) { + foreach ($this->headers as $k => $v) { + $headers[] = trim($k . ': ' . $v); + } + curl_setopt($c, CURLOPT_HTTPHEADER, $headers); + } + + if (isset($this->config['prevent_request']) && true == $this->config['prevent_request']) + return; + + // do it! + $response = curl_exec($c); + $code = curl_getinfo($c, CURLINFO_HTTP_CODE); + $info = curl_getinfo($c); + $error = curl_error($c); + $errno = curl_errno($c); + curl_close($c); + + // store the response + $this->response['code'] = $code; + $this->response['response'] = $response; + $this->response['info'] = $info; + $this->response['error'] = $error; + $this->response['errno'] = $errno; + return $code; + } +} \ No newline at end of file diff --git a/gui/xml/index.php b/gui/xml/index.php index da632a8..24ca1a1 100644 --- a/gui/xml/index.php +++ b/gui/xml/index.php @@ -1,15 +1,16 @@ [bot_name]: [botsay]
    endResponse; - $send = "http://".$_SERVER['HTTP_HOST']."/programov2/chatbot/conversation_start.php?say=$say&convo_id=$convo_id&bot_id=$bot_id&format=$format"; + + $thisFileURL = $_SERVER['PHP_SELF']; + $chatbotURLpath = str_replace('/gui/xml/index.php', '/chatbot',$thisFileURL); + define("CHATBOT_URL_PATH",$chatbotURLpath); + + $send = "http://".$_SERVER['HTTP_HOST']. CHATBOT_URL_PATH . "/conversation_start.php?say=$say&convo_id=$convo_id&bot_id=$bot_id&format=$format"; + $X = file_put_contents('URL.txt', "$send\r\n",FILE_APPEND); +#die(); $sXML = trim(get_response($send)); /* $response = htmlentities($sXML); diff --git a/install/install_programo.php b/install/install_programo.php index 49d5a64..c3d0369 100644 --- a/install/install_programo.php +++ b/install/install_programo.php @@ -48,7 +48,6 @@ } $PHP_SELF = $_SERVER['PHP_SELF']; # search params for start and end of sections -$config_template = file_get_contents('../config/global_config.tpl'); $replTagsArray = file('../install/config_template_tags.dat', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); $replVarsArray = array(); foreach ($replTagsArray as $value) { diff --git a/library/error_functions.php b/library/error_functions.php index a4cdb2b..6062411 100644 --- a/library/error_functions.php +++ b/library/error_functions.php @@ -176,6 +176,9 @@ function writefile_debug($array) $file = print_r($array,true); + if (DIRECTORY_SEPARATOR == '\\') { + $file = str_replace("\n", "\r\n", $file); + } if(isset($array['conversation'])) { @@ -265,5 +268,16 @@ function outputDebug($fileName, $functionName, $line, $info) { print "
    ----------------------------------------------------"; } + function SQL_Error($errNum, $file = 'unknown', $function = 'unknown', $line = 'unknown') { + $msg = "There's a problem with your Program O installation. Please run the install script to correct the problem.
    \n"; + switch ($errNum) { + case '1146': + $msg .= "The database and/or table used in the config file doesn't exist.
    \n"; + break; + default: + $msg = "Error number $errNum!
    \n"; + } + return $msg; + } ?> \ No newline at end of file From bb4a39cf050c85b814c2622ac13450211cc2621a Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Sun, 26 Feb 2012 23:23:02 -0800 Subject: [PATCH 004/123] Edited the README file Corrected some typos and incorrect information. --- README | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README b/README index e8cccb5..35eeb71 100644 --- a/README +++ b/README @@ -23,10 +23,14 @@ Configuration of Program O itself is done in /config/global_config.php Using the bot --------------------------------------------------------- After installation you can chat to Program O in one of 3 ways --HTML form (example in /gui/plain/index.php or /gui/pretty/index.php or /gui/jquery/index.php) +-HTML form (example in /gui/plain/index.php or /gui/jquery/index.php) -HTML form using CURL (return response in XML or JSON) (example in /gui/htmltoxml/index.php) -send XML using CURL (return response in XML or JSON) (example in /gui/xml/index.php) +There is also a (very experimental) demo page for using Program O with Twitter. This +page will require a bit of configuration and 'tweaking' to suit it to any sort of use. +Consider it to be nothing more than a "proof of concept". + Upgrade --------------------------------------------------------- If you are upgrading from v1 you will need to make the following changes to the db From 6918eb53eab22dbd21ce6efe80193c36736a10e5 Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Sun, 26 Feb 2012 23:56:48 -0800 Subject: [PATCH 005/123] Updated and modified the README and INSTALL files for the addon ParseBBCode Made minor typo corrections and added a warning about the BBCode input parser. --- chatbot/addons/parseBBCode/INSTALL | 8 +++++--- chatbot/addons/parseBBCode/README | 8 +++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/chatbot/addons/parseBBCode/INSTALL b/chatbot/addons/parseBBCode/INSTALL index b852a67..baa34c2 100644 --- a/chatbot/addons/parseBBCode/INSTALL +++ b/chatbot/addons/parseBBCode/INSTALL @@ -3,8 +3,8 @@ * PROGRAM O * Version: 2.0.1 * FILE: parse_BBCode/INSTALL -* AUTHOR: ELIZABETH PERREAU -* DATE: MAY 4TH 2011 +* AUTHOR: DAVE MORTON +* DATE: FEB 26th, 2012 * DETAILS: BBCode parser INSTALL instructions ***************************************/ @@ -40,6 +40,8 @@ parseInput($convoArr); - +Please note that this addon comes "standard" with the original Program O version 2.0 package, and +is already installed and set up by default. the only time you will need to install this addon is +if you have previously uninstalled or removed it. diff --git a/chatbot/addons/parseBBCode/README b/chatbot/addons/parseBBCode/README index d9afe72..398229d 100644 --- a/chatbot/addons/parseBBCode/README +++ b/chatbot/addons/parseBBCode/README @@ -32,7 +32,7 @@ one line, to the end of the file colors.dat. For example: Taupe, #483C32 HunterGreen, #355E3B -Note that any color names that normally have spaces should use Camelized Text, as seen in example #2, +Note that any color names that normally have spaces MUST use Camelized Text, as seen in example #2, above. There are 147 color entries in the default version of colors.dat - these colors are the only ones that are supported in every current web browser, so use care when adding a named color, as it may not turn out exactly the way you intend. It won't hurt anything, but some browsers may display a @@ -50,3 +50,9 @@ Links: [url=http://example.com/]my page[/url] - Displays a link with the text as "my page". [link]http://example.com/[/link] - Displays a link with the link text being the same as the web address. [link=http://example.com/]my page[/link] - Displays a link with the text as "my page". + + +Lastly: +The BBCode input parser is currently experimental in nature, and is quite likely to "break" the user's input +before it goes to the interpreter, so it's use is NOT recommended at this time. Plans to add a function to +strip the BBCode tags from the user's input are in the works, but have not been designed yet. \ No newline at end of file From 38aadb751a1290120dffbc8339479bb7fbe329c7 Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Fri, 2 Mar 2012 17:46:19 -0800 Subject: [PATCH 006/123] Improved error_functions.php Added the function save_file to the debugging routines, for specific debugging purposes. --- library/error_functions.php | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/library/error_functions.php b/library/error_functions.php index 6062411..9fae902 100644 --- a/library/error_functions.php +++ b/library/error_functions.php @@ -73,34 +73,32 @@ function runDebug($fileName, $functionName, $line, $info, $level=3) { global $debugArr,$srai_iterations,$debuglevel,$quickdebug,$writetotemp; if(empty($functionName)) $functionName = "Called outside of function"; //only log the debug info if the info level is equal to or less than the chosen level - if($level<=$debuglevel){ - + if($level<=$debuglevel) + { if($quickdebug==1) { outputDebug($fileName, $functionName, $line, $info); } - + list($usec, $sec) = explode(" ",microtime()); - - - - + //build timestamp index for the debug array - $index = date("d-m-Y H:i:s").ltrim($usec, '0'); + $index = date("d-m-Y H:i:s").ltrim($usec, '0'); //add to array $debugArr[$index]['fileName']=basename($fileName); $debugArr[$index]['functionName']=$functionName; $debugArr[$index]['line']=$line; $debugArr[$index]['info']=$info; - if($srai_iterations<1){ + if($srai_iterations<1) + { $sr_it = 0;} else { $sr_it = $srai_iterations;} $debugArr[$index]['srai_iteration']=$sr_it; - //if we are logging to file then build a log file this will be overwriten if the program completes + //if we are logging to file then build a log file. This will be overwriten if the program completes if($writetotemp==1) { writefile_debug($debugArr); @@ -280,4 +278,18 @@ function SQL_Error($errNum, $file = 'unknown', $function = 'unknown', $line = 'u return $msg; } + function save_file($file, $content, $append = false) { + if (function_exists('file_put_contents')) { + $x = file_put_contents($file, $content, $append); + } + else { + $fileMode = ($append === true) ? "a" : "w"; + $fh = fopen($file, $fileMode)or die("Can't open the file!"); + $cLen = strlen($content); + fwrite($fh, $content, $cLen); + fclose($fh); + } + return 1; + } + ?> \ No newline at end of file From 3ec162c0212d7ec88f4a713e0b07ec32032cdad9 Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Fri, 2 Mar 2012 18:04:34 -0800 Subject: [PATCH 007/123] More fixes 1.) Added 'Delete' buttons to both the spellcheck and wordcensor pages, so that entries can be removed, if needed 2.) Removed unnecessary variable values from the config file and config template file. --- admin/default.page.htm | 6 ++++-- admin/spellcheck.php | 12 +++++++----- admin/wordcensor.php | 29 ++++++++++++++++++----------- config/global_config.php | 3 --- config/global_config.tpl | 3 --- 5 files changed, 29 insertions(+), 24 deletions(-) diff --git a/admin/default.page.htm b/admin/default.page.htm index 2dd0e76..666e061 100644 --- a/admin/default.page.htm +++ b/admin/default.page.htm @@ -351,6 +351,7 @@
    +
    @@ -369,7 +370,7 @@
    - +  
    @@ -399,7 +400,8 @@
    - +   +
    diff --git a/admin/spellcheck.php b/admin/spellcheck.php index 887ca23..253fa3a 100644 --- a/admin/spellcheck.php +++ b/admin/spellcheck.php @@ -38,8 +38,10 @@ function showhide(layer_ref) { $group = (isset($_GET['group'])) ? $_GET['group'] : 1; $content = $template->getSection('SearchSpellForm'); - if (isset($_REQUEST['action'])) { - switch($_REQUEST['action']) { + $sc_action = isset($_REQUEST['action']) ? strtolower($_REQUEST['action']) : ''; + $sc_id = isset($_REQUEST['id']) ? $_REQUEST['id'] : -1; + if (!empty($sc_action)) { + switch($sc_action) { case 'search': $content .= runSpellSearch(); $content .= spellCheckForm(); @@ -48,11 +50,11 @@ function showhide(layer_ref) { $x = updateSpell(); $content .= spellCheckForm(); break; - case 'del': - $content .= (isset($_GET['id']))&&($_GET['id']!="") ? delSpell($_GET['id']) . spellCheckForm() : spellCheckForm(); + case 'delete': + $content .= ($sc_id >= 0) ? delSpell($sc_id) . spellCheckForm() : spellCheckForm(); break; case 'edit': - $content .= (isset($_GET['id']))&&($_GET['id']!="") ? editSpellForm($_GET['id']) : spellCheckForm(); + $content .= ($sc_id >= 0) ? editSpellForm($sc_id) : spellCheckForm(); break; case 'add': $x = insertSpell(); diff --git a/admin/wordcensor.php b/admin/wordcensor.php index 6b160ae..d572b3d 100644 --- a/admin/wordcensor.php +++ b/admin/wordcensor.php @@ -35,11 +35,14 @@ function showhide(layer_ref) { //--> endScript; - - $group = (isset($_GET['group'])) ? $_GET['group'] : 1; + $form = print_r($_REQUEST, true); + #die("
    \nRequest vars:\n\n$form\n
    \n"); + $group = (isset($_REQUEST['group'])) ? $_REQUEST['group'] : 1; $content = $template->getSection('SearchWordCensorForm'); - if (isset($_REQUEST['action'])) { - switch($_REQUEST['action']) { + $wc_action = isset($_REQUEST['action']) ? strtolower($_REQUEST['action']) : ''; + $wc_id = isset($_REQUEST['censor_id']) ? $_REQUEST['censor_id'] : -1; + if (!empty($wc_action)) { + switch($wc_action) { case 'search': $content .= runWordCensorSearch(); $content .= wordCensorForm(); @@ -48,11 +51,12 @@ function showhide(layer_ref) { $x = updateWordCensor(); $content .= wordCensorForm(); break; - case 'del': - $content .= (isset($_GET['id']))&&($_GET['id']!="") ? delWordCensor($_GET['id']) . wordCensorForm() : wordCensorForm(); + case 'delete': + #die("Delete called! id = $wc_id"); + $content .= ($wc_id >= 0) ? delWordCensor($wc_id) . wordCensorForm() : wordCensorForm(); break; case 'edit': - $content .= (isset($_GET['id']))&&($_GET['id']!="") ? editWordCensorForm($_GET['id']) : wordCensorForm(); + $content .= ($wc_id >= 0) ? editWordCensorForm($wc_id) : wordCensorForm(); break; case 'add': $x = insertWordCensor(); @@ -144,7 +148,7 @@ function getWordCensorWords() { $linkClass = ($linkId == $curID) ? 'selected' : 'noClass'; $word_to_censor = $row['word_to_censor']; $tmpLink = str_replace('[linkClass]', " class=\"$linkClass\"", $baseLink); - $linkHref = " href=\"./?page=wordcensor&action=edit&id=$linkId&group=$group#$linkId\" name=\"$linkId\""; + $linkHref = " href=\"./?page=wordcensor&action=edit&censor_id=$linkId&group=$group#$linkId\" name=\"$linkId\""; $tmpLink = str_replace('[linkHref]', $linkHref, $tmpLink); $tmpLink = str_replace('[linkOnclick]', '', $tmpLink); $tmpLink = str_replace('[linkTitle]', " title=\"Edit spelling replace_with for the word '$word_to_censor'\"", $tmpLink); @@ -178,7 +182,10 @@ function insertWordCensor() { $msg = '
    You must enter a spelling mistake and the replace_with.
    ' . "\n"; } else { - $sql = "INSERT INTO `wordcensor` VALUES (NULL,'$word_to_censor','$replace_with')"; +/* +INSERT INTO `morgaine`.`wordcensor` (`censor_id`, `word_to_censor`, `replace_with`, `bot_exclude`) VALUES (NULL, 'bitch', 'B***H', ''); +*/ + $sql = "INSERT INTO `wordcensor` (`censor_id`, `word_to_censor`, `replace_with`, `bot_exclude`) VALUES (NULL,'$word_to_censor','$replace_with', '')"; $result = mysql_query($sql,$dbconn) or die('You have a SQL error on line '. __LINE__ . ' of ' . __FILE__ . '. Error message is: ' . mysql_error() . ".
    \nSQL = $sql
    \n"); if($result) { @@ -236,8 +243,8 @@ function runWordCensorSearch() { $replace_with = strtoupper($row['replace_with']); $id = $row['censor_id']; $group = round(($id / 50)); - $action = " - "; + $action = " + "; $htmltbl .= " $word_to_censor $replace_with diff --git a/config/global_config.php b/config/global_config.php index e934fdb..a4eb9d6 100644 --- a/config/global_config.php +++ b/config/global_config.php @@ -72,8 +72,6 @@ // Development server settings //------------------------------------------------------------------------ $time_zone_locale = "America/Los_Angeles"; // a full list can be found at http://uk.php.net/manual/en/timezones.php - $host = ""; //the localhost name - $dbh = "localhost"; # dev remote server location $dbPort = "3306"; # dev database name/prefix $dbn = "morgaine"; # dev database name/prefix @@ -156,7 +154,6 @@ // LIVE server settings //------------------------------------------------------------------------ $time_zone_locale = "America/Los_Angeles"; // a full list can be found at http://uk.php.net/manual/en/timezones.php - $host = ""; //the localhost name $dbh = "h50mysql91.secureserver.net"; $dbPort = "3306"; $dbn = "gccpchat"; diff --git a/config/global_config.tpl b/config/global_config.tpl index 6aa122a..9367646 100644 --- a/config/global_config.tpl +++ b/config/global_config.tpl @@ -71,8 +71,6 @@ // Development server settings //------------------------------------------------------------------------ $time_zone_locale = "[lsTZ]"; // a full list can be found at http://uk.php.net/manual/en/timezones.php - $host = "[localhost]"; //the localhost name - $dbh = "[local_dbh]"; # dev remote server location $dbPort = "[local_dbPort]"; # dev database name/prefix $dbn = "[local_dbn]"; # dev database name/prefix @@ -155,7 +153,6 @@ // LIVE server settings //------------------------------------------------------------------------ $time_zone_locale = "[rsTZ]"; // a full list can be found at http://uk.php.net/manual/en/timezones.php - $host = "[remotehost]"; //the localhost name $dbh = "[remote_dbh]"; $dbPort = "[remote_dbPort]"; $dbn = "[remote_dbn]"; From d889156d0acd65f2c511105f9cbfb088aab4567a Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Sun, 4 Mar 2012 10:41:18 -0800 Subject: [PATCH 008/123] RSS Feed fixed Altered the RSS reader to use cURL, and added a Support RSS page, too. --- admin/index.php | 38 ++++++++++++++++++++++++++--- admin/main.php | 52 ---------------------------------------- admin/support.php | 47 ++++++++++++++++++++++++++++++++++++ config/global_config.php | 8 ++++--- 4 files changed, 87 insertions(+), 58 deletions(-) create mode 100644 admin/support.php diff --git a/admin/index.php b/admin/index.php index 26aa167..383fa79 100644 --- a/admin/index.php +++ b/admin/index.php @@ -266,7 +266,7 @@ function makeTopLinks() { ), array( '[linkClass]' => ' class="[curClass]"', - '[linkHref]' => ' href="http://www.program-o.com/"', + '[linkHref]' => ' href="'.NEWS_URL.'"', '[linkOnclick]' => '', '[linkAlt]' => ' alt="Get the latest news from the Program O website"', '[linkTitle]' => ' title="Get the latest news from the Program O website"', @@ -298,7 +298,7 @@ function makeTopLinks() { ), array( '[linkClass]' => ' class="[curClass]"', - '[linkHref]' => ' href="'.SUP_URL.'"', + '[linkHref]' => ' href="http://github.com/?page=support"', '[linkOnclick]' => '', '[linkAlt]' => ' alt="Get support for Program O"', '[linkTitle]' => ' title="Get support for Program O"', @@ -403,7 +403,7 @@ function makeLeftLinks() { '[linkOnclick]' => '', '[linkAlt]' => ' alt="Run a demo version of your bot"', '[linkTitle]' => ' title="Run a demo version of your bot"', - '[linkLabel]' => 'Demo Chat' + '[linkLabel]' => 'Test Your Bot' ), array( '[linkClass]' => '', @@ -433,5 +433,37 @@ function makeLeftLinks() { return $out; } + function getRSS($feed = 'RSS') { + global $template; + switch ($feed) { + case 'support': + $feedURL = SUP_URL; + break; + default: + $feedURL = RSS_URL; + } + $out = ''; + if (function_exists('curl_init')) { + $ch = curl_init($feedURL); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HEADER, 0); + $data = curl_exec($ch); + curl_close($ch); + $rss = new SimpleXmlElement($data, LIBXML_NOCDATA); + if($rss) { + $items = $rss->channel->item; + foreach ($items as $item) { + $title = $item->title; + $link = $item->link; + $published_on = $item->pubDate; + $description = $item->description; + $out .= "

    $title

    \n"; + $out .= "

    $description

    "; + } + } + } + else $out = 'RSS Feed not available'; + return $out; + } ?> diff --git a/admin/main.php b/admin/main.php index 70555d8..ce2597a 100644 --- a/admin/main.php +++ b/admin/main.php @@ -43,58 +43,6 @@
    [rssOutput]
    endMain; - $mainContent = str_replace('[rssOutput]', getRSS(), $mainContent); - function getRSS() { - global $template; - $out = ''; - $itemTemplate = $template->getSection('RSSItemTemplate'); - $xml = @simplexml_load_file(RSS_URL); //loading the document - if ($xml) { - $title = $xml->channel->title; //gets the title of the document. - $rss = simplexml_load_file(RSS_URL); - if($rss) { - $items = $rss->channel->item; - foreach($items as $item) { - $title = $item->title; - $link = $item->link; - $published_on = $item->pubDate; - $description = $item->description; - $out .= "

    $title

    \n"; - $out .= "

    $description

    "; - } - } - } - else $out = 'RSS Feed not available'; - return $out; - } - - function getXML_section($file, $tagName, $count = 1) { - $startTagSearch = "<$tagName>"; - $endTagSearch = ""; - $atomicTagSearch = "<$tagName />"; - $alternateAtomicTagSearch = "<$tagName/>"; - $foundPositions = array(-1); - $pos = 0; - $n = 0; - while ($pos !== false) { - switch (true) { - case ($newPos = stripos($file, $startTagSearch, $pos)): - case ($newPos = stripos($file, $atomicTagSearch, $pos)): - case ($newPos = stripos($file, $alternateAtomicTagSearch, $pos)): - $foundPositions[] = $newPos; - break; - default: - $pos = $newPos; - } - $n++; - if ($n > 50) break; - } - $startPos = $foundPositions[$count]; - $endPos = stripos($file, $endTagSearch, $startPos); - $len = $endPos - $startPos; - return substr($file, $startPos, $len); - } - ?> \ No newline at end of file diff --git a/admin/support.php b/admin/support.php new file mode 100644 index 0000000..f8a5a13 --- /dev/null +++ b/admin/support.php @@ -0,0 +1,47 @@ +getSection('NoRightNav'); + $logo = $template->getSection('Logo'); + $topNav = $template->getSection('TopNav'); + $leftNav = $template->getSection('LeftNav'); + $main = $template->getSection('Main'); + $rightNav = ''; + $footer = trim($template->getSection('Footer')); + #$lowerScripts = ''; + #$pageTitleInfo = ''; + $divDecoration = $template->getSection('DivDecoration'); + $topNavLinks = makeLinks('top', $topLinks, 12); + $navHeader = $template->getSection('NavHeader'); + $leftNavLinks = makeLinks('left', $leftLinks, 12); + $mainTitle = 'Program O Support'; + $rightNavLinks = ''; + $FooterInfo = getFooter(); + $titleSpan = $template->getSection('TitleSpan'); + $errMsgStyle = (!empty($msg)) ? "ShowError" : "HideError"; + $errMsgStyle = $template->getSection($errMsgStyle); + $mediaType = ' media="screen"'; + $upperScripts = ''; + $noLeftNav = ''; + $noTopNav = ''; + $pageTitle = 'My-Program O - Support Page'; + $headerTitle = 'Actions:'; + $forumURL = FORUM_URL; + $mainContent = << +
    + For specific support questions, please use the Program O Forums and post your question.
    + Below are the most recent forum posts: +
    +
    [rssOutput]
    + +endMain; + $mainContent = str_replace('[rssOutput]', getRSS('support'), $mainContent); + +?> \ No newline at end of file diff --git a/config/global_config.php b/config/global_config.php index a4eb9d6..ec19182 100644 --- a/config/global_config.php +++ b/config/global_config.php @@ -239,9 +239,11 @@ // Set Program O Website URLs //------------------------------------------------------------------------ - define('RSS_URL', 'http://www.program-o.com/ns/feed/rss/'); - define('FAQ_URL', 'http://www.program-o.com/ns/faq/'); - define('SUP_URL', 'http://www.program-o.com/ns/feed/Support/'); + define('RSS_URL', 'http://blog.program-o.com/feed/'); + define('FAQ_URL', 'http://www.program-o.com/faqpage/'); + define('NEWS_URL', 'http://blog.program-o.com/category/program-o-2/program-o-news/'); + define('SUP_URL', 'http://forum.program-o.com/syndication.php/'); + define('FORUM_URL', 'http://forum.program-o.com/'); define('BUGS_EMAIL', 'bugs@program-o.com'); //------------------------------------------------------------------------ From a930097940e719c214a1a616744634a0985a0417 Mon Sep 17 00:00:00 2001 From: SantasHelper Date: Thu, 8 Mar 2012 11:53:35 +0000 Subject: [PATCH 009/123] Update README --- README | 1 + 1 file changed, 1 insertion(+) diff --git a/README b/README index e8cccb5..3ebc160 100644 --- a/README +++ b/README @@ -3,6 +3,7 @@ * PROGRAM O * Version: 2.0.1 * README +* TEST ********************************************************/ Introduction From 7d6787e49c83271c98ad3bf900fdd084bd410140 Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Thu, 8 Mar 2012 09:26:19 -0800 Subject: [PATCH 010/123] Major Revision! 1.) Added a per-bot setting for altering whether or not to use stored PHP code from the database. 2.) Altered the structure of the bots table in the database to allow for the new setting noted above - see upgrade_bots_table.sql 3.) Changed the layout of the admin and install scripts to accommodate the new setting. 4.) Fixed an error when using the install script to alter the coonfig file, rather than the admin pages. 5.) Added a stylesheet for use with the default bot page. --- admin/default.page.htm | 39 +- admin/select_bots.php | 19 +- chatbot/addons/custom_tags/custom_tags.php | 4 +- chatbot/addons/parseBBCode/parseBBCode.php | 2 +- chatbot/core/aiml/find_aiml.php | 35 +- chatbot/core/aiml/make_aiml_to_php_code.php | 11 +- .../conversation/display_conversation.php | 6 +- .../conversation/intialise_conversation.php | 2 + .../core/conversation/make_conversation.php | 2 +- config/global_config.php | 605 +++++++++--------- config/global_config.tpl | 6 +- index.php | 15 +- install/config.tpl.htm | 14 +- install/config_template_tags.dat | 1 + install/install_programo.php | 20 +- install/new.sql | 1 + install/upgrade.php | 5 +- install/upgrade_bots_table.sql | 1 + library/updates.sql | 10 +- style.css | 10 + 20 files changed, 417 insertions(+), 391 deletions(-) create mode 100644 install/upgrade_bots_table.sql create mode 100644 style.css diff --git a/admin/default.page.htm b/admin/default.page.htm index 666e061..3deeb38 100644 --- a/admin/default.page.htm +++ b/admin/default.page.htm @@ -202,6 +202,17 @@ + + + + + + + + @@ -230,19 +241,6 @@ - - - - - - - -
    @@ -276,8 +274,21 @@ + + + + + + + + - + @@ -52,7 +47,7 @@

    -
    $output
    +
    $output 
    endPage; diff --git a/install/config.tpl.htm b/install/config.tpl.htm index b2277b0..c090b17 100644 --- a/install/config.tpl.htm +++ b/install/config.tpl.htm @@ -277,7 +277,7 @@ - + @@ -409,7 +409,7 @@
    NOTE:
    If you wish to use a different account for your Program O Admin user, then you will need to edit the configuration file manually. If you have any questions about this, or anything else about - Program O, please visit the forums at . + Program O, please visit the forums at http://forum.program-o.com.
    @@ -450,12 +450,20 @@ +
    + + + + +
    - +
    diff --git a/install/config_template_tags.dat b/install/config_template_tags.dat index 0b394ee..94226ce 100644 --- a/install/config_template_tags.dat +++ b/install/config_template_tags.dat @@ -7,6 +7,7 @@ [default_bot_id] [default_format] [default_pattern] +[default_use_aiml_code] [default_update_aiml_code] [default_conversation_lines] [default_remember_up_to] diff --git a/install/install_programo.php b/install/install_programo.php index c3d0369..bc246f5 100644 --- a/install/install_programo.php +++ b/install/install_programo.php @@ -152,14 +152,14 @@ function save($page) { $addScriptInfo = 0; if (isset($_SESSION['botNames'])) { $botNameList = $_SESSION['botNames']; - $nameArray = split("\n", $botNameList); + $nameArray = explode("\n", $botNameList); $flippedNameArray = array_flip($nameArray); $defaultBotID = $_SESSION['default_bot_id']; } $botArrayTemplate = '$botNames = array('; foreach ($replVarsArray as $key => $value) { if ($key == 'botNames') { - $nameList = split("\n", $value); + $nameList = explode("\n", $value); $listCount = 1; foreach ($nameList as $botName) { $botName = trim($botName); @@ -194,7 +194,7 @@ function getMain($page, $page_template) { } $numBots = $_SESSION['bot_count']; $sql_template = << Date: Sat, 10 Mar 2012 10:14:28 +0000 Subject: [PATCH 011/123] Update config/global_config.php --- config/global_config.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/config/global_config.php b/config/global_config.php index 3b03e26..32efe9b 100644 --- a/config/global_config.php +++ b/config/global_config.php @@ -73,15 +73,15 @@ $time_zone_locale = "America/Los_Angeles"; // a full list can be found at http://uk.php.net/manual/en/timezones.php $dbh = "localhost"; # dev remote server location $dbPort = "3306"; # dev database name/prefix - $dbn = "morgaine"; # dev database name/prefix - $dbu = "Dave"; # dev database username - $dbp = "411693055"; # dev database password + $dbn = ""; # dev database name/prefix + $dbu = ""; # dev database username + $dbp = ""; # dev database password //these are the admin DB settings in case you want make the admin a different db user with more privs $adm_dbh = "localhost"; - $adm_dbn = "morgaine"; - $adm_dbu = "Dave"; - $adm_dbp = "411693055"; + $adm_dbn = ""; + $adm_dbu = ""; + $adm_dbp = ""; //------------------------------------------------------------------------ // Default bot settings @@ -100,7 +100,7 @@ $default_update_aiml_code = ''; $default_conversation_lines = 5; $default_remember_up_to = 10; - $default_debugemail = "dmorton@geekcavecreations.com"; + $default_debugemail = ""; /* * $default_debugshow - The level of messages to show the user * 0=none, @@ -164,8 +164,8 @@ //these are the admin DB settings in case you want make the admin a different db user with more privs $adm_dbh = "Required"; $adm_dbn = "Required"; - $adm_dbu = "Dave"; - $adm_dbp = "411693055"; + $adm_dbu = "Required"; + $adm_dbp = "Required"; //------------------------------------------------------------------------ // Default bot settings @@ -234,7 +234,7 @@ //------------------------------------------------------------------------ // Set Misc Data //------------------------------------------------------------------------ - $botmaster_name = "Dave Morton"; + $botmaster_name = "Your Name"; //------------------------------------------------------------------------ // Set Program O Website URLs From 689f7c744ff812a0614a3612f12c4ade3a68fb42 Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Sat, 10 Mar 2012 07:21:17 -0800 Subject: [PATCH 012/123] Update config/global_config.php --- config/global_config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/global_config.php b/config/global_config.php index 32efe9b..2758086 100644 --- a/config/global_config.php +++ b/config/global_config.php @@ -299,5 +299,5 @@ // Set Script Installation as completed //------------------------------------------------------------------------ - define('SCRIPT_INSTALLED', true); + #define('SCRIPT_INSTALLED', true); ?> \ No newline at end of file From 4c5e5071295a1a61257b92543548b6ecaa6f74d0 Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Sat, 10 Mar 2012 08:09:11 -0800 Subject: [PATCH 013/123] Update config/global_config.php --- config/global_config.php | 90 ++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/config/global_config.php b/config/global_config.php index 2758086..2352eff 100644 --- a/config/global_config.php +++ b/config/global_config.php @@ -49,7 +49,7 @@ //------------------------------------------------------------------------ $server = strtolower($_SERVER['HTTP_HOST']); //leave this to auto detect $dev_host = "localhost"; //the name of your dev server - $alternate_local_server_name = "programo.local"; // Use if you test on a local network - the network name of the server computer. + $alternate_local_server_name = ""; // Use if you test on a local network - the network name of the server computer. //------------------------------------------------------------------------ // parent bot // the parent bot is used to find aiml matches if no match is found for the current bot @@ -65,17 +65,17 @@ // And the database settings //------------------------------------------------------------------------ - if($server==$dev_host or ((!empty($alternate_local_server_name) and $server == $alternate_local_server_name))) + if($server==$dev_host or ((!empty($alternate_local_server_name) and $server == $alternate_local_server_name))) { //------------------------------------------------------------------------ // Development server settings - //------------------------------------------------------------------------ + //------------------------------------------------------------------------ $time_zone_locale = "America/Los_Angeles"; // a full list can be found at http://uk.php.net/manual/en/timezones.php - $dbh = "localhost"; # dev remote server location - $dbPort = "3306"; # dev database name/prefix - $dbn = ""; # dev database name/prefix - $dbu = ""; # dev database username - $dbp = ""; # dev database password + $dbh = "localhost"; # dev remote server location + $dbPort = "3306"; # dev database name/prefix + $dbn = ""; # dev database name/prefix + $dbu = ""; # dev database username + $dbp = ""; # dev database password //these are the admin DB settings in case you want make the admin a different db user with more privs $adm_dbh = "localhost"; @@ -85,7 +85,7 @@ //------------------------------------------------------------------------ // Default bot settings - //------------------------------------------------------------------------ + //------------------------------------------------------------------------ //Used to populate the stack when first initialized $default_stack_value = "om"; @@ -101,22 +101,22 @@ $default_conversation_lines = 5; $default_remember_up_to = 10; $default_debugemail = ""; - /* - * $default_debugshow - The level of messages to show the user - * 0=none, - * 1=error+general, - * 2=error+general+sql, - * 3=everything - */ + /* +* $default_debugshow - The level of messages to show the user +* 0=none, +* 1=error+general, +* 2=error+general+sql, +* 3=everything +*/ $default_debugshow = 0; - /* - * $default_debugmode - How to show the debug data - * 0 = source code view - show debugging in source code - * 1 = file log - log debugging to a file - * 2 = page view - display debugging on the webpage - * 3 = email each conversation line (not recommended) - */ + /* +* $default_debugmode - How to show the debug data +* 0 = source code view - show debugging in source code +* 1 = file log - log debugging to a file +* 2 = page view - display debugging on the webpage +* 3 = email each conversation line (not recommended) +*/ $default_debugmode = 1; $default_save_state = "session"; $error_response = "Internal error detected. Please inform my botmaster."; @@ -133,7 +133,7 @@ $debuglevel = $default_debugshow; //for quick debug to override the bot config debug options - //0 - Do not show anything + //0 - Do not show anything //1 - will print out to screen immediately $quickdebug = 0; @@ -146,13 +146,13 @@ //debug folders where txt files are stored $debugfolder = _DEBUG_PATH_; - $debugfile = $debugfolder.$default_convo_id.".txt"; + $debugfile = $debugfolder.$default_convo_id.".txt"; } else { //------------------------------------------------------------------------ // LIVE server settings - //------------------------------------------------------------------------ + //------------------------------------------------------------------------ $time_zone_locale = "America/Los_Angeles"; // a full list can be found at http://uk.php.net/manual/en/timezones.php $dbh = "Required"; $dbPort = "3306"; @@ -169,7 +169,7 @@ //------------------------------------------------------------------------ // Default bot settings - //------------------------------------------------------------------------ + //------------------------------------------------------------------------ //Used to populate the stack when first initialized $default_stack_value = "om"; @@ -184,23 +184,23 @@ $default_update_aiml_code = ''; $default_conversation_lines = 5; $default_remember_up_to = 10; - $default_debugemail = "[debugemail]"; - /* - * $default_debugshow - The level of messages to show the user - * 0=none, - * 1=error+general, - * 2=error+general+sql, - * 3=everything - */ + $default_debugemail = ""; + /* +* $default_debugshow - The level of messages to show the user +* 0=none, +* 1=error+general, +* 2=error+general+sql, +* 3=everything +*/ $default_debugshow = 0; - /* - * $default_debugmode - How to show the debug data - * 0 = source code view - show debugging in source code - * 1 = file log - log debugging to a file - * 2 = page view - display debugging on the webpage - * 3 = email each conversation line (not recommended) - */ + /* +* $default_debugmode - How to show the debug data +* 0 = source code view - show debugging in source code +* 1 = file log - log debugging to a file +* 2 = page view - display debugging on the webpage +* 3 = email each conversation line (not recommended) +*/ $default_debugmode = 1; $default_save_state = "[save_state]"; $error_response = "Internal error detected. Please inform my botmaster."; @@ -214,10 +214,10 @@ //initially set here but overwriten by bot configuration in the admin panel - $debuglevel = $default_debugshow; + $debuglevel = $default_debugshow; //for quick debug to override the bot config debug options - //0 - Do not show anything + //0 - Do not show anything //1 - will print out to screen immediately $quickdebug = 0; @@ -228,7 +228,7 @@ //debug folders where txt files are stored $debugfolder = _DEBUG_PATH_; - $debugfile = $debugfolder.$default_convo_id.".txt"; + $debugfile = $debugfolder.$default_convo_id.".txt"; } //------------------------------------------------------------------------ From e1e60a3203c1fb96f3d6c6b4d83e86d7f713abfd Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Sat, 10 Mar 2012 11:35:44 -0800 Subject: [PATCH 014/123] Multiple bug fixes 1.) Corrected a security issue in install_programo.php 2.) Added permissions testing in install_programo.php to check to see if config.php is writable, and try to correct the problem if it isn't. 3.) Updated URLs in config.php to reflect the correct addresses. 4.) Removed the last dregs of testing data from the config file. 5.) Corrected some minor typos in the config file template that resulted in unexpected values in the installation script's form data. --- config/global_config.php | 46 ++++++++++++++++++------------------ config/global_config.tpl | 9 +++---- install/install_programo.php | 7 +++++- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/config/global_config.php b/config/global_config.php index 2352eff..b24baea 100644 --- a/config/global_config.php +++ b/config/global_config.php @@ -70,28 +70,28 @@ //------------------------------------------------------------------------ // Development server settings //------------------------------------------------------------------------ - $time_zone_locale = "America/Los_Angeles"; // a full list can be found at http://uk.php.net/manual/en/timezones.php + $time_zone_locale = ""; // a full list can be found at http://uk.php.net/manual/en/timezones.php $dbh = "localhost"; # dev remote server location $dbPort = "3306"; # dev database name/prefix $dbn = ""; # dev database name/prefix $dbu = ""; # dev database username $dbp = ""; # dev database password - + //these are the admin DB settings in case you want make the admin a different db user with more privs $adm_dbh = "localhost"; $adm_dbn = ""; $adm_dbu = ""; $adm_dbp = ""; - + //------------------------------------------------------------------------ // Default bot settings //------------------------------------------------------------------------ - + //Used to populate the stack when first initialized $default_stack_value = "om"; //Default conversation id will be set to current session $default_convo_id = session_id(); - + //default bot config - this is the default bot most of this will be overwriten by the bot configuration in the db $default_bot_id = 1; $default_format = "html"; @@ -109,7 +109,7 @@ * 3=everything */ $default_debugshow = 0; - + /* * $default_debugmode - How to show the debug data * 0 = source code view - show debugging in source code @@ -131,12 +131,12 @@ //initially set here but overwriten by bot configuration in the admin panel $debuglevel = $default_debugshow; - + //for quick debug to override the bot config debug options //0 - Do not show anything //1 - will print out to screen immediately $quickdebug = 0; - + //for quick debug //1 = will write debug data to file regardless of the bot config choice //it will write it as soon as it becomes available but this this will be finally @@ -166,16 +166,16 @@ $adm_dbn = "Required"; $adm_dbu = "Required"; $adm_dbp = "Required"; - + //------------------------------------------------------------------------ // Default bot settings //------------------------------------------------------------------------ - + //Used to populate the stack when first initialized $default_stack_value = "om"; //Default conversation id will be set to current session $default_convo_id = session_id(); - + //default bot config - this is the default bot most of this will be overwriten by the bot configuration in the db $default_bot_id = 1; $default_format = "html"; @@ -193,7 +193,7 @@ * 3=everything */ $default_debugshow = 0; - + /* * $default_debugmode - How to show the debug data * 0 = source code view - show debugging in source code @@ -202,30 +202,30 @@ * 3 = email each conversation line (not recommended) */ $default_debugmode = 1; - $default_save_state = "[save_state]"; + $default_save_state = "session"; $error_response = "Internal error detected. Please inform my botmaster."; - + //------------------------------------------------------------------------ // Default debug data //------------------------------------------------------------------------ - + // Turn off all error reporting error_reporting(0); - - + + //initially set here but overwriten by bot configuration in the admin panel $debuglevel = $default_debugshow; - + //for quick debug to override the bot config debug options //0 - Do not show anything //1 - will print out to screen immediately $quickdebug = 0; - + //for quick debug //1 = will write debug data to file regardless of the bot config choice //it will write it as soon as it becomes available but this this will be finally overwriten once if and when the conversation turn is complete $writetotemp = 1; - + //debug folders where txt files are stored $debugfolder = _DEBUG_PATH_; $debugfile = $debugfolder.$default_convo_id.".txt"; @@ -240,9 +240,10 @@ // Set Program O Website URLs //------------------------------------------------------------------------ - define('RSS_URL', 'http://www.program-o.com/ns/feed/rss/'); define('FAQ_URL', 'http://www.program-o.com/ns/faq/'); - define('SUP_URL', 'http://www.program-o.com/ns/feed/Support/'); + define('NEWS_URL', 'http://www.program-o.com/ns/feed/news/'); #This needs to be altered to reflect the correct URL + define('RSS_URL', 'http://blog.program-o.com/feed/'); + define('SUP_URL', 'http://forum.program-o.com/syndication.php'); define('BUGS_EMAIL', 'bugs@program-o.com'); //------------------------------------------------------------------------ @@ -299,5 +300,4 @@ // Set Script Installation as completed //------------------------------------------------------------------------ - #define('SCRIPT_INSTALLED', true); ?> \ No newline at end of file diff --git a/config/global_config.tpl b/config/global_config.tpl index abb6608..e714c40 100644 --- a/config/global_config.tpl +++ b/config/global_config.tpl @@ -184,7 +184,7 @@ $default_update_aiml_code = '[default_update_aiml_code]'; $default_conversation_lines = [default_conversation_lines]; $default_remember_up_to = [default_remember_up_to]; - $default_debugemail = "[debugemail]"; + $default_debugemail = "[default_debugemail]"; /* * $default_debugshow - The level of messages to show the user * 0=none, @@ -202,7 +202,7 @@ * 3 = email each conversation line (not recommended) */ $default_debugmode = [default_debugmode]; - $default_save_state = "[save_state]"; + $default_save_state = "[default_save_state]"; $error_response = "[error_response]"; //------------------------------------------------------------------------ @@ -240,9 +240,10 @@ // Set Program O Website URLs //------------------------------------------------------------------------ - define('RSS_URL', 'http://www.program-o.com/ns/feed/rss/'); define('FAQ_URL', 'http://www.program-o.com/ns/faq/'); - define('SUP_URL', 'http://www.program-o.com/ns/feed/Support/'); + define('NEWS_URL', 'http://www.program-o.com/ns/feed/news/'); #This needs to be altered to reflect the correct URL + define('RSS_URL', 'http://blog.program-o.com/feed/'); + define('SUP_URL', 'http://forum.program-o.com/syndication.php'); define('BUGS_EMAIL', 'bugs@program-o.com'); //------------------------------------------------------------------------ diff --git a/install/install_programo.php b/install/install_programo.php index bc246f5..478008e 100644 --- a/install/install_programo.php +++ b/install/install_programo.php @@ -13,6 +13,11 @@ session_name('PGO_install'); session_start(); $_SESSION['errorMessage'] = (!empty($_SESSION['errorMessage'])) ? $_SESSION['errorMessage'] : ''; +if (!is_writable('../config/global_config.php')) { + // Read and write for owner, read for everybody else + chmod('../config/global_config.php', 0777); + if (!is_writable('../config/global_config.php')) $_SESSION['errorMessage'] .= "Please make the /config/global_config.php writeable."; +} require_once('../library/buildSelect.php'); require_once('../config/global_config.php'); define ('SECTION_START', ''); # search params for start and end of sections @@ -46,7 +51,7 @@ $remote_dbu = $dbu; $remote_dbp = $dbp; } -$PHP_SELF = $_SERVER['PHP_SELF']; # search params for start and end of sections +$PHP_SELF = $_SERVER['SCRIPT_NAME']; # search params for start and end of sections $replTagsArray = file('../install/config_template_tags.dat', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); $replVarsArray = array(); From abb9695e896c454b8e51d9bcf25bc389cfbd2cfe Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Tue, 20 Mar 2012 02:33:46 -0700 Subject: [PATCH 015/123] Security and documentation fixes Removed a potential security problem in several files and updated the README and fresh_install_README files to reflect improvements and upgrades to the system --- README | 18 ++++++++++++------ admin/botpersonality.php | 1 + config/global_config.php | 16 ++++++++-------- fresh_install_README.txt | 26 +++++++++++++++++++++++--- gui/plain/index_better_divs.php | 2 +- gui/xml/index.php | 2 +- index.php | 2 +- install/install_programo.php | 7 +++---- 8 files changed, 50 insertions(+), 24 deletions(-) diff --git a/README b/README index f0ad181..ce31ac5 100644 --- a/README +++ b/README @@ -44,11 +44,17 @@ Simply point your web browser to your freshly uploaded Program O directory to run the install script. Then follow the instructions on the page. The installer is designed to only run once. -Please note that the installer script will NOT install your -bot's AIML files. Once you complete the installation -process, simply go to your new Program O admin page -(e.g. http://www.example.com/ProgramO/admin) and log -in using the admin credentials you provided during the -install process. Then select "Upload AIML" from the + +Please note that the installer script will NOT create your +database for you, nor will it install your bot's AIML files. +The database, along with the username/password used to +access it, needs to be created in advance. See the file +fresh_install_README.txt for more information. + +After you create the database, and once you complete the +installation process, simply go to your new Program O +admin page (e.g. http://www.example.com/ProgramO/admin) +and log in using the admin credentials you provided during +the install process. Then select "Upload AIML" from the navigation link on the left and upload your AIML files. Then sit back and enjoy your new bot! \ No newline at end of file diff --git a/admin/botpersonality.php b/admin/botpersonality.php index 2e0d488..a6ad6bf 100644 --- a/admin/botpersonality.php +++ b/admin/botpersonality.php @@ -222,6 +222,7 @@ function addBotPersonality() { } } } + $skipKeys = array('bot_id', 'action', 'func', 'newEntryName', 'newEntryValue'); foreach($_POST as $key => $value) { if(!in_array($key, $skipKeys)) { diff --git a/config/global_config.php b/config/global_config.php index b24baea..268e118 100644 --- a/config/global_config.php +++ b/config/global_config.php @@ -154,18 +154,18 @@ // LIVE server settings //------------------------------------------------------------------------ $time_zone_locale = "America/Los_Angeles"; // a full list can be found at http://uk.php.net/manual/en/timezones.php - $dbh = "Required"; + $dbh = ""; $dbPort = "3306"; - $dbn = "Required"; - $dbu = "Required"; - $dbp = "Required"; + $dbn = ""; + $dbu = ""; + $dbp = ""; //these are the admin DB settings in case you want make the admin a different db user with more privs - $adm_dbh = "Required"; - $adm_dbn = "Required"; - $adm_dbu = "Required"; - $adm_dbp = "Required"; + $adm_dbh = ""; + $adm_dbn = ""; + $adm_dbu = ""; + $adm_dbp = ""; //------------------------------------------------------------------------ // Default bot settings diff --git a/fresh_install_README.txt b/fresh_install_README.txt index d60b2e8..10e2e49 100644 --- a/fresh_install_README.txt +++ b/fresh_install_README.txt @@ -4,17 +4,37 @@ Install files for Program O v2. step 1 - upload -------------------------------------------------------------------------------------------- -# Upload the files to your server. +# Upload the files to your server, to the directory of your choice. +step 2 - Create the Database +-------------------------------------------------------------------------------------------- +This step will vary, depending on whether you're installing locally or remotely, and also on +where you host your remote installation. + +For local installation, access your local MySQL database server through the tool of your choice +(e.g. the MySQL Toolkit, phpMyAdmin, etc.), create an empty database and note the name; then create +a user/password for that database with full permissions. + +For remote database creation, it's highly recommended to contact your hosting provider for details. +Often times you will need to log on to a "Dashboard" or "Control Panel" page in order to create +the remote database, and there are literally dozens of different versions, each with their own way +of doing things, so trying to outline remote database creation here would be near impossible. + +Please note that we STRONGLY suggest the use of phpMyAdmin for monitoring, maintaining and working +with your database IN ADDITION to the admin pages. The vast majority of hosting providers +offer phpMyAdmin as a part of your database package, so contact them if you're unsure about +whether you have it. For local installations of phpMyAdmin, visit http://www.phpmyadmin.net +for detailed information on installing and usint this very powerful and easy to use database +management tool. -step 2 - Run the installer +step 3 - Run the installer -------------------------------------------------------------------------------------------- All that's required is that you point any major web browser to your newly created Program O directory (e.g. http://www.example.com/ProgramO/) and the install script will do the rest. Just follow the prompts, and supply the necessary information in the forms provided. -step 3 - Upload the AIML files +step 4 - Upload the AIML files -------------------------------------------------------------------------------------------- To perform this action when the installation is complete, go to the admin page (e.g. http://www.example.com/ProgramO/admin/) and log in with the admin credentials diff --git a/gui/plain/index_better_divs.php b/gui/plain/index_better_divs.php index e109f08..18ff24d 100644 --- a/gui/plain/index_better_divs.php +++ b/gui/plain/index_better_divs.php @@ -29,7 +29,7 @@ } $output = (isset($convoArr['send_to_user'])) ? $convoArr['send_to_user'] . '
    ' : ""; -$thisScript = $_SERVER['PHP_SELF'] . '#new'; +$thisScript = $_SERVER['SCRIPT_NAME'] . '#new'; $content = << endResponse; - $thisFileURL = $_SERVER['PHP_SELF']; + $thisFileURL = $_SERVER['SCRIPT_NAME']; $chatbotURLpath = str_replace('/gui/xml/index.php', '/chatbot',$thisFileURL); define("CHATBOT_URL_PATH",$chatbotURLpath); diff --git a/index.php b/index.php index ddf6b32..de05587 100644 --- a/index.php +++ b/index.php @@ -23,7 +23,7 @@ $format = $default_format; } $output = (isset ($convoArr['send_to_user'])) ? $convoArr['send_to_user'] . '
    ' : "Hi there! Please tell me your name."; - $thisScript = $_SERVER['PHP_SELF'] . '#new'; + $thisScript = $_SERVER['SCRIPT_NAME'] . '#new'; $content = << diff --git a/install/install_programo.php b/install/install_programo.php index 478008e..5188d9b 100644 --- a/install/install_programo.php +++ b/install/install_programo.php @@ -22,7 +22,7 @@ require_once('../config/global_config.php'); define ('SECTION_START', ''); # search params for start and end of sections define ('SECTION_END', ''); # search params for start and end of sections -define ('PHP_SELF', $_SERVER['PHP_SELF']); # search params for start and end of sections +define ('PHP_SELF', $_SERVER['SCRIPT_NAME']); # search params for start and end of sections ini_set("display_errors", 1); ini_set("log_errors",true); #ini_set("error_log","../logs/error.log"); @@ -51,7 +51,6 @@ $remote_dbu = $dbu; $remote_dbp = $dbp; } -$PHP_SELF = $_SERVER['SCRIPT_NAME']; # search params for start and end of sections $replTagsArray = file('../install/config_template_tags.dat', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); $replVarsArray = array(); @@ -97,9 +96,9 @@ $content = str_replace('[errorMessage]', $_SESSION['errorMessage'], $content); foreach ($replVarsArray as $search => $replace) { $replace = trim($replace); - if (!isset($$replace)) $$replace = 'crap'; + if (!isset($$replace)) $$replace = ''; $repl1 = $$replace; - if (isset($$replace) and $$replace != 'crap') { + if (isset($$replace) and $$replace != '') { $content = str_replace($search, $$replace, $content); } } From 7eaf9b66baae4d2b0652d67254333bf37779cf26 Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Sat, 28 Apr 2012 00:25:20 -0700 Subject: [PATCH 016/123] Major bug fix Located and corrected the issue where certain data was not being written correctly into the config file. Signed-off-by: Dave Morton --- install/install_programo.php | 126 +++++++++++++++++++++-------------- 1 file changed, 75 insertions(+), 51 deletions(-) diff --git a/install/install_programo.php b/install/install_programo.php index 5188d9b..26582f3 100644 --- a/install/install_programo.php +++ b/install/install_programo.php @@ -1,7 +1,7 @@ Help Page for more information.'; } require_once('../library/buildSelect.php'); require_once('../config/global_config.php'); + define ('SECTION_START', ''); # search params for start and end of sections define ('SECTION_END', ''); # search params for start and end of sections define ('PHP_SELF', $_SERVER['SCRIPT_NAME']); # search params for start and end of sections -ini_set("display_errors", 1); -ini_set("log_errors",true); -#ini_set("error_log","../logs/error.log"); -ini_set("error_log","error.log"); +ini_set("display_errors", 0); +ini_set("log_errors", true); +ini_set("error_log", _INSTALL_PATH_ . "error.log"); +if (!file_exists(_INSTALL_PATH_ . "error.log")) file_put_contents(_INSTALL_PATH_ . "error.log", ''); $currentDir = getcwd(); $defaultBaseDir = rtrim(str_ireplace('admin', '', $currentDir), '\\/'); $sep = (substr($defaultBaseDir,0, 1) == '/') ? '/' : '\\'; -$localhost = $remotehost = $local_dbh = $remote_dbh = $local_dbn = $remote_dbn = $local_dbu = $remote_dbu = $local_dbp = $remote_dbp = 'Required'; $myHost = $_SERVER['SERVER_NAME']; $domain_name = $myHost; switch ($myHost) { case 'localhost': case $alternate_local_server_name: $localhost = $myHost; +/* $local_dbh = $dbh; $local_dbn = $dbn; $local_dbu = $dbu; $local_dbp = $dbp; - break; +*/ + $hostLocation = 'local'; + break; default: $remotehost = $myHost; +/* $remote_dbh = $dbh; $remote_dbn = $dbn; $remote_dbu = $dbu; $remote_dbp = $dbp; +*/ + $hostLocation = 'remote'; } +if (empty($dbu)) $dbu = $_SESSION[$hostLocation . '_dbu']; +if (empty($dbp)) $dbp = $_SESSION[$hostLocation . '_dbp']; -$replTagsArray = file('../install/config_template_tags.dat', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); +$replTagsArray = file(_INSTALL_PATH_ . 'config_template_tags.dat', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); $replVarsArray = array(); foreach ($replTagsArray as $value) { $value = trim($value); @@ -64,23 +71,18 @@ $rsTzVal = (function_exists('date_default_timezone_get')) ? date_default_timezone_get() : ''; $validPages = array(1,2,3); $page_template = file_get_contents('config.tpl.htm'); -if (isset($_POST)) { - foreach ($_POST as $key => $value) { - $_SESSION[$key] = rtrim($value); - } -} $page = (isset($_REQUEST['page'])) ? $_REQUEST['page'] : 1; if (!in_array($page, $validPages)) $page = 1; $func = (isset($_REQUEST['func'])) ? $_REQUEST['func'] : ''; if (!empty($func)) $nextPage = $func($page); $pageTemplate = 'container'; $pageNotes = ucwords("Page $page Notes"); -$content = getSection('Header', $page_template, false); -$content .= "\n"; -$content .= getSection($pageTemplate, $page_template); -$content .= getSection('Footer', $page_template); -$content .= getSection("jQuery$page", $page_template); -$notes = getSection($pageNotes, $page_template); +$content = getSection('Header', $page_template, false); +$content .= "\n"; +$content .= getSection($pageTemplate, $page_template); +$content .= getSection('Footer', $page_template); +$content .= getSection("jQuery$page", $page_template); +$notes = getSection($pageNotes, $page_template); $submitButton = getSection('SubmitButton', $page_template); $main = getMain($page, $page_template); $tmpSearchArray = array(); @@ -98,13 +100,13 @@ $replace = trim($replace); if (!isset($$replace)) $$replace = ''; $repl1 = $$replace; - if (isset($$replace) and $$replace != '') { + if (isset($$replace) and (!empty($$replace))) { $content = str_replace($search, $$replace, $content); } } if ($page == 2) $content = str_replace('[BotsTableForm]', page2form(), $content); -$content = str_replace('[cr6]', "\n ", $content); -$content = str_replace('[cr4]', "\n ", $content); +$content = str_replace('[cr6]', "\n ", $content); +$content = str_replace('[cr4]', "\n ", $content); $content = str_replace('[nextPage]', $page + 1, $content); $content = str_replace("\r\n", "\n", $content); $content = str_replace("\n\n", "\n", $content); @@ -115,7 +117,7 @@ } $content .= << + endPage; @@ -124,7 +126,7 @@ function getSection($sectionName, $page_template, $notFoundReturn = true) { $sectionStart = str_replace('[section]', $sectionName, SECTION_START); $sectionStartLen = strlen($sectionStart); - $sectionEnd = str_replace('[section]', $sectionName, SECTION_END); + $sectionEnd = str_replace('[section]', $sectionName, SECTION_END); $startPos = strpos($page_template, $sectionStart, 0); if ($startPos === false) { if ($notFoundReturn) { @@ -140,10 +142,15 @@ function getSection($sectionName, $page_template, $notFoundReturn = true) { } function save($page) { - global $replVarsArray, $quickdebug, $writetotemp; - $curSession = print_r($_SESSION, true); - #if (!checkDBContents($dbn)) fillDB(); - checkDBContents($_SESSION['local_dbn']); + global $replVarsArray, $quickdebug, $writetotemp, $hostLocation, $postVars; + $postVars = ''; + $postVars = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING); + ksort($postVars); + foreach ($postVars as $key => $value) { + $_SESSION[$key] = rtrim($value); + } + $x = file_put_contents(_INSTALL_PATH_ . 'sessionVars.txt', print_r($_SESSION, true) . "\r\n"); + checkDBContents($postVars); switch ($page) { case 1: $out = 2; @@ -173,7 +180,7 @@ function save($page) { $botArrayTemplate = rtrim($botArrayTemplate, ',') . ');'; $value = $botArrayTemplate . "\r\n"; } - $repl = (isset($_SESSION[$value])) ? $_SESSION[$value] : ''; + $repl = (isset($_SESSION[$value])) ? $_SESSION[$value] : 'missing'; $newConfigFile = str_replace($key, $repl, $newConfigFile); $newConfigFile = str_replace("\r\n", "\n", $newConfigFile); } @@ -187,7 +194,7 @@ function save($page) { } function getMain($page, $page_template) { - global $dbh, $dbu, $dbp, $dbn, $dbPort; + global $dbh, $dbu, $dbp, $dbn, $dbPort, $adm_dbu, $adm_dbp; switch ($page) { case 1: return getSection('GeneralConfig',$page_template); @@ -198,7 +205,7 @@ function getMain($page, $page_template) { } $numBots = $_SESSION['bot_count']; $sql_template = <<\n"; return ($result) ? getSection('InstallComplete', $page_template) : getSection('InstallError', $page_template); } @@ -240,37 +249,52 @@ function page2form() { $out = ''; $namesArray = explode("\n", $_SESSION['botNames']); for ($loop = 1; $loop <= $_SESSION['bot_count']; $loop++) { + $cur_use_aiml_code_checked = ($_SESSION["use_aiml_code_$loop"] == 1) ? ' checked="checked"' : ''; + $cur_update_aiml_code_checked = ($_SESSION["update_aiml_code_$loop"] == 1) ? ' checked="checked"' : ''; $curConvoLines = (!empty($_SESSION["conversation_lines_$loop"])) ? $_SESSION["conversation_lines_$loop"] : $_SESSION['default_conversation_lines']; $tmpSection = getSection('BotsTableForm', $page_template); $tmpSection = str_replace('[bot_ID]', $loop, $tmpSection); $tmpSection = str_replace('[current_bot_name]', rtrim($namesArray[$loop-1]), $tmpSection); + $tmpSection = str_replace('[cur_use_aiml_code_checked]', $cur_use_aiml_code_checked, $tmpSection); + $tmpSection = str_replace('[cur_update_aiml_code_checked]', $cur_update_aiml_code_checked, $tmpSection); $tmpSection = str_replace('[cl_value]', $curConvoLines, $tmpSection); $out .= $tmpSection; } return $out; } -function checkDBContents($dbn) { - $local_dbh = $_SESSION['local_dbh']; - $local_dbn = $_SESSION['local_dbn']; - $local_dbu = $_SESSION['local_dbu']; - $local_dbp = $_SESSION['local_dbp']; - $local_dbPort = $_SESSION['local_dbPort']; +function checkDBContents($postVars) { + global $hostLocation; + $x = file_put_contents(_INSTALL_PATH_ . $hostLocation . '_postVars.txt', print_r($postVars, true) . "\r\n"); + switch ($hostLocation) { + case 'local': + $tmpDbh = $_SESSION['local_dbh']; + $tmpDbn = $_SESSION['local_dbn']; + $tmpDbu = $_SESSION['local_dbu']; + $tmpDbp = $_SESSION['local_dbp']; + default: + $tmpDbh = $_SESSION['remote_dbh']; + $tmpDbn = $_SESSION['remote_dbn']; + $tmpDbu = $_SESSION['remote_dbu']; + $tmpDbp = $_SESSION['remote_dbp']; + } + $death = << Variables: -local_dbh: $local_dbh -local_dbu: $local_dbu -local_dbp: $local_dbp -local_dbn: $local_dbn -local_dbPort: $local_dbPort +Host Location: $hostLocation +dbh: $tmpDbh +dbu: $tmpDbu +dbp: $tmpDbp +dbn: $tmpDbn +dbPort: $tmpDbPort endDeath; - $host = ($local_dbh == 'Required') ? 'localhost' : $local_dbh; - $conn = mysql_connect($host, $local_dbu, $local_dbp) or die( "mysql_connect error:". mysql_errno() . ', ' . mysql_error() . $death); - mysql_select_db($local_dbn,$conn); + + $conn = mysql_connect($tmpDbh, $tmpDbu, $tmpDbp) or die( "mysql_connect error:". mysql_errno() . ', ' . mysql_error() . " at line " . __LINE__ . ' of file ' . __FILE__ . ' in function ' . __FUNCTION__ . '.' . $death); + mysql_select_db($tmpDbn,$conn); $sql = "show tables;"; $result = mysql_query($sql,$conn) or die ("Houston, we have a problem! " . mysql_error() . ", sql = $sql"); $out = mysql_fetch_assoc($result); From cb4aef6fe4cc908369d23317fe0ac9c5de05eff9 Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Sat, 5 May 2012 09:38:32 -0700 Subject: [PATCH 017/123] Feature Upgrade/Bug Fix The following changes were made: 1.) Added basic XML validation to the uploads page, to help ensure file compatability. 2.) Simplified the install script, allowing only a single chatbot to be added during the install process. 3.) Modified the Help page for the install script to reflect the change in the form mentioned above. 4.) Added a Troubleshooting section to the install script's Help page, just in case something goes haywire. Signed-off-by: Dave Morton --- admin/upload.php | 47 ++++ config/AIML.xsd | 540 +++++++++++++++++++++++++++++++++++++++++ install/config.tpl.htm | 77 +++--- install/help.tpl.htm | 65 +++-- 4 files changed, 667 insertions(+), 62 deletions(-) create mode 100644 config/AIML.xsd diff --git a/admin/upload.php b/admin/upload.php index ce98207..716bfda 100644 --- a/admin/upload.php +++ b/admin/upload.php @@ -9,6 +9,7 @@ // upload.php ini_set('memory_limit','128M'); ini_set('max_execution_time','0'); + libxml_use_internal_errors(true); $file = (array_key_exists('aimlfile', $_FILES)) ? processUpload() : ''; $msg = parseAIML($file); @@ -106,7 +107,33 @@ function parseAIML ($file) { $sql_start = "insert into `aiml` (`id`, `bot_id`, `aiml`, `pattern`, `thatpattern`, `template`, `topic`, `filename`, `php_code`) values\n"; $sql = $sql_start; $sql_template = "(NULL, $bot_id, '[aiml_add]', '[pattern]', '[that]', '[template]', '[topic]', '$fileName', ''),\n"; + # Validate the incoming document +/* +$xml = new DOMDocument(); +$xml->load('./lures.xml'); +*/ $aimlFile = file_get_contents($file); + $validRootTag = ''; + $curRootTagStart = stripos($aimlFile,'', $curRootTagStart) + 1; + $curRootTagLen = $curRootTagEnd - $curRootTagStart; + $curRootTag = substr($aimlFile, $curRootTagStart, $curRootTagLen); + if ($curRootTag !== $validRootTag) $aimlFile = str_ireplace($curRootTag, $validRootTag, $aimlFile); + #die("
    $aimlFile
    \n"); + $validate = new DOMDocument(); + $validate->loadXML($aimlFile); + $validate->preserveWhiteSpace = false; + $validate->formatOutput = true; + if (!$validate->schemaValidate('./aiml.xsd')) { + $msg = "Cannot parse file $file! Please note errors that follow:\n"; + + $errors = libxml_get_errors(); + foreach ($errors as $error) { + $msg .= libxml_display_error($error); + } + libxml_clear_errors(); + return $msg; + } $aiml = new SimpleXMLElement($aimlFile); $rowCount = 0; if (!empty($aiml->topic)) { @@ -226,6 +253,26 @@ function getAIML_List() { return $out; } + function libxml_display_error($error) { + $out = "
    \n"; + switch ($error->level) { + case LIBXML_ERR_WARNING: + $out .= "Warning $error->code: "; + break; + case LIBXML_ERR_ERROR: + $out .= "Error $error->code: "; + break; + case LIBXML_ERR_FATAL: + $out .= "Fatal Error $error->code: "; + break; + } + $out .= trim($error->message); + if ($error->file) { + $out .= " in $error->file"; + } + $out .= " on line $error->line\n"; + return $out; + } ?> \ No newline at end of file diff --git a/config/AIML.xsd b/config/AIML.xsd new file mode 100644 index 0000000..6e77dac --- /dev/null +++ b/config/AIML.xsd @@ -0,0 +1,540 @@ + + + + + An AIML object is represented by an aiml element in an XML document. + + Schematron validation + + + + + + + + + A topic is an optional top-level element that contains + category elements. A topic element has a required name attribute + that must contain a simple pattern expression. A topic element may + contain one or more category elements. + + + + + + + + + + + + + + + + + + + + + A category is a top-level (or second-level, if contained within a + topic) element that contains exactly one pattern and exactly one template. A + category does not have any attributes. All category elements that do not occur as + children of an explicit topic element must be assumed by the AIML interpreter to + occur as children of an "implied" topic whose name attribute has the value * (single + asterisk wildcard). + + + + + A pattern is an element whose content is a mixed pattern + expression. Exactly one pattern must appear in each category. The pattern + must always be the first child element of the category. A pattern does not + have any attributes. The contents of the pattern are appended to the full + match path that is constructed by the AIML interpreter at load + time. + + + + + The pattern-side that element is a special type of pattern + element used for context matching. The pattern-side that is optional in a + category, but if it occurs it must occur no more than once, and must + immediately follow the pattern and immediately precede the template. A + pattern-side that element contains a simple pattern expression. The contents + of the pattern-side that are appended to the full match path that is + constructed by the AIML interpreter at load time. If a category does not + contain a pattern-side that, the AIML interpreter must assume an "implied" + pattern-side that containing the pattern expression * (single asterisk + wildcard). + + + + + The majority of AIML content is within the template. The + template may contain zero or more AIML template elements mixed with + character data. + + + + + + + A mixed pattern expression is composed from one or more mixed pattern + expression constituents, separated by XML spaces (&#x20). + + + + + + + + + + A simple pattern expression is composed from one or more simple + pattern expression constituents, separated by XML spaces (#x20). + + + + + + + + + + + + + + + + + + + + + + An atomic template element in AIML indicates to an AIML interpreter + that it must return a value according to the functional meaning of the element. + Atomic elements do not have any content. + + + + + The star element indicates that an AIML interpreter should + substitute the value "captured" by a particular wildcard from the + pattern-specified portion of the match path when returning the template. The + star element has an optional integer index attribute that indicates which + wildcard to use. The minimum acceptable value for the index is "1" (the + first wildcard), and the maximum acceptable value is equal to the number of + wildcards in the pattern. + + + + + The pattern-side that element is a special type of pattern + element used for context matching. The pattern-side that is optional in a + category, but if it occurs it must occur no more than once, and must + immediately follow the pattern and immediately precede the template. A + pattern-side that element contains a simple pattern expression. The contents + of the pattern-side that are appended to the full match path that is + constructed by the AIML interpreter at load time. If a category does not + contain a pattern-side that, the AIML interpreter must assume an "implied" + pattern-side that containing the pattern expression * (single asterisk + wildcard). + + + + + The input element tells the AIML interpreter that it should + substitute the contents of a previous user input. The template-side input + has an optional index attribute that may contain either a single integer or + a comma-separated pair of integers. The minimum value for either of the + integers in the index is "1". The index tells the AIML interpreter which + previous user input should be returned (first dimension), and optionally + which "sentence" of the previous user input. The AIML interpreter should + raise an error if either of the specified index dimensions is invalid at + run-time. An unspecified index is the equivalent of "1,1". An unspecified + second dimension of the index is the equivalent of specifying a "1" for the + second dimension. + + + + + The thatstar element tells the AIML interpreter that it should + substitute the contents of a wildcard from a pattern-side that element. The + thatstar element has an optional integer index attribute that indicates + which wildcard to use; the minimum acceptable value for the index is "1" + (the first wildcard). An AIML interpreter should raise an error if the index + attribute of a star specifies a wildcard that does not exist in the that + element's pattern content. Not specifying the index is the same as + specifying an index of "1". + + + + + The topicstar element tells the AIML interpreter that it + should substitute the contents of a wildcard from the current topic (if the + topic contains any wildcards). The topicstar element has an optional integer + index attribute that indicates which wildcard to use; the minimum acceptable + value for the index is "1" (the first wildcard). Not specifying the index is + the same as specifying an index of "1". + + + + + The get element tells the AIML interpreter that it should + substitute the contents of a predicate, if that predicate has a value + defined. If the predicate has no value defined, the AIML interpreter should + substitute the empty string "". The AIML interpreter implementation may + optionally provide a mechanism that allows the AIML author to designate + default values for certain predicates. + + + + + + + + + + + + + An element called bot, which may be considered a restricted + version of get, is used to tell the AIML interpreter that it should + substitute the contents of a "bot predicate". The value of a bot predicate + is set at load-time, and cannot be changed at run-time. The AIML interpreter + may decide how to set the values of bot predicate at load-time. If the bot + predicate has no value defined, the AIML interpreter should substitute an + empty string. + + + + + + Several atomic AIML elements are "short-cuts" for combinations of + other AIML elements. + + + + + The sr element is a shortcut for: + <srai><star/></srai> The atomic sr + does not have any content. + + + + + + + + Several atomic AIML elements require the AIML interpreter to + substitute a value that is determined from the system, independently of the AIML + content. + + + + + The date element tells the AIML interpreter that it should + substitute the system local date and time. No formatting constraints on the + output are specified. + + + + + The id element tells the AIML interpreter that it should + substitute the user ID. The determination of the user ID is not specified, + since it will vary by application. A suggested default return value is + "localhost". + + + + + The size element tells the AIML interpreter that it should + substitute the number of categories currently loaded. + + + + + The version element tells the AIML interpreter that it should + substitute the version number of the AIML interpreter. + + + + + + + Text-formatting elements instruct an AIML interpreter to perform + locale-specific post-processing of the textual results of the processing of their + contents. + + + + + + + + + + + + + The condition element instructs the AIML interpreter to return + specified contents depending upon the results of matching a predicate + against a pattern. NOTE: The definition in this Schema is currently far too + permissive. AIML conditions have several forms and constraints that can't be + expressed using W3C Schema alone. For this reason, AIML objects that + validate using this Schema alone may not actually be valid AIML. + + + + A condition must be a block condition, a single-predicate + condition, or a multi-predicate condition. + + + + + + + + + + + + + + + + + The random element instructs the AIML interpreter to return + exactly one of its contained li elements randomly. + + + + + + + + + + + + AIML defines two content-capturing elements, which tell the AIML + interpreter to capture their processed contents and perform some storage operation + with them. + + + + + The set element instructs the AIML interpreter to set the + value of a predicate to the result of processing the contents of the set + element. The set element has a required attribute name, which must be a + valid AIML predicate name. If the predicate has not yet been defined, the + AIML interpreter should define it in memory. + + + + + + + + + + + + The gossip element instructs the AIML interpreter to capture + the result of processing the contents of the gossip elements and to store + these contents in a manner left up to the implementation. Most common uses + of gossip have been to store captured contents in a separate + file. + + + + + + + + + The srai element instructs the AIML interpreter to pass the + result of processing the contents of the srai element to the AIML matching + loop, as if the input had been produced by the user (this includes stepping + through the entire input normalization process). + + + + + + + + + The person element instructs the AIML interpreter to: 1. + replace words with first-person aspect in the result of processing the + contents of the person element with words with the + grammatically-corresponding third-person aspect; and 2. replace words with + third-person aspect in the result of processing the contents of the person + element with words with the grammatically-corresponding first-person aspect. + The definition of "grammatically-corresponding" is left up to the + implementation. + + + + + The person2 element instructs the AIML interpreter to: 1. + replace words with first-person aspect in the result of processing the + contents of the person2 element with words with the + grammatically-corresponding second-person aspect; and 2. replace words with + second-person aspect in the result of processing the contents of the person2 + element with words with the grammatically-corresponding first-person aspect. + The definition of "grammatically-corresponding" is left up to the + implementation. + + + + + The gender element instructs the AIML interpreter to: 1. + replace male-gendered words in the result of processing the contents of the + gender element with the grammatically-corresponding female-gendered words; + and 2. replace female-gendered words in the result of processing the + contents of the gender element with the grammatically-corresponding + male-gendered words. The definition of "grammatically-corresponding" is left + up to the implementation. Historically, implementations of gender have + exclusively dealt with pronouns, likely due to the fact that most AIML has + been written in English. However, the decision about whether to transform + gender of other words is left up to the implementation. + + + + + + + AIML defines two "covert" elements that instruct the AIML interpreter + to perform some processing on their contents, but to not return any + value. + + + + + The think element instructs the AIML interpreter to perform + all usual processing of its contents, but to not return any value, + regardless of whether the contents produce output. + + + + + + The learn element instructs the AIML interpreter to + retrieve a resource specified by a URI, and to process its AIML object + contents. + + + + + + + + + AIML defines two external processor elements, which instruct the AIML + interpreter to pass the contents of the elements to an external processor. External + processor elements may return a value, but are not required to do so. Contents of + external processor elements may consist of character data as well as AIML template + elements. If AIML template elements in the contents of an external processor element + are not enclosed as CDATA, then the AIML interpreter is required to substitute the + results of processing those elements before passing the contents to the external + processor. AIML does not require that any contents of an external processor element + are enclosed as CDATA. An AIML interpreter should assume that any unrecognized + content is character data, and simply pass it to the appropriate external processor + as-is, following any processing of AIML template elements not enclosed as CDATA. If + an external processor is not available to process the contents of an external + processor element, the AIML interpreter may return an error, but this is not + required. + + + + + The system element instructs the AIML interpreter to pass its + content (with any appropriate preprocessing) to the system command + interpreter of the local machine on which the AIML interpreter is + running. + + + + + The javascript element instructs the AIML interpreter to pass + its content (with any appropriate preprocessing, as noted above) to a + server-side JavaScript interpreter on the local machine on which the AIML + interpreter is running. The javascript element does not have any attributes. + AIML does not require that an AIML interpreter include a server-side + JavaScript interpreter, and does not require any particular behavior from + the server-side JavaScript interpreter if it exists. The javascript element + may return a value. + + + + + + + + + + + + + + + + + + + + + + + diff --git a/install/config.tpl.htm b/install/config.tpl.htm index c090b17..49b5d3b 100644 --- a/install/config.tpl.htm +++ b/install/config.tpl.htm @@ -226,30 +226,25 @@ - - + Botmaster Info: - - + + - - - - - - + + - Botmaster Info: + + @@ -258,59 +253,36 @@ - - - - Remote Database Data - - - - - - - - - - - - - - - + Remote Database Data - - + + Debugging Options: - - + + - - - - - - + + Debugging output method @@ -322,7 +294,8 @@ - [notes] + + @@ -332,6 +305,8 @@ + + @@ -344,10 +319,18 @@ + + + + + + + [notes] @@ -395,6 +378,10 @@
    # Note: This is generally the server computer's local network name.
    * Note: Place each bot name on a separate line. At least one name is required.
    +
    + For more detailed information with this page, please see the Help Page, or + click the help icon for an individual field. +
    diff --git a/install/help.tpl.htm b/install/help.tpl.htm index 734ff81..402a84e 100644 --- a/install/help.tpl.htm +++ b/install/help.tpl.htm @@ -96,7 +96,7 @@ rather than using the local DB settings. Set this to the local "network name" of your computer.
    -
    +
    Botmaster Info
    @@ -235,7 +235,7 @@ configure. - There are sample pages in the gui directory that you can + There are sample pages in the GUI directory that you can examine and use, but bear in mind that these are simply example pages, and will likely require some modification prior to use.
    @@ -283,44 +283,44 @@
    Local Database Data
    Local Database Host Name -
    +
    Default: localhost
    Required: This setting is part of the local database login credentials that allow Program O to connect to your bot's local database. Under the vast majority of circumstances, this is simply 'localhost', but there is - the occaisional rare exception. + the occasional rare exception.
    Local Database Port -
    +
    Default: 3306
    Required only in the very rare case that the database server has been configured to use a different port.
    Local Database Name -
    +
    Required: When you performed the task of creating your local database, you should have already named the DB, along with configuring username and password. The DB name from that information goes here.
    Local Database Username -
    - Required: Just as with the name of the database, above, this is part of the connection infrmation that you +
    + Required: Just as with the name of the database, above, this is part of the connection information that you should have gathered when you set up your bot's DB.
    Local Database Password -
    +
    Required: This is the password for your script to access your bot's database. There's no confirmation for this field, like there was with the admin password, earlier, so be careful to ensure it's entered correctly.
    Local Database Timezone -
    +
    This is the timezone info for your local server (the one you're currently using). This setting should be automatically set by the installation script, but you should verify it's accuracy.
    @@ -328,14 +328,14 @@
    Remote Database Data
    Remote Database Host Name -
    +
    Required: This is the host name of the remote database server, and again is part of the information that you should have already gathered when you created your remote database.
    Remote Database Port -
    +
    Default: 3306
    Just like the local database port, this setting is only required if the database server uses a non-standard port. @@ -343,28 +343,59 @@
    Remote Database Name -
    +
    Required: See Local DB name for details.
    Remote Database Username -
    +
    Required: See Local Database Username for details.
    Remote Database Password -
    +
    Required: See Local Database Password for details.
    Remote Database Timezone -
    +
    Required, but automatically generated: See Local Database Timezone for details.
    +
     
    +
    + Troubleshooting +
    +
    + File Permissions +
    +

    + If you've been directed here, then your web server doesn't allow the install script to modify file permissions automatically. + If this is the case, then modification of the permissions for the config file will have to be done manually, BEFORE + the install script can continue. Otherwise, you'll end up with a corrupted installation, and will have to start over. + This problem seems to be limited to Linux systems, since Windows handles file permissions differently. +

    +

    + To manually change file permissions on a remote server, you usually have a couple of options. If your hosting provider provides + a user control panel, you can use the File Management section to change permissions. You can also use the FTP client software + that you used to upload the file to change permissions. The methods for changing file permissions with an FTP client application + vary from program to program, so if you're unsure about how to go about the task, try reading your FTP program's Help file. + Search for either "file permissions", or "CHMOD". If you still need help, feel free to visit the + Program O Support Forum to ask for assistance. +

    +

    + The correct setting for the file permissions for the config file is 755. If you're unsure what this means, then have a look at + http://www.perlfect.com/articles/chmod.shtml for a good tutorial + about the CHMOD command, and what the various settings mean. For our purposes, that setting means that the config file can + only be written to by the file's "owner", while everyone can read and execute the file. If the file can't be properly written + to, then the script can't save the settings that you've set or changed, and your bot won't work correctly, so it's important + that the config file has it's permissions set correctly. +

    +
    +
    From d3df3c3cb19e919700c134a0cfb8696e3d7f222e Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Sat, 5 May 2012 09:42:14 -0700 Subject: [PATCH 018/123] Uploads and Downloads folders added Added placeholders for the proper directories for the upload and download pages. Signed-off-by: Dave Morton --- admin/downloads/index.html | 0 config/uploads/index.html | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 admin/downloads/index.html create mode 100644 config/uploads/index.html diff --git a/admin/downloads/index.html b/admin/downloads/index.html new file mode 100644 index 0000000..e69de29 diff --git a/config/uploads/index.html b/config/uploads/index.html new file mode 100644 index 0000000..e69de29 From fd15d4c2f9ee064504d517f0d92beace1dbe9a27 Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Fri, 11 May 2012 04:57:07 -0700 Subject: [PATCH 019/123] Filename adjustment changed a file from AIML.xsd to aiml.xsd --- config/AIML.xsd | 540 ------------------------------------------------ 1 file changed, 540 deletions(-) delete mode 100644 config/AIML.xsd diff --git a/config/AIML.xsd b/config/AIML.xsd deleted file mode 100644 index 6e77dac..0000000 --- a/config/AIML.xsd +++ /dev/null @@ -1,540 +0,0 @@ - - - - - An AIML object is represented by an aiml element in an XML document. - - Schematron validation - - - - - - - - - A topic is an optional top-level element that contains - category elements. A topic element has a required name attribute - that must contain a simple pattern expression. A topic element may - contain one or more category elements. - - - - - - - - - - - - - - - - - - - - - A category is a top-level (or second-level, if contained within a - topic) element that contains exactly one pattern and exactly one template. A - category does not have any attributes. All category elements that do not occur as - children of an explicit topic element must be assumed by the AIML interpreter to - occur as children of an "implied" topic whose name attribute has the value * (single - asterisk wildcard). - - - - - A pattern is an element whose content is a mixed pattern - expression. Exactly one pattern must appear in each category. The pattern - must always be the first child element of the category. A pattern does not - have any attributes. The contents of the pattern are appended to the full - match path that is constructed by the AIML interpreter at load - time. - - - - - The pattern-side that element is a special type of pattern - element used for context matching. The pattern-side that is optional in a - category, but if it occurs it must occur no more than once, and must - immediately follow the pattern and immediately precede the template. A - pattern-side that element contains a simple pattern expression. The contents - of the pattern-side that are appended to the full match path that is - constructed by the AIML interpreter at load time. If a category does not - contain a pattern-side that, the AIML interpreter must assume an "implied" - pattern-side that containing the pattern expression * (single asterisk - wildcard). - - - - - The majority of AIML content is within the template. The - template may contain zero or more AIML template elements mixed with - character data. - - - - - - - A mixed pattern expression is composed from one or more mixed pattern - expression constituents, separated by XML spaces (&#x20). - - - - - - - - - - A simple pattern expression is composed from one or more simple - pattern expression constituents, separated by XML spaces (#x20). - - - - - - - - - - - - - - - - - - - - - - An atomic template element in AIML indicates to an AIML interpreter - that it must return a value according to the functional meaning of the element. - Atomic elements do not have any content. - - - - - The star element indicates that an AIML interpreter should - substitute the value "captured" by a particular wildcard from the - pattern-specified portion of the match path when returning the template. The - star element has an optional integer index attribute that indicates which - wildcard to use. The minimum acceptable value for the index is "1" (the - first wildcard), and the maximum acceptable value is equal to the number of - wildcards in the pattern. - - - - - The pattern-side that element is a special type of pattern - element used for context matching. The pattern-side that is optional in a - category, but if it occurs it must occur no more than once, and must - immediately follow the pattern and immediately precede the template. A - pattern-side that element contains a simple pattern expression. The contents - of the pattern-side that are appended to the full match path that is - constructed by the AIML interpreter at load time. If a category does not - contain a pattern-side that, the AIML interpreter must assume an "implied" - pattern-side that containing the pattern expression * (single asterisk - wildcard). - - - - - The input element tells the AIML interpreter that it should - substitute the contents of a previous user input. The template-side input - has an optional index attribute that may contain either a single integer or - a comma-separated pair of integers. The minimum value for either of the - integers in the index is "1". The index tells the AIML interpreter which - previous user input should be returned (first dimension), and optionally - which "sentence" of the previous user input. The AIML interpreter should - raise an error if either of the specified index dimensions is invalid at - run-time. An unspecified index is the equivalent of "1,1". An unspecified - second dimension of the index is the equivalent of specifying a "1" for the - second dimension. - - - - - The thatstar element tells the AIML interpreter that it should - substitute the contents of a wildcard from a pattern-side that element. The - thatstar element has an optional integer index attribute that indicates - which wildcard to use; the minimum acceptable value for the index is "1" - (the first wildcard). An AIML interpreter should raise an error if the index - attribute of a star specifies a wildcard that does not exist in the that - element's pattern content. Not specifying the index is the same as - specifying an index of "1". - - - - - The topicstar element tells the AIML interpreter that it - should substitute the contents of a wildcard from the current topic (if the - topic contains any wildcards). The topicstar element has an optional integer - index attribute that indicates which wildcard to use; the minimum acceptable - value for the index is "1" (the first wildcard). Not specifying the index is - the same as specifying an index of "1". - - - - - The get element tells the AIML interpreter that it should - substitute the contents of a predicate, if that predicate has a value - defined. If the predicate has no value defined, the AIML interpreter should - substitute the empty string "". The AIML interpreter implementation may - optionally provide a mechanism that allows the AIML author to designate - default values for certain predicates. - - - - - - - - - - - - - An element called bot, which may be considered a restricted - version of get, is used to tell the AIML interpreter that it should - substitute the contents of a "bot predicate". The value of a bot predicate - is set at load-time, and cannot be changed at run-time. The AIML interpreter - may decide how to set the values of bot predicate at load-time. If the bot - predicate has no value defined, the AIML interpreter should substitute an - empty string. - - - - - - Several atomic AIML elements are "short-cuts" for combinations of - other AIML elements. - - - - - The sr element is a shortcut for: - <srai><star/></srai> The atomic sr - does not have any content. - - - - - - - - Several atomic AIML elements require the AIML interpreter to - substitute a value that is determined from the system, independently of the AIML - content. - - - - - The date element tells the AIML interpreter that it should - substitute the system local date and time. No formatting constraints on the - output are specified. - - - - - The id element tells the AIML interpreter that it should - substitute the user ID. The determination of the user ID is not specified, - since it will vary by application. A suggested default return value is - "localhost". - - - - - The size element tells the AIML interpreter that it should - substitute the number of categories currently loaded. - - - - - The version element tells the AIML interpreter that it should - substitute the version number of the AIML interpreter. - - - - - - - Text-formatting elements instruct an AIML interpreter to perform - locale-specific post-processing of the textual results of the processing of their - contents. - - - - - - - - - - - - - The condition element instructs the AIML interpreter to return - specified contents depending upon the results of matching a predicate - against a pattern. NOTE: The definition in this Schema is currently far too - permissive. AIML conditions have several forms and constraints that can't be - expressed using W3C Schema alone. For this reason, AIML objects that - validate using this Schema alone may not actually be valid AIML. - - - - A condition must be a block condition, a single-predicate - condition, or a multi-predicate condition. - - - - - - - - - - - - - - - - - The random element instructs the AIML interpreter to return - exactly one of its contained li elements randomly. - - - - - - - - - - - - AIML defines two content-capturing elements, which tell the AIML - interpreter to capture their processed contents and perform some storage operation - with them. - - - - - The set element instructs the AIML interpreter to set the - value of a predicate to the result of processing the contents of the set - element. The set element has a required attribute name, which must be a - valid AIML predicate name. If the predicate has not yet been defined, the - AIML interpreter should define it in memory. - - - - - - - - - - - - The gossip element instructs the AIML interpreter to capture - the result of processing the contents of the gossip elements and to store - these contents in a manner left up to the implementation. Most common uses - of gossip have been to store captured contents in a separate - file. - - - - - - - - - The srai element instructs the AIML interpreter to pass the - result of processing the contents of the srai element to the AIML matching - loop, as if the input had been produced by the user (this includes stepping - through the entire input normalization process). - - - - - - - - - The person element instructs the AIML interpreter to: 1. - replace words with first-person aspect in the result of processing the - contents of the person element with words with the - grammatically-corresponding third-person aspect; and 2. replace words with - third-person aspect in the result of processing the contents of the person - element with words with the grammatically-corresponding first-person aspect. - The definition of "grammatically-corresponding" is left up to the - implementation. - - - - - The person2 element instructs the AIML interpreter to: 1. - replace words with first-person aspect in the result of processing the - contents of the person2 element with words with the - grammatically-corresponding second-person aspect; and 2. replace words with - second-person aspect in the result of processing the contents of the person2 - element with words with the grammatically-corresponding first-person aspect. - The definition of "grammatically-corresponding" is left up to the - implementation. - - - - - The gender element instructs the AIML interpreter to: 1. - replace male-gendered words in the result of processing the contents of the - gender element with the grammatically-corresponding female-gendered words; - and 2. replace female-gendered words in the result of processing the - contents of the gender element with the grammatically-corresponding - male-gendered words. The definition of "grammatically-corresponding" is left - up to the implementation. Historically, implementations of gender have - exclusively dealt with pronouns, likely due to the fact that most AIML has - been written in English. However, the decision about whether to transform - gender of other words is left up to the implementation. - - - - - - - AIML defines two "covert" elements that instruct the AIML interpreter - to perform some processing on their contents, but to not return any - value. - - - - - The think element instructs the AIML interpreter to perform - all usual processing of its contents, but to not return any value, - regardless of whether the contents produce output. - - - - - - The learn element instructs the AIML interpreter to - retrieve a resource specified by a URI, and to process its AIML object - contents. - - - - - - - - - AIML defines two external processor elements, which instruct the AIML - interpreter to pass the contents of the elements to an external processor. External - processor elements may return a value, but are not required to do so. Contents of - external processor elements may consist of character data as well as AIML template - elements. If AIML template elements in the contents of an external processor element - are not enclosed as CDATA, then the AIML interpreter is required to substitute the - results of processing those elements before passing the contents to the external - processor. AIML does not require that any contents of an external processor element - are enclosed as CDATA. An AIML interpreter should assume that any unrecognized - content is character data, and simply pass it to the appropriate external processor - as-is, following any processing of AIML template elements not enclosed as CDATA. If - an external processor is not available to process the contents of an external - processor element, the AIML interpreter may return an error, but this is not - required. - - - - - The system element instructs the AIML interpreter to pass its - content (with any appropriate preprocessing) to the system command - interpreter of the local machine on which the AIML interpreter is - running. - - - - - The javascript element instructs the AIML interpreter to pass - its content (with any appropriate preprocessing, as noted above) to a - server-side JavaScript interpreter on the local machine on which the AIML - interpreter is running. The javascript element does not have any attributes. - AIML does not require that an AIML interpreter include a server-side - JavaScript interpreter, and does not require any particular behavior from - the server-side JavaScript interpreter if it exists. The javascript element - may return a value. - - - - - - - - - - - - - - - - - - - - - - - From 9751bd8140ed944e53ba612f9772effb30d3519a Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Fri, 11 May 2012 04:59:12 -0700 Subject: [PATCH 020/123] filename correction - stage 2 A continuation of the renaming process - git doesn't scan for case changes in filenames. Signed-off-by: Dave Morton --- config/aiml.xsd | 540 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 540 insertions(+) create mode 100644 config/aiml.xsd diff --git a/config/aiml.xsd b/config/aiml.xsd new file mode 100644 index 0000000..6e77dac --- /dev/null +++ b/config/aiml.xsd @@ -0,0 +1,540 @@ + + + + + An AIML object is represented by an aiml element in an XML document. + + Schematron validation + + + + + + + + + A topic is an optional top-level element that contains + category elements. A topic element has a required name attribute + that must contain a simple pattern expression. A topic element may + contain one or more category elements. + + + + + + + + + + + + + + + + + + + + + A category is a top-level (or second-level, if contained within a + topic) element that contains exactly one pattern and exactly one template. A + category does not have any attributes. All category elements that do not occur as + children of an explicit topic element must be assumed by the AIML interpreter to + occur as children of an "implied" topic whose name attribute has the value * (single + asterisk wildcard). + + + + + A pattern is an element whose content is a mixed pattern + expression. Exactly one pattern must appear in each category. The pattern + must always be the first child element of the category. A pattern does not + have any attributes. The contents of the pattern are appended to the full + match path that is constructed by the AIML interpreter at load + time. + + + + + The pattern-side that element is a special type of pattern + element used for context matching. The pattern-side that is optional in a + category, but if it occurs it must occur no more than once, and must + immediately follow the pattern and immediately precede the template. A + pattern-side that element contains a simple pattern expression. The contents + of the pattern-side that are appended to the full match path that is + constructed by the AIML interpreter at load time. If a category does not + contain a pattern-side that, the AIML interpreter must assume an "implied" + pattern-side that containing the pattern expression * (single asterisk + wildcard). + + + + + The majority of AIML content is within the template. The + template may contain zero or more AIML template elements mixed with + character data. + + + + + + + A mixed pattern expression is composed from one or more mixed pattern + expression constituents, separated by XML spaces (&#x20). + + + + + + + + + + A simple pattern expression is composed from one or more simple + pattern expression constituents, separated by XML spaces (#x20). + + + + + + + + + + + + + + + + + + + + + + An atomic template element in AIML indicates to an AIML interpreter + that it must return a value according to the functional meaning of the element. + Atomic elements do not have any content. + + + + + The star element indicates that an AIML interpreter should + substitute the value "captured" by a particular wildcard from the + pattern-specified portion of the match path when returning the template. The + star element has an optional integer index attribute that indicates which + wildcard to use. The minimum acceptable value for the index is "1" (the + first wildcard), and the maximum acceptable value is equal to the number of + wildcards in the pattern. + + + + + The pattern-side that element is a special type of pattern + element used for context matching. The pattern-side that is optional in a + category, but if it occurs it must occur no more than once, and must + immediately follow the pattern and immediately precede the template. A + pattern-side that element contains a simple pattern expression. The contents + of the pattern-side that are appended to the full match path that is + constructed by the AIML interpreter at load time. If a category does not + contain a pattern-side that, the AIML interpreter must assume an "implied" + pattern-side that containing the pattern expression * (single asterisk + wildcard). + + + + + The input element tells the AIML interpreter that it should + substitute the contents of a previous user input. The template-side input + has an optional index attribute that may contain either a single integer or + a comma-separated pair of integers. The minimum value for either of the + integers in the index is "1". The index tells the AIML interpreter which + previous user input should be returned (first dimension), and optionally + which "sentence" of the previous user input. The AIML interpreter should + raise an error if either of the specified index dimensions is invalid at + run-time. An unspecified index is the equivalent of "1,1". An unspecified + second dimension of the index is the equivalent of specifying a "1" for the + second dimension. + + + + + The thatstar element tells the AIML interpreter that it should + substitute the contents of a wildcard from a pattern-side that element. The + thatstar element has an optional integer index attribute that indicates + which wildcard to use; the minimum acceptable value for the index is "1" + (the first wildcard). An AIML interpreter should raise an error if the index + attribute of a star specifies a wildcard that does not exist in the that + element's pattern content. Not specifying the index is the same as + specifying an index of "1". + + + + + The topicstar element tells the AIML interpreter that it + should substitute the contents of a wildcard from the current topic (if the + topic contains any wildcards). The topicstar element has an optional integer + index attribute that indicates which wildcard to use; the minimum acceptable + value for the index is "1" (the first wildcard). Not specifying the index is + the same as specifying an index of "1". + + + + + The get element tells the AIML interpreter that it should + substitute the contents of a predicate, if that predicate has a value + defined. If the predicate has no value defined, the AIML interpreter should + substitute the empty string "". The AIML interpreter implementation may + optionally provide a mechanism that allows the AIML author to designate + default values for certain predicates. + + + + + + + + + + + + + An element called bot, which may be considered a restricted + version of get, is used to tell the AIML interpreter that it should + substitute the contents of a "bot predicate". The value of a bot predicate + is set at load-time, and cannot be changed at run-time. The AIML interpreter + may decide how to set the values of bot predicate at load-time. If the bot + predicate has no value defined, the AIML interpreter should substitute an + empty string. + + + + + + Several atomic AIML elements are "short-cuts" for combinations of + other AIML elements. + + + + + The sr element is a shortcut for: + <srai><star/></srai> The atomic sr + does not have any content. + + + + + + + + Several atomic AIML elements require the AIML interpreter to + substitute a value that is determined from the system, independently of the AIML + content. + + + + + The date element tells the AIML interpreter that it should + substitute the system local date and time. No formatting constraints on the + output are specified. + + + + + The id element tells the AIML interpreter that it should + substitute the user ID. The determination of the user ID is not specified, + since it will vary by application. A suggested default return value is + "localhost". + + + + + The size element tells the AIML interpreter that it should + substitute the number of categories currently loaded. + + + + + The version element tells the AIML interpreter that it should + substitute the version number of the AIML interpreter. + + + + + + + Text-formatting elements instruct an AIML interpreter to perform + locale-specific post-processing of the textual results of the processing of their + contents. + + + + + + + + + + + + + The condition element instructs the AIML interpreter to return + specified contents depending upon the results of matching a predicate + against a pattern. NOTE: The definition in this Schema is currently far too + permissive. AIML conditions have several forms and constraints that can't be + expressed using W3C Schema alone. For this reason, AIML objects that + validate using this Schema alone may not actually be valid AIML. + + + + A condition must be a block condition, a single-predicate + condition, or a multi-predicate condition. + + + + + + + + + + + + + + + + + The random element instructs the AIML interpreter to return + exactly one of its contained li elements randomly. + + + + + + + + + + + + AIML defines two content-capturing elements, which tell the AIML + interpreter to capture their processed contents and perform some storage operation + with them. + + + + + The set element instructs the AIML interpreter to set the + value of a predicate to the result of processing the contents of the set + element. The set element has a required attribute name, which must be a + valid AIML predicate name. If the predicate has not yet been defined, the + AIML interpreter should define it in memory. + + + + + + + + + + + + The gossip element instructs the AIML interpreter to capture + the result of processing the contents of the gossip elements and to store + these contents in a manner left up to the implementation. Most common uses + of gossip have been to store captured contents in a separate + file. + + + + + + + + + The srai element instructs the AIML interpreter to pass the + result of processing the contents of the srai element to the AIML matching + loop, as if the input had been produced by the user (this includes stepping + through the entire input normalization process). + + + + + + + + + The person element instructs the AIML interpreter to: 1. + replace words with first-person aspect in the result of processing the + contents of the person element with words with the + grammatically-corresponding third-person aspect; and 2. replace words with + third-person aspect in the result of processing the contents of the person + element with words with the grammatically-corresponding first-person aspect. + The definition of "grammatically-corresponding" is left up to the + implementation. + + + + + The person2 element instructs the AIML interpreter to: 1. + replace words with first-person aspect in the result of processing the + contents of the person2 element with words with the + grammatically-corresponding second-person aspect; and 2. replace words with + second-person aspect in the result of processing the contents of the person2 + element with words with the grammatically-corresponding first-person aspect. + The definition of "grammatically-corresponding" is left up to the + implementation. + + + + + The gender element instructs the AIML interpreter to: 1. + replace male-gendered words in the result of processing the contents of the + gender element with the grammatically-corresponding female-gendered words; + and 2. replace female-gendered words in the result of processing the + contents of the gender element with the grammatically-corresponding + male-gendered words. The definition of "grammatically-corresponding" is left + up to the implementation. Historically, implementations of gender have + exclusively dealt with pronouns, likely due to the fact that most AIML has + been written in English. However, the decision about whether to transform + gender of other words is left up to the implementation. + + + + + + + AIML defines two "covert" elements that instruct the AIML interpreter + to perform some processing on their contents, but to not return any + value. + + + + + The think element instructs the AIML interpreter to perform + all usual processing of its contents, but to not return any value, + regardless of whether the contents produce output. + + + + + + The learn element instructs the AIML interpreter to + retrieve a resource specified by a URI, and to process its AIML object + contents. + + + + + + + + + AIML defines two external processor elements, which instruct the AIML + interpreter to pass the contents of the elements to an external processor. External + processor elements may return a value, but are not required to do so. Contents of + external processor elements may consist of character data as well as AIML template + elements. If AIML template elements in the contents of an external processor element + are not enclosed as CDATA, then the AIML interpreter is required to substitute the + results of processing those elements before passing the contents to the external + processor. AIML does not require that any contents of an external processor element + are enclosed as CDATA. An AIML interpreter should assume that any unrecognized + content is character data, and simply pass it to the appropriate external processor + as-is, following any processing of AIML template elements not enclosed as CDATA. If + an external processor is not available to process the contents of an external + processor element, the AIML interpreter may return an error, but this is not + required. + + + + + The system element instructs the AIML interpreter to pass its + content (with any appropriate preprocessing) to the system command + interpreter of the local machine on which the AIML interpreter is + running. + + + + + The javascript element instructs the AIML interpreter to pass + its content (with any appropriate preprocessing, as noted above) to a + server-side JavaScript interpreter on the local machine on which the AIML + interpreter is running. The javascript element does not have any attributes. + AIML does not require that an AIML interpreter include a server-side + JavaScript interpreter, and does not require any particular behavior from + the server-side JavaScript interpreter if it exists. The javascript element + may return a value. + + + + + + + + + + + + + + + + + + + + + + + From 37c4a49922bda3bd7855a6730cbeeb831c4cf9ac Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Sat, 2 Jun 2012 06:03:30 -0700 Subject: [PATCH 021/123] Major bug fix Fixed the following bugs: 1.) Repaired multiple problems with the install script - everything SHOULD work now! 2.) Fixed several issues with the admin pages in the following areas: a.) AIML upload/AIML validation b.) Bot selection/editing c.) minor typos in several places 3.) Corrected problem with the script not properly storing the correct value in the users table of the DB, as described in https://github.com/Program-O/Program-O/issues/5 Signed-off-by: Dave Morton --- admin/default.page.htm | 81 +++++----- admin/error.log | 0 admin/search.php | 4 +- admin/select_bots.php | 67 ++++---- admin/spellcheck.php | 4 +- admin/teach.php | 11 +- admin/upload.php | 28 ++-- admin/wordcensor.php | 7 +- callmomtags.txt | 6 + chatbot/core/aiml/find_aiml.php | 9 +- .../conversation/display_conversation.php | 2 +- .../conversation/intialise_conversation.php | 12 +- chatbot/core/user/handle_user.php | 10 +- config/aiml.dtd | 74 +++++++++ config/global_config.php | 143 +++++++++--------- config/global_config.tpl | 12 +- gui/xml/index.php | 4 +- install/config.tpl.htm | 45 +++--- install/config_template_tags.dat | 2 + install/error.log | 0 install/install_programo.php | 98 ++++++------ library/db_functions.php | 8 +- library/error_functions.php | 6 +- 23 files changed, 379 insertions(+), 254 deletions(-) create mode 100644 admin/error.log create mode 100644 callmomtags.txt create mode 100644 config/aiml.dtd create mode 100644 install/error.log diff --git a/admin/default.page.htm b/admin/default.page.htm index 3deeb38..91a843a 100644 --- a/admin/default.page.htm +++ b/admin/default.page.htm @@ -158,13 +158,13 @@
    -
    +
    - [options] - +
    @@ -172,12 +172,12 @@ [blank]
    -
    +
    - + @@ -228,7 +228,7 @@ @@ -241,6 +241,14 @@ + + + +
    - +
    + + + +
    @@ -248,7 +256,7 @@ - + @@ -271,7 +279,7 @@ - + @@ -295,14 +303,15 @@ +
    - +
    - +
    @@ -320,7 +329,7 @@
    -
    +
    Add a Misspelled word and it's correction:
    @@ -331,25 +340,25 @@
    - +
    -
    +
    Search for a Misspelled word:
    - +
    -
    +
    Edit the entry for the Misspelled word [missspelling]:
    @@ -360,8 +369,8 @@
    - - + +
    @@ -369,7 +378,7 @@
    -
    +
    Add an improper word and it's replacement:
    @@ -388,18 +397,18 @@
    - +
    Search for an improper word:
    - +
    -
    +
    Edit the entry for the improper word [word_to_censor]:
    @@ -410,7 +419,7 @@
    - +  
    @@ -426,7 +435,7 @@
    -[blank] +[blank]
    @@ -446,7 +455,7 @@
    For help, click the icon in the title, above.
    - +
    @@ -622,10 +631,10 @@

    Help...

    [blank]

    [action] Admin Account:

    -
    +
    - +
    @@ -633,11 +642,11 @@

    Help...

    - +
    - +
    @@ -650,7 +659,7 @@

    Help...

    [blank]
    - +
            - +
    -[blank]
    +[blank]
    @@ -723,13 +732,13 @@

    Help...

    finding what you're looking for (e.g. "I % you", "%hot" or "%live%")
    - +
    -
    +
    @@ -752,8 +761,8 @@

    Help...

    - - + +
    diff --git a/admin/error.log b/admin/error.log new file mode 100644 index 0000000..e69de29 diff --git a/admin/search.php b/admin/search.php index 51ee0e2..db130fd 100644 --- a/admin/search.php +++ b/admin/search.php @@ -114,10 +114,10 @@ function runSearch() { $id = $row['id']; $action = << - + Edit this entry - + Delete this entry endLink; diff --git a/admin/select_bots.php b/admin/select_bots.php index 8d252c8..aff1ec4 100644 --- a/admin/select_bots.php +++ b/admin/select_bots.php @@ -58,17 +58,19 @@ function getBotParentList($current_parent,$dbconn) { $sql = "SELECT * FROM `bots` where bot_active = '1'"; $result = mysql_query($sql,$dbconn)or die('You have a SQL error on line '. __LINE__ . ' of ' . __FILE__ . '. Error message is: ' . mysql_error() . '.');; - $options = ' '; + $options = ' '; while($row = mysql_fetch_array($result)) { + if ($row['bot_id'] == 0) $options = str_replace('[noBot]', 'selected="selected"', $options); if($current_parent==$row['bot_id']) { - $sel = "SELECTED=SELECTED"; + $sel = "selected=\"selected\""; } else { $sel = ""; } - $options .= ' '; + $options .= ' '; } + $options = str_replace('[noBot]', 'selected="selected"', $options); return $options; } @@ -98,6 +100,7 @@ function getSelectedBot() { $dm_i = ""; $dm_ii = ""; $dm_iii = ""; + $dm_iv = ""; if($_SESSION['poadmin']['bot_id']!="new") { $bot_id = $_SESSION['poadmin']['bot_id']; //get data for all of the bots from the db @@ -115,61 +118,64 @@ function getSelectedBot() { } } if($bot_active=="1") { - $sel_yes = " SELECTED=SELECTED"; + $sel_yes = ' selected="selected"'; } else { - $sel_no = " SELECTED=SELECTED"; + $sel_no = ' selected="selected"'; } if($bot_save_state=="database") { - $sel_db = " SELECTED=SELECTED"; + $sel_db = ' selected="selected"'; } else { - $sel_session = " SELECTED=SELECTED"; + $sel_session = ' selected="selected"'; } if($bot_format=="html") { - $sel_html = " SELECTED=SELECTED"; + $sel_html = ' selected="selected"'; } elseif($bot_format=="xml") { - $sel_xml = " SELECTED=SELECTED"; + $sel_xml = ' selected="selected"'; } elseif($bot_format=="json") { - $sel_json = " SELECTED=SELECTED"; + $sel_json = ' selected="selected"'; } if($bot_use_aiml_code=="1") { - $sel_fuyes = " SELECTED=SELECTED"; + $sel_fuyes = ' selected="selected"'; } elseif($bot_use_aiml_code=="0") { - $sel_funo = " SELECTED=SELECTED"; + $sel_funo = ' selected="selected"'; } if($bot_update_aiml_code=="1") { - $sel_fyes = " SELECTED=SELECTED"; + $sel_fyes = ' selected="selected"'; } elseif($bot_update_aiml_code=="0") { - $sel_fno = " SELECTED=SELECTED"; + $sel_fno = ' selected="selected"'; } if($bot_debugshow=="0") { - $ds_ = " SELECTED=SELECTED"; + $ds_ = ' selected="selected"'; } elseif($bot_debugshow=="1") { - $ds_i = " SELECTED=SELECTED"; + $ds_i = ' selected="selected"'; } elseif($bot_debugshow=="2") { - $ds_ii = " SELECTED=SELECTED"; + $ds_ii = ' selected="selected"'; } elseif($bot_debugshow=="3") { - $ds_iii = " SELECTED=SELECTED"; + $ds_iii = ' selected="selected"'; } if($bot_debugmode=="0") { - $dm_ = " SELECTED=SELECTED"; + $dm_ = ' selected="selected"'; } - elseif($bot_debugshow=="1") { - $dm_i = " SELECTED=SELECTED"; + elseif($bot_debugmode=="1") { + $dm_i = ' selected="selected"'; } - elseif($bot_debugshow=="2") { - $dm_ii = " SELECTED=SELECTED"; + elseif($bot_debugmode=="2") { + $dm_ii = ' selected="selected"'; } - elseif($bot_debugshow=="3") { - $dm_iii = " SELECTED=SELECTED"; + elseif($bot_debugmode=="3") { + $dm_iii = ' selected="selected"'; + } + elseif($bot_debugmode=="4") { + $dm_iv = ' selected="selected"'; } $action = "update"; } @@ -196,8 +202,12 @@ function getSelectedBot() { $bot_conversation_lines = ""; $bot_remember_up_to = ""; $bot_debugemail = ""; + $debugemail_0 = ""; + $debugemail_1 = ""; $bot_debugshow = ""; $bot_debugmode = ""; + $bot_default_aiml_pattern = ''; + } $parent_options = getBotParentList($bot_parent_id,$dbconn); $searches = array( @@ -205,7 +215,7 @@ function getSelectedBot() { '[sel_html]','[sel_xml]','[sel_json]','[sel_session]','[sel_db]','[sel_fyes]', '[sel_fno]','[sel_fuyes]','[sel_funo]','[bot_conversation_lines]','[bot_remember_up_to]', '[bot_debugemail]','[dm_]','[dm_i]','[dm_ii]','[dm_iii]','[ds_]','[ds_i]','[ds_ii]', - '[ds_iii]','[action]','[debugemail_0]','[debugemail_1]' + '[ds_iii]','[action]','[debugemail_0]','[debugemail_1]', '[bot_default_aiml_pattern]', ); foreach ($searches as $search) { $replace = str_replace('[', '', $search); @@ -226,6 +236,7 @@ function updateBotSelection() { $value = mysql_escape_string(trim(stripslashes($value))); if(($key != "bot_id")&&($key != "action")&&($value!="")) { $sql = "UPDATE `bots` SET `$key` ='$value' where `bot_id` = '".$_POST['bot_id']."' limit 1; "; + $x = file_put_contents(_ADMIN_PATH_ . 'updateBotSQL.txt', $sql); $result = mysql_query($sql,$dbconn)or die('You have a SQL error on line '. __LINE__ . ' of ' . __FILE__ . '. Error message is: ' . mysql_error() . '.'); if(!$result) { $msg = 'Error updating bot details.'; @@ -381,10 +392,10 @@ function getChangeList() { //get bot names from the db $sql = "SELECT * FROM `bots` ORDER BY bot_name"; $result = mysql_query($sql,$dbconn)or die('You have a SQL error on line '. __LINE__ . ' of ' . __FILE__ . '. Error message is: ' . mysql_error() . '.'); - $options = '' . "\n"; + $options = '' . "\n"; while($row = mysql_fetch_array($result)) { if($_SESSION['poadmin']['bot_id']==$row['bot_id']) { - $sel = " SELECTED=SELECTED"; + $sel = ' selected="selected"'; } else { $sel= ""; diff --git a/admin/spellcheck.php b/admin/spellcheck.php index 253fa3a..5ae5f14 100644 --- a/admin/spellcheck.php +++ b/admin/spellcheck.php @@ -237,8 +237,8 @@ function runSpellSearch() { $correction = strtoupper($row['correction']); $id = $row['id']; $group = round(($id / 50)); - $action = " - "; + $action = "\"Edit + \"Edit"; $htmltbl .= " $misspell $correction diff --git a/admin/teach.php b/admin/teach.php index a6a5d7d..ea581b3 100644 --- a/admin/teach.php +++ b/admin/teach.php @@ -79,17 +79,20 @@ function insertAIML() { //db globals global $template, $msg; $dbconn = db_open(); - - $template = mysql_escape_string(trim($_POST['template'])); + $aiml = "[pattern][thatpattern]"; + $aimltemplate = mysql_escape_string(trim($_POST['template'])); $pattern = strtoupper(mysql_escape_string(trim($_POST['pattern']))); $thatpattern = strtoupper(mysql_escape_string(trim($_POST['thatpattern']))); + $aiml = str_replace('[pattern]', $pattern, $aiml); + $aiml = (empty($thatpattern)) ? str_replace('[thatpattern]', "$thatpattern", $aiml) : $aiml; + $aiml = str_replace('[template]', $aimltemplate, $aiml); $topic = strtoupper(mysql_escape_string(trim($_POST['topic']))); - $bot_id = (isset($_SESSION['poadmin']['bot_id'])) ? $_SESSION['poadmin']['bot_id'] : 0; + $bot_id = (isset($_SESSION['poadmin']['bot_id'])) ? $_SESSION['poadmin']['bot_id'] : 1; if(($pattern=="") || ($template=="")) { $msg = 'You must enter a user input and bot response.'; } else { - $sql = "INSERT INTO `aiml` (`id`,`bot_id`, `pattern`,`thatpattern`,`template`,`topic`,`filename`) VALUES (NULL,'$bot_id','$pattern','$thatpattern','$template','$topic','ADMIN ADDED')"; + $sql = "INSERT INTO `aiml` (`id`,`bot_id`, `aiml`, `pattern`,`thatpattern`,`template`,`topic`,`filename`, `php_code`) VALUES (NULL,'$bot_id', '$aiml','$pattern','$thatpattern','$aimltemplate','$topic','admin_added.aiml', '')"; $result = mysql_query($sql,$dbconn)or die('You have a SQL error on line '. __LINE__ . ' of ' . __FILE__ . '. Error message is: ' . mysql_error() . ".
    \nSQL = $sql
    \n"); if($result) { diff --git a/admin/upload.php b/admin/upload.php index 716bfda..7225165 100644 --- a/admin/upload.php +++ b/admin/upload.php @@ -112,19 +112,29 @@ function parseAIML ($file) { $xml = new DOMDocument(); $xml->load('./lures.xml'); */ - $aimlFile = file_get_contents($file); - $validRootTag = ''; - $curRootTagStart = stripos($aimlFile,'', $curRootTagStart) + 1; - $curRootTagLen = $curRootTagEnd - $curRootTagStart; - $curRootTag = substr($aimlFile, $curRootTagStart, $curRootTagLen); - if ($curRootTag !== $validRootTag) $aimlFile = str_ireplace($curRootTag, $validRootTag, $aimlFile); - #die("
    $aimlFile
    \n"); + + /*******************************************************/ + /* Set up for validation from a common DTD */ + /* This will involve removing the XML and */ + /* AIML tags from the beginning of the file */ + /* and replacing them with our own tags */ +/*******************************************************/ + $aimlContent = file_get_contents($file); + $validAIMLHeader = ' + + +'; + $aimlTagStart = stripos($aimlContent,'', $aimlTagStart) + 1; + $aimlFile = $validAIMLHeader . substr($aimlContent,$aimlTagEnd); + #die('
    '.htmlentities($aimlFile)."
    \n"); + $saveFile = str_replace('./uploads',_ADMIN_PATH_.'aiml',$file); + #if (!file_exists($saveFile)) file_put_contents("$saveFile", $aimlFile); $validate = new DOMDocument(); $validate->loadXML($aimlFile); $validate->preserveWhiteSpace = false; $validate->formatOutput = true; - if (!$validate->schemaValidate('./aiml.xsd')) { + if ($validate->validate() === false) { $msg = "Cannot parse file $file! Please note errors that follow:\n"; $errors = libxml_get_errors(); diff --git a/admin/wordcensor.php b/admin/wordcensor.php index d572b3d..c4ad611 100644 --- a/admin/wordcensor.php +++ b/admin/wordcensor.php @@ -182,9 +182,6 @@ function insertWordCensor() { $msg = '
    You must enter a spelling mistake and the replace_with.
    ' . "\n"; } else { -/* -INSERT INTO `morgaine`.`wordcensor` (`censor_id`, `word_to_censor`, `replace_with`, `bot_exclude`) VALUES (NULL, 'bitch', 'B***H', ''); -*/ $sql = "INSERT INTO `wordcensor` (`censor_id`, `word_to_censor`, `replace_with`, `bot_exclude`) VALUES (NULL,'$word_to_censor','$replace_with', '')"; $result = mysql_query($sql,$dbconn) or die('You have a SQL error on line '. __LINE__ . ' of ' . __FILE__ . '. Error message is: ' . mysql_error() . ".
    \nSQL = $sql
    \n"); @@ -243,8 +240,8 @@ function runWordCensorSearch() { $replace_with = strtoupper($row['replace_with']); $id = $row['censor_id']; $group = round(($id / 50)); - $action = " - "; + $action = "\"Edit + \"Delete"; $htmltbl .= " $word_to_censor $replace_with diff --git a/callmomtags.txt b/callmomtags.txt new file mode 100644 index 0000000..45516b8 --- /dev/null +++ b/callmomtags.txt @@ -0,0 +1,6 @@ +CallMom tags - Extensions to the AIML Specification + +new tags: +oob, dial, sms, search, url, urlget, botecho, map, directions, launch, email, contact, battery, version, setbot, host, botid, schedule + +An explanation of these new tags can be found at http://code.google.com/p/aiml-en-us-pandorabots-callmom/wiki/CallMomOOBTags \ No newline at end of file diff --git a/chatbot/core/aiml/find_aiml.php b/chatbot/core/aiml/find_aiml.php index f145f39..a34981f 100644 --- a/chatbot/core/aiml/find_aiml.php +++ b/chatbot/core/aiml/find_aiml.php @@ -491,14 +491,15 @@ function find_aiml_matches($convoArr){ $bot_id = get_convo_var($convoArr,"conversation","bot_id"); $bot_parent_id = get_convo_var($convoArr,"conversation","bot_parent_id"); $default_aiml_pattern = get_convo_var($convoArr,"conversation","default_aiml_pattern"); - $lookingfor = get_convo_var($convoArr,"aiml","lookingfor"); - + #$lookingfor = get_convo_var($convoArr,"aiml","lookingfor"); + $lookingfor = mysql_escape_string(get_convo_var($convoArr,"aiml","lookingfor")); + //get the first and last words of the cleaned user input $lastInputWord = get_last_word($lookingfor); $firstInputWord = get_first_word($lookingfor); - + //get the stored topic - $storedtopic = get_convo_var($convoArr,"topic"); + $storedtopic = mysql_escape_string(get_convo_var($convoArr,"topic")); //get the cleaned user input $lastthat = get_convo_var($convoArr,'that','',1,1); diff --git a/chatbot/core/conversation/display_conversation.php b/chatbot/core/conversation/display_conversation.php index c842ed0..8312561 100644 --- a/chatbot/core/conversation/display_conversation.php +++ b/chatbot/core/conversation/display_conversation.php @@ -44,7 +44,7 @@ function get_conversation_to_display($convoArr) `userid` = '".$convoArr['conversation']['user_id']."' AND `bot_id` = '".$convoArr['conversation']['bot_id']."' ORDER BY id DESC $limit "; - $x = save_file('conversationLogSQL.txt', "SQL = \r\n$sql"); + #$x = save_file('conversationLogSQL.txt', "SQL = \r\n$sql"); runDebug( __FILE__, __FUNCTION__, __LINE__, "get_conversation SQL: $sql",2); $result = db_query($sql,$con); diff --git a/chatbot/core/conversation/intialise_conversation.php b/chatbot/core/conversation/intialise_conversation.php index 7dade11..c8cd144 100644 --- a/chatbot/core/conversation/intialise_conversation.php +++ b/chatbot/core/conversation/intialise_conversation.php @@ -334,9 +334,9 @@ function load_bot_config($convoArr){ { $convoArr['conversation']['debugmode']=1; } - - - + + + return $convoArr; } @@ -397,7 +397,7 @@ function log_conversation_state($convoArr){ $user_id = $convoArr['conversation']['user_id']; $bot_id = $convoArr['conversation']['bot_id']; - $sql = "UPDATE `$dbn`.`users` + $sql = "UPDATE `$dbn`.`users` SET `state` = '$serialise_convo', `last_update` = NOW(), @@ -502,7 +502,7 @@ function check_set_convo_id($convoArr) **/ function check_set_user($convoArr) { - global $default_convo_id,$con,$dbn; + global $default_convo_id,$con,$dbn, $unknown_user; //check to see if user_name has been set if not set as default $convo_id = (isset($convoArr['conversation']['convo_id'])) ? $convoArr['conversation']['convo_id'] : session_id(); $bot_id = $convoArr['conversation']['bot_id']; @@ -518,7 +518,7 @@ function check_set_user($convoArr) $user_id = (!empty($row['id'])) ? $row['id'] : 0; $user_name = (!empty($row['name'])) ? $row['name'] : 'User'; } - $convoArr['conversation']['user_name'] = (!empty($user_name)) ? $user_name : UNKNOWN_USER; + $convoArr['conversation']['user_name'] = (!empty($user_name)) ? $user_name : $unknown_user; #die("User name = $user_name
    \n"); return $convoArr; } diff --git a/chatbot/core/user/handle_user.php b/chatbot/core/user/handle_user.php index 0076bb0..5ac1120 100644 --- a/chatbot/core/user/handle_user.php +++ b/chatbot/core/user/handle_user.php @@ -35,7 +35,7 @@ function load_new_client_defaults($convoArr) function get_user_id($convoArr) { //db globals - global $con,$dbn; + global $con,$dbn,$unknown_user; //get undefined defaults from the db $sql = "SELECT * FROM `$dbn`.`users` WHERE `session_id` = '".$convoArr['conversation']['convo_id']."' limit 1"; @@ -46,6 +46,8 @@ function get_user_id($convoArr) { $row = mysql_fetch_array($result); $convoArr['conversation']['user_id'] = $row['id']; + // add user name, if set + $convoArr['conversation']['user_name'] = (!empty($row['name'])) ? $row['name'] : (!empty($convoArr['client_properties']['name'])) ? $convoArr['client_properties']['name'] : $unknown_user; $msg = "existing"; } else @@ -69,7 +71,7 @@ function get_user_id($convoArr) function intisaliseUser($convo_id) { //db globals - global $con,$dbn; + global $con,$dbn, $default_bot_id; $sr = ""; $sa = ""; @@ -87,8 +89,8 @@ function intisaliseUser($convo_id) $sb = mysql_escape_string($_SERVER['HTTP_USER_AGENT']); } - $sql = "INSERT INTO `$dbn`.`users` (`id` ,`session_id` ,`chatlines` ,`ip` ,`referer` ,`browser` ,`date_logged_on` ,`last_update`) - VALUES ( NULL , '$convo_id', '0', '$sa', '$sr', '$sb', CURRENT_TIMESTAMP , '0000-00-00 00:00:00')"; + $sql = "INSERT INTO `$dbn`.`users` (`id` ,`session_id`, `bot_id`, `chatlines` ,`ip` ,`referer` ,`browser` ,`date_logged_on` ,`last_update`) + VALUES ( NULL , '$convo_id', $default_bot_id, '0', '$sa', '$sr', '$sb', CURRENT_TIMESTAMP , '0000-00-00 00:00:00')"; mysql_query($sql,$con); $user_id = mysql_insert_id($con); diff --git a/config/aiml.dtd b/config/aiml.dtd new file mode 100644 index 0000000..3c66e11 --- /dev/null +++ b/config/aiml.dtd @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/global_config.php b/config/global_config.php index 268e118..7542967 100644 --- a/config/global_config.php +++ b/config/global_config.php @@ -65,61 +65,62 @@ // And the database settings //------------------------------------------------------------------------ - if($server==$dev_host or ((!empty($alternate_local_server_name) and $server == $alternate_local_server_name))) + if($server==$dev_host or ((!empty($alternate_local_server_name) and $server == $alternate_local_server_name))) { //------------------------------------------------------------------------ // Development server settings - //------------------------------------------------------------------------ - $time_zone_locale = ""; // a full list can be found at http://uk.php.net/manual/en/timezones.php - $dbh = "localhost"; # dev remote server location - $dbPort = "3306"; # dev database name/prefix - $dbn = ""; # dev database name/prefix - $dbu = ""; # dev database username - $dbp = ""; # dev database password - + //------------------------------------------------------------------------ + $time_zone_locale = "America/Los_Angeles"; // a full list can be found at http://uk.php.net/manual/en/timezones.php + $dbh = "localhost"; # dev remote server location + $dbPort = "3306"; # dev database name/prefix + $dbn = ""; # dev database name/prefix + $dbu = ""; # dev database username + $dbp = ""; # dev database password + //these are the admin DB settings in case you want make the admin a different db user with more privs $adm_dbh = "localhost"; $adm_dbn = ""; $adm_dbu = ""; $adm_dbp = ""; - + //------------------------------------------------------------------------ // Default bot settings - //------------------------------------------------------------------------ - + //------------------------------------------------------------------------ + //Used to populate the stack when first initialized $default_stack_value = "om"; //Default conversation id will be set to current session $default_convo_id = session_id(); - + //default bot config - this is the default bot most of this will be overwriten by the bot configuration in the db $default_bot_id = 1; $default_format = "html"; $default_pattern = "*"; - $default_use_aiml_code = ''; - $default_update_aiml_code = ''; + $default_use_aiml_code = '0'; + $default_update_aiml_code = '0'; $default_conversation_lines = 5; $default_remember_up_to = 10; $default_debugemail = ""; - /* -* $default_debugshow - The level of messages to show the user -* 0=none, -* 1=error+general, -* 2=error+general+sql, -* 3=everything -*/ - $default_debugshow = 0; - - /* -* $default_debugmode - How to show the debug data -* 0 = source code view - show debugging in source code -* 1 = file log - log debugging to a file -* 2 = page view - display debugging on the webpage -* 3 = email each conversation line (not recommended) -*/ + /* + * $default_debugshow - The level of messages to show the user + * 0=none, + * 1=error+general, + * 2=error+general+sql, + * 3=everything + */ + $default_debugshow = 1; + + /* + * $default_debugmode - How to show the debug data + * 0 = source code view - show debugging in source code + * 1 = file log - log debugging to a file + * 2 = page view - display debugging on the webpage + * 3 = email each conversation line (not recommended) + */ $default_debugmode = 1; $default_save_state = "session"; $error_response = "Internal error detected. Please inform my botmaster."; + $unknown_user = "Guest"; //------------------------------------------------------------------------ // Default debug data @@ -131,30 +132,30 @@ //initially set here but overwriten by bot configuration in the admin panel $debuglevel = $default_debugshow; - + //for quick debug to override the bot config debug options - //0 - Do not show anything + //0 - Do not show anything //1 - will print out to screen immediately $quickdebug = 0; - + //for quick debug //1 = will write debug data to file regardless of the bot config choice //it will write it as soon as it becomes available but this this will be finally //overwriten once if and when the conversation turn is complete //this will hammer the server if left on so dont leave it on... use in emergencies. - $writetotemp = 0; + $writetotemp = 1; //debug folders where txt files are stored $debugfolder = _DEBUG_PATH_; - $debugfile = $debugfolder.$default_convo_id.".txt"; + $debugfile = $debugfolder.$default_convo_id.".txt"; } else { //------------------------------------------------------------------------ // LIVE server settings - //------------------------------------------------------------------------ + //------------------------------------------------------------------------ $time_zone_locale = "America/Los_Angeles"; // a full list can be found at http://uk.php.net/manual/en/timezones.php - $dbh = ""; + $dbh = "localhost"; $dbPort = "3306"; $dbn = ""; $dbu = ""; @@ -162,70 +163,71 @@ //these are the admin DB settings in case you want make the admin a different db user with more privs - $adm_dbh = ""; + $adm_dbh = "localhost"; $adm_dbn = ""; $adm_dbu = ""; $adm_dbp = ""; - + //------------------------------------------------------------------------ // Default bot settings - //------------------------------------------------------------------------ - + //------------------------------------------------------------------------ + //Used to populate the stack when first initialized $default_stack_value = "om"; //Default conversation id will be set to current session $default_convo_id = session_id(); - + //default bot config - this is the default bot most of this will be overwriten by the bot configuration in the db $default_bot_id = 1; $default_format = "html"; $default_pattern = "*"; - $default_use_aiml_code = ''; - $default_update_aiml_code = ''; + $default_use_aiml_code = '0'; + $default_update_aiml_code = '0'; $default_conversation_lines = 5; $default_remember_up_to = 10; $default_debugemail = ""; - /* -* $default_debugshow - The level of messages to show the user -* 0=none, -* 1=error+general, -* 2=error+general+sql, -* 3=everything -*/ - $default_debugshow = 0; - - /* -* $default_debugmode - How to show the debug data -* 0 = source code view - show debugging in source code -* 1 = file log - log debugging to a file -* 2 = page view - display debugging on the webpage -* 3 = email each conversation line (not recommended) -*/ + /* + * $default_debugshow - The level of messages to show the user + * 0=none, + * 1=error+general, + * 2=error+general+sql, + * 3=everything + */ + $default_debugshow = 3; + + /* + * $default_debugmode - How to show the debug data + * 0 = source code view - show debugging in source code + * 1 = file log - log debugging to a file + * 2 = page view - display debugging on the webpage + * 3 = email each conversation line (not recommended) + */ $default_debugmode = 1; $default_save_state = "session"; $error_response = "Internal error detected. Please inform my botmaster."; + $unknown_user = "Stranger"; //------------------------------------------------------------------------ // Default debug data //------------------------------------------------------------------------ - + // Turn off all error reporting error_reporting(0); - - + + //initially set here but overwriten by bot configuration in the admin panel - $debuglevel = $default_debugshow; - + $debuglevel = $default_debugshow; + //for quick debug to override the bot config debug options - //0 - Do not show anything + //0 - Do not show anything //1 - will print out to screen immediately $quickdebug = 0; - + //for quick debug //1 = will write debug data to file regardless of the bot config choice //it will write it as soon as it becomes available but this this will be finally overwriten once if and when the conversation turn is complete $writetotemp = 1; - + //debug folders where txt files are stored $debugfolder = _DEBUG_PATH_; $debugfile = $debugfolder.$default_convo_id.".txt"; @@ -234,7 +236,7 @@ //------------------------------------------------------------------------ // Set Misc Data //------------------------------------------------------------------------ - $botmaster_name = "Your Name"; + $botmaster_name = "Dave Morton"; //------------------------------------------------------------------------ // Set Program O Website URLs @@ -300,4 +302,5 @@ // Set Script Installation as completed //------------------------------------------------------------------------ + ?> \ No newline at end of file diff --git a/config/global_config.tpl b/config/global_config.tpl index e714c40..7071ca0 100644 --- a/config/global_config.tpl +++ b/config/global_config.tpl @@ -93,7 +93,7 @@ $default_convo_id = session_id(); //default bot config - this is the default bot most of this will be overwriten by the bot configuration in the db - $default_bot_id = [default_bot_id]; + $default_bot_id = 1; $default_format = "[default_format]"; $default_pattern = "[default_pattern]"; $default_use_aiml_code = '[default_use_aiml_code]'; @@ -120,6 +120,9 @@ $default_debugmode = [default_debugmode]; $default_save_state = "[default_save_state]"; $error_response = "[error_response]"; + $unknown_user = "[unknown_user]"; + + //------------------------------------------------------------------------ // Default debug data @@ -177,7 +180,7 @@ $default_convo_id = session_id(); //default bot config - this is the default bot most of this will be overwriten by the bot configuration in the db - $default_bot_id = [default_bot_id]; + $default_bot_id = 1; $default_format = "[default_format]"; $default_pattern = "[default_pattern]"; $default_use_aiml_code = '[default_use_aiml_code]'; @@ -204,6 +207,7 @@ $default_debugmode = [default_debugmode]; $default_save_state = "[default_save_state]"; $error_response = "[error_response]"; + $unknown_user = "[unknown_user]"; //------------------------------------------------------------------------ // Default debug data @@ -276,14 +280,14 @@ //------------------------------------------------------------------------ if (empty($_SESSION['commonWords'])) { - $_SESSION['commonWords'] = file(_CONF_PATH_.'commonWords.dat', FILE_IGNORE_NEW_LINES); + #$_SESSION['commonWords'] = file(_CONF_PATH_.'commonWords.dat', FILE_IGNORE_NEW_LINES); } $commonwordsArr = $_SESSION['commonWords']; if (empty($_SESSION['allowedHtmlTags'])) { - $_SESSION['allowedHtmlTags'] = file(_CONF_PATH_.'allowedHtmlTags.dat', FILE_IGNORE_NEW_LINES); + #$_SESSION['allowedHtmlTags'] = file(_CONF_PATH_.'allowedHtmlTags.dat', FILE_IGNORE_NEW_LINES); } $allowed_html_tags = $_SESSION['allowedHtmlTags']; diff --git a/gui/xml/index.php b/gui/xml/index.php index d5aef56..6a60b38 100644 --- a/gui/xml/index.php +++ b/gui/xml/index.php @@ -44,7 +44,7 @@ function get_response($path){ define("CHATBOT_URL_PATH",$chatbotURLpath); $send = "http://".$_SERVER['HTTP_HOST']. CHATBOT_URL_PATH . "/conversation_start.php?say=$say&convo_id=$convo_id&bot_id=$bot_id&format=$format"; - $X = file_put_contents('URL.txt', "$send\r\n",FILE_APPEND); + #$X = file_put_contents('URL.txt', "$send\r\n",FILE_APPEND); #die(); $sXML = trim(get_response($send)); /* @@ -52,7 +52,7 @@ function get_response($path){ $response = str_replace("\t\t", "\n", $response); $response = str_replace("\t", "\n", $response); */ - file_put_contents('conversationXML.txt', $sXML); + #file_put_contents('conversationXML.txt', $sXML); $xml = new SimpleXMLElement($sXML); #$xmlConversation = $xml->conversation; #$user_name = $xmlConversation->user_name; diff --git a/install/config.tpl.htm b/install/config.tpl.htm index 49b5d3b..a026ce0 100644 --- a/install/config.tpl.htm +++ b/install/config.tpl.htm @@ -139,7 +139,7 @@ var url = 'help.php?page=1'; helpPopups(url); }); - $('span.helpButton').attr('title','Click here to get help or the current item.'); + $('span.helpButton').attr('title','Click here to get help for the current item.'); $('span.helpButton').click(function() { //alert($(this).attr('id')); var tmpHash = $(this).attr('id'); @@ -380,7 +380,7 @@
    * Note: Place each bot name on a separate line. At least one name is required.
    For more detailed information with this page, please see the Help Page, or - click the help icon for an individual field. + click the help icon for an individual field.
    @@ -400,37 +400,38 @@
    -
    Bot Info for [current_bot_name]
    +
    Bot Info for the Primary Chatbot
    -
    -
    -
    -
    -
    -
    -
    -
    -
    diff --git a/install/config_template_tags.dat b/install/config_template_tags.dat index 94226ce..0125cfe 100644 --- a/install/config_template_tags.dat +++ b/install/config_template_tags.dat @@ -52,4 +52,6 @@ [remote_writetotemp] [remotehost] [rsTZ] +[use_aiml_code] +[update_aiml_code] [writetotemp] diff --git a/install/error.log b/install/error.log new file mode 100644 index 0000000..e69de29 diff --git a/install/install_programo.php b/install/install_programo.php index 26582f3..ef32bb7 100644 --- a/install/install_programo.php +++ b/install/install_programo.php @@ -63,8 +63,7 @@ $replVarsArray = array(); foreach ($replTagsArray as $value) { $value = trim($value); - $tmpVal = str_replace('[', '', $value); - $tmpVal = str_replace(']', '', $tmpVal); + $tmpVal = str_replace(array('[',']'), '', $value); $replVarsArray[$value] = $tmpVal; } chdir(dirname( realpath( __FILE__ ))); @@ -147,9 +146,11 @@ function save($page) { $postVars = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING); ksort($postVars); foreach ($postVars as $key => $value) { + if ($key == 'use_aiml_code') $_SESSION['default_use_aiml_code'] = rtrim($value); + if ($key == 'update_aiml_code') $_SESSION['default_update_aiml_code'] = rtrim($value); $_SESSION[$key] = rtrim($value); } - $x = file_put_contents(_INSTALL_PATH_ . 'sessionVars.txt', print_r($_SESSION, true) . "\r\n"); + #$x = file_put_contents(_INSTALL_PATH_ . 'sessionVars.txt', print_r($_SESSION, true) . "\r\n"); checkDBContents($postVars); switch ($page) { case 1: @@ -203,40 +204,39 @@ function getMain($page, $page_template) { return getSection('AIMLConfig', $page_template); break; } - $numBots = $_SESSION['bot_count']; $sql_template = <<\nVars:
    \n$death"); $out = mysql_fetch_assoc($result); if (empty($out)) { $sql = file_get_contents('new.sql'); diff --git a/library/db_functions.php b/library/db_functions.php index b4ec6d1..4729931 100644 --- a/library/db_functions.php +++ b/library/db_functions.php @@ -23,8 +23,8 @@ function db_open() { global $dbh, $dbu, $dbp, $dbn, $dbPort; $host = (!empty($dbPort) and $dbPort != 3306) ? "$dbh:$dbPort" : $dbh; // add port selection if not the standard port number - $conn = mysql_connect($host, $dbu, $dbp) or sqlErrorHandler( "mysql_connect", mysql_error(), mysql_errno()); - $x = mysql_select_db($dbn) or sqlErrorHandler( "mysql_select_db", mysql_error(), mysql_errno()); + $conn = mysql_connect($host, $dbu, $dbp) or sqlErrorHandler( "mysql_connect", mysql_error(), mysql_errno(), __FILE__, __FUNCTION__, __LINE__); + $x = mysql_select_db($dbn) or sqlErrorHandler( "mysql_select_db", mysql_error(), mysql_errno(), __FILE__, __FUNCTION__, __LINE__); return $conn; } @@ -34,7 +34,7 @@ function db_open() { * @param resource $con - the open connection **/ function db_close($con) { - $discdb = mysql_close($con) or sqlErrorHandler( "mysql_close", mysql_error(), mysql_errno()); + $discdb = mysql_close($con) or sqlErrorHandler( "mysql_close", mysql_error(), mysql_errno(), __FILE__, __FUNCTION__, __LINE__); } @@ -47,7 +47,7 @@ function db_close($con) { **/ function db_query($sql,$dbconn){ //run query - $result = mysql_query($sql,$dbconn)or sqlErrorHandler($sql, mysql_error(), mysql_errno()); + $result = mysql_query($sql,$dbconn)or sqlErrorHandler($sql, mysql_error(), mysql_errno(), __FILE__, __FUNCTION__, __LINE__); //if no results output message if(!$result){ } diff --git a/library/error_functions.php b/library/error_functions.php index 9fae902..b61fd3b 100644 --- a/library/error_functions.php +++ b/library/error_functions.php @@ -54,9 +54,9 @@ function myErrorHandler($errno, $errstr, $errfile, $errline) { * @param string $error - the mysql_error * @param string $erno - the mysql_error **/ -function sqlErrorHandler( $sql, $error, $erno){ - $info = "MYSQL ERROR $erno - $error when excuting $sql"; - runDebug('see above', 'see above', 'see above', $info, 1); +function sqlErrorHandler( $sql, $error, $erno, $file, $function, $line){ + $info = "MYSQL ERROR $erno - $error when excuting\n $sql"; + runDebug($file, $function, $line, $info, 1); } From 79aa2477c27721802f95942d4e837e4944a03a2e Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Wed, 6 Jun 2012 17:56:56 -0700 Subject: [PATCH 022/123] Fixed and simplified the install script After discussions with several users, it was decided that the install script was overly complicated, both for users, and in the way that it handled things. This has been corrected, and is now much more user friendly. Signed-off-by: Dave Morton --- admin/captcha-fonts/BoringLesson.ttf | Bin 331208 -> 0 bytes admin/captcha-fonts/GILLIGAN.ttf | Bin 36316 -> 0 bytes admin/captcha-fonts/NESBIT.ttf | Bin 30232 -> 0 bytes admin/captcha-fonts/comic.ttf | Bin 126364 -> 0 bytes admin/date.php | 7 - admin/index.php | 11 +- admin/logs.php | 2 +- admin/select_bots.php | 8 +- admin/style.css | 35 +- chatbot/conversation_start.php | 7 +- config/global_config.php | 306 ---------------- config/global_config.tpl | 403 ++++++++------------- config/install_config.php | 49 +++ gui/xml/index.php | 4 +- install/config.tpl.htm | 506 --------------------------- install/config_template_tags.dat | 2 +- install/help.css | 2 + install/help.php | 4 +- install/help.tpl.htm | 473 +++++++++++++------------ install/install.tpl.htm | 309 ++++++++++++++++ install/install_programo.php | 296 ++++------------ install/upgrade.php | 14 +- 22 files changed, 870 insertions(+), 1568 deletions(-) delete mode 100644 admin/captcha-fonts/BoringLesson.ttf delete mode 100644 admin/captcha-fonts/GILLIGAN.ttf delete mode 100644 admin/captcha-fonts/NESBIT.ttf delete mode 100644 admin/captcha-fonts/comic.ttf delete mode 100644 admin/date.php delete mode 100644 config/global_config.php create mode 100644 config/install_config.php delete mode 100644 install/config.tpl.htm create mode 100644 install/install.tpl.htm diff --git a/admin/captcha-fonts/BoringLesson.ttf b/admin/captcha-fonts/BoringLesson.ttf deleted file mode 100644 index 7748cef91434a9e2d7e51e829c46d139097cf3f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 331208 zcmb@vdAz0RRoA=UY40-+RcD`5T~k-rNmV6XO?92FI#rNPPaz4A#1Lq}G>Ssin9PMXPNvOW2dNjG`NYd_}65C7d?c;AO{=2_l<{3D-y z@S%79&PO~snKU26@7I0e+u!_-k9^B_pXc}AnD@$!@0imN$>dN zxBt-B{hhDo_ZztWENV+dqHfo8R&uCqw(`lgaQE zZ+q*T-}2*M{PMNQF#B!t-+CJ-R^HOTYci}n!0#8m?d_la`Jec<_NVzx9nEV${WCxD z&6A(_yx*S;t6#(K*T4PEpZ|{b3s*mMGOYau=`Vc7o8SJ{U;ov=|NWC;eS_=2_>Rwf z=VyP>)1Uu0Cc}o;fAk%n_11TM?qv3HlVS7w_)VJ0lTCBa|J=Vuf%f~&U!DvcyoLYC z;F^!Uj2tk zc}>o|b~5>69=z@kUH{0OK_w9M+8yYuCDK4&ty z^Qm{9y7Ms?K5CS^buIqXo5deQ@&}#mznZ+hJFL@pzHKsj&t&qzYu++|2~eo2QIwoneNi7 zKKc!ZPhEKW!qXq|mZvYg>cZRJ{FZ0>OZn!)Tc3XF!3)n!o_xcn@cA)sxbK8H;;4JSEsc;9=P$#?=zP19X^<$IdPU;hR#J$`uK8BZMEcX%IkJoO67UVY#- zPrl)mP;%c>AH)bVS-bN^{jB||{(ZyihR+_pc=+?fyN16ue9iDThi@N#VEEzT=ZAkk z{KoLxlQU1OwDh$0eYD7YvWA{ClLz^G?6K=d!~CIJH?H5haclp^qs{Gy9&K;?b<593 zo8#-Zj&ELmq`A%Km8;Fg=`63qt>$og`RL~57KKt{h(@+w^en@=@|%xfx0x+P4DBp|t7MW~^E9<*GBKuMli1k$<8B}pl=A8q87V*H{?38((U>k>WN>ZFC& zMW`Y(7o;g!7WLZ@9W@`{RQ#b{W{+v6YNJPFB`?^tBul3JE7#gv&^1Cbc=FOdtogcLr>2oLWnR2|M0wYzFnr}&N3F*;;_vn@c z%Xq!I+weUUmvX9{mrY(R`_Qf9i#CBWESW55JKQ_u%HhQ;DNGHzsp4I&#(T`l)4auc zr!tV0rdhVpSf0X$wS(*XYWqXC9&Tu16hw7-X#WOGC*dK@65$};G0}A|A`aRs^6mIR zNk``!E*v~8leC%5_Ap|eAM8!h-I0y!myxOzUG(H3Mc5Q{InwSQB-y!^f@q#|&4Twm zB%T&~ST0MQJ%Ct9f3$h5rL(3f$BHhdiL4S?jCy4O@K(gBL^pCd?Cl@Gaj7}wMm0=2 zKi}NjNPT35xRQO*jLi+pQg&93HYvHHFCs)96mSFijwPe^s#KL@5|y^bmo)#4b5s^; zQ{PlsQeIfBdX6p~mZIEzw2yErg@ufCBtIl8jeV*|id2a{kF7}u<%OnRvoE8XQv7K5 zaQo1HXrpFzW_IbJIf{0CbMbv;{C)cazCZB%C5uBc%4E@ebd%jfjc-gISU{JGM!8*S6&#ksBR&9+(L)pQ$e z-<~jiQuG6-yOh%0?@2NqkgMstzFBRPWjH&uUBkI%wHbJ~vm|V(w%ci`$NVeJl%t{9 zY=^zjMD=|;w9N@FZ*H_xu8`q^Rjg9U(4K55Ypvgbs`b^*j05HN_jC|IQLB9PE0jno z&hr5QYinx_MD_9lcdIhVp;AE%*0R#B!!@Zl`|c%PF^SB>R}X>h%pAkeo|{27)NHoh z`Ua&^>8vUsx0xu0Ogo68X}3>ECIvxJH;{XMb5kNrcj64~0aX&{XAcYEaX3X0aNo8H ze6`uy9Cj2Kl_Fg(S>kjyl|z2XeG6O78V3jmyWJj^cKZ`;yWj7&7T<1S2sE2bWF8)yZgpjESK-q(?PepP>l+)*^i<)IKEglYdCtr`~2`V?7mZLuUgi>QmqO zqeEt!?Kbj5Fl5^3=Ox5;ZHtZ)il@WIE~!$>@%ak#4Z6ilZuUO9q&r9W>to1yK`c@?N8C|+GeA(u_dSP&TtM2 zp}JEs5fPWIGaAtybd< z9_`YfNBbati?j9#PEPN=c-}6E{L)KSksFQEw_BT@ZQ8xT@}K`cbg+kVFIgZ>%&6c; zyGe7d*{et$TALxK(}wLHV(WUk`c7}75B+a;8`Mu)28`8plmZT{YG(&-L&HY<+%NZi z-yM;9gZGQZ_XB+2-}l3F|BKxnIlv)p1kZaz`oQmz;8}#X(^255m6hK@Nq&_K%}qW& zb04)a!*rU?44-^&FksDQLwk7*RZuxfLDC2)+3I?DhC)=T{ETEk#a!F2n-5a<&wInkCfI*C$x99h^OQC4yU?~w2XMQ#mI_=Lw{+AdNcMY;|#Eff&r+5D>5oExaHh*Wy*j7Bd8jJ zW-Kx@qVryXPD>m@pigw05l~f|wr)=}vO#l4E;KCaV0)zee(2WL7*kHpPe3WxkcSB! zpBY%W>4gQ`3suf1>F9|NtzM_0(@)h1)YfK+hSA5-u9;~iwd>XqeYC^DBn(IW)3acM zvpXq+O43HE*0Oedeo`}AbmemlthTX}yi@2}(iRlNTa?{|UbKYj8klePZM!}ZD4;nVoP zKIyLR{P@m~3-2F$!5^m|-1+(IM+bXApxDtdg)v{n?Bj6^`5OoPDm!^;`+HF4{-f=F zjyi1KvbMK60HP-RIUp6+yjaLlyI#%yo`SU3tP}Q2Mz^p3h3(<9Ma#uE)#(p z(9_{=5Sj_g!Fb6N2|U&SHAtm`G1JedBGZbH_{nrh`ursQ?C$u4mbqP zRl#Fr_AH^MNg`e^%fz79fI4K>u;C%_2^$GE&UySY{{bEp8G#2*&_U;JMr~61psiU;GU>r@98$Ks7{I#~6 zT=ueS-Ru~Jk1t49lZB!Z@dzk8%GDRp-PpIKW=mIBa|qMnmWEByhs$!S^h-;0zHbqS z3q}s5pmFow8O2M2)>a`zFWtZ%FXyVf?=xv%zM_<1lxG8J;b z{0Adetuf`W9rgjN7;D`AxoZy^y?u9b&i#{nIyY@C1#ih$0ag?X2|@I^gLku2-s<~3 zjdt((WcKdcZS59{257?3$_y)s6#YRA3Q~bKxu6M3nb=R8%@phA+_@lj*pte9Q7lFf z_6P>`UUw_9SO+0;E7okqlFy<| z?Yg93KCNT21N!@Eb80FO)^gg_9LMgafZ#^R5Lf7R(X1eYE(TIkO%g!Xy7m_(avmli zQ0jLR z_BJWC!lp14$s&?L9|UDaOR#X)RIJ^0eGXM(b)dV1@;VW>fe8`JFfH>OK4G}Hon!cE z@X&&qe;VPjZ^=SqtzbNc*g}IOqgmFPosoBsL$(H!J|T*M04k$!OT*ySdbcRrzoQ;4 z(RH;*aI8@sf6)Lcl8T0WdX_AX{3v-loY=*T6uu%Ky%LI>ic6=kV$o@;qW~zjTD~{8 z)s&_^t96}bQW#vI1hXUFwKWsce4~mONz$>W2*z%k0{Q`lu!_3z# z-;WE7Ltw5_ovotB??bwFigrs)3@vgueg4n!TOo|C6M1mLBx+Z){t@8nZ)DUEiIGlM zkiMefg1C%iF-obwX)(Zq{~%M{I|E3W!fVhzRfiS5Fh4*!THl*U31z^IwpP`_xvm~y zdUr-kAH4!&FgQd~7Dw9(?5b9%TWLR>#5XK@_Bez>5n#E$>>=;x{>tw91L}jWA6ie1 zquI8w#Q*F_DhyK%ZD4z=3UUQd=l3*_A(u@=t!bJqNCP!TT`1GB1_Zj^%vQur&C4tX z4sFHw+(cB=qBK={qysxOGI^vAVH4L0)tk#I?#)RIdvHw}WJ6R>EBeDcf(M|BWzkEx zMx$7_RIbov`609oxlu0|v>qWVe=5-#n^P1d%RQ*q43uZLh-hpvq*g?4nG4TOamJBd z9f{d*3Wtd-R|1a(W7Gpb`G6`}3*Lf6MPe6#niTC-`cRsfv$`$F!3oP6yceNE04Def zSqciBAkB0(;}m%r8v)hc*6v|A&j_8i%y?)e!fTY1lO(66>3GGOJV}*tEp+ri1VrJH zIs}lnhF86){74({tt!|O`oSs0mokj_p)@J@Ty14HzF3r+@dc6b1sJ9gae!rHM$1|57sD1eK2msd=f*`X(=+#;v3Km8ju)J`zwjysZD4_B-j9&v(=AZ1{$>Dp3 ze>D8_;YWv`8h&NEKD`G(`XSR-O!;*XpVoFfC!jDU^~FI8`$qmd>2LohydK; zXCOM^$}u0uaT1uebI!6rqMt!47w3CdY*DHV_^Sy_Sgco}Wh<2_5q0LzMZBVp(UCzT zAghHY*d)k%;SW);DZ+w1$*xPKRtD~)l6xjZY!0$7w(DoeIuY;=TF#KAE$-Al|Hdg?q# zPz^cX!l+ZlwqlgX#-?6=9^JSORwaKR>!T}hXEir&XP#OGQ8f%M;In4!ok$jBDm?`) z$wizrBs&o_HnSR}`vVV(YKS;!U6S}JE(8BwPGB zq`7Pi%2kCOC(PxkZXj@S$|;%Sve97^pPE$YBn#ZONZl^u|MeO$>=EOIQKjNk8T}FA zqcjkGQheob7-#;W<7WJ1926=iMH~$gwakrf9X*E-!wEwe2$m>fag)$Qw3i;bW8IMo z^Va@=;En;}3wTX{!4jk2zQKTjut{iQvOJu!r$@8vm+dPus%I>vb(d^Z_$Zwnsue;k z<(QC5S9AxU%C~QD;^-y^F_t=JdQa@u{j}fP zKj}@CLPktD0Kq-6fyEmtEY!QI#Si_~7B|pfxZr@rguPZKP6qLfwuzIRSm*OuB?S%a ziESir=((#>qVqvJJ47vD-GDQ=xS+pniBWJa(891`!dxc7L%UoDN$nv!3C6Yc>T{u54s# zJat6IF^eN1>x`*9TjjF&69l%Luv^TXiRVXrNxJP5+fMidmSA=<%|h$HirLohAyGe7 zzDNi|lZJlu*Que|LwK|2&G10qT$fJDqsfQ4W-3Xa|9Y_Ai=E>+c1w*(UW-4D{ z=><7Ru_nxn60mj*xQe?#nL*zvpUU}1jKPHga9V>v>YB|r#3OV-3GhKFK+0t+2MO@c z5l}e+C)oWv?fp~Es4wKA4hW6=Yion%+1$+3f5$|ydKpbcmr{cbqLNV9E(#GxnU%GW zde6*aaLd4Pa1qMZ;i(%=pF52)+5iNQv{Spoo?RPNIYr&~;kZF9utta7np~y0m&z2^EqD<5ps)<7`rO!Rz=H?Le#R8Gx{sa0Td^-D(0J*Siz(pPY-4e^>PM(Z(LV#od})N*h|cA)a%Q`HrlOvzX-byFOO zalWyEawtHPqioz*7!BrxXc6v$*P9>Es*qPR<|{T*q62h#a_TpvSo;vXpmdVyke^C) z_o53r8EkR*LxC!IN*#>zkxWz(C6HeJNBY|Knp~V&l!oXwp-j02w)e0_z5Pq*KAgl; z!q_5fd*DxAZ{f1N4tWU0Gr~mPFyK(%ZLX|rQY3By^s-be1jwK5pAkDkrO>UNEpNw; zV{y#WWXMN=4aeI9*xTuW+~W83wlm<;@VfnW41Hr@V4UuW#Uf4xN0ur)T)2R1B6*;T z2qdL2*LOvI-r(a`@R#kUZ;5P-`}}i?8>)1wv_#pcY*12uBNCd{22wOMjRSq0lH7%H zz;K>&yj`x(W;42%o@8$FRWqd#(1SoC6U9&StH#z>y#NAjcFJBc59R5J;_P<%vo0CW z+r8Trc%~4Vf>Luva+Tp3y9C9cXmvk=x5kyhoFeYRrXlnFOxdI)Jo>*T|b14YcXlz^4IMunShs)-mg z>E$IkOLt&vR$L=6yK~fiSNm)IZ}-m*uNj^k{>1Q!#0k7}_@d#@4S!+y%HgYrZybnD zm^|^sLI2bJf5Ch4L&P0ioJ^ZvWmV0;;r;J<{}S(CV1dc+^}o~qO8;xdC5&E;G05O5 zZjBK`CVXE1LCsZr6iy%i&RwYs7gY(2T>tpz(mKviYUpmgPUzERi*4eoXdpLa=CrUZ*^}a&80y3z4SA z64xAm2)rG(kwtF!4!INMufrvFOf#UY${d8&h!&XajlhL=%!91_si8l9f;~UFlEGZ6cXrB==x)UxIFk;IUV@!^*(ybb@Rgbg5!jy^mA;4-091!U zacxR7%@{>f-1s@yty5$_+%gw_Jv07CdQ&Q6`2*F`@|GMx0F)R9FLEXtehkBZj4%_b zSB8<6Odmp9oI-Iws4z-teEBmCK@;cXF^&Vffnp0TCj7%L6u}x$Uy&pU;K0vuT>Up! z>1-o;dPll@p~M9O_?mP=K@WggQV1J@p%5ySCBwy%hDde}J7EAevx1{MCJLZhHDV8k zwCuuHbqO~Xf=4i=k=5p-;*1!dz9X9sOYbLvNF!&A_87fYm*^E&a6T-5FV2tOf7JPf zt6<@@_@jO|KcX=mZvpofoZ>NLn+x2}nfmwcUQ^QEXr1VGls`#?cYER_7Za1ow+0J_ zV{V)vSMH>JbCo++Zs8TMs3`zs)z;uk}nVJ#Y?h5N^T6lY-GHjK)H(Q(b z5kI-@oX>q9dxX32LA%u(Fvv8({M3995OM(*{%~)=1{OuA^d$MM9_-^SzuS+EmRSW) zJ2gxN(xQ1XSh}`0ob$3O=N(6X2{zQcTUi-CmYcLuq;TgQoLeM@9ClwMx^i;3p93!D zM%>Qlcp@wh;c9@83PKc!wI{NIlL&J6q!PwBoC58%IJ~Oo1XS-l1VDgz3}E73Gwh!} z&{k1a%tjz1!qBsTKnH{|v%45Pkt00g#Q+GUJVYc|s~i%Q_K25An}Z2XO(;+t(M?dv zbYHm!3c~5=+2>++j!7<~7sCanKX@rEfm@$T-IRK9PRE|YPSKPlSYW+2%g3Lrgo+r4^T{HFq9^2iL=_l zBBB>CZA(ysT1w+6AGK0(q`f%PF*&OLhY%BVzE)9a7$I}}0GT&cY54_UO$ndCxneAb zb{#np(e%~Kg~K>Ph?Q#D%-(CMF_*Kh^%@;fg6SB=b+@^trOe!7YAu?$?z#yI_TP^^ zdM!s`Bxy)a-$H9iok3s z+Lw|G<-?Xa5?=wnDoQBUO2#gZi$f*h&3S?kJReAzp^E@4$_s^vKrf*qZjkPb*;OdW zgmLQ>B&pN~3zGgcN=nw$w>Jm-a!5SU{d}pbx(FyS%@3}@b;4Ph@XITf=0V7f9`S3y zc^GEMY`qkst5Rj4<^XXrsj(y$%99^T0zpJfo=x}9VYxP{qA+dOa74_Q89G*Q^fHiD z*Rg+u6^emOLv`p?aF53DRobXm4NS!^2yq<(iQ(3?D4A?3XAbcZ1JsNa3sJCO5d@sU z6{PF(q7*wI7c9?m7j>LK5g;nHL#~`acS0_jiU&w1uk}Y3lEUrUG^8S?Ai9+ySYl<$ zWtf5ly~JFrW|6%hYZWh|V}t~>l{}a67~pffy;MLc$w<;mSr&v)>`)8@dh)5cN(nXa zX%>gO;pChSJdU~Ci_C&gDm23+4aki}6{xjQ2CkR}+A?Ns_Wd?X@iuB`wOGjB2|XD% zO5S?99l$@P!^IsT(`qv4Sj7EaZ#TEXoo zvz0>o81LJ>w|UQayQcv=fwZsWeUmr49x&HGVgH1)lY9Pfuzf)B^`lFB8qL1ODBJGH z2;x|r&fSR)%k;)nCFD02EGaZaoemsa#a`2y1qT31V}{Dc)hvg;QkXDMNfaX@Jhqgj zS<5lDdfBuw+OguVF~wyNJE78}gni&r6S8?5W&$+=G{R2 zLRWxeA&y}780X6g&m|+bo%V--%g&*N9b+Ks$WLQ@3FaShD;o#wC($ArBGaYH=87MYO6mJfnn5)?|D*0sROx;!Wyu{4EOD9B~=3v81Em7}~K$m3}4 zTy*f>!TTY~5)+^A7a%?aJ~8vX_#aE1Kg7yvlByepDuZzgjTut#!sze>paa2D8-jyl z5NeQXO&ml1m}9AtM5T`^Z$;2g%jqeqY_eMc)TWEp<1s1R=nSyI3E=S}JEBeoRHrbu z&G!QQR)~;dGrcR@B&BarjBz1Fxe!-PhLlD6^wb1HlqmN@i<*fqWki`_?Hr4wpp<%- zD`(igK-P$y59AyHeLNItUK?>iyN;yNsq42MYag8<5d(P;e-GFsBti_o(41kZIW!Pe zSW7k2qr<@hf2wg~*Y;|{Bk~RWUIZh!;Qe3`DOA85T#P*KhR=+FGjedgRaA>L2*^<4 z>8wR~MwfBKQHyEmE}K(~o*z=7;@4VTdc+*?qDQpfC}8sbwFOk!NYreC4` zE3FX*ooPWVRu_+QFZDjvZl3n;%e`d3KY_7&!CQV15BDeT6WpQ*b>^nT*8tY|Q1?u)t2LWegZism*?WUN!qyO8g>Y`HZGeY2!y+I!3poj zncA}o(T=5XuiOBFYSnOOHag$ipGk8^)=Pg1o2k$qiLSq6^q4P-UnGU7uD#@0S9RmT z*E(F575gnWOHV^jjDwmtL`o_qu_0w;<`SI7l*PHjb0Svlh!v)C ztc`!HMdu5hw!qNU>VlFJ5d~4&#GSKV^1_@3iqWP476WFdrnZp?3k95&cFsKVah?^J zf#hAd9t#h7LgGPtzl8vJt#E8bXW>c8oRm=p5xEPimlEw?YKv@c$xVzX0c>!Jt?Um9 z2m7gMdO$D$Phy00`xw$pi!4_hRGS8b1_hG%ersiR8VwBCTqu+47F@0lwxn6u!eC<~ zu$q8-ln(VAoDFs2YlAs`iPox~C=qG-0oI zb$nLrCz(nRR~6IeY^PvX{CLWNU&^J6>^s?Cv*t8Rnx%qb(sOJ$feb&#) z2DeSrjg*a0Gw~z5Wh^OoK)Pwn<%MU6c1H`qs6m`4fRj>+gC1KWWa&=MzzM#m5ko>8 zkVr79H3 zbIsx$Pw+KNFPWB7qI`_kBMrnOj#98|ElqtT6L9)PNbq)gXE<1lMA(Iz2scwBL})Q zyhPBPV|kDH(`C0`3MH^iWlMyPQbB{N2zAuV@@!f`2L+j_&KfO@4&;>KDrri3i^zl- zJ_#%IRx(#-NQ@kr2M;)=6bFlGq)ipvDOZWV7Pzge*ftHzB)T_%C(o zScJ|Y)d&e$A38uaG(R7aI|Wi1S8UU1yfUfWzRGA<=Sk{|6D3H>2;h{wAaEEllq{o> zLrEf|sF#9Qfu(91{JS{{_y2*>3Qop(X_8nwRQ@0bHm~eCkU76=_p@)){wf9p;l6|2 zG+2T+^fUC$X}ezPaUvM$Qi5Gmws0t;lKghFi9W#cX>F4DNNH7c&23X?Ps+&6O>&R| zePx~a5=-=VmV>Ypht%6+)8ot&0rDxkNJKeBq%yMU5*WV8vg6K%Yj-}WzqkFB{tH=6 z{!RVg>%YJMC;cz?|DpfS!^6X4!^?+P4<9{z3NyHWW%wJz*AL%1`G_aZ^qoAQ34p z9$zOKScVHSj$zEHUQ)ti&8sdA^pOQOi#xoSdETNC5Fo2Hi;7Lm8g>6uuwdl)7WJ8g zgfXWLq&F4FI#H|27@Dq))6%A7eq0{Q$`_G{K_7F0aqZm4PH786y{veo1RM@Nzsd%2 zy;mzPS>yhwaK|`bCfT88ly$fN@u-aQsUUlG9aV=3U(94DeOyr3fM?kHoWCU{s;9N31wmGepX>wW~ECZghWas|I zD)>1b1GGRzyf{_s%bo61par(N+l%!RWI-!r018sjoT&e!Jz-M%M6{K5sH`>R??iF% zpva1u&hRo8U{PoqSnC#vOSuYk-w>~o+UWVT=v_1{Y7QQ**E))OKhWs)?%ZaG;T%8;z=Erb-Lf7==GDRIws2}SSq>S}59l;=b- zt0b!MTQ}x}TVGksbuPZI%oRBmTy5+AvuN#ReIHp&6` zj%?IF?%&g_7KBEZpprEPklSHsM2M`?4^4*4LK;P3k6=+}(vsV?hJGzb;cN>8|+ zWuyB0ZYigkck;psbRo{2ds#(jWIA>mI06mjEtrUTiY8!=?mB;qG+Ca|7}y90MK3Br zb!|6W+1ct;`Z`SqfxKQc8g+Npm5O;~e%l^Ne5fVUN~_IFsqxdf=;C@L2WZYry|FOx;m@vXS4BZs0JY+IAjWK%+F zB^1gRV1!U{Xie`?P!XQP2i+uQ@p#Jeo$wsXgIG1tBu>J?Ym8`(eI&u+&^D%i>4|)( zy-7!Vi4-;Lr3m7c>%!j02{!gvxrz#rE`_1rB2N+{sFE!On3^K=R|PeMPvx}(D0k#k zUiSE0CF2O65Sg;&72AJAsbq}KI(D(UqnmBHr-1{(h(rQx#ALi6hx%1NGx5UFH&7By zm*o)oK&FB(muf>{l3F)Rpuk2fpOzmB;6IGgXv#!RO$dEzLJ(WvqCkb*az_&C(^;6o z3L?}S$N*4RlT$$YB)`(Os@(?r4)8^0%aeaSr(q+d?)JKiBvhxgGhI^kQu=UfyM|r9 zxc!{q4$sw-jJ8&LI-eY*HN3ypd5aJiA~BMCjr(M0a@KXMxt`uR%lP+S`gim{J^aM*Gs7=UyXm{8|JU?yO}}mW zUDNNG{=oEyr~h&KU#IVzJ~!)T>$8)yGn4=R#3u~j!vDMbujciPG@8-gT zzPqM9b3s>xFC%WxYF=&X%>8J02Wx;4ugB637Qfaw=Y*kKlb&S*lSL#4y1#uufs}g$ z3OD4LLXHpimf79N4PzSUdNH=O5Vc@wlGLv0cc+d(q+A9s90LbNQ~GVT*XeRTK_W~w9cI(`2;eRvGmV98yX_j)5fU0WQq(FS z@+!EL28CR3t{1+<xs>RH8;Z~K8ibmBG{mRslJGMoa>N8?Q zhLqu!YeCb@{F1d)ngXb4NuaV*XjOt8A!)@-05+lR2JDd8^EsD+4 z2tvNJFO+C8{fr0e{iqt9c8lQwzKUX~?k|~p#Q2H>g)EpXQYKkG^^@hP4Z?L!^2O63 zZsEQ1mxa+`Eq)wv%!r1nXdEdNMK~_R=g3!AZjJFXekKMa+6Nh@#Bpv77^1`0rGnP@aILxI z-VVB5vPZ%gIj?gt z+b>wyzVLJ`3TnO*3l-#u6`;v7;Jn8i5YJ&~3GYRJaS!awYvynf`?~NGj46YBb}&=P zjY+5>py{BcVJS@E0l)aQ(aqS=muu|B1ro;=neGHvbQ&(MoF+r%!p6r|!9-z47!gXs z)v!UBy|XA{-`uE%A_Sgt09-IVSF_|T#tkqprcMmj_4V3*FH2k9gok1*KqD+ZiRON+ zSU10e+H!dCz1du-S>qj|-yoeFnJxt1K%ZtaveKfL@q!>!Eh=N~=R`XO8HO3EgT59> zJY{0lnQTB6R7d<8usVf6U97=uvUHq43izR(i-$3j-8`;c!~Z~}-p2M$st)_qE5J^K z<8qHc973$)L`uv-;P6OzGxNHvVdgwyDZw$%i6r1arx#ow;ESle&w$Y~ON2Um^U_ja zy!e$AF*EmWg)K0^1jJ*Nx&?R?j40SFZIp>ERBo!s%SJkZi=qV2l$TUT%oJv3Cahft z>n|2Maw!YEogo6nGU;2p%uxqk5$2JE&6(9jR2(8s*GR-XC@1{0xM zR7ng)jA2g1gW+tqCb>$d2Syqy1t%1ox%jhnV$vfV!4v<9AmD;rAw@QH!d&N)$QDK8 z6#oUmFv<Cq12{|MD>c)!_7-HVzGY(p)<&E-NwkK3BD)O*))B!iP@y>c zEEd-zHo5?92w1a62zQ#l*rvnb!C|$bG7<_X2Zb)-t{IlIt=;lsQkMMDo5nOLoP>%f z0-%uWYaw0p=YB_0xNyQP=}5*XKq`LXby3G%6ssT(4dT_`o^oEAa))f50YXN*a z&lY)EW_hY(%f;FtL+)!yI87S#`yv_-cgygFLMU1i1wkM?wIJYrHv0n7IdVS#8pr@# z^Wi>rEn8tKTuF|&0!YQk5DBIFRaqkpqmNNuCGA>}6j}rNk`vV2{K(E>K}n$r6&doK zgd1f=D(XfpE8k4L&;ADa+NbbSJohj&y0~tx>3?-oyXl|j?EgbO)5Q8J5R{r)9URF1 zTKqt7Z~K>LDEhE`{sT&U{>S0l1p(jZNG&l)EoX&tAPIh_)!Ye%@@4+?aZpi&M%7s3}CGVpEpk-Hrhr!Yg$ zfWrpvI!%&KS&SU(bn_5SH+uIlAGkH~+r~4(#x6N71c3C#ut9o~su|vsRFIwp$}F~z zQ)VG-)2o$S$Fbn{r{+SW>>0sHrcwT>n-2(& zyqO~B24zVw;Z02HalS+!rGR`A$QM#Fz>gL*@uvM4A;phm*EXL?Eq2^IA7Y4Ku#vK9 zt6HtL<1i()4k5QO83X4XAj;z_rR97foyGcvLQq<%p;TE%bhjv|bR<+oOVez?O6U?W z4=a)d8nEX=Fn-N$&E0pEO>QS83Fap}DW7pU8J=T2LIuubcfz!RJ;IU|{3udXXIzTV zGN^LV3dl~wp=^r1X}%K)=!8j>1{7OW734P>2Xzh$o<-6gHWhNF2#w`)k9^;bpA+|D56J;ja!~HGISH?&0rdXTyIw{Pge(!+#jw zH{6+g+7mDC{}WFi_(h&V@Cfg-#FF6qYJZRSZ}9#e-v2j#t-r(fZ}R?bj(u~k{Wad- z&oOD5zfD}pWO$I+lG^+51KeBu#F-WB_i$V8o`j*7d#W5a!B<`jo*n@jKlDEZu{n8UOKLXu{7W-P|4Z~!`H(I4lfFb*@vKI(l~g$ zoKK0v(34X*d;#*xvs6AETvLxNoKv{5^b2vhK(Z9Ni2?MF3#O~qFs27 zfPyL{UI~miou%3W@DF?wd$D)E1ta@aZw^7W!t;fdu zp6V6|Ujm2_(fM|5SO&~g1VJ^q5?dXo?C-`86j~1acah6d24RVx#i6AeK%?4av3oQx zo!L%Z zI)G%;tLSW5=fLCtN#b-3vcIK6nH180mtIl^0rea36`qLjz?a7f^cF9U^y@V#)kETV6&=-kR$CF8qU7j63~1x7n*`dp zOW`HNnX8A4mp5Q6TiI!aiixR z%oI5Hq5Qz@}5R26HdQ;4zFCxn#9DX1kVkY(O3gu%Qve zLV@_gsK(oUAhuBrMATvkO%tYqA)_r*rE0OUaRP`f=|L#0Y>Ct$!g4i0MAGTypb3g;_%h?KL-rkj} zSNo%P|U@(_(AXzP&{xBR?4ALEP+z^ZMm|V z?H3UvKa(psu%KzgOx#O$wzK$lz2BL$ej>pqIJM6E$UW;YBx`tFTa7A$;&nR+nLFm> zT`y7BFlZ?zo;lYd;E2QwjLC9*~g%9kL@xjk&|lTb-YhY z#|>(O1&l$it4!)-Q!qVyWk_l|bGd;v z(}5;p1?IT<-{|%&7D>*UufTusp!HIr8Wg`35lw(fljuf?<9Xw`SIcMmo1|~Ht1qJ- zr2}|Yp~yUI|1z$KXMdQF?xGGE-uKgMo(pz-?!!3WK0BShUbh<6Q9<84_x&`FJ^OnV zWg_tz-HiY0hreX;EFX*Ow=G*7oQ8zdD-Q%#l$BT&PGs=NCIFO8qvOF^-KPK9L6e=- z(wbEwHUP2}VRA@oTA4p-9cXiOLhZCUaC{ku5?koM|s9ax>ZfjtAc$RVzs&^Lb!|Jq^pO;Ak1(w!Yt za)}B|@IWfa#~G(!?16|U7dOP=k6FTHk9PcE%!ZZzEtYoHOT#C0YbXLtOLNoQup=?f z8qnGAQXC2OBd(cy0wQ1K8H3-@{#<{ne{^``@X5nxg6Y3}_@?1o$LAFM%i(8-UqP8K zOka!>;DgyS;lrn|pT1%G#_1_zBXXIV|B+(E}By?Df)9F`w&pbkq&_|oC6RPJ3A0OMO$v16X1HlsT=Fv zsE3?A(q0STbX`U!g5!1oBLH8=nRtRqmh>6>Z;VT(xobg4mQ9ZlBuaF2Cs55;6LdP2 zEh?;S+(!@suD)qv8{;Z-&`3Lim<8Z0WR4V$ZeE!R#`6F|u*Bkd4mQl#HZ+n?J}e1x z&TySz;_`w}v5tl`IbjJdzmdA|3>hG#tL)|;y0}WpybS)AqMJGbAob|tT!@tA;&EAo z{KaD%sl#k))rd%|s4+rB{u-C>JHM53eRfCe_1iGAh8fA@AwQ( z10f#TzZcJngw>vSBRba+P_WdkL5gPU*dCuI5a=E41?r|(!7Am_cNX2S!e?B^1>RTB z3+(C$5RmeMpcK78$D~3Bh(+jX{r>`VUW&6R5g&`rQahN~!R{WRn`4i>eT8<9f>@EB z79Lwcr@M|PEB+au`AO=LGD#1CfO15#a5x#Y6z9oEZi#_U7jYnx4ry1FC@c93)ZcdT z%=Pox#ejk0e3p;oU@RDEN1nRZ^b^K6w^TRun(oozI{H9}ekCyym-M4VcE)A!x>8b; zorR~z#xA8iyppFB#3R6F3A*u!6GFYRvgQM@|9#_c@-Rj*K;cRthzR$_vKOgkbVn=W zDW;EhmwfO+IuRpDspbOhFG5_FjkqZN18QHSyPLztCC%11SPgYlk-Un+@CR_oYfo&kr#BkK23S*pIi5@c{GcAS&n8R(Lz#~CZ z6+EE1Ap6)_%x_=k=}4>ymRF<_20RED=Q)Z?`Z$F-Sj&-Odja;=V}tMPpTZUk|70vK z%K&3dL^j(4Ql4U>gp-*2kgmys4s4%f5d@wUDFs}KVUQ@4oys&3?xk^ph-ND}KsPZ6 zZlPyISUNO)98Y8G+R*ioOVmM&vu*R30>TAO0~`2}EwIarU=tQ@rzQ1U@Zz62G@D1; z5xja2{s&2R8IQH{=_C~?aD6P0tS~&+Xhai4GosE|q^qkuwGnRp2tJ{JU~kk^Xb|^~ z;wQMGHG-PPK&!G03scyMv>A>A))n0qgB8-`070sP$mj@H2?B#X@>erO%hWyF<5_MU!+a|3&BNrM_^@pms5SSpY7zCE-WaJ-cDaDx6oo;8_PUgp^5duiL|LMGtBfB1`XU2X&6uQ zn=!`4@Oef8aMErd>R}i|hC&W<0cou;*6DQ$n4&d3=QOEY zNrE0P%XBM77!tyPas-kgg4CRqFe-;2o;YBw4ooVS1?!+-hZ?IUEycoD;5{=bl1V-vS_`YVg!F8C4(cEDOPb(gl8==G{h_lo*N6*J=p5B|<$AciT3u6QS~i4@ zvM^>O#X^e08!?>3CQK#O0*Or}&%F3>45SoK%eSvU$Us);j=R95*&9#B^pe9CB9&DV zP?Mh>~Q;kH|s)!UBS)CJ2I+6ez9i zq4K6eQvhaz10yWN}e;(l1M`j9qo{)0-l3Ze^-qrY}>2p zbZCMqjEKgXS@7t$B)g9D4?<+0Psk8N$O@FOFC;M5AY)i3)S%% zdMVnS>)g{+iy=qIzsynLU;-!C?A5NNymf+)PuXmX0k z@l6~7F*DJ;D9?%j!JuKclklrFOcBMyRX?K>?qCo&9hV2Ig5oODV`D`k-z9NJOb7%~yS6C(<*xpaNw)@E;mnZb z7YGkmOT?f{BAy7UNcXVmM-3R8*)>Ru00W&D6)2E61oWYNBI&K^7P$HJ>5+HYB@?WW zO(n?me&v_a#ub=elHR6%vP;USP2(3Q>!shX`bYeC@lNV~v=PcZn67sGU?K^W`KVgB z15-jp|1dh(EqyLYTC=(-e2=VWyitHUJV$q=_M+u8z zhtm)PW%TzeggYOmA-gMo$h}jZmSw+l>=MW2>!DkN>Q9GpWYFgc2V+1^#>Na2 zbuIS{1rf(5@n9%AyA-t?Q8ojhgV8M>WP^VPXV1b9v-ob`rukwlaDTdQPtH$f&THJF zz^Nwg)r8Y0BtKCc7(#IX_=sj_?=e|0C~z|Db&n=^V9Ua=tT2zbFtfbn0hZ0Mz0W3s z8akMUSUo{=B=A`YLTl_5V^iG|8WYt;0He(&3c->9)~z&i!a{sD80N8?ZIB+r0y}bx zM_TxZEzY>$zicsOHy`9bCdO*HDto&=`WU|M%#R9elgi1N&Wo-OT=b0!k0 zabPD#fquw|s7a@>IN)FipQAU2Dezi^1Ob}NdwaWDV(fk9Z}@ZZ4A(Gml^C;yz%PV~ zIeB7Bs!8j*c3l*&rimlP4(A#H4*1A0d1*JulsPUlMW5fn&o(bhEy^7rqcLe^k{Yp( zK@gcyrB*d0v%28Anpf3qq=~WQX|tdQ$kk;61avjh7Gd^AAXuLdb#Ti`Qlc>WZ?ZLt z3g>jc!3}F;83z95so^>jNIh~OrbL`bz$H*NZ7g*b>FDL4jdkEf@u&kE^`7jn4hz|) zgV}jpL#HjvnR4N@teetCtq0IiG~Ove0$ga?svm|B*dDe8r^)3+GjvFsD#SMEGakXF z5MW233XQA@qGd=hFSJIZfi;u_A;|903Iwk{hcMhNRS2!;4MRU2V`Cda!^Xu!ACZzs zj|iE<{YKBIDLSK6A!lQIdj)!oJ5lN`+`(~ZG&3DYx}&XQ$ViGBwIbcVoNE2rr>(t7 zCTS}iPy~F*IO#|ucYMT=Wp=cqSYYK#o_%r}fg&_cc4y9?>3q670UT6I2Uj{2;M}#< z(SwcchSdacjEWOI<#HUvq_er$O>?9?0v1-wZ2BGOkIMSyaa%@3&AyxFqGdNac2puT z+}QEjUWStC6k~@Z$LJTfJn5^3qgFha3Bb|c7VjuZHfwGG>#PUd#dvAh5;{j!ZrY@{ z9CcWO7LXZH^Pmm6_nAASd+x{57sx=<&vz4+p|n_6$JIA61km5eR}$%sz^b);_;dQ^ zKLW-+ZlQyYBBgnX((Y%+pyZ9`E|hpObf}Tt=Q(~MP||Va+CYa25Fs8$ek?TrQC=U| zN{5RZ9cz$uI8zo61fpgi%%MZ+%Q!=W;V!Zf801LklA~luVY6fH975_iqs**7i~$u z6^W~gbQaPY%;8uD8Jt+uO6fU6S1w-EY&4dH8K@{Mm>(WUiT8o%wt9?&C51hzcig2i zZgub@AtYX`eY7sBr`qxi%O_GHhjmBebde$_$g(Z{)MsZ8fO`3l3ejB3iM)D=I8VKJ zQVM2A@W$O_5RnpHb&R<-ItTKc@EQt>r_PJ$pF5e3VLK^3%qVNfGhab-?lw{KL}qes z91SD`-F8Ta7&0k57^PCmdGY9&P@r;Waph7ek?K?7k6d1kv{wucv>nJ_!%R5Oy+7RJ zOQOMX9gco4RRWJx8deP;FTJ}tg7>~pYfMi=B|HwUEVh^)>8?t%PZeDyBS5ulL;wr93&20W%a0nNdaG2?>DLfF zTUjNmvPsQU;HZVed4px&{E}e2e!83@K$3Z915z1!|tpX&p`c-5?{q7BBrhCTumYp4E zu`Fo3sh3n7uwbKDl@{FJ_BthvGsxl%Z>r+yXcSMcpjz)KoF*M9LPjY$Kb!uqAwy^| z8%4+Cl}1-a3zc`sBF@)~Zd7LSeuWt${EMVQzI0R62iasu@rJL~D!I%0s=iuJ)yGq_ z1dN@)%0t%Jms$1W&wM;cX$mQ#&v9v$LL^9;vT6S;v7^Lz-Tt}NT!=$#YqE0Z)4QMK z*__|q|HJ;D^*`4CRR0Upho>Jpebw}J(~q0}sp)4;zi{%^PdwcGUi)PK_wagk?HloF zeFAU&Tz{JP$Mb$Q?+@qwHr{XL?K4Vze#y&tyZh_M@zy`)^G?{YrX6@c#alnw%XuI0 z-sRo0a_0^^*1Ws_PM+`iN3Gl$on>rKt(3l zoQeY>X~$JOM~88Wn7p_*Kqx^)nYL$9k#IA>y6|j{4UZv5z4X0A*bg?izXC67d%d0FE!N9x#$3;YkZbt>qs(@gaIz;33cR; zdL4}VW4<{XB@cvwxTy|}Pzllx#ulqHa8R|C8k9DSDwXO()x%k3lrCgd2erelSr$~6 z5+S(7hC7KJijbz+t~OrSWe&D3<2Vn98T&P{OXE>`cyaWB-jBua0gd2!n+JUP+|5g> zRh~5BdDY5=VK{m9^X4@^Lzu1_pI>$X9!H<_%h3&}KX7p0n(;SsR0wf`>7Xo+Rg`t< zFHM}3K2qB`l0MhcsA)y{b&$~ExZD$GIZEl}O40N49eK9ea?}xMw$v;20_{+}bzF_@ zY%Myy$V8YKMT{OkTbVJBfI_>RoZq}fF>%gx*#IkTWH&pK2+8f*j%Y2OGAVzjC5RKx(kpLj?>Y_msV(btGz}=X8 zD^>ue3?K|MVoc8B2~J~92vSW@5S3dqmIQ(4Gt&Dc2|O4U)!lWj1f)un^KD#%E_WGW zcZB??2|=7pAsCW`p-#qeQ+LafZhMEYj@<6E@<))V)tU%oZA@<81%y)o3nO%lK_*W_ z7YNtnDdlU+X{k{r`}1kLT*Xv`T+9uTnjplB%FQRQutkXGA-`}du^%YrsgI^)(u|-9 zI7y(7k{ouM;Y_!E0x~UC8!E0N#2!H-M}0k_#r1Z$zW{JN7ECLS@@KZroh$UUY;Fyw z&YVhQ17u>+NkV3Vu@*&=PFPD7WxD%CqIcN;$R$bG&pt9@eEFbwOq7xdi$wZGvNm{Dolor6_o|YAON;XrGTxLG56h z5NaVJ$^upOBoi=cmacKaE8$O1(P=UpQ^v9bGE1oPW6g}MZ;RNVIfljBK!m&~ExR)y zs<@0SZ)|g`PXnOBtL&^952a0=)>cL~mOn{|VawJ=sGUTi%+R{mtz}iwJ#4YC+ur+; zEI`VByp~I~HZv^3323OA)6I3%0aXAyoD~$5?!>lakjKuH#-uLb#7=|1q1;S)!Y^oQ z6T4GTMDs86QJ6odi0+)qp<23W3VZL+zQ&-dni9ty69q#(=B&xF&p9}#-zDy>(?LqE zYEs0|v;Hl38U8B{k5z%@i!iuR^3&0X+3fjeSfkhWuD~!f3)TSN_(8>A^_v5jcB4_M5b#NI22S#LRIG2#IIl`OQf+vNirk(eH zO8^;hP$a2!_h%qtb_TXm5q&W}7zYZoD0!>Z55l*7OMOa5ls7!O$wzgOfOnm8L@8fH zX;op(KHzMS0z|qFf%Xx~BqP@%A$#5o-H$rtQ|}KhIE%L(Na$2c1?q6d{N7t&StYle zmhp_c1vDa!S`k)T!zf2yMW}kjKX<_@%WJTjm24_fq@DVN%UO#iBM#lRl}y8ncOlzP znc?He8Y+mw@9U5(88+AS5|yOqV`FeCN{nc1IX!`rt&9`R$JK@4OP5NCsD&CB=2%?@ z38mh|8j`2OK0< z00@~p2PI_JlB`T3u3rxZ@T(@)feTr-l&SPZrQ?sx$m0;O!hL-e>~eW1&^I@)LZ|=~ zPi04uu_$4X6!~0O$uLOgZjcS<#A=U#3a2;9J;Evc0pn_?1@akP7$7hmCnCLUq+7V> znh%#0ujEU(sb2;N z1YCMlFzfJ2yhK4~fxOUzSE3?dK==>iOF0I{Apmx<$isX=cma6^^VBQxDM)#2-F)@H zcXshj!3&U&lqvpPgG=J>?jB=41iVlVwwz5Y2&d}VI5ZEu6>y}41>##Q0J%+jv30~l zmjd#Dy*hE?dPsdPYDocOD_hjX%L|xziQpyJVAtCU+Xxejty6A{8}nRe?GQ04I3)5I zQ4eXBI0E!r(B4S*8V?@keJ%U5E`nVB1vILCEDg|CCe z7p}MCyG#8NF~WW-EawxTM}gxYEcd~(4?J`Wx>&SEhbg6aa9pw&*PlA5WJ)vDp2ecB z+G>_)o`vsg%nmv~PXCKY-%qOPkvXYjsEW14vd=NvqN3D#En|90Dax~SJT^QGt6sP| z%ZZ{>!p@My9F>3Kka7qVb2-@3i$O??j-Ys-=z?}uV^Nm+SkLn9=+=o9lf040MQy1x zRk;MK=!%m!O=ZkeRx|2=(~(hqZ3z20=Hxk@Le;!gwd@q}^@$@SzL5F;R{uYU(0M_m z%otuYGEaWCKjE?2QR>;~!nlOAuJ6a^_kutkN4Hvdndc7Q9QPU?PbPA4F)$g_b|u1C zw0(xajE9azF6^_9G03(bikZP3ZnVtGJ_Dp@gWS;!^k&B#xeGX4II_Zwz6PC6l+k@c z!n##slk=#<+tk^+(}CR$<0m! z+3$pkv)w3$+=&xAou;Xn(d9i@2rJDiUl|_*0S-aaS!qIXKy_!`F%L9wK%=qlK%oi^ z#}NXKYf1Anh8xC7f(jCQf_H(u7nsyuSzGm?IF^kacIto-paeKkFij(6%4$Yw8pbt4 zhJihr&&084N?`@R410x^&>2S3zW4dBMw)ZR)`>le^#x~&X>3JmNN5rO+rTHLgrJ$a zbTZirG79%IH!Ws}Cp##`FABg78TRAugPt@nYu}&D5)&{7C@f^S{H#V@iWI z`ZLf)GY&!4R^!Ac#cHt0FqvpJJx&~AsDdP){RHritY#HlEVA+}U>#L>kb*#OxW3w- zJYZ4E=mC+({>**nXPB1+taNzn5N}*V8m_S^T@j;ttA92)-MI3aet&L;<%*;V z#-enToTxCRC$+K6QJrWxzg4>U)F?E1(doZ2b$VMzP)m7Hw6hVWJc0PeJ%f>{Hl8F&`@3S9W|(b2hVrrOh?Z}) zS5K~u8WagVW3!@(NgBVY)y^Y6Uba`?a zbyCBHyY=Dx`bIYDkzCsoeqoG6Kq$~GbJu7}t$897oz%fRm878juwE`17GlY`2*I_Z z-^3HBt8pZX5~l;gH@=6_OR&_g62*n~u+Eec7=2jV0@>88!YRfS=_L+?xfF<4L#01e zd;cJZb09yx$g+Z5VL#3RiUYe-PsUmsbfv`XE=pF zlp07XyV6p;JCd+xGzIgpq|~AQgea@28$3Ri{an%B~L@uOM=O_iK z4gG=m^?rELIo8ogweXIrC@UKW@whpYh72U4hb;?`;8;=8Bsh&0i-zEc4S%vdt6h-3 z8E5U8(~SSzs=05`N;pl@h8#-;dHjGO%Z4%1%KPr3}(_+Xb64LLJ{;_ zw*5+4oc(B#dqhDY=2KNo%0fSn5TkiVsd1<>OGEi6>%7{hmP!|w6*j?e)Z1~pP8Jo9 zKl_)5m@B0iq{$Il&b?CocK(7!ozAGlSfG?AMdS;&`r{C^OT+NqzzUhTW;LNT3^=-a zRiT;Kdi1Pu#49ZlJw$G1?M_Nk^?uUnnAXP1$fw_Gl(OdUYSg$hj>KrsI)sA-^cg;yz!j0o4i+f<56iZ z^Y*#LYb^P?Z*qQeiv4N+koUy3S&OG3Vb+g8M3@moo;i5zcVSHsh8BK-T+bHhT_RR~ zmUm$p&rznl2)JAidf`14rBs=9oG0 z)%mnksq&R4R5(90s|a?+4)dRX!QvcyK0?7#|Kq zY>Hq!z7*@G8lyP^J;n6L2TE3KJnjoi&QY_{w)Nl~N@Y3S*wEGJ0m|`ic!URp9b*U< zK!prRknC{Eql3~9IN{4}l;$jffzm?6Qfhs?Ekjp>4w^bTKNw^{w2d)0c{o&s-WXLQ zTLk31|Bk4sVX=!$DWP}$5y}s%S7fS#rBYmjU6LoDUQvMV=PAr5+DWJfaM_t ziPI zvpgnK^MIt`rjv1nxZ4D@ug95?-xVId$SYG`MVN0gXh^p20_es4;Ll9ApkN%%0qP&gKYj z*WEG)4T*S%S*lYL*Q)mXPa?~fdo+kD5Sp`Ui9XM(t7~jlE5As|3K_Wqe~Q?!a&t!? z53&&@*1GwiNLn!8Jb_SbPBtX@Dl>x)gaB+(L6Z>F!pxnhmT6lBSXYV0f%)t)*{34I$F2=p-~D_|^w zdtRmyWtbefMn!FMmL+X$v2TLxE01d)!RA0&h=Av19T+R@D!$F_2AfpW-4Lb?jB5<><#@+x@Aan!kkAc z49sMkr3j4MZa6Y18JxQz#SI0n>%W)EYmp%`Rx0n{rNC9FG*S7Fgx&5-_ z^{B>JjFk=e!0XxM&#Zsymr1>j5#*0!00Bch^Fw@3j0;1=-Lyp+nmcUm0W4jA z*E&wRr?7-F5Eh{UJZ?gcDIhKg4FSZ^0^fuY`LaMj6TOV=c^tN9;YQBK+RColpjJJLXhrMkW+i3)r2a{;XFZQ z(k(S!T6D6ftvpX>!A2ob7Dh(UYj%wLe!&L$ps9=pS+7N*73W&+Sp8wLp;^lMU_Q#g zBUzle&n^kXiL~_;F^&sKqktPUUMP@7lI)H*(eeQJjW*>(6Y1d z3GWw|A?8gH4k3V}QQFe&`?2Kml!Xas`)n<}$fn}yvx@pEL*Eu-C8G!;Z(pGIB*H4q zC84$iq>4JtiEfa0-{vgKQ5+?pQ>k%~oXj?;Q8+*~4ySs})fnW(5W;6js*eO|j$WZ0 zE9wX>4E3R^F5+B{Hc+z{X`nEDA)(70IUHmoX>=uM(A*cwB`kDPmC!_J>skhqC`gTv z2=EZsAhP3EH(bFcBbEGCXvxjDD-YgGNYfkNSdt}O+nZJ9&pc{u@2^{huD@=p`UH~1K`YLm3&D

    oDc`xo?Cn*AJ=JHFb>Nzf6z#83cA3w|>|quR8v#ghi= zblOMVMp;K?4^$>5CSeLlQl^2FB#aQ>k}{woSX7US+CkzjBgZ(geaGP=r9R3&%dKq6 zyUgMiXWxa*I+k}0U6Et6EsO*nwujmh7Jl&*Kmu0}PTHJkfJ?(J!4q&ugusfJ6ka9* zxru)|5f0ofSCCSiu+n1v`3=$k2wdFAX2kBZg86#6w@ZPawJait`m7tWy4_6N>J8U= z1~e!bqA*W@wm60`3GR=Z{O*T=#<1~wi}MT z_s%@J$yTtD({?%vf zDa|&Z$HKUcOvV9&@3iGS%^DHA#?6B4jub4mxP@SuK`?Ym($`=a-xd0G#92D%wBKWE zjmUYtkAG0}d&p{%ECr$7cy_rNOgh)yVPa`2D|kr@LF@$ZH(di>U-eF!wobx^aoCag z`z~r0LHxms{!agVjQy7)B-J0a@p#YVSk?GZYpT+ccD%El`z#;f5W&8}7ZH(>{%1?) zz;%n4c^=+y@3;D(BzLU202!+m@4Mapr7u2FOtL1vvPgkt-KH?fLpYEVA zv~dEsC#cJMhWmNPX5J{TdGq6s7deiJBvfJNl}u{Be5o2(KyA&i4`p`eH5a8G43J5| zEx z^p|v%K3=q}V08|c#kSFaqQG-~IAl+QcoX=xHFcaS!WG`xsGn4L-(?M~(1#SJ;_*UY ztKZ@&(}qxw4uK}3X@Ya!??65sG>KKAE=qlwc|t2uWdmUImIpGg#ZE&-&y#{X{OLr^ zOJ7BXMDa_8a3)VtG*AF-!{~p^j?)N+DG&)*Lg_Cq1sYvK0E;@Dz>-tc;dq#a>V|u+ z)fK~A6<9xL;~`)w*KJ1~gN2_6F8XLPBy%Rerp{RFZE=Fw4w8-k4eZ@PE+N3S(BQ!H zXs0P!ZqUj+O$`sH+lRZWgi8?RXHkcdp}_|HL9p|z-!i-!d5VW|5U7=%vOleLgVP<# zXa6bOtmXQTbKV`JkzU6AujDw9!2?1CcSKJd-c9THBpx|Pn=Sl14p#;OlCAa&J6(V@ z&=6OoSnt%wp#_T=-k>|Ms93Wa3@gBu>?}ikd-Fsk8f-c zaQ4J0Y+Z+^4klkjRqtaKcJ_y%{sj|c1md1raICdm_Cc*(XB$FiEqHb|0p7( z%<%Hwv3!X|2)GOt&>u7O7PY2N*P9^o8tS0dnvx3zZ^LOU#Qb6qf&CI8 zn-TIf2Bup^B{)Q%rb{52bO&AMw0uPtp^!%8vAFT9x_$}1Xh_V674*pl;`i>9$(ys5MVegd>eCc@8%?_pQJ$rB2dq2eM~Z z@NQ?72;KBFhLUg=R*@7*+3y|e=&SU`*xHmH+fb48ol$?*33rLz;{Gw6N5>gi&wiGVvk%1q=-BO6o(Hd)Yr+?V`!4$HE0BkrsaX;~$ z*(n#6(9(@%kT|>!DF~8QEH2C7tc2L`%p)=Q5n^A96uV)JYw41Rs4=Sq#xp;BslhxT=~nTa&EN7dM6# z)?2K7`i1?sPCwFpp!;bs{8!IjKKl!^Z=Zd1_KUNR&Hlsew^qLO$M(PxF)g6|gTuuD&-%-@8JXDy`rNI6H=SE0y}@M?__&FfD0Ef0W(elp zjSQi#&mO2Px}iJUj9aA!iKSFMvpvx#z4Y}c?`DL058%@kJO+qlh^}8qKo|gajRV8u z)1Xji{@J_3{0f+pUovyGhfr&BmzQ-_Y(((o<x^3uX*g^)HzWg>JKIt#7g#B@@zBuRw9&Q>Hg9!`QW^oWMd**0Nkv|!Y^btsz z%kp=4OH^&hPr6&b?=q0YY*Yi;I2v)Ezu3z{(@xRhYMM~5H8VoN?|qCO)z_-fyT%AB zQ0%*~Pn=&D{Dkgw#uBNL7_O#Cu3jE4nG1Yu?4vgjYJ0Ii%8T;VSV*6Kr12R?sL_<* z20!EKE*c5xd{$F6*?N3Z<&{MVnW8%(I!MH5$FvcL(P)M>1=j{;xmho!_Xbq4;V7?9 z^ev|^RmCk zK}9+7tFltHo1Q!G+%+_S9b*t^S{qLb230(HTeE~Tl!5ZOXh}}_pyHvHY#ByCWgrS~ z=n#$SsIOL~@jjS?2_@=5bx!x|8|Ac+V2%Ja%?41RRzbW!6rfmF+|1Xq2rp>xLBYNC zjPnIrVGK2itH^o`kaFn4deAgo)QIc@qxvC(oJB_W*`%xu_Mq6>K_vlhxp<;DRrA`J zhH}j;%4GI6xnnb8xh(Z@ehN)9`XU;3!qH+cQR(3Z#u}!SwG%`=V?}`3+7dAXNWn6A zC{<#=6cx>?=NM1{8-cJr7|kXtMbV!qQq&SWg4Ky;B=Q(^WFDS^<4g_>xOux5j1791 zqeQj}G0;Sa)R{yTwS#J5egIr8tB4Nw6ajd}3k$BDuPRe8WkG~i3@iFHhe!bx+V;g7 zLpAUghHyR%dkal8&3wtKmtYEHavZz=p zf(XIG>X80fDb{PK*Cu}`IDY%J!0(&UJ}KS4#Y&m6F?`a#VB8nt$I9aX7Sw)dmVR`1E5pNM0A5O;TRI=AW$)a(@lyk_Czcx z+Z&7{P)k3-zeC>^>l`DV*2dsFkPPLRO$fJdp4Vc6jFrK}>cDHot2&{!k@XYXR=c_o zo%?lU9q|BqlT)j-!Vd;um&_nZ67aW652PKO`6p+3GDC^W$dM+y7g%0`AR!Nt12ijx z@r9;9BnB=cHn^9V9=U;ZPhSG!$#M)QE89|}Mpn9vz86%2$SbpK{zcX<5VFf!hP?r^ z0>5tm2xgG)N@Z83pMpGq`od62W z&T)wzq7fB3p_^tWP2`R=Ueo}@1t~*Np`BLcF)ajT+_kf7HQ;yZpt&fe1HVO&Sc|fF zg-0NzSy?2M|9lF{<*sHFNiS20W0gNN+PH?KCpI@mZrM>Nl-gN8>=C3hR!E692$S|_ zX+{}>OdFd}J;dYCz^u6(AZKia$T(TX)&YmfMmteTk%5qbz0x`?gF`4>-1pDns!>ES z#@eCcj$}j@cQJuE@TX%3$+cW?kRJkdyw4WKEVDYTD;K>vZ$VlD`eeV2q1EiE6DPim z1XySnu$N7V;UR^=S(?b%L#s$9=h5f;MHZhToMb1IyMlfiZt z`;e^!AL(xv&yA8t+l>0xphcFP#Ili97Rm@>xv`E!I_|&>lIoKQ@pkg6RlO9p*r|tX z;q555lss3KQx=Wk&>AjSbGu^rgNUT!^ua|>_K@B&q}#%nn?EI*vbKvdtXgXn{0J*kA|J3QIjyATiUx0vpyY1A`#ceiu~( zj}RM-q>qwU#&$Z3uecOIy-jMQ=^9k~{aMzQdc!$A?>cHYh#?7ySwgHMNX6b&qM#*? z3s5R^;xRILyta>$I^K9xj)6MXD=OVg?Z~Yom;63=cNd+(LGTcR3!Hd1_@H zuaj@=Up4)S{&(;*`6SyIU7P*c*|*KUbM}3+x2;@#a+k;*Yb*2qxA7V2h=n>^dG5-r z|L6Tbo4#q~3_dHH&6 zO!ygt2l;4B$!;WfP?$tQhnu}h9)GM1;o%W)#wJrH3ak-0AY8rPLZiwO%8ZkW+w*Qu z+%C81Ub>xYRS?*ggL#o@A(}s8rnwW+0R}*9xl>dz(gjK3@NyZ6sqXA`m@qM%^K-tb7k_Y}~(C+)f*G>_vocia4G*djA{zq4Gl@G-R;! zfKaGM7rj|lA6@p z1(ED(@kB-$b4kbw#if!*?ndKOiGl_YVyHySbdVY?5SU)m2r-<6c6m_sl9#bM%D3?5 z>NRdcN!&}Cn}$0LXEjBIlC9z*xN>q?(OPSP-NbOMWx&wnQ(ef`NbuK~Xn02ZWAl07 zUfHj1+->TBh6r;^I6PgZmKKg z@uceIZ`@hBask2okwBp(yM7Kj*F>x1O7eR_sRVuZHg{vr&M182R{NkrIlw%4`bR`l>d5s=W zGWLM$H`#Vt_n=W|)8C}jKU$DZW`OTX=z+J~Z7#OQ%8NkLNrpDB8X1KN> zw}^y}JK37-GhJ~ef4h{JF7wl|mH;kz$7bG-_m3Vc(Iqqt2%U_T32q?J<@7XK59lv% z)$B+JF7oB*1>omQU>-Fs-@f+osm>eNHybZaqpV>HKrswX_8rOn1^OFjSbp-B1ze#$ z_%_r4q+>f;2QO;PCx!(fuNzttoZG^Hp%uXRbG*i*080z1vh@grU!Mu8C_IlC!GS{K z{@!dwkjbA>4!poy7em0-wjnim#Y?@5S~4Mv`VgZ)X(U1V1R?;{Z%iNb#ViuXO6A@+ z1B)rtrD9FemIX*m#1Vi51P}>dPC;t4l^9gez0@yO@wkCoO)DV@JT@K&;EWmsxUZo` ziB*BEHj(&~M^q}R2Bvm)JH9S}2>61gjl+rLJxn1?Psfm@_lNS}tSuapvI=Nw3zR!H zVJxANc`=DYyVPU^P=vy&0UVmLC@9LVO%Paul0{Cj0|bwUEG%vDH}X*^)``Nbms&BJ zRe~*YT_uZ*Vhm8zR5y$yB2eh(OeZG-8_lfjObply>Bz|(txyFsgH1sD9?Pe> z%+>W&8U+>hQLkcLq!2}Ki~vl9^spK5WGa^u%GG>`s~A4bnC?y*WrG?b?OQwBaz6eS z*mQQ4bXtr2Ll|=6BBWABDNXZYg))&n&_gf^=;Z;bOh#~-6tcqreTcw}DRt})q)D*i z+=Z3#384=8^HCl#RV)SUOe0Oh`)CdIX9@V6=2B+)m`(JMkOc@KMI&*DBi-Z#{sqG> zL12*seSRQZ@S$^oc7a(eB!pub?9&trllJ#%q>b$@0{!87cIK?2f_(@PfZeg0#hRv2 zr(q~E?f~%5Eh3++hh}@Ig>7D(>)7h!&~mvImk?@2nKVwq=VA6s`?~yeVHwy1;=y< zn7PV(B>`gx_v<;ED@*;zn3yu4E}qjiEyxu<1LrF>h0@T&J%D8^si1 z$3dJmt@;rE$wzP_!8{65b<9^>in%`4D!A(fE4_r3R&qyMHm1z z=2W(1Kx$eJNs!I7WzkA0Nd-&IEH=@M|IKxxo=V};PWhahPkMcdP6<_;RMg+W)Z}J$ zsbAcfxJ+N9Lpbh|k2%>Y@=RQ6MaZAGVDUyA%_Af+Ay*mldw~p)oz;2X)5-``$f&|eKyzAp94MjA7lgvlv;^&5q_G{-H*H5-LAjL~ z#TZ44I`o^f8MZbgh&MNnkr)NX>~(5|K)WGi$bbYIVf$gkCFVmOEBi|0DO^kQkPjKC zXh3nOpivnlxST#KOVUEzPI%A_#%z=ji;vBwQdSeBB~t*eFA`{>bQH-zK0F1jRNO@O z*cIgkH2ZQQ5)x{_NsVxjTQhL+W(0yh=j>=sW_$4N zN+a4eqc1pUP_8wE6gZ=eOFhtKqU;|yaeI0S_2m&Oy5{=f`2HT+jTb3RQqq81d&gMb zsa-9N%v<4v3&|oHI@ie-+k=ztQ*drFvq>^+sW>1D+$nzcI~321)Dv1kCV!>nnub(k zRC7ZJU{lPzCHQS26lKs>>bB%Q?Y<1>!pY=oY>@<8{Z4aS!T)CM)9>%!-~FNf)%~C8 z-_!rA{=@w*FsMF%czXC_!z)+*{*#aPuk2sBa@4=Df8ol}SzNT!FCcqhTJW~Ll&U~bS@E)C!|k# zDHTK})|uSG_<*74p#$}cKF=PWevxs91u34c; z46#|eUK3!I3^1fz5;FtW%2ZYKW$})g6^tCwWZZRe12S1JOB&3(tb$9dUDZs|C~bK!xXd8pYV z@VvRI!-f7rd;#`tj}V^+20wAR2&-c9aM=@v4~auat%;Sv60pR=_;zp;XOD$78GT4h zrqMDudVGwk0{~X`6`GYm~ub33G&1uQ;csGbu4eAWddS zMVhs-i`*m9JcHlT*p^pjw_M@(z!WzmtsrI4liZNrmM_Kf(VV*UX(R85f}iDP7FQ*$ zM(_cH1rqG2GP=23z)6Rs(#KxOu>^(3nn1#v3@NP!H!o<7RGd;hTO2crE=q0@1K(rd zvf(6U^m5A};J|`qW9h1RM5qviU?gKSc1&MfFB<&9(NTbmb6x?Q&Isk;i($qR2jAJM z4!r7o%S1i(W#t9Do_SI6M#Cf_wulDdi+-}0y4B)*ZoWFZqSLSnCT|%tas5q$J8BKQ zkTT2QbXiRu@`hY>sjANM;!}1Q=4ngeFYkWRtsd}hj)6odn#}!V7CPji&HCcZE7v(y zbB=C(le&fQ5Qla&HzC<>o(BH&5752o%axb5+ow3KW&&}#*p5lJpvOtlD?g05r-53!T4v3ppdFaf;8^gT!uWdxPQ zNktG_)SD4>S#i{zL}s*d2_I<;inNPbdDAt9#bGQIR!+2#sz><@7NLRg|3=+a)!Ho# zDWA=Ma*RdIsTLQgBs9we_s|VPH9vLi5W`O?ut-j09Y{+BTv<niFgF#0fSw@{myEVHc_TBD$rTEQ;NM)`?NZKF2IayY0 z`EVtT%vV2!!PrfxFZ}}jb52xGs8-x2Cs=26KYl|yp!tV67ZUMWqr5b$i^MeUP|Ib-aY`XGxPmxWcPCa1+q#gq1Bo4k*+x`v+CYwi z?ZsG)0)xj8VTE=Kaxc6{CtmAysUa1)P(821e~oX=b&gEQTH6V#y;zQYL+uwzCcW8V zuf|5WtsL`H!L#0^sNy|=8IgWq$&MZsK9;Fc`ewo`nQ0y%U;04s-v$F4gNv>W-Yv8W z8So)39-v)86KGrnti1tbM%FGuJP8X5k+}FmDWRU`)6>IJgI7F*Oz%-xz-{;dus2?D z$b(1Rl@O{5pPl99%C4OeF(fOcj_kAzVGVTP;B>~&p7yU73GKBFjU*!9b|W;vmx^sm ztcoBs4K37PGXb^W8C-xY5_}~O6-DS#P_*DknkG8_#}aAZgax*WR8eoum!TLE%Ad?z zh9zp&2WGt$2#O>#$}aai`~l3JwxF8h?O?oQ9oaJh1i@_t0612^Sx&?IOTj2Auz*u% zfqpFUh%UljaDDo{^5k_4A1-L~4I3+$^fBbR?1O6J9ES|jCAsTwiiTF2&GCG7k6+xt z!9}{d&k+_LINux_&@K$j5W7^7|pTN+nT^fdu}=}ir_L}pIT{CNi)^d z0MX~k&jlz)E zQK7SZq0LTS6h}ZJZu^Is#B6A-dF!h?Mu%13tn(nh%ZX(0KuxS|_oY0VF!P^PHA>_W zkPO?Vx|M>8eSw!!nmPC4KrO&cZK_r+G|N;9oXVXxT_0D3ads45GXKbE7yUoDj$jkD z?gU4QBx@SDQ+Wlan55)^+CU^l6T=|OKvD`D95m%Q;5O7=Yy>w_as7|;J)i*CVG7{w zQDO&PcA(#X1&{C><13 z9tmO$of4Kal(y`FvITqOG;t!9j7=3}a;pn@W)BHVsb&nu3E1Jv5eq=zRzhSfVS+cu zNpgf?8K3YvBrn_6O;+xt5_fe;d9`63adakk^}x>z{#_I0%Ul3|tt97TQ__;sv&Mix zSjo3!8!^{(LcZfeL}|IkWHFC0K|i)@BRI3epdLiN;V*gsoX+EiBedxfEUvFujUdP-_G@25K8?+m(B?qk z18PN;oQ8v3|6Hs5YwjAz2lHx^i(VbCQ-$ zyQ;zKG@gaan~K;?xke9w1G*Jxwa)sI z3cdhGJDum-#A21EE=t8J7M>{Gkcqk^s@wS2CSR+OTw#!qi`@9C0}izD&dDA!2d-!4223i@2g{72BO7F$wDa?hG`M zX7G}p;aSMSU34s45yt`~ONEQZ=eGjq3@JXclz_6d)h7U>1+n;u33@O!Pudln7du$Xcl~4@1RHu(WGC$!CghXD_xK8jh9w;tWRajLk5@0Hi^9k!%8^Uuz z{USW;=IWvp#N5#~lhkBglWUn!u@X#xkRWi<^)5j_T!74)e4)<>p|j2d2_zqm2q?`} zBZRq<9;q=fA!@4aVYRUSz&zF{YzD*C7yx1Tu&iR80d{Vzxro3jS#USGjV$>K>8{JA zT)R3U($(*OpTx+&ldfNR+wcHE_MN%}6YF1~B*2Ys?P(YotWpz2lMCcd(fkF=L2rR) zibK?FHFAYJ7A~JDW`g4~;Z<=7;G)k?H)o72K%4z0<~3Js?_>QLqXzy4im$>IqSWsLm!Q@}_wPNBvxqh>a_$F1KprMgw34m#^LQqek)qwsA;sMm?Ll^HDRJbqOJ#a3IFt*ByaTU4sW?y2 z1T;CB+c^*r$0Z-8Q&h!~1#Zv?wxpE|(Xt{z6 zX+o%Qs|vfmjvxWuJZu@sidrdNyypVf$)iyEbdIo?-pTRw#_&a-o%mHJ(g>FraIAtH zAggQ?q$Ptrcf!i#kbIz-Y*J0mLv?3MI7?4!bP`UZi^ zq=Y%inENSIDyax>6JMxwBZN4}KXO8&VmS{7865g9ZbH^R67Jhw??3n{ag-z?hY%tL zVT-UkJNiJ9LQdT>1k9%oH=?;iucA*-Za}~C((kV9 zF_ZjM|CZ^`Oh49r5<&j_;qMPWI{f1B@!>aTe{S}M*SY8mt$!invv^OYIg1mD;PeU-MD9kjshHfFfKVvSxoP!UAOk zP}U0MBf!8i1Y?_oy;ZZQ+)~*`d-snSD6!&YCy}6m>FIp&IU|-_iecM;tTG?*i5b)c zVZnyVFe}*g#LdNg3KEuoK?|KISnL}Ja;X4?h~K;5mDU3ldbse*7AR-VK9s*|xZI|WuZlGWjL~5$> z1AFJp9;ex0mC#NQK3W;zF?=n9K>1pav*jYGf~(|y3HV#{mlgo(8e3XiXq#AVYhK|G zmRxX?1JR|H3_PH1(PwWd0L9|lurbn|YE6_GZm#Q>3lk{q&75)|r@ z!znBi=iQ~+sG+)Z8JjG11oB!P8WM#Ptt{O2%Zjzw44QMQ2iv+eyIR|r9$ZV$PiB~i z?1hQ`uv~Rv&%kPB>rjy^iJF2O(jNFi*d);G@)JqFBLNtz#Kby^aH}>MaXNVPN`N+} z>&ryobELr?0#@6+Y#&Sm&=Q0=b@De%VU-KFk^oVTQ6ux`*bB0kFM?Iq%X}mEEAj=; zv!xDm=jSHZswYR9ENFk0&JZC;CNeEUg0wQ{&RltGx^Y7Jf|8RCLJRP%U5w$=dGw2{ zh-pzV=UC=q|BBo;>04yt8s6Oz=f*K@nskjcaqARhJJzx)y%;GOzsJy-p~%{`ZcXRz zXktcw{V^h*%MmAo{1`YQJ#M0>&CMI*K-faRgszKC9IM;YzAfyg1}(GAKsE}leY3-= z_v~<(llSm8MPHh$i39-h>hwGNEON0H!K=M2b!i>V(fam?SB0_=U7q*87fFr-^q7-c zBcR8a+Uhr`K#5n`Uij!LC{4y*a5*o!IAJN(BUIhYVr_EI4E?JXWAnTw1Jx;&096t$ zWoio#HGudAvjnPEl*mGWnjJGq{gul(+L=)ye3lZ>awtXZZkia^C~zn=s32TrhOBV? zHXFgVX<-nTt^yDk%RzkZQOy7|pm06+Gi|KeLlX2NGFD{~N@N3yOqNoIgr8&PMXeh> z_k4awxuGIJQQ=XPM2iYcAS3gN8srpDc?@ugiP+lWGGLwL)FH)aa$x%f8x15baVn0* zAYrNb`9XVyp@8jRj=&3;mr!H?By`GI#hrj{uFgQaf<3GmQADQ4jzcRbU1;S-nN0;=f8 zead?H4|nEMLv|X|6j9bpW6QXRp%_L<(CCCYE}4jmIcwtpO>JD3V-OX2^0C-4KoFqa z;be@F+$sQ2jcu8$Y9DLb1p(|_Yd5)(h+kL++k29_$V21LxjCg@%jOUPfk3`>KI?^38#7=mkV1#ig zj+Qraf%g~^oRf!ZnY;$IP#5-^HJ3z&L@p6;yS8M(dDsPi==S%^OTZAz#(3`x4@fR; zhTJ-f`Tf4BQ?$oV%QyT3yX$iO-BnkUE6%Jf(+g0u@e-er8?Cg}yF8G;~@asilJ zq%#1+WfGI+p#fQ01JXJ;lRNxZ;z6x+PSTu$r?fg5kgZE$Gmm24fbNnw?OIAx@1ktw#G9H zkS=2q1rr3;Fbnoxp|Bo26p>8G%<{=C$!QEPnO%3#bE!+Zx+SOfcC^jS*_^;D82a%lvMu(Z z0BOKV?c3j?kZGme0IphsWI+nc&e0NNxm?j)72epET1I@%)G^OURfdmMb~q;WrVbpO z7{iDWTcX(Tw`(yntOAz?Ho2O?jRbaqQIz25%4nqR!IHzS^F+0z|3RiF&%?+_~NAs6cTQ!Vp$#%-1aJIEMlyqo6{&tJZ*v-qs;>K9uS!Q zg{51`<#$>Cpv8#@J?Lh<6dl^QmGm|-V=&!;2OvSNr`dfQC*eZT=1E>yCuF2TODtj% z?&==5iqMir%CZZj1M!n_L4JR^AJ5m)TrF%7o00@Jna&t)w`$Vn-w>_bc!s|l; zzn52(bxi|Xt*;nOZMw3TRl^-uz(s*Zg6#d8Y`($eHfi2mJjCL6kbGOk?M1>tmh}x( zo)fn^B|`u_kgL0T-8a9KbJ3-~7_lhcq-(AhACBEXx+{8WxFW*G zMxiA}XMY1y`kOEFB=D!!sGE=X<&~25xK^I(RsDM2r?OVShjLPxRNk7S%G9O{0`3{j z!hzkwxRQD^QEzQ&@JF|M9bL>Rv=Oxw+`qJ|d6Dce28_1bJLV}^HcCy}kG5`xM3{bt z-d?o|BO$Qz7n0{(YY0Dz(iAT@^qIjrY#<67UxQpMFD@d;3J)kLQnjt%i4VEiWJXhA z6Ye-L@o>py#qCVI%NQNURCEDL(A=f7j1+|ZEt6)=Dyvz7Pf$`>L#mrd8horaBBNIO z^0}hu=HN3rffUhvQhBp;;lv%|iu}7uhW@9UF3NZ0%Ix@k$eWfAjhQozT-&%0F7sNR zBp~Pxf_e$dNSs@Afr|l18zyd5oVWkgB|q~sW;u;^&EIQm0IJBdX$n968=CFAg~xDb znX?@YaSl20e7Qs-DnSYC~jVQ3fZ zcw+>LTgWy7ef?Vd9)DjbiPkK1so9Yaio0oWX9dC)hO3;#2#29jOIY;UzOCNrdfZeuJD;EKsCCeKP`2g4ul!*N&xRp*98qsx_W8H5rWx z{pW&&ql!Qcujax$sRC-M6ZLU>n7o!$>K0Lgcx$sA$-zE5Ns=?n&11-x$nB~s@iJv^``JLKis+$O1bCAVv(?jb3P4Y6zGJ(y$Nf>Tx$wtv^b_(a zT?su{uYPzbB38i&&95tVqo+9E4_VnJMgN!nxOTCSa6b&9*33qeAFA2#^u{~W3Vb+s z$35|YKg@zS2K(6$7N$WHi2^^#MSX}&ZH{R9fd#0c&aT1aOtPg=BnwYeutD$9)S0Yw`(3(I?ssxhC z@S>}Dev2S19SzaykehjAW0+`<;@Ty+hHc6)I-eG%G)RzmoBC8YzEk5e6GkM+Q3skr z7il6_29b%tjZCeYuqajFx1kjv13{N4?lB3EpM?G8leyx2Zd{AjA3A;dAwWKCcGYc~ z%|M#8MHF#zWXC=V2PdI2+n3lOh&9F~*_KUPn{kv|p095fBWEGA=qAD^k5Jz+TgZY$ zW{iEAzN@@TEe+TY$0m63IN1zX!j2dCN)^z*(T@ECda8_5Wts?Yo4Jrl!ucW>cC@7d z`J+@mK{P=hHWSZcRJjhKAl5G{Er0`jPyUAHXspe#Fnxc`S<@lXOJs2no5gX3IuMl!b8z&oG+up^ZM zmCiOKYUV(|e~66a+Q@8v6!t_s@{ooW7T{5pI3(~=;h7Fk{4jA0DB;y)-{TJqV{I8~ zqWzYt&^T5rJ60}aAivhOs0DKQz{b;L6$?woIXkwrN-*A@xxo+l&*CBbEoLb?0c?Z? zcpWjQ*)$GE_B6>OgJnbMu!g~tG!#mk(p%wa%@#lt=eZ)d%sAERQEh2fzPeKS&dw$s z0apf!<3>x!;|RCK*;o-D3rJf~4FrfbO{8r7UTjK@SEP(D9<8Z}bwn-1x+EwUeEPbI zaIZTxlU|nccoJ@E2oQay5CzF5!rxPKozKRiZd)Q*hrWwGTjP zmiLf@a39>Y|%uvnVQLk#r=nH zlx*M*scAD-Yk5XB+GgxVR3|r0^3?tmkAJ{WsS8a69_jQSEEY_Mue3EGoTfOQ1#nLI z@E)On@;l|89L(Ed|3rd%O*fy%prT|dwUj)c#u%~!W?v=N<@a{iFkb&*?yn6eh9AMN z=HtVsCa-?-P5qPnzu2!3d||iyt?sw_i<37`{^H7`E06XUS3ZybyDK;Ozq@i_*!lk7Jp50rrUB!iMlM4p!1kSV=SaHyr<(zmG%v#oIXkJcss*7jhUG;@3G|#qo6@U z(|8fbM>&3$OK>MYR5j zhdeI_ARbMEW}VR304fpiUlsyZN!MbLsPG9sb{8l%n<_rpQ`A+a+%07+uc9>QUBkK; zOe0k=yQz3$#vmhY3Br=uogfWWa!eMW#HDi+kZqh&7cN~*bPU}lvNpRdME^Y-gmhVwU9dc7gz>_ zAAx7%LFN5tEvTt%3m=o1pCP02DZ>UREM6Jo!v7JX#lI?s1Y-`wUV+IXO;+sjbHwS* zS?YSlgU|#~2tB?rf!0}@qWc4O1<)$a9*g}}dA_g{@Ta?umV?AZY^Id5eT<)F4~T_< zqBODuSni;tn^`S@Q3~7suL?^Gf1;I5ar3$wQB&ZW!EE%TsaDhok-92L1j5B@ zij{7o>xE7$oi)HD=>IrhB__ji=m=4#Y6NpZ7wW2!rlQxV?eqDEo@wRh^tWROqZ2TWHC~acHSXQ+5V-r zvM#+6q))fQ3q{Ts+m~hytrXp(Q|^~9i-gb1Ei5*&VPWTqbl}X>tUMTj7%rC9t~-G? z$^>?o*=sbI-L>9Is{qC|nCp0hW67rO>~$OmK4bNUKaenUkfg-;sDPY2C~i7IW&Q!_g^5LA-YB z#*ud)W9Yeb<#JpRTYttJhb=D|BgzS(5-~oSe6Md#6&%3QJTG0|yW^j_&++I+yw=qr z+=GMvUyDIjmk*TEJ| zN2>t8@8^Y$!h`}r&ZK=%%BJPU;R;*R2rh=*3(*14Mhb;&X43`~t>`5@Kj{xx`(#e| z3!rF#X;`Vk3m=6G9oJ$g)=-xf@B+Pfeb@o>p)ODcI`QuAPC2%SQ7MPaDs}=-6@^w) z#|vtR+Ch$L;A(t2RqvsTaR;SU&VlRPT<;iq65K}jfQ*`ucg98t5LLV1KDz@Sr9cge zi~;a*;o+S8Roa-w+Gog%WkO}KoSd8whfoqxrKV>FcSLQb0;pN??zo}#B|o5+XV4#G zBw5fp5N*TmW${$aV(DseAW1&WGoHEnB@Cr0gM^^AFhZzaJ#SHJkvu~7l6@Z9zh(9f00OI$pa6l zi|4}_Ly*Dv^HnuN81iI<0Lf&M7?`iQ&Z#!Za~VJ?H7fF%xo)5quF*>3VQLc%w|z&! z44f)*E70mT{!GKc*4Cl8-C%|@XBE--jhOL^ET}=ry#T&BhDQSaK&dUnrEj_GEJ@cV z>-XTAz}h*4m8OQ>&@bOAxBH7Jzhw@qqGe-DSgdpKY;^@%UJjDQJ z?x2JIeY45o4da;*oNt{?k$8-Kg|ZA1n7(V5*RIC5)KA|q2tw>zBkpW?lvMZ*X|c(u zmUWXPbUd>gkUr3vp9o}V%uq+Y)|sn?<3h?BHN$QM47-qE&3C5=h$tGaK)y%D9mwPYIqq3iE*an>VXAe2>mUjOJ8CYm61rlZpR|4r7XAdlZNgoD3(#%Ar-i>WZS6KSm!$ zP)%_N4^tlf?%wg^yL+xD%0$XZ4Fvonk63i!fd45F1;jIWObRJ4zl+$>8J*M?xWyYj zhzdY)m- ziafmGD-QCdAMdx#&5K8;>Uq`&na#w#V%I~F6d>+RUzyA}*uW^2r<3WzkC1mmm?tL-ezrgW-aQr04XlwWnoc}V% z|IYCd(mj{&pTfp)edP=K&s$mG$MLz#T|+*L1>x??7!UH_B}xrGs-oYg${vhY`uNfg zuvEA4XWxI;9=5=evuN4{`-4}ac$fO!K9_UHai)mkUY3T6c+xfk$#%7(x)hfA?AR87 zfZ#7?Ncqb$C+<75duQK)wjXR2!(AU6VPmqBq(&jv1(g*Bq3TCw%^EP7KT0eR2x!|` z7M|s|NFm8$jKF9B{%aHco&j?zEQpi^%oI-369jl6S_zt=aQ!^)hDHQ4Y3>XU67(ck z=+0zxHm;7$^Ht?9z)k`aRi*^G#Tg`Q8tE4fw`5R zIkq(5GwN1{sI(jw1d!SceAv6etJXSfuu#A>EYjCzrgy>>0v!sw`AeXf#O+v`hV;U) z>E#Q&u2yIb)VVw*e-u5phT1B|*dxn!k__z&(1pAzHRV5%cJ7wpZ=fvDtv;JMqUcZ7 zshFU21ifj1S>iZ0G9)JbVvd!88XfIp%L(&@ktsW2CMi_ONfezdThhsc-T_30A^Zg& zr)KemD2NY;#WvTrh1T06i#*ud7oUg-5}2F-K5?F?w8h{*m{}yRbso8TbA~|Ggg?Tb zIe))uN9VThhQ^estq|*q?L+2n&%_STPuZSr8_3Ttw5!yk^_f07A>S8p)lQrKb%Skt57N z*!MJ5v@n&jvdm|$Xi8teWn_$ZgRojf9&G_zOq^8qZ0k^6%5{qQnJQ5+OmtPr%V?pP zKSwW&s-F@un%x{SSTu#uVmw?f$r;nP?+f&)OBZSFTHcebNeFCGU4xvce0XH`$RW~q zL_0~k+&~;&};}?4Y9YwpIC8uZqU6?tqq8n^$tP zJT)pIpXh%l#G~jo0TIp?sigTH208`bqo-uM4>U{yU2yQ8H?9VH1&N~o>ElUWr~*+o zd_n8dE96NM;s4yJZE}ses*Qp&E6k$qAu1}U?dm5a;CVF|AR6E#NTh8wG2Lm~bVT)G zYf+XtNRDW_@TaQ{P$PnoRT8F7vZq{CXQ$D_BlxqBnNVTyKlO?Bl>1$f9r_14&O)V1 zrUm0rIdX>|sxxXtv;cXa7RrrGpn}hslX0FB=U|zM6YoCW<5WBkJ|F-hZ=XNb3naM{ z_+?lSxv8eZG+J7MqIQm>mj)Q>R{}_Np$Am(r~V$p1X-lM4c8VYf`_eHaR-H0sDJ?EU-Xjv@Ck7o~H>Os6l(0GBc#47J;N<$E4 zrw5-lVKWSfYE;~#@cN0LGr-Rh+<_vV^|U=Ivx;;0%>z3Wk-4hWN=|CY0ZDNby0`fz zY2FfhB_H9@iiH<|n7ft&@az=iN*!Q=cq!EfP0)-!K~h>)=Bi|S&JEIZxUiVv4m!OP z$g1d6P5l%tl$|8PD%zHUnE~<{8He8h%oT_}k-AXN)-9(eA4m~yoJtkUDi{c^@Bsk zpPgW0IALTEdL1B+0thI|6U{1~(Ph0<{hkh#y#PQW>&f zXl!W?FyZYF&mRJZFYRQA~4)LB&e!q!EEf#Q~nopkudP2KnhO+I@qGZ+&XYe6R8yT&pq=yQ! zM||jgP+>|RCIw`SpH=`YU2Wm~Itk(0G~0eC9?HR1mjE*{K9eQS>O-*Llg5W&`d929 z%-th~6i)jkb_+iEG-|apBm>5R!t6_wnA*sAZ%HwyegaIr%7YzdSILGh@w=f-7{%Md z3Mqr%CesywCaaZ@kan{bV^$S4;{6~_{Uxps;QwV4~Y zI^kDQ=f4>!x71(N{#l)(M9&2#{CR zKkTg4qUO%aA;J8PE-gkOKI*$Tz!k%@Zp}X$-5*m zLXg}c(jAu4u<*|&Z_P?ph;w>$%ZR*B5Bi}gWK>RHLtCL_&WTw}1)e?x#s{kX0FYGc)g)Hh4lO<%dae~Og z`?}UK=4CcwzqaGv)v!xGdb!vta9i#pbCG;P1EZRdH^rF*mL5qlCJfvubE0eF{oOhb_vPyV#tbY1k{UNM6+kZ*_qW&lQ5A`2i`NWei z>;LWKX8+;I&B;IKc;V#rhQD0d!hvKq`R}lK{0)w8(cC+vHN`&F-T01=|Zy87*EAQ%FSMkeD#IN}%J9 z_JF%A3Z4d|@+CGK9UcH(dJP-_t!lH2;D_W~5BN~bDH>D2FC$D5OMt}v0tA9)K=|4U zge*17>Z+2f@prgPY=|Y;8@J4$fk0!)MZ73|hJuLu{G}rdBf5)FVl&;`9cQJczNNat z-jIvF?ktqSjUN*Mmt@7ntyoaH!E&&}wdJw$zkonaK0F@aP* ziHw>}dxQAN3a0`$i-`9@Le}Vi0l21e{x6|uF-R?bq~mV<9NE#kpllw%TMP|Fkc0$v z-<_T|YAGvJg@HXJVYYMwNI;SnXcazZk;0LJicK)S8J9F8z}6NQM#&lN1aZXxuNP!r z`d45sxXPUR_?xv7cxYtdg!^HL{6*e{zxgz}>SR%uiu;ByK`^J|GN{gEP4)E0%x# zKs#c3n4j8eG}~lvois=l!PcwFy`t~RENw8w2giggh?+D9h~@Y1F;Fj6hrecRS#6myy_C>koK6Qm8r z@s>uTbF!9yvzb9lqY1YcrEiO(v0S_*jJHwU<=v*Pz?X{MaAiR5dC&PiCVO8qYQVgf z)C-#@wCIIQUC`b!mtwdD)I`IBS_Pd)d7Rf4YU4UnWWJ4!WtTt)zOF59*YF}v=8O{cIDJSs%%~61#_VXr$aWOa(#Qbm~up4#+~E zAr)W=l>-{c)M?@f04hwB67os?$8^)rg^%cLa@V%DG(Yt`z#Vqq=be~|#+NnRuEgaD zDNe8lvWpbB5`fev&di_l+zYElFCYasT?aHfNdswcK#!QNp>b1Oh*cpmGx*CCsJL{* zC36f7+}zc4lrXI!3E-@7>3V{uNrup(DyKohT%xJTf<%1jcGgyRI&4;B)paPzTs`gU z?5H_pTQ}G&l!gQ@W`h>;!_cEWi_Ud&;7&4U0uQGZBLi4H#2%tmyl1x`>L(aF?56v@=5+eAv6`?Tean)g(YuEDp{sbS|_#R%-mrBX%w`u zTWIb$f=oT}M1S@1$Bt#A!YgK>x0XYvw6&lEe1nFoAc+h&-N1)y)Rs_5J_D@f{g8J> zcl=<{Z>}>Ymqm`glUU5|z=Qh>^tsCeTofjoH>|S**!3(J;tYpOlW4=_H`_@8&dSIK zi}F&6kHIMfisws^YSc=-DMC&*nqvo>&AzBi&eek`hD7v}s;aX4eaq_D^lJiPLm|&(X$Cx5F~E)(Swge)L?Em}(@y`rAJt z1u_STh8KlzH`=T>IZuVs$-WndHh zUn;(k+pa z_+g2=s`TtyARRcItIVm%0a%(hdIN~Yz(nhN0B37;=GxreumwfzxWr%Oy`i5XMc7vS z;AwDQ{a?D-6OOJJ#qKq*FOVE*m9G!oq?o{gaG}s#ZDt`w;MKsb zn#q^lD6c|8v0hDAf(kFAv^Rx+Ds4bHNe%Q@q6)Ik2pY>VD^QcVC43 z5g3i|i2Ei)1<9ub4*g!L>4qz(C$tN_>0UogpO7LxB<(t3e zQ(>FH>vAG@i9DkdbOBCU%2NgO1UM=*hjsrkN{1KJX>~?@w&OS7zoZ4i@R#N-})6h;wBLaZjOuc)9aDiTxA6U z^2jK_bg;haVGJjMoQhYGpfo5<^r{koNVje5bnux;n}}~sa0d>Pqs1(Z08HW=+knf^ zN-6HCQMJ>g&}`h9+jmgY@M}&N+EjKnn_RfJ<$eDh_;?jYZjUP>0Cj+`NhjxELXt}ED7BN6-kYav{_gPG;&?A^Y17v>bQ9%aW0_=WdMa@ zoOD)$0@7-HexUY~ARo|`0J##QE2AVc@yPFCmwGO&9FtNOy*o|ZB%{x7?(qA>fa^Ru z2maEnz9SZ*&QMB{gyw|q6F`&bCQMYJt<9&)sAMS8x)4%1gQbg1R=eFVM(G<8JRSGo zavDe5)_a+3B@k-6$(ct87NK6H=dS7*#IQC}*oAhpcV7C*h;rS|0=Y@*wNYHR_}Op=dGJ?zpD5UXq{N~F31+gWPqY%*12t^#Yh z&8#|>3D9^(!wyP-_-L#8qr)YEfjXrM9~nFCqL?IU^v)104u{5C1WpPCU`wW>#v%~? zd4LJpTj~_qN|-aM6Ifu_3-oxz(5ZY23K=k_V;rs8TU~WxRmA`(FEm`1txx#L8iXjP zt_Z12C?>3fG38MieN+imBZZgLBldlswJFS_rcCkdVB3uFK$9 z{C_4p2XzdHY)REc$R)(qvi^4em z>z41g-O|7K_Bc~b)uqpCNl9juny$?AfrwlJnkS|oa6RLP!xi_EOpAb7^OIs7^o&RV z$xsHf#P`zOsIe%TuwhS)Lna|VZtxfCAtb}ERc3!!tc@DIxTVdDo5FOACEH-)g+=JB zAzdjllds;U(aA3YV`)$~?bmi!H4pe&qRC~xsF=*5LBj^t5`S>RN64QnNFnjI1&O#! z!&rVY&8Ma!#EmR%%Nkpybl5z(89XjK7}qG0PYEP{|CuIvD=jjLJE{DmY`Q@$%j6Xc z4tCi*3!mgfEot(EQu7q2=2Dcwd!>MsT%nculugOhU8w#%xxRM-~gQQ{IH z?Lu!yzi;Wl7_+#MN^R;HZl z9F(ZPho%@B*(`Wq_U(N8mkt_{3@HhJ=yl%Eap6dETe?DN7H_#IX;2uhIV~<1!zH`k za(~xImRVSBawQ_SoHUIC9itU2xqoAW`moceeXTgcCkgdBahV+INw?18PB@L|Q3nKR z&hVJtPSSVUcW2KG=R*W6vLWB7Tt5YmJDmPEvDkbK#=GC1esKB;a^C1akCFV#`@c@a z#-An%#S15Ap8Qj@&mZN9pI`5<%U-HiAHvrTL(*m8zzoU^WAw#GSg)x#Bz=WyJ|f!$^{p4P>lh@&TNNo);SE=g9dCpm~vKTPjdJiww~cnvMvGJ zkafn6^9Sd^KjX?6#__JH@o{;=_+3-wnGh@VD|Qi)YH!f$APS4=hOmuX%V`5BPU0oC z>9TG>_ubK$A_i$Qz|NVqlvytv9H_^;(~CUgwkQ?#pp};V%=M1F2LwjW7*?(Xw=@F` znE9GO$p2uQ1XWvFfADX>%p!~d92O+4Jj7%G@gt`5xL z?q+@{cR}|uc4@$BxLW8c z9@UE=K-K4hv6Dh*J%V4(LK2V<>j+cB&ZIi`DFgUE>y9W~TR4?|_1YEE2(C5Fgx#2f zC{#EoY7VGmYFPy7MCx?uRu1k@)Pwj4G)n{)87h~;sudoYjG-1|fv7?#XU^^)zciCpLzFTF*ZpK`Hoe(=%0iLV_8{v9qitFw5|dDtvoDP(_?cjRYReUrlwzM1 zu22avXSf0ZE6m+7H6v7Ah7)j20l0Z5e<4huq{LoXRDPSxl58;nBPHkPG+nx`*SpNy5no-xLR z)CnUD9M@9St|`^@WdThMs0Ah5a)3$8NOLHuF9pKi%?gEZmATOoq8P;>gfqhAMZ80! zuN7=|COG;bSOW|bU(LWE7nK)e$qdPs^W-RyPH?gKUIZG!M#5E1Khfp#su&<49!!`YH~)775T?yxjx%3@Q+$mqY|fcM&-Ek5$mBFn3(wpQ(0(Y`ZLE zC!P=F;e2@3Y3L+gcoaPf>q$TXkP&jqSkNpkpuFMueucMBZ2@ew1FIb5-KY_v=az`%;UXkd45(oIT9Xz3C$zX=&~RdchPHL4f&eCUE&W!4&)99DRf4LxfZ9 z^e)|~A@|rRT@>giS{DADwmHmU&s=R%s%+v+`aSY5yEB50U1HqA51{iJiw)Be&?g43 z;9NI%;qTbd?u

    OM-0#IvhN*^hV99rc_LK7%Ny1Kqn2WvIqzvjjbuPW=7i4f;wV` zsxTbP1|wgIZW^yu+a-{Q9O>l>lx4Y=fPB5XhpPi#I-)w7GJMu{k;N0_Tt$hOv%^v3 z$j$}x0BEDvO$y{1B2Is%zC55PP$i1sgnp~(0x6KE>2VohScL(ijLT1YXy`X`-%GIp zgC=2+27(f!!Xh-{+1eUfhKU}K{@MQhAmFf~h8$%(9giFw33RyKaVwqCi0r&GN#F(J z7a2k8Ta`6^EMR(SD=>=$1s7qIU}V(t&<+MjE>Ms_$8V7`83iDLvSdKyU!y~q!L#HI z#miAoxpvAiSMO%{3 zI*OF2xW#N;qU}lSJsU`a4$sgtwXc(z)wXm4mejD4eksv%GeSbPBoV>Vc^xrQRssu< zAZcKeCm{g|BUfy@R=x}p+;D`Try!OYJw%}_R+9v7_)g<{eWP5B_WRX6obi2;Io7}{ zp?TM>VQS52m(Vd;5R^iEmL%9lxoZqkYzgsmLe3Z;GlbFZ#S=J$x_D2ptcAmO9SvLb1c9MgPwaSRz|01X4=rg{EhFZc4oi1CPSl{&y#Z7(L}8hyvR-rhTIe|yR%pfF>Y*rH+f=48I^ z6B@gr9f6*Hfz8Nsd_+FYECvY3pz<^#7hD_3S4^%|hyu8Je4UC>NdKg%GC{&9LSYdr zwFkNE`^56Sidiw`TM3Bgs?w=J6^3OJK$$ABgmrdme?$?XA0>(|B09djbfJ+QWDc#= zi8Rm?xwJLN-2W;*wjp+1IT_7~*8^34xm=?J>5y>ZONTXiG8LF^{t+dg{%0TJi#JMf zg+lGxWkBpFH<~3ewJwgQ=h5GN&r*avB32(&I5Nd;WQH~6NBS@9wuc9YTf@V{^M@}Q zUOJgS`6v4O`}@16yI<{op?eSeOMG+p%{+f`da-+Y@{Y+nx~J)xPp@>tE;f>H<9G$f zmvY>@(ho1ea`EDomF_n={yoRrIQ}0FJZFZl(9}8@}UP~EHM})kf6aJRrFy*b+vs$=L{>i7ve#1fM2Dsj}9rLwLxUJ)b2qi z-e-b)`LxE6aXW$Ra40&$G%^AvZfdT;yFf&+w7h_6Yw193L_n(AR3p&fqy3}cnoA>+Gy}>L23>39 z;nZ{SrqKH`)(IUKu*8J^m}f{IQ%s&H|DhNGKzN#o_-hcU3?qShpd`&bjVZ;7V$j_o z8+2VMI91dc0xuyZM>v`7J44acg|pw4xN4dPAyUE}p8;p^{j04{!$?|v(8<5TF~r8Q zbRf8qpRPP;mkyz`B7dgJH;%x(SR?fNzahV{ONpZ6|6tuFw-A+h)0ewS%r#nZbhap{ ziX)3?acNHhylZbud&@bSZLVmD>htsRG{s)-*#Gy4+y0-+K)1<+I~9VnwNA53N!VTO z6-z-RvS5Ft2xqA{X@G*U)sS1sFejR5ssKK1*`rJFZfsQA7GqXfZJOzUjZ!+Dk;<4P z|DpWB>R;A{wfUm>G*Ao5XywQ$IJ@=4G-kv4%>$*jN};jzDdNdElT7D2J6I%VjH-hR@}R;$p{hAfO4&j`N2OhA}4N;^=rgy;oDxjq4Y* z|HM$C8_5H_JiY}Th*!V}3`SpAG#6YbeNljq@q{>3z1-MlP5_ldT|C#zvpPscZ2`q# zpZAhM`_(i>V5-^b5Wd#|G}~KUqFI40cTQO#esCZV4H^Jysj$G7O=~gFBI|W;|<?lOBD+)MSj5F`DqAc4*lAW{&&QuWY2X7kfK=M%i%!ZBsn z^sBTWq?f$`EN%;jn^e%QNX%3YWlVPV-De$Y)M{@JvDMhlNm^K+Q(el1J`;)VvRGZYtqv21ro=|w@Gk(f;@j>HUdNG2unm8|&SgCWg0uYcJ zSyM5Jx4%NDzF$_x7EP5!Cn-dl3x${jJnAESZm=5;7&RF|et-kqbj7?)OJbcP$Rc<= z#q_7R+9*d%0?SI6a*-O4k#SHF>3a|b2QUMdXkf9&xA1-$W0X2_lS;!O!Xf1a@j-qG z_CfX{RJ`;V$f&6zyR4GWVOL7eK`<3a_Xye9jkgXzWWmd2gCgoObp+J~El4kZby!7g zFp(8LZ8U3aaP%JxUo(cfSL)kizK*NCIGiHFG-JhL%5!sW$wuO)_D)1;;9QN=|`sQBssm$Ekj(=@y4`|91kud1uM zy1J{YdYkF#nNoL4Goq(^da4_3Jq$2nku3y?ScJl2ECUiYMgrpqYzf3>0!CO!JdPKc z#SmL|hA38qAz+p`0^1njhdogYvE_&dS%g?TU?Gk2`~A;-ziROSwyVDPTi)f~bI(2Z z-t#}_+Nd6Hwv;p~g2_{3x1gLnJ)afnB^Ifxn<@bdS} z-spdW<5xL8%z;P0|F;}q+5Xo#z{P{O_&neL6*D;=UOUE)4I`_leC=@W_!HyTjz6(B zfKGSbnaLb~B!K-xFJyK+da0PxSFq{|FgEys4vA_QG#1EKsKED?A9aYft;X#!YtX3E zaiubu0)-v#zBuIC5PZY4{5s^9Q`R{T0_3-9d^GvC>0Ar_&prwb915P$*x5>etb)

    6Cav%za!&mWg5QDaXs>L9QThyF5pzT=sm`fNwP#BLV#h7gwgo>?5 z^_D|3$hXxdw@N}0Y}wnlNAE0*%U3?27pjE74PoVHpgB(;govT5t~N}LZXy?Y>D%$UL=h`25#MUUcL94;QZch;|!v|U+(;@Jh0uhjU~aF z0gAKNf42PG%6f)ebU8?SvNWttO~! z8zkcF`}zN3pA|dvxVugbLMjITj?j^a}lQNPn1T+xEM zTCp;p5~_Ib$eJyML-b!6&c~2s_@u){nLgu5iZ;h+(>5u?&E#mOhL0s3oVLpf(K?Z% zKsz{3`=O7)hE>=c#4&_3I+3)Sl&b`7f`S;XP)8ZCGCZo_ITT;#Dawad!zjIda%UR1 zw?A2(i|A7ZC(<~5rZ6H3%;|l0tl#2dhV?8F=+g9RXmJQ^uP?zs6b#b#0Ft5mm?V*s zo;TM}VpNx#sxJ^To(GIr76G_}sx*4d4cTN+{0n4o4ls>+mL-<<$M`>Z{20rLRppQ@ zCD)B(vMMaBV~_Vh;z+3Zt5I9|flD?T$1yfqG6&(GGzox+U7X>6G?&!83M#rTsu;Qe zu1r=Nl6+uVV69k;lxy*ApE_NeLJ3jeKKhZm8CoNh`K>!N|Ea%2RgogkZtAe(Yy88t zPn3`-x;^rcg#b}doE+aHQS5+)7np1QEg#lt=Ok_BL;K< zuq+9OYyi6uGFPE5(I`C(1$(>@GlRlM38`YPgYp9tO}+eGF4X2~Vy^O_uZ|K*CK5p4 zEWe-!)jjnxo}ZTI6&AMgPP;-ej`W#x;I1?N0}G%KEx#jhvMRRUWU*_NRce65(1dA2 zs^&YN!YScYm=)Lqj1kblcT!V3t+ZJuP2UE^Be!y_`01fL&vO(Pv zo`Ta!z!t9X(h#|yj9@E@WJ;M{qoqt%gpbm|<2!=?JW^bOk#!O@LC=$R-iiQgz|8kP4H=NU}NS7$43|=7*kc(x8Qut?`8b@Tp5YcZa*HXg| zSS#If^0I+ZU_RlqDfn>Sl$9!VV*33_BD4nX^#)4C!%NapBp^5;2Ha%=$cLB7z^wgA zqvY9i_&h<|*6_gvje(F3KdnISA(NQnuqfmmzgaN_K}i*pyw*ozM=<_QZRArVzr*h+f(1i51@sR0r;C ztSz(N*v0X=kfWzLeunbKvRgzn25=UjLmOZ{+xV4$x8mB*(XK`~!}^!to6p zf0+YMUyp~ee;voyaQrX_YmxOY*mTs;<{(7_}7D2Qr{MOgXZbmIuXqL8<|J|dSCv_Ow~wfCv}A09<= zs*{pQ6{mf{x71DtMpS9c>xe6>;ORisz2F7PEgX8}&LSM0IYinw?<^&&+*?(iIMgh& zX;K2-;fJd591wm08ne7E4JGKkvH;K!fV$KZ8n2e12vp+Jro%zqrF`LXC6t?&RAJys zgOM;pYHMkIb|)tUUKVg~A*FfNH=+Er%R&-=K){dnN)HP8QgOV%7t1Xg_r@FVW_DX^Dd- z&qC*`iTiUfEHp4{J<$1)mE#Qxjlwp@m`t7hA4w<2z1zWDt&Eg5_py2yu+u3Q1uL4<<(oGAE0<*h&b}L` zMafN2k;?F;^(f1l?A=^~m$B&}w~*ix1`W^QaxBC)J0VGH_VGu$%aN};gDgUo6SiZu&FVC0d<@d$yd_yfx-F9 z>fG@#sZU6ZZ)Ad|=SoIAyD8~K*P*$QCY!$cO^DD|-zFRAV83g0dG@_AttgLL91xpANdAP%V_H9zVDF9Pw6Tdr3I zGj@n@6Oy_;aC3vP;`c0g<=4f*YIGV!j^mm)HUI0BPZzKN2~RAwF3#l#j}tN;a^L_l znFJN~8+-eT1BGI{FgZlbso#+v(mADbR5EK4u~|WF+>7d;ln629orZ?@V#7_E`*niU zxQRqD(ZeK#7cExO@FDFG@0}=H6~V{I=u-d7Wt`^)E-5wiz%B@JZEKiMa^ffP4S88^-fc4!9CwekBQD8_;+xwzBGz!kp*8shxS-iwDc?yl#BUIT_D7u!}U<)fW5vV@Nf7t1R(r2ak z?=wM)hPHc`xq{8|HJ9oJZ=SFSU@oK5^qHFVs;r(XA5M^G2u8NZ*p8OW$eo7+UFkyF znsd@%eM7!wboRkW-2|%OD3UmH@p+tVu*v)FY!@rkOS=JJhqq!t$xQuGTYRm0DDH}! zl$&U2FC+wHTC2*)gtz&X6uE>pn0r#X!X%1FclTSb_0p?z@k`dDu|KJ?Nmd zWqFRbu~0xiyhF-ZxRDQ}QDd*1ffr=bFYcwHrZ2}Ho6*_u2kt(3dfeO`7Ay~1HlEa) z7t+%?!Y)D5@BzLt&vwb)WM4gg;&5Vm)%4TT&rQEF{rdD9%&YxBAOE!J&rg4TZD)G< z^m2Ab-(lkIkagAcz~R~JH#qe8;l=Cm;`Ms>7I^0H-etYE-{bSQINr_ievbc-gXq-m z*Esa@4ICZc-^GD9uyfC`_i+3?2OhrZ&raLZX;z#&x3)Qb`tUR3zvTGb;b(^TbNm9| z|Cixsn#WJCU-SF@u@it>@M=x(b^fCIdSFsqS^4HpVMi}cq1O8Qc^!%?>ZJa^`h6|l z8Goa(3cz@wcg{By3@JQOvk1#^aC?Q-T0kRn)jVP)IkGJM6*YW@0Qm><-|jxO(BaHK{y3qN)HAjgKeYYRyINsgBaNsTQHL^ zl!^_~mDv&mH?YFaS2o` zql*tw-HQW|ovG0L3gtM!u4Qv1f5RhCnhvja)uIIv!X-$AQ1DS$0%mJ~4rEq=t0~68 z_LAGcP%{9pAEYx!QabCgq85#QY}<@A=PG!vA`pGgVqAA`c&*?xT&@=@(60PvR*-;h z1(Q;E833;Wt;(Zjc|>S8TQ*)6r-?4BIKoqIVzuQI7B8?hJeQq^=NZJcR~X0*xL7%~ zDYo+@Pf%XvKhrE|i%=-@0s5<*yueKrRGAr=pN;X(`VfkB-(+MT0AbQz&%|zQ*JQ=9zq8 zbGg4jJw|DA$>*HanmKRmX9>c^1%ca!ZJ76W0eUzGSkoF!ZhQL}>9N-~PRRz!RKEm` z?h+>40K&|m-S3GBWrRP2v7kjGvc~T*9G@sW4F1E3Qz!VW=O{c*GwkD*6z-57EDN2Q zwfESjD}hKUq&kuT!6+hVP@Gb?hLbW0r5$YT&m*K*4bcM5LqegKifOemKtj_d% z7$NernB^)#I_2DN3rYxFM4LseOB81cCCZK}Cp4lavs=BIGKDvz#o5$VAtnQMcm5bl#_&f(-vs*A^Q8 z)W~AxejU2A%(xaGZ*0YtVId=z@Xs4y6jKLbp(L291rpqV9FQ^CS=?oUb7rXB1Et7Jc{3ZYHP;&-kxsIqnlzfgi{nG2942 zq3g|R;?7I;ZUj>N$81yXLj~GVx{SG(tmBi8^@r9&dW|j*mb-l9T9neN9DE3or>w~bnK3UC^Ol7_X%-ucB zraFuty5G&bbrE>h{18zJ=j!D` zaY5NKAC$wM|keaIpeJ`>r?Q+!Ph z^fzpS#7}yXG8z<6&JAg~x|xZ!Pp@Z*G7aBp1ylyaCl!6@26fV9P;s&K>iVY7;4*y= zlj7+NEJFocS9_IBgk+o&Hn$D0tnQ>3V(755mO$!`tPy>mAej?36zR2Fq9|m>XL!sz z?G}R#)g^bhf{Y}dhws(<7h0yN@`U%4ZJ;>f>|kbvY*kc0+_ogv6cHeFnsfs8lJGix zOl(_~hxJ7BiBLUeRzwBF4voAoV4{PqUHV-eeS&|q&CwUJ3WFUc@f>i9a+4!T@vB4~ ztKQj>L3J@*w*k@Xke?qGA-}Bb)Ze<4+nni;msWQ zwVj3PY}MhF9Dk1Ef8h99j@NR01qXic;TJhx$nh&2&VKzz9ACiksT?m@TU&dg+gZftQ4_sSZMV$_m!(JfdAsl#cGUkm3+7!mbSOyN_l`9ZX za1c-%@Z{|kC@V)@1v)qMmwV$1 z6iF4@GuW|a5f-wTNom+6_Jqf}IFH31g~wo@OJqA^G_*Ang%Pw+8*E#6SLmKOjIion z(B&ZG^p7i&4JMK4*;xf;TFvKrrCc zn=96{;&TtLELv?cZx|kyiKF13{$KD{>J{WwmKLFv6B7-W)|_$#70c35)}t7q%E^9b zvWBiEB}%{4ER?O762}m-Vr-Fw=g5PqAbHFWgsja^uYKLK z-B;4XK2WjiifKiVL~J$Y2ym^r$e=W*79)5NQVhL$#*{N7RX}`W70|oTd~jcdv}2_M zC6hwRp|sxVIE78YqKf`!xa<=4%tM`B#y8`U?!K6z${` z`z#ctGIcAF*Rh6Obir4E%j&AEfXKMmP-(bP9da50Sx|y(y)Vja(57C z=-03Tjj)9|cmOmK2{JTnFGdO;OkQ~el~NU5*=;3awpdjwxAQF88A0)UM`|O;ZW?5? za?V*6YE&eu{$Gnl^VKX#(94zu`Q{VcX(;ES?k+4F(jrK*TE{jIHE0PN$p#$DBK*>V zu?8pp=H^AK+R_tAJo~7Mw$Z^Qett6@~il-1~ zsu1$sH8y@lO7}K9wlsF{knfhzloZjB5=Xod+UnYM*|r)Qc_^4^NAuxvocgu~(iy)o z3prJ7(Un3|02wv$v}ZhRdC9I}jyBbDv-Q{$cedgS>@)kEbVMJL4QI>kjx}&x?w)$a z?k>cwN)Ae5da|uW+<75Pi?NYb;kJOT=2cS)N{Skmou8+$@>+0A zBpzUxai(QSixomr8EYc3l%rNf zl8sai5z*{8fC*{Y(Q`~j8@}k3<>nNGj7ZX8CHgA9i?w63h&<^rZp|#FY94T_E?(1;cRPe?ZjOeUhE@F#Z)Ewssb4oA{GoJHC|3 zwZAjm9^N&)n^mJXSnkd!`Pseb zj!Jp~LsWTUE%DdHfu!TJSR)YkUJ89Nija`^G%|5BQ-TAAjuC_&l2? zjcWN&=Uu!WCAW6p5PX5=N;;^Y2Lvk$MuRX=k>}(FLT1(V_2i=G)+E&fsL3?MtQHFr zfzCi$owazmj{={|(G=XU3VmiJ*3cA^qJ&$dm7`gAuwLoXifjL{5YOpAkG5FxwZvCL zQ~X#p1r(oD&2E&~?M^HtK7!iDlJa!BRL;>P+R7+v6QkP$R%e)s$_{q8VdUuQ5G>Ym z#-P~+*ux2X#a!M|(nSUO6BA*(0>64cL7M`GxT!-W8CM-O_iuj0mB@{jYmU{0se!P2 zI{eJ6g%X=U7!~3XG+t$KNL(*>iV_=`ElyVqGNhkaZB!yb8VKUZT9iavk#e zPn%F>zGrzgd61W@Ba^FHHWNWrXZ-q+?OBGEtGg?=u-yy_TUCnL!l-cHq%mROGv1rI z?84E?S(28-=9ZO~Vj;zufznk; zx4}fNTt~W7&grG{HaWF63B98nhS#w_bMIirw72P1bP4yLrXs-5 zvw@{qtz>hH(HpNH!;kO-6E6VDP*LCx%VVh5VL|53+Qf*Fg?(N!oQm}L4wi`_cszEJ z$$y&!5_c1mKT)$7hlPKPmr_!buC@<$mJf_hC z)CttX)XOA2j^`}b0#Eiv78%B+R+oS)=7J7jLEwSqDjNWk(Z&b4zyu{!YJn&&Lme;B zqA46@V%!WfC?JYe09D*r&Woq;E;|+yecc+5t>Joy1=Tx08aY>RKOGZP9uR-UP@&- z61$(DWRbjGEDDuVRas|CD%ee`4XNnSv*_i zroJEn?J%-Tfx*1gk|3CG%6Ej-rvHK6$DZhDoAk z8QMcKJX7SW0g@HcAhgCuW+KqV9x}H3fcZLQGmt({a)u6sav&&nZmyM+2AW?MhA$>d zF=AjK10@QQ>kC;^?m9yuNLF-6(iBqWn{~|ky|MyG{u%`Wv4B0PO#^a zM3tmGL5@7jbd_`)G_IFeRGw+9XFoK}My5ycoFprYB`BQ&^b%ji=$gLb1*R;`1^FX( z)#YNMOLLbSY-f<*Bg3}6h5O}C#sNDS*cw@3y#>2N@vK%eZlkZG%BiB(4jFI!1}&-n z7*==!gDsp3od06+;>Ri1jO$WPp&NVrLMzE~@47YvHi?_sSs#u3s9EYbeci?jLmk`T zIaz{~STGSgm7P{4kq(?4y=h08WK5}Sr$w6GB$?E$P?|X?{PrXt){URx0-vNwy60p2 zD>f6mlAe~)NM?wrWkM=M{2PNOnulTyhr$U9O8yzANSf5huZb)9;sUpSkft+Ip{P^7 zUz2POe{qdA+o%eq>j#`8Rcb0)1yf`A*ZVwfPH&XWPw?m(NPOeNpEZ7E{~x>W9)EPY zI=yuI)6;iPKQR5^^kdUcP48U$xyS$Hu-(5GJ?Oo|vHrc&&*Iw!k-J*cFo1^N%kh74 ze1PM3Iev@dH#mNs-BO8Wy6^D4>yl-CQa&v)Ggf3 zUo-mrpa5)v*LcAK0k9f845Bw|Hc7Ipa3Vz;o-)K+i$xIU-8iy96muoRivlkJ0R;s} z4q9Q!Rl)8>!DujH<8k>b3%~L{XBn5|;YpZ%>k38@VcK2(T9OdYHeask8a^DqZoTu| zTu*Wz@OD>(K3U#8t9nisLHD153CgVar#Q?MEN3une$gFS3#h_wyo{^ zz)VZd&R)oVxknZ_YJD1vh7bG^P}ra|JTU>y@Ra3|Ma%(}sj+xm5i-t{^#KY*dUx3p z(i$-GR3aaVC9Bp0PhHN)EvJ~>+iJK~`^wE)@VTMa*<@8Sj};*0>2CbnKq@wuSzyHG zcfUZXdjjGIw(&>LzTOz5G)k4cX?ba@6jSlYcEvW-MT&$_vFdQkt9-B4MBuKBC}UK> zwF{jc#ZuUumAb1`;Sp7lAS#2&7>{0I$+6ZU6z_DwYf4d!S`A-&#iJD-U=b2K$GLZhS`jojEX3)AS%Z|QI+NjqZ3JT{dKs-j>w;hs@)vs+J)5+M zz4Vq_%s8GH#Wzl@2i=hm`9U0NnK}}sg)W`A8Mosm9)I5(7_(VNDk^BO3xhiW+%typ zL3k#6OopN+`t$5ArjQKurFBHPnD=uog}{7NEqNM^0h&_vMVH6rUAjnN#4A0SQLN<7xFFEIPd)B-V+{8266bjdG5jiEF07HOdl zpkfs&jJwX$wfr^^^n8{CR(8-%!MsLaqQ<&Q4BI!KyPymwXqkfUjSo^9ZjmZ(ftVuz=!t3*}JEkXir&c#JH>N2=EnQUA zYe2Dfvsx~RCf(Y{#NW|vl~=A-4MapYPn?uooS-n_0_3Wk>t}U0XLoNw*>4_FNHTB& z;i0)X`Q04TUPAwalzx}1B3~0jBON|V{af3HFh5}fMkwrTK65Sz{(R2EUIG;5Q@AMa zV1lb0KqJxs0j{b$mMZ9*0@Z$CB_zKiJHs;yN)xBkIz!SFHM0}=6R z*XfE1ZG7Ep}+$Kgh?21_*FSNN9go2aa zk;0I-vS_A}?v#TLkunC$#NK0`)A6kf$G4`%BhP+hv2o+Vg&P|a!9jGu%y_VR%2Gol z3MSh2a4{pD>bMlKrsniCD#W&Y0Hxv&hin!fuXmC2QdJ}wuaob#3c6?7z#x!4XM=EU z7sfuXxbs17zv`=mN2>(V@{<*ow&Bx3XvPJg4I@ zpDn!c3a7(uepf5WT+89Cm4>{m&UFNrIGlvP)jHy4Pe__6j!4?NU^*(dh zEoG1p6s8m|_%_=`sH+So1HnF7@IW)c;bM#7!Fk}-ab%(sdDHhokzqCZm1VWin{3nP z=*$$bswn&-go(nRN$EU3m1TcezrS<}n?>J9nK^X?JCLZIaaF_uGPKo$Q zM!C{ZAx$-MDyDnY%7{QWZV00aeMr`SJlX%kv3&wWj6|Ub1oLEhbh{=chAQBGMH7E_ zEEm;e%mFS4u&p+7j)+LQ3wkf8)7M9~tvK!h?XSC00aMd`M8>F=M6}JIA1{g0KWd0r z7>>kQdkF1H)&g_P6<%w{3o$n_NN#?WNnyZ~(U&zjJfv8R_gkgI@F|{Q6|rL$>#fO*6<+P(j(^R8f4zSb$A8Cx559kb3S{{D`@g_JWO!CXdnTV( z*p}&Si|<k`f88KY?n}Be2y7P!PN+HZ;LMh2c-Z{8HyUCz?a(O? z5;dLKv3aDKPJ&aNI19A5EYdOr;|HpP?GA{k@ZVYQVT+Fo{fZ?*UAc0roa=X43^>a# z3paq8lqm-j=?x3e2C$z@l0%YVGv=~>%>f=Z3V3^l zTezLZ5|%5YSPc^O&vIhsQm*E3OIEe?SB3y%w;N`UHO_LhrZ;=8ZJvq1&Pr%yF*=%s zkF(p5hk^B*m81wrnu^9Z-Tw6-s}i9n^FNcHF%YrT`%%~joJo8^Cbq|LHr()g*{!Dg zT+um(ENolhV*(7E>W)D&qGNDCTiPrJ4(VvGT4e1;QGc5_6$HG_Wcqi$ml@OB@>6ze zFR7qI1DQq=m=$IR}!ZdfMexd&) zb#8+_T4=T8DD$RhPFQQ_bq$hIi&;r~kL82}YW0jqvNQva_>#)7jRh!=c7&%W>95Ie z?=pEww9VV>^2MikH7REbNXoCQ#Smmjgg@-^>Gbj za_6)Lz?lT${vBodS74NfSdWylD7{cl#4~;B3*fTAYM6+Px&dC#;IO%TE z74va+8G8-#fomD^VH>uUSUXK5S>;O%pRu|mSR6Q&9p5yWDP%k<5+e~OkC&uN4>WSg za-fu_2K11|PKfyey9B8?X&$WN3P#`7&WjX=m1*R}nMqoQOjT?k2Wq#8fTE?|l zS2k;NCR-FXEsAQI8uO4EDwJETJ3awgST+J-K+v`XHlTqqZudJLwo=h=~5Y%m(4=qp=7^UM|XxWRL$WS=Sm%y}4 zeusbJeCkH1!?V_dXia-YEr(D)O8!a7?D+S!<9@v~?QA%%s42DP57E*|dHfUWj_%$U zCXfWy-8mt@IH#U%nPMDtwvJ}r-P0@(5=KE7dMHTz0AE1qL z`8X3~On8pqfy>-GUKk1f=lrE2AQYw?_NjI%WVO-10fX0vK7U7}b{dW489s-TZy0Q` z-wewA!o}jH@GESvs;fE+#FMQhm-JW!2%6$kyH;umY&~17Y8^N03HoCx7ljsH7nfK1 z&|U&o7swM~@{(OuOt*X`c#j`7)hqE28iW)pL?|*oJJX(Q2Hat$Z6s=fW&4-G$YFgD$Tc* zyIB*Yp6j-~5a*9J?F! zx6}5yPU&K%p*N)+v{Mkzy%|*~nR_B=OXZ(ua|@JWRF@PJWut?%;A-IJCu1;zKqRy7 zu=x+&GYCC_`I=UedyQJQQ!OP>kr>G>mj1Ukkc$oNT1dVDgLbC$2NaZcnrG-CpeDVS z^}!d1=hTI*aa~3`nqnB0)duc4ptkuvs25csm}Ll0;f`PJ&Mx2sf{BJh55uz|GutTG z<7}Sa#P+&@GguTJ-d;`xe?1|Mu-)qN(bC=a7Kcu z7Ad#XNSToxFkiVv;wTrTU+BVUqrM_m(S+>LSwT>+fYqor){zQW5fR9c$N8+2O3Ea# zdv%9y9In|>Qkyd7g!WqXvr|+p`P0X{I4;Val@iOd{ZDi}>OplBz7l@N)`j-ONm$xx zCpMJ??w0rFoG|ZLhZ9#29~{{Rh<_vtnr+$`bUO2F$9Q zxkH9=EBzdcy;%NFh;Eg#BfLMV+3yhQFZkt20&R|At^1nc_lKu-Z|y!XJu$s)`jP3M zPXB89)#+W+yVw5a!>@C=0)nd{yo1A)5Zs6DogDv+gH;r> zFSvVwzn0_oIIy8*g#}kz_)d<0&%r#ItjO@Kd}7s&Bh@CQyDet18YQ^ zx9;xSq)2X9wJJv;d#oxTH21Rtuo|k-tZ)@mK;Acr!f6j^f)X$8WfN~vBiRJji3bI% zLC!+#W@FGeV^D>G{M?fG3U2eU>!u{e3&=eJl%@n$^{ z;kp|bRf0Qj-*SRVRxoIwQ=<;h5%{=z5QzGm-X}Kn=Nc?rr1N8#gFp9GNp5l{CHlU>&$Y9*|$Qfh8qW~jR{=OGDX*{ za!_kkmVwZBR?w!K*vK#~r#K-yWtPJ7 zHk*Dh2O&9W1K!GgWx+88jUznU=BeBn>m0_GvIJ>M;g)-Q>bx3+@2OfWlo9BdR35F1 z)hv02a<9NT+>LS@c*Zl2f<#Z?}a$OVV0w+g&Yb zOef~@9hhL<^3lrFB-U1Lt%yBns<5lp*y1o~_OI1a4_8}Q&+VJWHDTQzZevJxt;DN) z2lK`5U5qOzQw&N5#WQp>D*|U?_6l}6`#>TFAz#M$Gm0^WvjyiFP{l4`>&I~D@av>nCxPk_pMH9hUD4^=cEnKWgeF5)p+tss?Dg z8s2NUmJwT!Xef#vT~0+ib(F_WvI30+f!tA803~yB%zps26jP8h&N0lWubRO$60kn+Z)zgNsodsFfvPW%EJ+wH>HSqEnKpJPltjZJmb`QIc!16m;@f495Lw z*CxQNXtSv1qw8$~96@^IwPn+OF+6h86IDxJFkaeZJHEmlirMy)!D|JT0NH!}*|Up# z?!PCRG&8*_#%MErPUwLzrYh>qGBVT#fmtW}mypea>@Oc)l-!izN4>l6e#^pvZ9n5a zMFaXM`Blp_j32XjHW$By4nUAU&$w{WI>HSyuZFXMo@UGo?$D2^><0=4apsn?YL>#E zsE$~4=s1imB;+hsG-@k0QPMXt-?GpIy%I!`B>fk13y=I}YY9N3(yVFjrb+o3TL=|T zee(<1u(Iqx(|!UnORVkIBUM2qfGt{dIwC9SQWeA`Aj@heEzoKJqVxz9?L4@FhmyDz zpqZF1PeGA4$OiX_@!_jw-~+}_FH2MRE^U{dvUkE_iGb2GQZcPGB*HVx{?ob9+sZOj zoN^CHt&W9>4{lrT26RW+nfQQNGrul88z73 zca0_x!TBD`1fa&_B#UK8sRl;7LbM?eft6{CpD65K5Vn{&*Ykg;@MaYL- z$fR6Ltm~))P>dx||4D2US**A9zt*6#37MFs%Yejpg^5aoDQWE*wFK7`RJN873cbyP zpimgcH@YOA*;!H}6))EcGtm^&Nx>x2#`K7J3ZfVlGI^?d5c-tzTD-`nv;*?0tfzl- zsn*tIN{$9U=m=}58Wx{oF{@*2*GY(M&+I>{k?^&3EEgB zThwNTE3FMI_v01B`-`U6c#iNBu=`tBMCM@pcjNDY-T&nD;_1Ix`+pyQ>G{b3{Yvb^_9G}HL>+JUDfB$LVZD-Ov&hgP4AJ1`z;}MRL z<6(~D9LBsD4@2Bb;$$xI$qbwR0>@d7V;oD4PvCeC$00|@@hPPL`+WWhKA+-XCQtWc z{Qe9+f1BgSIevuW?Q8!9JNNy84f{};qeKfD)Q@ZHIm2B<+=&GVhzZ~el&L8*u?7_8 zsOzFfXJo|7Dzp)s@IuS3h!t^3y$r!;=LAl#_D_g=brG9Ea8Y7a#H3kKLkyL1yO%N^4RAU$CtGuiMQB=Osh@oI#zjNtttFvo~C!jS5T?-Hlxg{h&-gx zfuC1_V#3M!UCgN8|89_208XA3XJK8f$4E6|T<|~3E(D<}2?RnRMjW+N0&!QjT@4fK z0Vp(6jV8?m;MaN40!hfL*+C*_Ey{^v#++k4SmbN{!*C7|9NY=L75y-WVF-kck&yxL zZAOJ{mymgZ35_Ape&wo%#wK1o66VQHVi?$8Fr1<$?k0{k6;}3`d36RXHbx$8ciKP* zg1A{&TQCby#<(H~-V5r#E46_4Oo0ja3;3@xi(M*jULU8$CO-y;jLxMn|6r4NbuuuVRi&1<3Z z@?*{FU~Op~!KiSNeg8_W)<$ogeRR3d{3N8^iily-VZd$4*rCd@|51y)2q z@0tfZ*05%(w)le5i=H`&u$0 zQ|Ir-+8&f1w1}Jh9+F_7K6RQ+3D0>eB!WXwJ&p+HtK%T2iN@C2fKC(p0NLSYV2ua1 zK!AD>R$QqXDVp1Z5kF-%`d+^Y*6z_je+E^ffCak{kdYOoN)Kr{Hu!?$!c4smk5vJp zZzm`CM6y7>tmRM41`vv1X!mT0|3em^*g*qeC1|2~NsT~dmaDAjdW#k}!d-5H!xSmOAlyJ9^!9i=8B-Cc9NBKb=Tu|Bfb|=l=e`E?!f+$j zOE8>X!^U@N>`~HGrezuGPkh%=VY0-=WoUW@=7Qi&RA&me1Mt#kLVUA)f*#ink)UFv ztEpJ{R=OF^y6Q^u<1V!>Y9Xqp1_o^~By=4dfK!<)y`%WS-QE_Zc-^~B4HFAvsQy|< z+C)wxIL9;Z#B*`XtHWo>0}|hbOE@A)LB*9jBJGIb<^q9XZ4{sZyAU8Na74UJm6iT( z!$Sb0+d~yhsB}Y_W(mM@3`RVNnmk1$K@_fGmz^i6Hq)dGm}#=*N5lfwERMNQe5>$z zSIBBo6u|}z!Is*RHcrHpfr{U(K(2m!}}!duAzeh60i5rJPzC>Ic|ja~RK_4XjQ6{T-V3y)^a@;`yJRFzM*y zHW6Mb!p;J!Z#wscRDqVo47h~tf!hl3@(9l%L#_o`Km({^b5Jk?W}xHr0IE*4<@S0e zMz?wzHo+@6CPd6zQdBC{okN)!7oMd$(vJ9y40@f(ZdNWd@SN_^N|W zxp$#`NUXpFAQd(k+g{4hfT|@T(-6RuMLOwva}_~L6*mCAAV0U%B_SHdlD!z2%#q97 zqmw;drPdp`Q^JH8xl#y4)^jmGf{2*p;{)N>INc_$2)+!AHpL`tilR7g>sx z#KmR^SLCuucua_L20|OCq~wW4kRM1zre@9{sl3j1hMQE~b#84TSrW6}pT9tlm=WQNykBnz`)4XNvUMP#*fc;2!>l=t-R{a`_O=K=@k>?ExUm)hk7Mg=RL zN_L)?m^X zn1ta%D*zYrMA}kHkfE96BEYFKQ}8ye7PicqMnrls8Pft#7pZ>|(4?q6=;Wez*|F8Lf-P*hOf8E+g@&CHD^ZdV#f84{v zPw=KcnY9#$pXK;o4%Sy3m@_hbC5Jv})@K~Rl*8ZWU=7CjshMAr-2%RyPv(5Y=loVa zzlj5%^YHx~f1TrNIM_u1g#1UZ_wZQT$ZzMJ5BqZMemtgo_-3+~wfbi%gY{m%zuRia zt~3v(bb)S{{|+F_y+AB=_(2@Qd*PsY?nLRyDD2=EzN@^WIp*v`-4eZ9P5f3Ix$4DP z9$^*2tX8_OMjTfu8YoVqAb=IfHJfr8l|RiOXdZ7q9(RFoe)`mFjtoJ5e&f3J15{{6I~ID#W^Nqct}+abFVF3%zp4Q9#^uf;AvP#jA@w zEP92&w|9c=Z(MYZfE)V@VHb!)4ILjXN_u%{8(wg+fA8bvErDz-ED+BwDU-Kn<*Q2l zDvbX2kw))|3e<%!z*88Q#p>F8nxVP++bVsgQgEYy7FFPi{JFU;lnE$cMop{_HCF=m z9A0Sw{DJ8(295j54UwnB!+O1vBH=jpH328SRBO2ClorGs6l))!3aqn={0)amJBTAX;FU z-@%94gO5X2hR%D|MMcq{{nG9MOY-209ph9HNd{$^;cV}{jFb$OX0COZ!5oBvo}VwX zCy`+~w@VQz81$WCuNJdvSbMniY=acJQxrF5%bycOgby2WD&WsGkdV9_<1L`dx{g+c zUdo?Q*;KOtUG!LJ7j*$(DKqZ@e{7J)uNlh9F-C5l$7=30)My&9I$Ii1G^JF_Ql~0? zA*CQ~f1(w_4lXVU1Xq=XEboSOPTvR|CkL~H_9!uMTLS~hqBd}CYg-Tm1KF0Hn_Sv5 z+c5Ar8kKR6VgeMb6o=fXaXq~%!H_bXL9vaVqQ<3?t>1%(W2}I>k$6XR53>}LQlhA- zc!44@oYf4%GHi+rV5WC2$$q&9mV0a~1Ys&ZQ9CJID76yQVUZ-LaD7P&$&7*ESd=En z;{nxPCLyRwS-!$V*@+;A1GtSepvhYrZ|@#gImYIl+ z3RsSx`XN)RPN$*iwQ7Lj{MI2C2c;r_ZzXaYu>?{@62j{ka$tWg%{GGB5mBpxTHIxK zK=-B&C!4%fS;AowMtCt^VKvzwr)LJt%vLK^$R~+-AY!q#b~d5Ve=;0jUq|;u6uDLP zyL)!*4%`c&F3M(^7tf{)3cm}=Q!dLNT*FC33&8=Ed#L{zD)i8uEC$1C*iU!`UKC zMcrN4IFU}qCV>A}G1q36yDUJOrEiLzb~r(e^`B%c0_UkL1x5=F%kpPQ04{rm8)E|z zSMbC5Ssu}wCJ47llec@GqBUUJW%qHnAj^Iw zIO{Gw?LL+rY^#viSfIeROcxEsDXR@2jfLlJgk@;51l$HOmdhg1Z1_eB)+zl=F60yF zW9iF?$WyR`1k-^hD5JY)ovqQVl`|~qOJ@`K!i_9iLzCuNN^1KuM{}?ZkOG*-Ue}Ng z7-HXRRm}I1Pe>pII@g)AvN&BrLIuR^seCa$w2I|?-d2vtX;Cmg`ATv~h+bG^jNeuR ze`JhP)7e7%DJ0J>Ykfm0mq(Jy!`zvll3(ArUzX)6X(6oMr%}zM=>RR9_D>qTS0IUs zv^b=e0S$99UnqAHj0J=IWBDQ*9>L&6I~SMw5YJaZk|q6Z2;x4a&KDgPd2#~5;YV?j zJ?xcaTNAv(eBpA85{d)cs<5x9f^wOTsW+e@)9R>E=Eqqo#(hp}@u>Q-y_s+ONqMtB z5DBvN0woWbDZ7`*AQY1e#d44#GKg(cG1M05)tElvN~ad(QWl?@6Swr=dcdkl>z78C z80MdBiiHwm^X&U)r@_cZC*MIo9al^wMx?0MRFyH}8eDJmTFi)~#bC0>G7Q=tw@*e8 z9MNLAlR5mSAEHrN+wu)|Gxk$XC&Z}lKyn#UXa_sp6Xxdd#gIckNE$z}pv>vUhqoB^ zedrt82(stiv9w#Ik>01*bNmL${NC1K~ghejEG|uknH% z8X&C#q&YdGucRwY^~zcfBxQtj`J&^L4pn7Q0D;KlK#@esx+z-W<)I*X!Ir`ryvI-0 z>$fzE^1Zg~C{;z@Teb$bXVf#N6(8e87U}f5epN2;e%BI?K^@S<^!{6hKd+`+m&v2e zqAxFf>S_V<4qw=lPNv|>7YKz>bspYUnZer3sW*j3ZW)^`vh|-@ z-g80!qlNjd^$WrZDuo3&Rr?Ntb4AB&swMenO2SYa(Mfyg$_=EB&!PA*jaK{Y;%q33XN}$}xHxwIg@q*OpF2%`eo-pEA0#xoJonX@OvTJt<^jV>S2e?}H}=wIXzaniLAi;Ihv!t` zR0~H~09P-pn!eM|anZNSjI`wX0ldAzw1P(No$hjT;`ZOdFwHKg@2D7Dl)M@Gka$U+UNmD8+D+)sS zK%hF&rV!7#&$~1CT&1!28Se9Fmci!gl5M@O}sHX5$WkAi`&jZ#N_U9gXawswM-c1sD-GC zloe8WPYr)8fYNg-UO$fetP|dCvWD0xuB!i!-GkOB*Vosd zdI#%yiN7Oi%Pk5=4%6}N+ARaO)MulceKzI{~Q3bbzwtD5Bt8lhr{xMmYguJjS zC?2)4x36n53q3}ACw;lx8jLv}F|UF($~hYx8ueUTi!2XsnU$gR@xlT}aW&a(C{2)L z>EXpb*n~3d?iRd(Qn*IpyjSA#78!*S`VO`e^|YM^UnEQph(77qOjH^vByB>6@f0093E!cnN<;tqweLDBcJqhSgZiO-sKGz9rh=L2OiZx%H?N8x-|v}sn< zwRRwgBVb`4)rD9=~H`?izgqUdUkrBW0(!zsV=w$qw&j_bQGr59Dk1k?`Veyv}5JG?xh@` z`=5xy6?_GD)iP71VKtpAm|@0HD?KmTo@$tZd#v&01(5~LiKd@L!v#GZInUgD?pl|) z)x<7>vsBzgH+rW~--B!BWL!&WAa1v!P*HGkDY<-lma;B9$Abp!>cmhLF7>-&S_>qB z8V|TNK2r4MC8}WCFq=oC@2j-(J@0xU-H4Shr&cas5Eik);C~~aaBc9=3*htR+@3dazu85;ixGip*TBaL% z)Ii_8GsU7za@SG;k1Cj$+#VnVgNM3?4Oy0c<&$KwvoByELu){}KXY9Dk4>u87#KEO zUMChp#uNJUBi|xXp=}l6DC$}DM@_}aVBEKKq~!~#)B^P4KQ$1BC>=Rb!?^Eb>b}J7 zmSXKt&wbB1k5khWvf>#eY&sD(z|cEZ7C#gn(t^^=uI9uLClE1)w1RqAM|Nr(t$P{*L~)Mu+t9_TUGY7fhF^@Q&sPT-#_jFQ=|QQD z8YL1y!#F*tq@yR3&+a~xfyD@LLDhpD=~T^_W2uQ0#syD8O)y5(E7Wqe7M+dgCzqmh zK~2F}pTw(VcvFk2gjVT|o(DTf0$NV&?lJ_U^@{;hTC(0yS7hwAt{_jBOxUL(IHNU2 z>7Y4uST5}yBi3NTgbShm;YV<>FrHDxN)4emL0OpEPR*gC!iDFDgVZS{!WkTkyNZt@ zrF!5aOpq*Qa}M=}$Wh+qq8C};4Q5fznLIk2ICVmIOI_2~TkaPh94qWOY(kNqYR5xM z0lQf67(XcKm1_lhLFUYVMd3r5*>HCghBH~rm0k=mjOz{ZG~e@MT?e^JAk55G_Ewwh zcC|7ux#G2Za&sn(lNodmlN`^wdd19%(Y!2ab3JWCO+^>S>NbZpgX}srYV=nL`K@cA9nA$LRX#H#wF6QJgtx>@y(I>{BdqVEtXLSr|j zsKV9oOgDl7aD$pFo>h^hC!kx(-q%$@p<4v?x3O_GvU`=tv-}i6u{4CI7*=76~lI-LQe~$}sp!T>=py~)>qP@sgT^c#|jg-l0m zBOx!-@q|4RrKpr7oL9eY`3go>sl1_kNRj4Q%aG>Bcf48H4=PF801a640)HSZ*dA7J zo|5_9a`QZJD60^`Rf|=&CG0w}_CYN@Cv5aq@20P$A`==BlbeF4?7^1Zo&bI7E)y)% zE^!irVo9~7jgV-JG*PR!Dx6d!ZjHx;>$>(8Yw**f0JF?E5!U2y))BeTlAnCDL=Glg z&3SJGgc%V{Caf!;IjdREoEsuMVVnKH2BvPeVZqXJjtS_R&FEkpRw*-P4*6tqiDsQM zpzEhb2wackSSJK#n6<){xv5HA29zgtxP8a;L-J^6VpFn>4}aVEi^H+$zn#8sdh6Po z9)H&GCVXHxu}XQu5_KpY#^0u9D;VP?7y%8`+%SIR8vt->K0T64+NKp9564No zZ>1?$UQG`VWaK(v&&9B#rlQO~Ft4Ya+Y+ruN~DJZe^mzojw3#6IrCYe2KsP^ig8ic z^ijQlXr&wn<^G5hL#U!F!^LI{EOeq=w**t%TSTa_it|wa>t9{ACN9DR$R*aXDYu2P za1^2;m%fezO_en+nNpWm&c#5Y(xc1E#=Q_icEB_D^+4!mgGjuv5A{XPwDLiCu_R|tD(f0BJT43-gsVj%g9?^l)EjD1b39#hNgJh8w3`+F#(PCXIvDa|Y z9kzNPKwEqSpo8p2S<+nf0EOj*ku3IQGjCPWR`CigK2Zxk$(IN)bqarzskMZ|UWZ>w zl+}s4<;^x-G|P9loBgQPDPdIZ-psG!QZbH{gr77yzNNLy^+?GS081oIbYc=gFm+a4 z(wBXBht>Y=jqFjt&7@PUrpnJVmy=jqdY|w4c{8oNRh4d4z4`~cZM!pC)9wDhau6U6 zu6Ke(VHnP2QDBA`Vm?q0{pD>8!RR>A(ES+`w2CVulkT^A$Nymba%8QDsA%TN>)2y*k?=~c21o#hiEap%K<1u zRb*6UU?$6&aVl)h*lx<}-6yMC&7cJRhBTtqu)vPi+>fCYsA%~6&|X8h&Py{Tqq)}!D8v;`FyUZo1kW#M7&^EBkV*f7?0;*eXKGMi0kqQj8t zCKQBFbTQW&ZKiZcDNS2^22a$HQKEy4vvcpsJ&-ANxz%3^Ub)P-L=N4zzSv_U<^{9U zPMzKz#~m<;%*hhzX0E%Ts+{GjCZnT`9(EaT>Xj-XQFeo2w>n}oQA(O!eN+Ddp^Gf6 z*(Mt*5#ixuCK$|F0cNImNx0!g?=tcMIdzX2!sdi5DndJ zEA~(^An=u6?c*rYTf_Q^jS?c~2}0>s51^|xaMa6tHmj=Xu+?6((AWq> z0O)qx%;%n4DdgA@K!uT>a%0gt2Aakx)>YqD3DsAoi+_`alq8qP;5Y?o2U}{*hzphN zGW6)>=~oa9{8-e4O^(HH*y*@?NH7$>a@t$_z9zu=V-FOO7y*F7+Z8fueP{Z3>Dwac z7wtNlwyH0*+rs=R40H66%KHdNpquQhVOdJsHou7G*lC0DJCC>up}RHS>7CIgoD#! zNr{XSJgMfVYIz18a2L-Z24>G2G{IHMqvXV%gLLPayw|-{hAdQc?69{29a(iAdyGQ{ zJ6XCtcHE8NoKwf*A-0Lhxqz-lt$BoK`#^Wub8RdN!7fxBP@^vllxYPp>wbHCecHyT zK{@tl7WUwDmxtg@(xnhFM`aeRfDtKW;IKhM`gC>?zdMQ3kc+YDzbzJI*!9{HMYeF) zQ|=(s&6iWo0Z1ZJyJi48#pCc=4=OWX8PE2t^M427i|H--zky&Ye;%vep^;Q^Wl z@=xVehqtg;79;`KR#Oclq+|z0_RyovZ6uIA#P9~q3QhdD=^TOAQGWn z6C0ppVIUB0njrBV7MhfcSQ{+XKNejkZDBL)hGR^n^#&=fwhO% z9v(k1{v6+iwP&n7V|+g?^!Kh$4QF0axuRf4mEZ0Uy4Bv(MfFTT())iXuK{46ch zFw^~#Pbfo$T0_W0dvh}^Xy5cjRXhj>Ax5k0p78vq+w>7%*<9#cb|v4(h! z9i@eRP4&I?n8Ax=894Z;-j%^H!c_(&493gavZVu4o_T4Lt6Yqe%Ege>xf2-4mnhc3 z&n?JthT?^#p&1CKx@=lH>v&Y2l-Dl!f(Ic&W)LX}kh=mdiMCx1-j#uY$7kQ(%CTBr zULF;uk&G`&7GBNxyWv3>znT9Fn3W|WJP>sx&+TQyW`6vEmw<^`rwe?jVpAY{IWytR3-+Jo?pNy5U-|*0OQ;6rp1!R zMII)`7pLozmk(wF?8Qui)$wMsWl@k=pic{_&2q*yOZkexiIP?1Fzkm9cajuYvBxM0OrEW|zh^2(o`94a zNQ1x;!9)VQ5{fJx>kt((mUawQwkTfJ`e@X*F52G7ARAr=FIRSlQ}As`G%(y!LOlyLGH*FY=Z5 znlC8;;1qB^hqD@L%CbST+j90~-dC$yKcL!_uIxMP5XRLBmQ?IAnCrHQh ze2WsZ!Su%-7}#E{ejdo-H%ZOzaE6U{*Y7bNZ`dzwjo^0*t?{aIiF-0 zN1!pa<)(9_<^f%+-S$N=W*%c$q{INFqm_wVhLk&x6Lh5N#T9*tZDW;lj-`1Fmq~aVBRyQ=g2r(efwodt&}7%~ zbQv?ahXZ6AB@mq8186FPH%bdiB*rhjjf9kOAWel)Z4@1)YTypMQhU$Qdr8{u-;u0nut$=8t}rU6SSaw0D-;INt56 zEE96beiAAurz1E{1~!~dPbk?_%QFy(@(pR>wKFm)VbRa#U_^wCi+uys(2_7!96!#( zwyu~-EGh-LM_&`g*kKDSgwV;SGGZUJb{dC^v-8xS*-y=ly(n?SPwN0oft_3~TMSj_ z93_Y&qP0WBxK}7h>V|NK<THuj1QpjJWag<>|*<&s;FwJ zW9r@r7oU0se)jA5DL?rC&<^l|@)+x?G&2zl3}hf()${Zgc~tRN$&`tKbyR4dFIhxZ zy%p-9Xi+szS?yb-Ppc$V?Zu+Nnm@0qd5U8M2e&P-D>M(EIV2l6>cd>sND^Xmp(P24 zPH0W0Bn?Ak3J3qDP?6T0vh9)(Nfi^~iVKYdn>ZhUqLMsIV)+%u*-$N}ih30;32@Gm zh@hR4W_orMVomS|Il{g5*q@5c2oNG}s0Ba|I+asPVKd}*E0yk`p$@u*h>muo{nK3L z*@~|~gq+Qb9VeaY;_Ug;%xTt7e$JJjty-#^LTaCMHJ$7K)DyI!Q$1Y``|A;~N^T}; z53?gEPOq2X&=^n-9>~mx-Babg<6Z_Ar3HB>KHnvU`BNPCNR`H)NNh@$*|~C9fvd9A z$~ZJcyA}F@>TWf=O&FMmORfk{H5HywsE~~6cT8ZD3agNFVV4z3?*bKec~B+aEK0nT zP-I;v9)NR(=uXon8d)SQErPf7SyrR>*1GmfqQ)#7T(hH1w(N&E512|B6gg-nrD{0v zw8Tp&$t6}q3r||~LpVeZu}~t_kvof*pp5E$DK-MYO^CGn$dig$^y3-P=lBc)N2ce< z%5jLRv-@%#o(#o3p2lMz{`BF!{ZEb`oW|+K^z!L{n*Q$gH1*V`stTGO>OCE8(i8wx(elhY?#!+{XTmiQmD+FM z6DjC{o77VACQ06nI~yvtD(xf}z`}YR2lE8Q=2Z$xlC}aTs+sxLTqROA>G_Mgi26J0 zy_`LBfmK;6}()EOSM zNJYp%7eR12^HQ0nS9O7ZYclS!5m!PHk9Lc6E#_M_t#-t4;yTa+uezK{5~;XslwK$bB&^1Jm z;0=Rjy{&dt<}nEggM-57(CoR)twET9d!6WrrdoKUAuGfULeEfn{-=>7Qa>fnE`IjPkImHAMhkl!CK-3CL zLcNHy0*3RA;;@s^Dhia!Z%IRxrKNYsW-j!I}Pj9Of^)G89@o4B~$aJ~4x zOFv^NTe({;=_jRTi^{}`TL2|J#sRt|n&<&yX_T{$^?3rKpLZd@`)BIgXZL@rqiC$w z@#h*N>DuY_+sr=32-kDlKBk>q&7Z*t4afmg?H?t#7LTk2J}|Y@!4HcG-8D);2Fd7A zJiyZCf~LX?sNI@oOIL_uoRCWNRs2QK&qN9m=J2C$WwCn-WEN{LyQVmS(FJCs+d~V0 z#9e}QS_ne%IxTb-)>yjI{P1|J&r@l%Nk$8Vw1C6%Q;Bi$L@|&?*lfY-mtjj5}4bNC;5FqkJ>5S*vw;O^g-UL3dUnQrW;9&qs~I zKoWTF`0gNEr}?QW&)5!?uvH}vI_09{eXvYU@J0^APch85u)tViaou1ScC{euQ+S@o zLoXa9?2<55l(ri9LqRm?aMVaoagk`9$_eX`8c0)ya6NLCYTXE|qk>dammA`L)^a&U zj~GcgKY{Gj6x+OqrBo_HNuD_i~F42| zqE^GXuo}pS?5g0;UDAM0S?p0Zg#*?J8nabmdL(rU%_wsuF=>eDpppiZ{= z0Q9qhRKY61V{0TzmJl132og12lo7=YGKv3*SsBFjqXm&EX_5pYk%W84b{l z6aomLfQA_;3#_mrD0(EChG7}Pt$dchQ7KH(j`uGYipUd{YQ>7LvKbVKlhE5_Duu(0 zvhA0-j*s`XMvxvvHmZgpKo&_8+GoWp_q~L>*ai4fZS$#b)%x*{#fI_UQca5ba@`Q- zm`l>CaKbh@2b!bQF%_v0r;t1W|LvU=MB7=d+T=PI6kynS6xCN;Yndr4HH$Wm@{bI8 zffakmHWh0*hCR2EI%AB)b^AH}b`JqgImS2j<5PF|?7n~#-9@nLo2_I+BK1>vb7~1^ zuW-H8X7`q8XVX)78pvaPU9ajIGtFWJe{zF3QK__hA)t)>m{4I@}KYlXXR;o z3pcs44wGQvF{{avlc@g4w9G%w|#xjKgG0=90Xk{61pr;YJh16j-O@No72JURy z`V-nA2v8dnV9l{h!9iA5E3~Bv>ab>biga2q*(njN-DiZdg6z=JP{Y&A(xc(TQ7p@` z2vW^XXVuZwSRV6NaNa3ef(^_n;3BonUDkrxal-1M*3&q{3&jSRRdkYVwNv+_w$X$* zm_gsd-NqVJV(2Z5 zPT^!i2;xnyPy$KeWywYTkv*Sev8Sq<60Zyw8VdBMlPn}sP{@xxkd*aR+AMNMAz|A= zwFtW}a&R;u$7C~hhh(Wc913kb_C?A)Xf&-=)xkxjG6+jgRv04vT2_h7K>v6pD`1%F zCbP!0LqY267qq3e#x3=feq2kYV!aG-hN8#dn8NM(6gSPO%Ebv6z4uy1{(S4++}K1q z6kVeayI$eYc51f4TAD=Z;^ZC#{#Jjszc9UreO$kM zdfoKewWqD!KfZVE!rJcI4)aZk`2EPg=H>)kH>G}txv09T&x!Jj=3DwN@2f9Sac6M$ z=zc-$l@bf+&Elmhmc$r-NVEWM#;6Qx>Q2E=Rercuaw*eJ>) z&=*t_0t)0LX7doOUYx8fPzn1>hS}a1q+^UOD+-IcgBX<_0Tj>7qmWUW& zn2DJ}G=R8imVZ&8=yr^#x zOKZV-MC2s{#OUoSIsN~fy$QHw*;U{7?mg$+`{tqQ4K;VEo~oYIWvc6SbyW$8R(DIZ zga(5!2!VvLEF)tOA47zKKm;ry5rO3x+j8(HhYyU60eK)!LJTq3m_deE47QkTzzjA- z3W`ExY%r+1^ZWhRKKGU2%V&M5y63)g&)H}1wbx#IpY>mR?X|U902&sstUV{u@pRP< z_vFFRQ@1PdOQ_%q+{sfG5LC%|eQ5lAY>;@EF76bz6Nj-$rU2gws(Wzi!TudlZKQ{X zF~S7SCgX;Z$ZNC<$f+hjBx_La1SwNHp2WEiFvlmFpCEZ9t z9`eV@gV;CfDZWMamoiseKQMpjmP!>gnZ!A}0=$NpT)L}ugf*M#0Hk2vUIEF^BSY_I zs#PCuq>FE5osSH4U1)2jP;iTQw}9Vr7^i*8n_NJRv10s_xy#HHS7d0vxwm*o84*F- zg<$fHDKdJ*m2!*qo4sWUNq21atNo)@G1BiuZ$eGAF`zEO{zT)cx#arJ;9%zT5c0QN zi+0u+X;rRU1ZFN@Ic)!HzPSPUg|R>uRv(R>BCJv%b7CvYFpV@;3fZSPdwdG5dhO1R zyoGHq7ZCsq5fNJRDTx|OXofy|wpIpU`#67l_uyp7=`Cy_WxE_r5tFy_BG1ARRUlQQ zC{n&ad{xz4XTC{?g48Y&1BkSsK(scVL1kBY;&!HUMREyi%j?*8nQppwti}7JuB{b+Y@_(OtbpViQKi*;A=?W9msk@-<;@NV=xUA zF0sMFzPpjN)OE-`|8zKz*Rv!CncK>238C zjPV3@z$A?-7#)LLNf=`CM06~u;iWFdy5E$%=c$*pKmdMaum>*q6DtSH?>H3*iB&YD zYWYS*q(s`llmeox4P+}zw$`l8R?ttV8&jRxlvQm4{j9jnwj63KG6*)AgAei_ornDs zz_wdEWH}5s{Q(l{_Rl6fQK=ScQ^_2@qBZ6tNmjCKEs$pZBBOv33bOJn!b)PS39d~h z7cm;`%2noz;Q}O*CYlcQvsx;9UPe2&$=E-kN>x=4_z(o4F9KT0R(YbY}8g^ut^EW0gL9J*V4X3+;NQkdAuiSIs4%(`}m& zeNdpvKWm`SWo>mdQ?~1a5ZFU?g-#^tpsIXIDKfrr06r?*QmDbBT-N+n>3>ejsJp{B z7AObAWl|3@FOtol=ar4ppy?|#15ZOYt-!OOxU8u)_kwBkF&PZW(VIEj*ofhVoH9Gb zgBw63i6#WGO=x*4v7f1GOvBrY=m%*a%TcC$ilxZ8iQOf<@oFkN%8}5vbs&r>N__FxJ^~O&Y34aGy^UwAsC0IX7vlH`7H;KR8n>#a zo16iOg|YfB$Z_`Ep!K;NtK^H4KW)whx8uYb0bvRdtjc6x<9G(L3`zqbW8XXNUkJ9p zZ+rVAAIIMpQg-iLw_Lgb-v2%b5NwnVAJ9FSZ8355PC)wGGvbdCbNE&X0*<%kCjKJbG9yj>~x@0 z1Trn2X3_>h_tI;el(u`nxtDGZZ+s9DF$>XJ`X@i^eGa#vzeslpt!yq=8lJF!#Gx68 zgyVB{+X7S(TnH*UM6TRt{Xse*ZkwVO>Pik^T11?d7ea;x=twetDJJPxJW-tF62i_c zMn_B;BwSO>EPYG_j<`W2Ruj4$2}{06V>wm6p4GZ9Oji{> zZqcN4wpJ3O8Y;4%`oZKgI zq>wsH&&;sZ%+5^vi~XPbJl+y)!G#EUzs+{T6o@cj=W032_}qr{xv8G1u@KW4%LF5oTl*A`4$g z&&IrM;E4EVr443f?uTer`0raz+^%RtYqEb1G8o7)5AZKzsxKjD1opy@^hWz2A>bWk zsvA;O{0Wp8Y(vzXk|_3e`nUCPV3yq^iRVL-aDzrhM15~$9eG|KB`PIXvSztL4aRW7 z$cQftJyL)0GS(0Eh9$4Oa#BaCLZ{Jm>Kh<7db_mEqbR^)6X-hP%iBxl1>dYPgB4l3 zIQ~l&ag89>Ao5RYI-i{-LXrHbLWcX{uQCF4{rAbZwY6(8Dp)sX6t4OUFXz>#zN~-q z^nG|v-<`aE@}~KP`M1o!WB%RqADI8>{3qr=yY!mJp1$k*9CQ%Fql99~HmX*?Yll>F!Rx zmC78w8||Bwz9`S*dd+#8%mOsvcU>NtD(=Ga%=3X9va##F`Zklv;BaTMof2kdmv7HK z(G3+n9oVwDQe{V}cRl46Yrb7N7(M?Gaz<#H5&{J}!u{ZkE2;XT779F6 z2w{+9V4qIi?Ro{h!a7o5cXVM5G3FH~*sK%{wx_HFv;iFlQxqf#CWmG|BZ9bE)8hp> z3O-PUIw_>Iz)pdQC*I>kLjpHRo~9EPw>GZDXScZnuufMr*-Sd4H*PTGiBs_vd2C zjBHm3!U1MbVfgJ7oa$xjH|%gWnA$kI&zjtzQcFxh4dwA$!<1H;2H$H6S&@(hSb?t1 z6fO)t7FQA?uQiW}X&d9)k2vF}z;al=ru?KPF*4zPKe9G>F%(iI3=YSKsg7omZqWpsnV&^fcF&ADJ~!b7I7{f~c!3lD5BD+f1ql}Up%i&SbV zy)kKM9jI;;qBB51X0!2OS*u1&L*jznI80WpAtkhi1{xRk!xF{HmEVU~q=Kf?jil^* z%}$X0hn1D_k>bDd!Qge}_#u+Zx>z=yy|{`)dtLj& zobiQbioD-zkKqn>72Vv~9w-HtdxIP776ApM&Z-ziMAWm%P1fa;y?3n=qa|h5pjf-1?cTYn zku5ypn>=7v2u#=F3_8ODhpYNL9OoaF>B~SQrpazC$GOEQj!+`%T3RW-TKwj<_J*+O z)6_cb+?);VEjEtW20?U7}lCJGb27%U@?vJ{OMD+VSW6*5)H z<;fesJzvgyo(ZE4P#@(jYs*O>EYwLddX9-n7Ec0CfcDK_ooNsNH|k1Od}VV4a1m-c zx)C{u^P=NqotLMjxJ0wHKn*J{2}S{mpN2}6s+0;5wAQ4&g{#A$*(TO=lv~nm=;DEz zv%DK63e`6|;G}{mJR%GfGBAxGKjg4(+jFo4g?>J}|Xh#32Lo-4^pv@)2ou?fZx7`Hnieq^Bdq<$%!Rv?xgwm%ZAvnHVo zwA=~<(aY(wX)I~Z$60M~h{YlEI3IH&m(v=s7rROLM-tmT);&r!O?d|gw}tKI%oBVw z0z~AhL3mH;CuWKF-bgQkhDnNYX-;x6;G6l*rB_S+60A-(L_biPld0L~NXywdl zh@rZ2)u!fGRhCMJ%E=nWXKJM-V)P2gyu5vPjuh}H1;MGfw6v%WF0`?GYKRf7_O*N& zai<1ipb1e6Xh({rAV(l=3e0#@`!p;U=FkJt3|W+fQNZyi8CWYSn(+%pOLe?k3~E8H zq)GdB2bpY!0PTH6Ce4vm|CW|2xmYzL2G&Qc;WI2v3=$GZAqUfyZFX~u=rfFkwX_Y5 zOo_M$@Q3j&rBSLetzbijmEvA#t14w^p=of(>GZO2@PkUmn+(5@tqo?ZJyoy!^jYg8 z*$izbdDA)GY;+F?S8bUNv$*__Ws^M3GRU^=Pdl(ioJ2a&;5THiq|^*TR6iC2?qC^! zI5F)Y*P$rnYkHtIPy8)jNdJIgclu42m2n#WBj2U#&HK6b`5e#IcHACe)FvArqkasm zw2%WOBmknHBb+@g|INp&VpR3p?0JZ5%#qOX@lLZID==f6GGG*Qmml+vTr9_LqMSh3 zy_e+pR#@#cpFOEcQbsNo(ALyd9`h8It|qNCE{u*F25A}P@^)#iFQK%umpB-kE65D+ zUT1)2k3>$XGmlb$qOT>dM%~KIr{Dvfn=&Q`It4sN$C6lIO){yzy`OU;+h-`DWnr5Q zWYSm5C#A*67Y^)JNN1VEGOW-ak;BXz@ygj`83h?ajN*5+P$7l~36?NAioIXiv^H&cq#A|? zUd0`YjC5qItGZLT=1D{$ELA9Iq0D~JgJP5;OSoRIYLaPuk6Rs$%k>m#H=uJ_-Ef<1 z*T2Cd0i&Bda!>uVs9>}p< zXQdAHk_RP$WbWW=Cdvm?EQBVhBrIW7XcdYpKbN-H$-I-O@}mH|sjN4w4jhLFE@EVp z_aU#STnIl+$K~)XmA3p4%~!@T{=gQkUV^dk*<);` z0F8wzvi340kl-;^nzu=4k4IOA9j>Xm$sVb)jly|lf?H;5p-L~;$zS5fPTraql}$-3mnjO%TcR3_x{TFfL_)P7U6}t0&(*FhhG)+Z%v6fY`!nW7`6DYK zMOyt~AzYSC{K`1t2`TD6^$3U3;f6=)c*Em-k<%`RB`U_K(fSgb0`{+Y+y5tv*7Y**)4VLimN71|PT$4- zv39l9F5>SY@vXB8roqb;fu&-^lvQY<=%CE*F6+Tr1=Gw`F>lmu>{Lgf>f1r{fXQP} z@G(Xej0WZYGa6+`aF1DKEU7Yi_{6G(#fX~RtYC=bM|PN3aH7(cXz@cksjfELnjkd6+1ch&mHmOK6kU;$1wuD3)xw{v$+RD7x7^k<`iuPy= z+<>TC!v3XZcO%gnChit@{LXgoTw0t+yta>GMA^M67m6I0u`HUVaU()bxZBQJ{F=|PTJIBn1){qq8sgbb_~5p3%UX< zR=I_6+PPYLB3J2?>6W(kV!~ydc)10Mnv>h_KWwDBdg~-){t>I8=-|hPSIZ1m1+s9G zprJPPtvln>N8GwtlLKuWthQ&2OVhPhM&@DWMW9J0N70rDJCD}K zD?R#I3h@YFG0F30k;sTi&-) ziUmijffgK;0t!j#lE{M)L50x&wYb^sK+afKLq2Wew?;Z%OJ89~EnFo+@~EveMAi1>_C8dnM$mvscFo4{j~vl%A< z6bOudYW*tQun)b6%)}jdla+nK+RZPdbjT~8IV}X|aHZe^UNmfzO)+RZG7STcB7-|X z7-IIipZSF3B=%pKTh!1@!(}XDD#Vn(=H3Eafr@z^h_%Y)DyY9@C8SVXeqz!s;bZ#$ zesnd8P^x)uU%K!FW0wc_s7?`UWq6zRS1Ff^;tP$l&=j?l(Bn@jugV)u4b|$V7(&#t zvRHC}N*p~^|5fFWpdi(# z+|vB2s3~U}OR#^Y8__xtiid`6!>5pQ{0P%b4leWx^3GOz^x$cKAzE@6X&rTTpcmqS z)%M$f8&rli3}Z6d@Wa6drJRcmLBx&0oZ&VgoPZqc88GzkT@RM~7EZhG=j%S~GEcl| zHk-bWAAf=LvnSsppy$DK3Rp#5WGzOQKT8@S$P3tA4GI;h1vF^IKu%+5qQ<|`Prj0B z@hH1~B>E^V#=@?xZK|kiqbX$*X+^vk=5fo*+`>1&LaLa?-0m4scy4tqHeiTy_`x+| z?D5)gxN0p~Hzd1sam!LBe~59K>_#$Z#l51#ttiG-PITUQn;{dT!mteD?I8|18MXPt za5=KQa5dC0)D_0@H9~h^b(gQakx1{r57>8@O}{6JH* z!r&c%GV;)@Ao9sIsAA<1%5O7D7uQqZX9RIs4bwv#eItLluDyhU zC0vTysW{JjN)Ro@UGK1V#|!oo?_lAvJfHL>v2LwXfE&P+je;x0<%M~9K5K9)OMP#W zZE~U{W?#xL&XFJ)w)a~|vwJ}e6^8RLma}>s3m9dlem0iXmmrwpIv<+>ir73-0i$=o z0L{T9f$zu{)N@M1<&pS<{_hY-`3UVnTMiA0`_5W%iJ{IGic3xy7LKO5@A=|!dx}bi z-a$0CEU=&=hx_4NF^_e3;k2QBLOc{xj7&=fyzG<(UQgv;2hBDHf8@4m0)lAX@u1@* zt%oH9DXt8g$Wfk*Gaw|POk4Ny!L(nT>F|9Dp}y+F6^qf!&#EvUsIi9^Vn_gX7nqI+A>UoprPcAI|$EBZN`j<;zz4Yd# zzczV%>BCFE_fe~|;x<*mP7VM8K~lnxfTrLkTwaW(!6mR^-QrIiE5tGlLU+OJD{A8f zGK53%JrIjejxc06`OXK7vB0{5%PJ|DV}v>)--&Cqv>b>o1UqpRsnYbS8MhlCNFCaI zi=|-E1GpeSv}N2DK7nDb4bF(40NjSOV?c>p(mgf_QR!qpux`R>eBg_T$q@z+LPKjx znOyJ>;Zplpvr19kNc@b6<%UaDfMAu*2Z{rUJ0aT69ae&!cUpytcWUCHjR0RLi6C8 z+(kkf=vP*35*o(lI^loTk2Nbnq*}j_^cV!juELMoIEoM*s5M}E%zTkUvxWG~$a+FY zHWu+Go{;z{`9CH5K(&HqxRlZD9QxZY!LQ^dyO&{4=ZDAk*9IIGEtMIu!PV$u<$tUb7Qb09<&RMBNY}O zg`yaTLthGxrK2lvFS)qjMa*Jb3_?vR(>U~+<0H%K?%tTvPWPmrF=~xVI5B?|OZ|?K zGMU5!F_p1Cd=R^4BEyP&iMD8evP>?C_7+RYx?8vlVXMmr8noIqJ7Bue_{#`FS9cE=p+HAt%ji$E zFyS~Z%qnCke}-n+nS4I4#k{5)oXo6UObYlZ2$Pr_1d8Gx!s;R6tZ(1or~WcZ%A3?~ z;$R@YkN-$O3@fR$0jNLnYIWmu;7m>7@bd4Dqu;TOAMp=UN~{nGQ|gKm9}8fNk;b<+ zFL59Q4*=JVfkr0H)ZvO1-rZ43xXN_@pT;|bHDxL!DzrM>s~%uKa4`~BJL2J&TMF9O z?vuYv?kI3mU-jSyO3PC1XvEE=5DQ6={^ZGFc;bW6HvqHDD{Ij~d&)YGs2GiOy~ktE zi5@5+aMhV>X=e?(dfBPE6gc_*4ZKCtnE=ctlFs+&5Y>se74Vev_jb8l)6)ZpyJ27&nO{Y%p*GM7jq#NBYt+l?jZIpK>c698ac{NRtu`)q& zd@XEmw`IoWpl0T8FJfn^8SG9c^zXHsNkSwJ;+^j15-Agz`zhZ@Fh^FwA52 zdiSyku`%Z?v-%YI1^*>pX$&ipKwLt5>TE_Ow2sOpg_Nx)F!edo=s<()I>!j_3k^_j za~$|uQjIJV+1>3J9-US^vU~9{U`5XQe1>{JIXi4Ed%V91TnZ6INCXk-tVK zlS2r8cuclmw}R?k$KUQP45T0VRTjQ|B3C|;W_EBC=i31R zUcluvz9^ldsRk@31q5b^n1&3um({W@!sXPe*Yjgf%9tMFUkLuD2iSL#y?F&|lGGdn zdfULHog){s7C8ZN+Dw9so4y|Wp6Ldp!6KnS_-a{XX5f#?j9FEEzbq(tom^wuO4do5 z;cNI4N-`fr6Nt`T+L=N#a%h&Z-ysyICjHLoEJe-`6;gboDcxFIa5RadX$?4v1N2eb z78poDNr&+zCcb7Ys*%@V!@MS-fi~1fmBmuH7kdHf@uDD~3Vh^5f2=Lqyr`UZmNEob zOFr?luy3T!${F(K08dUV2#18eTU&MTv<^g@1okgkfgpk6;?fJ`I47(2FA_=jiHAAJ`sS?IAM$M6bgVw z3dgDliDpo0wo(&Y8;4T|Yp-Bevw|?7I*%+>6un06N=~5C z-?VQtLUKE8OG~J?{^j`gqF6D}#&FJM%Kp z_{rhthJQQ!=F;aqc5UgUOXrrlDLaTxU&!$|$1w*xgifz>Jd@)!95*=5aqMy&a9rfr zVhP}{^uNAzXXzTN{XV|`jnP*1QLFnNDSH(&&cftahsshBqY!&wHZ;Ww4Z-2K3MnQB zw~Q~CL!#z!F(THMFn$kWJFolgN{?EC^PGaDl+*kN0Ixyp%qR=NKyBDX${aD*RI8m|fV7lENqNu~doDeIEA$QTjDKI0_b%WdJVU z52H5)9%3)zlZ{xQB`J*JqMwQ@e=xt~4>-Bm4Rno&^(=`i){Q1IYQ+v9#)ENXinSDH zRtTYSCm9RSz>p6xUJ3-I+C^f{NYNTRW=B_U^q2vrW|p~m8nrDX159hHs!EwvF!u=u z$kei{f@Sj{`&`>C=#AA6{3c&wJxKZEc|+2%WF6eek{x|eBs6$Cb}oKmGR!U-$MP(# z7}JS{D}*mhr+%p!yxW{+2~debtV(3jWHO6}sK-4{H4sC22Dx2e{_>N+-bu{X7`PPZ zG)1lM#L!aEKOB|Z*hXU(AoJ1A*jk_&+_EC{#8s+TW^`S$N)R3xSH>!94>f`Rw<9KZ zk8TjH=)~B#Fu54YIG$^_7Yndrt2m}>?iDH2e0K&jvTQMSCU$E<4*lO2*)>B{1>NN- z`fM|V#0Y{V!T*gqTBu2rvYw`HA`mHzppl?OxT60cN@X|E_+s!R0{)hDrA7IToEF(| zg=cFW82txV6UgD?kPy);q0Tp>!XNfyxw?0FRIXCeMOMdDGVL+;{o%p3062pH3th`N zc^D6uCVKPO)vDm}dtO#m0t7!Y)n6qIUS&{KQ2-fCZ87)z-eKZ(9yrY~<6_M#zowu#V8|NHr5f*oRm9`lav0n2A=Skj4 zz?|%#p-MA40ssV5*VR5Jv&-spXdEaNxu-k|1d72yWhTJy;6Rdj0H-7$R_Xu{^S5OA+{cF4c{T zg!8k+&D}N2Zgx?8kJ`j6qSN70%hEPA8v`6reHxziRvWd;OY9Wb<(REZdFmf%o~-df zZ{Q2fc==`RNkQH0?D>Q2yFls@(?Yas&S**+1y;el)wNZHjZ)oEgxbLQxCYzgB*|(5 zG(M0!IuGqfBRW&hJ=>#s;FRNz>EN;W@ zA;LCx3VERT2V>kvXx+7c-Ua+qm~q!V9YG7 z)XP0aF+qUsKqY3ittd2MN6GR;IVw@vT2wWiE{bF~TWqn69x+|{c98sU3E-EBD>-bk z^$~>YKas|ajJ6{HgU&Y_)FLQD}j)4q` z8iOMZG_Ncw@-jiqkA?tH%dNYpj;=u}44YPv6!g482MjJ7u7o9tphDjRAG2Rv*3?J# z;bigCo&p$2N=coru;*%^H57;$B+KiRL}!b^s|FvTgHoeul(CtHGOhd)GUPIZ1+rKf zMjHeq%cGP^hNpngwihF1u^Pv=C-zJr9+lD8s|JU3L-ODjr708C4j>WuvWBl87gJKV zPFOOmBD6QvWcZXPj&edysDL!?+0~VC83!#{4?tCD+pxpITy$}P3SiVJdkz< zdXDG>kO=YcHQm5=tOxkZ^=eE>fIvC}jP_3y2$ItOc+#a=5Kk-3$kRTPb}W%g>q9ye zA~0m=F?~QN{M+K>%d=Mz2#jksGc;xtv_)ED1LbLE20z>8l@NM$_UCmh<%z4sRr0pw zU<$&qYf;GIWO$UW-5IZnrnF37A=Pi~eh;KR&-NEu(}B=og@u~KFA zskPz4^gs8{8{Rs+V|eHAKMa4Hy>R}saebFQ_}DADAL@5m&GO&#|4rT3@&E1JTlxQX ze!U%kl2c2k`bUZ6vrf#NNAWA^x>qmFy3gVGY$Eb}H{K=hl z>7T^F30w${1G9sG6UwBP5dpfvG@z7m)dA}VuH7CB6bZ07jjIs+5OAb+O1(`1f#%(@ zD-rSmS{L5g?2hN&Y}4YFD*;*h!XJ=xK0pbB1V50aJ7R8XcXW7Vew6dTaA&+-c#$O% zBKw$01mA7;UAmhmk6aO*1ekA4^yy)CJ3yU3<~eJAACL644kYbyOAPB{V80RU@l;%b zd?+VoI_B+8s)ekCfnYn_QTz{jH{Q~%2WyE>tV4Kuh^<0WJ+Db;v2=&*^U>{Nv;knCNn=&)28I^VPGMRpDXN&d zZ_$xTvO(}|Ax}fuMIm_8^j&cQ$EGnANZ%3=O*+cWeJKe0$JIa)396y7A(jRhbMM}w zk~Z4fWJBvP3;U7LQdq21Yeuw{v=5DFc5DgTvXhbW@0bkzkF{>E9=kcK&21F5E;5PP z%2ixrUW=0ijNU>}k1%Oy3ZRG45$H^9_bJcY|Eky@{!XALdcwGN55}YcsG=q`o5F8D z1HN!39|JF6V%1)U*Vuj9QL=*1hH((`&%MWf7-=FeN6c6lC1c9&V~2|k5Hp6K^ebz? z304$>fz$W(RT>s`80cq$!L`-76Q^n*l#W(2({;m@!CZ7V zSJf;^SGDH#8c@e5!Y^OOC>K{bQ^AHB-pHd)Mil}w^{?mh{7xr>~&;}X2au<10s!x zXKq@QtV2^Rlm}~|Rw52#{36;Cma29_0&e|Khb}Ke*9m;9Xiy>44gd{dd z+M!`71jOfCjms+}B0-L<<)>%}=bR?e;6x@oZL?W5wdYiU-_8L`crmaOOsXkrBi--bO2zsNT%t!Cx3KG@L_4ex@HaCBW?Q0l=+)& zp`p{(lE#+16 z5Jeew0(jTbkHRhbg`mNq5n3bgl%OC@V>MF1{b-(`Gjjvh+4g}dju(iv9m%es)uJ&U z%2}GYj;{oQzq&TplgA;KkGJOT*$Gj^fz1tyi{#qKgvl|J$e^VlB-~sxy`sS5nk87$ z=eewt@21ySVfH;s@?oqk16?k&qbekl$n4&dWlj8A*&u&7eSPiaJT$rlkP97KS}hx_ zoE!X*|4i9x+|WR7*l)WAo<$65HjTr*Jx%h+V9`)mIH;?SQz-P)Tj6QiUYgQ-xS~X5 zoND!0Ow#AomD0Di*69drd88euO5qQgAlw#bV?}%{AZ8DeWN>Kma>bLESS728PRqos^BrE zKt>Y@N%oBIw{2Jgp(TBsQXN`_7eJz@leyMt_kEMmHc||fq<27N5Y6IU>Z+7DQoD+D zW3o~LpV!uGV_8+kzDeUJJRaTCc+1L-gDes-TP-Dp9<;f``~o>8I9RNfI(b#X16*9- zCw`vgOuBdf0URY8xD!kGbOkDFq|VAwxXS7ka^nT3MsSYDf7Iwcdm0No(&^t*ESL_T zZfn9rt8n7-`R#rEexDbA2sJCF#GaTU)ulW z?iRBrpUC}{;mq)l@D+If@R7+EJofhfG5%lcS-i5e-F>+GaDQ#`j>%UqJ-qaAe{Jbm z{NKh4;11tcm#%W7<1cZ19mmT!{y4`Ma=e=3Pjb8fTgMJ|?FjzldX&M#?_XM+V*L=$q3@Sfq99A1*SEOLoo|_Ob=no* zb5R1Y0C)qA3zQS&G;~{&%RHI{%{rm60r-%qeBCVqizc^X0!j2);8%gV1`C}Ny@w!* zs-**iAsXswVHWU1jB}UiVxPbkaYrjkY%4BX0fq=m*rTROf$lsWlLaVIQ$%bd0L%4^+sjwtU+@spje2p_)Pkznu{nqJi#KS zGPZ_q$B*lPqq(O?lM}+Ct znQn}sg2}yDjepMG?op+$`hxTkCl+qVc`8no7iJBu6y3v9?#Hbrw9fURfJRwAR2P7F zw%0mnRvwH%Iz2RjY744lN}0g!F?)@zyjWXhM>H<7%#D5F&aH#Hdsr^7T#MCcPEV{( zd{s78t%`%S8x-O+#tTksQzx6y_H4sYcigO))t9MSaeizTNH{*!DpOD9M9GM(pGbTO z3sof@0~kZh(Lr;2I8qXB-DP)g?&>;qbn`~GnTn}2ZaUcVk};y3E)y|6ocv|qoGLhg zrFmYuym!Zsy3gt8MyF3@l9LH16awucZhomU5Hkn}8*H`T&z)08GYlTxjm9iinLa=n zQFfwiqqJ)6PzBOuH|6USfu^HXfZzA4szog)XVN|(WmDhkhEZS&f;3QxLUJ`a0NO~Q zkaZmoP-{vrK>@)FpV@a+Ai{&RpqhYvau+jjKgHrPAzCcGcy-tU^Pw(K20HQf_7?UB z^(*Q|Ib?=%1^N1^l5fhxr9>SklPD;wLTgn);>hlxi*W~~RnECN4{F6J^dz{A?g1Hr z)K0x*A$L^me)G~6e3SxJEiwkcqdlRr(OA+zM2R)r{pXvpOz4Ln|BAAP#2bYwH9a%9 zBWg1hK+TeOgf2Z(kU~&P0aPhQl090-R-$2d`uO~3gCrvo?@N+TqltRvV)3M)DT9Qd zwlG4dUcPKmYLPrb_LQ9XiM9rx-PxVoow0-&iAl~l84w$}*qtVHf>kw9JzcMqLMZ^} z+v?H*IR(QYNu!q==HQgWo;qo=;q>&{ zaZ?PV$Y()`kUy+~Bm?@AS!uZKZxqbHsUkO_9lA{-(G2?=8wcWc)|s;kBAL*M4yM1# zE@CLTSM@$&v3u5GP+K^bzP0Y$C@65UdY;K%)~nD*Xlf_}rOCGt*A~-@V*ux*htc%U z(b!04Fi(W%JtE!$@{l&Dkb&fbyqJa#iLB8pNvdA1VT2ehQserY(|e~g=TF;1$>SJ( zrrnDzOrFdqcW%cGMvTD}A(tt|{=$4EVH^_zHEKDT8chbbCljHeyE#@31Pv1q(L2jR z07bjg)ytO-5Cp-&0)t@N=-R`4jP48MxSCZn2h0%%29l5Su}S18TatM+gH&@|mD z=oF5VA9*-xhTRAlcA-Q}-~n;C@@-EY_KFxD2ps7**Fbi;m6lFeXK)SK4_86(ZA7s3 zbYOCvrF?UuypULdx#?}B)ja`u*OEg9P#F6|2>|IeY_UuN!mw^k3{6B5cH7keD&72q zH)O5}c73|TZ!hTwpGNk!4Q`)lH6=rg2=c;bWCOC~xPjGdInE?`T&7baE<@$YfDI4i z9g|L4BJClABaXfYh|fe3A;V!Y?-4vQ0DQbxq>45ujLxaWM$nv9&v*!dyg2AZS zH&GjoeVt^ETpmGt4VS4YkHqYd8xH8P1VG4ocRnVPI9ri@wXPhA5e1hIUAHg-LJzN| zTB*}bp7D&b!lxX%!e)mdH9lhy#cv)4o^r?@n~Nu%pl8pv7&dv%_%XwP>4Q}W^DS&N z&0UA;D1q=v{7zPMf84Z&(|Zhy=VrsXjE@WiI>4kGrokO@@bmFPilYR3M+ppnU1Gv7 zM{_Dto-ZrBQGC@w7aszMGhRi3D8b2a!s;km^)qggaeWwl7(q3~Av{cZ^xHe9Pj9mf zl&Hn!*hl zMh5h01(v_iIosZ1?AbvoGe$s-88yS4?>Bxk$H3rC;+JcM_*p}{TxJ-vBF|pa(g^v| zj|pXc{o3KVdL9=HvzaVq@1i#h8{MV;1;cxXpB{c;_`TT@lDs`TGrKl>{_L}s-v8KV zPH*8deVys9pW^s&&ROFy`F++eJj(GbjzfO`F2`?i?%Ib3I6jW!G6!oOCV#+R*E?); zEOVUZzz{Nh4)>nq_#F=I4caw+o8v>=doh21h2xhwevD(ZHT<8P|2oIN;rJQSJ%hjR z#m4Z^()0UgE$!jQ@o~+z@G)5vFdpQ8{3aT-0V7;dWe>(HeY{{}wB)946XIZ(1;h17 zO-+LXM%X{1dUWS|KPRer7D%YLm!%=@%!V*DbuB#@3(K|G7MRxsW-&v`XAGPO?g43x zv!)f-upt1l@-gpcI(VdHv>MdZCpX8S8sHVdepTw>3MEJYg|b~VbAU)8$pls!4a9ym z(H|6u6+zz*$}VW!fRmmezzfkz&`h(W+zXA^^0m1$JV2m@V4>J5c%)d->XT>iyU>(i z#j`|%N3}Ij#I-UqguKZd@J!|T7OWitr#Vdqx<-_Z;V5{j@-xSl27D$C&7rL#AULei zX5ho#jTM8d#Rdz#G#tn=%}j<;x!Jb-9jyw6&JEg+g+L5z)618IabmpPT1;>r#8cFPKJ=DAkpe)d>zMDCs=x+^Yb=GL7V0#y_K2z%!I{gW-bjBKOnK$Dm;Zv0by znEDfEBz=YAtWoPX0(G6ME=?1Wq6{tN{t%q69NJ02*?j6Gp=cJP%gAso2wK7TP?wyr zG$o-$slBF{2lq$clS`e(lV;o007A>KIXIpSR2Op@kCZ%lMm$s1^)m_J7^jH;g!F`C^RGFX>F zju;OYu`|Z>?eA6k)X_CsyX-Mp=keg3tD*5EaxY>Er;tNeJXryo18%r67Iow1;pD$V zU)*kvh`z)-L6;K?nJEL%qy3?x@wQd@cVa0Tzb|vNyo4O^X8CheLcY=e&WJ~Oc?U!| zTQr7;06GQVqpA==FEi)cyx5KJ04|jjbOlulgMRZ$R`oAs!xuCNh5S{%NK%{UR1r~E zwNX$e4)r|3Ed_Q>l_^!8p)L?(jo(CeE!X z7)8)}WC9f|%$$t#lsHVw%x&U@bm~j>;B^5IdHejSULdKCrKaZejNDYyVQwugK~Y<$ z(MtmiD})Nxhs#pINBup939=Y%0%?#`BY0R%skj4UwG9D$P?D(ATs10t@gf~DHf6Ri z%XqX(g=x0feNVqH@epINAU~dE*$*bJa^exfvhQYztvrghbn;GB?YMqS$XyMCPi3*5#yg%OEL^LiaWwf}!AXJ3LW%XbDU=h!RN7Qb+nG)3et}66|Red^#8iuJXf*p-Crlh&dscp0ggpTUgWx zgMm1?9_Zro{5;X^4%q9)tt-N}m+?(job z11eE~;d`lOS!IYm5gt2CAnQHWD*43TR!F;J6zpY^lq%s$vcAUZv~rWmkVV4S(yUx~ z%et|?zfmxUmJVTLOVcPA%aeBlAO0cEAy4D)6Tcoq&wTz25-^~~bhSJ^ujy+UtECnK zhFbDqzyZ)rC%7jz`5g*}w z18Q)9M<8ST=1G1Qw6+yY2-ge-P~xE+;Ne3$ax$;DWjVFIC(A-G_hSFR9|$p|a4eDT zF@j+cyF{E7(h`8NpfLNA0&i_(d@5kY`~|U!CY0k{gxQtyrBM9tDi}ubIW{Yq;}>n~ zQ(Aa~?m3eoR)t*cs>wl4)iu`ybcFNeDP@mLkzhrw2EaTW&<_*JAp*XV45>lb>3Npy z1vrRV!;W`FmoKh!SL#jV_RD2ON`AapH z)U`Ds(ybrKv^%gInkyhS-WZnHj8N6)nsPk2QGAGawwScm!`6}@ExmM2^9wui*AdZ^ zJ1>U_OMIZDjr(q6-(S@1?@t+y?_0zytcW;KA-F81xgT-$EpEeyd%h+qdX&VHVsj@q zBDiqE2ma-#X5|(lm>%6SBJT@>erS2^$7wWe;>ZGF&Q{25Ma_3(sOK`69_yNdh5)ev zTk}I8ceuMeF=!D~M8as#cRWfVdFwE$<8LZqYa1Ilj5-)(DJ7}sNqGy>bvKAdUYm`P z&sw%rrW2<+PJZac$6KIP@48A~oHUSTrvy|8GKA;rb)hWmyKa%5U8tv0;H!7$~N z>`hI<64S@92D2;3hF9an+1@L~y$nbec|Q>=5JXl>Q455GqgYHISe>w43U-&QM#%;% z40zM?42#szAh$mh-trK9w@o1HGdRbds+MDw@aR~6>hJaku<8;^`#!DzvHqv~Us(Fk zW3TFeesZUO-{j8Zf8%)RKj<4i+BgdC;yqZIwjK9e77dYO=@!xQ~f#dZYujKelj{lM4J30Oi$ER?-l;iJn z{9TT3<#-9l_i%hO$KT-iB#w{gxU;mh^f6goNYv~uT3@ie0F}|=1u=z01P*Qc-S{gs zdq99-&HOgQLjH)&Mu!JLmtF&hK&y&I(J&4<*O~``PRuD9Q@}4HOervwK(By6&8WXS9(wifoN4VG*XFyi3QyfJ8?vV0Vhw>W%5;y7~m=n&{dIQ#e-4$U&KYV(1A_T|j=>Mr$SFC-_`&w%;BKfGmndogmuE z$5kH8>(kPB<0PgjP6gRUTjS`h^o>pcJsEE%u2;N|rzgW=NT5m92HcxY8oyGN^K{ zv57`|)*`Nm4|p5#Ju(Ny=JpkQi}=94x8V)?l3jNL;*wbf|0NmQ6LNEP9B^<|jI3K9(##RtcPEQp$naUaINzsEqmP?1G) zAQ_-71GKt4@1J^!Fn$L-2H$aW+zi-=mhM`qIf0aO)OxjU|MTibTlQ^RlTplRCiWcf zBNQz|zF8Pf0?wrkPS)}-Vf=WMH$;0;l15KtXe<|R3FB>4cgag#fj=sC!_5J?=RN29 znCyMcr~&g@QZH1b+Jqt) zh-h-#Q9)Erboi!*FbFKhR7|WdO{+l9lavYj7@yjts%S{O8|Os#8yx|Bt|eAm;dPI> zg0rm)+o4;Ozz%Z|qxNKlWS{1X`s}gjRdz*xO=AhVT>qWrG%8|Vg-{fLIqzn-$)w`D zKqV`z7b+_RAPX&mRDh+TflL!Pz^C~irG$J^|1sV4?fyB30Oe2b}1&;IRRP^ER0ZlL|GMI&L-~$p;}>T&f``;K`}{d3GrXIk)rnr z1JW1y6V;khiQ$4@hqS{MX`+otEEYjTp1dn~_&@l1LEkOwNmF=0n4ua+^`Nj=CBP{i z-!sZ)w`#Z*af{W&l>e3g&~!q@7s<^!Z*vP~ z;)-OMbM$SSk*0%@PIuB5@QC72z73&?Z;F|Mi=w-m9|AjPZao!&60CffRD7fZDsWY^ zoxWT@t z%G_W`T3NOz(h7A%Mrk2SZe&l5P6+75$_M$R5R}YHFRZ?4Owk<-! z_=7O?7Y{G?{n`62&S{nI?1gjPDyB{Ik}|tqs6<@y64l25qYk{)muk^6xBiX*u!wUeg^8pErE}>8)5bIMb&(iH#o>8EmlZ>IRHXTF(v<;3uNn7_0+3js|DCe7we)`FO@ubuBW^!rYsO@iecM_dXEicwBd{&Kb>BbT(ot zvKXnWoJC6FhXwAc(jUZuIA^yXDh(1%&RBWnjqV(n^g#}a-*ud=)tMKO!ctcfv?cy3 z?+yJFDZ;km2Tz0h>i@#cesZ@l><(KZ9^OWA&U_x&FnTn#@xMTFq}94-gF`Mk@f{Q< znoHmW*crSUxK%UxvK!@9Xeic;rYtH{Uv6xhN?SbV1*I@hT<2MfMCd-GHN{(;kpqjm zsP1<@cke~fBp19^-;1BghneQ`h!^)bJy{cMAhdDLwergyuDYfj1-|}A_iv3Gcz8=Z z6ZA@97eQO7_)b(t)MN;}3uKeR&0Y@-T0Fd6;23&?#x1-f6(K@8|2WoMBa@T%#t$Gf{mXueMCvB<^S`X5fD>>)n zqXJq`fCF4=%T%$G<|#<2!8fHz_a3}2A)nj9()db3HoiZAGeyVMi&V?qErGex{j-mi(!FkiGC8JqHV# zZm48Z%A(n(;o#gpQ3n9cL}6#<8`pVsj>P#(eFM)_si;l#c0BB@dVdCJ65WJ}Dz)dS zHg}dHJFUY%G+3(1ehng%@@%zE=HcStfMsD*c9l^d^$$ghxhCi1 zHjI9jO+6AQpL)dWK1(f~O{QwhRk#rWbgPbK0+hC(n&i-C_2sFP*R>i`Em4J!jGcDL z%wlS|XmoKn9I8d&q)=sNs}q^ULxg$*n4rC-P7x+ogjlV>0>fUQ$72m~D&K-a28`(# zN2~TF9i{RIKzX6zGM4#AdRT)HJ=PT=l}R=$;1tG`M`iR;B~*f`}o%m_KxHV5)}4dv|i#+@W#E#UwmJEgzmV?jM~o1%D|=u=kQ-0H)>&Yfp$J5a&gDKrXPj{jU~b^3x(;33XgJa zdGe)P3imaZXbG`&lJt5EARfq$Gb)8q+io1muf`&(0wZiar?!SCUq`1BpGZ+pDair@wNqtxJ<)XelpFsrXs|REN#n(y+V!a_}TT~aoNGR zrpTB%0wSQbKnzAk>Q!MySg^(S5|gcu*S8`m6Or>*@WBO$g7#Bpdw=? ztcfi88cU<5KD8!*f-EPocpS6!{bJ1Ens>r6UoB)t%bC}?#O_&GUZg~phW#WG{w-!tyyMolU_dQpb^QCQhQEu zoi}t`I7p+&@Ga$OtUO3Qu%Wo-w76VUg3|Sja2y$kWSNBz^Bl1+$21OfjF7P8{3}~rXM2bwfW2A!EA+d=BkIA98c%C!hzjpHskL#j!PWaW(M|Z7_j3E&*eDa!1iOPhk^T3$nej2O!97#s@Z%vigzAkvzOjp>H4jo2W{ij#YtlXywZ13KiE;YJYVw3ryA%>X+i-BM<~aI75T^dc_5 z1Uv-Rn2VTC{8SU4_u6|vU@=kLy&2q6)*>Lva7!TMf3Qu0ss)pKAOD7ZiRN>31!_d5 z$)l_Tw-1Q}R}hH$w1j`ez{GPrZ%vcY!Ycs4-OLY#yo~A=hw<{@R-szZb5S1ZJr%lY#n6i&K-D~hv8`XA^$31P)C%OoI>N$v zXHuQ}lmYy`ZoZR3VfzKY}h0K1rK!wKf=$ccmoNeYJw5nw||8!!PWG<#MmV z5MO*PV}{RMm{_oqvDJoN`Zc$13Z&r1>S*o+?$hhLJkQjQtJki^9mK_Pkw46PZQ` z#)g;j5P!D}VWTv+A~j;shenI&XiIX@K%~6!sdY|8zItK0L)l98W?}H~f2?)mE+#s& zu*J!un`6a`FG&Svu)h=^WO6&TAK*n(6-(U`hZXwqB^fPxk!YJf4MMG4Ad~1H33!UX z!6=lq@WA?Mo<3>@Xt+vNd|FIMoiNgR<60_R6vh=$WdTj)s36Q`+vO$|)r2P<9D%UG zniUG+s?`|f3SuW=5JGk*pN38meXU@#Gr`dh!5Uzg_-Y1ixTw4!OJ+zmoF_+tbb^b; z_ae|>9#jog2LAal`vIc0L~l_8cG{cwd32Ou7*u`KDxK@x5h3v;<6anDAaE|)vM%{J zNE7=Evjtg*IZ5u}$y z?8@jOaPFU~pjBb+xWGSC?S?0CS;$sAAIQV`@T{=VNxWbmdK7Ur0}6nQkWc7*r^~{4wGx52-QGKIbh*uJp8#C z$>6$b8E78WijSIf;%Jay8xLkM2qiDGq(cjU(GX-MySA>PU7}3$L+Jnq#K-&uYAB3PPwTNzDP}&8R(jQ{WdYLS-=W(;1zxvZM8Yn(dVp6$kRXD-x=4hNSly-|~mEiv6; zVPZjmcu7`e5fDNeTT^JwjI^T#b;Jx+VK}scpsVx6bMzP6C6I{H=;aEOWx1Ase7(Da zs{v9+we@RTv=3xcpE_2R2AIa^EYnAA}6jKu}_YxRQuQyu?l_3JH}KJs|zF-TU28 zQ&!ZFqim<+k%J z1;GJ27AR6CqZlJVSu(&L6r)3ttLH@knnG_ueCYYyWSrx1AYs-JJT)3B0}~U0 zkKVOXh8BA*O=T{`%eT4Vw#Dk@t}RJsSxZyjEzpLzKt*g;)zr2GpzArMz8h9l%@46Lso6cyL&@li^aw~q34QVg1`=$XN zv?avP3Bnj3GlV&k@h-7OQ`E)t!Lt5_?`7zw7w7dtdGP5QXih2)oszN6PD}Oh;+y!# ziMS*Abhv?6Sj2i((o9ypF=*_>3U8XJY?z92Hd3%a8}!k zN#O>4r^)Jchs|Kb>ofs{8N+13?DfgKHX+TxLujAr^!YX;dGHnaG_x2WAcM-&h+J@1 zh(9s8hBo`xWSOlob=sfykC`eHB#a_4m+)KdK`#3~$#4X3Fe^$iwL}QGqv*%qMM=pd zfHGBL3G3|C{)i$%KS~r`M09+4=|V$J77nd60%@Qpa%pRjc}Q^8f?*o0Cp>XnGhW5= z~s73`}@0>cOU3}rTaeim-veA zD|r6e^ji1w$#+h^vwJx`^W{t3u#Ju6EgY}m_~RT;Tk40;!gBG7rKRq-IewGlT^#=x z2c9#-r}OuxIewDk+xY#-{QYr`f6np!{AL9~_dbq)#_>-%{sG6|@`4a=e7|pXc}yj(^DUtsH-cQv~VnXi4WC5_K`Qj@$id3qi?cpiU;xmmuH_4&k=04Bi z3D7(kK*I%MPjE|{)<4`m46ZpEk)#<=o-pWEBM;lqwL3!Z8`yEMUvyYvLVv6(q>m{k zPXtGnj|&J-GZCMlTqu|Q{HQu;?rBUR2df$e-5at&*Oh`(MV%q=@c7YKP(A7q>mlr(_KLqsBoh9Y{BZJ!9vX&Wd=Og=Pn8Ut zNyS1iqq6B89%&KfZXO%d@j0=cZFV@|q%=gX*+U#E^!|aB&zVXNt5=Xq&L{_Wq{Xt8 zO2hZ!L~*g>IS{~J8-w!)_lM@WakO`Mx}84F(3EUmsr@I03jM;a^YZvgbRb>^CVzA45$)Npen!0kUW~)Q^UPn3F z+~^Yh8m*o%`?_W4MVsl^m>!}>sW+l>t-}uf@#<7($+S*X)1hPiz8T~-h6bETbrlEF zv5K#$okDz79`>pf@cM$})yEsW!Hy^@0q~+}U#lo7edfTKn78Jc_R#Yg%|MFK^0K>v z6US7`?YmHRIL#}SSAG~16;4e+WzLn7tjzE<^TwI3-suYIjeB=uK!~r3Gv??51M8cx zjiA2hwU2H>W1OKX1V;nz7*1Su8pW3w?s`0+MG%kRJQT-%!Ch4_u6C2hvoxXt0t#!` zUo-)hpxtw4XS4fGonm34wxTF869P^L^3m2h z{Tp-(Se@H(yK#j9ti3So!O%70qG^YLtE3N<;3;?pU!7Z)lT3f910yCIr{yULmnUCL z9+*;~g?3NWY%KJJm|6>DF{5}Uc|=+maThi=%=!oA5hUW z&8oh$flxC=?4(xCuCqy~H#Gqg=u8131@S9Y5A9<%KfQB4!Rsv?Q+7?iObbGK*&D#( zscp1cBpL=1GgXb1GAI$vGINpIx1bxU<$uY2p4q8=YQ)MUj{&ciq4ZC_7YobI;Kr7d z@16EfXyNf`ezL>*=gB;h*TqOpS$;08ZhDV+8!<%2VOUSW8!z4_vw@@6++pW5Kv^QY;q%ODh7e}R|wVj%gWfI zsj}!Kg-COu5R-sMeT45dyv@L<$p|3`aDbbxn73(3Osl51ipNt-e~zn-a>OLCtb{2S zsR0=o2NjV%k0>~R8Ms6POKL7U$4SHo`6bu~*^5x|(q|y^DI005 zlFyM#dJf%Cfpm|Mt?hX0@Iw~7+QJl3m#HJDE@+{9;#Y@NM6u5Zp-DnYpsH3BG1R?Q z-yZXITZ{=V7yU z(O5QuRn38}8gXH#4@X8av6L|0`p5`+TwDbtk(_-LVJ2zHJY}rW6}$v|uVEqOQQEr< zh%Mp<*`yI{4f>yf4c?Ek`aa6NG5_=WEUc{RMBN zis`c6hQo?C6v_+#(g3opI)KDm$ykUqi%PJ!vebWi!^IlkIGg-T7H!b^m8ag-zp>k#JUDrF|1Cf_ahhxs+>We4&%Z&XL zygU66hj?_81FwOOS^g=jH>Mxp_%|F+aWJDl{r4PT+3BxwfQvhE@j1@_6)QO&U^mM7 zQco0>w{#c#AL`%I|IktgI^F*1Eav!F@oc5b>Ui{0F{htk)%bg>Qa1R34vA_QG#1EK z4()y~=25$7+p6$QjgJzII-RLh=l^T9yw7*RH#`d}xy>(^taBX%$Zu3#Kd3wGQ-^k* z8D-WqGxt__0i6~+g~rZS0u&dF<{pEkupgt$`@cD%2Z^D1FCf@4`gr2RXZeatpIt2; z{hd?T%idN zhNZ%{9|9P;-b7~WZ%w9!ngY#zhU&|)Kv5qmsKuQEFW#>SMB(r?0ujV@A&8;b2W(9E zMH6BG00*=kD<3h0;g!O8JSoO(VGs&or+I5aQ?OL4L;MpGieN!oJA3H4GyU?FkCAFz z>Q9KQ8u0XCs0fxFThTm@VOo)y%?a+XZsDP8(s(1V?wTuo{2}(sgs2(DtpKFr{4f>) z+nu}DCxYB~|3?;zhsRnA;F13kFmLCEa@ttu$q5^=d5lx@@V{@FQ$&5p8i5Sly6d`n z)0*M<-I8$y(ciD>{6rquoV1N4!I}YzALilJJY43gAf}P4h1peX@>1mjX9dJn`IU4jioq9q0{Wu^AmSJY`{5S||1j zZA+G}c!j7?C)xBk^Nwr5-eQ5urcNS;b{zj-nl`fnkGqRSwd2v`pR8^51IifhjIu@R zKpovNj`8(U9=-TfMbz&!2cv!J*bY9x%6v+wV%naVErmmzztWwLSCa0NzAno2sdC6W zg;xu2o3=?A(@ehY)bO#SX=M}hX`JnV-)UN%Hw^Ch0Wt$BlyiWe11&>AR+ zj8R8Y-|z0TDfBuwjSii6 ztm;M-{!9pLF3-V0R1DJg0Ft45Rg%cbnKxr7@m80csxNAZ=Kv$g-<9dqTpGRShHNq@ z^9vAp7BG#f=F}(pTIN4^e3fuw)i`8J&7He`bb7?z8bZaA56(oXXC0_ z57uu2|D;I(gzw@A|D(C2Fgw)|oGT$;-I0IHm@<23WbwNI3gD7roJkU)SaC@zklB2nys#VjzzT4#Y6 z&&IakbcdRwdbA{=@kF(SkrlB|?M4h}0I-CFLpFe22$@mnOEgLkL&5IP$IPJcQA3)T z`=I>5M01Z;=(Yl6hAtH6gec;Yi3H#{OMRgS)jjRfpBv`qoQN8v9Uh<AmX8lDJhNe^YXrMiS)~QY3{9Ipq-wtXDO?gxg;_0B1zKVBQgTx}t-Oho=G+F= zqqLe>@vooh^gkA=CV;q_F*8`N6nsRD4H)GI#YP#NuV<|`<M0dldJ@o8Uw=RY( z6)P6sm%KOw4xRp@-Na9{6e>45-Ntp_?kxR`k3x?b4&7*nXL z|3{>>cQ=@;)sZZcj_6^)P93$S!Wq+=3WQ*vjng#{rfRA{Wp_3rtfky?V^7aydM&H* zaibQY9k-cw#n8|Wt`B#E`EjO@s0IrPqq+vpg3N@TQh3K%N@f{# zJx{%y`GtXLU`2DdddrS~?W$~GvhR5J5RNrLMJk(EXB?T=V(&I3coli7ZYZ>Dc#bK@ zLTuwCBzcW<{E_aO$X6b(;xnCT_|bSWeHHIO7ajXj)GbM0RbHH)YH#P|B?OwhFt0$~ zT)fccD!4UOlgLlm(Tsg#d6V*{0xlIO^fj%@6`N}&4$TcEGxKSbkpNzqt>Ff5I3J7t z%X^vEA+LQBTaq?lbK?az1<3i{%WK{TI@d3&b7zK0eS-9%1eF0z&qzi*yC&&H*KJE9 zPd0t^Oo%{8YV)8N=wQEVbb0o@F|R0(zB|u~Te*OyQJ!6#PzSA|ZAO7Soh*e$Js|m| zk}^KiRwj?EVI#IZR*S(8LykLt$!osY-54a8Q1 zBO}qFAnoGC!VuQJCcW4e_(XTKEZHtQ;2u>}59E=siejiHjVGIe(6tSyF43IU4fe*5 zJ8a~6!KBT7N@gu0 z7MekArWe&esS#qzHyw@HixoF%o-WtC4Vhx1he--AN~xqVhqR5qcf4#>1RtTG%hO+| z`Jo1{5CC$qD$os`2+{JyIhN8ODUgIH<@7RPk$NXxuJn9XbT7N(w}K@g{*4VVM)zd0 z0Gb4h_G|?uSrI6{-uS{=<*F7Kv~!)d($bn!dL_JmS*u1Ve%B3 zKnbaGq)>FNv(6SaXn9WXB>%%sAC$h7CW)b$d%Cup%2L5*IZZK@z$XSQ0+`Eaw8TB5 z=tW(RFwk=?6O2YOw1dS z16TQ02)#&pjV}?FhzCh%n<`7*W;Tn8>Fu2PZn8!^VFp6Pqvxv06rc?`_bCeoveO05#qM7;9*YccU_92=Spm>Ss=oDnV~iNm|6{LrR?a}!C=UcmADVQ zm|~#fB?GNALM^eG9Nc#wj`OggjC;^QZ%cTN?;+I5wEO2$M&L&NAdMP(9q z4K;ij_Sn3gb$=lB&TjwzQua3Bnq^mA=RNN^=Y8+@y;ZmFz4g&uU0pX-m2{Kps;<6O zBq7x_(m~9~*G%&fBSbPWkf2CH#0fYFCJMud1_SZ&(V57HWHjS6qL}~=DEc6xAi?27 z)F_M-Cp<(LhVXzP>7L*3zxH`=B|f57z308>J!hZ2*Is+=eb#@iwfElG=;tgCS~i}v znm6cn2nui51xOk`z&GaEE>_l`{kq|I_dDb3#~&MiYW(@}7sp>>UhThp;`7EoGX9a3 z-SIW!YuFuqmx;H1)>YF3hi7lL#-Ya#FWw9<-c0Y_9M2ryyR6stTm1c1jt_8rnB)KB zAUd`C1rEJ@Jx9m+`#JCicJ4X$L5`p1z{5BGp>caW!-{j~RyM}Z?|)+WCmdhW|3v>` zj-TQDfA4>ydHnSHHNW5QI03i?uh#Tl=P#K&Weg*wVc3&O#kD@I*=zNGz_0%8`deo4^=cK-)85gEzSlJ-F~@RNN{?|C>RK zY>O?J$qBV$gY)x9`iIAnG|s)$D&VAr!blE2{*$V`Vcp#yhl;84~J@}ig2ep zY-q*{4PE>R`$7Uy2$@}uVAb zr*CQ%k6v}1YrMfL3Fqey^KRa`fC|ne#TDIRN3}g1~LVHp~Zm06m-o2WTO4e3|Ry$6nhw zB^xMH{Sq{~OPFv22s43p&l3~M8~zYuL5oIYjo)<~pC~-M{QI5LJN&KZs60+H?BmuH z?vNiW3!R#^_u058fk>&OI+6jwC?cpxTvE4&lQIdV9Sseoj~w_f!-eJO3TMwojp9lG z?E(8(9nSVKLgZ&L%TGCBZtgN&Z@pVT-q|~R(zPw`;=3V z;lH%CmBv3cuvodLeRq}_*W%-~&A2kGWaJY5nE*yHbr2Rxg1K5D!41d(8H1h0T_!kZ zhT0@_@J0(n=I_EJ7+%V8eMhReWIUjnwd%5U$B%NDX}U-?4H6=M!`=}X3&JYn3(#_SM3J1B$O;e zMK*FO`J^(IOwFyA$UkIi8G~>h-$jxg?nnUG(rw9yFKD8gQb-Zj$k2pfW~X{GQt_K6~gYr`i$~7{?ia@iHOc}BP>O&T} z@HdgYJjH2BV7y@)Bz`iIl+mDoauTHH>SiX^KBJyB$~1hZ6+j!rCl`I_26ZxI&~UN! z>iTBP;5K6qlj0c+WI+X7JC9si6^fnH!sfQ&mCccEYy-s+X9qJYWV53B;kGrgrHBAw z&}0yBl!VtAV`AH?GHfThPg>(~iy|r@c4*|Cy&7yVQq$iZ=4+r&@Q=1R#v)c>aKI#< z15QzIN+c_Om8fIYJ3H`FosT!{K#V$+=O3$(Up98yakKkWdxpEsSzI%&_y+Vg?^bTn zbQv<-2rT9={J{5(ALzFCX&O6{ZAS<#+|hyE*VH_utC#CprE&hcit73B`E=lil9R?`jgvdfFZfWFs2Y`KdNsYfncl7Nf>ZT)MZBx6y)$%nhRQ zN&TZ@`wHfaqm(OgeqJ}o*N+%}QJ|)N8V7v&`urN|w5S0M+kjLqUhkcZ$y&w*ClX^B zI9Lz?)!h$>rtnLDi&OvW*0K^3%D3kb(Q@%mplxQm1+Era7g4kw(XBxRPu^LAvU1c_ zpmRfiiTaHzP$X4o&tS)zMR;%V64Ctk7#1Gy;yj+HzvzrWm&kU;XlQFDit@1*wk^CX zbk7_{Sam-z*=?TuIIpsmFGP2$!R23=Zxk?>L3kyy<*U;RU!LU+`Dj735_|Z*o_xNUCM#=<*3vEK5gO zk79%>C;Q#e7P^+4sQn_dLSq&(=pkgs!%z6QJ80$n^4w7HAXcluWj^4!x+Vl|tL`h? zg8SY;hHIKzgpG^feSPRm3%yDWI3-b;n8<~?6oBPcSz4-=7Fd{9=&41b_!q-f^WJk! z8Rt)#SYS^*QdgHD?Cy}A#wMjf#5A-MK5s_8^-8C=#1T$T6=RDeWEZh&AbCu+=^t%= zdhP3;?Y@#9_JN9BF9B@^5T3#kAi%ZdVoa0fv|Y&_(O(F6xGp0*UM64W(9szFNyCnHA5~SEiZv1Fl$Cw)VA69A?rwm9VrK6dKhn zebzWE$cB{_P&80j41)Cq%1c``q#D?3M9<1Uy!A+$&9q2@Ho`lV6D#9;lQ+EpPQBf? zRGig;Uj)5z$F!;xv&A$2p4&oP)9QKTRf>ikFDA**7NWEGwkM&KqQmVnPe#Qq6au|@ z8~cj%!wVT2C={JW1AWllu)Di6yUoK;crYK>6f3?N(IW+AC(B?S9)hOqVMWTgGg5DI z)K(!;1M|9E3&-Na>u5vKUm$Nsq~iX?+^Z?0c}J@$=HZ|SY7T&V(G|w*Z2G5a*__R|Ou~yJRb2aRMDqL@3D+Dh}3FRO!JGF=lrO_m; z1FzX60+A|F3PsvL6m=S!_``pysUd@GVxSOesXwD`nR&mtSb#uK=#?i0F!&cr!w%9U zbr5Ih*RTPNu!T8zphq$ZGBj*2MhYHGS$PDNQWagy=7jh3(JPI2$HPUvCTsbTEa$(0mrfk zzqDYi!HK`IanYu>_5@<~8ygGqM!mCiU422^w&*E*jKWB;d1hGm&(*8^+S%}PpwXXH zXP&>6tYk;V6P;v`EUxp^uI2^L$R&W&HSZ0PCVMWr$Wsv6a=6LJ(o!389P*QtOtnSv z6yi)1LcY7k#;-{2-lJHivztP`TSIeFL_OZ_ z@C{kWsalJn6q*9aXo=@M?>UQ04h>VZsg|3~$Dh2r8DHQmv(L##^dZ@Bw%qPm1Bb=l z>F4e3LEN(Dpd@A_+grq)7s9j{8|f8p3+QTIHKm}WsA1XZc`8d8GBflcOZ|mJYs)V0 z1;<3<0cJ{;K1Udd$kQ_Ke6C7n+n2iS85n z{H?VWO#kDeuNQs6tZTj&`QmBOGE$M8Tx>~j8@}k3C2r5;pxABYNXu;2HGGC3d?LR*8?Gwy~r<-7b}LGKsbd%G$)}_JGw1rlPWg9c~&qx;(NE zm%j`xor67`uovRwFG{+oK!0MqbW8B7_Y<@!V2GPKR8nxYQFH(1NBN({7s8;5)rF~n zu>0WRG;#|iHiIxK#3N{Y%Hoi?UI?;L8kj9kR}3cJ@;}*7?Kw)c2WU-J{xNp|D zVDTC6Ph584=(1fDvG_KN5Wb6rRA&N8*CdT6V~d!arm0>y8B0W1gy|s#_(z#ZAAnvw z&d1Db4%-3IDarMa3Tbx(z0B=LV0Y#bG(?g%?eEB!Dyl2tk!XjN}li(9L$GQ#hg3N)5RM+XYhg-xAu=FOnZj`OqcNN z^HcWo=@_o5kItqqd*|a1NG< zA$V9n#pJ&Y0*Skk$)BiMyoZH)Px>-#>?^;$8JU3u#cS2Fruw5rrqkg9^`_;T50VsG$kv z<=lYBG+KZq4X75V+7SQyb!IXNSwh@?9@2*DOx3RCNgd7 zzy)h?gKzD!vKAY3Z9_FK)~G+K&}u$ZJz#;lr~#?K@;pQ}l{QVm6jiD-9(XIPKpT7J zb;<@{O((F47Xh!3zqTf@BOi1c9tZRq^XTm2WlLQ(J6o!GaBa4D9PxWcZDhh z8VRKFQY-6`*!}#Zh?MPOQK+1%$~s%pz;03{@8jV;oL!DS_%mx*gU+ZRbiuD zCp6&A;@PS?ViTcd_W2rWLrtEu$fyfM^0O~(s4JO!VBi2ST2ptv$iDVxxzC#_c!rd z3ag^DE4Sb|$&hF$-h6>Skb34PGqkYAHMjaxI83esls_8=(lcI%;E+#9!Pte`4f;2j zg0$GgRNJi^ozTa*!gU%~mrl<3j`u0Tc8W*~e&V;G8MM00**bW4Sld*7OX{g=XlxlY z=aZLeVVGoUk)b^#!!t#`I$#F7Kx^DXW&&O8A!EM}n6FEA1L^Z3R~SI32ZG|@=2|)F zp!sEGI5AsVBnAffsYF3?olrFOt}9f6Y({ z+!&-I?BqG0Z@E?xA$$yS4tuN}&S>Dq&P^_a!Q!-PUoKw!UCK4@bt$LPjXj>wOS0U%uFZf=;-+?2YhME6tqC(h zk`yKkUcBk3C4p8BWQb5Qv0MgG_}{tXxS zOP=I=KDNJNGqEcfX?Yt}Cr~Vvd&m<1#^8zOp;*JAaKegGe%@1LO>UIe#8qN({9AK4 za3~duI`#Vn$>#7E_ZYz&RiSkCh->6ZxzQ?^8pD6R&%?&}ZL;}E9$f*6uRZ%k!`ICI zefJ&14~^HxmyiGL_#NZ-jNdo@-SHoe?_2q)Cw_0gJ^K)P(1-f<*@woT#J39~ceSRz z2MvFS<6m)ngyT0kewE{wIDV1iUvPYwgP88wzvcM9IsO?3aoeu^H2xTQ>~gGf;F_I* z+Gjt+$x8PZyQjhb|4sL~-S@71W%rj?IyUe9VD|=&V~$5SKG^;2J#pR5mHP<)P9_Y0 z!dcB9HmqB)9esRK_Did8j!>~xTM?9#(r3%73z)c};#-RrtNunO9&-|Hm{3h%KxLN_ zga!CB#Q}nV<}pS~o)#aD+lzqL>qB)W?#Bl#hg132NBsKsv2-Y z@Zs9M_*WRo2RqCpFkkgEdgW} z_Tl{mLdLtvi?868LEV6|IF6@_%)J`i6Vmr=G$CGiZk6lntq>v#G$r*Vt!96ffH<2>gf+OF>a()VWUd=}d+bwh@e- z=w-A9tqX!l$e-_9^=#52_R?E!HRE{VExxw18gxfFlm~IBW!n6yEp+L`&3ij;;^DVV zz&o3Dq@scbyYO-+fP2C)J_yfbkJ(VwM1P*$!xWMieQ6z0E~fpQOChivRZE^mV}Rx~ zLEOLHCH2hAI1l~&h>2IjdLz*0!_~AQ zm0w$9DIabyg;@`f8b6Prv61Ee-~u|B63TbEYvi~Zk!_B~XbHp*kf&}X9_!c_nEhpH zftXSLsFrWITb^N-h#5$_hbtHz&VaV){$ye~8-eaaZJPW@x1S7pZ@1+Ysg_Y`_SG z-HlJ0DuO?s>#&yqMfnsi$~&0gDhJSrG(do>>JH`0hH-|-gOb0}R!*Ogh0NQJO@(t2 zy<4@lCg@f_`lm5`^!GtTyxNt#VjC=SglOsB>_O9Hg%gn>LnvN`oHWG^F3H!xWIBwwjm%;?&XMmpsI_+@+@+n4M+Z0YI1+(m6~Zw%(k(M)=HW3%k3uq&ZzXVLL!8MoDJj zZ=T!0=!RV}>*Iws*Md+QGdfZk%2pOFG}4`VFd$OLV42u^EOXr2ys))7&L4f@qw}?! z7cSgf8wn0#0A|L6%~O^dB2h5Wwug%u8B~Xbh&3&zr%@rc=L0Aee>h~f_;kICT$idM z$#|Wd+bd!rq(p%1IXi@FBd>xXX;3XO)XJ(LuEKqmc$X^f%6@ZGe#L`6A0W%{#}J5h zjRB3p!d^u2ew96o=eZn*`DEeMf}?$I>36k}{8mVVQKl{aGBq(%^W6F zH>UlJ{)PE=274U*)cefgu#}gCpfI&?-QvkELR;l^(i7~H1rIb69M3m-J#eFJ+O@0# zWi$3dkzqCZm1VWin_@HO^4I3?mI8|6wvg*4ThQ!(AIUPc7EaYGnY7(-+wE&em>2Ly;1i9!_! z=E?Ht4oyl7O+b1@6MuIs7u97f0WJuzZ8l1dh)B8%de3Row?(#XIMRUjSKX+9jlXJ_*2<=L?0&~n2UR%c-F*os&-1u6v!e}g`uV`|3Sg{x$ zv`PE^v%SJ9V(ZVobolXpn7?QK>|(Jvw|H=IZSlzBR`>7|ub+M6?CmS}tz2OyH#4zk zzs$jm>#VVie|+{PjyoLxGsg!w{xt{w_1QZ({zndc@Utg5zKX+@!idtIeK!XY-C3FJ zOZkgGKdY0yoxfkk@d}Q|I3DEqLXOYj_;!xJ$niFgKg01_j+-1u9QgZZ`1`Yx+4K18 z3S{{DXMdD~$ndO&_DTG`%C=1JnScBIOXpuP|6k{yHGkF0WomX~{$2CGdXLHAcQ6`! zf(>E$^Wj>BEdDH2=n4e&1lS@_D@#B@iy#otfBb-jgaWc%$hRyD(>HoOFd+zSO4qPf zw!UfD0!6;S0}uho11<^n5Y9UFHGfnF<;LW^2(7u^WzlF3;s)oBnN%Eq;MGq6yfC5=a83kMfw`#%s%R<5?=8OL&2G&8$8aHW(kPvms=qj2AK>P$=UDL=es774iAa6PSAk`&DcRlM9$uuVyhu8M2v3Z&-jffP)m05|Ry@F_-mA z$9h`2%m{}hAvw4{ELTRc8q}Nx*W;R8&Eb};YWXh>0mg1SGoMeaah`b743}ZmOhk?x zxe{7gj810ZvqYIjp`P`dr@m8NMdO=p|N0-R5+MxJe+uB5YhZhU3 zk&yECuTYjm+r2F=Cq5OqG$|dxMy-)4h9E;C>=fz#cKfT7&E<-0G`ymXbC2Gh?p(XI zFr#Mtxkf<6>;>uw;3ap~h?W~=Tx~-gh1uwW9x)4eEjrsWaH7mH2eV6D1|pZA18a|4 zT)U{6O1X1d1K`X8b+>IuO=9$I6cUs}^fK&)ss zXe^=3zyy#eUJI4r1-F~p6E%~XcrA1Wq)gAIVNjKSv|&T zqN-}H4x~yPkj~!T;74vyhXzZ@iqA%t+4J;LLhFi%HhHzUL#bcsr@0IzY>vv|juf$dA{w z$)C>Q>(uFjWX39pp<{+AMHr|>a*z&HXe>+Y2`R<3#R6J576e#a>s>)b z)mOoV?~Q5JC{zbIce}PIw=@W`#xL0w@3Uiis==2S!set5jVwCYGcA)$vtZ)xsU0qy zayRLU`8d0Xy$1QfwG8>N4ckhroo14vauUO5tT_o52To=8IOv@$i&IxCF#-w zjoh*xDCKDZJ*2S{V!psGK`u_32dfy1Lw^AZ#3Z0hCxE=b6sF1uY{#Z?y-=mhZ<<5P zxEAZmMs3cdKw;COs^+ON52>L+Now2i7oa7ig>DhFJwf>i8WT7Qhe<~&I)B(=j-o_7 zoe&G$@IT-p=!fi4D>MjbD5jvar(Rh(BhYii@f6f;pARii6BwoATWC2GIZ>c+l#{@; zOn!%dQc2qgb$Hfx5Upv?sO1pqN69}qSsefU?7CksUE2lar7Kcv`4H`I>f=AL?vP#j zWCB@W-JLS>#5whB%M|0FvvoA{?ww(QkT43;(gQ4r@x@0apSAO;Y&HTzD5~1ed=Gt; z+gnVKG2R?MDG-YE5 zc-nc%&h#g{0VyoBy@{G&+5cs5%Cw`#CY5p7iSA^%m|eBsAuSpL!+*OWw`giwmG0Z> z-K+^x&wcw|i1SCAb_H=XGKI+n{t`$K#%e3jbkZ7&Q7Qu@>AXYX3PnZ*h9Jf$3dEj} z+Q@faH7)|L1?GnV3Hg6Sb=AH=JQu)ubNdl!9)g{GD+2|lWxH`Dy$ry|v5Xr1N zZ2o=sJVH-kzNVFwUT-b?sg@F`NQ~qrOaI#%$iI-RK5lhMAFtWMYELiIz;L@;2Dpl>()smrp5teS_K61&CVz5X--be zgpi^VL8VI8IMoqozae7@q=UD8;`hP}rtJEFg{UObM$54O4lcx!^S;t!U_5E-6&*{Fm`^fm@_@40x#~&I0 z>G;pb_m3Y~`AbiHO82srdH01JpT9EiiL&W`k;4@bTn*tzIa~?Bec0Z|@sBuIMKSw= zyBGMIIev=+8(LOaaJ7X$&++d#m?x7J8NQjn*!HpyIdgAZp`rU8tcd>!%NfBolTTXM z6P%=RX>EyVbK-T-4+t!AH$Xamfqx$d47XjX47LD2W&TP)@rx(p5Fo28H)RCVNYv)7 zyZbgdQW{pRDpANDs|pB>RRgC`jb?=l1Qw|rzc!jn3aRPh6s?*90!q{oH1}N75;N4rx6cE>}0T@-v zPI%i1Dp|pxfliG&Ku6%?@L7D#aR+e4g@Dy0kK`Sh zqmY_+b+RhvKBlzC=E{nMNk6=57C-8PSSX!S{^NJOMb20 z?B36N1!antl0osjx|tP$l99cFUCus`h?kHbWBhrG1!Bx&NZ`%XYyzTo07Z}6mKT&C z{LN?j{=ikj zvNkY*muw~#$C;>ciiAM-1Ob$d0-{$>;V7jLvR6AS-xuoDhS55zsh7w^6y{qsK-<;u z-pswcu?2~SqUh1(QnXWwff|?DmnkTwcsKRxd7b6fvuL4F8&K|RT4;dE(onLeyxgto zTKCLv`euh>v@A$O=_QZ?yA>ucwFPrWKgg63^h5Yqs*eb7L^mafMXC*ah!0*8iGcv{ zg%ng0cV)O;b5y858PLpIln`a7ND8PC6rcv7i^g?Wr<543gwDStzF1A@)E@h&N0lW~N^&ihf=(Wb!Ei9{*#$_7 zHj8RLxZWnf5u`_1n|AG|;E|J_Xj;aC;nD`%@fGe+%(kBnUMr{s$ljlwJv+be!TX{~ zGt;YLj5adngdX@}uA<&7BSUKtn00FQGKzVK{pG_8n*rv;@T1;6>weYBfo;Fw0YwA) zsQESP)DNFJe<3%&f&oB~Kh3ys(K^BnGOvcKfu0u32ktPAsq6;|25}}ySv5=HPgF-N zIt(1f77}t6D;lj8n<)94nO|9Gf>8;g$dd63xrIlb*;@k8Xf#`z)O0ET#uh@u)8712 z&}b|N(Da{x%o1yd^+;6^Fkp)woq@OZ03&^tB$qTd^fG9lzMF$TO@K7?h0W=dc z#BMsAPLnb`4n0-D8O;(nn;*@(p zYMnN?4wQU>JnV%$O{L3f3kg;``?+b4OAOnYky&V3PI%N+H4FenTB%;@sKMT`tLzvJ z7t$)^ldn{eFMmm08}aOm4vun(ftN9WDgfhnw`~u-6Jt^VlIOYQcQrrT?>5bOP3TEh+7Jv9Zy%P5FoU12>S`BM$Mp-qC52AR}r zfpr~q0E)2$>OYBXB8&C5@z)kqHX#$UbQzF1SD0utn3CSEQA==5MP+LVq0rkh2nvO9 zoG~Qv%O9-QxL^lA(N-NhoDa>uf>b(O1mJhDti1kmuhWY zrsQbwgO0F0>FpBjJv%K!Vs%ZH~|=EI-h zcq6#|Z*{-9a*FexW3TeBS{eE;;rJr#7fh~QP>T^XXgYiFZWLXK8e_XIcobnY}|;LG%IQdJTrHMD<=!s zMgK;1mrm|afiQvu?Fp*%ZnT%s84AZX5z_)Nj60;#yx|HO*O(PU@`U|cc)%54K$^sD zF@uBzXC}ddgQ?yzodD7kHZsvoR+cYH^ndMa9_}t3;nHd*WO@wpqPo+i!qHqqS{zt0*sGMpC}ihY1O5EUhk>q^i0eTWv!t!p0P}`h3o%ed~3;xft3k}v=`6f!WRD!daSOL;?GJ+tPYBY)^ z!LQSz1rT1JW(P?fq)28xVbD^qp?dn2#TZL2_uV0aUQvcd71Wi->=z9UnG z_qA5Yd_g3Tpt;&6a$Q}VhV+Tsw{BqWO0(4WK1GPdHT#h(V@S-0ZSv7%UJFaEJl?Dh zwwBfru>3{#{UbMLd>Js}5JK4$xf~@3l8eY-!f}lg+bI3?nEqh%a*Tw^Eaq4d{d>=h znAvlq^Yp5Fr3>(Oh{m>x!onM`&Ru={)WLyEoU3~zE#6`kKQRk>pq?{zlc*)WhCx=` zOnTmGyd!!4tJeBLJ`*^OW)KXel9+c?-gf4Aj;K=q`Z7!Is&!<=dkISUqaxz&Ybl6A zoxdAvdr*4NB5v~g$buL3=`(Cfc+RAd2o6E@I3b)<--BEx8e3}vI!){Y6o;FEH6GXk z0qQ|maiwacXl@VQ_^Gqe_xeq+b`J*n^HMbmSg;ELd9$Ka=^-u01}8W!EY$n(SQQ}p zc1nUzWDDfWTK>dr0HFwmcF%_RKji7oE*bzUK@-hOZUic`TxCVqTeWE6O}p_oOb^6^ z0JJF{Pa16wTmhj9cS!_?sZxSLNI)g@_L$tMLZpR;9fA#fPBjJ$SijME9%w)>3^!su z&6icT*!WJ3JxZF&v@ApYiSIfpOqSTV0!^>QToAm8>P+Q!0bc0qtoDzNrH+=hLnNpe z>1rxgzSVAmv#z?5@<^rDMJq)0)WDz(hKz2218^!+q$!FYq&8WU;#E_f?nf5JP>^jS zZ6aq7oUP0|@md`7>hM|efXw&c5{^i6P;n(iq#ZHbm?JQ(jRG`a7XoAjj)<3OveMt} zcnDy0d#Hj5m2W81ECE=K!H5S@lgEf8h{84OvhyU(W}egmGY#q^u#X?IX1?x1@vXt9 zT_LMWQ3X3N1Y2us+BgwY1}c8D0U^+Wg6c*CYh z0y^mGGp`8ydc|8-O zTRjb%;1wJbBIYYO(sbF42c&XPmU8<0UT zFO3UXfOWFvJJiEdZnJSf&zhkwx64)KJRAacC2ofr)Ncqr#rR6_5puh_HNXENORF{`ul7Z?%qz7SLo^&Q z3H^mu0dC}p^rh4wuV%7~0H?{!!DL!3?3p!|Ht^Zo4H$J@~oCFAv5 zE$Gg$F{i}Z(J2`lkNM}75F_=7t*w~nqYl@z*;5K64T;$l;TT>J(7Fa@{rTb3Sl{6t zE59=QALCoczc9XQe0qHE_;*(R_7gAZ-^6PD|C;}AS$RMI-?H+2{=a4AJpbRqf84|U z-{VVvGHWUJKgsc39IUU{GiRj#8V-HXtk2kkDf_?1!5WP5Q!~FLy9Im;f0^?UpY!+f z_ZvCzIrs14_zN6g&%rJNAmsn}dJm7cjr?}s_lTc=i8Go=-hF&C*~?n}vy{R5F6ZwZ zHDp(sM~!uXZkPWKAWK>xmO6Ydg5tYy&@^`<8Yp@vIh{*#724*9f?IFc)@#IMmSb(W0c6hqmDb7yCCIpZNNOY%DAg&u%g={03iD z>X%{kcTO~VS5&Z3_yC^5z${kR=F{}e)!$a>GnaxJ1+-`aSL9FPwooRZfEhKhKD5aj z@aAd@;18UEF>owkS~I6Q z6cTCgX+=#NL|D_`K}_d}k9LX5BB_f9I^W$UQUaNVlGzK**?uB?-O<&f%L^oB5^l(s zi3NusE{2CVuq}k#LNir`^i0ID+Esh*G-oG&=rjV;)~vKT+YhG4keG&UJOFc7TUCg@ z%^wsAgls96GXVB~6GRd25I0bsIH*gQxBVw^6ZU`NoW zg-yu0q=bY9OO#WYP>QKfVs*kAlSvnsuMd&-CUk;(z1r~FisgHMQSv|yr&%LN(KVg& z8qjic@;^_1rx^w~Vzv%71vBGV6yxiWx4qEDz;%~+=C#7BYUs|M%@tzUxZ;~rAX;FU z-@%94gSQ|nug?2cMMcq{J!xscl05j7?>JRNvO!tqb+-Q@-juv5EnMp`FLMwEdVWqO zPa?1BB$py`Fz7q4y;{twVeS6X3k^~vr6_L5mOp~Del1P~{J91aQkL&{E2y%rqm@@L z_0L<`T(baO^jK&Ybpc?hGv5J!Y>>yV3ChYb-rPKo)!b>Q(KKRpwl<<@YN?i`PF4Cs zYC+!qL@R_H+*}X{t||&yriOJ+-v}J11dD|BC^2wb0|VKjHgIoqTMz^T*_NF|E^V1@ z7CLus_QG0-Z(kkTKb*hWuL<5J1i@4>@yn?l_{yd%1YMT$wOQPfnt zK#>@(Y6f8$Hbn+7GjlGeKwGq@|ktC>aeMu|HjDg@-lqSgI z0o7h6A*f1OzrsY>i6DjpxQ#TR$y*zfcaON50#b`owRLJ6JA~PX2dcBpfUz@-y8zDU ze#BKux9r5Pd+vT%sn*P6(8EImM8Gf3vAfg^K85se&31FvkYR~;U0mL8u>p(s<Wxo=f zb(fy=0Lu=xRY>eCP+(tXh=$_S)eex(!t-{*GBjBOl0l61vPyIt&d9+wWt_>4`~~`0 z`!XW(6zm|w4B!dM=ILRLN zO0vBPUSYm)IYtS^fo)SbR#Zi~&A>D>pdr)hXi?_JSu5WAT-M@I?PGtl-1d|DW`7_u zWa|ZL9x_vRZ&5%frWC5>ONPiGwoS!QTcB5C`pCo`T2U@#@u?|uYyZthteUiXX>f^Q z{!>h`P-5(!v)K#NVPvC|@1UQ7D<%>nQq*gz$#~-$TyOMR%!uU0%VeKr7_>cZpNb&( zMvLW6=I}q`5S_}_mT$0|cR%%XLX7$jB$pwDez4m;X=%Q`c;zq-lE>ehQ|Aoh{kskO zKK6@k1lee$M7is%F1^<@#2-2@c+)rHvjM7 zG1g~%hokR*7by8W$KxEI!f~Ag4{?8m<0Tv~;9y0C{<$3YbL?}R=J+g*9ga1zsnaLr*x<)ivl1-CI^ZnQr1n;vZFa@CGe(TOJNPZ z<0qTxx3r)s6g7oXpXSfy1dPMS9D;hLyxOH&vQd!EjX?EE&v(oP(8Toq+lD_cr(2iF zqs^i(FTFvth{1$^tI2TX7kI|KPEGUCRVq+j{=SU24p+3ew!j*!&76AK`si(AvqiT4 zr^)tR(Ens%zU#9&VFi`L9Gt3s2f?|je5F}Zex@Y!#Sxvf$E=m-U4%w>49gTd(SO0^ zfaHSX-mEk@-VLxfP5iVL#=~GRlX$hIwJmItLT}ne4))WK7^jfZ9BK3x+W~mw=#|wP72=2AihL6=oVZBMn*H^89of|#Ko? z=nK5Yc+>Jt@qbWCmg8iC8gfG`RBNRU0B&XLx?*Ni5{HheWxd2L z#v+hU0sPo<(4sJJM77fcq=7x4mWt-Fj4D- zVUAXasz_ZSmG89hry)4h_f+dF0pQTmZ(blc_?gZr(dzZXaKH}n-3Dujo#w9k|Ij^T zi*kQ;^_d@KJumThL~XH21pcglpA%G`J(6Otj~=SO={q%YP1qV zeSDY-ZK62xn>pucqosUMrKYKiJ4h@iD__{|v-Sk8a)J_>R#Kz%Bw}dukTc23!RGqs zgly0sNCkA?N6&|&q*rGivuq$g@{sDVF`3+QTQvUj4Ek*13!baMb7#8I{T0ZfyVf%6 zG|;I%uo$wq*}ZnY>#}LuMzZsRiZ&!s2E;=q)u_;H2?%cjF{Dhbu>3?6!VP(WJUq!tY zCmQqjLZbhM@;Nnuc!D`uA$~w5n@(tM4iJ>Z7 z>UV9FULXn7ctq0pNYR(^L&ZJEbNMVaJXP1yEOBWr)oFFbe*hquLD zaxHKcps~}gnOa~ND8dsJ*IE8H3l#lUF@YFWWXrd_wiiKHLO~tY(|qC%XI}CM0HiTb zLY2E?1$5~E4-`HTD4JMYBDrxXH&1hLE(6B0c+pIp9`F347Y9heB2eF&2Dle73kGcs41Tq&$a&-*lES~R5l+E7trtlZaWo3I z#ck8d0L#Y=^xZ!!7G;vVmI`=Ob`?syOb7-KbqyP`EdA0a$zo@(=Rb!$|k3 zl`e6+rC9r_C*6zApC%lm_*IsoLgnUt z4)v@ z%;`a?jT$8qK*KoGtE8hRQ_kK2lYzwua6#3B9qClfm}9AlRQ*&u2{pkOQLj+T)mn5m zqMuxf(nXqru|A1c$?&EYRSB)q8$AzpkOj2t?CtRiM(Y;?CVz@%HIA7&s}uJiPnS&C zry)3RYmCxCbLg<#+FvKuV8nzAq5i{<;9_Asql%RlLT@VSt6+IXUIl*_vzLT}v?(>h z861ndijN|tdSEV0kSu0%4fTe|QNHD(H(B2eX3@-~xY^EFuoC$RLc0toSsys{3MgP|sG=4G+PM%8r^O1f-)(I&CBp2C(iq z^Qg}*n9fR5VYs1QNXD5nhAD7=+}mUCBO3P9=~IS0(q*y_9PtXs28=<}(;bD}kaY!8 z@%g$I_{JL3z>W5u(RQRnjh=Pig>Lz=SL3na@3)S}q=w#+RrSdyK+QRIljZD`Xx zm`?}jNFc14rA&Jz|ATR@5?GAXuz}&qF!;({0udqP z8kn#-!=dc#N)di@L@AsZhGeG>WhoaO!Zak00a=Xhphh188n|Bv*RgUq>N>rlKnYLj zHzXG;nSt0&LRseH1xF-mQ8`ICpV_MQD;Qa|GC}vSBF(ebA>EI2yjj=}DogqR9a!@M z|3Fx^iabK`kREY&5B<87pbXghs^Vrr;@iuw`#X zfIe-P36|-XIEg{ADUHghP_!&`vSr(iKYs$Cih+JsN zPtL57FB9(Ox(NYcMnqEx>&j=&YSuF+L4+r4a~xR1)a^DbSX$080bR2h1B|a#>WrB~ zKAByjS*HRR`e_jY*CRQ$34s|Ft#D;-suGtM%F{aBzIFTod9*vSDcRbye|GrUetrD! z#_t}#cjX;Vyr6#vJ}@It-_G$A$6w}nE5{o+{vgNi<9HLt>o~rS<10D7g5%3MzLNud z*t?n%7_WaN#~G z>ea`oz-XUd;u|=KvNbtXf}pY=TEY@_C>_V&re-S`<0BXW4brpg+ZOsUID=VBmH>Cxq7BQ1nb99q7GR@h`BD}yMhW9A0l*7N1; zRHw3vNNf?14Hx|Hh3i?Hoa=XPnOclc*wwlOc1pL`Y%|$t)v}&b70UzH*;em*Z=vMw z%GXU!WOA*2UEUy(3qjG7rj8#;hpr|A5nY35hukl$aq$FixEcBqdt@pwP3y#8Az#o< z{ANoa48s#O+r+5|QLy;em%#9=8Z;PNOeb8J1x;7PZ0?8sR0NJ6@ z&nJs@EyZ5LNp~E6<2;4MTmaBjv5go>>T3ijET@~6#lGz3tx4J{UZKV0wct~z7n;$g zG;|8Jm2lYW@FO8qRt0ah>7rS_yCwEd^-c++arY;F6=B6Vk!(8KA!nJ5Nq0> z8nr4&1i{oCWuG_9Z|Z+{_6Ug<-goMS*$65c7e0=r5BkqW4Eb_h(GdDz3blbidU%{s-ePx4OlH zS0B{$+1|puI?+d3lv=FW{4UHSQbo7XoyS@2pjtM{C<&&60M#9Wy<7@2x{Z*6qS4w7 zE!pQHSD3XIB?mZ!x=1`F3q-5zsEwe$^c%-c7hQiTYKSUr%I8j2JK(j?JGI|EecBSD z#qcc$pbS-!w<<4YimVx@!q&Xo&3U!^J?d5yC_%p=kEk^)u%k8iQ>g_i8vZ`C*AT8V zQ=@gv*|1R6LIte!ze#u2M6F+MteKhhD7XM^K?R0Sse*D@c$jovhMcDiV_hW=31l8lnyDSX^YR`i8?Y$bdYg&?me{+GNmmyXP1Iku5gyfp$Asy z`)tHKXLj1@Gke3Z3kH!nSt8xSbvIO#vs~3^bhOdKF5^wR(j;WcZZPatM{FiaNwceO z=szHIk%cweWJ4t)Jbc^?gE=d}%=|71H{9r*EcbdR8L9^-RvSInCX*B%^w~9VUSyI9 zR~q?gw6*O+)?KIWkt-8LIhEQoumdq8*fSGc;{Yl}*0sI(Kkh76H(AcZ*2`jHF#vFb zMx~1U;U7XRKBb1>e}Qj#Mq!j5CVegf$INjIxI9IWgLPPbAQLn;gA6>s0Vnu+^%+v- zSaT>wS)wH?_E0e(@Rh1l2#qVZ(e?W6Efnd^esyQ9gvfbf0)z`pR_iZ?KS(__sp|M?t9=$x{3Zj7@%NB#*t$viWPZh30DDblS77tWYCZ&z0mQ4 z+bqosCxK}&dd3z*c^n>RbYSWxoz8KE1KecRB$UkXU5^_aZg zn*)t-a9S-Xkx_yt)%-Lq&%gsx@f>1c@q9rOT%|rrPV6~Icb>_2rKK@sp`zn}y&34p zs`J=md}XkcrQ2i2-3ZP(y&ey-T}++@bTw)%BSbp}y33wxV^Ilqq2hoVePN(X8-Q8& z+uN(-HbxEVu}`bdIG+*p^%DU+J=b0rDvHF@+eW^V{XbxC?RU!GU;W+d~iw) z@}e+5I=*ZuS;Im{@?0olyhRn5tx6*{1R0nJ=gBU8(_5{g<_t3wmTp+1hR)1zMxs5i61v3M9esL8Xl%3 znzIN*BD8B_1C%Tb1j0=-B)-E+lXDSkhsFBGqRZqhY=+%%jH$FHkizwDk>v-aHLc2@ zyivKZAVh~}u%9$eW`Z!53tp4}Vp*&+nY%vh^StX+xM&?a?gf9q+5ay@q$PrdTs+Y05*!!M|BfC zriMCU4e=T~N(=j%>U;ZfgBK|>2<ly8HT^G~mBnvPE*1RJ5)mGVI#TBLqG2=te8NWrReK2)(O zkiDFlQ)DkSdOXu1Lo4+Z&^qZZ5s29)%FxH)I{jkJoFJMfHmOVuxnHS(Q6OGf8(@3~ z-n3ZKDS#DjiidSw^6{xmfCUCdikT)$wq;R}*m&>~Vw>fRYnJj=ffF^W$YIzIAMU9) za&#SDax^AiA6%K)*3xc(#~zPjPh2e41T756>%_nSTr-PLIUYnqGFvZD5|}(&bAJ!3 zM4o_@n@EGe5s^d!yc3G79qSMkGM0AWs(IO|();LO&7oWs*y(Dcs6KJ5MThaOg(kzf zTE#j-nP6xfjE$wtCazp0s0hdk!eV6m?D%of+YlM|Q zO?_s8K~92oX9x6v+DBowi8HD;pKuzf`Ea}8DJK{Dz9$ls6@+ErWz66{4v=k>KyZQ&psDmGlopgo@~4!jNMz4{wFDTarYK?2SCk#JM^y;U@L2T^ zSvG~fa1I7_3d*Oi#It!^j{|!Q;WVnd>Ypfy%M5|$0KKfO5NtWZ|{#jaM zt2QFOCm<5#8`8pSXJk^tqMyydhzJ|z2L`C2C1I-A z+Tvk*SIi_09)Hvd&bpWQoPHvYi zhNg3l62uYF+96`36$+BOA>3iPbi#gvtz#WxsUWkaJmb{qDxewT1E@St)2;x!*nVg# znwsVqyKjSw&%72t`wje*AN_ym2Y5kwjCEC-nFt03GLWz8c}9yos`#s9%EZ7r8g!PE zEF!Dk3UyF4uNJ4SjxEwBS3OA!_x=Tj|Db&TL3*&Mq<^YB?hih-j(%w3HnAvQN! zmXPR#))Y$CFhr(s@ZTIN(wa;5T{0r8VnSSTp^;z{=L1kxvS&#wPhp%L)oQA!SLKoc z*Q|*M+9_$~=RhIW1b+*^(Pso0DFA>xKw0Q8_!IkgscLz3I53GavR&m-mjeyj+wPpktJ!TlINa|qxgtExRCq?ALNaRHF@p^ntU}I(T{bAA3sgAdL6v;7 zD)CZ6k!_uL0InILJB}M@WRbM=2);6A*^H*Gb?p~KjafOk=0KZb*$;7gVJc-%(?yGfp@+zL;XA*((Agso*L*)T+NRh|_$j^!wY^fQrnt`u zakVX2CNq9t9v&k>0RpJCEt+)UtU1vNdTLWu#Xk?vyiDdzA4{4jdTrm#@|+3RKvZhK zfluV12X0hL#hWBDym@y`#a5-A>=KN~dL2h`nCUXZ1Bl`rlq78hPSkdD#(gzJzehcP zQ5R8vC!IWAiKauMD3UZKtBs?FFG?quyetyhODVA7+Vzn*(l*7>o7v!oTg|arAhfoG z0Z7y=Jk@Ph*~vsa7B@|BpRl4=4bLh%(Vi0^88{er0fL2wVn{d&)RdFil}9B#``em< zjT;w@TD^O#h@_IBI2>w$9O6L;o&XPdGs8}Y>Tx+|X+-$ekV1b%BaphGLnraUl?F&< z`yKHiOlPqYZ|bS*1;ORaOJkZ}wFUmI(YVJ(TtNMuc(hxjYcb!ZX{{rM6W5{t^Qqfu zB$0~CM(L$8?M*;PZiCbAM?@FmwaHrsl7I&(NS;{O-QJ~;+|BrC@*YAnIh0SXx!($y z1YHX;-Oz^^Pm|TI$`mXiVQ^6R6q-HNxpfFBbFUK}(KHK>G-L(M5PC@C`M-q)(5Pmq z&+o7VQ$TH@7TwPCQ)JT^VLyAk0~7gQ+**EZr636LZZDRr?2H?Z^G~mFEGrZjOdR?( zKBUtwu_V-sI4fW{XH)nY#ql&7v}~;vnEidW-`MOLWmA#L_5d9qZEsME~A{{2rXB zZ-2Z0OC3dHwT?g67|GDisNZJx@s4mkx9xT9vBf`s|{(YG?+I}I|6wU=E} zoWSS;v(fFL1wi61!8$Djp?ICzSK_=94Ad)eeV-2Gzw#sggxTfEpg<%*-aO*4JxdtSAn; zvl5ZY2IhGF)LR%x0?&QF`;x88{8W|a-42zoRV5BO<)ZKVV3}OtixP;R;x*sG0%M8A zb%R~l)q-qK;dvepy>OJUOTtu9+Umt03Zg-Wqegm)i$v=*PFRQ3K$yfK8>snwP z4Wy#FSQG!VmdiRN)}pNBva`RxP%rjL|3{~&aNk7VZ3%BC+-}{OGAYIE-MB{S~=w}6~f>nUW)=88sAvP=#Bx<@S*+pSZq+(>x{emMFewkBXu6TmP z;UqDi@-na)4bY7g0tlgiju|Kmtgs=ddL){TVI9J){4IZ@Qkbf3&0ZrEktZtEiWR4_ z85D_=&}1@|%3(&?_GD_~ty!%Rqz93Ws$mF_Mbd=!S@Ft!FX1kB0ZyuKKJ(35KYnz+ zX8gBQlcT;|H-uStNm><7*e2&dbJRMfA{F9Pk|*H5qmzPYKdV)nTnB>+^t+Ft`ig6< zGj*k9(Z*5!ks)ueVh_cpVXa5M?^aTW#z@?7oYQajFyNG9c*ksb<}QD`S8{>H*vH|W zHnJg+`YBADR>IjUTraiRy(QY!j1*o5^4RJKk^>{$Ru1`R4h5&kHd(4&r(IBH5lXA5 zz8v^PiGf%o+!O^-Ck8~iJP*J;z8ygIQgPKGB8W>@4EfO9lm>vK@1<0dOPI*qQNv;z z0<^th#i=1q_XLAr5y%fw6p}^JAh8;`OTsmikt4!s{38aZo%yLV2}`K36gXn|0-3sA zvbih&2_JA)p0>AelRK+02^Ox~OxB!C^+%>0%oOJsyf!hGDGZ2#wmU>C%XkAljmRye z4zuY3d<->kXVW&G&<{a?+L!=qzPl70WM#QRTe_eQYhF*0PAeumC8G8FyrFC$2efq5 z@HDgZ=s0l{%W^D&G_%uLb#yhB$NUwXcbcAH2eS#dNo$kJRb697nh*yw7+XkftU+}qf)hvpptQPqt-%DI$HGM`j#g??7!uUbmE>DoLEpt$dJ_aL z{1umxuEm>1A?IPEC{IA24;BF^=FpRv&11BBaRwjb^RmW_8hoXd{^eRMLck=|#TU;g z`u?ML=cGMOj>oq}!~nxg%oL&l#Lct(ivq1r76$%9j*ZeQS+ zrGjhWPM)%WxY9VU4~?IXRY-*C@=jqpaTuFq3h;GM-GfsP4_tcKr?Wy(`N4~6s$#@Vh7N$(L9y01S}byyMc#6u>`5!9S{c5lT9%NF@vKT&~L<~ z?vwMm#3pT}VI+sn$^<|si^vrWCd;Z?>S`!JJ}?7Zj(0WSJVj3wUv2>CbJw8>{kt8(2|VCK%{qxOGk zFpG>&#sXO=L$nTXgjEV;PHbfvrjaJ&f_p8KcPq5&b!Kjd)wUN(5CHNv%O==nHI>i| zee`Ut48Zoue|zWfWXkC+>>_2moJv`e zT2LTb8_%G!t2}W#?p%>vmbK+|?7PgcVAW?LelYU#E?mrc@q$LO0%KllDh37|;Z@r_ zeQGl^?s(g?_6C_|_MML0jXm%+B=8PoKA~?;bgpSJ4HPc3XL9QeWF?k}d zXQ<(&F2;Jul)UGum$X0tab>UvF8C8G2h;C36$pt{G^A?zMn$AV+Q5_oqRb6sD@(T4 ztj$)?PpBKN&TPu6Hi3Rt+-6%2H5M5Jo6Nxn`H#-S{s~~)ol|5vjyL=P66z1mWqG1f zE!3uxIebNH%tw-}WZ7CE&HP110VNb<<(Y()#8?xYn@lcZG}@J`_>18JB$6hY4)wEI zDtlf=JGaT$KcPxhRS)_0F&IC85*pnhw^&3 zLv@8tBK-rKqQG~31XYj@>F8KP}P`* zw-?b5(mJ(N1K zj44Wd@z*{A5Jzd|IWfJB=UP$e>>4k`@ewS-%6BwwRZllL0}>Nsja{%!*mHx{=W?u) zpPBq=b1t|YC)NlEQ-EMqoPCYs8OSmyjVu}aCL6Mp1 zimAdn1ABzfb5&*OEd1N}hb@^kb1Dy&dLW|9$4TA1v)Ho?^_hEZK z-5lQdAR=NGqP6r-e%SjQZb5&M?h;zrT%j~PVgE=#GZ2Zu=jyfvs3N!!RCI`3xzGHA zbV9;5MJ?2o9Kf`QI4v)P3=PncWc*T0(yw@;ILRf1om-5Km@-JXp_s`T8zKVTAeZ)@ z0Mfe5QxRDDm4PTh8D|+0bN$G5@Bt|!!lKhrEG-W2w;QH73?|UVf_;cj8CLRkq|O<%s>3ms`+`c>+O28&i^b|1aThYjc44qu zW5SU?5H+|AWj9F@`H&>sppg+#-y4}np4Uf-O39V1S*@i8V>n@CBo>AqX*_rt>&JS-l2=|i zsUuaP(`Y*N4GUE1bR6kxFl3?1?1?IrVqZ&sPXimaU+|5b{(Mi6Td`6o4<&(0E| zNd8nI!~OVnMxcK9ei^s1aWzH->!wHHiob|*UVrwhhp(UgpF~dI>0aM`?c%=0I~VU- zJiYkA#g8n0V(~L8U;4yzR-RaSjHP%FSBAwIju{90;IKv%Z)T1^#%{8InB$ckUx2^< zvpJ54sQ#y{8}<9USFC(GPv3}@p&1xHe%W3O41(Lz-F3c|${f8L?VFjtsLesK==h7~ z%mOqJcU=*gD(=E^F|#Ns8@nC|yg^Ffa6Q@D6|QIbUC%u+3>7^c*s{Gg;fC5xljVq# zd--FJAZLQ6DIrjxXSpAOaYYrPiD>+j(jUMVQUV@o31N_AV4o8Y3skg4udt4k-*O6J z4Ke1GAlS?l4YsGOM2s50E2an?g~_P}p9w)+Dff85Mhkr)b>WCYN=xhnFw{&@1i`}- z`p>VaR8-wmfsh9mj(;jWpkljV$-cny`wya6XN80D4df}$V4@hp}DBqJ&Vy$CQSh74NYQCcP0inz2 z27{q1RIHoA#FHaYahq@i`7x>1PE06|MO8y}Xn z@FkWo=r%y6@v1eXtf`@a#^oj~QLLQ#eRNqWXgb44%D&ee{@H(6S(zTGJe3ayugf@kMkAxzO zmWjKFS;N`*nErD22r|R{k}U>VE;z(8R6?rMCT6|PFSa$NtEWLv`&1lBjc`80I9%C> z7c<+yC>w|)+nNzk&r-EzCOnf47x_*&N@9j;H?%Xiukdt&l8n_M5119A(zS}t@W4^F zJ}*x257YEzAQIDLx2EIVViczZ5OpoB5K%3D<7#_D*z{>?XDQs6kL@isjo2=N=$6Vc zm@Umelg(#JHT%BZ^7>{KCSoy|MjmA;8ZTB1Ogt)Ns*k?Q9R7aY|OBsG@VS!{6NJ!s}1`}NRoStduX^D$TBi3W{&OO$Eh z5HxtTt5-TRCfT94MO(&Yd5`l*GAJ-8$3;<{ zw*VhY#B1$*(9VK<(#xule26AsmV}BFi*;0Y+4eK3ke_KHZaLK1{Q+c zfQmSA!&b7G8)D>abW1Vs#AwKDbXd}9l87L>G;0-r7)%@{f#+1!JwkmTV&mf}6i0Nq z5WUAWqF`NQo5#rPJ~;^)RNuW`%J2vm*aM0K$uv|0tzVX8+o%vn1~t1y3lbv6KAz`G zva(tkOxfB7;|Gk}oe)1VP<&Fq6izF!mL0Y~5^ORjp$xR#3Ioy0>9T1oY0gKlHaNuM zka>cSIg!h04cLp_B>W?Z?H=nMrJAOqgM-_`c5~(lz8L``a@F#&mZXJTQoyKd9u9P? zt+Hx?_{hH{2T;TX$}=S)Og;c*pJVqdjVO6qqn~Ky>>k8WUAbyg^Q$ULr9)-6f$^DI zX^EJk0^*mq?=Fx69;F~Sm5`RUs)G}4?4BB8M5{whUq;-iff%S`)dJd)VkyWGNSgv4 zZ)%^0<-!7bAeteIk}wK59wh^7MMX1y!Dy*Ybc>5xkSl4@zWrgG%@ClyugIi1GV9;c zQY9y=X2ihyh&6nUrHMg80x9HhwzkV|ZV`Qkv9Ol5p^+((@BscWzNIuubxkYSkYS~` z7uu>y8Cqx>!f|?|EFAoxlJO?PFJxpp$f`baiI+ezMZj<0uh4+mFmng4%# z=N_zSR@LYCyPj*G`~95mX}am|aR!>cOwY)08D_$a7X}!Ii4vSBLl{QlI9LNCF<1~4 zH6m6Kio}#s(cmpwh9ZetVw55wl4y*jWhol5VoXJ$v?>V!F;#QP^ZeF+PxqMWfAiPr z{?7Nkmwj1#?X@rKx7S|VZWfatl5CPEEQ4%Y|I`C}#0jJk4R%AeN^(snMDc?d@CIZ6 z;?!v;avh37zR&}udFn?QkUmVeJN$u@$~YDO<)dSz zhU_RI0VVoHs{Pb8kPMnc2KI?Zy-!Hj;%fL_c~eoQ-ZF&n#yG6LcB zUV>v=VX;$vwxr5Q8982xwoq4D%#&G~nzT~6Fgj)!gr%3u=+aDILTM#0aWFPdkQvl_ zjR6`SiJVeqqLP84FOpZiZlz`{_(0<(kHNM^0e8}{B-WuxCe^p^bB$;FbS2a*Y_oz4 z`g-}KwCMT5j@<%jEE8Xb<@zIXm~|tDoF%qVkRZe;?$|LXY1tY^!aBNypKP&n!&KD? zD>OC%4vwp1`RL6-Fv!UMm}biY0Ms}@X~jW-=pH0k!e}VAer40rwC<6L2s%xtHZ%BI z-k4>iBYj(N1xaP8LO~5>^@F>BawG}YYp5oe;CsyKs9c^WOPc|W%i@OHBqRS! z&Jd{?TSo<^w(^j-HUqMQ%{gRVWG{@N#b^#mMu1`trIu5P;i1$b#Bgf}$OAbx>+IB_ zT=JkKki_kLO+@*CibJ7EC<%*O6>5d-%Fd;2b~5b*s`SXfW-9A-s{_X&f`@=?@;>Ah zm50Jl)$urdOQFp_MDvw?j33w{3Z^h;lq5{b*|Tk)g1YA{c_0Z;08&be<`@Jb+w1y> z0%rFbUMV0k7FWR!=d8@sNIR*z8Mk36nOLE#3}ET5seJ_ALehUzHV`yqkG`1<1Ph(W z+RBhXoX1#bMw8GUi>`D#Jag(Md!))b3g?vxUYV?!D!n`>eTkppuy1~?jv$l1(m@Dx zWXuh*IX0X6m)kz22&9wVyU9%G4kE4$V*N}y@W_g+0!92FjgZ1Ugwo=J`KGm# zw(z2|DT!q=MP?Z#s=_EsUmSWQrWS+?^S{PxXIExJ+#Q^mN^x0#25yu;vJz6H)f+S6 zv25Zl{e%~UsP~j397=!G@q@^Kp;&LES0i$~KC71&E*GxPAC!5x_GVs%^EL4{RF0oJF&HX{J+HM#1 z?@9byXBSLUFOvmGMXysNPmkIkf64(dJPmFQNrs1XKYu zDD$5llp(-1uu7j(CGwJHyP(G1_Pqc*1&1Ix;1!IhG$lH`5hvADsWjT?uBT7+{|Sa3 z9QeWnjmCYHb+ziE6#RfkK1Jac#!xk`hnw313@YtfQm8d^nv*WOi|9+Wr2`EbYY`9# zp%6ZYaIC?ajxOi|WK(xOFW+ugkwVq(qERx7*rZlz^4f=dn?Ds9ss=OX$SzPv%c_1r zCJOyj0D9XF(ZpMEhmV`bWKi&Jr5ZDor9rj}9;7I)dB0Hx}+ysgOj!jpeXv;#9EJ7oHO%Z31ul5GmUmnR_{e z!EPE05NiYwIc!!ru>)+Jg2Tds4L&5BNDug+ZW%cytst4Iba{8S1fWGr(-%&p_F7FM zO6ME&hin`AP|9gDh2e9o8!HRx1{4W+C0h?bbM;=o1=(Jdv>&;Zl$>yYS1d*b%47h!=rz=Fyh3a5>$ z)hBY5HrW~gxBY@k|9cq{g_GO;Uj?boT)yfu{~WuaXy6x5pD7ruGGybV_G)nJ@~wUB zbG$lUivvO1`i_!5E>+j63z>(RgO4!+f|iIgIGi{vIrC@eb@pBZOGZn_ni%Dd2k)r~ zYjz4VokfFW#LsAz8P+Un?EJB%iP|_bHz&dwR^2C$7Lzs6c>8A%b@mi}At9XLnk|n1)J`);AVgG?ERHf;!tFe>K8Ezn&jJ{L z`^@a#JXxQmM7nfv2FFx=)_pJ;`C|;@UI3IS3PXZU6$tHao@jz*?r6(+E2SVfq6Q*3 zObW;(xl1AuA%X%sWAY#|S?vpgGhJdMHgm#T%p`^eXu9Da8cQW4D5>3r5ky~{0f_@J zvFmq;G4CL3fEO<1H-`~49SsJNg`_2v^DI0*W0b~|QjH58LDh}pG4ff85daeiKtE^w zPPkzo^CA*c@4%X@@Ci#dbCBF2uWaT-2=*|g-~m=N9Ft8p%y?uP0FEMqIY1a<@|vF+ zLvrH#uizGEXolf2h?o;%@?Y?50iHm`Od}EyeZtGs>&-MpZ+#b5np2XIa@VGhW+Z zuR<__lqUNMdLCsL2O)cBkjO%yEOkG{%fq%XZA0B%uNi%N%dtvoo4uRv>@9*|41JI| z_uRRW0ZD*pzvVL1L+(;qDp!K)VR@VB}7 zKEh9*{-g>$5z{DO6=jjNfG%$$3?AfF*xd~Z6)6QYh+-h80h%cBe;*J2F2y1$r+&oy zC^g2$uH_YqsA;1iWfNi5cmd`y%gfrrdtf0&Ol7W&=~0L`zYq)7 zXefw$au2GyatP(#gwn)?%HWS8$G}G_mV!4lOU6+`fCtK{s`@M+YSwhTdrF1h_22Wib+lw7LKO6ulXXnEyYQO)x z;FO9(42LbzNowI-4IX zexFmDC;Zx{OD4ZJi6Wfw!Gc$}bDkdmUdF58@B@~@VIXqPW8f#>el;;)46$TP_H8O> zj$!v?a3N?=P>IFyKOO!~_pRNhvDvw6aQEm}r=OYr`SkPCC&8N*XEtW`XHIutap!LL zcF*nWegr(?EBU*$z=f*u+A_r<0s!TBzsob=xarxfda8k!?zWGux>j^KSh_-~= z#wT=`vlCauuPV6FN2BwUEm*09J^5OnRZzhyoDqrx@jIcmoj2?RyP6iKsGrw22!)Jpj?Sl5M_h@V%9fQYfKC2C zJdUC+O6x2i5_dG%gDq%s@X%N%_93PBHEgD;4V}44uYN?026-VOPzKs^02Q%_oWRzb zfr!9hI04UzinYN-UDlHv+TTX|J*5v2wjxEi%5sfbPkjeIfV)QM9W<(!_4ktiJ!PU6 z^q^S*saOLnV9jD~pg00K4W9_Wuq~6xMDaS@yLD)0!6ursS6yj@Or@;wi;Gfrjbx4r zMoAU{I;7PyYbJ7tZy>TUAZmpvU}<8G1m-Al&YO@0z(6NP+lWH(ft-+#rt~W-sNz=4 zAx1!C?x~n2KGj^b3rUMXU~DSfT)|KT??9~r(_&^0Yp`J<#tB!*7T1xDMf}7P5<4aT zn;%q=nxV{xtlh(y7rjVWN#GS#C%0ZS6vlm6s&JLhRqP`GOQACGA~YdcMI6bVud_GP z82c<^L`Oo`f-+_=H_mb)PeWtc(jh)W3}71&$C%Ic&W;RkhE;(gJ~FWwNAH%5gLI6_ z+Dk4T^1||tmg$5na8*Y5bMc(48Qpe%Sll|~9lch+g%kLr9{U}TGKs_jF@-TfK8Ri9 znd3)>V7{J*iJTPls}m%xl48gs=12J8i5eZ981~j!VaU>OS#!MNk6Ce3@=(uVK!Ch# zlM53!54VzyE@LhPR+kSnh}tzAFwLmH(}U2NjnlXEAWQ$-+Y=o^IQ9$Eav93cq#-+l zH#00|m~QH1R_y{QV5gv(#M&ULDE1-j9#Wlk^o{;BKFK8I9nNlIV4!{<`;jOy?4%L{ zaQ?{9>cQ!#Gqr@n;6J*U`5njj;r}qX1civ3Qg@UXEec~m8b@sgaa0H*pswo!jYJyM z;f@u??o3K}%5wihSZA=O%n6ATS`F@<9)KUX8HuMO{Na~b3bU_`r~i=DnZOMl>d_m> zEnBrSBW{pF5Rz#6gQq8xr~W+i4U}2dl|^)zJ!PLqrWlQMt;d7sWF9CXaMhJ-X=e|* z^T0VVl1vci=2%lkTjzw3UF)Ofu+B~`XjVjLXQo^1M=KY0vU<}u%$@Z39>3=hF6cEr zaDs&r#7T_2Y0Ig@n*=FjopdwzOKmMKuju97g&hrhs9vE75>_S%j;)3D?Xk>Y4$jQH z_aQh_ZTDgJWL@QxX%o!G*}TPNo@ULbzO2B~BT1h$;)#V{h-WfqcDkdC9g=gtsEn*3 zE01x#Vde0bD#ezmR#0o^3?(FFd9Yw1uKJ+rd2m3y>5C&WKaRpys^6q2p;AJD-_A5q zdUmMDS~Y}(=!;ZU+u#%#b1rSIfJ%@V6uAZtH5Mvc8Wo`0T0=}=kbTe^(uPOO@tM}P zR8+R6>b;}TOZK|x_gdwY9U$;+b+{7%P`Q=qocft`;1=)n!of%AD#kUtlZDif4|>14 zs3g4`9<8FN*Q?@f+`=8LZx=SJ-2jeaC-J!yO$87bUWfNip=ghME z6!}H}i@(wUE0Ta;LR;!|N+Co?<&r|m(Gx&@_B0yMAiJ(H!u!$&C^xB>_d^WM%o$^b zAQ0BKk`*wLKr}-dF*AG3OO#YS%S3iJMr-RqcS}&w@I-kPP?HPrX01@EN+N%W?e^5k z6`z9cl%THSK$870ycmPek#=^9vd3>U0+%7()Puc&VPL}CoG$VgXk>B-!4D6rQ0e9< z@95Yp2Ll8NK^D$E?;Id6wkvAfwV$?E?wHOo?!h=43*P}3(@W{WNOsWJQom)mQRP6S zuvG9!5yVfjHq|I$qJ7~qw34Fg4&ijPJVN~xwZTqNQU>$vUz{1u;+uA~v5mB{hb3h~ zLwxExj}1QyuJq)G0G-~AR`-qkj=qDA^vOSBuGNb70q#9@|NhfH!ugU#NG9N?} z6`i-#GnuC65SFpsArxza@#=cEBBzTADUN7Lw`dE7CNVUv4o7i-K1$mh11TtJFb-ns z*Yriz^XhDvVFDVcLv2*qEQNWo51<|^3eqXZM=taSZ4vXLaN;Zl1XxNwv9qvkq|C}0 z@@EH6N^A&+gz;!$-pMmbe1sm~CM#FMmuiOKZ!ViF81)`R0`&?5jHGKkFy zW-h_@Ax}$-R)Yj}=u}v>sO3vu(irGF<1~g6dVf^_dT_!TAr=z=K??h-2#bXzwIWp) zGL%CG$XWgyi+iDq+=pU@6$?%gkw8b)!8a!k$#0dFNwQ+BVi&Eub0xEe59>(I;dwVb zdStA&ZH8vQH0>~NYpIE?mBXc-wNJp+EFcW1#v@A=MW0c+k`t)(p89PdB(GDq)TG-M zC+qXhVJso>jVqtW6u^wFt+YI-W3SC?ecqz7>< z1o^0Xe=#@mx6#84k_y2Z78mul0_m6xo;Y^w>qV@7+^^U971uzFmIN7%Z=ecDLb$5y zR`Ck-#Q6J%A7feKWs`53yl3(c^rt%=PUojLOwUbUF#Y=Jd#4|mesKC@-Hmr18@vxR z>VNY0n_y9#f-?F9zJNBl9~9~&+@!%wG#>Rtm5XSM(MGodpaeR}()GpOO-h!B^*+L% zKWNH)yM!O545bGh>C|~t9u%JFJz^OueGN-cLE?j6-0$Fs0$4!IW%MuqXLBCvSt;*< z6nGk%3Z4QxnyQz@KWO0#4b+KkPs28vr&2);wAEzHb+*@Nb22#xtpSxdJTj2c`4LL1 zN;k9z+oDi05ahtULO z9PGp7q47l(xxBur&&U01voQju03A5wKN@#4r6`rWug%I(q@q{t3x=Xns)|Za<@*qq z3X29ay$wWYpADLTVt;-p;&ltlQ0kGxTOk zjWgDD8VYGkU^2(CbjXgIaEPjL}BJB->VaJ5=#J`pMX5XS=#SeN@+ zZ3%giCNUB(VkmIi0FuOcTl&hq9&NZH;< zRa!H@Du=va_?ymwwy_lxWc;aLgYx} z9-4d6{%u*c!H@yuG(6XJ{mA(wRj|%A9 z?Coy$pj~CyW_gBwl5Kx%m)}O z8NQuekP@mN*)UtomJp|w%tAZ?&LCRNoT-8BMv!AI<_);qEeXGC1f0FWSvI z3vPtt{b+7>jSG^MhuPGn%>egW?~|H(t?6XxnG!O&lW}sEISUvXQyEWxsH$Ys1O_QX zlyFOEaPg)M4hb<|cMb6yg>WG!B0w~#5|zV@2CZb*-A0)LFRd=I!E7??tP>W>5vOED z2+z!qZ@4FyRH{q$w(dl0sEU#HC6LkAzC{TZw^m!%0uM`^E%0_vGZh8shm z9}IW2_YYWOohdxfa#q=3-o{^P`|+|dIl&asw5=R0d;*9yH3Td!pMB@h(Iz%3M+f2(gmYk=jYBQ!t3^BaHuWtDVllbA16v zSWb_$TK}nZS`ZQoi+~R}J{J=BM-!=<8F_3F^Xnmdvw$`@^_HnHRKhF-+zueD;8#Nw zlJ3zDc(Bevw-#W1a~%n%X=Q(D3P_g9mgVafYngR;LaPev3q({Gi>&Iu<{5_!3MntqD#;*b4pbvghZjzWqwr39(Z# z3}~TYERAtn0(dsJIkoQ?K7F1p;FV@4Wb-a)dIEoyI?U#LO)L`??u~TLTAB%`eZ3KLV(5OFY zvf(S8y>gWl%)$!mts{7if+pdcv~eae-)an41aZ;*$Uy(DW=;iOe79XC*;09kP(+fp zrK4n9gH1o=r!Q6)6ymLRl(52lZPN{ONO+Y^*+7`=0kismn&=xoa)kfFCI|-Xs-SsC zj)@076bW*TV2}lwVzi_oBqsOpqC}zeK?VPK;{uLjAaA&i*^USwLFFC_DgBWaa5Neo zn{o|IW}$OEtJ)XgXa+p!Bbf-YJ60kaZq8|A~d`ZG?0ERXpgfgqKf>6Zy(QM3CNJc%rwX$p-O1U^V)H)`0 znHH5kLPwVLMP@DwC5ujva!aJer^vzzLO`(9fTmf+38$qCCX#Z_LE|M!HZD;ddQN*H z|4X?)@ErEjXqNzVkJ44fW-C+-*UfWvBEKEDS)G-}4A#{Yy2n0hiRz3BMPRC@l z(=13RN|PCLvioF}jNrNOhlE>Q%qB)J5V5#bP9VlSfPHDmS@}uNZDTv6Q4mWo2N-b{ zh?Hbv#`6Y9Nl!xN0Gyc`_A9Et@dnWIn z{0C0_nwu_y(QJ2~UbNyar13`wpBns5ch~r1N2WpuIJUz7?^>Pj=jA#m2h)e0`E$FQX!8h0Y{w3KgcRZh24V-xDOOCkpZ$3O8^ zt=#!On=r%@)RyiT#GT636MzCfL|@}@Rzi`KmN8MQt_d1Mpo*c+AG|<+#pgUQCAs?Y zs%mxfkTjbNOG28V73$cZK-)+*!0&G$!=rSPpm~kw8NapM*oD))^kF z1T~vdXSpD=(sC@0`f!X8O0Q?963?Io%LpN@-jX6gc|*i!>GdR?bd6O&z@gUPGSxDN zAZA+nE%)W)!-(Z_C|DN<{6u>!uBwr83zGnj2{KRRN?aCQ+eb zG4D#8N|8>ahG}Uf0aXF)1nnd{jXcE zD64$q&`ZpP4>~r!gz8$^ndrMH{;f(7Ouv_iN^r1w86JV$#gU8~!J2(qM7^|oc@x57 z+M!)q!%A!d^0`dJib+L-NvJV2VZ8$Vc?Fxu7o5$o|FDG+9o12}~bR+cAwfa{rP4ORAv225+p< zl^)7C!-gfSWI&n(c0SLD;-r&ZqKp>IN!l8A6!OW+@}h$p!zPD!x><%fU%njtoCaoo zn3)X930q}vj-s1TjBs&@DtLj}43jR*RmfvT<FoC2))+IGLUS_b zA_K;FmHx`a?7ml^%^oWG6QE$aiw(+NG^ra&k~|R& zDlgrGe1o?6hf$etMPvSQB*T}M0g0tB!{PHp00LBkh6QEO`i2Y;C{#|3W3sJ^B4RoA zr=?4ly$hrY$L-Bb_t_~0P?1In?P*Aep*md_V@29@gG0IsVJxq3*MZ+I_>xnDDHm5% zo;}DuqD?`@_euqdLr%B0?L&Fgqo99u1oi>4d+@H*Zra6)K9{uBqkQn5l&Bg~1#L(U z=6RYz47lP1!Ca z8uN6z3x}Gr-nx}0z6PNHss*M%6bftOLA*2=AaiQfwlK9*C3sVTwO`0Rd{svLQ%L-i z_o)ClZs*6JBJY)QNEZ&*vb;_55k&Hkk*d35#sub4iMK>yvgDArLR(zpxd|h}bT=AH zq=BlrBDnT=RYBTRM75=KLt0|<7ri)qnGzKJTcN;}jqnR^Ng$kaprerHCWFb?&V|d2 zJM84MaS%(YLNUn@mEdB|sS;|DJh*p=Os8U#7pfyot1mRuGm%DF$H`m` zS2tFNXKxzjlqxzB9o+Y>d9w4-(z~yXlq1MwB)wLwr1wNwHBcMMAO@@-_+2~8;p#HU z0-UBkWg-BRgWBrwj7+v-W+Uwap=B^aLR4_%2Zo0UMuZ0qjDRJuUr}kC3_A%(;o$+E zyTE;IeE?tKoYZ0DhD9BuJDfqgBT!CU6I_ryq%j>6AgPQT568P21Vd^@ z!yg~hRv!}aQH@YOoK?p2}fCS>XAOnAa)%P zmq?Q8LvBK!J0!8oU1EF~4IX8vtu2V+&=v}gX6$Csf#7S}bWjvt)F8Y&*sfE?d}@<2 z;T!`!SX`Lsdz*lD$e~h@Ut97sFp6?1O_h4qrO`JLvo0 zGyTEo&rd%#{a@WZcW!sL^8al2CH%jc-rC182@GW+3ruezv#ZI`_b zoW5p%0h8RaQXt)?>SX4xhssh%N-1RpIJ&71r>D`Z(5;M^P|nWLJj=-9sxOS5u%;dQ z6PTD_|6Q3*-n zGZ#wjf&YBC-nwehudTnU#wh{ce6FMrXnHM4!(Z6K?!52 zJ2cCX?D;sS?m`7wHJDOVW|qIW7A5@f_^K$mu#C`ZM#Uz1CN5B%3%+0~VWHbnWWKB2 zq9GDpa={f3f)kB|S+1s)OBm~a_3xPYl<1T@k1(RAutdSU8z$3|?|&$o1lJ#qHljwd zolU5yH~S!OUoM2w8B!YTH1mly&Knby_b5c)|4eEek>o^&eO5X}nTv7C!HxRo8b3{hTg(gO;v*P&# zb~2dFw6W87F%&5mN;1D1Vo9Llx^-FK>HU7QBy}3EK6+e|ChISjJw3JsuLEqAWl6S` z$$m}|qphT!8KViEcowPLfxGfM;&ix>0r}y{ROD8?s_x37lzVqj2O)q$S#N+oaDmID zejAjT1XQ1RHm(s1v1%uF8<9V&9ztS!Ru#cF6#>^Kn}C~I4cR<_OGG(NbIsXJpcs&R z=Bno-WQrH|MAdM66oS2Fy*S2-qsKi`G|f0@hJ(T`($!rV{-h%|i+@1$rCXG;6QxlI z-Gg?R!)c|+KpDfs8b%o!6Nr~AajUee={2R4OXetyqD^y(amUiiF||+@{vEqXabtwm z7960haXIz_gW-oD+R?CF=adfH|hsX&z2NwrVyJ zFs0C!(Z&wyoo7|VQ-z~G#4JC3A5$GB15TOI)llEHQOETvQ;;a&DOe0NC{gV8nXYMD zO$uf`Iqp0Qy8?W+qQ%|Jv;hD@1N+%Mmb!`VfwcslJpd*!djSCVy`VX|uE7LChPb5R zlBf!BW@EuWYI>6SAtmeQV3`}EVllrPG6!U)!6k3yI91^EkaY@52oe$$h)RN8E` zWS*AAAnKany{$yn{fbPYr&!U$UEX0{T4)lYPykl;*r=NiR+m$Pt1t-9DD-4(jWPLz zsFd#5%#8KpZh63zCV|36YwDbEY2dPdE22UbIYwlipEEU#*+{nJxIza$vFh2dvM;hB z5!Bu`^HDDgE?{b$jARpz0JT&v|JQJ(CxR(rGj}0X2u33#M=eK~bvo&{8eNfvRR`!A z`|Q;sdKQi4v;39p%E+bTVrfwh`bU+bB8-RYYrdl(YeUE(9(Pn2?TG3}!sC|1GK?me zp(qB_swD*ga*vRFaPiXpTm$x>VH|OCR)L|okp>nrS!@$E$~K4W+9N29$hHlcY&oLN zUH<2HxHQx{MR?Llvwnh#IJG%tBuz>CW@r4Q!!+HasqkG&T^0V5!`po06#=YWEE$IGUHx)SlJK2R|h& z9J&jOgN$76G$px8fq74fIpPIh;84mXd{IRlKD~&#IsUkpi2aA<9dFdfsEwVcJ~0x1 zlSB$Y6WN@m((qJ;BK}6m$(w~`iexzzg(&?5zfd3t#kw;(8H>XNP=)hLqFhs@DT7tP zkm%t`1y{l#v_L$GsQjvJry2{KQR}43Mq{TKP(T^EAYYev>68G2fFbCYEIh+gV?v&) zTN6KaMiNWb#O0!tY$2_1TEd{ZM9|}D&6K{==3!x^$`$x@#~3<5R~P|T zR@8Oh0ErS~)sBzp=(zQnRFo7U0k&$$p>>moz(w#y#!VegL>`+P8Mtn01m{1$?EFc$ zXg^R-EEOE`C7!@j+m?vJg#-4;uGk0nlH!&Szobk%ce~HOblm@ z=o`}V55g%T?XEzdX18G>p_4a6O73O&WD32AgjVp;3+;Mbx{Kml(+4_i`>CL*bv5qAz&0U!&f7K;V&MPs?l(II+7j-*$hCO@MqbW zRgfpq==((3Y=NZKO9%HswqQ+Ix}dDtfa!uG!4;5OXqJfFr~P_0*c;9)28QFtB2&YC z2wSF{0Mj9LX+)awLmuP~lE^Q;3^^-KVHylHAi2GVAyRuD$+D9jZf-J6F%O_bG#bU$ zDzUxQU|_!)iJ~}H9Q^ybV{|;)QNlu2mh6V;x^d4&F{O9B zn3)#MV{_DXvs!wL{&e_frD&USwY*2<^*6Sh?|vhc6w(ahB^zjQ8& zQ>jnl3NG>2BQz3F6(ElC?r<0@7VBlSaQYN)n_0)9x@(+KGYG*E1ZECVm(Qu^po9>v zPXRTiw5q5c{r-*Umy*DzR=)p=Mjth{AcMXnW}e+8Eosmmfre;^9!zuP^k!+tGrG5o zD%6)drHwrUbX9rL^P<(G7O85hZRt>>Uj!cARw~<-OAVT^6lk&NQ2kFdbk%!fC5Ogw zZ5f)vR}AYBk&CBi8pg3L%fqvluIYqNunAhCz@I*AiTjGG+6?jmuPCbV%Vwk{YAMuT zNvP7&6yX}>6rJ^=Y9}a}E8;PhKrM_uu5*b?c#1uQkp!89mVnWMq^UnfLw5N;l-5O* zd-6F2Eid5bJkW^(^%8*r2s}sFU{!4wMk4d9UwiMje1X;Y9~C%ATO`Ul(i;umeSzF; zl50-dJvBWZ-yVT))O<^zdKDXRjHoE^GBgfzBW#g>qG(p)l72DGTtr7ibPAo}EF?fg zi&|hnL5nf;000PCfloFYniDgXz^=kgb*S3$I=4J@jWECng(0^PGag`7kt~9sgiy-W zKJ{X0bf(~oYFSu@gbBBS81Mu%@bZ>TyJZ6Xxk}SQ@J8&AvObjU;w`k*=kk`o#7v32 z-$AwNtM^>T2ObV1z|?W9Qg$0>lEpXUFFHR1BKj)>HueW>LHXgsG69%CEW}if&fxMGHii%z>=2w8-pfNB4r*%aR|ZXSCOL*3p>^ zCMPqdJGD^dQK6%N%{EoV&j~ifMbFl5+{s5~9!^o>GXX;h*6%2BCKBYaI&4ln*)P)1 zb(r>VGh2@_4;Hc6&kmK$CW1hjKb*>ZA0CdUE49q&{3w$p5+&z4KeQ=)oZf^VPil;5 zII}A$gwM`P);I783f;q%6QCA8WJbclCCO-MtW+)f-15_(= zrELbtAQ}ZTd-P`#!`LFv5Z*aYu1O_lb3K#K1~iJ5Q6ibBO}vMX!E$wjRJS6>gm7lM zzRvcQ*}{tOA<|-_SWYQuC*>5j>e2#+Od1(3 zOe{CJ&FtVZDRUuKk^t4JUeEngUxEPl;^N8XM#m?M_?zTt8*sPMqJ}_Fl3y9!PN^uv zkS>j1=kk0-L(U95%dyUzE|dEaSThQdRs|8 znYVLksbwjxC6BZK0Omjj_;2R$UGp8)S!UVBCWj^_Dl0r+Ux#d2D2PZTo#1-gm>N$+ zGg}+WLHbOLRIUGbKY_-r4Dl3FM zleJQd(-TtB>?A6&h8prCimsU^_tbUq@ENFxIo5$H{qG_eQL2CKgvWVFK2Q4O(3GCpk5K8 z&?*%CN-Q;)PADXxx~HYss(u-9Qd-R}n9z+Be=xp}oPumsx+D*o3=Rykz}w>~#wQFb zadie&G;L-BCc_K^h{CayC}}pN+gBQkBX+q;)JhtVO(T;PLSn09p%qz5U=L1bB>+ja z5g~voqCo@niD()!#S5X^)>St_#jeiIusW6qeOLlkv1s+UZp9B$Ya+Bq3Pm~&&9Kl- zGdT)~((uBX^*v-!pyFO&LL{Khj3PH1Iw1+03MHh`j7ekXZr*J$cl(eG>zh6xU5Q;P z%N21>CjV?tHd7lariuqx=$XIE1WG=!16f5#XB}OEmCR}scT@2~m7_{sc3adl6`sJ? z@V1p{aAskX!NZa>xFvgYj=Z28c|k$>(!!whFTRhwpft$h$O}r40>OfUH-d5mlR-BR zm{tJAkr$LBFDUJ>rz0;YF&TFPpmufS1;xqXkr$LBFDPmQM_y1+eo%61TRn=*kr$N0 z!2*S;tExpsA;n?^6-ca9Wr%0IM1q3CstBoLD{6V2nl|J%qO21>H83Y`8L1|_fEJGi8LD58^nT8yBL5aP_kr$N0 zgs^|{$l)U|D9-ASyr8)HbL0i($O{THY;B2HfgbUd6u5BY z1%+PX$P0>=6GvW9j=Z28c|keyf^sDrP>#HyV1L1;1$Q*C?|~nTQrHeqyDXYDLVl(i zB=mnrUQpO};Rmi{?*!jRUQmv_ptvQ9T{rlqKJtQcU^I?Oe&hw^$P3Dm z7nJPY0H{0if)Z1cBQGdGQ)M8*=7Yd>*^+SN1?9*KipE*&gJJ89P36c73bt16d3?4P zl#Y{Zy6&}ei<8M5n_%&-H^0D-2hlCe5q!}n{NsyzE*5f$i>>RvT1dzR9HMmH{4qQc z6LNuni~P*9+iGzhC(Fkc2)39n?%Uf%-d_JzVlFHu-G$}pbb&;Ov3P8WpJT`PIkwD} zd7qez_r5&F0R$ZDeEnDE$R9@~cirO3bZSaVrngM0OH2H)yLp9#{P2K#zn2zyw{-0K zua@vm#$VDsw#pfx#9SuI$_giott>C|v%0)WLVlKV@AoqM`Ic9fuK$Y2CeY!UrdwK@ znOR<5Jw|fNtE;5Cy25Vn)wR_%67sX`{fgfZy0W^ueEnBMHi4wOyfHhwva-gRoqVs8 z>e?zva&X-S39a(sYn`mJh*fwWp4`XhS>iRlK;_7Epn)&d> z{TkovWW2t$a_Pr9Gqd)M2p{ywuXIT*drd;S?C-=h~kv(U{=&T+o`baK$1=ViWLGx$8g_#3Zv z^W#(9?D!|UxzR6oGvi0Px$!xk9mmJ{^ZET5?tiPBAN?U=9^@L4;@SN8y<9)e@2~Sa zQ+*|66`v*WK`{uDkHuuDj!8 z*S+%7UH9O>?YeK~{#}HA-$%OcM?c@IbK z8-Jr4yy!#S;NItUg9ra}H+by9Zt#vD>;~U|Z#VcS{Qh(9fAQD5!LNQxH~9T`c7s3t zWH8o^H7J`EGdAN4nt)-`EYm{Bzy#tKZfQ-}q=ZeCv02!*73WH+Jkbq*@1J$U&%C}H{^cKcqq%?FjrRAt(T%U|Mz?d8-7ENg9rusCryG6C zE#2t5U)+s8@a}H(V@uuWXUE;>-~Y>Q^r^?X(Vv{^Mql_yHy&N;#;Z4VgyRf14}cE_EM1lQ_B*@11vDzHs{k58VCQ`(Azj1J4R|;ljo9 z7cN|=XBDw~d;XvA9_k+JzOj3lvOm;4-o33m-@TE$liZ)|Uf#Whw-533&AB?+eN93= z+&!neizkos{ITv4Lf%W9xANcc_jCP7_eA$-!rs%px_edk03q%q&gJexcRP0vba!{J z?e62<{ajx^tqX*|2u=Q8cviS;bF3Qgp7~&RiMsfI|GkDsBYL(8)2QX{e{`$edbiPS zc3a(cx6|!X8~e!4@$OW2np!%;$qnba8#%+_CQfj89-KJOxs1|r3Hop8u5iY~ZQYl4 zFQgR#Nv1iD^MdG6`Hto!orE4r`jUO{PJNvU5=3BQUGzJ@aX_3o?T-fO$Rfz*6$ t_h9!oyEnk4Z|J@kK7CvF?So-AoSc8`iANvpx?kwJn|~3K$KA}${}=Y6Y{LKm diff --git a/admin/captcha-fonts/GILLIGAN.ttf b/admin/captcha-fonts/GILLIGAN.ttf deleted file mode 100644 index b6c0a48cf98c5275deb56afc9b5ba6b6397556be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36316 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 z%0Ac&1Eil}ST8J22(6(^B$)(E2lp+kq&7mw$0*3^#~`Qwed4Fk@39iWcDfK|L4pe# z%DRw(A%wQ9x>0kiZ(RZ!Rt|U;dIxYqzA2wnZ{1X4adtk6lVTvDSjL?`IFKy(S1$-S zSs8pI5L>Wz-L?tggOg*0%@0Ee`vy%l0C#F6?u21np=Nd|NDb z&I4Oh{CPNInHgNLfVZ&-!Q0UB1zG|691N=hegQP*HFN+D{&8{%J_{FeN>BJ(&EL8J zfFUyivLsO5V&KQ|OJZ1~?}Xf?O$8Inu-La=vPG|eR{Z8N->&w*gU z=5b;^4WEF%FC|!#UVwH=$-{+s%Jc6&B`AqD$G622q7;GFF)0z@+{i0>wkH&2qKcl1YRNSB%I zTM4m;wGZStN+mlyvwGOH`(0ATwSOo$P|8C*=9?JHh~AI_)wz?abG2H2O4GyP43x4x zn%JQk8KYYcFqJUV$NKcmu)#UCZKyq$A5>!+L=LV(AYvEGiV|rZwx@QN1F%mB zDfy{Gsge+YYLE+*7KpV>86xX~s=v%xS|lBD2{2zRK)02fr~R(DKj2ebCTH{C4q`dNFad$8v+4? zL9v5D7#AikY!0W0v17@h63pcw$Bs=oHg?&7T_Iq~MF9baB(N{Po>>KO+4+OK*{|R1 zOizE^U-x{k`|Iy0jH&I4-k{s)lG{G{-ny}wLs+@(@k#uK1`RtA#1=ZIoYRzv`%R8^h9#sxK6&_PA7v6iE zU-=Xwum9t7R4EJ`RT_XA&<=D03=|8m6{4PwfHJ4$AmM+iI^mnpd+?2CQ%p@fmmoE0Tclbd15%8E@7 z$=T!rXXc1`UV?~9nzSRomf|O z#a@iT&Mz5LFjo5?tWzl(jW}jm{8rEexyaM*Z|QUjs?uE6i`k)StuLQm+{)xcSIeH$ z*^Vx&W$Q(+#jNytxy@bk2b?Gq>W%imT)nu=N$`gquB+1EG`07ts17(yfl^{Ww%OK88N;!o z<_Hz4a+^=gvSz*HspWLK90k+QM;RpL*j5*UbRqAlPDyjS+e)F?v!*Fq9&KrC>Pcr; zWP6)b4=+Aici0@}nL&h18H;Ag>_A$?KCkZSnX8p-b9*k8HT}v01;JLE5!w^A;Y>%S z)=~DlPuH|`b#GvgkD%aX7X5PTmJNgLPEaa%iWjiT2(_A?(|2Vdj@wX)(D%w|i)S^q zHF^d*WM-?GQSMv3Z74uxO3@m1Z@jD06=X&YzjJ%Ot2gasm1<8%Iy*8@?#O!`uD-Oj z*pBs+kx4bo&Q#jX$nkLgv&`z+ePE{*UF;)q(@k&m?%d?i_A}2hgZma*-mO|b%{es z9ZfHF4>jnEigv4EPCsK7(x_(#ZHAp*SJlVHOFepZ$^5aJow7&fW#?L&Uq0vXL{IsY zrCBFuRfiA_BggW!-f&T;S8zhi>#0x4p z9P273gCkNoKVG8oolNGG;ZEk3345BGEtp;A$gtFZl_kq+QLYpw%jsysaFSXk>%4?f z%4KK5vS$1=Nrm8~GCY$Ul2u{lfE9#fT*#<+U?fp!if5;=N>*_vN|70HHaLu>g_HrI zsX~der2OzG?1^j%za&$!3w;uaZ3Y8ILXz01FU3tySYtfn%y$mWG*oi%_)=TUMIk55 z3YTMX9Z|VT;=ZtqWl0;0k*u~kP)X7ql1eD#WAiU&(~|K@&RKxX4J8FEqQY@wqy!E! zN|}=6k<8KcJ=TOd(A%J`t2S6IW@v@oD>|A-rFbAyRKxCEs$oURTQJhqW)#B~I~*Mv zvZHG2RhxTUEgYY?+U?w%@3wq2K>Cm@p&V(HYvmrR0e@E6{FNJ*?Fq}3jbkz8MJe9TjO9R?_~QLF)2_}o;&9{(=ozkyP={x_(Lns?QWQ!BUh`q8o^7v8s&Rk z92R2*8KoPLJ;Cid+H6b|)F!+pjcbbO`U>(EH=l0_Fn~SlGC!3r8k?ix1>2NcYz-q& z+{Q&wsoK=tWP~|9(skzN3~1dwIFwLK!*;avadg0HYF=~IoRZE}{Zzk=VTOax6sznS zX{%t_sy=|)=Yh1>R%u&-P^Q_)b@y3z5JoA(Qk%lynn~Msma6tAoS=I=Y^W4_r>xP? zW?B?356i3*@~H(lpmH^H$=b$N-_Q4(Mo-Q(=MIhL)0W*-v$|KTnOJP++RNx>7Is{t zKQkMyF(V= z?2;%8PD&4CG6i;Cf{wFM6}MRYWb$NIO+;=>zN;a^noJEegyn|nqC;cEr1_iP4kxQy zS}Ai9f=aKH%c;HS))^|(@{D0O5f#O(X5FmY)^uoYU=({zj1U>8JuU4zw9)fTPM2pd zLX8|N3qqWl(@)-TeFkk}&$;DrS-A%T5w%k@a8q_#Nwo%cwXxNCBrH3MmvYiJ{^LBg->n)~*U&pxLUVbmt)*{NRU-(}clZQd z2v7tg62-Xc)=>F9@wt|h4}rpBKsmt1!l&7Gc?MOBpTdp*mB3PCz)>NrVc@@5FmUb^ z0NjE*$5GOo1N<9CX`6-cS0av^3y?4T5vIdm5+XGyL>jj_8SZ6%Lhx0>M`mBb!}Jqu z1TO;qQHbJW7=nCWh=y6f-(u>Esz>8S;P=?y?*#a6{)!MSM1pR4O^DX-2vNCOh${JN z>;l)o-}ZtK?WY3MLUdduMCUN@wh&!k#0kVC@JkGFCxBlFF>8qsy|`oPMT@ZSh!Fjk z3Nf$%piZ;7KL@(Zp`4TW9^{$9dxaRH4a2l;_;n#hZYQiZ?HGk-W4L)3#t)Ca-v@3w)LQJhrVB1?Vfnm-{F})87~f;;dQp4zLcVXn)!R$tTZ^3M=fr3+u$3np9xxVS z$?x$@pgbvT^^@Sv35RE0bqPSEHx-H~X`XqL^Lwe&MZ(fnlJ9e(sy>X4#`lD#9v7za zKHrVg?`cEiIRhY%Mtjsh0|IdErW`1%&ZnMdh(P-S|33=I=jT(O9@_LaZG4wJ-6E~7 zf-sMwv9zSVTdm@6?NLJGG}4wvQJ{W8IYu3dlywj1uTj6psqg%HUA>|H9`zIIUGX(X zrH7Q8*qQq>G^dM-l`UA>*oVtA* zzcyc|oRjO*I^{{4sE<&W4w2Fr$;u|^unWDz!)QNzU6|y_Y1>7=_JA1E-Vvkn8KTBh zvg~a7XUlP}-q^{4_xa&iG&8hH2&^-!k6}CF6WDA)f{>w`>e4eEA!h z&SZ1>Lb23P#yv(0R?U@at*yPIv#YyjR&QVb!0b6E4Gs-66CIm3K7YZ&xc!TlES+4o ze8tLBPF=Nn&DwSAH*DOrdCS(*rna4a#+loBk$0T^i5Y}D&)GF|cJ9o#{5nq%$z1H- zbN&T;1+l$nK9U?C`CNR-%)giJyKMhQ?wt7Nz~zuH{v3p9$qyWYkFJKKOXw*B3e2b9 zwnO`?#l3KW2hcY8rg&O>PdqFBS^SvYmgmLG;uY~*@uqT;`i%OkN~(emdZkwBtk!B# ztrw3OLSS1#&+nl3WEo%kP#Hh`6J?YtO@B~^tl7u^D75Daj&E|j9v^Rr501CQA3xp} zzdv!%j*t7kFe>mU_5XWk7E}MFAM3rjgaNZx>>=v?1vBr^0dYA!aRp;%=gix5P#mJ~ zQ{pnQUtB8oF}^Mq=f`hYLUG2uy5w5MCU3o|rfAD({X3*%^~Xua>-UpR#^;2-=6rqq z80nTc-5TGWsy__RsMU|cHH`Y-kuHqSx7MG9i`425k}j#=Lb^0gC+mATUtYh9bVdCc z(v`&7>J+Oer&Fwn)3ub`Db~g5`ue$?Z;tbCsUPHA;_Vbu@s(}Vs*Cb3C0$C+E^51< zbb0+`(iQb>q$}g=r_`5oz6$JJVA(;sHom?tK3^ZF8!5R9T(X=k^^K%c^<|{n;=8-* z55NH7G5L*3+j zbNtkn=?6LA%8NN7rijonBDPKcJ?XCcINW-o{sigL`f<|9I9*$Rk@Jnze;)NePC8M4 zj&!nqH>s@uJbL`ENH>vZ71yhzE9(JVe6s#?(zSJdZ}~9)5!X5n@t{#aGf>;RHP1EKo|AF5& zWjz!8+-6mVDwM3V4mR>}f{F}|EKJkoHh%OFZMif<;#Xa;yDqb3TyK2{KcTB|K4?pF zkl)}5!;*+7#NrkPSLlbu$iX0&5SjxrDkKZX9)sq|8cG!adlElrIV{_?lMeG;8h|e+%8(5VDAbeq1Nobc?RXe| z1V6T|rd-ZZwnM||DhoSSN%a`NV|z9=H9>BnNr7#p?n~SWehj)er3TCi*jtVVOyDs; z2)RJ}990h~#+Sn?lv4(NStImO5px=@<+_kdnrkNbZHI>A>cDa+N{&{{X{i(aCb4)n zXZUEe4caD+#AM?vJVSw&!*-xMk_zNZKAYVU_i+df{)j>Y#wuabn6X5>glUPNEk4Wh zRJsnbsjdehZOmoaHb%P-M9?C}?_vi`O)cAZ@@&u3=sJqjT;Bkt2PW{_9-EZ%ZSfr4c3t{S zHa=hn-?d!>sR}(q^&E-c0vRo}>9ZilZ&^`*33r?_ummn=j>}MhwlRJh2WchE7`cuYdZ=Y< zmYoV6B91`=3s*77iD^b8-_=YQF0*_QMB2}CpZ3wftm``|$#1ey zKg)_Bm&dq{F&rfLskEc{Ax6dE$A<^_+1j`1b;uCo_kD=(MxGb>uEY;*WBl}daX}TH7 zR`RJ#HeW#MQ1swIyeDoHA@Kn}qM(TG@Q7XL@~XOFMBjLx4}PMYF>b&Qa?ZRMKiDLG zA3491@;uO@-^Dhz<74vZq6Hs>1#UYb4Kxch1Iv7z20;Bn5s62MWmdpAMS91NmFU`iuzV})Kw!yBb<0BvNyQ!B(l8Sy`12Wz zyo(Zl1Olr_GqRH37omQ!gtVo>mpsr)d8w4twxvmWl(uMk1g5|+y(k^HAv@72pL5EM z+?2;o0X6Xg+qdakMmF3`r_yPvLJeH-M{eNgj)%@o7?rq<=necOnvs)i4WVA4)F7t; zSf<*j1}JR$jIze!eei~ z@Z^qY!8<(9N?3@8AL*Dn@l8yY96J$D^FOMl@e|2vE-pvEnESWuduFc7hmy}#@s+Ds V4?#CR(u42>D`f-?%6r^X{s+T-MRWiF diff --git a/admin/captcha-fonts/NESBIT.ttf b/admin/captcha-fonts/NESBIT.ttf deleted file mode 100644 index 7b590bffc3d0f5fb1891b697637c809ac00b6a0b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30232 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 HtOfrU!y#x& diff --git a/admin/captcha-fonts/comic.ttf b/admin/captcha-fonts/comic.ttf deleted file mode 100644 index d17e1be6c4e3848b0c172e4558b521ea6b1994b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 126364 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 U_`3LQucm(aet5sR7s%ZI0n;jcOaK4? diff --git a/admin/date.php b/admin/date.php deleted file mode 100644 index d0a181e..0000000 --- a/admin/date.php +++ /dev/null @@ -1,7 +0,0 @@ -\n"; -print "The formatted hour is " . strftime('%H') . "


    \n"; -require_once("global_config.php"); -print "The current time is " . date('h:i:s') . "
    \n"; -print "The formatted hour is " . strftime('%H') . "
    \n"; -?> \ No newline at end of file diff --git a/admin/index.php b/admin/index.php index 383fa79..53c3586 100644 --- a/admin/index.php +++ b/admin/index.php @@ -8,7 +8,9 @@ //----------------------------------------------------------------------------------------------- // index.php - require_once("../config/global_config.php"); + if (!file_exists('../config/global_config.php')) header('location: ../install/install_programo.php'); + require_once('../config/global_config.php'); + error_reporting(E_ALL); ini_set('log_errors', true); ini_set('error_log', _ADMIN_PATH_ . 'error.log'); @@ -16,14 +18,9 @@ ini_set('display_errors', false); $msg = ''; - # Show errors on the dev server. Comment out or remove to disable. - if($server==$dev_host or ((!empty($alternate_local_server_name) and $server == $alternate_local_server_name))) { - ini_set('display_errors', true); - } - $bot_name = 'unknown'; - $bot_id = 0; + $bot_id = 1; session_start(); $myPage = (isset($_GET['myPage'])) ? $_GET['myPage'] : ''; $hide_logo = (isset($_SESSION['display'])) ? $_SESSION['display'] : ''; diff --git a/admin/logs.php b/admin/logs.php index 0e40182..ccbe7d8 100644 --- a/admin/logs.php +++ b/admin/logs.php @@ -79,7 +79,7 @@ function getUserNames() { function getuserList($showing) { //db globals - global $template, $adm_dbn; + global $template; $nameList = getUserNames(); $curUserid = (isset($_GET['id'])) ? $_GET['id'] : -1; #die ("user names:
    \n" . print_r($nameList, true) . "\n
    \n"); diff --git a/admin/select_bots.php b/admin/select_bots.php index aff1ec4..a07fab3 100644 --- a/admin/select_bots.php +++ b/admin/select_bots.php @@ -138,16 +138,16 @@ function getSelectedBot() { elseif($bot_format=="json") { $sel_json = ' selected="selected"'; } - if($bot_use_aiml_code=="1") { + if($use_aiml_code=="1") { $sel_fuyes = ' selected="selected"'; } - elseif($bot_use_aiml_code=="0") { + elseif($use_aiml_code=="0") { $sel_funo = ' selected="selected"'; } - if($bot_update_aiml_code=="1") { + if($update_aiml_code=="1") { $sel_fyes = ' selected="selected"'; } - elseif($bot_update_aiml_code=="0") { + elseif($update_aiml_code=="0") { $sel_fno = ' selected="selected"'; } if($bot_debugshow=="0") { diff --git a/admin/style.css b/admin/style.css index 1b52a49..c839dad 100644 --- a/admin/style.css +++ b/admin/style.css @@ -31,11 +31,16 @@ label { cursor: pointer; margin-right: 20px; } .leftHalf table tr td, .rightHalf table tr td {vertical-align: top;} div.row span.label {float: left; width: 49.5%; text-align: left; padding-left: 3px; background-color: #DDFFFF; border: 1px outset #999; padding-top: 3px; padding-bottom: 3px;} div.row span.label:hover, div.row span.formw:hover {background-color: #EEE;} -td span.label {float:left;width: 99%; text-align: left; padding-left: 3px; background-color: #DDFFFF; border: 1px outset #999; padding-top: 3px; padding-bottom: 3px; vertical-align: top;} -td span.label:hover, div.row span.formw:hover {background-color: #EEE;} -td span.formw {float:right;width: 99%; text-align: left; background-color: #DDFFFF; border: 1px outset #999; padding-top: 2px; padding-bottom: 2px;} -td span.formw input, div.row span.formw textarea { width: 150px; margin: 0; } -td span.formw input[type="radio"] { width: auto; margin-right: 5px; } +td.label {text-align: left; padding-left: 3px; background-color: #DDFFFF; border: 1px outset #999; padding-top: 3px; padding-bottom: 3px; vertical-align: top;} +td.label:hover, div.row span.formw:hover {background-color: #EEE;} +td.label label {float: left; width: 100%; margin: 0; background-color: transparent;} +td.label span {float: right;} +td.label span.required {float: right;} +td.formw {text-align: left; background-color: #DDFFFF; border: 1px outset #999; padding-top: 2px; padding-bottom: 2px;} +td.formw input, div.row span.formw textarea { width: 98%; margin: 0 1%; } +td.formw input[type="radio"] { width: auto; margin-right: 5px; } +td.formw input[type="checkbox"], td.formw select { width: 100%; margin: 0; float: left; text-align: left; } +td.underline {border: 2px outset #666;border-top: 1px outset #666; border-left: 1px outset #666;} div.row span.formw {float: right; clear: none; width: 49.5%; text-align: left; background-color: #DDFFFF; border: 1px outset #999; padding-top: 2px; padding-bottom: 2px;} div.row span.formw input, div.row span.formw textarea { width: 150px; margin: 0; } div.row span.formxw input, div.row span.formxw textarea { width: 80%; margin: 0; margin-left: 6px; margin-right: 2px; } @@ -60,6 +65,7 @@ span.eightyPercent input.wide { width: 67%; margin: 0; float: right; } .testBorder { border: 1px solid red; } .center { text-align: center; } .bold { font-weight: bold; } +.big { font-size: larger; } .narrow { padding-left: 0; float: left;width: 25%;text-align: left; border: 1px dashed blue;} .errMsg { position: absolute; @@ -98,10 +104,13 @@ span.eightyPercent input.wide { width: 67%; margin: 0; float: right; } .red { background-color: transparent; color: red; - font-size: large; - font-weight: bold; + /*font-size: large; + font-weight: bold;*/ } .orange { background-color: transparent; color: orange } +.small { + font-size: smaller; +} .validCSS { float: right; margin-top: 10px; } .validXHTML { float: left; margin-top: 10px; margin-left: 5px; } .userlist { @@ -608,6 +617,8 @@ border:none; background:url(http://github.com/images/help_small.png) no-repeat 0 0; width: 18px; height: 18px; + padding: 0; + margin: 0; } div#AIML_List { @@ -658,13 +669,15 @@ table td#notes { vertical-align: top; } +#notes { + text-align: left; background-color: pink; border: none; height: 80px; +} + +#notes div {background-color: transparent; border: none; height: 20px; margin: 3px; padding: 2px;} .floatRight { float: right; clear: none; } -span.formw label { - white-space: nowrap; -} - +#helpLink1 {float: left; margin-bottom: 10px;} \ No newline at end of file diff --git a/chatbot/conversation_start.php b/chatbot/conversation_start.php index 47895e9..d9d526f 100644 --- a/chatbot/conversation_start.php +++ b/chatbot/conversation_start.php @@ -14,9 +14,10 @@ //chdir( dirname ( __FILE__ ) ); $thisFolder = dirname(realpath(__FILE__)) . DIRECTORY_SEPARATOR; $thisParentFolder = preg_replace('~[/\\\\][^/\\\\]*[/\\\\]$~', DIRECTORY_SEPARATOR, $thisFolder); - include_once ($thisParentFolder . "/config/global_config.php"); - if (!defined('SCRIPT_INSTALLED')) - header('location: ./install/install_programo.php'); + + if (!file_exists('../config/global_config.php')) header('location: install/install_programo.php'); + require_once('../config/global_config.php'); + //load shared files include_once (_LIB_PATH_ . "db_functions.php"); include_once (_LIB_PATH_ . "error_functions.php"); diff --git a/config/global_config.php b/config/global_config.php deleted file mode 100644 index 7542967..0000000 --- a/config/global_config.php +++ /dev/null @@ -1,306 +0,0 @@ - \ No newline at end of file diff --git a/config/global_config.tpl b/config/global_config.tpl index 7071ca0..22a6ea7 100644 --- a/config/global_config.tpl +++ b/config/global_config.tpl @@ -8,247 +8,151 @@ * DATE: MAY 4TH 2011 * DETAILS: this file is the ONLY configuration file for the bot and bot admin ***************************************/ -//------------------------------------------------------------------------ -// Error reporting -//------------------------------------------------------------------------ - if(!(ini_get('allow_call_time_pass_reference'))){ - ini_set('allow_call_time_pass_reference', 'true'); - } + //------------------------------------------------------------------------ + // Error reporting + //------------------------------------------------------------------------ + if(!(ini_get('allow_call_time_pass_reference'))){ + ini_set('allow_call_time_pass_reference', 'true'); + } $e_all = defined('E_DEPRECATED') ? E_ALL ^ E_DEPRECATED : E_ALL; - error_reporting($e_all); + error_reporting($e_all); -//------------------------------------------------------------------------ -// Paths only set this manually if the below doesnt work -//------------------------------------------------------------------------ + //------------------------------------------------------------------------ + // Paths - only set this manually if the below doesnt work + //------------------------------------------------------------------------ - chdir( dirname ( __FILE__ ) ); - $thisConfigFolder = dirname( realpath( __FILE__ ) ) . DIRECTORY_SEPARATOR; - $thisConfigParentFolder = preg_replace( '~[/\\\\][^/\\\\]*[/\\\\]$~' , DIRECTORY_SEPARATOR , $thisConfigFolder); + chdir( dirname ( __FILE__ ) ); + $thisConfigFolder = dirname( realpath( __FILE__ ) ) . DIRECTORY_SEPARATOR; + $thisConfigParentFolder = preg_replace( '~[/\\\\][^/\\\\]*[/\\\\]$~' , DIRECTORY_SEPARATOR , $thisConfigFolder); - define("_BASE_DIR_", $thisConfigParentFolder); - $path_separator = DIRECTORY_SEPARATOR; + define("_BASE_DIR_", $thisConfigParentFolder); + $path_separator = DIRECTORY_SEPARATOR; -//------------------------------------------------------------------------ -// Define paths for include files -//------------------------------------------------------------------------ + //------------------------------------------------------------------------ + // Define paths for include files + //------------------------------------------------------------------------ - define("_INC_PATH_",_BASE_DIR_.$path_separator); - define("_ADMIN_PATH_",_BASE_DIR_."admin".$path_separator); - define("_GLOBAL_PATH_",_BASE_DIR_."global".$path_separator); - define("_BOTCORE_PATH_",_BASE_DIR_."chatbot".$path_separator."core".$path_separator); - define("_AIMLPHP_PATH_",_BASE_DIR_."chatbot".$path_separator."aiml_to_php".$path_separator); - define("_LIB_PATH_",_BASE_DIR_."library".$path_separator); - define("_ADDONS_PATH_",_BASE_DIR_."chatbot".$path_separator."addons".$path_separator); - define("_CONF_PATH_",_BASE_DIR_."config".$path_separator); - define("_DEBUG_PATH_",_BASE_DIR_."chatbot".$path_separator."debug".$path_separator); - define("_INSTALL_PATH_",_BASE_DIR_.$path_separator."install".$path_separator); + define("_INC_PATH_",_BASE_DIR_.$path_separator); + define("_ADMIN_PATH_",_BASE_DIR_."admin".$path_separator); + define("_GLOBAL_PATH_",_BASE_DIR_."global".$path_separator); + define("_BOTCORE_PATH_",_BASE_DIR_."chatbot".$path_separator."core".$path_separator); + define("_AIMLPHP_PATH_",_BASE_DIR_."chatbot".$path_separator."aiml_to_php".$path_separator); + define("_LIB_PATH_",_BASE_DIR_."library".$path_separator); + define("_ADDONS_PATH_",_BASE_DIR_."chatbot".$path_separator."addons".$path_separator); + define("_CONF_PATH_",_BASE_DIR_."config".$path_separator); + define("_DEBUG_PATH_",_BASE_DIR_."chatbot".$path_separator."debug".$path_separator); + define("_INSTALL_PATH_",_BASE_DIR_.$path_separator."install".$path_separator); -//------------------------------------------------------------------------ -// server name -//------------------------------------------------------------------------ - $server = strtolower($_SERVER['HTTP_HOST']); //leave this to auto detect - $dev_host = "localhost"; //the name of your dev server - $alternate_local_server_name = "[alternate_local_server_name]"; // Use if you test on a local network - the network name of the server computer. -//------------------------------------------------------------------------ -// parent bot -// the parent bot is used to find aiml matches if no match is found for the current bot -// in the database the bot_parent_id is the id of the bot to use -// if no parent bot is used this is set to zero -// the actual parent bot is set later on in program o there is no need to edit this value -//------------------------------------------------------------------------ - $bot_parent_id = 1; + //------------------------------------------------------------------------ + // parent bot + // the parent bot is used to find aiml matches if no match is found for the current bot + // in the database the bot_parent_id is the id of the bot to use + // if no parent bot is used this is set to zero + // the actual parent bot is set later on in program o there is no need to edit this value + //------------------------------------------------------------------------ + $bot_parent_id = 1; //------------------------------------------------------------------------ -// Select which server we are on -// And set the default bot configuration -// And the database settings +// Set the default bot configuration And the database settings //------------------------------------------------------------------------ - if($server==$dev_host or ((!empty($alternate_local_server_name) and $server == $alternate_local_server_name))) - { //------------------------------------------------------------------------ - // Development server settings - //------------------------------------------------------------------------ - $time_zone_locale = "[lsTZ]"; // a full list can be found at http://uk.php.net/manual/en/timezones.php - $dbh = "[local_dbh]"; # dev remote server location - $dbPort = "[local_dbPort]"; # dev database name/prefix - $dbn = "[local_dbn]"; # dev database name/prefix - $dbu = "[local_dbu]"; # dev database username - $dbp = "[local_dbp]"; # dev database password + // DB and time zone settings + //------------------------------------------------------------------------ + + $time_zone_locale = "[timezone]"; // a full list can be found at http://uk.php.net/manual/en/timezones.php + $dbh = "[dbh]"; # dev remote server location + $dbPort = "[dbPort]"; # dev database name/prefix + $dbn = "[dbn]"; # dev database name/prefix + $dbu = "[dbu]"; # dev database username + $dbp = "[dbp]"; # dev database password - //these are the admin DB settings in case you want make the admin a different db user with more privs - $adm_dbh = "[local_dbh]"; - $adm_dbn = "[local_dbn]"; - $adm_dbu = "[adm_dbu]"; - $adm_dbp = "[adm_dbp]"; + //these are the admin DB settings in case you want make the admin a different db user with more privs + $adm_dbu = "[adm_dbu]"; + $adm_dbp = "[adm_dbp]"; //------------------------------------------------------------------------ // Default bot settings - //------------------------------------------------------------------------ - - //Used to populate the stack when first initialized - $default_stack_value = "om"; - //Default conversation id will be set to current session - $default_convo_id = session_id(); - - //default bot config - this is the default bot most of this will be overwriten by the bot configuration in the db - $default_bot_id = 1; - $default_format = "[default_format]"; - $default_pattern = "[default_pattern]"; - $default_use_aiml_code = '[default_use_aiml_code]'; - $default_update_aiml_code = '[default_update_aiml_code]'; - $default_conversation_lines = [default_conversation_lines]; - $default_remember_up_to = [default_remember_up_to]; - $default_debugemail = "[default_debugemail]"; - /* - * $default_debugshow - The level of messages to show the user - * 0=none, - * 1=error+general, - * 2=error+general+sql, - * 3=everything - */ - $default_debugshow = [default_debugshow]; - - /* - * $default_debugmode - How to show the debug data - * 0 = source code view - show debugging in source code - * 1 = file log - log debugging to a file - * 2 = page view - display debugging on the webpage - * 3 = email each conversation line (not recommended) - */ - $default_debugmode = [default_debugmode]; - $default_save_state = "[default_save_state]"; - $error_response = "[error_response]"; - $unknown_user = "[unknown_user]"; - + //------------------------------------------------------------------------ + //Used to populate the stack when first initialized + $default_stack_value = "om"; + //Default conversation id will be set to current session + $default_convo_id = session_id(); + + //default bot config - this is the default bot most of this will be overwriten by the bot configuration in the db + $default_bot_id = 1; + $default_format = "[default_format]"; + $default_pattern = "[default_pattern]"; + $default_use_aiml_code = '[default_use_aiml_code]'; + $default_update_aiml_code = '[default_update_aiml_code]'; + $default_conversation_lines = [default_conversation_lines]; + $default_remember_up_to = [default_remember_up_to]; + $default_debugemail = "[default_debugemail]"; + /* + * $default_debugshow - The level of messages to show the user + * 0=none, + * 1=error+general, + * 2=error+general+sql, + * 3=everything + */ + $default_debugshow = [default_debugshow]; + + /* + * $default_debugmode - How to show the debug data + * 0 = source code view - show debugging in source code + * 1 = file log - log debugging to a file + * 2 = page view - display debugging on the webpage + * 3 = email each conversation line (not recommended) + */ + $default_debugmode = [default_debugmode]; + $default_save_state = "[default_save_state]"; + $error_response = "[default_error_response]"; + $unknown_user = "Seeker"; //------------------------------------------------------------------------ // Default debug data //------------------------------------------------------------------------ - // Report all PHP errors - $e_all = defined('E_DEPRECATED') ? E_ALL ^ E_DEPRECATED : E_ALL; - error_reporting($e_all); - - //initially set here but overwriten by bot configuration in the admin panel - $debuglevel = $default_debugshow; - - //for quick debug to override the bot config debug options - //0 - Do not show anything - //1 - will print out to screen immediately - $quickdebug = 0; - - //for quick debug - //1 = will write debug data to file regardless of the bot config choice - //it will write it as soon as it becomes available but this this will be finally - //overwriten once if and when the conversation turn is complete - //this will hammer the server if left on so dont leave it on... use in emergencies. - $writetotemp = 0; - - //debug folders where txt files are stored - $debugfolder = _DEBUG_PATH_; - $debugfile = $debugfolder.$default_convo_id.".txt"; - } - else - { - //------------------------------------------------------------------------ - // LIVE server settings - //------------------------------------------------------------------------ - $time_zone_locale = "[rsTZ]"; // a full list can be found at http://uk.php.net/manual/en/timezones.php - $dbh = "[remote_dbh]"; - $dbPort = "[remote_dbPort]"; - $dbn = "[remote_dbn]"; - $dbu = "[remote_dbu]"; - $dbp = "[remote_dbp]"; - - - //these are the admin DB settings in case you want make the admin a different db user with more privs - $adm_dbh = "[remote_dbh]"; - $adm_dbn = "[remote_dbn]"; - $adm_dbu = "[adm_dbu]"; - $adm_dbp = "[adm_dbp]"; + // Report all PHP errors + $e_all = defined('E_DEPRECATED') ? E_ALL ^ E_DEPRECATED : E_ALL; + error_reporting($e_all); + + //initially set here but overwriten by bot configuration in the admin panel + $debuglevel = $default_debugshow; + + //for quick debug to override the bot config debug options + //0 - Do not show anything + //1 - will print out to screen immediately + $quickdebug = 0; + //for quick debug + //1 = will write debug data to file regardless of the bot config choice + //it will write it as soon as it becomes available but this this will be finally + //overwriten once if and when the conversation turn is complete + //this will hammer the server if left on so dont leave it on... use in emergencies. + $writetotemp = 0; + + //debug folders where txt files are stored + $debugfolder = _DEBUG_PATH_; + $debugfile = $debugfolder.$default_convo_id.".txt"; + //------------------------------------------------------------------------ - // Default bot settings - //------------------------------------------------------------------------ - - //Used to populate the stack when first initialized - $default_stack_value = "om"; - //Default conversation id will be set to current session - $default_convo_id = session_id(); - - //default bot config - this is the default bot most of this will be overwriten by the bot configuration in the db - $default_bot_id = 1; - $default_format = "[default_format]"; - $default_pattern = "[default_pattern]"; - $default_use_aiml_code = '[default_use_aiml_code]'; - $default_update_aiml_code = '[default_update_aiml_code]'; - $default_conversation_lines = [default_conversation_lines]; - $default_remember_up_to = [default_remember_up_to]; - $default_debugemail = "[default_debugemail]"; - /* - * $default_debugshow - The level of messages to show the user - * 0=none, - * 1=error+general, - * 2=error+general+sql, - * 3=everything - */ - $default_debugshow = [default_debugshow]; - - /* - * $default_debugmode - How to show the debug data - * 0 = source code view - show debugging in source code - * 1 = file log - log debugging to a file - * 2 = page view - display debugging on the webpage - * 3 = email each conversation line (not recommended) - */ - $default_debugmode = [default_debugmode]; - $default_save_state = "[default_save_state]"; - $error_response = "[error_response]"; - $unknown_user = "[unknown_user]"; - - //------------------------------------------------------------------------ - // Default debug data + // Set Misc Data //------------------------------------------------------------------------ - - // Turn off all error reporting - error_reporting(0); - - - //initially set here but overwriten by bot configuration in the admin panel - $debuglevel = $default_debugshow; - - //for quick debug to override the bot config debug options - //0 - Do not show anything - //1 - will print out to screen immediately - $quickdebug = 0; - - //for quick debug - //1 = will write debug data to file regardless of the bot config choice - //it will write it as soon as it becomes available but this this will be finally overwriten once if and when the conversation turn is complete - $writetotemp = 1; - - //debug folders where txt files are stored - $debugfolder = _DEBUG_PATH_; - $debugfile = $debugfolder.$default_convo_id.".txt"; - } -//------------------------------------------------------------------------ -// Set Misc Data -//------------------------------------------------------------------------ - $botmaster_name = "[botmaster_name]"; + $botmaster_name = "[botmaster_name]"; -//------------------------------------------------------------------------ -// Set Program O Website URLs -//------------------------------------------------------------------------ + //------------------------------------------------------------------------ + // Set Program O Website URLs + //------------------------------------------------------------------------ - define('FAQ_URL', 'http://www.program-o.com/ns/faq/'); - define('NEWS_URL', 'http://www.program-o.com/ns/feed/news/'); #This needs to be altered to reflect the correct URL - define('RSS_URL', 'http://blog.program-o.com/feed/'); - define('SUP_URL', 'http://forum.program-o.com/syndication.php'); - define('BUGS_EMAIL', 'bugs@program-o.com'); + define('FAQ_URL', 'http://www.program-o.com/ns/faq/'); + define('NEWS_URL', 'http://www.program-o.com/ns/feed/news/'); #This needs to be altered to reflect the correct URL + define('RSS_URL', 'http://blog.program-o.com/feed/'); + define('SUP_URL', 'http://forum.program-o.com/syndication.php'); + define('BUGS_EMAIL', 'bugs@program-o.com'); //------------------------------------------------------------------------ // @@ -256,53 +160,48 @@ // //------------------------------------------------------------------------ + //------------------------------------------------------------------------ + // Set timezone + //------------------------------------------------------------------------ + if(function_exists("date_default_timezone_set") && function_exists("date_default_timezone_get")) + { + @date_default_timezone_set(@date_default_timezone_get()); + } + elseif(function_exists("date_default_timezone_set")) + { + @date_default_timezone_set($time_zone_locale); + } + //------------------------------------------------------------------------ + // Load words list and large arrays into session variables + //------------------------------------------------------------------------ + if (empty($_SESSION['commonWords'])) + { + $_SESSION['commonWords'] = file(_CONF_PATH_.'commonWords.dat', FILE_IGNORE_NEW_LINES); + } -//------------------------------------------------------------------------ -// Set timezone -//------------------------------------------------------------------------ - - if(function_exists("date_default_timezone_set") && function_exists("date_default_timezone_get")) - { - @date_default_timezone_set(@date_default_timezone_get()); - } - elseif(function_exists("date_default_timezone_set")) - { - @date_default_timezone_set($time_zone_locale); - } - - -//------------------------------------------------------------------------ -// Load words list and large arrays into session variables -//------------------------------------------------------------------------ - if (empty($_SESSION['commonWords'])) - { - #$_SESSION['commonWords'] = file(_CONF_PATH_.'commonWords.dat', FILE_IGNORE_NEW_LINES); - } - - $commonwordsArr = $_SESSION['commonWords']; - - if (empty($_SESSION['allowedHtmlTags'])) - { - #$_SESSION['allowedHtmlTags'] = file(_CONF_PATH_.'allowedHtmlTags.dat', FILE_IGNORE_NEW_LINES); - } - $allowed_html_tags = $_SESSION['allowedHtmlTags']; + $commonwordsArr = $_SESSION['commonWords']; + if (empty($_SESSION['allowedHtmlTags'])) + { + $_SESSION['allowedHtmlTags'] = file(_CONF_PATH_.'allowedHtmlTags.dat', FILE_IGNORE_NEW_LINES); + } + $allowed_html_tags = $_SESSION['allowedHtmlTags']; -//------------------------------------------------------------------------ -// Set Program O globals -// Do not edit -//------------------------------------------------------------------------ - $srai_iterations = ''; - $offset=1; - $debugArr = array(); + //------------------------------------------------------------------------ + // Set Program O globals + // Do not edit + //------------------------------------------------------------------------ + $srai_iterations = ''; + $offset=1; + $debugArr = array(); -//------------------------------------------------------------------------ -// Set Script Installation as completed -//------------------------------------------------------------------------ + //------------------------------------------------------------------------ + // Set Script Installation as completed + //------------------------------------------------------------------------ - define('SCRIPT_INSTALLED', true); + define('SCRIPT_INSTALLED', true); ?> \ No newline at end of file diff --git a/config/install_config.php b/config/install_config.php new file mode 100644 index 0000000..e0ded71 --- /dev/null +++ b/config/install_config.php @@ -0,0 +1,49 @@ + \ No newline at end of file diff --git a/gui/xml/index.php b/gui/xml/index.php index 6a60b38..e41dc7e 100644 --- a/gui/xml/index.php +++ b/gui/xml/index.php @@ -10,7 +10,9 @@ XML interface ***************************************/ - include_once('../../config/global_config.php'); + if (!file_exists('../config/global_config.php')) header('location: ../install/install_programo.php'); + require_once('../config/global_config.php'); + $response = ''; session_start(); function get_response($path){ diff --git a/install/config.tpl.htm b/install/config.tpl.htm deleted file mode 100644 index a026ce0..0000000 --- a/install/config.tpl.htm +++ /dev/null @@ -1,506 +0,0 @@ - - - - - Program O Installation - - - - -
    -
    - Program O Installation - [title] -
    -
    -
    -
    -
    -
    [cr6] - - -
    - [mainPanel] -
    -
    -
    [cr4] - - -
     
    - - - - - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - Help! -
    General Configuration:Database Server Config
    Botmaster Info:
    - - - -
    Remote Database Data
    Debugging Options:
    - - - -
    Debugging output method - - - - - - -
    - - - - -
    - - - - - - - -
    [notes]
    - - - -
    -
    - - -
    -
    -
    Individual Bot Configuration
    - [BotsTableForm] -
     
    - [SubmitButton] -
     
    -
    -
    - - -
    - - - - - - -
    - - -
    # Note: This is generally the server computer's local network name.
    -
    * Note: Place each bot name on a separate line. At least one name is required.
    -
    - For more detailed information with this page, please see the Help Page, or - click the help icon for an individual field. -
    - - - - -
    -   The configuration for both the bot and the database are complete, - but you now need to upload your bot's AIML categories. Please log into the - Admin Page to complete the process. -
    -
    -
    -
    NOTE:
    If you wish to use a different account - for your Program O Admin user, then you will need to edit the configuration - file manually. If you have any questions about this, or anything else about - Program O, please visit the forums at http://forum.program-o.com. -
    - - -
    Bot Info for the Primary Chatbot
    -
    - - - - - -
    -
    - - - - -
    -
    - - - - -
    -
    - - - - -
    -
    - - - - - -
    -
    - - - - - -
    -
    - - - - -
    -
    - - - - -
    -
    - - - - -
    - - -
    Installation Complete!
    - [notes] - - -
    Installation Failed!
    - [errorMessage] - - -
    - WARNING! - - Could not create the database. Please check your database settings and make any - necessary corrections. Some database hosts do not allow users to directly create - new databases. You may need to contact your hosting provider to learn how to set - up a new database. If you have questions about this, or anytyhing else regarding - Program O, please visit the forums at www.program-o.com - -
    - diff --git a/install/config_template_tags.dat b/install/config_template_tags.dat index 0125cfe..4fc8f01 100644 --- a/install/config_template_tags.dat +++ b/install/config_template_tags.dat @@ -28,7 +28,7 @@ [local_admin_dbu] [local_admin_dbp] [localhost] -[lsTZ] +[tzLocale] [quickdebug] [remote_admin_dbh] [remote_admin_dbn] diff --git a/install/help.css b/install/help.css index cb92e60..e23c239 100644 --- a/install/help.css +++ b/install/help.css @@ -27,6 +27,8 @@ div.row span.label {float: left; width: 49.5%; text-align: left; padding-left: 3 div.row span.label:hover, div.row span.formw:hover {background-color: #EEE;} .center { text-align: center; } .bold { font-weight: bold; } +.underline {border: 2px outset #666;border-top: 1px outset #666; border-left: 1px outset #666;} +.big { font-size: larger; } .ul, .ll, .ur, .lr { width: 6px; height: 6px; diff --git a/install/help.php b/install/help.php index 159f695..7def261 100644 --- a/install/help.php +++ b/install/help.php @@ -14,12 +14,10 @@ define ('SECTION_START', ''); # search params for start and end of sections define ('SECTION_END', ''); # search params for start and end of sections -$page = (isset($_GET['page'])) ? (int)$_GET['page'] : 1; -$section = "Page$page"; $template = file_get_contents('help.tpl.htm'); $content = getSection('HelpPage', $template, false); -$helpContent = getSection($section,$template); +$helpContent = getSection('HelpMain',$template); $content = str_replace('[helpContent]', $helpContent, $content); die($content); diff --git a/install/help.tpl.htm b/install/help.tpl.htm index 402a84e..f341f5a 100644 --- a/install/help.tpl.htm +++ b/install/help.tpl.htm @@ -5,38 +5,39 @@ Program O Installation help @@ -68,7 +69,7 @@ - +
    What all this stuff means...
    This help file is intended to describe the various settings used to install Program O to your website. @@ -77,141 +78,100 @@ please check out the Program O Forums.
     
    -
    General Configuration
    -
    - Domain Name +
    General Configuration
    + +
     
    + +
    Botmaster Info
    + +
    + Your Name
    - This is the base address for your bot's page. If your bot can be reached at http://www.example.com/myBot/ - then your domain name is www.example.com
    - The install script is set up to detect this value automatically, but you should verify the value, just - to be on the safe side. + Fairly well self-explanatory. Just enter what you want your bot's visiters to know you by.
    -
    - Alternate Local Server Name + +
    + Email Address
    - If your development server is on a local network, this setting will help Program O to determine the correct - database settings to use for your bot. Without this setting in place, trying to access your local bot from - another local network computer could result in errors, as it will try to use the remote database information, - rather than using the local DB settings. Set this to the local "network name" of your computer. + Used internally to set up contact details, and to (optionally) send debugging data. + See below for debugging options.
    - -
    - Botmaster Info -
    -
    - Admin User Name -
    Required: The username that you wish to use to log into the admin pages.
    -
    -
    - Admin Password -
    Required: The password that you wish to use to log into the admin pages.
    + +
     
    +
    Bot Configuration
    + +
    + Much of the information in this section is pretty much self explanatory, but in the interest + of being thorough, the following items are included. :)
    -
    - Confirm Password + +
    + Bot Name
    - Required: This is checked against the above password to make sure you didn't mis-type it. - Saves your butt if you made a mistake. + It's been said that names are the cornerstone of existence. Names also have power. Choose your + bot's name wisely.
    -
    - Your Name -
    Required: Also self-explanatory.
    + +
    + Bot Description +
    + This is where you provide whatever "back-story" or attributes you like for your chatbot. Feel + free to leave this blank, but remember that a more well thought out bot will USUALLY make for + a higher quality bot. +
    -
    - Email Address + +
    + Bot Active
    - Required: Used internally to set up contact details, and to (optionally) send debugging data. - See below for debugging options. + In order for a chatbot to be visible and/or usable, it has to be marked in the database as active, + but there are times when you need to disable or hide your chatbot. Changing this setting in the + admin pages is the best way to handle that. If you want folks to be able to use your bot, then + this needs to be set to TRUE (checked).
    -
    - Debugging Options + +
    + Error Response String +
    + This is what your bot will say to the user if it encounters an internal error. What you set it to is + completely up to you, but it must have a value of some sort. +
    -
    - Debugging output method -
    Default: File
    - This setting allows you to choose how to create debugging information in the following ways: -
      -
    • - Show in Source Places debugging data in the source code of the page, within - HTML comment tags. Note - only works when Response Format is set to HTML(See below) -
    • -
    • - File Creates a debug file that's based on the user's internal id and the current time. - Stored in the /chatbot/debug/ folder. -
    • -
    • - Display Prints debugging data directly to the screen. Note - only works when Response - Format is set to HTML(See below) -
    • -
    • - email Sends debugging data via email - WARNING! can create a LOT of email - traffic! Use sparingly, and with care. -
    • -
    + +
    + Default Pickup Line Pattern + Default: * +
    + This is usually best kept as the default pattern, unless you're upgrading from Program O version 1.0, + which used "RANDOM PICKUP LINE" as the pattern. Even if you are upgrading, you can also leave + this value as * and modify your AIML pickup line category to use * for it's pattern.
    -
    - Debug Level + +
    + Chat Line Count
    - Here you have a choice of four options: -
      -
    • Nothing No debug data is created. Improves bot performance by a small amount.
    • -
    • General Only basic information is generated.
    • -
    • - General/SQL In addition to the data generated in General, above, all SQL statements - are also recorded. -
    • -
    • - Everything The results from a vast number of operations, calculations or function calls - (sometimes several times within certain functions) is generated. WARNING! HUGE amounts - of data is generated with this setting, to the point where script performance will be affected, - especially if the debug output method is set to "Show in Source". And for God's sake, DON'T - use this if you're going to have debugging data sent to your email! -
    • -
    - For the most part, this should be set to "Nothing", unless you're trying to trace a problem with your AIML - files, and even then, it's best to only use "General". The only time the other two settings should be used - is to troubleshoot the script itself. + This setting tells Program O how many lines of chat to display at a time. Set it to zero (0) for unlimited, + but beware that this can affect your bot's performance if it gets a lot of users at one time.
    -
    - Save-State + +
    + History Count
    - Default: Session
    - Again, you have some options: -
      -
    • - Session This is the default behavior, and it causes Program O to save all current user data into a - single session variable, in array format. -
    • -
    • - Database Instead of saving the user data in the PHP session array, this saves the data to the - bot's database instead. -
    • -
    - There's no real advantage, one way or the other here. It's simply a matter of preference. + This setting determines how far back your bot "remembers" the conversation. There is no "unlimited" + setting for this one, since that would eat up resources far more than necessary, and would severely + affect bot performance. It's suggested that this be set to no more than 30, to prevent undue lag. + If you're using the JSON response setting, you may want to set this to only 1, and use client-side + scripting to add the responses to a div of (possibly) already existing responses, so as to create + an "unlimited" chat log for the visitors. Just a suggestion, though. :)
    +
    Response Format
    @@ -240,135 +200,190 @@ modification prior to use.
    -
    - Error Response String -
    - This is what your bot will say to the user if it encounters an internal error. What you set it to is - completely up to you, but it must have a value of some sort. -
    -
    -
    - Default Pickup Line Pattern - Default: * -
    - This is usually best kept as the default pattern, unless you're upgrading from Program O version 1.0, - which used "RANDOM PICKUP LINE" as the pattern. Even if you are upgrading, you can also leave - this value as * and modify your AIML pickup line category to use * for it's pattern. -
    -
    -
    - Chat Line Count + +
    + Store PHP code in DB
    - This setting tells Program O how many lines of chat to display at a time. Set it to zero (0) for unlimited, - but beware that this can affect your bot's performance if it gets a lot of users at one time. + This is a "performance enhancement", designed to streamline the script by re-using generated + PHP code for previously encountered AIML categories. This is still experimental, so you can + disable this option if you run into trouble.
    -
    - History Count + +
    + Use Stored PHP Code from DB
    - This setting determines how far back your bot "remembers" the conversation. There is no "unlimited" - setting for this one, since that would eat up resources far more than necessary, and would severely - affect bot performance. It's suggested that this be set to no more than 30, to prevent undue lag. + This setting goes hand in hand with the previous one, and is again, experimental at this point. + Our tests show that the script can run up to 35% faster with these two settings enabled, but + we've also experienced some minor, random glitches with it, so use at your own risk. :)
    -
     
    -
    - Database Server Config
    - - Please note that you must have already created the database for your bot, - though that database can be empty (the script will create the necessary tables automatically). - Please have the database connection information ready at this point. - -
    -
    Local Database Data
    -
    - Local Database Host Name + +
     
    + +
    Database Configuration
    + +
     
    + +
    Database Connection Info
    + +
    + Please note that you must have already created the database for your bot, + though that database can be empty (the script will create the necessary tables automatically). + This is so because many web hosting companies do not allow their customers to create new + databases except through their web hosting control panels. + Please have the database connection information ready at this point. +
    + +
    + Database Host Name
    Default: localhost
    - Required: This setting is part of the local database login credentials that allow Program O to connect to your + This setting is part of the local database login credentials that allow Program O to connect to your bot's local database. Under the vast majority of circumstances, this is simply 'localhost', but there is the occasional rare exception.
    -
    - Local Database Port + +
    + Database Port
    Default: 3306
    Required only in the very rare case that the database server has been configured to use a different port.
    -
    - Local Database Name + +
    + Database Name
    - Required: When you performed the task of creating your local database, you should have already named the DB, + When you performed the task of creating your local database, you should have already named the DB, along with configuring username and password. The DB name from that information goes here.
    -
    - Local Database Username + +
    + Database Username
    - Required: Just as with the name of the database, above, this is part of the connection information that you + Just as with the name of the database, above, this is part of the connection information that you should have gathered when you set up your bot's DB.
    -
    - Local Database Password + +
    + Database Password
    - Required: This is the password for your script to access your bot's database. There's no confirmation for + This is the password for your script to access your bot's database. There's no confirmation for this field, like there was with the admin password, earlier, so be careful to ensure it's entered correctly.
    -
    - Local Database Timezone + +
    + Database Timezone
    - This is the timezone info for your local server (the one you're currently using). + This is the timezone info for server. This setting should be automatically set by the installation script, but you should verify it's accuracy.
    -
    Remote Database Data
    -
    - Remote Database Host Name -
    - Required: This is the host name of the remote database server, and again is part of the information that you - should have already gathered when you created your remote database. -
    -
    -
    - Remote Database Port + +
     
    + +
    Chatbot Administration Info
    + +
    + Admin Area Username
    - Default: 3306
    - Just like the local database port, this setting is only required if the database server uses a non-standard - port. + The administration pages for Program O are restricted to the chatbot's administrator, and so + must be provided with proper credentials. This is the username for the Admin account.
    -
    - Remote Database Name + +
    + Admin Area Password
    - Required: See Local DB name for details. + Just like the admin username, this setting is required. This password is encrypted in the + database, to help improve security.
    -
    - Remote Database Username + +
     
    + +
    Debugging Options
    + +
    + Debug Level
    - Required: See Local Database Username for details. + This setting determines the amount of data generated by the debugging functions. Here you have + a choice of four options: +
      +
    • Nothing No debug data is created. Improves bot performance by a small amount.
    • +
    • General Only basic information is generated.
    • +
    • + General/SQL In addition to the data generated in General, above, all SQL statements + are also recorded. +
    • +
    • + Everything The results from a vast number of operations, calculations or function calls + (sometimes several times within certain functions) is generated. WARNING! HUGE amounts + of data is generated with this setting, to the point where script performance will be affected, + especially if the debug output method is set to "Show in Source". And for God's sake, DON'T + use this if you're going to have debugging data sent to your email! +
    • +
    + For the most part, this should be set to "Nothing", unless you're trying to trace a problem with your AIML + files, and even then, it's best to only use "General". The only time the other two settings should be used + is to troubleshoot the script itself.
    -
    - Remote Database Password -
    - Required: See Local Database Password for details. + +
    + Debug Mode +
    Default: File
    + "Debug Mode" is a setting to determine which output method is used for providing debugging data. + This setting allows you to choose how to obtain debugging information in the following ways: +
      +
    • + Show in Source Places debugging data in the source code of the page, within + HTML comment tags. Note - only works when Response Format is set to HTML(See below) +
    • +
    • + File Creates a debug file that's based on the user's internal id and the current time. + Stored in the /chatbot/debug/ folder. +
    • +
    • + Display Prints debugging data directly to the screen. Note - only works when Response + Format is set to HTML(See below) +
    • +
    • + email Sends debugging data via email - WARNING! can create a LOT of email + traffic! Use sparingly, and with care. +
    • +
    -
    - Remote Database Timezone + +
    + Save State
    - Required, but automatically generated: See Local Database Timezone for details. + Default: Session
    + This setting determines how Program O will manage user data. Again, you have some options: +
      +
    • + Session This is the default behavior, and it causes Program O to save all current user data into a + single session variable, in array format. +
    • +
    • + Database Instead of saving the user data in the PHP session array, this saves the data to the + bot's database instead. +
    • +
    + There's no real advantage, one way or the other here. It's simply a matter of preference.
    +
     
    -
    - Troubleshooting -
    + +
    Troubleshooting
    +
    File Permissions
    @@ -396,8 +411,4 @@

    - - - - - + diff --git a/install/install.tpl.htm b/install/install.tpl.htm new file mode 100644 index 0000000..8c29f5d --- /dev/null +++ b/install/install.tpl.htm @@ -0,0 +1,309 @@ + + + + + Program O Installation + + + + +
    +
    + Program O Installation +
    +
    +
    +
    +
    +
    [cr6] + + +
    + [mainPanel] +
    +
    +
    [cr4] + + +
     
    + + + + + + + + + +
    + Help!
    + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Botmaster Info:
    Bot Configuration:
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Database Configuration:
    + +
    Debugging Options:
    + +
    + +
    + +
    +
    [notes]
    + + + +
    +
    + + +
    Installation Complete!
    + [notes] + + +
    Installation Failed!
    + [errorMessage] + + +
    + WARNING! + + Could not create the database. Please check your database settings and make any + necessary corrections. Some database hosts do not allow users to directly create + new databases. You may need to contact your hosting provider to learn how to set + up a new database. If you have questions about this, or anytyhing else regarding + Program O, please visit the forums at www.program-o.com + +
    + + +
    + Please note that all fields are required, but many are already filled out with generic, default + values. +
    +
    + For more detailed information with this page, please see the Help Page, or + click the help icon for an individual field. +
    + + +
    +   The configuration for both the bot and the database are complete, + but you now need to upload your bot's AIML categories. Please log into the + Admin Page to complete the process. +
    +
    +
    +
    NOTE:
    If you wish to use a different account + for your Program O Admin user, then you will need to edit the configuration + file manually. If you have any questions about this, or anything else about + Program O, please visit the forums at http://forum.program-o.com. +
    + + + \ No newline at end of file diff --git a/install/install_programo.php b/install/install_programo.php index ef32bb7..5e30b92 100644 --- a/install/install_programo.php +++ b/install/install_programo.php @@ -13,107 +13,43 @@ session_name('PGO_install'); session_start(); $_SESSION['errorMessage'] = (!empty($_SESSION['errorMessage'])) ? $_SESSION['errorMessage'] : ''; -if (!is_writable('../config/global_config.php')) { - // Read and write for owner, read for everybody else - if (!chmod('../config/global_config.php', 0755)) $_SESSION['errorMessage'] .= 'Please check the file permissions for /config/global_config.php see the Help Page for more information.'; -} -require_once('../library/buildSelect.php'); -require_once('../config/global_config.php'); + +require_once('../config/install_config.php'); define ('SECTION_START', ''); # search params for start and end of sections define ('SECTION_END', ''); # search params for start and end of sections -define ('PHP_SELF', $_SERVER['SCRIPT_NAME']); # search params for start and end of sections +define ('PHP_SELF', $_SERVER['SCRIPT_NAME']); # This is more secure than $_SERVER['PHP_SELF'], and returns more or less the same thing ini_set("display_errors", 0); ini_set("log_errors", true); ini_set("error_log", _INSTALL_PATH_ . "error.log"); if (!file_exists(_INSTALL_PATH_ . "error.log")) file_put_contents(_INSTALL_PATH_ . "error.log", ''); - -$currentDir = getcwd(); -$defaultBaseDir = rtrim(str_ireplace('admin', '', $currentDir), '\\/'); -$sep = (substr($defaultBaseDir,0, 1) == '/') ? '/' : '\\'; - $myHost = $_SERVER['SERVER_NAME']; -$domain_name = $myHost; -switch ($myHost) { - case 'localhost': - case $alternate_local_server_name: - $localhost = $myHost; -/* - $local_dbh = $dbh; - $local_dbn = $dbn; - $local_dbu = $dbu; - $local_dbp = $dbp; -*/ - $hostLocation = 'local'; - break; - default: - $remotehost = $myHost; -/* - $remote_dbh = $dbh; - $remote_dbn = $dbn; - $remote_dbu = $dbu; - $remote_dbp = $dbp; -*/ - $hostLocation = 'remote'; -} -if (empty($dbu)) $dbu = $_SESSION[$hostLocation . '_dbu']; -if (empty($dbp)) $dbp = $_SESSION[$hostLocation . '_dbp']; - -$replTagsArray = file(_INSTALL_PATH_ . 'config_template_tags.dat', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); -$replVarsArray = array(); -foreach ($replTagsArray as $value) { - $value = trim($value); - $tmpVal = str_replace(array('[',']'), '', $value); - $replVarsArray[$value] = $tmpVal; -} chdir(dirname( realpath( __FILE__ ))); -$rsTzVal = (function_exists('date_default_timezone_get')) ? date_default_timezone_get() : ''; -$validPages = array(1,2,3); -$page_template = file_get_contents('config.tpl.htm'); +$page_template = file_get_contents('install.tpl.htm'); $page = (isset($_REQUEST['page'])) ? $_REQUEST['page'] : 1; -if (!in_array($page, $validPages)) $page = 1; -$func = (isset($_REQUEST['func'])) ? $_REQUEST['func'] : ''; -if (!empty($func)) $nextPage = $func($page); -$pageTemplate = 'container'; +#if (!empty($_POST)) die("
    POST vars:\n\n".print_r($_POST, true)."\n\n
    \n"); +#die ("page = $page"); +$action = (isset($_REQUEST['action'])) ? $_REQUEST['action'] : ''; +if (!empty($action)) $message = $action($page); +$pageTemplate = 'Container'; $pageNotes = ucwords("Page $page Notes"); $content = getSection('Header', $page_template, false); -$content .= "\n"; $content .= getSection($pageTemplate, $page_template); $content .= getSection('Footer', $page_template); $content .= getSection("jQuery$page", $page_template); $notes = getSection($pageNotes, $page_template); $submitButton = getSection('SubmitButton', $page_template); -$main = getMain($page, $page_template); +$main = ($page == 1) ? getSection('InstallForm',$page_template) : $message; $tmpSearchArray = array(); -$content = str_replace('[title]', "Step $page", $content); $content = str_replace('[mainPanel]', $main, $content); -$content = str_replace('[localhost]', $localhost, $content); +$content = str_replace('[http_host]', $myHost, $content); $content = str_replace('[notes]', $notes, $content); -$content = str_replace('[SubmitButton]', $submitButton, $content); -$content = str_replace('[rsTZ]', $rsTzVal, $content); -$content = str_replace('[local_dbPort]', $dbPort, $content); -$content = str_replace('[remote_dbPort]', $dbPort, $content); $content = str_replace('[PHP_SELF]', PHP_SELF, $content); $content = str_replace('[errorMessage]', $_SESSION['errorMessage'], $content); -foreach ($replVarsArray as $search => $replace) { - $replace = trim($replace); - if (!isset($$replace)) $$replace = ''; - $repl1 = $$replace; - if (isset($$replace) and (!empty($$replace))) { - $content = str_replace($search, $$replace, $content); - } -} -if ($page == 2) $content = str_replace('[BotsTableForm]', page2form(), $content); $content = str_replace('[cr6]', "\n ", $content); $content = str_replace('[cr4]', "\n ", $content); -$content = str_replace('[nextPage]', $page + 1, $content); $content = str_replace("\r\n", "\n", $content); $content = str_replace("\n\n", "\n", $content); -foreach ($replVarsArray as $key => $value) { - if (isset($$value)) { - $content = str_replace($key, $$value, $content); - } -} $content .= << @@ -140,163 +76,27 @@ function getSection($sectionName, $page_template, $notFoundReturn = true) { return trim($out); } -function save($page) { - global $replVarsArray, $quickdebug, $writetotemp, $hostLocation, $postVars; - $postVars = ''; - $postVars = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING); - ksort($postVars); - foreach ($postVars as $key => $value) { - if ($key == 'use_aiml_code') $_SESSION['default_use_aiml_code'] = rtrim($value); - if ($key == 'update_aiml_code') $_SESSION['default_update_aiml_code'] = rtrim($value); - $_SESSION[$key] = rtrim($value); - } - #$x = file_put_contents(_INSTALL_PATH_ . 'sessionVars.txt', print_r($_SESSION, true) . "\r\n"); - checkDBContents($postVars); - switch ($page) { - case 1: - $out = 2; - break; - case 2: - $out = 3; - break; - case 3: - $newConfigFile = file_get_contents('../config/global_config.tpl'); - $addScriptInfo = 0; - if (isset($_SESSION['botNames'])) { - $botNameList = $_SESSION['botNames']; - $nameArray = explode("\n", $botNameList); - $flippedNameArray = array_flip($nameArray); - $defaultBotID = $_SESSION['default_bot_id']; - } - $botArrayTemplate = '$botNames = array('; - foreach ($replVarsArray as $key => $value) { - if ($key == 'botNames') { - $nameList = explode("\n", $value); - $listCount = 1; - foreach ($nameList as $botName) { - $botName = trim($botName); - $botArrayTemplate .= "$listCount => '$botName', "; - $listCount++; - } - $botArrayTemplate = rtrim($botArrayTemplate, ',') . ');'; - $value = $botArrayTemplate . "\r\n"; - } - $repl = (isset($_SESSION[$value])) ? $_SESSION[$value] : 'missing'; - $newConfigFile = str_replace($key, $repl, $newConfigFile); - $newConfigFile = str_replace("\r\n", "\n", $newConfigFile); - } - $newConfigFile = str_replace('[addScriptInfo]', $addScriptInfo, $newConfigFile); - $x = file_put_contents('../config/global_config.php', $newConfigFile); - $out = ""; - break; - default: - } - return $out; -} - -function getMain($page, $page_template) { - global $dbh, $dbu, $dbp, $dbn, $dbPort, $adm_dbu, $adm_dbp; - switch ($page) { - case 1: - return getSection('GeneralConfig',$page_template); - break; - case 2: - return getSection('AIMLConfig', $page_template); - break; - } - $sql_template = <<\n"; - return ($result) ? getSection('InstallComplete', $page_template) : getSection('InstallError', $page_template); -} - -function page2form() { +function Save() { global $page_template; - $out = ''; - $bot_id = 1; - #$use_aiml_code_checked = ($_SESSION["use_aiml_code"] == 1) ? ' checked="checked"' : ''; - #$update_aiml_code_checked = ($_SESSION["update_aiml_code"] == 1) ? ' checked="checked"' : ''; - $convoLines = (!empty($_SESSION["conversation_lines"])) ? $_SESSION["conversation_lines"] : $_SESSION['default_conversation_lines']; - $tmpSection = getSection('BotsTableForm', $page_template); - $tmpSection = str_replace('[bot_id]', $bot_id, $tmpSection); - #$tmpSection = str_replace('[bot_name]', $_SESSION["bot_name"], $tmpSection); - #$tmpSection = str_replace('[use_aiml_code_checked]', $use_aiml_code_checked, $tmpSection); - #$tmpSection = str_replace('[update_aiml_code_checked]', $update_aiml_code_checked, $tmpSection); - $tmpSection = str_replace('[cl_value]', $convoLines, $tmpSection); - $out .= $tmpSection; - return $out; -} - -function checkDBContents($postVars) { - global $hostLocation; - $x = file_put_contents(_INSTALL_PATH_ . $hostLocation . '_postVars.txt', print_r($postVars, true) . "\r\n"); - switch ($hostLocation) { - case 'local': - $tmpDbh = $_SESSION['local_dbh']; - $tmpDbn = $_SESSION['local_dbn']; - $tmpDbu = $_SESSION['local_dbu']; - $tmpDbp = $_SESSION['local_dbp']; - $tmpDbPort = $_SESSION['local_dbPort']; - default: - $tmpDbh = $_SESSION['remote_dbh']; - $tmpDbn = $_SESSION['remote_dbn']; - $tmpDbu = $_SESSION['remote_dbu']; - $tmpDbp = $_SESSION['remote_dbp']; - $tmpDbPort = $_SESSION['remote_dbPort']; + $_SESSION['errorMessage'] = ''; + + // First off, write the config file + $myPostVars = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING); + ksort($myPostVars); + $configContents = file_get_contents(_CONF_PATH_ . 'global_config.tpl'); + foreach ($myPostVars as $key => $value) { + $tagSearch[] = "[$key]"; + $varReplace[] = $value; } + $configContents = str_replace($tagSearch, $varReplace, $configContents); + $saveFile = file_put_contents(_CONF_PATH_ . 'global_config.php', $configContents); + #die("
    Executing function Save - Config file saved as config.php.test\n\n
    \n"); - $death = << -Variables: -Host Location: $hostLocation -dbh: $tmpDbh -dbu: $tmpDbu -dbp: $tmpDbp -dbn: $tmpDbn -dbPort: $tmpDbPort - - -endDeath; - - $conn = mysql_connect($tmpDbh, $tmpDbu, $tmpDbp) or die( "mysql_connect error:". mysql_errno() . ', ' . mysql_error() . " at line " . __LINE__ . ' of file ' . __FILE__ . ' in function ' . __FUNCTION__ . '.' . $death); - mysql_select_db($tmpDbn,$conn); + // Now, update the data to the database, starting with making sure the tables are installed $sql = "show tables;"; - $result = mysql_query($sql,$conn) or die ("Houston, we have a problem! " . mysql_error() . ", sql = $sql
    \nVars:
    \n$death"); + $conn = mysql_connect($myPostVars['dbh'], $myPostVars['dbu'], $myPostVars['dbp']) or $_SESSION['errorMessage'] = "mysql_connect error:". mysql_errno() . ', ' . mysql_error() . " at line " . __LINE__ . ' of file ' . __FILE__ . ' in function ' . __FUNCTION__; + mysql_select_db($myPostVars['dbn'],$conn); + $result = mysql_query($sql,$conn) or $_SESSION['errorMessage'] = "Houston, we have a problem! " . mysql_error() . ", sql = $sql
    \nVars:
    \n$death"; $out = mysql_fetch_assoc($result); if (empty($out)) { $sql = file_get_contents('new.sql'); @@ -305,12 +105,50 @@ function checkDBContents($postVars) { $death .= "sql:\n$query\n\n"; #die("This is where I died: " . __FILE__ . ', line ' . __LINE__ . "\nDeath = $death"); if (strlen(trim($query)) > 0) { - $result = mysql_query($query,$conn) or die ("Houston, we have a problem! " . mysql_error() . ", sql:\n
    $wuery\n
    \n"); + $result = mysql_query($query,$conn) or $_SESSION['errorMessage'] = "Houston, we have a problem! " . mysql_error() . ", sql:\n
    $wuery\n
    \n"; $success = mysql_affected_rows($result); } } } + $sql_template = <<\n"; + mysql_close($conn); + + return ($result and empty($_SESSION['errorMessage'])) ? getSection('InstallComplete', $page_template) : getSection('InstallError', $page_template); + } ?> diff --git a/install/upgrade.php b/install/upgrade.php index be73f86..847bac8 100644 --- a/install/upgrade.php +++ b/install/upgrade.php @@ -4,12 +4,14 @@ * PROGRAM O * Version: 2.0.1 * FILE: install/upgrade.php -* AUTHOR: ELIZABETH PERREAU -* DATE: MAY 4TH 2011 +* AUTHOR: ELIZABETH PERREAU AND DAVE MORTON +* DATE: JUNE 4TH 2012 * DETAILS: PLEASE RUN THIS FILE TO UPGRADE THE DATABASE FROM VERSION 1 TO VERSION 2 ***************************************/ - include("../config/global_config.php"); + if (!file_exists('../config/global_config.php')) header('location: ../install/install_programo.php'); + require_once('../config/global_config.php'); + //load shared files include_once(_LIB_PATH_."db_functions.php"); @@ -51,7 +53,7 @@ function create_bots_tbl() if($result) { outputDebug( __FILE__, __FUNCTION__, __LINE__, "Created bot table"); } else { - outputDebug( __FILE__, __FUNCTION__, __LINE__, "Error: Creating bot table - Exiting"); + outputDebug( __FILE__, __FUNCTION__, __LINE__, "Errorwhile creating bot table - Exiting"); exit; } @@ -63,7 +65,7 @@ function create_bots_tbl() if($result) { outputDebug( __FILE__, __FUNCTION__, __LINE__, "Added default bot"); } else { - outputDebug( __FILE__, __FUNCTION__, __LINE__, "Error: Adding default bot - Exiting"); + outputDebug( __FILE__, __FUNCTION__, __LINE__, "Error while adding default bot - Exiting"); exit; } @@ -80,7 +82,7 @@ function update_aiml_tbl() if($result) { outputDebug( __FILE__, __FUNCTION__, __LINE__, "Altered AIML tbl"); } else { - outputDebug( __FILE__, __FUNCTION__, __LINE__, "Error: Altering AIML tabl - Exiting"); + outputDebug( __FILE__, __FUNCTION__, __LINE__, "Error while altering AIML tabl - Exiting"); exit; } } From 753ffa711b0285b0f0996cfe40281af6a265f2a8 Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Thu, 7 Jun 2012 23:15:32 -0700 Subject: [PATCH 023/123] Fixed install script Corrected an error where the database was not being selected properly, and revised the error reporting function to list ALL errors. Signed-off-by: Dave Morton --- install/install_programo.php | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/install/install_programo.php b/install/install_programo.php index 5e30b92..bb611da 100644 --- a/install/install_programo.php +++ b/install/install_programo.php @@ -94,19 +94,18 @@ function Save() { // Now, update the data to the database, starting with making sure the tables are installed $sql = "show tables;"; - $conn = mysql_connect($myPostVars['dbh'], $myPostVars['dbu'], $myPostVars['dbp']) or $_SESSION['errorMessage'] = "mysql_connect error:". mysql_errno() . ', ' . mysql_error() . " at line " . __LINE__ . ' of file ' . __FILE__ . ' in function ' . __FUNCTION__; - mysql_select_db($myPostVars['dbn'],$conn); - $result = mysql_query($sql,$conn) or $_SESSION['errorMessage'] = "Houston, we have a problem! " . mysql_error() . ", sql = $sql
    \nVars:
    \n$death"; + $conn = mysql_connect($myPostVars['dbh'], $myPostVars['dbu'], $myPostVars['dbp']) or install_error('Could not connect to the database!',mysql_error(), $sql); + $dbn = $myPostVars['dbn']; + $db = mysql_select_db($dbn,$conn) or install_error("Can't select the database $dbn!", mysql_error(), "use $dbn"); + $result = mysql_query($sql,$conn) or install_error('Unknown database error!',mysql_error(), $sql); $out = mysql_fetch_assoc($result); if (empty($out)) { $sql = file_get_contents('new.sql'); $queries = preg_split("/;/", $sql); foreach ($queries as $query){ - $death .= "sql:\n$query\n\n"; -#die("This is where I died: " . __FILE__ . ', line ' . __LINE__ . "\nDeath = $death"); if (strlen(trim($query)) > 0) { - $result = mysql_query($query,$conn) or $_SESSION['errorMessage'] = "Houston, we have a problem! " . mysql_error() . ", sql:\n
    $wuery\n
    \n"; - $success = mysql_affected_rows($result); + $result = mysql_query($query,$conn) or install_error('Error creating new tables for DB!',mysql_error(), $sql); + $success = mysql_affected_rows(); } } } @@ -115,7 +114,6 @@ function Save() { endSQL; require_once (_LIB_PATH_ . 'error_functions.php'); require_once (_LIB_PATH_ . 'db_functions.php'); - $conn = db_open(); $bot_id = 1; $sql = str_replace('[bot_id]', $bot_id, $sql_template); $sql = str_replace('[bot_name]',$myPostVars["bot_name"], $sql); @@ -138,12 +136,12 @@ function Save() { $sql = str_replace('[debugmode]',$myPostVars["default_debugmode"], $sql); $sql = str_replace('[default_aiml_pattern]',$myPostVars["default_pattern"], $sql); $s = file_put_contents('botAddSQL.txt', $sql); - $x = db_query($sql, $conn) or $_SESSION['errorMessage'] = 'Could not enter bot info for bot #' . $bot_id . '! Error = ' . mysql_error(); + $x = db_query($sql, $conn) or install_error('Could not enter bot info for bot #' . $bot_id . '!', mysql_error(), $sql); $encrypted_adm_dbp = md5($myPostVars["adm_dbp"]); $adm_dbu = $myPostVars["adm_dbu"]; $cur_ip = $_SERVER['REMOTE_ADDR']; - $adminSQL = "insert ignore into `myprogramo` (`id`, `uname`, `pword`, `lastip`) values(null, '$adm_dbu', '$encrypted_adm_dbp', '$cur_ip');"; - $result = db_query($adminSQL, $conn) or $_SESSION['errorMessage'] = 'Could not add admin account! Error = ' . mysql_error() . "user = $adm_dbu, pass = $adm_dbp
    \n"; + $adminSQL = "insert into `myprogramo` (`id`, `uname`, `pword`, `lastip`) values(null, '$adm_dbu', '$encrypted_adm_dbp', '$cur_ip');"; + $result = db_query($adminSQL, $conn) or install_error('Could not add admin credentials!',mysql_error(), $sql); mysql_close($conn); @@ -151,4 +149,16 @@ function Save() { } +function install_error($msg, $err, $sql) { + $errorTemplate = <<There was a problem while working with the database. +Error message: $msg +MySQL error: $err +SQL query: +$sql + +endError; + $_SESSION['errorMessage'] .= $errorTemplate; +} + ?> From 570794e4f5719965188985fa66f0e7d8a4822210 Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Fri, 8 Jun 2012 16:32:12 -0700 Subject: [PATCH 024/123] Multiple minor fixes Corrected a pathiing problem that caused certain files to be "invisible", and merged the DB upgrade code into the install script, to make the process more user friendly. Also added a TARGET attribute of "_blank" to the links generated by the RSS reader. Signed-off-by: Dave Morton --- admin/index.php | 2 +- chatbot/conversation_start.php | 2 -- index.php | 9 +++++++++ install/install_programo.php | 20 ++++++++++++++++++-- install/upgrade_bots_table.sql | 31 ++++++++++++++++++++++++++++++- 5 files changed, 58 insertions(+), 6 deletions(-) diff --git a/admin/index.php b/admin/index.php index 53c3586..ee2fa37 100644 --- a/admin/index.php +++ b/admin/index.php @@ -454,7 +454,7 @@ function getRSS($feed = 'RSS') { $link = $item->link; $published_on = $item->pubDate; $description = $item->description; - $out .= "

    $title

    \n"; + $out .= "

    $title

    \n"; $out .= "

    $description

    "; } } diff --git a/chatbot/conversation_start.php b/chatbot/conversation_start.php index d9d526f..7ee8bc0 100644 --- a/chatbot/conversation_start.php +++ b/chatbot/conversation_start.php @@ -15,8 +15,6 @@ $thisFolder = dirname(realpath(__FILE__)) . DIRECTORY_SEPARATOR; $thisParentFolder = preg_replace('~[/\\\\][^/\\\\]*[/\\\\]$~', DIRECTORY_SEPARATOR, $thisFolder); - if (!file_exists('../config/global_config.php')) header('location: install/install_programo.php'); - require_once('../config/global_config.php'); //load shared files include_once (_LIB_PATH_ . "db_functions.php"); diff --git a/index.php b/index.php index de05587..bb5c256 100644 --- a/index.php +++ b/index.php @@ -1,6 +1,15 @@ " . print_r($_REQUEST, true) . "
    \n"); diff --git a/install/install_programo.php b/install/install_programo.php index bb611da..8375325 100644 --- a/install/install_programo.php +++ b/install/install_programo.php @@ -109,6 +109,8 @@ function Save() { } } } + $sql = 'select `php_code` from `aiml` where 1 limit 1'; + $result = mysql_query($sql,$conn) or upgrade($conn); $sql_template = << diff --git a/install/upgrade_bots_table.sql b/install/upgrade_bots_table.sql index 62094d5..f4f94bb 100644 --- a/install/upgrade_bots_table.sql +++ b/install/upgrade_bots_table.sql @@ -1 +1,30 @@ -ALTER TABLE `bots` ADD `use_aiml_code` INT( 11 ) NULL DEFAULT '1' AFTER `format`; +CREATE TABLE IF NOT EXISTS `bots` ( + `bot_id` int(11) NOT NULL AUTO_INCREMENT, + `bot_name` varchar(255) NOT NULL, + `bot_desc` varchar(255) NOT NULL, + `bot_active` int(11) NOT NULL DEFAULT '1', + `bot_parent_id` int(11) NOT NULL DEFAULT '0', + `format` varchar(10) NOT NULL DEFAULT 'html', + `use_aiml_code` int(11) DEFAULT '1', + `update_aiml_code` int(11) NOT NULL DEFAULT '1', + `save_state` enum('session','database') NOT NULL DEFAULT 'session', + `conversation_lines` int(11) NOT NULL DEFAULT '7', + `remember_up_to` int(11) NOT NULL DEFAULT '10', + `debugemail` int(11) NOT NULL, + `debugshow` int(11) NOT NULL DEFAULT '1', + `debugmode` int(11) NOT NULL DEFAULT '1', + `default_aiml_pattern` varchar(255) NOT NULL DEFAULT 'RANDOM PICKUP LINE', + PRIMARY KEY (`bot_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ; +CREATE TABLE IF NOT EXISTS `wordcensor` ( + `censor_id` int(11) NOT NULL AUTO_INCREMENT, + `word_to_censor` varchar(50) NOT NULL, + `replace_with` varchar(50) NOT NULL DEFAULT '****', + `bot_exclude` varchar(255) NOT NULL, + PRIMARY KEY (`censor_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ; +ALTER TABLE `users` ADD `name` text NOT NULL AFTER `id`; +ALTER TABLE `users` ADD `bot_id` int(11) NOT NULL AFTER `session_id`; +ALTER TABLE `users` ADD `state` text NOT NULL AFTER `last_update`; +ALTER TABLE `aiml` ADD `bot_id` INT( 11 ) NOT NULL DEFAULT '1' AFTER `id`; +ALTER TABLE `aiml` ADD `php_code` TEXT NULL AFTER `filename`; From cadef23f0c6edb763d268806c11fbcaab34c7895 Mon Sep 17 00:00:00 2001 From: Elizabeth Perreau Date: Sat, 9 Jun 2012 13:53:07 +0200 Subject: [PATCH 025/123] FIX debugging information will now display as chosen by user --- library/error_functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/error_functions.php b/library/error_functions.php index b61fd3b..12ace37 100644 --- a/library/error_functions.php +++ b/library/error_functions.php @@ -135,7 +135,7 @@ function handleDebug($convoArr){ //echo ">>>>".$convoArr['conversation']['debugmode']; - switch($convoArr['conversation']['debugmode']){ + switch($convoArr['conversation']['debugshow']){ case 0: //show in source code $log = str_replace("[NEWLINE]","\r\n",$log); display_on_page(0,$log); From 806f7c2caa2d0e626a4a0722cb3d351c4d2d5e96 Mon Sep 17 00:00:00 2001 From: Elizabeth Perreau Date: Sat, 9 Jun 2012 14:12:04 +0200 Subject: [PATCH 026/123] UNFIX Revert change it was not the cause so just switched back --- library/error_functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/error_functions.php b/library/error_functions.php index 12ace37..b61fd3b 100644 --- a/library/error_functions.php +++ b/library/error_functions.php @@ -135,7 +135,7 @@ function handleDebug($convoArr){ //echo ">>>>".$convoArr['conversation']['debugmode']; - switch($convoArr['conversation']['debugshow']){ + switch($convoArr['conversation']['debugmode']){ case 0: //show in source code $log = str_replace("[NEWLINE]","\r\n",$log); display_on_page(0,$log); From f11e580babb1b2b996b15c8acb96d7e35c1e4034 Mon Sep 17 00:00:00 2001 From: Elizabeth Perreau Date: Sat, 9 Jun 2012 14:46:50 +0200 Subject: [PATCH 027/123] FIX: Will now accept parenthesis in the template response https://github.com/Program-O/Program-O/issues/9 --- chatbot/core/aiml/replace_tomakesafe.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/chatbot/core/aiml/replace_tomakesafe.php b/chatbot/core/aiml/replace_tomakesafe.php index 14fddde..43f65f1 100644 --- a/chatbot/core/aiml/replace_tomakesafe.php +++ b/chatbot/core/aiml/replace_tomakesafe.php @@ -46,7 +46,9 @@ function entity_replace($whichway,$text){ ':-\)'=>'smiley_wink_7', ':\)'=>'smiley_wink_8', ':-\('=>'smiley_wink_9', - ':\)'=>'smiley_wink_10', + ':\)'=>'smiley_wink_10', + '('=>'open_bracket', + ')'=>'close_bracket', ' '=>'htmlentity_no-break_space', '¡'=>'htmlentity_inverted_exclamation_mark', '¢'=>'htmlentity_cent_sign', From 75ae2a368725afb3dc10c3f9b44aa3923087af48 Mon Sep 17 00:00:00 2001 From: Elizabeth Perreau Date: Sat, 9 Jun 2012 15:29:17 +0200 Subject: [PATCH 028/123] FIX: issue7 Random pickup line fix .. and changes resulting from the parenthesis fix --- .../core/aiml/buildingphp_code_functions.php | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/chatbot/core/aiml/buildingphp_code_functions.php b/chatbot/core/aiml/buildingphp_code_functions.php index db6b92a..0d84c41 100644 --- a/chatbot/core/aiml/buildingphp_code_functions.php +++ b/chatbot/core/aiml/buildingphp_code_functions.php @@ -38,20 +38,20 @@ function clean_for_eval($parsed_template,$count=0) $parsed_template= str_replace(";';","';",$parsed_template); $parsed_template= str_replace(";",";\r\n",$parsed_template); $parsed_template= str_replace("]';","];",$parsed_template); - $parsed_template= str_replace("). '.",').',$parsed_template); + $parsed_template= str_replace("close_bracket. '.",'close_bracket.',$parsed_template); $parsed_template= str_replace("',' '.","',",$parsed_template); $parsed_template= str_replace("',' '.","',",$parsed_template); - $parsed_template= str_replace(".'')",")",$parsed_template); - $parsed_template= str_replace(").''",")",$parsed_template); - $parsed_template= str_replace("). ')","))",$parsed_template); - $parsed_template= str_replace(".' ')",")",$parsed_template); + $parsed_template= str_replace(".''close_bracket","close_bracket",$parsed_template); + $parsed_template= str_replace("close_bracket.''","close_bracket",$parsed_template); + $parsed_template= str_replace("close_bracket. 'close_bracket","close_bracketclose_bracket",$parsed_template); + $parsed_template= str_replace(".' 'close_bracket","close_bracket",$parsed_template); $parsed_template= str_replace(".' .",".",$parsed_template); $parsed_template= str_replace("\r\n.","\$tmp_botsay .= ",$parsed_template); $parsed_template= str_replace("\r\n';","\r\n",$parsed_template); $parsed_template= str_replace("\r\n ';","\r\n",$parsed_template); $parsed_template= str_replace(">. call", ">'. call", $parsed_template); $parsed_template= str_replace("> . call", ">' . call", $parsed_template); - $parsed_template= str_replace(").';", ");", $parsed_template); + $parsed_template= str_replace("close_bracket.';", "close_bracket;", $parsed_template); $parsed_template= str_replace("''.", "", $parsed_template); $parsed_template= str_replace("\r\n'.call","\$tmp_botsay .= call",$parsed_template); $parsed_template= str_replace("\r\n '.call","\$tmp_botsay .= call",$parsed_template); @@ -59,12 +59,12 @@ function clean_for_eval($parsed_template,$count=0) $parsed_template= str_replace("\$condition = \"\"","\r\n\$condition=\"\"",$parsed_template); $parsed_template= str_replace("\"\";\$tmp_botsay","\"\";\r\n\$tmp_botsay",$parsed_template); $parsed_template= str_replace("\$tmp_botsay","\r\n\$tmp_botsay",$parsed_template); - $parsed_template= str_replace(").' ;",");",$parsed_template); + $parsed_template= str_replace("close_bracket.' ;","close_bracket;",$parsed_template); $parsed_template= str_replace('$tmp_botsay .= \' if','if',$parsed_template); $parsed_template= str_replace(".'. \r\n",".'. ';\r\n",$parsed_template); - $parsed_template= str_replace(").' \r\n", "); ", $parsed_template); - $parsed_template= str_replace(")';", ");", $parsed_template); - $parsed_template= str_replace(").\\\"';",").'\\\"';", $parsed_template); + $parsed_template= str_replace("close_bracket.' \r\n", "close_bracket; ", $parsed_template); + $parsed_template= str_replace("close_bracket';", "close_bracket;", $parsed_template); + $parsed_template= str_replace("close_bracket.\\\"';","close_bracket.'\\\"';", $parsed_template); $linebyline = explode("\r\n",$parsed_template); foreach($linebyline as $index => $value) From 7da2351261e00029fe34af9d9a62e884b128704e Mon Sep 17 00:00:00 2001 From: Elizabeth Perreau Date: Sat, 9 Jun 2012 16:28:47 +0200 Subject: [PATCH 029/123] FIX: srai was returning blank responses when an srai iteration had no results --- chatbot/core/aiml/find_aiml.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chatbot/core/aiml/find_aiml.php b/chatbot/core/aiml/find_aiml.php index a34981f..f91841d 100644 --- a/chatbot/core/aiml/find_aiml.php +++ b/chatbot/core/aiml/find_aiml.php @@ -130,7 +130,7 @@ function unset_all_bad_pattern_matches($allrows,$lookingfor,$current_thatpattern $aiml_thatpattern = $subrow['thatpattern']; //get the that - if($default_aiml_pattern==$aiml_pattern){ //if it is a direct match with our default pattern then add to tmp_rows + if(strtolower($default_aiml_pattern)==strtolower($aiml_pattern)){ //if it is a direct match with our default pattern then add to tmp_rows $tmp_rows[$i]=$subrow; $tmp_rows[$i]['score']=0; $i++; From 21fca73c8545c3950faad72c2767ddd98091a2d4 Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Sat, 9 Jun 2012 09:20:56 -0700 Subject: [PATCH 030/123] Multiple fixes Bugs: 1.) Bot select form: a.) Fixed debugging issues b.) Added ability to configure bot error response on a per-bot basis c.) Fixed a bug where the debugging email field was a checkbox, rather than text 2.) Upload form: a.) Removed "strict" validation. Now the AIML files only need to be well-formed, rather than fully valid XML b.) Revised the error messages to make them more easily understood Signed-off-by: Dave Morton --- admin/default.page.htm | 58 +- admin/select_bots.php | 26 +- admin/style.css | 6 +- admin/upload.php | 21 +- chatbot/conversation_start.php | 2 +- .../conversation/intialise_conversation.php | 709 +++++++++--------- config/global_config.tpl | 8 +- index.php | 111 ++- install/install_programo.php | 5 +- install/upgrade_bots_table.sql | 2 + 10 files changed, 478 insertions(+), 470 deletions(-) diff --git a/admin/default.page.htm b/admin/default.page.htm index 91a843a..d127b0c 100644 --- a/admin/default.page.htm +++ b/admin/default.page.htm @@ -176,8 +176,8 @@
    - - + + @@ -225,27 +225,26 @@ - + - + - + @@ -254,13 +253,13 @@
    - +
    - Yes - No +
    - +
    - - + - + + + + + - + - + diff --git a/admin/select_bots.php b/admin/select_bots.php index a07fab3..1dffffe 100644 --- a/admin/select_bots.php +++ b/admin/select_bots.php @@ -138,16 +138,16 @@ function getSelectedBot() { elseif($bot_format=="json") { $sel_json = ' selected="selected"'; } - if($use_aiml_code=="1") { + if($bot_use_aiml_code=="1") { $sel_fuyes = ' selected="selected"'; } - elseif($use_aiml_code=="0") { + elseif($bot_use_aiml_code=="0") { $sel_funo = ' selected="selected"'; } - if($update_aiml_code=="1") { + if($bot_update_aiml_code=="1") { $sel_fyes = ' selected="selected"'; } - elseif($update_aiml_code=="0") { + elseif($bot_update_aiml_code=="0") { $sel_fno = ' selected="selected"'; } if($bot_debugshow=="0") { @@ -179,14 +179,6 @@ function getSelectedBot() { } $action = "update"; } - $debugemail_0 = $debugemail_1 = ''; - switch ($bot_debugemail) { - case 0: - $debugemail_0 = 'checked="checked"'; - break; - case 1: - $debugemail_1 = 'checked="checked"'; - } mysql_close($dbconn); } else { @@ -202,8 +194,7 @@ function getSelectedBot() { $bot_conversation_lines = ""; $bot_remember_up_to = ""; $bot_debugemail = ""; - $debugemail_0 = ""; - $debugemail_1 = ""; + $debugemail = ""; $bot_debugshow = ""; $bot_debugmode = ""; $bot_default_aiml_pattern = ''; @@ -215,7 +206,7 @@ function getSelectedBot() { '[sel_html]','[sel_xml]','[sel_json]','[sel_session]','[sel_db]','[sel_fyes]', '[sel_fno]','[sel_fuyes]','[sel_funo]','[bot_conversation_lines]','[bot_remember_up_to]', '[bot_debugemail]','[dm_]','[dm_i]','[dm_ii]','[dm_iii]','[ds_]','[ds_i]','[ds_ii]', - '[ds_iii]','[action]','[debugemail_0]','[debugemail_1]', '[bot_default_aiml_pattern]', + '[ds_iii]','[action]', '[bot_default_aiml_pattern]', '[bot_error_response]', ); foreach ($searches as $search) { $replace = str_replace('[', '', $search); @@ -236,7 +227,6 @@ function updateBotSelection() { $value = mysql_escape_string(trim(stripslashes($value))); if(($key != "bot_id")&&($key != "action")&&($value!="")) { $sql = "UPDATE `bots` SET `$key` ='$value' where `bot_id` = '".$_POST['bot_id']."' limit 1; "; - $x = file_put_contents(_ADMIN_PATH_ . 'updateBotSQL.txt', $sql); $result = mysql_query($sql,$dbconn)or die('You have a SQL error on line '. __LINE__ . ' of ' . __FILE__ . '. Error message is: ' . mysql_error() . '.'); if(!$result) { $msg = 'Error updating bot details.'; @@ -263,8 +253,8 @@ function addBot() { } $dbconn = db_open(); $sql = <<', $aimlTagStart) + 1; $aimlFile = $validAIMLHeader . substr($aimlContent,$aimlTagEnd); +/* #die('
    '.htmlentities($aimlFile)."
    \n"); $saveFile = str_replace('./uploads',_ADMIN_PATH_.'aiml',$file); #if (!file_exists($saveFile)) file_put_contents("$saveFile", $aimlFile); $validate = new DOMDocument(); $validate->loadXML($aimlFile); + #$validate->loadXML($aimlContent); $validate->preserveWhiteSpace = false; $validate->formatOutput = true; if ($validate->validate() === false) { $msg = "Cannot parse file $file! Please note errors that follow:\n"; $errors = libxml_get_errors(); - foreach ($errors as $error) { - $msg .= libxml_display_error($error); - } - libxml_clear_errors(); - return $msg; + foreach ($errors as $error) { + $msg .= libxml_display_error($error); + } + libxml_clear_errors(); + return $msg; } +*/ + try { $aiml = new SimpleXMLElement($aimlFile); + #$aiml = new SimpleXMLElement($aimlContent); $rowCount = 0; if (!empty($aiml->topic)) { foreach ($aiml->topic as $topicXML) { # handle any topic tag(s) in the file @@ -205,7 +210,11 @@ function parseAIML ($file) { $sql = rtrim($sql, ",\n") . ';'; $success = (updateDB($sql) >=0) ? true : false; } - $msg = "There was a problem adding file $fileName to the database."; + } + catch(Exception $e) { + $success = false; + } + $msg = "There was a problem adding file $fileName to the database. Please validate the file and try again."; if ($success) $msg = "Successfully added $fileName to the database."; return $msg; } diff --git a/chatbot/conversation_start.php b/chatbot/conversation_start.php index 7ee8bc0..a433f6d 100644 --- a/chatbot/conversation_start.php +++ b/chatbot/conversation_start.php @@ -14,7 +14,7 @@ //chdir( dirname ( __FILE__ ) ); $thisFolder = dirname(realpath(__FILE__)) . DIRECTORY_SEPARATOR; $thisParentFolder = preg_replace('~[/\\\\][^/\\\\]*[/\\\\]$~', DIRECTORY_SEPARATOR, $thisFolder); - + require_once ($thisParentFolder.'config'.DIRECTORY_SEPARATOR.'global_config.php'); //load shared files include_once (_LIB_PATH_ . "db_functions.php"); diff --git a/chatbot/core/conversation/intialise_conversation.php b/chatbot/core/conversation/intialise_conversation.php index c8cd144..5f63c8f 100644 --- a/chatbot/core/conversation/intialise_conversation.php +++ b/chatbot/core/conversation/intialise_conversation.php @@ -21,28 +21,28 @@ * @return $convoArr (updated) **/ function intialise_convoArray($convoArr){ - //set the initial convoArr values - /*$convoArr['conversation']['convo_id']=$convo_id; - $convoArr['conversation']['bot_id']=$bot_id; - $convoArr['conversation']['format']=$format;*/ - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Intialising conversation",3); - - //load blank topics - $convoArr = load_blank_convoArray('topic',"",$convoArr); - //load blank thats - $convoArr = load_blank_convoArray('that',"",$convoArr); - //load blank stars - $convoArr = load_blank_convoArray('star',"",$convoArr); - //load blank stars - $convoArr = load_blank_convoArray('input',"",$convoArr); - //load blank stack - $convoArr = load_blank_stack($convoArr); - //load the new client defaults - $convoArr = load_new_client_defaults($convoArr); - - return $convoArr; -} + //set the initial convoArr values + /*$convoArr['conversation']['convo_id']=$convo_id; + $convoArr['conversation']['bot_id']=$bot_id; + $convoArr['conversation']['format']=$format;*/ + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Intialising conversation",3); + + //load blank topics + $convoArr = load_blank_convoArray('topic',"",$convoArr); + //load blank thats + $convoArr = load_blank_convoArray('that',"",$convoArr); + //load blank stars + $convoArr = load_blank_convoArray('star',"",$convoArr); + //load blank stars + $convoArr = load_blank_convoArray('input',"",$convoArr); + //load blank stack + $convoArr = load_blank_stack($convoArr); + //load the new client defaults + $convoArr = load_new_client_defaults($convoArr); + + return $convoArr; +} /** @@ -55,14 +55,14 @@ function intialise_convoArray($convoArr){ **/ function load_blank_convoArray($arrayIndex,$defaultValue,$convoArr) { - runDebug( __FILE__, __FUNCTION__, __LINE__, "Loading blank $arrayIndex array",3); - global $offset; //set in global config file - $remember_up_to = $convoArr['conversation']['remember_up_to']; - - for($i=1;$i<=($remember_up_to+$offset);$i++){ - $convoArr[$arrayIndex][$i]=$defaultValue; - } - return $convoArr; + runDebug( __FILE__, __FUNCTION__, __LINE__, "Loading blank $arrayIndex array",3); + global $offset; //set in global config file + $remember_up_to = $convoArr['conversation']['remember_up_to']; + + for($i=1;$i<=($remember_up_to+$offset);$i++){ + $convoArr[$arrayIndex][$i]=$defaultValue; + } + return $convoArr; } /** @@ -74,11 +74,11 @@ function load_blank_convoArray($arrayIndex,$defaultValue,$convoArr) * @return $convoArr (updated) **/ function load_blank_stack($convoArr){ - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Loading blank stack",3); - global $default_stack_value; //set in global config file - - $convoArr['stack']['top'] = $default_stack_value; + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Loading blank stack",3); + global $default_stack_value; //set in global config file + + $convoArr['stack']['top'] = $default_stack_value; $convoArr['stack']['second'] = $default_stack_value; $convoArr['stack']['third'] = $default_stack_value; $convoArr['stack']['fourth'] = $default_stack_value; @@ -86,8 +86,8 @@ function load_blank_stack($convoArr){ $convoArr['stack']['sixth'] = $default_stack_value; $convoArr['stack']['seventh'] = $default_stack_value; $convoArr['stack']['last'] = $default_stack_value; - - return $convoArr; + + return $convoArr; } /** @@ -97,19 +97,19 @@ function load_blank_stack($convoArr){ * @return $convoArr (updated) **/ function load_default_bot_values($convoArr){ - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Loading db bot personality properties",3); - global $con,$dbn; //set in global config file - - $sql = "SELECT * FROM `$dbn`.`botpersonality` WHERE `bot` = '".$convoArr['conversation']['bot_id']."'"; - runDebug( __FILE__, __FUNCTION__, __LINE__, "load db bot personality values SQL: $sql",2); - $result = db_query($sql,$con); - - while($row=mysql_fetch_array($result)){ - $convoArr['bot_properties'][$row['name']]=$row['value']; - } - - return $convoArr; + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Loading db bot personality properties",3); + global $con,$dbn; //set in global config file + + $sql = "SELECT * FROM `$dbn`.`botpersonality` WHERE `bot` = '".$convoArr['conversation']['bot_id']."'"; + runDebug( __FILE__, __FUNCTION__, __LINE__, "load db bot personality values SQL: $sql",2); + $result = db_query($sql,$con); + + while($row=mysql_fetch_array($result)){ + $convoArr['bot_properties'][$row['name']]=$row['value']; + } + + return $convoArr; } /** @@ -119,10 +119,10 @@ function load_default_bot_values($convoArr){ * @return $convoArr **/ function write_to_session($convoArr){ - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Saving to session",3); - $_SESSION['programo'] = $convoArr; - return $convoArr; + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Saving to session",3); + $_SESSION['programo'] = $convoArr; + return $convoArr; } /** @@ -131,13 +131,13 @@ function write_to_session($convoArr){ * @return $convoArr **/ function read_from_session(){ - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Reading from session",3); - $convoArr = array(); //initialise - if(isset($_SESSION['programo'])){ - $convoArr = $_SESSION['programo']; - } - return $convoArr; + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Reading from session",3); + $convoArr = array(); //initialise + if(isset($_SESSION['programo'])){ + $convoArr = $_SESSION['programo']; + } + return $convoArr; } /** @@ -148,18 +148,18 @@ function read_from_session(){ * @return $convoArr (updated) **/ function add_new_conversation_vars($say,$convoArr){ - - runDebug( __FILE__, __FUNCTION__, __LINE__, "New conversation vars",3); - - //$convoArr['conversation']['convo_id']=$convo_id; - //$convoArr['conversation']['bot_id']=$bot_id; - - //put what the user has said on the front of the 'user_say' and 'input' subarray with a minimum clean to prevent injection - $convoArr = push_on_front_convoArr("user_say",strip_tags($say),$convoArr); - $convoArr['aiml']['user_raw']=strip_tags($say); - $convoArr = push_on_front_convoArr('input',$convoArr['aiml']['user_raw'],$convoArr); - - return $convoArr; + + runDebug( __FILE__, __FUNCTION__, __LINE__, "New conversation vars",3); + + //$convoArr['conversation']['convo_id']=$convo_id; + //$convoArr['conversation']['bot_id']=$bot_id; + + //put what the user has said on the front of the 'user_say' and 'input' subarray with a minimum clean to prevent injection + $convoArr = push_on_front_convoArr("user_say",strip_tags($say),$convoArr); + $convoArr['aiml']['user_raw']=strip_tags($say); + $convoArr = push_on_front_convoArr('input',$convoArr['aiml']['user_raw'],$convoArr); + + return $convoArr; } /** @@ -169,12 +169,12 @@ function add_new_conversation_vars($say,$convoArr){ * @return $convoArr (updated) **/ function add_firstturn_conversation_vars($convoArr){ - - runDebug( __FILE__, __FUNCTION__, __LINE__, "First turn",3); - if(!isset($convoArr['bot_properties'])) { - $convoArr = load_default_bot_values($convoArr); - } - return $convoArr; + + runDebug( __FILE__, __FUNCTION__, __LINE__, "First turn",3); + if(!isset($convoArr['bot_properties'])) { + $convoArr = load_default_bot_values($convoArr); + } + return $convoArr; } @@ -189,95 +189,95 @@ function add_firstturn_conversation_vars($convoArr){ **/ function push_on_front_convoArr($arrayIndex,$value,$convoArr) { - global $offset; - runDebug( __FILE__, __FUNCTION__, __LINE__, "Push on to front of $arrayIndex array",1); - $remember_up_to = $convoArr['conversation']['remember_up_to']; - - //these subarray indexes are 2d - $two_d_arrays = array("that","that_raw"); - - $arrayIndex=trim($arrayIndex); - - //mini clean - $value=trim($value); - $value = preg_replace('/\s\s+/', ' ', $value); - $value = preg_replace('/\s\./', '.', $value); - - //there is a chance the subarray has not been set yet so check and if not set here - if(!isset($convoArr[$arrayIndex][$offset])) { - $convoArr[$arrayIndex]=array(); - $convoArr = load_blank_convoArray($arrayIndex,"",$convoArr); - } - - - //if the subarray is itself an array check it here - if (in_array($arrayIndex, $two_d_arrays)) - { - $matches = preg_match_all("# ?(([^\.\?!]*)+(?:[\.\?!]|(?:
    ))*)#ui",$value,$sentances); - $cmatch = 0; - - //do another check to make sure the array is not just full of blanks - foreach($sentances as $temp){ - foreach($temp as $chk){ - if(trim($chk)!=""){ - $cmatch++; - } - } - } - - //if there definately is something in the sentance array build the temp sentance array - if(($cmatch>0)&&($matches!==FALSE)){ - foreach($sentances[1] as $index => $value){ - if($arrayIndex=="that"){ - $t = clean_that($value); - if($t!=""){ - $tmp_sentance[] = $t; - } - } - else{ - $tmp_sentance[] = $value; - } - } - - //reverse the array and store - $sentances = array(); - $sentances = array_reverse($tmp_sentance); - } - else{ - $sentances = array(); - if($arrayIndex=="that"){ - $sentances[0] = clean_that($value); - } - else{ - $sentances[0] = $value; - } - } - - //make a space so that [0] is null (in accordance with the AIML array offset) - array_unshift($sentances,NULL); - unset($sentances[0]); - //push this onto the subarray and then clear [0] element (in accordance with the AIML array offset) - array_unshift($convoArr[$arrayIndex],$sentances); - array_unshift($convoArr[$arrayIndex],null); - unset($convoArr[$arrayIndex][0]); - - }else{ - array_unshift($convoArr[$arrayIndex],$value); - array_unshift($convoArr[$arrayIndex],NULL); - } - - for($i=$remember_up_to+1;$i<=count($convoArr[$arrayIndex]);$i++){ - if(isset($convoArr[$arrayIndex][$i])){ - unset($convoArr[$arrayIndex][$i]); - } - } - unset($convoArr[$arrayIndex][0]); - - if($arrayIndex=="topic"){ - push_stack($convoArr,$value); - } - - return $convoArr; + global $offset; + runDebug( __FILE__, __FUNCTION__, __LINE__, "Push on to front of $arrayIndex array",1); + $remember_up_to = $convoArr['conversation']['remember_up_to']; + + //these subarray indexes are 2d + $two_d_arrays = array("that","that_raw"); + + $arrayIndex=trim($arrayIndex); + + //mini clean + $value=trim($value); + $value = preg_replace('/\s\s+/', ' ', $value); + $value = preg_replace('/\s\./', '.', $value); + + //there is a chance the subarray has not been set yet so check and if not set here + if(!isset($convoArr[$arrayIndex][$offset])) { + $convoArr[$arrayIndex]=array(); + $convoArr = load_blank_convoArray($arrayIndex,"",$convoArr); + } + + + //if the subarray is itself an array check it here + if (in_array($arrayIndex, $two_d_arrays)) + { + $matches = preg_match_all("# ?(([^\.\?!]*)+(?:[\.\?!]|(?:
    ))*)#ui",$value,$sentances); + $cmatch = 0; + + //do another check to make sure the array is not just full of blanks + foreach($sentances as $temp){ + foreach($temp as $chk){ + if(trim($chk)!=""){ + $cmatch++; + } + } + } + + //if there definately is something in the sentance array build the temp sentance array + if(($cmatch>0)&&($matches!==FALSE)){ + foreach($sentances[1] as $index => $value){ + if($arrayIndex=="that"){ + $t = clean_that($value); + if($t!=""){ + $tmp_sentance[] = $t; + } + } + else{ + $tmp_sentance[] = $value; + } + } + + //reverse the array and store + $sentances = array(); + $sentances = array_reverse($tmp_sentance); + } + else{ + $sentances = array(); + if($arrayIndex=="that"){ + $sentances[0] = clean_that($value); + } + else{ + $sentances[0] = $value; + } + } + + //make a space so that [0] is null (in accordance with the AIML array offset) + array_unshift($sentances,NULL); + unset($sentances[0]); + //push this onto the subarray and then clear [0] element (in accordance with the AIML array offset) + array_unshift($convoArr[$arrayIndex],$sentances); + array_unshift($convoArr[$arrayIndex],null); + unset($convoArr[$arrayIndex][0]); + + }else{ + array_unshift($convoArr[$arrayIndex],$value); + array_unshift($convoArr[$arrayIndex],NULL); + } + + for($i=$remember_up_to+1;$i<=count($convoArr[$arrayIndex]);$i++){ + if(isset($convoArr[$arrayIndex][$i])){ + unset($convoArr[$arrayIndex][$i]); + } + } + unset($convoArr[$arrayIndex][0]); + + if($arrayIndex=="topic"){ + push_stack($convoArr,$value); + } + + return $convoArr; } /** @@ -287,59 +287,60 @@ function push_on_front_convoArr($arrayIndex,$value,$convoArr) * @return $convoArr (updated) **/ function load_bot_config($convoArr){ - - global $con,$dbn, $default_format,$default_pattern,$default_update_aiml_code,$default_conversation_lines,$default_remember_up_to,$default_debugemail,$default_debugshow,$default_debugmode,$default_save_state; - - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Getting bot config from DB",1); - - //get the values from the db - $sql = "SELECT * FROM `$dbn`.`bots` WHERE bot_id = '".$convoArr['conversation']['bot_id']."'"; - - runDebug( __FILE__, __FUNCTION__, __LINE__, "load bot config SQL: $sql",2); - - $result = db_query($sql,$con) or die('You have a SQL error on line '. __LINE__ . ' of ' . __FILE__ . '. Error message is: ' . mysql_error() . ".
    \nSQL = $sql
    \n"); - - if(($result)&&(db_res_count($result)>0)){ - while($row=mysql_fetch_array($result)){ - //$convoArr['conversation']['format']=$row['format']; //set in the form - $convoArr['conversation']['use_aiml_code']=$row['use_aiml_code']; - $convoArr['conversation']['update_aiml_code']=$row['update_aiml_code']; - $convoArr['conversation']['conversation_lines']=$row['conversation_lines']; - $convoArr['conversation']['remember_up_to']=$row['remember_up_to']; - $convoArr['conversation']['debugemail']=$row['debugemail']; - $convoArr['conversation']['debugshow']=$row['debugshow']; - $convoArr['conversation']['debugmode']=$row['debugmode']; - $convoArr['conversation']['save_state']=$row['save_state']; - $convoArr['conversation']['default_aiml_pattern']=$row['default_aiml_pattern']; - $convoArr['conversation']['bot_parent_id']=$row['bot_parent_id']; - } - }else{ - //$convoArr['conversation']['format']=$default_format; //set in the form - $convoArr['conversation']['use_aiml_code']=$default_use_aiml_code; - $convoArr['conversation']['update_aiml_code']=$default_update_aiml_code; - $convoArr['conversation']['conversation_lines']=$default_conversation_lines; - $convoArr['conversation']['remember_up_to']=$default_remember_up_to; - $convoArr['conversation']['debugemail']=$default_debugemail; - $convoArr['conversation']['debugshow']=$default_debugshow; - $convoArr['conversation']['debugmode']=$default_debugmode; - $convoArr['conversation']['save_state']=$default_save_state; - $convoArr['conversation']['default_aiml_pattern']=$default_pattern; - $convoArr['conversation']['bot_parent_id']=0; - } - - //if return format is not html overide the debug type - - if($convoArr['conversation']['format']!="html") - { - $convoArr['conversation']['debugmode']=1; - } - - - - return $convoArr; + global $con,$dbn, $default_format,$default_pattern,$default_update_aiml_code,$default_conversation_lines,$default_remember_up_to,$default_debugemail,$default_debugshow,$default_debugmode,$default_save_state, $error_response; + + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Getting bot config from DB",1); + + //get the values from the db + $sql = "SELECT * FROM `$dbn`.`bots` WHERE bot_id = '".$convoArr['conversation']['bot_id']."'"; + + runDebug( __FILE__, __FUNCTION__, __LINE__, "load bot config SQL: $sql",2); + + $result = db_query($sql,$con) or die('You have a SQL error on line '. __LINE__ . ' of ' . __FILE__ . '. Error message is: ' . mysql_error() . ".
    \nSQL = $sql
    \n"); + + #if(($result)&&(db_res_count($result)>0)){ + if($result !== false and (mysql_num_rows($result) > 0)){ + while($row=mysql_fetch_array($result)){ + //$convoArr['conversation']['format']=$row['format']; //set in the form + $convoArr['conversation']['use_aiml_code']=$row['use_aiml_code']; + $convoArr['conversation']['update_aiml_code']=$row['update_aiml_code']; + $convoArr['conversation']['conversation_lines']=$row['conversation_lines']; + $convoArr['conversation']['remember_up_to']=$row['remember_up_to']; + $convoArr['conversation']['debugemail']=$row['debugemail']; + $convoArr['conversation']['debugshow']=$row['debugshow']; + $convoArr['conversation']['debugmode']=$row['debugmode']; + $convoArr['conversation']['save_state']=$row['save_state']; + $convoArr['conversation']['default_aiml_pattern']=$row['default_aiml_pattern']; + $convoArr['conversation']['bot_parent_id']=$row['bot_parent_id']; + $error_response = $row['error_response']; + } + }else{ + //$convoArr['conversation']['format']=$default_format; //set in the form + $convoArr['conversation']['use_aiml_code']=$default_use_aiml_code; + $convoArr['conversation']['update_aiml_code']=$default_update_aiml_code; + $convoArr['conversation']['conversation_lines']=$default_conversation_lines; + $convoArr['conversation']['remember_up_to']=$default_remember_up_to; + $convoArr['conversation']['debugemail']=$default_debugemail; + $convoArr['conversation']['debugshow']=$default_debugshow; + $convoArr['conversation']['debugmode']=$default_debugmode; + $convoArr['conversation']['save_state']=$default_save_state; + $convoArr['conversation']['default_aiml_pattern']=$default_pattern; + $convoArr['conversation']['bot_parent_id']=0; + } + + //if return format is not html overide the debug type + + if($convoArr['conversation']['format']!="html") + { + $convoArr['conversation']['debugmode']=1; + } + + + + return $convoArr; } - + /** * function log_conversation(() @@ -348,92 +349,92 @@ function load_bot_config($convoArr){ * @return $convoArr (updated) **/ function log_conversation($convoArr){ - - //db globals - global $con,$dbn; - - runDebug( __FILE__, __FUNCTION__, __LINE__, "logging the conversation turn in the db",3); - - //clean and set + + //db globals + global $con,$dbn; + + runDebug( __FILE__, __FUNCTION__, __LINE__, "logging the conversation turn in the db",3); + + //clean and set $usersay = mysql_escape_string($convoArr['aiml']['user_raw']); $botsay = mysql_escape_string($convoArr['aiml']['parsed_template']); $user_id = $convoArr['conversation']['user_id']; $bot_id = $convoArr['conversation']['bot_id']; - - $sql = "INSERT INTO `$dbn`.`conversation_log` ( - `id` , - `input` , - `response` , - `userid` , - `bot_id` , - `timestamp` - ) - VALUES ( - NULL , '$usersay', '$botsay', '$user_id', '$bot_id', - CURRENT_TIMESTAMP - )"; - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Saving conservation SQL: $sql",2); - db_query($sql,$con); - - return $convoArr; + + $sql = "INSERT INTO `$dbn`.`conversation_log` ( + `id` , + `input` , + `response` , + `userid` , + `bot_id` , + `timestamp` + ) + VALUES ( + NULL , '$usersay', '$botsay', '$user_id', '$bot_id', + CURRENT_TIMESTAMP + )"; + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Saving conservation SQL: $sql",2); + db_query($sql,$con); + + return $convoArr; } - + /** * function log_conversation_state(() * A function to log the conversation * @param array $convoArr - the current state of the conversation array * @return $convoArr (updated) -**/ +**/ function log_conversation_state($convoArr){ - - global $con,$dbn; - //get undefined defaults from the db + + global $con,$dbn; + //get undefined defaults from the db - runDebug( __FILE__, __FUNCTION__, __LINE__, "logging state",3); - + runDebug( __FILE__, __FUNCTION__, __LINE__, "logging state",3); + $serialise_convo = mysql_escape_String(serialize($convoArr)); $user_id = $convoArr['conversation']['user_id']; $bot_id = $convoArr['conversation']['bot_id']; - - $sql = "UPDATE `$dbn`.`users` - SET - `state` = '$serialise_convo', - `last_update` = NOW(), - `chatlines` = `chatlines`+1 - WHERE `id` = '$user_id' LIMIT 1"; - - runDebug( __FILE__, __FUNCTION__, __LINE__, "updating conversation state SQL: $sql",2); - db_query($sql,$con); - - return $convoArr; -} + + $sql = "UPDATE `$dbn`.`users` + SET + `state` = '$serialise_convo', + `last_update` = NOW(), + `chatlines` = `chatlines`+1 + WHERE `id` = '$user_id' LIMIT 1"; + + runDebug( __FILE__, __FUNCTION__, __LINE__, "updating conversation state SQL: $sql",2); + db_query($sql,$con); + + return $convoArr; +} /** * function get_conversation_state(() * A function to log the conversation * @param array $convoArr - the current state of the conversation array * @return $convoArr (updated) -**/ +**/ function get_conversation_state($convoArr){ - global $con,$dbn; - runDebug( __FILE__, __FUNCTION__, __LINE__, "getting state",3); - //get converstation state from the db - $serialise_convo = mysql_escape_string(serialize($convoArr)); + global $con,$dbn; + runDebug( __FILE__, __FUNCTION__, __LINE__, "getting state",3); + //get converstation state from the db + $serialise_convo = mysql_escape_string(serialize($convoArr)); $user_id = $convoArr['conversation']['user_id']; - $sql = "SELECT * FROM `$dbn`.`users` WHERE `id` = '$user_id' LIMIT 1"; - runDebug( __FILE__, __FUNCTION__, __LINE__, "Getting conversation state SQL: $sql",2); - $result = db_query($sql,$con); - - if(($result)&&(mysql_num_rows($result)>0)){ - $row=mysql_fetch_array($result); - $convoArr = unserialize($row['state']); - } - - return $convoArr; + $sql = "SELECT * FROM `$dbn`.`users` WHERE `id` = '$user_id' LIMIT 1"; + runDebug( __FILE__, __FUNCTION__, __LINE__, "Getting conversation state SQL: $sql",2); + $result = db_query($sql,$con); + + if(($result)&&(mysql_num_rows($result)>0)){ + $row=mysql_fetch_array($result); + $convoArr = unserialize($row['state']); + } + + return $convoArr; } /** @@ -441,36 +442,38 @@ function get_conversation_state($convoArr){ * A function to check and set the bot id * @param array $convoArr - the current state of the conversation array * @return $convoArr (updated) -**/ +**/ function check_set_bot($convoArr) { - global $con,$dbn, $default_bot_id; - //check to see if bot_id has been passed if not load default - if((isset($_REQUEST['bot_id'])) && (trim($_REQUEST['bot_id'])!="")) { - $bot_id=trim($_REQUEST['bot_id']); - } - elseif(isset($convoArr['conversation']['bot_id'])) { - $bot_id = $convoArr['conversation']['bot_id']; - }else{ - $bot_id = $default_bot_id; - } - - //get the values from the db - $sql = "SELECT * FROM `$dbn`.`bots` WHERE bot_id = '$bot_id' and `bot_active`='1'"; - runDebug( __FILE__, __FUNCTION__, __LINE__, "Checking bot exists SQL: $sql",2); - $result = db_query($sql,$con); - if(($result)&&(db_res_count($result)>0)){ - $row = mysql_fetch_assoc($result); - $bot_name = $row['bot_name']; - $convoArr['conversation']['bot_name']=$bot_name; - $convoArr['conversation']['bot_id']=$bot_id; - runDebug( __FILE__, __FUNCTION__, __LINE__, "BOT ID: $bot_id",1); - }else{ - $convoArr['debug']['intialisation_error']="Bot ID: $bot_id does not exist"; - $convoArr['conversation']['bot_id']=$bot_id; - runDebug( __FILE__, __FUNCTION__, __LINE__, "ERROR - Cannot find bot id: $bot_id",1); - } - return $convoArr; + global $con,$dbn, $default_bot_id, $error_response; + //check to see if bot_id has been passed if not load default + if((isset($_REQUEST['bot_id'])) && (trim($_REQUEST['bot_id'])!="")) { + $bot_id=trim($_REQUEST['bot_id']); + } + elseif(isset($convoArr['conversation']['bot_id'])) { + $bot_id = $convoArr['conversation']['bot_id']; + }else{ + $bot_id = $default_bot_id; + } + + //get the values from the db + $sql = "SELECT * FROM `$dbn`.`bots` WHERE bot_id = '$bot_id' and `bot_active`='1'"; + runDebug( __FILE__, __FUNCTION__, __LINE__, "Checking bot exists SQL: $sql",2); + $result = db_query($sql,$con); + if(($result)&&(db_res_count($result)>0)){ + $row = mysql_fetch_assoc($result); + $bot_name = $row['bot_name']; + $error_response = $row['error_response']; + $convoArr['conversation']['bot_name']=$bot_name; + $convoArr['conversation']['bot_id']=$bot_id; + runDebug( __FILE__, __FUNCTION__, __LINE__, "BOT ID: $bot_id",1); + } + else{ + $convoArr['debug']['intialisation_error']="Bot ID: $bot_id does not exist"; + $convoArr['conversation']['bot_id']=$bot_id; + runDebug( __FILE__, __FUNCTION__, __LINE__, "ERROR - Cannot find bot id: $bot_id",1); + } + return $convoArr; } /** @@ -481,17 +484,17 @@ function check_set_bot($convoArr) **/ function check_set_convo_id($convoArr) { - global $default_convo_id; - //check to see if convo_id has been passed if not load default - if((isset($_REQUEST['convo_id'])) && (trim($_REQUEST['convo_id'])!="")) { - $convo_id=trim($_REQUEST['convo_id']); - runDebug( __FILE__, __FUNCTION__, __LINE__, "CONVO ID: $convo_id",1); - }else { - $convo_id = $default_convo_id; - runDebug( __FILE__, __FUNCTION__, __LINE__, "ERROR - Cannot find CONVO ID using default: $convo_id",1); - } - $convoArr['conversation']['convo_id']=$convo_id; - return $convoArr; + global $default_convo_id; + //check to see if convo_id has been passed if not load default + if((isset($_REQUEST['convo_id'])) && (trim($_REQUEST['convo_id'])!="")) { + $convo_id=trim($_REQUEST['convo_id']); + runDebug( __FILE__, __FUNCTION__, __LINE__, "CONVO ID: $convo_id",1); + }else { + $convo_id = $default_convo_id; + runDebug( __FILE__, __FUNCTION__, __LINE__, "ERROR - Cannot find CONVO ID using default: $convo_id",1); + } + $convoArr['conversation']['convo_id']=$convo_id; + return $convoArr; } /** @@ -502,8 +505,8 @@ function check_set_convo_id($convoArr) **/ function check_set_user($convoArr) { - global $default_convo_id,$con,$dbn, $unknown_user; - //check to see if user_name has been set if not set as default + global $default_convo_id,$con,$dbn, $unknown_user; + //check to see if user_name has been set if not set as default $convo_id = (isset($convoArr['conversation']['convo_id'])) ? $convoArr['conversation']['convo_id'] : session_id(); $bot_id = $convoArr['conversation']['bot_id']; $ip = $_SERVER['REMOTE_ADDR']; @@ -518,9 +521,9 @@ function check_set_user($convoArr) $user_id = (!empty($row['id'])) ? $row['id'] : 0; $user_name = (!empty($row['name'])) ? $row['name'] : 'User'; } - $convoArr['conversation']['user_name'] = (!empty($user_name)) ? $user_name : $unknown_user; + $convoArr['conversation']['user_name'] = (!empty($user_name)) ? $user_name : $unknown_user; #die("User name = $user_name
    \n"); - return $convoArr; + return $convoArr; } @@ -531,36 +534,36 @@ function check_set_user($convoArr) * @return $convoArr (updated) **/ function check_set_format($convoArr){ - - global $default_format; - - $formatsArr = array('html','xml','json'); - - //at thsi point we can overwrite the conversation format. - if((isset($_REQUEST['format'])) && (trim($_REQUEST['format'])!="")) - { - $format = trim($_REQUEST['format']); - } - else - { - $format = $default_format; - } - - $convoArr['conversation']['format'] = strtolower($format); - - if(!in_array($convoArr['conversation']['format'],$formatsArr)) - { - $convoArr['debug']['intialisation_error']="Incompatible return type: $format"; - runDebug( __FILE__, __FUNCTION__, __LINE__, "ERROR - bad return type: $format",1); - } - else - { - runDebug( __FILE__, __FUNCTION__, __LINE__, "Using format: $format",1); - } - - - return $convoArr; + + global $default_format; + + $formatsArr = array('html','xml','json'); + + //at thsi point we can overwrite the conversation format. + if((isset($_REQUEST['format'])) && (trim($_REQUEST['format'])!="")) + { + $format = trim($_REQUEST['format']); + } + else + { + $format = $default_format; + } + + $convoArr['conversation']['format'] = strtolower($format); + + if(!in_array($convoArr['conversation']['format'],$formatsArr)) + { + $convoArr['debug']['intialisation_error']="Incompatible return type: $format"; + runDebug( __FILE__, __FUNCTION__, __LINE__, "ERROR - bad return type: $format",1); + } + else + { + runDebug( __FILE__, __FUNCTION__, __LINE__, "Using format: $format",1); + } + + + return $convoArr; } -?> \ No newline at end of file +?> \ No newline at end of file diff --git a/config/global_config.tpl b/config/global_config.tpl index 22a6ea7..27cc0a4 100644 --- a/config/global_config.tpl +++ b/config/global_config.tpl @@ -67,11 +67,11 @@ $dbn = "[dbn]"; # dev database name/prefix $dbu = "[dbu]"; # dev database username $dbp = "[dbp]"; # dev database password - + //these are the admin DB settings in case you want make the admin a different db user with more privs $adm_dbu = "[adm_dbu]"; $adm_dbp = "[adm_dbp]"; - + //------------------------------------------------------------------------ // Default bot settings //------------------------------------------------------------------------ @@ -98,7 +98,7 @@ * 3=everything */ $default_debugshow = [default_debugshow]; - + /* * $default_debugmode - How to show the debug data * 0 = source code view - show debugging in source code @@ -126,7 +126,7 @@ //0 - Do not show anything //1 - will print out to screen immediately $quickdebug = 0; - + //for quick debug //1 = will write debug data to file regardless of the bot config choice //it will write it as soon as it becomes available but this this will be finally diff --git a/index.php b/index.php index bb5c256..bf2c425 100644 --- a/index.php +++ b/index.php @@ -1,65 +1,60 @@ " . print_r($_REQUEST, true) . "
    \n"); - if (isset ($_REQUEST['bot_id'])) { - $bot_id = $_REQUEST['bot_id']; - } - else { - $bot_id = $default_bot_id; - } - if (isset ($_REQUEST['convo_id'])) { - $convo_id = $_REQUEST['convo_id']; - } - else { - $convo_id = $default_convo_id; - } - if (isset ($_REQUEST['format'])) { - $format = $_REQUEST['format']; - } - else { - $format = $default_format; - } - $output = (isset ($convoArr['send_to_user'])) ? $convoArr['send_to_user'] . '
    ' : "Hi there! Please tell me your name."; - $thisScript = $_SERVER['SCRIPT_NAME'] . '#new'; - $content = << +if(isset($_REQUEST['format'])){ + $format = $_REQUEST['format']; +}else{ + $format = "html"; +} +?> + + - - - - - - Program O Test Bot - - - - -
    -

    - - - - - - -

    - -
    $output 
    - + + + + + + Program O AIML PHP Chatbot + + + + + +
    +

    + + + + + + +

    + + -endPage; -print $content; -ob_end_flush(); -?> \ No newline at end of file diff --git a/install/install_programo.php b/install/install_programo.php index 8375325..a4895c2 100644 --- a/install/install_programo.php +++ b/install/install_programo.php @@ -112,7 +112,7 @@ function Save() { $sql = 'select `php_code` from `aiml` where 1 limit 1'; $result = mysql_query($sql,$conn) or upgrade($conn); $sql_template = << Date: Mon, 11 Jun 2012 21:23:47 -0700 Subject: [PATCH 031/123] Correct a typo in index.php that prevented the install script from running if the config file was missing. --- index.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/index.php b/index.php index bf2c425..aef1fe6 100644 --- a/index.php +++ b/index.php @@ -4,14 +4,15 @@ ini_set('log_errors', true); ini_set('error_log', './error.log'); -if($_REQUEST) -{ - include("./chatbot/conversation_start.php"); -} -else -{ - session_start(); - $display = ""; +require_once("./chatbot/conversation_start.php"); + +session_start(); +$display = ""; + +if(isset($_REQUEST['bot_id'])){ + $bot_id = $_REQUEST['bot_id']; +}else{ + $bot_id = 1; } if(isset($_REQUEST['bot_id'])){ From ae94c3ce888fa7a43a9bd79573966091666cb591 Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Mon, 11 Jun 2012 21:25:50 -0700 Subject: [PATCH 032/123] Corrected the pathing issue that caused the script to look for install_programo.php in the wrong folder. --- chatbot/conversation_start.php | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/chatbot/conversation_start.php b/chatbot/conversation_start.php index a433f6d..c28b252 100644 --- a/chatbot/conversation_start.php +++ b/chatbot/conversation_start.php @@ -11,10 +11,25 @@ ***************************************/ session_start(); $time_start = microtime(true); - //chdir( dirname ( __FILE__ ) ); + $docRoot = $_SERVER['DOCUMENT_ROOT']; + $docRoot = str_replace('/', DIRECTORY_SEPARATOR, $docRoot); $thisFolder = dirname(realpath(__FILE__)) . DIRECTORY_SEPARATOR; - $thisParentFolder = preg_replace('~[/\\\\][^/\\\\]*[/\\\\]$~', DIRECTORY_SEPARATOR, $thisFolder); - require_once ($thisParentFolder.'config'.DIRECTORY_SEPARATOR.'global_config.php'); + $baseFolder = str_ireplace('chatbot'.DIRECTORY_SEPARATOR, '', $thisFolder); + $relPath = str_ireplace(array($docRoot, DIRECTORY_SEPARATOR), array('', '/'), $baseFolder); + $configFile = $baseFolder . 'config' . DIRECTORY_SEPARATOR . 'global_config.php'; + $headerURL = 'http://' . $_SERVER["HTTP_HOST"] . $relPath . 'install/install_programo.php'; + + $debugString = " +Document Root = $docRoot
    +This folder = $thisFolder
    +Relative path = $relPath
    +Base folder = $baseFolder
    +Config file = $configFile
    +Header URL = $headerURL"; + #die($debugString); + + if (!file_exists($configFile)) header("location: $headerURL"); // Gives the full URL to the install script + require_once($configFile); //load shared files include_once (_LIB_PATH_ . "db_functions.php"); From b63cdf8b0d935ca2d8094dfea06a116f5d89adf1 Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Mon, 11 Jun 2012 21:35:15 -0700 Subject: [PATCH 033/123] Corrected a typo in the install script that broke a SQL query, preventing the script from populating the bots table --- install/install_programo.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/install/install_programo.php b/install/install_programo.php index a4895c2..5483c71 100644 --- a/install/install_programo.php +++ b/install/install_programo.php @@ -111,9 +111,10 @@ function Save() { } $sql = 'select `php_code` from `aiml` where 1 limit 1'; $result = mysql_query($sql,$conn) or upgrade($conn); - $sql_template = << From 7d474fd19e072d176324ba51230cf8d4fd59bf74 Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Mon, 11 Jun 2012 21:37:13 -0700 Subject: [PATCH 034/123] Corrected a labeling problem that prevented the install script from inserting the correct information into the bots table. --- install/install.tpl.htm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/install.tpl.htm b/install/install.tpl.htm index 8c29f5d..5c6fd62 100644 --- a/install/install.tpl.htm +++ b/install/install.tpl.htm @@ -132,8 +132,8 @@
    - - + + From 0ba1ac451506e3c52815908e68c6a05771ff36b8 Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Mon, 11 Jun 2012 21:38:41 -0700 Subject: [PATCH 035/123] Replaced the old pathing function with a newer, improved version that's more accurate and efficient. --- gui/jquery/index.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/gui/jquery/index.php b/gui/jquery/index.php index df1e62a..2e3bcb2 100644 --- a/gui/jquery/index.php +++ b/gui/jquery/index.php @@ -3,6 +3,27 @@ $bot_id = 1; $format = "json"; $convo_id = session_id(); + + $docRoot = $_SERVER['DOCUMENT_ROOT']; + $docRoot = str_replace('/', DIRECTORY_SEPARATOR, $docRoot); + $thisFolder = dirname(realpath(__FILE__)) . DIRECTORY_SEPARATOR; + $baseFolder = str_ireplace('gui'.DIRECTORY_SEPARATOR.'jquery'.DIRECTORY_SEPARATOR, '', $thisFolder); + $relPath = str_ireplace(array($docRoot, DIRECTORY_SEPARATOR), array('', '/'), $baseFolder); + $configFile = $baseFolder . 'config' . DIRECTORY_SEPARATOR . 'global_config.php'; + $headerURL = 'http://' . $_SERVER["HTTP_HOST"] . $relPath . 'install/install_programo.php'; + + $debugString = " +Document Root = $docRoot
    +This folder = $thisFolder
    +Relative path = $relPath
    +Base folder = $baseFolder
    +Config file = $configFile
    +Header URL = $headerURL"; + #die($debugString); + + if (!file_exists($configFile)) header("location: $headerURL"); // Gives the full URL to the install script + require_once($configFile); + ?> From 675edde1b2541701012ddb3358315653397f4816 Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Mon, 11 Jun 2012 21:40:23 -0700 Subject: [PATCH 036/123] replaced the old pathing code with newer, improved code that is cleaner and more accurate. --- gui/plain/index.php | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/gui/plain/index.php b/gui/plain/index.php index fb2ff69..7af5f4d 100644 --- a/gui/plain/index.php +++ b/gui/plain/index.php @@ -1,13 +1,26 @@ +This folder = $thisFolder
    +Relative path = $relPath
    +Base folder = $baseFolder
    +Config file = $configFile
    +Header URL = $headerURL"; + #die($debugString); + + if (!file_exists($configFile)) header("location: $headerURL"); // Gives the full URL to the install script + require_once($configFile); + session_start(); $display = ""; -} if(isset($_REQUEST['bot_id'])){ $bot_id = $_REQUEST['bot_id']; From 9604ea94b3f2e037218a5c6e409699fa278f6b29 Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Mon, 11 Jun 2012 21:41:36 -0700 Subject: [PATCH 037/123] Replaced the pathing code with code that's cleaner and more accurate. --- gui/xml/index.php | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/gui/xml/index.php b/gui/xml/index.php index e41dc7e..0cc0c61 100644 --- a/gui/xml/index.php +++ b/gui/xml/index.php @@ -10,11 +10,30 @@ XML interface ***************************************/ - if (!file_exists('../config/global_config.php')) header('location: ../install/install_programo.php'); - require_once('../config/global_config.php'); + + $docRoot = $_SERVER['DOCUMENT_ROOT']; + $docRoot = str_replace('/', DIRECTORY_SEPARATOR, $docRoot); + $thisFolder = dirname(realpath(__FILE__)) . DIRECTORY_SEPARATOR; + $baseFolder = str_ireplace('gui'.DIRECTORY_SEPARATOR.'xml'.DIRECTORY_SEPARATOR, '', $thisFolder); + $relPath = str_ireplace(array($docRoot, DIRECTORY_SEPARATOR), array('', '/'), $baseFolder); + $configFile = $baseFolder . 'config' . DIRECTORY_SEPARATOR . 'global_config.php'; + $headerURL = 'http://' . $_SERVER["HTTP_HOST"] . $relPath . 'install/install_programo.php'; + + $debugString = " +Document Root = $docRoot
    +This folder = $thisFolder
    +Relative path = $relPath
    +Base folder = $baseFolder
    +Config file = $configFile
    +Header URL = $headerURL"; + #die($debugString); + + if (!file_exists($configFile)) header("location: $headerURL"); // Gives the full URL to the install script + require_once($configFile); $response = ''; session_start(); + function get_response($path){ $ch = curl_init(); curl_setopt($ch, CURLOPT_URL,$path); From 0cf6cb4338455f3915ef761671651dd39daca248 Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Mon, 11 Jun 2012 21:52:47 -0700 Subject: [PATCH 038/123] added error_response field to the bots table. --- install/new.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/install/new.sql b/install/new.sql index fcdaa07..71e6277 100644 --- a/install/new.sql +++ b/install/new.sql @@ -26,6 +26,7 @@ CREATE TABLE IF NOT EXISTS `bots` ( debugemail int(11) NOT NULL, debugshow int(11) NOT NULL DEFAULT '1', debugmode int(11) NOT NULL DEFAULT '1', + error_response text not null, default_aiml_pattern varchar(255) NOT NULL DEFAULT 'RANDOM PICKUP LINE', PRIMARY KEY (bot_id) ); From 7d5239dfd9029e4096297409fc826480f7526d44 Mon Sep 17 00:00:00 2001 From: hannon235 Date: Tue, 12 Jun 2012 15:25:51 -0400 Subject: [PATCH 039/123] CHannon: fix issue with sql fields out of order in intall script: changed INSERT IGNORE INTO `bots` (`bot_id`, `bot_name`, `bot_desc`, `bot_active`, `bot_parent_id`, `format`, `use_aiml_code`, `update_aiml_code`, `save_state` , `conversation_lines` , `remember_up_to` , `debugemail`, `debugshow`, `debugmode`, `default_aiml_pattern`, `error_response`) TO INSERT IGNORE INTO `bots` (`bot_id`, `bot_name`, `bot_desc`, `bot_active`, `bot_parent_id`, `format`, `save_state` , `conversation_lines` , `remember_up_to` , `debugemail`, `debugshow`, `debugmode`, `default_aiml_pattern`, `use_aiml_code`, `update_aiml_code`, `error_response`) --- install/install_programo.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/install_programo.php b/install/install_programo.php index 5483c71..dfeb7cf 100644 --- a/install/install_programo.php +++ b/install/install_programo.php @@ -112,7 +112,7 @@ function Save() { $sql = 'select `php_code` from `aiml` where 1 limit 1'; $result = mysql_query($sql,$conn) or upgrade($conn); $sql_template = " -INSERT IGNORE INTO `bots` (`bot_id`, `bot_name`, `bot_desc`, `bot_active`, `bot_parent_id`, `format`, `use_aiml_code`, `update_aiml_code`, `save_state` , `conversation_lines` , `remember_up_to` , `debugemail`, `debugshow`, `debugmode`, `default_aiml_pattern`, `error_response`) +INSERT IGNORE INTO `bots` (`bot_id`, `bot_name`, `bot_desc`, `bot_active`, `bot_parent_id`, `format`, `save_state` , `conversation_lines` , `remember_up_to` , `debugemail`, `debugshow`, `debugmode`, `default_aiml_pattern`, `use_aiml_code`, `update_aiml_code`, `error_response`) VALUES ([bot_id], '[bot_name]', '[bot_desc]', [bot_active], [bot_parent_id], '[format]', '[save_state]', [conversation_lines], [remember_up_to], '[debugemail]', [debugshow], [debugmode], '[default_aiml_pattern]', [use_aiml_code], [update_aiml_code], '[error_response]');"; require_once (_LIB_PATH_ . 'error_functions.php'); From fc1c58cfbd3a32dc2a92d59b9ad273493c2a12e1 Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Tue, 12 Jun 2012 12:56:41 -0700 Subject: [PATCH 040/123] Altered the SQL query for populating the bots table to match the SQL query that creates that table. --- install/install_programo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/install_programo.php b/install/install_programo.php index dfeb7cf..b31433e 100644 --- a/install/install_programo.php +++ b/install/install_programo.php @@ -112,8 +112,8 @@ function Save() { $sql = 'select `php_code` from `aiml` where 1 limit 1'; $result = mysql_query($sql,$conn) or upgrade($conn); $sql_template = " -INSERT IGNORE INTO `bots` (`bot_id`, `bot_name`, `bot_desc`, `bot_active`, `bot_parent_id`, `format`, `save_state` , `conversation_lines` , `remember_up_to` , `debugemail`, `debugshow`, `debugmode`, `default_aiml_pattern`, `use_aiml_code`, `update_aiml_code`, `error_response`) -VALUES ([bot_id], '[bot_name]', '[bot_desc]', [bot_active], [bot_parent_id], '[format]', '[save_state]', [conversation_lines], [remember_up_to], '[debugemail]', [debugshow], [debugmode], '[default_aiml_pattern]', [use_aiml_code], [update_aiml_code], '[error_response]');"; +INSERT IGNORE INTO `bots` (`bot_id`, `bot_name`, `bot_desc`, `bot_active`, `bot_parent_id`, `format`, `use_aiml_code`, `update_aiml_code`, `save_state`, `conversation_lines`, `remember_up_to`, `debugemail`, `debugshow`, `debugmode`, `error_response`, `default_aiml_pattern`) +VALUES ([bot_id], '[bot_name]', '[bot_desc]', [bot_active], [bot_parent_id], '[format]', [use_aiml_code], [update_aiml_code], '[save_state]', [conversation_lines], [remember_up_to], '[debugemail]', [debugshow], [debugmode], '[error_response]', '[default_aiml_pattern]');"; require_once (_LIB_PATH_ . 'error_functions.php'); require_once (_LIB_PATH_ . 'db_functions.php'); From d762c5c329b8975830f6ce35782d5b794504318b Mon Sep 17 00:00:00 2001 From: Elizabeth Perreau Date: Thu, 14 Jun 2012 01:42:38 +0200 Subject: [PATCH 041/123] FIX: Path fix + gui fixes --- index.php | 63 +++++++------------------------------------------------ 1 file changed, 8 insertions(+), 55 deletions(-) diff --git a/index.php b/index.php index aef1fe6..26557ad 100644 --- a/index.php +++ b/index.php @@ -1,61 +1,14 @@ - - - - - - - - - Program O AIML PHP Chatbot - - - - - -
    -

    - - - - - - -

    - - - From cf1675e3738b90d28064442d811f506ebcad6880 Mon Sep 17 00:00:00 2001 From: Elizabeth Perreau Date: Thu, 14 Jun 2012 01:43:56 +0200 Subject: [PATCH 042/123] FIX: Path fix + gui fixes --- chatbot/conversation_start.php | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/chatbot/conversation_start.php b/chatbot/conversation_start.php index c28b252..5e19fb6 100644 --- a/chatbot/conversation_start.php +++ b/chatbot/conversation_start.php @@ -9,27 +9,11 @@ * DATE: MAY 4TH 2011 * DETAILS: this file is the landing page for all calls to access the bots ***************************************/ - session_start(); - $time_start = microtime(true); - $docRoot = $_SERVER['DOCUMENT_ROOT']; - $docRoot = str_replace('/', DIRECTORY_SEPARATOR, $docRoot); - $thisFolder = dirname(realpath(__FILE__)) . DIRECTORY_SEPARATOR; - $baseFolder = str_ireplace('chatbot'.DIRECTORY_SEPARATOR, '', $thisFolder); - $relPath = str_ireplace(array($docRoot, DIRECTORY_SEPARATOR), array('', '/'), $baseFolder); - $configFile = $baseFolder . 'config' . DIRECTORY_SEPARATOR . 'global_config.php'; - $headerURL = 'http://' . $_SERVER["HTTP_HOST"] . $relPath . 'install/install_programo.php'; - - $debugString = " -Document Root = $docRoot
    -This folder = $thisFolder
    -Relative path = $relPath
    -Base folder = $baseFolder
    -Config file = $configFile
    -Header URL = $headerURL"; - #die($debugString); - - if (!file_exists($configFile)) header("location: $headerURL"); // Gives the full URL to the install script - require_once($configFile); + + + + + require_once("../config/global_config.php"); //load shared files include_once (_LIB_PATH_ . "db_functions.php"); From 43c45312ef5ef6ad529ecf3a3434bee4169c903a Mon Sep 17 00:00:00 2001 From: Elizabeth Perreau Date: Thu, 14 Jun 2012 01:44:24 +0200 Subject: [PATCH 043/123] FIX: Path fix + gui fixes --- gui/jquery/index.php | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/gui/jquery/index.php b/gui/jquery/index.php index 2e3bcb2..bd56b50 100644 --- a/gui/jquery/index.php +++ b/gui/jquery/index.php @@ -12,16 +12,7 @@ $configFile = $baseFolder . 'config' . DIRECTORY_SEPARATOR . 'global_config.php'; $headerURL = 'http://' . $_SERVER["HTTP_HOST"] . $relPath . 'install/install_programo.php'; - $debugString = " -Document Root = $docRoot
    -This folder = $thisFolder
    -Relative path = $relPath
    -Base folder = $baseFolder
    -Config file = $configFile
    -Header URL = $headerURL"; - #die($debugString); - if (!file_exists($configFile)) header("location: $headerURL"); // Gives the full URL to the install script require_once($configFile); ?> @@ -85,3 +76,5 @@ + +?> From fd4654bab1ea3b091c79ec5b9b22c96a5f0b2a61 Mon Sep 17 00:00:00 2001 From: Elizabeth Perreau Date: Thu, 14 Jun 2012 01:44:58 +0200 Subject: [PATCH 044/123] FIX: Path fix + gui fixes --- gui/xml/index.php | 47 +++++++++++------------------------------------ 1 file changed, 11 insertions(+), 36 deletions(-) diff --git a/gui/xml/index.php b/gui/xml/index.php index 0cc0c61..6ccf90d 100644 --- a/gui/xml/index.php +++ b/gui/xml/index.php @@ -11,29 +11,10 @@ ***************************************/ - $docRoot = $_SERVER['DOCUMENT_ROOT']; - $docRoot = str_replace('/', DIRECTORY_SEPARATOR, $docRoot); - $thisFolder = dirname(realpath(__FILE__)) . DIRECTORY_SEPARATOR; - $baseFolder = str_ireplace('gui'.DIRECTORY_SEPARATOR.'xml'.DIRECTORY_SEPARATOR, '', $thisFolder); - $relPath = str_ireplace(array($docRoot, DIRECTORY_SEPARATOR), array('', '/'), $baseFolder); - $configFile = $baseFolder . 'config' . DIRECTORY_SEPARATOR . 'global_config.php'; - $headerURL = 'http://' . $_SERVER["HTTP_HOST"] . $relPath . 'install/install_programo.php'; - - $debugString = " -Document Root = $docRoot
    -This folder = $thisFolder
    -Relative path = $relPath
    -Base folder = $baseFolder
    -Config file = $configFile
    -Header URL = $headerURL"; - #die($debugString); - - if (!file_exists($configFile)) header("location: $headerURL"); // Gives the full URL to the install script - require_once($configFile); + require_once('../../config/global_config.php'); $response = ''; session_start(); - function get_response($path){ $ch = curl_init(); curl_setopt($ch, CURLOPT_URL,$path); @@ -68,16 +49,13 @@ function get_response($path){ #$X = file_put_contents('URL.txt', "$send\r\n",FILE_APPEND); #die(); $sXML = trim(get_response($send)); - /* - $response = htmlentities($sXML); - $response = str_replace("\t\t", "\n", $response); - $response = str_replace("\t", "\n", $response); - */ - #file_put_contents('conversationXML.txt', $sXML); + + //just output as an example + $responseXML = htmlentities($sXML); + $responseXML = str_replace("\n\t", "
    ", $responseXML); + $responseXML = str_replace("\n", "
    ", $responseXML); + $xml = new SimpleXMLElement($sXML); - #$xmlConversation = $xml->conversation; - #$user_name = $xmlConversation->user_name; - #$bot_name = $xmlConversation->bot_name; $count = 0; foreach ($xml->children() as $child) { $childName = $child->getName(); @@ -95,11 +73,7 @@ function get_response($path){ $response .= "$bot_name: " . $child . "
    \n"; default: } -/* -*/ } -/* -*/ } ?> @@ -126,7 +100,8 @@ function get_response($path){

    + + + - - - + \ No newline at end of file From 6ac1a12fad2dfd60bf931fac9fadd7fc9d51c9e7 Mon Sep 17 00:00:00 2001 From: Elizabeth Perreau Date: Thu, 14 Jun 2012 01:45:20 +0200 Subject: [PATCH 045/123] FIX: Path fix + gui fixes --- gui/plain/index_better_divs.php | 45 +++++++++++++++------------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/gui/plain/index_better_divs.php b/gui/plain/index_better_divs.php index 18ff24d..2ae5914 100644 --- a/gui/plain/index_better_divs.php +++ b/gui/plain/index_better_divs.php @@ -1,33 +1,30 @@ -
    ' : ""; $thisScript = $_SERVER['SCRIPT_NAME'] . '#new'; @@ -72,4 +69,4 @@ endHTML; -print $content; +print $content; \ No newline at end of file From 3072f1db9c37911e78ad19abb9e48a314d13732d Mon Sep 17 00:00:00 2001 From: Elizabeth Perreau Date: Thu, 14 Jun 2012 01:45:34 +0200 Subject: [PATCH 046/123] Update master --- gui/plain/index.php | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/gui/plain/index.php b/gui/plain/index.php index 7af5f4d..f989fbb 100644 --- a/gui/plain/index.php +++ b/gui/plain/index.php @@ -1,26 +1,9 @@ -This folder = $thisFolder
    -Relative path = $relPath
    -Base folder = $baseFolder
    -Config file = $configFile
    -Header URL = $headerURL"; - #die($debugString); - - if (!file_exists($configFile)) header("location: $headerURL"); // Gives the full URL to the install script - require_once($configFile); - - session_start(); - $display = ""; + $display = ""; + + require_once('../../config/global_config.php'); + require_once('../../chatbot/conversation_start.php'); if(isset($_REQUEST['bot_id'])){ $bot_id = $_REQUEST['bot_id']; @@ -65,4 +48,4 @@

    - + \ No newline at end of file From f5488cf6ed6eab80ac930c9c07ace84eb1592087 Mon Sep 17 00:00:00 2001 From: Elizabeth Perreau Date: Thu, 14 Jun 2012 01:50:49 +0200 Subject: [PATCH 047/123] FIX: Paths AGAIN --- gui/plain/index_better_divs.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gui/plain/index_better_divs.php b/gui/plain/index_better_divs.php index 2ae5914..35a2282 100644 --- a/gui/plain/index_better_divs.php +++ b/gui/plain/index_better_divs.php @@ -2,8 +2,8 @@ $display = ""; - require_once('../../config/global_config.php'); - require_once('../../chatbot/conversation_start.php'); + require_once('../../config/global_config.php'); + require_once('../chatbot/conversation_start.php'); if(isset($_REQUEST['bot_id'])){ From 1cb94677eb7c6d1b5c1829780ae67031c147d8f0 Mon Sep 17 00:00:00 2001 From: Elizabeth Perreau Date: Thu, 14 Jun 2012 01:51:17 +0200 Subject: [PATCH 048/123] FIX: more paths --- gui/plain/index.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gui/plain/index.php b/gui/plain/index.php index f989fbb..a4257aa 100644 --- a/gui/plain/index.php +++ b/gui/plain/index.php @@ -2,8 +2,8 @@ $display = ""; - require_once('../../config/global_config.php'); - require_once('../../chatbot/conversation_start.php'); + require_once('../../config/global_config.php'); + require_once('../chatbot/conversation_start.php'); if(isset($_REQUEST['bot_id'])){ $bot_id = $_REQUEST['bot_id']; From e3be772b32e097df9a06615a110798a42db1e662 Mon Sep 17 00:00:00 2001 From: Elizabeth Perreau Date: Thu, 14 Jun 2012 01:53:53 +0200 Subject: [PATCH 049/123] FIX: undeclared var... now declared! --- gui/xml/index.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gui/xml/index.php b/gui/xml/index.php index 6ccf90d..bd9dc7a 100644 --- a/gui/xml/index.php +++ b/gui/xml/index.php @@ -13,8 +13,11 @@ require_once('../../config/global_config.php'); -$response = ''; session_start(); + +$response = ''; +$responseXML = ''; + function get_response($path){ $ch = curl_init(); curl_setopt($ch, CURLOPT_URL,$path); From 6f46d019cf211347dc30455ad7da4fb206f5a553 Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Thu, 14 Jun 2012 02:57:09 -0700 Subject: [PATCH 050/123] Formatting changes, stability fixes and feature upgrades The following changes were implemented: 1.) Made uploading and downloading AIML files, and clearing AIML categories a per-bot feature. 2.) Addressed a problem where certain bot settings didn't always get changed in the bot selection/editing panel. 3.) Altered the layout of several panels to standardize the layout. 4.) Added "Help" panels to several sections (some are still under construction). Signed-off-by: Dave Morton --- admin/clear.php | 86 ++++++++++++++++++++++++++------------ admin/default.page.htm | 94 ++++++++++++++++++++++++------------------ admin/download.php | 62 ++++++++++++++++++++++++---- admin/style.css | 61 ++++++++++++++++++++------- admin/upload.php | 56 +++++++++++++------------ 5 files changed, 242 insertions(+), 117 deletions(-) diff --git a/admin/clear.php b/admin/clear.php index 4c334fd..b461828 100644 --- a/admin/clear.php +++ b/admin/clear.php @@ -10,16 +10,40 @@ $content =""; -/* -if((isset($_POST['action']))&&($_POST['action']=="clear")) -{ - $content = clearAIML(); -} -elseif((isset($_POST['clearFile']))&&($_POST['clearFile'] != "null")){ - $content = clearAIMLByFileName($_POST['clearFile']); -} -else {} -*/ + $upperScripts = << + + +endScript; + if((isset($_POST['action']))&&($_POST['action']=="clear")) { $content .= clearAIML(); @@ -44,31 +68,35 @@ $noTopNav = ''; $noRightNav = $template->getSection('NoRightNav'); $headerTitle = 'Actions:'; - $pageTitle = 'My-Program O - Clear AIML Categories'; + $pageTitle = "My-Program O - Clear AIML Categories"; $mainContent = $content; - $mainTitle = 'Clear AIML Categories'; + $mainTitle = "Clear AIML Categories for the bot named $bot_name [helpLink]"; + $showHelp = $template->getSection('ClearShowHelp'); + $mainTitle = str_replace('[helpLink]', $template->getSection('HelpLink'), $mainTitle); + $mainContent = str_replace('[showHelp]', $showHelp, $mainContent); + $mainContent = str_replace('[upperScripts]', $upperScripts, $mainContent); function replaceTags(&$content) { return $content; } function clearAIML() { - global $dbn; + global $dbn, $bot_id, $bot_name; $dbconn = db_open(); - $sql = 'truncate table aiml;'; + $sql = 'DELETE FROM `aiml` WHERE `bot_id` = $bot_id;'; #return "SQL = $sql"; $result = mysql_query($sql,$dbconn) or die(mysql_error()); mysql_close($dbconn); - $msg = "All AIML categories cleared!
    "; + $msg = "All AIML categories cleared for $bot_name!
    "; return $msg; } function clearAIMLByFileName($filename) { - global $dbn; + global $dbn, $bot_id; $dbconn = db_open(); $cleanedFilename = mysql_real_escape_string($filename, $dbconn); - $sql = "delete from aiml where filename like '$cleanedFilename';"; + $sql = "delete from `aiml` where `filename` like '$cleanedFilename' and `bot_id = $bot_id;"; #return "SQL = $sql"; $result = mysql_query($sql,$dbconn) or die(mysql_error()); mysql_close($dbconn); @@ -77,13 +105,14 @@ function clearAIMLByFileName($filename) { } function getSelOpts() { - global $dbn; + global $dbn, $bot_id, $msg; $out = " \n"; $dbconn = db_open(); $optionTemplate = " \n"; - $sql = 'SELECT DISTINCT filename FROM aiml order by filename;'; + $sql = "SELECT DISTINCT filename FROM `aiml` where `bot_id` = $bot_id order by `filename`;"; #return "SQL = $sql"; $result = mysql_query($sql,$dbconn) or die(mysql_error()); + if (mysql_num_rows($result) == 0) $msg = "This bot has no AIML categories to clear."; while ($row = mysql_fetch_assoc($result)) { if (empty($row['filename'])) { $curOption = " \n"; @@ -99,20 +128,23 @@ function getSelOpts() { function renderMain() { $selectOptions = getSelOpts(); $content = <<permanent!
    + Deleting AIML categories from the database is permanent! This action CANNOT be undone!
    +
    -
    - + +
    + + + +
    @@ -283,27 +290,26 @@
    - + + + +
    - + + + +
    +
    - - - - + + + -
    +
    +
    +
    +
    +
    +[showHelp] @@ -689,7 +685,7 @@

    Help...

    [upload_content]
    - List of Currently Stored AIML files:
    + List of Currently Stored AIML files for [bot_name]:
    [AIML_List]
    @@ -699,15 +695,31 @@

    Help...

    [blank]
    -
    -
    + + + + + + + + + + + +
        +     +
    + +     +
    - - +
    diff --git a/admin/download.php b/admin/download.php index c5d1596..cf07713 100644 --- a/admin/download.php +++ b/admin/download.php @@ -10,6 +10,41 @@ // download.php $content =""; + +$upperScripts = << + + +endScript; + $msg = (isset($_REQUEST['msg'])) ? $_REQUEST['msg'] : ''; if((isset($_POST['action']))&&($_POST['action']=="AIML")) { $content .= getAIMLByFileName($_POST['getFile']); @@ -23,6 +58,7 @@ else { } $content .= renderMain(); + $showHelp = $template->getSection('DownloadShowHelp'); $topNav = $template->getSection('TopNav'); $leftNav = $template->getSection('LeftNav'); @@ -31,15 +67,19 @@ $navHeader = $template->getSection('NavHeader'); $leftNavLinks = makeLinks('left', $leftLinks, 12); $FooterInfo = getFooter(); + #$msg = (empty($msg)) ? 'Test' : $msg; $errMsgClass = (!empty($msg)) ? "ShowError" : "HideError"; $errMsgStyle = $template->getSection($errMsgClass); $noLeftNav = ''; $noTopNav = ''; $noRightNav = $template->getSection('NoRightNav'); $headerTitle = 'Actions:'; - $pageTitle = 'My-Program O - Download AIML'; + $pageTitle = "My-Program O - Download AIML files"; $mainContent = $content; - $mainTitle = 'Download AIML'; + $mainTitle = "Download AIML files for the bot named $bot_name [helpLink]"; + + $mainContent = str_replace('[showHelp]', $showHelp, $mainContent); + $mainTitle = str_replace('[helpLink]', $template->getSection('HelpLink'), $mainTitle); function replaceTags(&$content) { return $content; @@ -145,13 +185,14 @@ function getSQLByFileName($filename) { } function getSelOpts() { - global $dbn; + global $dbn, $bot_id, $msg; $out = " \n"; $dbconn = db_open(); $optionTemplate = " \n"; - $sql = 'SELECT DISTINCT filename FROM aiml order by filename;'; + $sql = "SELECT DISTINCT filename FROM `aiml` where `bot_id` = $bot_id order by `filename`;"; #return "SQL = $sql"; $result = mysql_query($sql,$dbconn) or die(mysql_error()); + if (mysql_num_rows($result) == 0) $msg = "This bot has no AIML categories. Please select another bot."; while ($row = mysql_fetch_assoc($result)) { if (empty($row['filename'])) { $curOption = " \n"; @@ -167,32 +208,35 @@ function getSelOpts() { function renderMain() { $selectOptions = getSelOpts(); $content = << Please select the AIML file you wish to download from the list below.
    - +
    - - - -
    + +
    + +
    +
    +[showHelp] endForm; return $content; diff --git a/admin/style.css b/admin/style.css index a03ddd6..45950d0 100644 --- a/admin/style.css +++ b/admin/style.css @@ -67,21 +67,21 @@ span.eightyPercent input.wide { width: 67%; margin: 0; float: right; } .bold { font-weight: bold; } .big { font-size: larger; } .narrow { padding-left: 0; float: left;width: 25%;text-align: left; border: 1px dashed blue;} + .errMsg { - position: absolute; - top: 10px; - left: 10px; min-width: 250px; - width: 30%; + max-width: 98%; + width: auto; background-color: #FFF45A; background-color: #FCC; /*transparent;*/ color: black; vertical-align: middle; text-align: center; - padding: 0; - margin: 0; + padding: 2px; + margin: 0 auto; display: none; overflow: auto; + border: 2px inset #933; } .ul, .ll, .ur, .lr { width: 6px; @@ -381,13 +381,18 @@ span.eightyPercent input.wide { width: 67%; margin: 0; float: right; } } #main_content { position: absolute; - top: 50px; - bottom: 25px; - left: 5px; - right: 5px; + top: 75px; + bottom: 5px; + left: 0; + right: 0; text-indent: 1em; overflow: auto; + padding-top: 5px; + background-color: #FFFFCC; + z-index: 3; +/* width: 99%; +*/ } #rssContainer { @@ -491,26 +496,31 @@ border:none; } #help { + top: 5px; overflow: hidden; position: absolute; top: 5px; left: 5px; bottom: 5px; right: 5px; +/* +*/ z-index: 5; + background-color: transparent; background-color: #FFFFCC; } #showHelp { position: absolute; - top: 0; + top: 5px; left: 0; - bottom: 0; + bottom: 5px; right: 0; background-color: #FFFFCC; padding: 5px; display: none; overflow: auto; border: 1px solid black; + border: none; } #teachForm, #uploadForm, #downloadForm, #membersForm { @@ -521,8 +531,11 @@ border:none; right: 20%; z-index: 10; display: block; + /*background-color: red;*/ + overflow: auto; } + #teachForm fieldset { border: none; } #demoChatContainer { @@ -627,8 +640,11 @@ div#AIML_List { padding: 12px; text-align: left; text-indent: 0; - width: 250px; - height: 300px; + width: 300px; +/* + min-height: 120px; +*/ + max-height: 300px; overflow: auto; } @@ -684,4 +700,19 @@ table td#notes { td input, td select { width: 100%; margin: 0 auto; -} \ No newline at end of file +} +.formTable { + border: 1px solid orange; + margin: 10px; + padding: 5px; +} +.formTable td { + text-align: center; + padding: 3px; + border: 1px solid green; + border: none; + margin: 2px; + padding: 2px; +} +.formTable td select { width: auto; } +.formTable td input[type=submit] { width: auto; } diff --git a/admin/upload.php b/admin/upload.php index c035be9..7b64770 100644 --- a/admin/upload.php +++ b/admin/upload.php @@ -9,6 +9,7 @@ // upload.php ini_set('memory_limit','128M'); ini_set('max_execution_time','0'); + ini_set('display_errors','1'); libxml_use_internal_errors(true); $file = (array_key_exists('aimlfile', $_FILES)) ? processUpload() : ''; @@ -65,6 +66,7 @@ function showHide() { $full_path = ""; $cat_counter = 0; $AIML_List = getAIML_List(); + $all_bots = getBotList(); $uploadContent = $template->getSection('UploadAIMLForm'); $showHelp = $template->getSection('UploadShowHelp'); @@ -84,14 +86,17 @@ function showHide() { $pageTitle = 'My-Program O - Upload AIML'; $mainContent = $template->getSection('UploadMain'); $mainTitle = "Upload AIML to use for the bot named $bot_name [helpLink]"; - + $msg = (empty($msg)) ? 'Test' : $msg; $mainContent = str_replace('[bot_name]', $bot_name, $mainContent); + $mainContent = str_replace('[mainTitle]', $mainTitle, $mainContent); $mainContent = str_replace('[upload_content]', $uploadContent, $mainContent); $mainContent = str_replace('[showHelp]', $showHelp, $mainContent); $mainContent = str_replace('[AIML_List]', $AIML_List, $mainContent); + $mainContent = str_replace('[all_bots]', $all_bots, $mainContent); $mainTitle = str_replace('[helpLink]', $template->getSection('HelpLink'), $mainTitle); + $mainTitle = str_replace('[errMsg]', $msg, $mainTitle); - function parseAIML ($file) { + function parseAIML($file) { if (empty($file)) return ""; global $debugmode, $bot_id; $fileName = basename($file); @@ -102,12 +107,14 @@ function parseAIML ($file) { if(isset($_POST['clearDB'])) { $x = updateDB($sql); } + $myBot_id = (isset($_POST['bot_id'])) ? $_POST['bot_id'] : $bot_id; # Read new file into the XML parser $fileName = basename($file); $sql_start = "insert into `aiml` (`id`, `bot_id`, `aiml`, `pattern`, `thatpattern`, `template`, `topic`, `filename`, `php_code`) values\n"; $sql = $sql_start; - $sql_template = "(NULL, $bot_id, '[aiml_add]', '[pattern]', '[that]', '[template]', '[topic]', '$fileName', ''),\n"; + $sql_template = "(NULL, $myBot_id, '[aiml_add]', '[pattern]', '[that]', '[template]', '[topic]', '$fileName', ''),\n"; # Validate the incoming document + #die("sql = $sql_template"); /* $xml = new DOMDocument(); $xml->load('./lures.xml'); @@ -127,26 +134,6 @@ function parseAIML ($file) { $aimlTagStart = stripos($aimlContent,'', $aimlTagStart) + 1; $aimlFile = $validAIMLHeader . substr($aimlContent,$aimlTagEnd); -/* - #die('
    '.htmlentities($aimlFile)."
    \n"); - $saveFile = str_replace('./uploads',_ADMIN_PATH_.'aiml',$file); - #if (!file_exists($saveFile)) file_put_contents("$saveFile", $aimlFile); - $validate = new DOMDocument(); - $validate->loadXML($aimlFile); - #$validate->loadXML($aimlContent); - $validate->preserveWhiteSpace = false; - $validate->formatOutput = true; - if ($validate->validate() === false) { - $msg = "Cannot parse file $file! Please note errors that follow:\n"; - - $errors = libxml_get_errors(); - foreach ($errors as $error) { - $msg .= libxml_display_error($error); - } - libxml_clear_errors(); - return $msg; - } -*/ try { $aiml = new SimpleXMLElement($aimlFile); #$aiml = new SimpleXMLElement($aimlContent); @@ -252,14 +239,15 @@ function processUpload() { $msg = 'There was an error moving the file.'; } } - die($msg); + //die($msg); + $_SESSION['errorMessage'] = $msg; } function getAIML_List() { - global $dbn; + global $dbn, $bot_id; $out = " \n"; $dbconn = db_open(); - $sql = 'SELECT DISTINCT filename FROM aiml order by filename;'; + $sql = "SELECT DISTINCT filename FROM `aiml` where `bot_id` = $bot_id order by `filename`;"; $result = mysql_query($sql,$dbconn) or die(mysql_error()); while ($row = mysql_fetch_assoc($result)) { if (empty($row['filename'])) { @@ -272,6 +260,22 @@ function getAIML_List() { return $out; } + function getBotList() { + global $dbn, $bot_id; + $botOptions = ''; + $dbconn = db_open(); + $sql = 'SELECT `bot_name`, `bot_id` FROM `bots` order by `bot_id`;'; + $result = mysql_query($sql,$dbconn) or die(mysql_error()); + while ($row = mysql_fetch_assoc($result)) { + $bn = $row['bot_name']; + $bi = $row['bot_id']; + $sel = ($bot_id == $bi) ? ' selected="selected"' : ''; + $botOptions .= " $bn\n"; + } + mysql_close($dbconn); + return $botOptions; + } + function libxml_display_error($error) { $out = "
    \n"; switch ($error->level) { From 85c5104fe4f6c4a35a27f28c9971d8d0c1546c8c Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Thu, 14 Jun 2012 06:11:27 -0700 Subject: [PATCH 051/123] Minor layout tweaks Nothing special, and no changes to functionality Signed-off-by: Dave Morton --- admin/admin.css | 508 ----------------------------------------- admin/default.page.htm | 52 ++--- admin/error.log | 0 admin/style.css | 24 +- install/error.log | 0 5 files changed, 42 insertions(+), 542 deletions(-) delete mode 100644 admin/admin.css delete mode 100644 admin/error.log delete mode 100644 install/error.log diff --git a/admin/admin.css b/admin/admin.css deleted file mode 100644 index 3b71949..0000000 --- a/admin/admin.css +++ /dev/null @@ -1,508 +0,0 @@ -body { - background-attachment: fixed; - background-color: #a5c739; - background-image: url(http://github.com/images/repeated_header.jpg); - background-repeat: repeat-x; - height: 100%; -/*overflow: hidden;*/ - margin: 0; - padding: 0; - width: 100%; -} - -fieldset { - border: 1px solid black; - line-height: 125%; - padding: 12px; - padding-left: 5%; - padding-right: 5%; -} - -label { - cursor: pointer; - margin-right: 20px; -} - -legend { - font-size: xx-large; - font-weight: bold; - margin-left: -5px; - margin-top: -15px; -} - -p {text-indent: 1em;} - -strong { - text-align: center; - width: 100%; -} - -table {border: 6px outset orange;/**/} - -tr, td { - background-color: #EEEEEE; - border: 3px yellow inset; - border-bottom-color: #993300; - border-left-color: #FF9900; - border-right-color: #993300; - border-top-color: #FF9900; - margin: 0; - padding: 0; -} - -.bold {font-weight: bold;} - -.border {border: 1px solid black;} - -.error { - clear: left; - color: red; - text-decoration: underline; -} - -.floatRight {float: right;} - -.fullSize { - width: 99.5%;/* border: 1px solid red;*/ -} - -.halfSize {width: 50%;} - -.ll { - background-image: url(http://github.com/images/ll.png); - bottom: 0; - left: 0; -} - -.lr { - background-image: url(http://github.com/images/lr.png); - bottom: 0; - right: 0; -} - -.m10r {margin-right: 30px;} - -.m5r {margin-right: 5px;} - -.orange { - background-color: transparent; - color: orange; -} - -.pad5 {padding: 5px;} - -.red { - background-color: transparent; - color: red; - font-size: large; - font-weight: bold; -} - -.shadowBorderInset { - border-bottom: 1px solid white; - border-left: 2px solid #707070; - border-right: 1px solid white; - border-top: 2px solid #505050; -} - -.shadowBorderOutset { - border-bottom: 2px solid #505050; - border-left: 1px solid white; - border-right: 2px solid #707070; - border-top: 1px solid white; -} - -.small {font-size: x-small;} - -.spacer_left {margin-left: 20px;} - -.underline { - text-decoration: underline; -} - -div.row { - clear: both; - padding-top: 5px; -} - -div.row span.formw { - float: right; - text-align: left; - width: 49.75%; -} - -div.row span.formw input [ type = checkbox ], div.row span.formw input [ type = radio ] { - margin: 0; - width: 15px; -} - -div.row span.formw input, div.row span.formw textarea { - margin: 0; - width: 150px; -} - -div.row span.formw select { - margin: 0; - width: 155px; -} - -div.row span.formxw input [ type = checkbox ], div.row span.formxw input [ type = radio ] { - margin: 0; - margin-left: 6px; - text-align: left; - width: 15px; -} - -div.row span.formxw input, div.row span.formxw textarea { - margin: 0; - margin-left: 6px; - width: 40%; -} - -div.row span.formxw select { - margin: 0; - width: 42%; -} - -div.row span.label { - float: left; - text-align: left; - width: 49.75%; -} - -div.spacer {clear: both;} - -tr, td, .center {text-align: center;} - -#botNames {height: 20px;} - -#container{ - margin: 0 auto; - padding: 1em; - text-align: left; -} - -#container1, #container2 { - margin: 0; - margin-left: auto; - margin-right: auto; - padding: 0px; - position: relative; - width: 95%; -} - -#container2 { - background-color: #FFFF99; - border: 4px inset #a5c739; - overflow: auto; -} - -#footer { - background-color: #AFEEEE; - bottom: 15px; - color: #000; - height: 60px; - left: 5%; - margin-left: auto; - margin-right: auto; - padding: 0px; - position: absolute; - right: 5%; - width: 90%; -} - -#footer a { - color: #467AA7; - font-size: 1em; - font-weight: normal; - text-align: center; - text-decoration: none; -} - -#footer p { - color: #333; - font-family: arial; - font-size: 0.7em; - font-weight: normal; - line-height: 1.4em; - padding: 2px; - text-align: center; -} - -#formSubmit { - border: none; - float: right; - margin-right: 4%; - margin-top: 4px; - text-align: right; - width: 95%; -} - -#form_table { - margin-left: auto; - margin-right: auto; - margin-top: 20px; - padding: 12px; - width: 250px; -} - -#gcData, #lsData, #rsData { - background-color: #FFFF99; - left: 0; - margin: 0; - margin-bottom: 6px; - padding: 0; - padding-bottom: 6px; - position: relative; - top: 0; - width: 100%; -} - -#leftPanel {left: 5px;} - -#leftPanel, #midPanel, #rightPanel { - background-color: #FFFF99; - border: 4px inset #a5c739; - overflow: auto; - padding: 3px; - position: absolute; - text-align: left; - top: 5px; - width: 31%; -} - -#ll { - background-image: url(http://github.com/images/ll.png); - bottom: 3px; - left: 0; -} - -#logo { - background-image: url(http://github.com/images/bg_header.gif); - background-position: center 0; - background-repeat: no-repeat; - height: 217px; - left: 0; - position: absolute; - top: 0; - width: 100%; - z-index: 1; -} - -#lr { - background-image: url(http://github.com/images/lr.png); - bottom: 3px; - right: 0; -} - -#main {height: 450px;} - -#main { - background-color: white; - bottom: 200px; - left: 240px; - max-height: 400px; - min-height: 200px; - position: absolute; - right: 5%; - top: 60px;/**/ -} - -#midPanel {right: 34%;} - -#nav { - left: 0; - list-style: none; - position: absolute; - top: 44px; -} - -#nav .ll { - background-image: url(http://github.com/images/ll.png); - bottom: 16px; - left: 40px; -} - -#nav .lr { - background-image: url(http://github.com/images/lr.png); - bottom: 16px; - right: 0; -} - -#nav .selected { - background-color: #f7f7f2; - color: #37210c; -} - -#nav .ul { - background-image: url(http://github.com/images/ul.png); - left: 40px; - top: 16px; -} - -#nav .ur { - background-image: url(http://github.com/images/ur.png); - right: 0; - top: 16px; -} - -#nav li { - border-bottom: 1px solid #eaeada; - text-align: left; -} - -#nav ul {list-style: none;} - -#nav ul li a { - background-color: #AFEEEE; - background-image: url(http://github.com/pages/Img/bullet.gif); - background-position: left center; - background-repeat: no-repeat; - color: #666666; - display: block; - font-size: 0.8em; - font-weight: normal; - line-height: 1.7em; - margin: 0px; - padding-bottom: 6px; - padding-left: 22px; - padding-top: 6px; - text-decoration: none; - width: 165px; -} - -#navhorisontell { - background-color: transparent; - height: 50px; - margin-left: auto; - margin-right: auto; - width: 435px; -/*width: 90%;*/ -} - -#navhorisontell ul { - list-style: none; - margin: 0; -} - -#navhorisontell ul li a { - background-color: #008000; - border-right: 1px solid #fff; - color: #ecf9ff; - display: block; - font-size: 0.8em; - font-weight: normal; - line-height: 2.5em; - margin-right: 0px; - padding: 8px 14px 8px 14px; - text-decoration: none; -} - -#nocol { - font-size: 0.9em; - overflow: auto; - padding: 5px; - text-align: left; -} -/*#nocol .ul, #nocol .ll, #nocol .ur, #nocol .lr { width: 6px; height: 6px; position: absolute; background-color: transparent; z-index: 1;} - #nocol .ul { top: 60px; left: 240px; background-image: url(http://github.com/images/ul.png); } - #nocol .ll { bottom: 90px; left: 240px; background-image: url(http://github.com/images/ll.png); } - #nocol .ur { top: 60px; right: 5%; background-image: url(http://github.com/images/ur.png); } - #nocol .lr { bottom: 90px; right: 5%; background-image: url(http://github.com/images/lr.png); }*/ - -#notes { - bottom: 0; - left: 0; - position: absolute; -} - -#rightPanel {right: 5px;} - -#title { - background-color: transparent; - font-size: x-large; - font-weight: bold; - height: 30px; - left: 2.8%; - margin: 0; - padding: 0px; - position: absolute; - right: 33%; - text-align: center; - top: 206px; - width: 94%; - z-index: 2; -} - -#titlespan { - background-image: url(http://github.com/images/bg_list2.gif); - color: white; - width: 100%; - z-index: 5; -} - -#ul, #ll, #ur, #lr { - background-color: transparent; - height: 6px; - position: absolute; - width: 6px; - z-index: 4; -} - -#ul, .ul { - background-image: url(http://github.com/images/ul.png); - left: 0; - top: 0; -} - -#ur, .ur { - background-image: url(http://github.com/images/ur.png); - right: 0; - top: 0; -} - -#wrapper { - bottom: 0px; - left: 0px; - margin-top: 242px; - overflow: auto; - padding: 0; - padding-top: 0; - position: absolute; - right: 0px; - top: 0px; -} - -.floatLeft, #navhorisontell li { - float: left; -} - -.ul, .ll, .ur, .lr, #nav .ul, #nav .ll, #nav .ur, #nav .lr { - background-color: transparent; - height: 6px; - position: absolute; - width: 6px; - z-index: 1; -} - -#footer a:hover { - color: #467AA7; - font-weight: normal; - text-align: center; - text-decoration: underline; -} - -#nav li a:hover { - border-bottom: 1px solid yellow; - color: #000; -} - -#navhorisontell li a:hover, #navhorisontell ul li .selected { - background-color: #9ACD32; - color: #ecf9ff; -} - -#navhorisontell ul li .selected:hover { - background-color: #008000; - color: #ecf9ff; -} \ No newline at end of file diff --git a/admin/default.page.htm b/admin/default.page.htm index 464901e..60366ee 100644 --- a/admin/default.page.htm +++ b/admin/default.page.htm @@ -157,7 +157,7 @@
    -
    +
    @@ -167,19 +167,19 @@
    -
    + -[blank]
    +[blank]
    - - - - +
    + + + - + - + - + - + - + - + - +
    @@ -190,7 +190,7 @@
    @@ -202,7 +202,7 @@
    @@ -213,7 +213,7 @@
    @@ -224,7 +224,7 @@
    @@ -232,7 +232,7 @@
    @@ -240,7 +240,7 @@
    @@ -251,14 +251,14 @@
    - - - -
    - + + + + - + - + - + - + - + - +
    +
    @@ -283,13 +283,13 @@
    @@ -302,7 +302,7 @@
    - +
    diff --git a/admin/error.log b/admin/error.log deleted file mode 100644 index e69de29..0000000 diff --git a/admin/style.css b/admin/style.css index 45950d0..67ab29a 100644 --- a/admin/style.css +++ b/admin/style.css @@ -23,7 +23,7 @@ body { overflow: hidden; } table td {vertical-align: top;} -fieldset { border: 1px solid black; padding: 12px; padding-left: 5px; padding-right: 5px; line-height: 125%;} +fieldset { border: 1px solid orange; padding: 12px; padding-left: 5px; padding-right: 5px; line-height: 125%;} legend { font-weight: bold; font-size: xx-large; margin-left: -5px; margin-top: -15px; } label { cursor: pointer; margin-right: 20px; } div.row { clear: both; padding-top: 8px; border: none; width: 95%; margin-left: auto; margin-right: auto; min-height: 2em;} @@ -375,7 +375,7 @@ span.eightyPercent input.wide { width: 67%; margin: 0; float: right; } width: 99%; height: 50px; margin: 0; - font-size: xx-large; + font-size: large; font-weight: bold; font-variant: small-caps; } @@ -519,7 +519,6 @@ border:none; padding: 5px; display: none; overflow: auto; - border: 1px solid black; border: none; } @@ -636,12 +635,14 @@ border:none; div#AIML_List { border: 2px inset #9FC03A; - margin-left: 22px; + margin: 10px auto; padding: 12px; text-align: left; text-indent: 0; - width: 300px; + width: 80%; /* + min-width: 300px; + max-width: 600px; min-height: 120px; */ max-height: 300px; @@ -703,16 +704,23 @@ td input, td select { } .formTable { border: 1px solid orange; - margin: 10px; + width: 100%; + margin: 10px auto; padding: 5px; } .formTable td { - text-align: center; padding: 3px; border: 1px solid green; border: none; margin: 2px; padding: 2px; } -.formTable td select { width: auto; } +.formTable td select { width: 96.2%; margin: 0 auto; } +.formTable td textarea, .formTable td input[type=text] { width: 95%; margin: 0 auto; } +.formTable td textarea { height: 45px; } .formTable td input[type=submit] { width: auto; } +.formTable td span { width: 100%; text-align: left; margin: 0 auto; padding: 0; } + + + + diff --git a/install/error.log b/install/error.log deleted file mode 100644 index e69de29..0000000 From d77b4b9c19c00b98b28f5ead749c6d150b2e649d Mon Sep 17 00:00:00 2001 From: Program-O Date: Thu, 14 Jun 2012 18:37:54 +0100 Subject: [PATCH 052/123] TIDY: removal of the twitter gui reason: not yet implemented --- gui/twitter/index.php | 43 -- gui/twitter/page.php | 885 ------------------------------- gui/twitter/twitlib/tmhOAuth.php | 656 ----------------------- 3 files changed, 1584 deletions(-) delete mode 100644 gui/twitter/index.php delete mode 100644 gui/twitter/page.php delete mode 100644 gui/twitter/twitlib/tmhOAuth.php diff --git a/gui/twitter/index.php b/gui/twitter/index.php deleted file mode 100644 index 758efc7..0000000 --- a/gui/twitter/index.php +++ /dev/null @@ -1,43 +0,0 @@ - -* @license GNU Public License -*/ - -$tweet_text = 'Hello Twitter'; -print "Posting...\n"; -$result = post_tweet($tweet_text); -print "Response code: " . $result . "\n"; - -function post_tweet($tweet_text) { - - // Use Matt Harris' OAuth library to make the connection - // This lives at: https://github.com/themattharris/tmhOAuth - require_once('twitlib/tmhOAuth.php'); - - // Set the authorization values - // In keeping with the OAuth tradition of maximum confusion, - // the names of some of these values are different from the Twitter Dev interface - // user_token is called Access Token on the Dev site - // user_secret is called Access Token Secret on the Dev site - // The values here have asterisks to hide the true contents - // You need to use the actual values from Twitter - $connection = new tmhOAuth(array( - 'consumer_key' => 'Z6EiKT7pZq6OVqvWtZqxg', - 'consumer_secret' => '3VyLFSlQg70ZOvj2QAY0egPFB0etNAE8kTOQygZ7kew', - 'user_token' => '280185832-LiJ78IFzm1SolSZMaFuAkK5yu1XFMs5hhN48cXS1', - 'user_secret' => 'vOlhWfm9sCXYPoMPZUCZY5RlqEOJECcz6LtlIb4C2XE', - )); - - // Make the API call - $connection->request('POST', - $connection->url('http://github.com/1/statuses/update'), - array('status' => $tweet_text)); - - return $connection->response['code']; -} -?> \ No newline at end of file diff --git a/gui/twitter/page.php b/gui/twitter/page.php deleted file mode 100644 index d680d9c..0000000 --- a/gui/twitter/page.php +++ /dev/null @@ -1,885 +0,0 @@ - - - - - - - - -Program O AIML Chatbot | An Open Source AIML, PHP and MySQL Chatbot - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    - -
    -
    - -
    - -
    -

    Program O AIML Chatbot

    -
    - - -
    - -
    - - - - - -
    - - -
    - -Program O -
    - -
    -

    Download Your Own Program O

    -

    Program O is a free open source chatbot for you to add to your website. Chatbots can not only provide a geeky form of entertainment for you and your users. USER:I am banished from Chinatown BOT: It is a pleasure to introduce myself to you, banished. The can also provide a useful service for people [...]

    - -
    - Read more -
    - - -
    - - -
    - -
    - - -
    - -ShakespeareBot -
    -
    -

    Shakespearebot

    -

    ShakespeareBot was the first chat I ever created for my final year project whilst studying for my BSc. in Software Engineering. I really enjoyed creating him, and as I was working at a school at the time I had a great opportunity to test him out on over 1000 students! His chat logs still keep [...]

    - -
    - - Read more -
    - - -
    - - -
    - -
    - - -
    - -8planet - -
    -
    -

    8planet

    -

    My Very Enormous Monster Just Stopped Using Nine

    - -
    - Read more -
    - - -
    - -
    - - -
    - - -
    -
    -
    - -
    guy4

    Welcome to Program O

    Welcome to the Program O Project website -This is the home of the Open Source PHP MySQL AIML Chatbot Project. Program O is an AIML engine written in PHP with MySQL. -Here you can find support, help, bot addons, a brilliant and friendly community and ofcourse the Program O download files. -Please that ...
     

    Server Issues

    Hello everyone. - -A recent upgrade to the server has caused some quite major problems on the site. -Fortunately I have been able to fix it and everything seems to be running smoothly. -Hopefully it should be sorted now, but incase of any more down time ... sorry in advance. - -:) - -Thanks again
     
    Alpha By Mike Walker

    Alpha by Mike Walker on BBC Radio4 Extra

    What should we do when a computer becomes so powerful it gains independant thought? - -I happened to catch Radio 4 Extra's thought provoking drama Alpha by Mike Walker last night which asked just this question. -This award winning play centers around a Priest employed by the vatican, who is sent to interrogate ...
     
    May-4th-Be-With-You-Star-Wars

    May the 4th be with you.

    Its May the 4th 2011. Program O v2 release day or so I hoped. A ton of work and family commitments have prevented me from finishing everything I need to to make v2 a stable and easy to use product. I know you will be all disappointed, but I can ...
     
    Program O

    Download Your Own Program O

    Program O is a free open source chatbot for you to add to your website. -Chatbots can not only provide a geeky form of entertainment for you and your users. - -USER:I am banished from Chinatown -BOT: It is a pleasure to introduce myself to you, banished. - - -The can also provide a useful service for ...
     
    ShakespeareBot

    Shakespearebot

    ShakespeareBot was the first chat I ever created for my final year project whilst studying for my BSc. in Software Engineering. -I really enjoyed creating him, and as I was working at a school at the time I had a great opportunity to test him out on over 1000 students! -His chat ...
     
    ROBOTOTO

    Robototo

    Robototo is an Program O Based MSN chatbot. He enjoys talking to people on the MSN Messenger network. The only problem is that this poor chap does not know he is a Chatbot. - -This is a particular poignant chat with a lovelost user. Poor person I hope they are feeling ...
     
    8planet

    8planet

    My Very Enormous Monster Just Stopped Using Nine -
     
    Morti

    Morti

    Morti is a chat bot that created by Dave Morton of Geek Cave Creations. Based initially on both the ALICE Annotated AIML set, and Program O, both have been heavily modified with the intended goal of creating a more robust bot. Morti has a slightly snarky, whimsical outlook on life, ...
     
    - -

    Lots and Lots More Bots.....


    - - - - - - - - - - - - - - - - - - - - - - -
    - -

    Search

        

    Welcome

    - -

    Welcome to the Program O Project website This is the home of the Open Source PHP MySQL AIML Chatbot Project. Program O is an AIML engine written in PHP with MySQL. Here you can find support, help, bot addons, a brilliant and friendly community and ofcourse the Program O download files.

    -

    Translate

    -ArabicChinese (Simplified)CroatianEnglishFrenchGermanHebrewHindiItalianJapaneseKoreanMalayPortugueseRussian
    - - -

    Categories

    -

    Search

    -

         - -

    -
    - -

    Archive

    - -
    -

    Latest posts

    -
    - - -
    -
    - -
     
    - -
    -
    - - - - - - -

    -
    -

    - -
    - - - - - - - - - - - - \ No newline at end of file diff --git a/gui/twitter/twitlib/tmhOAuth.php b/gui/twitter/twitlib/tmhOAuth.php deleted file mode 100644 index 4785f54..0000000 --- a/gui/twitter/twitlib/tmhOAuth.php +++ /dev/null @@ -1,656 +0,0 @@ -params = array(); - $this->headers = array(); - $this->auto_fixed_time = false; - $this->buffer = null; - - // default configuration options - $this->config = array_merge( - array( - // leave 'user_agent' blank for default, otherwise set this to - // something that clearly identifies your app - 'user_agent' => '', - - 'use_ssl' => true, - 'host' => 'api.twitter.com', - - 'consumer_key' => '', - 'consumer_secret' => '', - 'user_token' => '', - 'user_secret' => '', - 'force_nonce' => false, - 'nonce' => false, // used for checking signatures. leave as false for auto - 'force_timestamp' => false, - 'timestamp' => false, // used for checking signatures. leave as false for auto - - // oauth signing variables that are not dynamic - 'oauth_version' => '1.0', - 'oauth_signature_method' => 'HMAC-SHA1', - - // you probably don't want to change any of these curl values - 'curl_connecttimeout' => 30, - 'curl_timeout' => 10, - - // for security this should always be set to 2. - 'curl_ssl_verifyhost' => 2, - // for security this should always be set to true. - 'curl_ssl_verifypeer' => true, - - // you can get the latest cacert.pem from here http://curl.haxx.se/ca/cacert.pem - 'curl_cainfo' => dirname(__FILE__) . '/cacert.pem', - 'curl_capath' => dirname(__FILE__), - - 'curl_followlocation' => false, // whether to follow redirects or not - - // support for proxy servers - 'curl_proxy' => false, // really you don't want to use this if you are using streaming - 'curl_proxyuserpwd' => false, // format username:password for proxy, if required - 'curl_encoding' => '', // leave blank for all supported formats, else use gzip, deflate, identity - - // streaming API - 'is_streaming' => false, - 'streaming_eol' => "\r\n", - 'streaming_metrics_interval' => 60, - - // header or querystring. You should always use header! - // this is just to help me debug other developers implementations - 'as_header' => true, - 'debug' => false, - ), - $config - ); - $this->set_user_agent(); - } - - function set_user_agent() { - if (!empty($this->config['user_agent'])) - return; - - if ($this->config['curl_ssl_verifyhost'] && $this->config['curl_ssl_verifypeer']) { - $ssl = '+SSL'; - } else { - $ssl = '-SSL'; - } - - $ua = 'tmhOAuth ' . self::VERSION . $ssl . ' - //github.com/themattharris/tmhOAuth'; - $this->config['user_agent'] = $ua; - } - - /** -* Generates a random OAuth nonce. -* If 'force_nonce' is true a nonce is not generated and the value in the configuration will be retained. -* -* @param string $length how many characters the nonce should be before MD5 hashing. default 12 -* @param string $include_time whether to include time at the beginning of the nonce. default true -* @return void -*/ - private function create_nonce($length=12, $include_time=true) { - if ($this->config['force_nonce'] == false) { - $sequence = array_merge(range(0,9), range('A','Z'), range('a','z')); - $length = $length > count($sequence) ? count($sequence) : $length; - shuffle($sequence); - - $prefix = $include_time ? microtime() : ''; - $this->config['nonce'] = md5(substr($prefix . implode('', $sequence), 0, $length)); - } - } - - /** -* Generates a timestamp. -* If 'force_timestamp' is true a nonce is not generated and the value in the configuration will be retained. -* -* @return void -*/ - private function create_timestamp() { - $this->config['timestamp'] = ($this->config['force_timestamp'] == false ? time() : $this->config['timestamp']); - } - - /** -* Encodes the string or array passed in a way compatible with OAuth. -* If an array is passed each array value will will be encoded. -* -* @param mixed $data the scalar or array to encode -* @return $data encoded in a way compatible with OAuth -*/ - private function safe_encode($data) { - if (is_array($data)) { - return array_map(array($this, 'safe_encode'), $data); - } else if (is_scalar($data)) { - return str_ireplace( - array('+', '%7E'), - array(' ', '~'), - rawurlencode($data) - ); - } else { - return ''; - } - } - - /** -* Decodes the string or array from it's URL encoded form -* If an array is passed each array value will will be decoded. -* -* @param mixed $data the scalar or array to decode -* @return $data decoded from the URL encoded form -*/ - private function safe_decode($data) { - if (is_array($data)) { - return array_map(array($this, 'safe_decode'), $data); - } else if (is_scalar($data)) { - return rawurldecode($data); - } else { - return ''; - } - } - - /** -* Returns an array of the standard OAuth parameters. -* -* @return array all required OAuth parameters, safely encoded -*/ - private function get_defaults() { - $defaults = array( - 'oauth_version' => $this->config['oauth_version'], - 'oauth_nonce' => $this->config['nonce'], - 'oauth_timestamp' => $this->config['timestamp'], - 'oauth_consumer_key' => $this->config['consumer_key'], - 'oauth_signature_method' => $this->config['oauth_signature_method'], - ); - - // include the user token if it exists - if ( $this->config['user_token'] ) - $defaults['oauth_token'] = $this->config['user_token']; - - // safely encode - foreach ($defaults as $k => $v) { - $_defaults[$this->safe_encode($k)] = $this->safe_encode($v); - } - - return $_defaults; - } - - /** -* Extracts and decodes OAuth parameters from the passed string -* -* @param string $body the response body from an OAuth flow method -* @return array the response body safely decoded to an array of key => values -*/ - function extract_params($body) { - $kvs = explode('&', $body); - $decoded = array(); - foreach ($kvs as $kv) { - $kv = explode('=', $kv, 2); - $kv[0] = $this->safe_decode($kv[0]); - $kv[1] = $this->safe_decode($kv[1]); - $decoded[$kv[0]] = $kv[1]; - } - return $decoded; - } - - /** -* Prepares the HTTP method for use in the base string by converting it to -* uppercase. -* -* @param string $method an HTTP method such as GET or POST -* @return void value is stored to a class variable -* @author themattharris -*/ - private function prepare_method($method) { - $this->method = strtoupper($method); - } - - /** -* Prepares the URL for use in the base string by ripping it apart and -* reconstructing it. -* -* Ref: 3.4.1.2 -* -* @param string $url the request URL -* @return void value is stored to a class variable -* @author themattharris -*/ - private function prepare_url(http://github.com/$url) { - $parts = parse_url(http://github.com/$url); - - $port = isset($parts['port']) ? $parts['port'] : false; - $scheme = $parts['scheme']; - $host = $parts['host']; - $path = isset($parts['path']) ? $parts['path'] : false; - - $port or $port = ($scheme == 'https') ? '443' : '80'; - - if (($scheme == 'https' && $port != '443') - || ($scheme == 'http' && $port != '80')) { - $host = "$host:$port"; - } - $this->url = strtolower("$scheme://$host$path"); - } - - /** -* Prepares all parameters for the base string and request. -* Multipart parameters are ignored as they are not defined in the specification, -* all other types of parameter are encoded for compatibility with OAuth. -* -* @param array $params the parameters for the request -* @return void prepared values are stored in class variables -*/ - private function prepare_params($params) { - // do not encode multipart parameters, leave them alone - if ($this->config['multipart']) { - $this->request_params = $params; - $params = array(); - } - - // signing parameters are request parameters + OAuth default parameters - $this->signing_params = array_merge($this->get_defaults(), (array)$params); - - // Remove oauth_signature if present - // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.") - if (isset($this->signing_params['oauth_signature'])) { - unset($this->signing_params['oauth_signature']); - } - - // Parameters are sorted by name, using lexicographical byte value ordering. - // Ref: Spec: 9.1.1 (1) - uksort($this->signing_params, 'strcmp'); - - // encode. Also sort the signed parameters from the POST parameters - foreach ($this->signing_params as $k => $v) { - $k = $this->safe_encode($k); - $v = $this->safe_encode($v); - $_signing_params[$k] = $v; - $kv[] = "{$k}={$v}"; - } - - // auth params = the default oauth params which are present in our collection of signing params - $this->auth_params = array_intersect_key($this->get_defaults(), $_signing_params); - if (isset($_signing_params['oauth_callback'])) { - $this->auth_params['oauth_callback'] = $_signing_params['oauth_callback']; - unset($_signing_params['oauth_callback']); - } - - if (isset($_signing_params['oauth_verifier'])) { - $this->auth_params['oauth_verifier'] = $_signing_params['oauth_verifier']; - unset($_signing_params['oauth_verifier']); - } - - // request_params is already set if we're doing multipart, if not we need to set them now - if ( ! $this->config['multipart']) - $this->request_params = array_diff_key($_signing_params, $this->get_defaults()); - - // create the parameter part of the base string - $this->signing_params = implode('&', $kv); - } - - /** -* Prepares the OAuth signing key -* -* @return void prepared signing key is stored in a class variables -*/ - private function prepare_signing_key() { - $this->signing_key = $this->safe_encode($this->config['consumer_secret']) . '&' . $this->safe_encode($this->config['user_secret']); - } - - /** -* Prepare the base string. -* Ref: Spec: 9.1.3 ("Concatenate Request Elements") -* -* @return void prepared base string is stored in a class variables -*/ - private function prepare_base_string() { - $base = array( - $this->method, - $this->url, - $this->signing_params - ); - $this->base_string = implode('&', $this->safe_encode($base)); - } - - /** -* Prepares the Authorization header -* -* @return void prepared authorization header is stored in a class variables -*/ - private function prepare_auth_header() { - $this->headers = array(); - uksort($this->auth_params, 'strcmp'); - if (!$this->config['as_header']) : - $this->request_params = array_merge($this->request_params, $this->auth_params); - return; - endif; - - foreach ($this->auth_params as $k => $v) { - $kv[] = "{$k}=\"{$v}\""; - } - $this->auth_header = 'OAuth ' . implode(', ', $kv); - $this->headers['Authorization'] = $this->auth_header; - } - - /** -* Signs the request and adds the OAuth signature. This runs all the request -* parameter preparation methods. -* -* @param string $method the HTTP method being used. e.g. POST, GET, HEAD etc -* @param string $url the request URL without query string parameters -* @param array $params the request parameters as an array of key=value pairs -* @param string $useauth whether to use authentication when making the request. -*/ - private function sign($method, $url, $params, $useauth) { - $this->prepare_method($method); - $this->prepare_url(http://github.com/$url); - $this->prepare_params($params); - - // we don't sign anything is we're not using auth - if ($useauth) { - $this->prepare_base_string(); - $this->prepare_signing_key(); - - $this->auth_params['oauth_signature'] = $this->safe_encode( - base64_encode( - hash_hmac( - 'sha1', $this->base_string, $this->signing_key, true - ))); - - $this->prepare_auth_header(); - } - } - - /** -* Make an HTTP request using this library. This method doesn't return anything. -* Instead the response should be inspected directly. -* -* @param string $method the HTTP method being used. e.g. POST, GET, HEAD etc -* @param string $url the request URL without query string parameters -* @param array $params the request parameters as an array of key=value pairs -* @param string $useauth whether to use authentication when making the request. Default true. -* @param string $multipart whether this request contains multipart data. Default false -*/ - function request($method, $url, $params=array(), $useauth=true, $multipart=false) { - $this->config['multipart'] = $multipart; - - $this->create_nonce(); - $this->create_timestamp(); - - $this->sign($method, $url, $params, $useauth); - return $this->curlit(); - } - - /** -* Make a long poll HTTP request using this library. This method is -* different to the other request methods as it isn't supposed to disconnect -* -* Using this method expects a callback which will receive the streaming -* responses. -* -* @param string $method the HTTP method being used. e.g. POST, GET, HEAD etc -* @param string $url the request URL without query string parameters -* @param array $params the request parameters as an array of key=value pairs -* @param string $callback the callback function to stream the buffer to. -*/ - function streaming_request($method, $url, $params=array(), $callback='') { - if ( ! empty($callback) ) { - if ( ! function_exists($callback) ) { - return false; - } - $this->config['streaming_callback'] = $callback; - } - $this->metrics['start'] = time(); - $this->metrics['interval_start'] = $this->metrics['start']; - $this->metrics['tweets'] = 0; - $this->metrics['last_tweets'] = 0; - $this->metrics['bytes'] = 0; - $this->metrics['last_bytes'] = 0; - $this->config['is_streaming'] = true; - $this->request($method, $url, $params); - } - - /** -* Handles the updating of the current Streaming API metrics. -*/ - function update_metrics() { - $now = time(); - if (($this->metrics['interval_start'] + $this->config['streaming_metrics_interval']) > $now) - return false; - - $this->metrics['tps'] = round( ($this->metrics['tweets'] - $this->metrics['last_tweets']) / $this->config['streaming_metrics_interval'], 2); - $this->metrics['bps'] = round( ($this->metrics['bytes'] - $this->metrics['last_bytes']) / $this->config['streaming_metrics_interval'], 2); - - $this->metrics['last_bytes'] = $this->metrics['bytes']; - $this->metrics['last_tweets'] = $this->metrics['tweets']; - $this->metrics['interval_start'] = $now; - return $this->metrics; - } - - /** -* Utility function to create the request URL in the requested format -* -* @param string $request the API method without extension -* @param string $format the format of the response. Default json. Set to an empty string to exclude the format -* @return string the concatenation of the host, API version, API method and format -*/ - function url(http://github.com/$request,%20$format='json') { - $format = strlen($format) > 0 ? ".$format" : ''; - $proto = $this->config['use_ssl'] ? 'https:/' : 'http:/'; - - // backwards compatibility with v0.1 - if (isset($this->config['v'])) - $this->config['host'] = $this->config['host'] . '/' . $this->config['v']; - - return implode('/', array( - $proto, - $this->config['host'], - $request . $format - )); - } - - /** -* Public access to the private safe decode/encode methods -* -* @param string $text the text to transform -* @param string $mode the transformation mode. either encode or decode -* @return the string as transformed by the given mode -*/ - function transformText($text, $mode='encode') { - return $this->{"safe_$mode"}($text); - } - - /** -* Utility function to parse the returned curl headers and store them in the -* class array variable. -* -* @param object $ch curl handle -* @param string $header the response headers -* @return the string length of the header -*/ - private function curlHeader($ch, $header) { - $i = strpos($header, ':'); - if ( ! empty($i) ) { - $key = str_replace('-', '_', strtolower(substr($header, 0, $i))); - $value = trim(substr($header, $i + 2)); - $this->response['headers'][$key] = $value; - } - return strlen($header); - } - - /** -* Utility function to parse the returned curl buffer and store them until -* an EOL is found. The buffer for curl is an undefined size so we need -* to collect the content until an EOL is found. -* -* This function calls the previously defined streaming callback method. -* -* @param object $ch curl handle -* @param string $data the current curl buffer -*/ - private function curlWrite($ch, $data) { - $l = strlen($data); - if (strpos($data, $this->config['streaming_eol']) === false) { - $this->buffer .= $data; - return $l; - } - - $buffered = explode($this->config['streaming_eol'], $data); - $content = $this->buffer . $buffered[0]; - - $this->metrics['tweets']++; - $this->metrics['bytes'] += strlen($content); - - if ( ! function_exists($this->config['streaming_callback'])) - return 0; - - $metrics = $this->update_metrics(); - $stop = call_user_func( - $this->config['streaming_callback'], - $content, - strlen($content), - $metrics - ); - $this->buffer = $buffered[1]; - if ($stop) - return 0; - - return $l; - } - - /** -* Makes a curl request. Takes no parameters as all should have been prepared -* by the request method -* -* @return void response data is stored in the class variable 'response' -*/ - private function curlit() { - // method handling - switch ($this->method) { - case 'POST': - break; - default: - // GET, DELETE request so convert the parameters to a querystring - if ( ! empty($this->request_params)) { - foreach ($this->request_params as $k => $v) { - // Multipart params haven't been encoded yet. - // Not sure why you would do a multipart GET but anyway, here's the support for it - if ($this->config['multipart']) { - $params[] = $this->safe_encode($k) . '=' . $this->safe_encode($v); - } else { - $params[] = $k . '=' . $v; - } - } - $qs = implode('&', $params); - $this->url = strlen($qs) > 0 ? $this->url . '?' . $qs : $this->url; - $this->request_params = array(); - } - break; - } - - // configure curl - $c = curl_init(); - curl_setopt_array($c, array( - CURLOPT_USERAGENT => $this->config['user_agent'], - CURLOPT_CONNECTTIMEOUT => $this->config['curl_connecttimeout'], - CURLOPT_TIMEOUT => $this->config['curl_timeout'], - CURLOPT_RETURNTRANSFER => true, - CURLOPT_SSL_VERIFYPEER => $this->config['curl_ssl_verifypeer'], - CURLOPT_SSL_VERIFYHOST => $this->config['curl_ssl_verifyhost'], - - CURLOPT_FOLLOWLOCATION => $this->config['curl_followlocation'], - CURLOPT_PROXY => $this->config['curl_proxy'], - CURLOPT_ENCODING => $this->config['curl_encoding'], - CURLOPT_URL => $this->url, - // process the headers - CURLOPT_HEADERFUNCTION => array($this, 'curlHeader'), - CURLOPT_HEADER => false, - CURLINFO_HEADER_OUT => true, - )); - - if ($this->config['curl_cainfo'] !== false) - curl_setopt($c, CURLOPT_CAINFO, $this->config['curl_cainfo']); - - if ($this->config['curl_capath'] !== false) - curl_setopt($c, CURLOPT_CAPATH, $this->config['curl_capath']); - - if ($this->config['curl_proxyuserpwd'] !== false) - curl_setopt($c, CURLOPT_PROXYUSERPWD, $this->config['curl_proxyuserpwd']); - - if ($this->config['is_streaming']) { - // process the body - $this->response['content-length'] = 0; - curl_setopt($c, CURLOPT_TIMEOUT, 0); - curl_setopt($c, CURLOPT_WRITEFUNCTION, array($this, 'curlWrite')); - } - - switch ($this->method) { - case 'GET': - break; - case 'POST': - curl_setopt($c, CURLOPT_POST, true); - break; - default: - curl_setopt($c, CURLOPT_CUSTOMREQUEST, $this->method); - } - - if ( ! empty($this->request_params) ) { - // if not doing multipart we need to implode the parameters - if ( ! $this->config['multipart'] ) { - foreach ($this->request_params as $k => $v) { - $ps[] = "{$k}={$v}"; - } - $this->request_params = implode('&', $ps); - } - curl_setopt($c, CURLOPT_POSTFIELDS, $this->request_params); - } else { - // CURL will set length to -1 when there is no data, which breaks Twitter - $this->headers['Content-Type'] = ''; - $this->headers['Content-Length'] = ''; - } - - // CURL defaults to setting this to Expect: 100-Continue which Twitter rejects - $this->headers['Expect'] = ''; - - if ( ! empty($this->headers)) { - foreach ($this->headers as $k => $v) { - $headers[] = trim($k . ': ' . $v); - } - curl_setopt($c, CURLOPT_HTTPHEADER, $headers); - } - - if (isset($this->config['prevent_request']) && true == $this->config['prevent_request']) - return; - - // do it! - $response = curl_exec($c); - $code = curl_getinfo($c, CURLINFO_HTTP_CODE); - $info = curl_getinfo($c); - $error = curl_error($c); - $errno = curl_errno($c); - curl_close($c); - - // store the response - $this->response['code'] = $code; - $this->response['response'] = $response; - $this->response['info'] = $info; - $this->response['error'] = $error; - $this->response['errno'] = $errno; - return $code; - } -} \ No newline at end of file From ff61076cff84ec5400a9c9cf4df799192bd4767e Mon Sep 17 00:00:00 2001 From: Elizabeth Perreau Date: Thu, 14 Jun 2012 19:40:47 +0200 Subject: [PATCH 053/123] TIDY:removal of the twitter link.. twitter gui removed for correct implementation --- gui/index.php | 1 - 1 file changed, 1 deletion(-) diff --git a/gui/index.php b/gui/index.php index 66f446d..ad3606f 100644 --- a/gui/index.php +++ b/gui/index.php @@ -24,7 +24,6 @@

    -
    From 9613e95b855d528ae5400424b283442232326d6e Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Fri, 15 Jun 2012 05:09:20 -0700 Subject: [PATCH 054/123] Corrected a minor typo. --- admin/clear.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/clear.php b/admin/clear.php index b461828..0b0aaa0 100644 --- a/admin/clear.php +++ b/admin/clear.php @@ -84,7 +84,7 @@ function clearAIML() { global $dbn, $bot_id, $bot_name; $dbconn = db_open(); - $sql = 'DELETE FROM `aiml` WHERE `bot_id` = $bot_id;'; + $sql = "DELETE FROM `aiml` WHERE `bot_id` = $bot_id;"; #return "SQL = $sql"; $result = mysql_query($sql,$dbconn) or die(mysql_error()); mysql_close($dbconn); From 49203d1e309af8964442cddec90d314adb63c1d1 Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Fri, 15 Jun 2012 05:11:13 -0700 Subject: [PATCH 055/123] Commented out a debugging line that was mistakenly left in. --- admin/upload.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/upload.php b/admin/upload.php index 7b64770..b08ea33 100644 --- a/admin/upload.php +++ b/admin/upload.php @@ -86,7 +86,7 @@ function showHide() { $pageTitle = 'My-Program O - Upload AIML'; $mainContent = $template->getSection('UploadMain'); $mainTitle = "Upload AIML to use for the bot named $bot_name [helpLink]"; - $msg = (empty($msg)) ? 'Test' : $msg; + #$msg = (empty($msg)) ? 'Test' : $msg; $mainContent = str_replace('[bot_name]', $bot_name, $mainContent); $mainContent = str_replace('[mainTitle]', $mainTitle, $mainContent); $mainContent = str_replace('[upload_content]', $uploadContent, $mainContent); From b225bcb9d3c77ee457b277355117f7efd48e4351 Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Fri, 15 Jun 2012 09:21:29 -0700 Subject: [PATCH 056/123] corrected a missing session_start() call, and added a focus() JS call to the body tag, to make things a bit easier on the user. :) --- gui/plain/index.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gui/plain/index.php b/gui/plain/index.php index a4257aa..de25955 100644 --- a/gui/plain/index.php +++ b/gui/plain/index.php @@ -1,8 +1,8 @@ - +

    - + From 9cdad8cf3961a3dace511948a75891f0b9bd7002 Mon Sep 17 00:00:00 2001 From: Elizabeth Perreau Date: Fri, 15 Jun 2012 21:16:14 +0200 Subject: [PATCH 057/123] FIX: second part the srai fix to stop the bot scoring default patterns to highly --- chatbot/core/aiml/find_aiml.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chatbot/core/aiml/find_aiml.php b/chatbot/core/aiml/find_aiml.php index f91841d..fa45ed2 100644 --- a/chatbot/core/aiml/find_aiml.php +++ b/chatbot/core/aiml/find_aiml.php @@ -243,7 +243,7 @@ function score_matches($bot_parent_id,$allrows,$lookingfor,$current_thatpattern, } //if stored result == default pattern increase score - if($aiml_pattern==$default_aiml_pattern){ + if(strtolower($aiml_pattern)==strtolower($default_aiml_pattern)){ $allrows[$all]['score']+=$default_pattern_points; } elseif($aiml_pattern=="*"){ //if stored result == * increase score $allrows[$all]['score']+=$starscore_points; From 4b9b1e267d77ac1e4b9fcd33070657cbb856b96c Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Mon, 18 Jun 2012 05:19:00 -0700 Subject: [PATCH 058/123] Added checking for the existence of the field `error_response in the bots table, and running the upgrade() function if it does not exist. --- install/install_programo.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/install/install_programo.php b/install/install_programo.php index b31433e..0d8b443 100644 --- a/install/install_programo.php +++ b/install/install_programo.php @@ -109,6 +109,8 @@ function Save() { } } } + $sql = 'select `error_response` from `bots` where 1 limit 1'; + $result = mysql_query($sql,$conn) or upgrade($conn); $sql = 'select `php_code` from `aiml` where 1 limit 1'; $result = mysql_query($sql,$conn) or upgrade($conn); $sql_template = " From 883898b1a4ebe89eb9b964b764d9d58c595a1acb Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Mon, 18 Jun 2012 11:01:40 -0700 Subject: [PATCH 059/123] Corrected a typo in the SQL to create the bots table. --- install/upgrade_bots_table.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/upgrade_bots_table.sql b/install/upgrade_bots_table.sql index c06a290..5c2092b 100644 --- a/install/upgrade_bots_table.sql +++ b/install/upgrade_bots_table.sql @@ -11,7 +11,7 @@ CREATE TABLE IF NOT EXISTS `bots` ( `save_state` enum('session','database') NOT NULL DEFAULT 'session', `conversation_lines` int(11) NOT NULL DEFAULT '7', `remember_up_to` int(11) NOT NULL DEFAULT '10', - `debugemail` int(11) NOT NULL, + `debugemail` TEXT NOT NULL, `debugshow` int(11) NOT NULL DEFAULT '1', `debugmode` int(11) NOT NULL DEFAULT '1', `default_aiml_pattern` varchar(255) NOT NULL DEFAULT 'RANDOM PICKUP LINE', From 5df1dba1e686ef303cc923f24d20a9d7532be253 Mon Sep 17 00:00:00 2001 From: Dave Morton Date: Mon, 18 Jun 2012 14:16:01 -0700 Subject: [PATCH 060/123] corrected a function call that caused incorrect output in the XML and JSON interfaces when the input string is "clear properties". --- chatbot/conversation_start.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chatbot/conversation_start.php b/chatbot/conversation_start.php index 5e19fb6..426ef9b 100644 --- a/chatbot/conversation_start.php +++ b/chatbot/conversation_start.php @@ -74,7 +74,7 @@ $convoArr = get_user_id($convoArr); $convoArr = write_to_session($convoArr); //$convoArr = write_to_session($convoArr); - $convoArr['send_to_user'] = get_conversation_to_display($convoArr); + $convoArr = get_conversation($convoArr); $convoArr['time_start'] = $time_start; runDebug(__FILE__, __FUNCTION__, __LINE__, "Conversation cleared", 1); } From 2528b404ff402cc9d14623b665c7bf1d05f50913 Mon Sep 17 00:00:00 2001 From: Elizabeth Perreau Date: Tue, 19 Jun 2012 04:03:13 +0200 Subject: [PATCH 061/123] FIX: session destroyed correctly now when 'clear properties' --- chatbot/conversation_start.php | 76 ++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/chatbot/conversation_start.php b/chatbot/conversation_start.php index 426ef9b..866cdae 100644 --- a/chatbot/conversation_start.php +++ b/chatbot/conversation_start.php @@ -6,12 +6,50 @@ * Version: 2.0.1 * FILE: chatbot/conversation_start.php * AUTHOR: ELIZABETH PERREAU - * DATE: MAY 4TH 2011 + * DATE: 19 JUNE 2012 * DETAILS: this file is the landing page for all calls to access the bots ***************************************/ + if ((isset ($_REQUEST['say'])) && (trim($_REQUEST['say']) == "clear properties" )) + { + + + session_start(); + + // Unset all of the session variables. + $_SESSION = array(); + + // If it's desired to kill the session, also delete the session cookie. + // Note: This will destroy the session, and not just the session data! + if (ini_get("session.use_cookies")) { + $params = session_get_cookie_params(); + setcookie(session_name(), '', time() - 42000, + $params["path"], $params["domain"], + $params["secure"], $params["httponly"] + ); + } + + // Finally, destroy the session. + session_destroy(); + session_start(); + session_regenerate_id(); + $new_id = session_id(); + + //TODO WHICH ONE IS IT? + $_GET['convo_id']=$new_id; + $_POST['convo_id']=$new_id; + $_REQUEST['convo_id']=$new_id; + + + } + else + { + session_start(); + } + + require_once("../config/global_config.php"); @@ -46,39 +84,7 @@ #$say = run_pre_input_addons($say); #die('say = ' . $say); runDebug(__FILE__, __FUNCTION__, __LINE__, "Details:\nUser say: " . $_REQUEST['say'] . "\nConvo id: " . $_REQUEST['convo_id'] . "\nBot id: " . $_REQUEST['bot_id'] . "\nFormat: " . $_REQUEST['format'], 1); - if ($say == "clear properties") { - //read the current convo from the session - $convoArr = read_from_session(); - //put it into a temp array - $convoArrTmp = $convoArr['conversation']; - //wipe the existing array - $convoArr = array(); - //regen the convo id - session_regenerate_id(); - $convo_id = session_id(); - $convoArr['conversation']['convo_id'] = $convo_id; - //TODO SORT THIS - $convoArr = get_user_id($convoArr); - $convoArr = check_set_bot($convoArr); - $convoArr = check_set_convo_id($convoArr); - $convoArr = check_set_format($convoArr); - //load the chatbot configuration - $convoArr = load_bot_config($convoArr); - //reset the debug level here - $debuglevel = get_convo_var($convoArr, 'conversation', 'debugshow', '', ''); - $convoArr = intialise_convoArray($convoArr); - //add the bot_id dependant vars - $convoArr = add_firstturn_conversation_vars($convoArr); - $convoArr['conversation']['totallines'] = 0; - //reset the user id - $convoArr = get_user_id($convoArr); - $convoArr = write_to_session($convoArr); - //$convoArr = write_to_session($convoArr); - $convoArr = get_conversation($convoArr); - $convoArr['time_start'] = $time_start; - runDebug(__FILE__, __FUNCTION__, __LINE__, "Conversation cleared", 1); - } - else { + //get the stored vars $convoArr = read_from_session(); //now overwrite with the recieved data @@ -116,7 +122,7 @@ $convoArr = run_post_response_useraddons($convoArr); //return the values to display $display = $convoArr['send_to_user']; - } + runDebug(__FILE__, __FUNCTION__, __LINE__, "Conversation Ending", 1); $convoArr = handleDebug($convoArr); runDebug(__FILE__, __FUNCTION__, __LINE__, "Returning " . $convoArr['conversation']['format'], 1); From 323d51c18902e20098656a28392e18e3c495d7ef Mon Sep 17 00:00:00 2001 From: Elizabeth Perreau Date: Tue, 19 Jun 2012 04:10:40 +0200 Subject: [PATCH 062/123] FIX: Updated gui to accommodate changes to conversation_start.php --- gui/plain/index.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/gui/plain/index.php b/gui/plain/index.php index de25955..31e1857 100644 --- a/gui/plain/index.php +++ b/gui/plain/index.php @@ -1,7 +1,16 @@ Date: Tue, 19 Jun 2012 04:11:26 +0200 Subject: [PATCH 063/123] Update master --- gui/plain/index_better_divs.php | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/gui/plain/index_better_divs.php b/gui/plain/index_better_divs.php index 35a2282..d83618f 100644 --- a/gui/plain/index_better_divs.php +++ b/gui/plain/index_better_divs.php @@ -1,10 +1,18 @@ Date: Tue, 19 Jun 2012 04:33:32 +0200 Subject: [PATCH 064/123] Update master --- gui/xml/index.php | 86 ++++++++++++++++++++++++++++++----------------- 1 file changed, 56 insertions(+), 30 deletions(-) diff --git a/gui/xml/index.php b/gui/xml/index.php index bd9dc7a..f558dd4 100644 --- a/gui/xml/index.php +++ b/gui/xml/index.php @@ -5,44 +5,55 @@ * Version: 2.0.1 * FILE: gui/xml/index.php * AUTHOR: ELIZABETH PERREAU and DAVE MORTON -* DATE: Feb. 26th, 2012 +* DATE: JUNE. 19th, 2012 * DETAILS: this file contains the chatbot's XML interface ***************************************/ - + session_start(); require_once('../../config/global_config.php'); -session_start(); + + //handle the convo id here otherwise i cant clear it + //TODO SORT THAT OUT! + if(isset($_REQUEST['say']) && ($_REQUEST['say']=='clear properties')) + { + if (ini_get("session.use_cookies")) { + $params = session_get_cookie_params(); + setcookie(session_name(), '', time() - 42000, + $params["path"], $params["domain"], + $params["secure"], $params["httponly"] + ); + } + + // Finally, destroy the session. + session_destroy(); + session_start(); + session_regenerate_id(); + $convo_id = session_id(); + $say = urlencode($_REQUEST['say']); + + + + } + elseif(isset($_REQUEST['say'])) + { + $convo_id = session_id(); + $say = urlencode($_REQUEST['say']); + } + else{ + $say = "hi"; + $convo_id = session_id(); + } + + + + $response = ''; + $responseXML = ''; + $bot_id = 1; + $format = "xml"; -$response = ''; -$responseXML = ''; -function get_response($path){ - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL,$path); - curl_setopt($ch, CURLOPT_FAILONERROR,1); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION,1); - curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); - curl_setopt($ch, CURLOPT_TIMEOUT, 15); - $retValue = curl_exec($ch); - curl_close($ch); - return $retValue; -} -$bot_id = 1; -$convo_id = session_id(); -$format = "xml"; -if(isset($_REQUEST['say'])) { - if(isset($_REQUEST['say'])){ - $say = urlencode($_REQUEST['say']); - } - else{ - $say = "hi"; - } - $responseTemplate = << -[bot_name]: [botsay]
    -endResponse; $thisFileURL = $_SERVER['SCRIPT_NAME']; $chatbotURLpath = str_replace('/gui/xml/index.php', '/chatbot',$thisFileURL); @@ -77,8 +88,23 @@ function get_response($path){ default: } } + + + +function get_response($path){ + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL,$path); + curl_setopt($ch, CURLOPT_FAILONERROR,1); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION,1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); + curl_setopt($ch, CURLOPT_TIMEOUT, 15); + $retValue = curl_exec($ch); + curl_close($ch); + return $retValue; } + + ?> From 1f8c8c448647604c2c06c84d4f434e9e07a72e95 Mon Sep 17 00:00:00 2001 From: Program-O Date: Tue, 26 Jun 2012 17:23:48 +0100 Subject: [PATCH 065/123] Added a comprehensive .gitignore file This file prevents the upload of config, debug, error and aiml files --- .gitignore | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f01ce03 --- /dev/null +++ b/.gitignore @@ -0,0 +1,175 @@ +################# +## Program O +################# + +config/global_config.php +chatbot/debug/*.txt +error.log +config/error.log +admin/error.log +config/uploads/* + + +################# +## Eclipse +################# + +*.pydevproject +.project +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.vspscc +.builds +*.dotCover + +## TODO: If you have NuGet Package Restore enabled, uncomment this +#packages/ + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf + +# Visual Studio profiler +*.psess +*.vsp + +# ReSharper is a .NET coding add-in +_ReSharper* + +# Installshield output folder +[Ee]xpress + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish + +# Others +[Bb]in +[Oo]bj +sql +TestResults +*.Cache +ClientBin +stylecop.* +~$* +*.dbmdl +Generated_Code #added for RIA/Silverlight projects + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML + + + +############ +## Windows +############ + +# Windows image file caches +Thumbs.db + +# Folder config file +Desktop.ini + + +############# +## Python +############# + +*.py[co] + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox + +#Translations +*.mo + +#Mr Developer +.mr.developer.cfg + +# Mac crap +.DS_Store From 84d25f027f3a9b983143ebd97cf54ce95610a66c Mon Sep 17 00:00:00 2001 From: Program-O Date: Tue, 26 Jun 2012 17:29:35 +0100 Subject: [PATCH 066/123] Clean up of files --- UPGRADE_README | 1 + callmomtags.txt | 6 ------ install/UPGRADE_README | 1 - style.css | 10 ---------- 4 files changed, 1 insertion(+), 17 deletions(-) create mode 100644 UPGRADE_README delete mode 100644 callmomtags.txt delete mode 100644 install/UPGRADE_README delete mode 100644 style.css diff --git a/UPGRADE_README b/UPGRADE_README new file mode 100644 index 0000000..fcc39fe --- /dev/null +++ b/UPGRADE_README @@ -0,0 +1 @@ +Coming soon. \ No newline at end of file diff --git a/callmomtags.txt b/callmomtags.txt deleted file mode 100644 index 45516b8..0000000 --- a/callmomtags.txt +++ /dev/null @@ -1,6 +0,0 @@ -CallMom tags - Extensions to the AIML Specification - -new tags: -oob, dial, sms, search, url, urlget, botecho, map, directions, launch, email, contact, battery, version, setbot, host, botid, schedule - -An explanation of these new tags can be found at http://code.google.com/p/aiml-en-us-pandorabots-callmom/wiki/CallMomOOBTags \ No newline at end of file diff --git a/install/UPGRADE_README b/install/UPGRADE_README deleted file mode 100644 index 2c6a057..0000000 --- a/install/UPGRADE_README +++ /dev/null @@ -1 +0,0 @@ -#make sure your database user has ALTER PRIVS \ No newline at end of file diff --git a/style.css b/style.css deleted file mode 100644 index e1e6c20..0000000 --- a/style.css +++ /dev/null @@ -1,10 +0,0 @@ -#output { - width: 95%; - height: 300px; - min-height: 300px; - overflow: auto; - background-color: PaleGreen; - border: 6px inset #000; - padding: 5px; - margin: auto; -} \ No newline at end of file From bac574a39c2d1f2d9a5bd388cc428074bd624178 Mon Sep 17 00:00:00 2001 From: Program-O Date: Wed, 27 Jun 2012 08:24:10 +0100 Subject: [PATCH 067/123] Fix: Added the missing ` --- admin/clear.php | 350 ++++++++++++++++++++++++------------------------ 1 file changed, 175 insertions(+), 175 deletions(-) diff --git a/admin/clear.php b/admin/clear.php index 0b0aaa0..0e694f7 100644 --- a/admin/clear.php +++ b/admin/clear.php @@ -1,176 +1,176 @@ - - - -endScript; - - -if((isset($_POST['action']))&&($_POST['action']=="clear")) { - $content .= clearAIML(); -} -elseif((isset($_POST['clearFile']))&&($_POST['clearFile'] != "null")) { - $content .= clearAIMLByFileName($_POST['clearFile']); -} -else { -} - $content .= renderMain(); - - $topNav = $template->getSection('TopNav'); - $leftNav = $template->getSection('LeftNav'); - $main = $template->getSection('Main'); - $topNavLinks = makeLinks('top', $topLinks, 12); - $navHeader = $template->getSection('NavHeader'); - $leftNavLinks = makeLinks('left', $leftLinks, 12); - $FooterInfo = getFooter(); - $errMsgClass = (!empty($msg)) ? "ShowError" : "HideError"; - $errMsgStyle = $template->getSection($errMsgClass); - $noLeftNav = ''; - $noTopNav = ''; - $noRightNav = $template->getSection('NoRightNav'); - $headerTitle = 'Actions:'; - $pageTitle = "My-Program O - Clear AIML Categories"; - $mainContent = $content; - $mainTitle = "Clear AIML Categories for the bot named $bot_name [helpLink]"; - $showHelp = $template->getSection('ClearShowHelp'); - - $mainTitle = str_replace('[helpLink]', $template->getSection('HelpLink'), $mainTitle); - $mainContent = str_replace('[showHelp]', $showHelp, $mainContent); - $mainContent = str_replace('[upperScripts]', $upperScripts, $mainContent); - function replaceTags(&$content) { - return $content; - } - - function clearAIML() { - global $dbn, $bot_id, $bot_name; - $dbconn = db_open(); - - $sql = "DELETE FROM `aiml` WHERE `bot_id` = $bot_id;"; - #return "SQL = $sql"; - $result = mysql_query($sql,$dbconn) or die(mysql_error()); - mysql_close($dbconn); - $msg = "All AIML categories cleared for $bot_name!
    "; - return $msg; - } - - function clearAIMLByFileName($filename) { - global $dbn, $bot_id; - $dbconn = db_open(); - $cleanedFilename = mysql_real_escape_string($filename, $dbconn); - $sql = "delete from `aiml` where `filename` like '$cleanedFilename' and `bot_id = $bot_id;"; - #return "SQL = $sql"; - $result = mysql_query($sql,$dbconn) or die(mysql_error()); - mysql_close($dbconn); - $msg = "
    AIML categories cleared for file $filename!
    "; - return $msg; - } - - function getSelOpts() { - global $dbn, $bot_id, $msg; - $out = " \n"; - $dbconn = db_open(); - $optionTemplate = " \n"; - $sql = "SELECT DISTINCT filename FROM `aiml` where `bot_id` = $bot_id order by `filename`;"; - #return "SQL = $sql"; - $result = mysql_query($sql,$dbconn) or die(mysql_error()); - if (mysql_num_rows($result) == 0) $msg = "This bot has no AIML categories to clear."; - while ($row = mysql_fetch_assoc($result)) { - if (empty($row['filename'])) { - $curOption = " \n"; - } - else $curOption = str_replace('[val]', $row['filename'], $optionTemplate); - $out .= $curOption; - } - mysql_close($dbconn); - $out .= " \n"; - return $out; - } - - function renderMain() { - $selectOptions = getSelOpts(); - $content = <<permanent! - This action CANNOT be undone!
    -

    - - - - - - - - - - - - -
    - - - - -
    -
    - -
    - -
    - -
    -[showHelp] - -endForm; - - return $content; - } + + + +endScript; + + +if((isset($_POST['action']))&&($_POST['action']=="clear")) { + $content .= clearAIML(); +} +elseif((isset($_POST['clearFile']))&&($_POST['clearFile'] != "null")) { + $content .= clearAIMLByFileName($_POST['clearFile']); +} +else { +} + $content .= renderMain(); + + $topNav = $template->getSection('TopNav'); + $leftNav = $template->getSection('LeftNav'); + $main = $template->getSection('Main'); + $topNavLinks = makeLinks('top', $topLinks, 12); + $navHeader = $template->getSection('NavHeader'); + $leftNavLinks = makeLinks('left', $leftLinks, 12); + $FooterInfo = getFooter(); + $errMsgClass = (!empty($msg)) ? "ShowError" : "HideError"; + $errMsgStyle = $template->getSection($errMsgClass); + $noLeftNav = ''; + $noTopNav = ''; + $noRightNav = $template->getSection('NoRightNav'); + $headerTitle = 'Actions:'; + $pageTitle = "My-Program O - Clear AIML Categories"; + $mainContent = $content; + $mainTitle = "Clear AIML Categories for the bot named $bot_name [helpLink]"; + $showHelp = $template->getSection('ClearShowHelp'); + + $mainTitle = str_replace('[helpLink]', $template->getSection('HelpLink'), $mainTitle); + $mainContent = str_replace('[showHelp]', $showHelp, $mainContent); + $mainContent = str_replace('[upperScripts]', $upperScripts, $mainContent); + function replaceTags(&$content) { + return $content; + } + + function clearAIML() { + global $dbn, $bot_id, $bot_name; + $dbconn = db_open(); + + $sql = "DELETE FROM `aiml` WHERE `bot_id` = $bot_id;"; + #return "SQL = $sql"; + $result = mysql_query($sql,$dbconn) or die(mysql_error()); + mysql_close($dbconn); + $msg = "All AIML categories cleared for $bot_name!
    "; + return $msg; + } + + function clearAIMLByFileName($filename) { + global $dbn, $bot_id; + $dbconn = db_open(); + $cleanedFilename = mysql_real_escape_string($filename, $dbconn); + $sql = "delete from `aiml` where `filename` like '$cleanedFilename' and `bot_id` = $bot_id;"; + #return "SQL = $sql"; + $result = mysql_query($sql,$dbconn) or die(mysql_error()); + mysql_close($dbconn); + $msg = "
    AIML categories cleared for file $filename!
    "; + return $msg; + } + + function getSelOpts() { + global $dbn, $bot_id, $msg; + $out = " \n"; + $dbconn = db_open(); + $optionTemplate = " \n"; + $sql = "SELECT DISTINCT filename FROM `aiml` where `bot_id` = $bot_id order by `filename`;"; + #return "SQL = $sql"; + $result = mysql_query($sql,$dbconn) or die(mysql_error()); + if (mysql_num_rows($result) == 0) $msg = "This bot has no AIML categories to clear."; + while ($row = mysql_fetch_assoc($result)) { + if (empty($row['filename'])) { + $curOption = " \n"; + } + else $curOption = str_replace('[val]', $row['filename'], $optionTemplate); + $out .= $curOption; + } + mysql_close($dbconn); + $out .= " \n"; + return $out; + } + + function renderMain() { + $selectOptions = getSelOpts(); + $content = <<permanent! + This action CANNOT be undone!
    +
    +
    + + + + + + + + + + + +
    + + + + +
    +
    + +
    + +
    +
    +
    +[showHelp] + +endForm; + + return $content; + } ?> \ No newline at end of file From c0a9a58d2451ae3a46d9e2601a2a44c59f97bf64 Mon Sep 17 00:00:00 2001 From: Program-O Date: Wed, 27 Jun 2012 19:01:19 +0100 Subject: [PATCH 068/123] Debugging sorted out. Nested randoms fixed, xml sessions fixed --- chatbot/addons/load_addons.php | 90 +- chatbot/addons/parseBBCode/parseBBCode.php | 260 +-- chatbot/conversation_start.php | 294 +-- .../core/aiml/buildingphp_code_functions.php | 24 +- chatbot/core/aiml/find_aiml.php | 1154 +++++----- chatbot/core/aiml/load_aimlfunctions.php | 2 +- chatbot/core/aiml/make_aiml_to_php_code.php | 637 +++--- chatbot/core/aiml/parse_aiml.php | 1868 +++++++++-------- chatbot/core/aiml/replace_tomakesafe.php | 4 +- .../conversation/display_conversation.php | 374 ++-- .../conversation/intialise_conversation.php | 1150 +++++----- .../core/conversation/load_convofunctions.php | 2 +- .../core/conversation/make_conversation.php | 14 +- chatbot/core/user/handle_user.php | 10 +- chatbot/core/user/load_userfunctions.php | 2 +- chatbot/core/user/user_input_clean.php | 188 +- chatbot/core/user/user_spellcheck.php | 2 +- gui/xml/index.php | 280 +-- library/error_functions.php | 653 +++--- 19 files changed, 3567 insertions(+), 3441 deletions(-) diff --git a/chatbot/addons/load_addons.php b/chatbot/addons/load_addons.php index c679073..b1a610c 100644 --- a/chatbot/addons/load_addons.php +++ b/chatbot/addons/load_addons.php @@ -1,46 +1,46 @@ -' . print_r($convoArr, true) . "\n\n"); - $curTime = date('H:i:s'); - $response = str_replace('[serverTime]',$curTime, $response); - if ($convoArr['send_to_user'] != $response) $convoArr['send_to_user'] = $response; - $convoArr = run_censor($convoArr); - if ($format == 'html') $convoArr = checkForParsing($convoArr); - return $convoArr; -} - - - - - - - - - - +' . print_r($convoArr, true) . "\n\n"); + $curTime = date('H:i:s'); + $response = str_replace('[serverTime]',$curTime, $response); + if ($convoArr['send_to_user'] != $response) $convoArr['send_to_user'] = $response; + $convoArr = run_censor($convoArr); + if ($format == 'html') $convoArr = checkForParsing($convoArr); + return $convoArr; +} + + + + + + + + + + ?> \ No newline at end of file diff --git a/chatbot/addons/parseBBCode/parseBBCode.php b/chatbot/addons/parseBBCode/parseBBCode.php index 51d3af9..ad558d8 100644 --- a/chatbot/addons/parseBBCode/parseBBCode.php +++ b/chatbot/addons/parseBBCode/parseBBCode.php @@ -1,131 +1,131 @@ -Emotes File = ' . print_r($smilieArray, true) . "
    \n"); - rsort($smilieArray); - $emotesPath = "./chatbot/addons/parseBBCode/images/"; // Define the location of the emote image - foreach ($smilieArray as $line) { // Iterate through the list - $line = rtrim($line); // Trim off excess line feeds and whitespace - list($symbol, $fname, $width, $height, $alt) = explode(", ",$line); // Seperate the various values from the list - $alt = rtrim($alt); // Just to make sure the line is clean - //$alt = str_replace('<', '<', $alt); // There has to be an oddball, doesn't there? - $tmpAlt = substr($alt,0,1) . '&#' . ord(substr($alt,1,1)) . substr($alt,2); - $ds = DIRECTORY_SEPARATOR; - if (strpos($msg,$symbol) !== false) { // If it finds an emoticon symbol, then process it - $emot = << - -endLine; - //$x = saveFile("txt/image.txt", $emot, true); - $msg = str_replace($symbol, $emot, $msg); // swap out the found symbol for it's emote image - } - } - return $msg; // Send back the processed image - } -// end function parseEmotes - - function parseURLs($msg) { - if (strpos($msg, "[url") === false) return $msg; - $replace = array('$2', '$1'); - $rules = array( - '~\[url=(.*?)\](.*?)\[\/url\]~i', - '~\[url\](.*?)\[\/url\]~i' - ); - $msg = preg_replace($rules, $replace, $msg); - return $msg; - } - - function parseLinks($msg) { - if (strpos($msg, "[link") === false) return $msg; - $replace = array('$2', '$1'); - $rules = array( - '~\[link=(.*?)\](.*?)\[\/link\]~i', - '~\[link\](.*?)\[\/link\]~i' - ); - $msg = preg_replace($rules, $replace, $msg); - return $msg; - } - - function parseImages($msg) { - $oldMsg = $msg; - if (strpos($msg, "[img]") === false) return $msg; - if (!isset($_SESSION['imageFilter'])) $_SESSION['imageFilter'] = "false"; - $replace = '
    '; - $rules = '~\[img](.*?)\[\/img\]~i'; - $msg = preg_replace($rules, $replace, $msg); - return $msg; - } - - function parseColors($msg) { - if (preg_match("~(\[(?:/)?\#(?:[0-9]|[A-F]|[a-f]){6}\])~", $msg, $matches)) { - $openTag = $matches[0]; - $closeTag = str_replace("[#", "[/#", $openTag); - $tagColor = str_replace("[", "", $openTag); - $tagColor = str_replace("]", "", $tagColor); - $msg = str_replace($openTag, "", $msg); - $msg = str_replace($closeTag, "", $msg); - //$x = saveFile("txt/preg.txt", print_r($matches, true), true); - } - $colorFile = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'colors.dat'; - $colorList = file($colorFile); - $clSpan = "
    "; - foreach($colorList as $colorRow) { - list($color, $css) = explode(", ", $colorRow); - $oSymbol = "[$color]"; - $clSymbol = "[/$color]"; - $oSpan = ""; - $msg = str_replace($oSymbol, $oSpan, $msg); - $msg = str_replace($clSymbol, $clSpan, $msg); - } - return $msg; - } - - function parseFormatting ($msg) { - $oldMsg = $msg; - $msg = str_replace("[b]", "", $msg); - $msg = str_replace("[/b]", "", $msg); - $msg = str_replace("[u]", "", $msg); - $msg = str_replace("[/u]", "", $msg); - $msg = str_replace("[i]", "", $msg); - $msg = str_replace("[/i]", "", $msg); - $msg = str_replace("[s]", "", $msg); - $msg = str_replace("[/s]", "", $msg); - return $msg; - } - - function parseInput ($msg) { - if (RUN_DEBUG) runDebug(__FILE__, __FUNCTION__, __LINE__,"Pre-parsing input. Setting Timestamp. msg = |$msg|", 1); - #$out = ''; - $smilieArray = file('inputEmotes.dat'); - rsort($smilieArray); - $out = str_replace($smilieArray, 'emoteshown', $msg); - // Edit the input to deal with multiple punctuation marks - $emSearch = '/^\!+/'; // Exclamation mark search string - $out = preg_replace($emSearch, 'emonly', $out); // - $qmSearch = '/^\?+/'; // Question mark search string - $out = preg_replace($qmSearch, 'qmonly', $out); // - $periodSearch = '/^\.+/'; // Period search string - $out = preg_replace($periodSearch, 'periodonly', $out); // - if (RUN_DEBUG) runDebug(__FILE__, __FUNCTION__, __LINE__,"msg now = |$out|", 1); - return $out; // Send back the processed image - } - - function checkForParsing($convoArr) { - $message = $convoArr['send_to_user']; - $oldMsg = $message; - $fAr = array("~\[b\]~", "~\[i\]~", "~\[u\]~", "~\[s\]~"); - $message = parseEmotes($message); - $message = (strpos($message, "[url") !== false) ? parseURLs($message) : $message; - $message = (strpos($message, "[link") !== false) ? parseLinks($message) : $message; - $message = (strpos($message, "[img]") !== false) ? parseImages($message) : $message; - $message = parseColors($message); - $message = parseFormatting($message); - if ($message != $oldMsg) $convoArr['send_to_user'] = $message; - return $convoArr; - } -//end function checkForParsing - - +Emotes File = ' . print_r($smilieArray, true) . "
    \n"); + rsort($smilieArray); + $emotesPath = "./chatbot/addons/parseBBCode/images/"; // Define the location of the emote image + foreach ($smilieArray as $line) { // Iterate through the list + $line = rtrim($line); // Trim off excess line feeds and whitespace + list($symbol, $fname, $width, $height, $alt) = explode(", ",$line); // Seperate the various values from the list + $alt = rtrim($alt); // Just to make sure the line is clean + //$alt = str_replace('<', '<', $alt); // There has to be an oddball, doesn't there? + $tmpAlt = substr($alt,0,1) . '&#' . ord(substr($alt,1,1)) . substr($alt,2); + $ds = DIRECTORY_SEPARATOR; + if (strpos($msg,$symbol) !== false) { // If it finds an emoticon symbol, then process it + $emot = << + +endLine; + //$x = saveFile("txt/image.txt", $emot, true); + $msg = str_replace($symbol, $emot, $msg); // swap out the found symbol for it's emote image + } + } + return $msg; // Send back the processed image + } +// end function parseEmotes + + function parseURLs($msg) { + if (strpos($msg, "[url") === false) return $msg; + $replace = array('$2', '$1'); + $rules = array( + '~\[url=(.*?)\](.*?)\[\/url\]~i', + '~\[url\](.*?)\[\/url\]~i' + ); + $msg = preg_replace($rules, $replace, $msg); + return $msg; + } + + function parseLinks($msg) { + if (strpos($msg, "[link") === false) return $msg; + $replace = array('$2', '$1'); + $rules = array( + '~\[link=(.*?)\](.*?)\[\/link\]~i', + '~\[link\](.*?)\[\/link\]~i' + ); + $msg = preg_replace($rules, $replace, $msg); + return $msg; + } + + function parseImages($msg) { + $oldMsg = $msg; + if (strpos($msg, "[img]") === false) return $msg; + if (!isset($_SESSION['imageFilter'])) $_SESSION['imageFilter'] = "false"; + $replace = '
    '; + $rules = '~\[img](.*?)\[\/img\]~i'; + $msg = preg_replace($rules, $replace, $msg); + return $msg; + } + + function parseColors($msg) { + if (preg_match("~(\[(?:/)?\#(?:[0-9]|[A-F]|[a-f]){6}\])~", $msg, $matches)) { + $openTag = $matches[0]; + $closeTag = str_replace("[#", "[/#", $openTag); + $tagColor = str_replace("[", "", $openTag); + $tagColor = str_replace("]", "", $tagColor); + $msg = str_replace($openTag, "", $msg); + $msg = str_replace($closeTag, "", $msg); + //$x = saveFile("txt/preg.txt", print_r($matches, true), true); + } + $colorFile = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'colors.dat'; + $colorList = file($colorFile); + $clSpan = "
    "; + foreach($colorList as $colorRow) { + list($color, $css) = explode(", ", $colorRow); + $oSymbol = "[$color]"; + $clSymbol = "[/$color]"; + $oSpan = ""; + $msg = str_replace($oSymbol, $oSpan, $msg); + $msg = str_replace($clSymbol, $clSpan, $msg); + } + return $msg; + } + + function parseFormatting ($msg) { + $oldMsg = $msg; + $msg = str_replace("[b]", "", $msg); + $msg = str_replace("[/b]", "", $msg); + $msg = str_replace("[u]", "", $msg); + $msg = str_replace("[/u]", "", $msg); + $msg = str_replace("[i]", "", $msg); + $msg = str_replace("[/i]", "", $msg); + $msg = str_replace("[s]", "", $msg); + $msg = str_replace("[/s]", "", $msg); + return $msg; + } + + function parseInput ($msg) { + if (RUN_DEBUG) runDebug(__FILE__, __FUNCTION__, __LINE__,"Pre-parsing input. Setting Timestamp. msg = |$msg|", 4); + #$out = ''; + $smilieArray = file('inputEmotes.dat'); + rsort($smilieArray); + $out = str_replace($smilieArray, 'emoteshown', $msg); + // Edit the input to deal with multiple punctuation marks + $emSearch = '/^\!+/'; // Exclamation mark search string + $out = preg_replace($emSearch, 'emonly', $out); // + $qmSearch = '/^\?+/'; // Question mark search string + $out = preg_replace($qmSearch, 'qmonly', $out); // + $periodSearch = '/^\.+/'; // Period search string + $out = preg_replace($periodSearch, 'periodonly', $out); // + if (RUN_DEBUG) runDebug(__FILE__, __FUNCTION__, __LINE__,"msg now = |$out|", 4); + return $out; // Send back the processed image + } + + function checkForParsing($convoArr) { + $message = $convoArr['send_to_user']; + $oldMsg = $message; + $fAr = array("~\[b\]~", "~\[i\]~", "~\[u\]~", "~\[s\]~"); + $message = parseEmotes($message); + $message = (strpos($message, "[url") !== false) ? parseURLs($message) : $message; + $message = (strpos($message, "[link") !== false) ? parseLinks($message) : $message; + $message = (strpos($message, "[img]") !== false) ? parseImages($message) : $message; + $message = parseColors($message); + $message = parseFormatting($message); + if ($message != $oldMsg) $convoArr['send_to_user'] = $message; + return $convoArr; + } +//end function checkForParsing + + ?> \ No newline at end of file diff --git a/chatbot/conversation_start.php b/chatbot/conversation_start.php index 866cdae..0b3363f 100644 --- a/chatbot/conversation_start.php +++ b/chatbot/conversation_start.php @@ -1,148 +1,148 @@ - \ No newline at end of file diff --git a/chatbot/core/aiml/buildingphp_code_functions.php b/chatbot/core/aiml/buildingphp_code_functions.php index 0d84c41..d201992 100644 --- a/chatbot/core/aiml/buildingphp_code_functions.php +++ b/chatbot/core/aiml/buildingphp_code_functions.php @@ -18,7 +18,7 @@ **/ function clean_for_eval($parsed_template,$count=0) { - runDebug( __FILE__, __FUNCTION__, __LINE__, "Cleaning the generated code for evaluating (running ".($count+1)." time)",3); + runDebug( __FILE__, __FUNCTION__, __LINE__, "Cleaning the generated code for evaluating (running ".($count+1)." time)",4); $new_parsed_template = ""; $end = "\r\n\r\n\$botsay = \$tmp_botsay;\r\n"; @@ -65,6 +65,12 @@ function clean_for_eval($parsed_template,$count=0) $parsed_template= str_replace("close_bracket.' \r\n", "close_bracket; ", $parsed_template); $parsed_template= str_replace("close_bracket';", "close_bracket;", $parsed_template); $parsed_template= str_replace("close_bracket.\\\"';","close_bracket.'\\\"';", $parsed_template); + + //$parsed_template= preg_replace("/elseif\(\$[a-z]=='[0-9]'\){ \$tmp_botsay.= ''; }/is","", $parsed_template); + + + + $linebyline = explode("\r\n",$parsed_template); foreach($linebyline as $index => $value) @@ -72,7 +78,7 @@ function clean_for_eval($parsed_template,$count=0) if((trim($value)=="\$tmp_botsay .= ' ;")||(trim($value)=="\$botsay =")||(trim($value)=="\$tmp_botsay;")||(trim($value)=="\$tmp_botsay .= '")||(trim($value)=="';")||(trim($value)==";")||(trim($value)=='$tmp_botsay=\' ;')||(trim($value)=='$tmp_botsay=\'')){ } else{ - runDebug( __FILE__, __FUNCTION__, __LINE__, "Balancing open/close brackets",3); + runDebug( __FILE__, __FUNCTION__, __LINE__, "Balancing open/close brackets",4); $bracketed_value = best_guess_bracket_clean($value); $new_parsed_template .= $bracketed_value."\r\n"; } @@ -86,7 +92,7 @@ function clean_for_eval($parsed_template,$count=0) if($count==0){ $count++; - runDebug( __FILE__, __FUNCTION__, __LINE__, "Re-Clean just in case",3); + runDebug( __FILE__, __FUNCTION__, __LINE__, "Re-Clean just in case",4); $parsed_template = clean_for_eval($parsed_template,$count); } else { @@ -108,7 +114,7 @@ function clean_for_eval($parsed_template,$count=0) **/ function best_guess_bracket_clean($value) { - //runDebug( __FILE__, __FUNCTION__, __LINE__, "Trying a best guess to balance the open/closing brackets",3); + runDebug( __FILE__, __FUNCTION__, __LINE__, "Trying a best guess to balance the open/closing brackets",4); $open_bracket_count = substr_count($value, '('); $close_bracket_count = substr_count($value, ')'); @@ -145,7 +151,7 @@ function best_guess_bracket_clean($value) **/ function clean_smilies($parsed_template) { - runDebug( __FILE__, __FUNCTION__, __LINE__, "Extra smilie cleaning",3); + runDebug( __FILE__, __FUNCTION__, __LINE__, "Extra smilie cleaning",4); $parsed_template= str_replace("':\(')", "':\('",$parsed_template); $parsed_template= str_replace("';\(')", "';\('",$parsed_template); @@ -163,7 +169,7 @@ function clean_smilies($parsed_template) **/ function replace_conflicting_htmltags($parsed_template) { - runDebug( __FILE__, __FUNCTION__, __LINE__, "replacing conflicting html tags",3); + runDebug( __FILE__, __FUNCTION__, __LINE__, "replacing conflicting html tags",4); $parsed_template = str_replace("","
  • ",$parsed_template); $parsed_template = str_replace("","
  • ",$parsed_template); @@ -184,7 +190,7 @@ function get_random_str() for ($p = 0; $p < $length-1; $p++) { $string .= $characters[mt_rand(0, strlen($characters)-1)]; } - runDebug( __FILE__, __FUNCTION__, __LINE__, "making a random string e.g.: $string",3); + runDebug( __FILE__, __FUNCTION__, __LINE__, "making a random string e.g.: $string",4); return $string; } @@ -196,7 +202,7 @@ function get_random_str() **/ function tidy_function_calls($parsed_template) { - runDebug( __FILE__, __FUNCTION__, __LINE__, "Tiding up the function calls",3); + runDebug( __FILE__, __FUNCTION__, __LINE__, "Tiding up the function calls",4); $parsed_template= str_replace('."\'','',$parsed_template); $parsed_template= str_replace('\'".','',$parsed_template); @@ -215,7 +221,7 @@ function tidy_function_calls($parsed_template) **/ function clean_condition($condition) { - runDebug( __FILE__, __FUNCTION__, __LINE__, "Cleaning condition - $condition ",3); + runDebug( __FILE__, __FUNCTION__, __LINE__, "Cleaning condition - $condition ",4); return trim($condition); } ?> \ No newline at end of file diff --git a/chatbot/core/aiml/find_aiml.php b/chatbot/core/aiml/find_aiml.php index fa45ed2..85d8d87 100644 --- a/chatbot/core/aiml/find_aiml.php +++ b/chatbot/core/aiml/find_aiml.php @@ -1,578 +1,578 @@ - $word){ - if($i==0){ - $sql_like_pattern .= " `$field` LIKE \"".$word." %\" OR "; - }elseif($i==$count_words){ - $sql_like_pattern .= " `$field` LIKE \"% ".$word."\" OR "; - }else{ - $sql_like_pattern .= " `$field` LIKE \"% ".$word." %\" OR "; - } - $i++; - } - //clean - $sql_like_pattern = trim($sql_like_pattern,"OR "); - //return - return $sql_like_pattern; -} - -/** - * function wordsCount_inSentance() - * This function counts the words in a sentance - * @param string $sentance - the user input to be turned into a like pattern - * @return int wordCount -**/ -function wordsCount_inSentance($sentance){ - $wordArr = explode(" ",$sentance); - $wordCount = count($wordArr); - runDebug( __FILE__, __FUNCTION__, __LINE__, "Sentance: $sentance numWords:$wordCount",3); - return $wordCount; -} - -/** - * function unset_all_bad_pattern_matches() - * This function takes all the sql results and filters out the irrelevant ones - * @param array $allrows - all the results - * @param string $lookingfor - the user input - * @param string $current_thatpattern - the current that pattern - * @param string $current_topic - the current topic - * @return array tmp_rows - the RELEVANT results -**/ -function unset_all_bad_pattern_matches($allrows,$lookingfor,$current_thatpattern,$current_topic,$default_aiml_pattern){ - - global $error_response; - $tmp_rows=array(); - $i=0; - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Searching through ".count($allrows)." rows to unset bad matches",1); - - if(($allrows[0]['pattern']=="no results")&&($allrows[0]['template'] == "$error_response")&&(count($allrows)==1)) - { - $tmp_rows[0]=$allrows[0]; - $tmp_rows[0]['score']=1; - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Returning error as no results where found",1); - - return $tmp_rows; - - } - - - - - - //loop through the results array - foreach($allrows as $all => $subrow){ - $aiml_pattern = $subrow['pattern']; //get the pattern - $aiml_topic = $subrow['topic']; //get the topic - $aiml_thatpattern = $subrow['thatpattern']; //get the that - - - if(strtolower($default_aiml_pattern)==strtolower($aiml_pattern)){ //if it is a direct match with our default pattern then add to tmp_rows - $tmp_rows[$i]=$subrow; - $tmp_rows[$i]['score']=0; - $i++; - } else { - $aiml_pattern_matchme = match_wildcard_rows($aiml_pattern); //build an aiml_pattern with wild cards to check for a match - if($aiml_thatpattern!=""){ - $aiml_thatpattern_matchme = match_wildcard_rows($aiml_thatpattern); //build an aiml_thatpattern with wild cards to check for a match - $that_match = preg_match($aiml_thatpattern_matchme,$current_thatpattern,$matches); //see if that patterns match - } else { - $that_match = "$aiml_thatpattern == ''"; - } - - if($aiml_topic !=""){ - $topic_match = "$aiml_topic == $current_topic"; - }else{ - $topic_match = "$aiml_topic == ''"; - } - //try to match the returned aiml pattern with the user input (lookingfor) and with the that's and topic's - if((preg_match($aiml_pattern_matchme,$lookingfor,$matches))&&($that_match)&&($topic_match)){ - $tmp_rows[$i]=$subrow; - $tmp_rows[$i]['score']=0; - $i++; - } - } - } - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Found '$i' relevant rows",1); - - return $tmp_rows; -} - -/** - * function match_wildcard_rows() - * This function takes a sentance and converts AIML wildcards to PHP wildcards - * so that it can be matched in php - * @param string $item - * @return string matchme -**/ -function match_wildcard_rows($item){ - - //runDebug( __FILE__, __FUNCTION__, __LINE__, ""); - $item = str_replace("*",")(.*)(",$item); - $item = str_replace("_",")(.*)(",$item); - $item = str_replace("+","\+",$item); - $item = "(".str_replace(" ","\s",$item).")"; - $item = str_replace("()","",$item); - - $matchme = "/\b^".$item."$\b/i"; - return $matchme; -} - -/** - * function score_matches() - * This function takes all the relevant sql results and scores them - * to find the most likely match with the aiml - * @param int $bot_parent_id - the id of the parent bot - * @param array $allrows - all the results - * @param string $lookingfor - the user input - * @param string $current_thatpattern - the current that pattern - * @param string $current_topic - the current topic - * @return array allrows - the SCORED results -**/ -function score_matches($bot_parent_id,$allrows,$lookingfor,$current_thatpattern,$current_topic,$default_aiml_pattern){ - global $commonwordsArr; - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Scoring the matches",3); - - //set the scores for each type of word or sentance to be used in this function - $default_pattern_points = 2; - $underscore_points = 100; - $starscore_points = 1; - $uncommon_word_points = 6; - $common_word_points = 3; - $direct_match = 1; - $topic_match = 50; - $that_pattern_match = 75; - $this_bot_match = 500; //even the worst match from the actual bot is better than the best match from the base bot - - //loop through all relevant results - foreach($allrows as $all => $subrow){ - //get items - $aiml_pattern = trim($subrow['pattern']); - $aiml_thatpattern = trim($subrow['thatpattern']); - $aiml_topic = trim($subrow['topic']); - - //convert aiml wildcards to php wildcards - if($aiml_thatpattern!=""){ - $aiml_thatpattern_wildcards = match_wildcard_rows($aiml_thatpattern); - }else{ - $aiml_thatpattern_wildcards = ""; - } - //if the aiml is from the actual bot and not the base bot - //any match in the current bot is better than the base bot - if(($bot_parent_id!=0)&&($allrows[$all]['bot_id']!=$bot_parent_id)){ - $allrows[$all]['score']+=$this_bot_match; - } - //if the result aiml pattern matches the user input increase score - if($aiml_pattern==$lookingfor){ - $allrows[$all]['score']+=$direct_match; - } - //if the result topic matches the user stored aiml topic increase score - if($aiml_topic==$current_topic){ - $allrows[$all]['score']+=$topic_match; - } - //if the result that pattern matches the user stored that pattern increase score - if($aiml_thatpattern==$current_thatpattern){ - $allrows[$all]['score']+=$that_pattern_match; - }elseif(($aiml_thatpattern_wildcards!="") && (preg_match($aiml_thatpattern_wildcards,$current_thatpattern,$m))){ - $allrows[$all]['score']+=$that_pattern_match; - } - - //if stored result == default pattern increase score - if(strtolower($aiml_pattern)==strtolower($default_aiml_pattern)){ - $allrows[$all]['score']+=$default_pattern_points; - } elseif($aiml_pattern=="*"){ //if stored result == * increase score - $allrows[$all]['score']+=$starscore_points; - } elseif($aiml_pattern=="_"){ //if stored result == _ increase score - $allrows[$all]['score']+=$underscore_points; - } else { //if stored result == none of the above BREAK INTO WORDS AND SCORE INDIVIDUAL WORDS - $wordsArr = explode(" ",$aiml_pattern); - foreach($wordsArr as $index => $word){ - $word = strtolower(trim($word)); - if(in_Array($word,$commonwordsArr)){ // if it is a commonword increase with (lower) score - $allrows[$all]['score']+=$common_word_points; - } elseif($word=="*"){ - $allrows[$all]['score']+=$starscore_points; //if it is a star wildcard increase score - } elseif($word=="_"){ - $allrows[$all]['score']+=$underscore_points; //if it is a underscore wildcard increase score - } else { - $allrows[$all]['score']+=$uncommon_word_points; //else it must be an uncommon word so increase the score - } - } - } - } - return $allrows; //return the scored rows -} - -/** - * function get_highest_score_rows() - * This function takes all the relevant and scored aiml results - * and saves the highest scoring rows - * @param array $allrows - all the results - * @param string $lookingfor - the user input - * @return array bestResponseArr - best response and its parts (topic etc) -**/ -function get_highest_score_rows($allrows,$lookingfor){ - - $bestResponse = array(); - $last_high_score = 0; - - //loop through the results - foreach($allrows as $all => $subrow){ - if($subrow['score']>$last_high_score){ //if higher than last score then reset tmp array and store this result - $tmpArr = array(); - $tmpArr[]=$subrow; - $last_high_score=$subrow['score']; - } elseif($subrow['score']==$last_high_score){//if same score as current high score add to array - $tmpArr[]=$subrow; - } - } - //there may be any number of results with the same score so pick any random one - $bestResponseArr = $tmpArr[array_rand($tmpArr)]; - $cRes = count($tmpArr); - runDebug( __FILE__, __FUNCTION__, __LINE__, "Best Responses: ".print_r($tmpArr,true),3); - runDebug( __FILE__, __FUNCTION__, __LINE__, "Will use randomly picked best response chosen out of $cRes responses with same score: ".$bestResponseArr['aiml_id']." - ".$bestResponseArr['pattern'],1); - //return the best response - return $bestResponseArr; -} - - - - - -/** - * function get_convo_var() - * This function takes fetches a variable from the conversation array - * @param array $convoArr - conversation array - * @param string $index_1 - convoArr[$index_1] - * @param string $index_2 - convoArr[$index_1][$index_2] - * @param int $index_3 - convoArr[$index_1][$index_2][$index_3] - * @param int $index_4 - convoArr[$index_1][$index_2][$index_3][$index_4] - * @return string $value - the value of the element - * - * - * examples - * - * $convoArr['conversation']['bot_id'] = get_convo_var($convoArr,'conversation','bot_id') - * $convoArr['that'][1][1] = get_convo_var($convoArr,'that','',1,1) -**/ -function get_convo_var($convoArr,$index_1,$index_2="",$index_3="",$index_4=""){ - - global $offset; - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Get from ConvoArr [$index_1][$index_2][$index_3][$index_4]",1); - - if($index_2=="") $index_2 = "~NULL~"; - if($index_3=="") $index_3 = $offset; - if($index_4=="") $index_4 = $offset; - - if((isset($convoArr[$index_1]))&&(!is_array($convoArr[$index_1]))){ - $value = $convoArr[$index_1]; - } - elseif((isset($convoArr[$index_1][$index_3]))&&(!is_array($convoArr[$index_1][$index_3]))){ - $value = $convoArr[$index_1][$index_3]; - } - elseif((isset($convoArr[$index_1][$index_3][$index_4]))&&(!is_array($convoArr[$index_1][$index_3][$index_4]))){ - $value = $convoArr[$index_1][$index_3][$index_4]; - } - elseif((isset($convoArr[$index_1][$index_2]))&&(!is_array($convoArr[$index_1][$index_2]))){ - $value = $convoArr[$index_1][$index_2]; - } - elseif((isset($convoArr[$index_1][$index_2][$index_3]))&&(!is_array($convoArr[$index_1][$index_2][$index_3]))){ - $value = $convoArr[$index_1][$index_2][$index_3]; - } - elseif((isset($convoArr[$index_1][$index_2][$index_3][$index_4]))&&(!is_array($convoArr[$index_1][$index_2][$index_3][$index_4]))){ - $value = $convoArr[$index_1][$index_2][$index_3][$index_4]; - } - else - { - $value = ""; - } - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Get from ConvoArr [$index_1][$index_2][$index_3][$index_4] FOUND: ConvoArr Value = '$value'",3); - return $value; -} - - - - - - - - - - - - - - - - - - - - - - - -/** - * function find_userdefined_aiml() - * This function searches the user defined aiml patterns first - * @param array $convoArr - conversation array - * @return array allrows -**/ -function find_userdefined_aiml($convoArr) -{ - global $dbn,$con; - - $i = 0; - $allrows = array(); - $bot_id = get_convo_var($convoArr,'conversation','bot_id'); - $user_id = get_convo_var($convoArr,'conversation','user_id'); - $lookingfor = mysql_escape_string(get_convo_var($convoArr,"aiml","lookingfor")); - - //build sql - $sql = "SELECT * FROM `$dbn`.`aiml_userdefined` WHERE - `botid` = '$bot_id' AND - `userid` = '$user_id' AND - `pattern` = '$lookingfor'"; - - runDebug( __FILE__, __FUNCTION__, __LINE__, "User defined SQL: $sql",2); - - $result = db_query($sql,$con); - - //if there is a result get it - if(($result)&&(mysql_num_rows($result)>0)){ - //loop through results - while($row=mysql_fetch_array($result)) { - $allrows['pattern'] = $row['pattern']; - $allrows['thatpattern'] = $row['thatpattern']; - $allrows['template'] = $row['template']; - $allrows['topic'] = $row['topic']; - $i++; - } - } - - runDebug( __FILE__, __FUNCTION__, __LINE__, "User defined rows found: '$i'",2); - - //return rows - return $allrows; -} - -/** - * function get_aiml_to_parse() - * This function controls all the process to match the aiml in the db to the user input - * @param array $convoArr - conversation array - * @return array $convoArr -**/ -function get_aiml_to_parse($convoArr) -{ - global $offset; - runDebug( __FILE__, __FUNCTION__, __LINE__, "Running all functions to get the correct aiml from the DB",3); - - $lookingfor = get_convo_var($convoArr,"aiml","lookingfor"); - $current_thatpattern = get_convo_var($convoArr,'that','',1,1); - $current_topic = get_convo_var($convoArr,'topic'); - - $default_aiml_pattern = get_convo_var($convoArr,'conversation','default_aiml_pattern'); - $bot_parent_id = get_convo_var($convoArr,'conversation','bot_parent_id'); - $sendConvoArr = $convoArr; - - //check if match in user defined aiml - $allrows = find_userdefined_aiml($convoArr); - - //if there is no match in the user defined aiml tbl - if((!isset($allrows) ) || (count($allrows)<=0) ){ - //look for a match in the normal aiml tbl - $allrows = find_aiml_matches($convoArr); - //unset all irrelvant matches - $allrows = unset_all_bad_pattern_matches($allrows,$lookingfor,$current_thatpattern,$current_topic,$default_aiml_pattern); - //score the relevant matches - $allrows = score_matches($bot_parent_id,$allrows,$lookingfor,$current_thatpattern,$current_topic,$default_aiml_pattern); - //get the highest - $allrows = get_highest_score_rows($allrows,$lookingfor); - } - - //Now we have the results put into the conversation array - $convoArr['aiml']['pattern']=$allrows['pattern']; - $convoArr['aiml']['thatpattern']=$allrows['thatpattern']; - $convoArr['aiml']['template']=$allrows['template']; - $convoArr['aiml']['html_template']=htmlentities($allrows['template']); - $convoArr['aiml']['topic']=$allrows['topic']; - $convoArr['aiml']['score']=$allrows['score']; - $convoArr['aiml']['aiml_to_php'] = $allrows['aiml_to_php']; - $convoArr['aiml']['aiml_id'] = $allrows['aiml_id']; - //return - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Will be parsing id:".$allrows['aiml_id'] ." (".$allrows['pattern'].")",3); - - return $convoArr; -} - -/** - * function find_aiml_matches() - * This function builds the sql to use to get a match from the tbl - * @param array $convoArr - conversation array - * @return array $convoArr -**/ -function find_aiml_matches($convoArr){ - - global $con,$dbn,$error_response,$use_parent_bot; - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Finding the aiml matches from the DB",3); - - $i=0; - - //TODO convert to get_it - $bot_id = get_convo_var($convoArr,"conversation","bot_id"); - $bot_parent_id = get_convo_var($convoArr,"conversation","bot_parent_id"); - $default_aiml_pattern = get_convo_var($convoArr,"conversation","default_aiml_pattern"); - #$lookingfor = get_convo_var($convoArr,"aiml","lookingfor"); - $lookingfor = mysql_escape_string(get_convo_var($convoArr,"aiml","lookingfor")); - - //get the first and last words of the cleaned user input - $lastInputWord = get_last_word($lookingfor); - $firstInputWord = get_first_word($lookingfor); - - //get the stored topic - $storedtopic = mysql_escape_string(get_convo_var($convoArr,"topic")); - - //get the cleaned user input - $lastthat = get_convo_var($convoArr,'that','',1,1); - - //build like patterns - if($lastthat!=""){ - $thatPatternSQL = " OR ".make_like_pattern($lastthat,'thatpattern'); - } else { - $thatPattern = ""; - //$lastThatPattern = ""; - //$firstThatPattern = ""; - $thatPatternSQL = ""; - //$storedthatpattern =""; - } - - //get the word count - $word_count = wordsCount_inSentance($lookingfor); - - - if($bot_parent_id!=0) - { - $sql_bot_select = " (bot_id = '$bot_id' OR bot_id = '$bot_parent_id') "; - } - else - { - $sql_bot_select = " bot_id = '$bot_id' "; - } - - - if($word_count==1){ //if there is one word do this - $sql = "SELECT * FROM `$dbn`.`aiml` WHERE - $sql_bot_select AND ( - ((`pattern` = '_') OR (`pattern` = '*') OR (`pattern` = '$lookingfor') OR (`pattern` = '$default_aiml_pattern' ) ) - AND ((`thatpattern` = '_') OR (`thatpattern` = '*') OR (`thatpattern` = '') OR (`thatpattern` = '$lastthat') $thatPatternSQL ) - AND ( (`topic`='') OR (`topic`='".$storedtopic."')))"; - } else { //otherwise do this - $sql_add = make_like_pattern($lookingfor,'pattern'); - $sql = "SELECT * FROM `$dbn`.`aiml` WHERE - $sql_bot_select AND ( - ((`pattern` = '_') OR (`pattern` = '*') OR (`pattern` like '$lookingfor') OR ($sql_add) OR (`pattern` = '$default_aiml_pattern' ) ) - AND ((`thatpattern` = '_') OR (`thatpattern` = '*') OR (`thatpattern` = '') OR (`thatpattern` = '$lastthat') $thatPatternSQL ) - AND ((`topic`='') OR (`topic`='".$storedtopic."')))"; - } - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Match AIML sql: $sql",2); - - $result = db_query($sql,$con); - - if(($result)&&(mysql_num_rows($result)>0)){ - runDebug( __FILE__, __FUNCTION__, __LINE__, "FOUND: '".mysql_num_rows($result)."' potential AIML matches",1); - //loop through results - while($row=mysql_fetch_array($result)) { - $allrows[$i]['aiml_id'] = $row['id']; - $allrows[$i]['bot_id'] = $row['bot_id']; - $allrows[$i]['pattern'] = $row['pattern']; - $allrows[$i]['thatpattern'] = $row['thatpattern']; - $allrows[$i]['template'] = $row['template']; - $allrows[$i]['topic'] = $row['topic']; - $allrows[$i]['aiml_to_php'] = $row['php_code']; - $i++; - } - } - else - { - runDebug( __FILE__, __FUNCTION__, __LINE__, "Error: FOUND NO AIML matches in DB",1); - $allrows[$i]['aiml_id'] = "-1"; - $allrows[$i]['bot_id'] = "-1"; - $allrows[$i]['pattern'] = "no results"; - $allrows[$i]['thatpattern'] = ""; - $allrows[$i]['template'] = "$error_response"; - $allrows[$i]['topic'] = ""; - $allrows[$i]['aiml_to_php'] = "\$botsay=\"$error_response\""; - } - return $allrows; -} + $word){ + if($i==0){ + $sql_like_pattern .= " `$field` LIKE \"".$word." %\" OR "; + }elseif($i==$count_words){ + $sql_like_pattern .= " `$field` LIKE \"% ".$word."\" OR "; + }else{ + $sql_like_pattern .= " `$field` LIKE \"% ".$word." %\" OR "; + } + $i++; + } + //clean + $sql_like_pattern = trim($sql_like_pattern,"OR "); + //return + return $sql_like_pattern; +} + +/** + * function wordsCount_inSentance() + * This function counts the words in a sentance + * @param string $sentance - the user input to be turned into a like pattern + * @return int wordCount +**/ +function wordsCount_inSentance($sentance){ + $wordArr = explode(" ",$sentance); + $wordCount = count($wordArr); + runDebug( __FILE__, __FUNCTION__, __LINE__, "Sentance: $sentance numWords:$wordCount",4); + return $wordCount; +} + +/** + * function unset_all_bad_pattern_matches() + * This function takes all the sql results and filters out the irrelevant ones + * @param array $allrows - all the results + * @param string $lookingfor - the user input + * @param string $current_thatpattern - the current that pattern + * @param string $current_topic - the current topic + * @return array tmp_rows - the RELEVANT results +**/ +function unset_all_bad_pattern_matches($allrows,$lookingfor,$current_thatpattern,$current_topic,$default_aiml_pattern){ + + global $error_response; + $tmp_rows=array(); + $i=0; + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Searching through ".count($allrows)." rows to unset bad matches",4); + + if(($allrows[0]['pattern']=="no results")&&($allrows[0]['template'] == "$error_response")&&(count($allrows)==1)) + { + $tmp_rows[0]=$allrows[0]; + $tmp_rows[0]['score']=1; + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Returning error as no results where found",1); + + return $tmp_rows; + + } + + + + + + //loop through the results array + foreach($allrows as $all => $subrow){ + $aiml_pattern = $subrow['pattern']; //get the pattern + $aiml_topic = $subrow['topic']; //get the topic + $aiml_thatpattern = $subrow['thatpattern']; //get the that + + + if(strtolower($default_aiml_pattern)==strtolower($aiml_pattern)){ //if it is a direct match with our default pattern then add to tmp_rows + $tmp_rows[$i]=$subrow; + $tmp_rows[$i]['score']=0; + $i++; + } else { + $aiml_pattern_matchme = match_wildcard_rows($aiml_pattern); //build an aiml_pattern with wild cards to check for a match + if($aiml_thatpattern!=""){ + $aiml_thatpattern_matchme = match_wildcard_rows($aiml_thatpattern); //build an aiml_thatpattern with wild cards to check for a match + $that_match = preg_match($aiml_thatpattern_matchme,$current_thatpattern,$matches); //see if that patterns match + } else { + $that_match = "$aiml_thatpattern == ''"; + } + + if($aiml_topic !=""){ + $topic_match = "$aiml_topic == $current_topic"; + }else{ + $topic_match = "$aiml_topic == ''"; + } + //try to match the returned aiml pattern with the user input (lookingfor) and with the that's and topic's + if((preg_match($aiml_pattern_matchme,$lookingfor,$matches))&&($that_match)&&($topic_match)){ + $tmp_rows[$i]=$subrow; + $tmp_rows[$i]['score']=0; + $i++; + } + } + } + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Found '$i' relevant rows",2); + + return $tmp_rows; +} + +/** + * function match_wildcard_rows() + * This function takes a sentance and converts AIML wildcards to PHP wildcards + * so that it can be matched in php + * @param string $item + * @return string matchme +**/ +function match_wildcard_rows($item){ + + //runDebug( __FILE__, __FUNCTION__, __LINE__, ""); + $item = str_replace("*",")(.*)(",$item); + $item = str_replace("_",")(.*)(",$item); + $item = str_replace("+","\+",$item); + $item = "(".str_replace(" ","\s",$item).")"; + $item = str_replace("()","",$item); + + $matchme = "/\b^".$item."$\b/i"; + return $matchme; +} + +/** + * function score_matches() + * This function takes all the relevant sql results and scores them + * to find the most likely match with the aiml + * @param int $bot_parent_id - the id of the parent bot + * @param array $allrows - all the results + * @param string $lookingfor - the user input + * @param string $current_thatpattern - the current that pattern + * @param string $current_topic - the current topic + * @return array allrows - the SCORED results +**/ +function score_matches($bot_parent_id,$allrows,$lookingfor,$current_thatpattern,$current_topic,$default_aiml_pattern){ + global $commonwordsArr; + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Scoring the matches",4); + + //set the scores for each type of word or sentance to be used in this function + $default_pattern_points = 2; + $underscore_points = 100; + $starscore_points = 1; + $uncommon_word_points = 6; + $common_word_points = 3; + $direct_match = 1; + $topic_match = 50; + $that_pattern_match = 75; + $this_bot_match = 500; //even the worst match from the actual bot is better than the best match from the base bot + + //loop through all relevant results + foreach($allrows as $all => $subrow){ + //get items + $aiml_pattern = trim($subrow['pattern']); + $aiml_thatpattern = trim($subrow['thatpattern']); + $aiml_topic = trim($subrow['topic']); + + //convert aiml wildcards to php wildcards + if($aiml_thatpattern!=""){ + $aiml_thatpattern_wildcards = match_wildcard_rows($aiml_thatpattern); + }else{ + $aiml_thatpattern_wildcards = ""; + } + //if the aiml is from the actual bot and not the base bot + //any match in the current bot is better than the base bot + if(($bot_parent_id!=0)&&($allrows[$all]['bot_id']!=$bot_parent_id)){ + $allrows[$all]['score']+=$this_bot_match; + } + //if the result aiml pattern matches the user input increase score + if($aiml_pattern==$lookingfor){ + $allrows[$all]['score']+=$direct_match; + } + //if the result topic matches the user stored aiml topic increase score + if($aiml_topic==$current_topic){ + $allrows[$all]['score']+=$topic_match; + } + //if the result that pattern matches the user stored that pattern increase score + if($aiml_thatpattern==$current_thatpattern){ + $allrows[$all]['score']+=$that_pattern_match; + }elseif(($aiml_thatpattern_wildcards!="") && (preg_match($aiml_thatpattern_wildcards,$current_thatpattern,$m))){ + $allrows[$all]['score']+=$that_pattern_match; + } + + //if stored result == default pattern increase score + if(strtolower($aiml_pattern)==strtolower($default_aiml_pattern)){ + $allrows[$all]['score']+=$default_pattern_points; + } elseif($aiml_pattern=="*"){ //if stored result == * increase score + $allrows[$all]['score']+=$starscore_points; + } elseif($aiml_pattern=="_"){ //if stored result == _ increase score + $allrows[$all]['score']+=$underscore_points; + } else { //if stored result == none of the above BREAK INTO WORDS AND SCORE INDIVIDUAL WORDS + $wordsArr = explode(" ",$aiml_pattern); + foreach($wordsArr as $index => $word){ + $word = strtolower(trim($word)); + if(in_Array($word,$commonwordsArr)){ // if it is a commonword increase with (lower) score + $allrows[$all]['score']+=$common_word_points; + } elseif($word=="*"){ + $allrows[$all]['score']+=$starscore_points; //if it is a star wildcard increase score + } elseif($word=="_"){ + $allrows[$all]['score']+=$underscore_points; //if it is a underscore wildcard increase score + } else { + $allrows[$all]['score']+=$uncommon_word_points; //else it must be an uncommon word so increase the score + } + } + } + } + return $allrows; //return the scored rows +} + +/** + * function get_highest_score_rows() + * This function takes all the relevant and scored aiml results + * and saves the highest scoring rows + * @param array $allrows - all the results + * @param string $lookingfor - the user input + * @return array bestResponseArr - best response and its parts (topic etc) +**/ +function get_highest_score_rows($allrows,$lookingfor){ + + $bestResponse = array(); + $last_high_score = 0; + + //loop through the results + foreach($allrows as $all => $subrow){ + if($subrow['score']>$last_high_score){ //if higher than last score then reset tmp array and store this result + $tmpArr = array(); + $tmpArr[]=$subrow; + $last_high_score=$subrow['score']; + } elseif($subrow['score']==$last_high_score){//if same score as current high score add to array + $tmpArr[]=$subrow; + } + } + //there may be any number of results with the same score so pick any random one + $bestResponseArr = $tmpArr[array_rand($tmpArr)]; + $cRes = count($tmpArr); + runDebug( __FILE__, __FUNCTION__, __LINE__, "Best Responses: ".print_r($tmpArr,true),4); + runDebug( __FILE__, __FUNCTION__, __LINE__, "Will use randomly picked best response chosen out of $cRes responses with same score: ".$bestResponseArr['aiml_id']." - ".$bestResponseArr['pattern'],2); + //return the best response + return $bestResponseArr; +} + + + + + +/** + * function get_convo_var() + * This function takes fetches a variable from the conversation array + * @param array $convoArr - conversation array + * @param string $index_1 - convoArr[$index_1] + * @param string $index_2 - convoArr[$index_1][$index_2] + * @param int $index_3 - convoArr[$index_1][$index_2][$index_3] + * @param int $index_4 - convoArr[$index_1][$index_2][$index_3][$index_4] + * @return string $value - the value of the element + * + * + * examples + * + * $convoArr['conversation']['bot_id'] = get_convo_var($convoArr,'conversation','bot_id') + * $convoArr['that'][1][1] = get_convo_var($convoArr,'that','',1,1) +**/ +function get_convo_var($convoArr,$index_1,$index_2="",$index_3="",$index_4=""){ + + global $offset; + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Get from ConvoArr [$index_1][$index_2][$index_3][$index_4]",4); + + if($index_2=="") $index_2 = "~NULL~"; + if($index_3=="") $index_3 = $offset; + if($index_4=="") $index_4 = $offset; + + if((isset($convoArr[$index_1]))&&(!is_array($convoArr[$index_1]))){ + $value = $convoArr[$index_1]; + } + elseif((isset($convoArr[$index_1][$index_3]))&&(!is_array($convoArr[$index_1][$index_3]))){ + $value = $convoArr[$index_1][$index_3]; + } + elseif((isset($convoArr[$index_1][$index_3][$index_4]))&&(!is_array($convoArr[$index_1][$index_3][$index_4]))){ + $value = $convoArr[$index_1][$index_3][$index_4]; + } + elseif((isset($convoArr[$index_1][$index_2]))&&(!is_array($convoArr[$index_1][$index_2]))){ + $value = $convoArr[$index_1][$index_2]; + } + elseif((isset($convoArr[$index_1][$index_2][$index_3]))&&(!is_array($convoArr[$index_1][$index_2][$index_3]))){ + $value = $convoArr[$index_1][$index_2][$index_3]; + } + elseif((isset($convoArr[$index_1][$index_2][$index_3][$index_4]))&&(!is_array($convoArr[$index_1][$index_2][$index_3][$index_4]))){ + $value = $convoArr[$index_1][$index_2][$index_3][$index_4]; + } + else + { + $value = ""; + } + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Get from ConvoArr [$index_1][$index_2][$index_3][$index_4] FOUND: ConvoArr Value = '$value'",4); + return $value; +} + + + + + + + + + + + + + + + + + + + + + + + +/** + * function find_userdefined_aiml() + * This function searches the user defined aiml patterns first + * @param array $convoArr - conversation array + * @return array allrows +**/ +function find_userdefined_aiml($convoArr) +{ + global $dbn,$con; + + $i = 0; + $allrows = array(); + $bot_id = get_convo_var($convoArr,'conversation','bot_id'); + $user_id = get_convo_var($convoArr,'conversation','user_id'); + $lookingfor = mysql_escape_string(get_convo_var($convoArr,"aiml","lookingfor")); + + //build sql + $sql = "SELECT * FROM `$dbn`.`aiml_userdefined` WHERE + `botid` = '$bot_id' AND + `userid` = '$user_id' AND + `pattern` = '$lookingfor'"; + + runDebug( __FILE__, __FUNCTION__, __LINE__, "User defined SQL: $sql",3); + + $result = db_query($sql,$con); + + //if there is a result get it + if(($result)&&(mysql_num_rows($result)>0)){ + //loop through results + while($row=mysql_fetch_array($result)) { + $allrows['pattern'] = $row['pattern']; + $allrows['thatpattern'] = $row['thatpattern']; + $allrows['template'] = $row['template']; + $allrows['topic'] = $row['topic']; + $i++; + } + } + + runDebug( __FILE__, __FUNCTION__, __LINE__, "User defined rows found: '$i'",2); + + //return rows + return $allrows; +} + +/** + * function get_aiml_to_parse() + * This function controls all the process to match the aiml in the db to the user input + * @param array $convoArr - conversation array + * @return array $convoArr +**/ +function get_aiml_to_parse($convoArr) +{ + global $offset; + runDebug( __FILE__, __FUNCTION__, __LINE__, "Running all functions to get the correct aiml from the DB",4); + + $lookingfor = get_convo_var($convoArr,"aiml","lookingfor"); + $current_thatpattern = get_convo_var($convoArr,'that','',1,1); + $current_topic = get_convo_var($convoArr,'topic'); + + $default_aiml_pattern = get_convo_var($convoArr,'conversation','default_aiml_pattern'); + $bot_parent_id = get_convo_var($convoArr,'conversation','bot_parent_id'); + $sendConvoArr = $convoArr; + + //check if match in user defined aiml + $allrows = find_userdefined_aiml($convoArr); + + //if there is no match in the user defined aiml tbl + if((!isset($allrows) ) || (count($allrows)<=0) ){ + //look for a match in the normal aiml tbl + $allrows = find_aiml_matches($convoArr); + //unset all irrelvant matches + $allrows = unset_all_bad_pattern_matches($allrows,$lookingfor,$current_thatpattern,$current_topic,$default_aiml_pattern); + //score the relevant matches + $allrows = score_matches($bot_parent_id,$allrows,$lookingfor,$current_thatpattern,$current_topic,$default_aiml_pattern); + //get the highest + $allrows = get_highest_score_rows($allrows,$lookingfor); + } + + //Now we have the results put into the conversation array + $convoArr['aiml']['pattern']=$allrows['pattern']; + $convoArr['aiml']['thatpattern']=$allrows['thatpattern']; + $convoArr['aiml']['template']=$allrows['template']; + $convoArr['aiml']['html_template']=htmlentities($allrows['template']); + $convoArr['aiml']['topic']=$allrows['topic']; + $convoArr['aiml']['score']=$allrows['score']; + $convoArr['aiml']['aiml_to_php'] = $allrows['aiml_to_php']; + $convoArr['aiml']['aiml_id'] = $allrows['aiml_id']; + //return + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Will be parsing id:".$allrows['aiml_id'] ." (".$allrows['pattern'].")",4); + + return $convoArr; +} + +/** + * function find_aiml_matches() + * This function builds the sql to use to get a match from the tbl + * @param array $convoArr - conversation array + * @return array $convoArr +**/ +function find_aiml_matches($convoArr){ + + global $con,$dbn,$error_response,$use_parent_bot; + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Finding the aiml matches from the DB",4); + + $i=0; + + //TODO convert to get_it + $bot_id = get_convo_var($convoArr,"conversation","bot_id"); + $bot_parent_id = get_convo_var($convoArr,"conversation","bot_parent_id"); + $default_aiml_pattern = get_convo_var($convoArr,"conversation","default_aiml_pattern"); + #$lookingfor = get_convo_var($convoArr,"aiml","lookingfor"); + $lookingfor = mysql_escape_string(get_convo_var($convoArr,"aiml","lookingfor")); + + //get the first and last words of the cleaned user input + $lastInputWord = get_last_word($lookingfor); + $firstInputWord = get_first_word($lookingfor); + + //get the stored topic + $storedtopic = mysql_escape_string(get_convo_var($convoArr,"topic")); + + //get the cleaned user input + $lastthat = get_convo_var($convoArr,'that','',1,1); + + //build like patterns + if($lastthat!=""){ + $thatPatternSQL = " OR ".make_like_pattern($lastthat,'thatpattern'); + } else { + $thatPattern = ""; + //$lastThatPattern = ""; + //$firstThatPattern = ""; + $thatPatternSQL = ""; + //$storedthatpattern =""; + } + + //get the word count + $word_count = wordsCount_inSentance($lookingfor); + + + if($bot_parent_id!=0) + { + $sql_bot_select = " (bot_id = '$bot_id' OR bot_id = '$bot_parent_id') "; + } + else + { + $sql_bot_select = " bot_id = '$bot_id' "; + } + + + if($word_count==1){ //if there is one word do this + $sql = "SELECT * FROM `$dbn`.`aiml` WHERE + $sql_bot_select AND ( + ((`pattern` = '_') OR (`pattern` = '*') OR (`pattern` = '$lookingfor') OR (`pattern` = '$default_aiml_pattern' ) ) + AND ((`thatpattern` = '_') OR (`thatpattern` = '*') OR (`thatpattern` = '') OR (`thatpattern` = '$lastthat') $thatPatternSQL ) + AND ( (`topic`='') OR (`topic`='".$storedtopic."')))"; + } else { //otherwise do this + $sql_add = make_like_pattern($lookingfor,'pattern'); + $sql = "SELECT * FROM `$dbn`.`aiml` WHERE + $sql_bot_select AND ( + ((`pattern` = '_') OR (`pattern` = '*') OR (`pattern` like '$lookingfor') OR ($sql_add) OR (`pattern` = '$default_aiml_pattern' ) ) + AND ((`thatpattern` = '_') OR (`thatpattern` = '*') OR (`thatpattern` = '') OR (`thatpattern` = '$lastthat') $thatPatternSQL ) + AND ((`topic`='') OR (`topic`='".$storedtopic."')))"; + } + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Match AIML sql: $sql",3); + + $result = db_query($sql,$con); + + if(($result)&&(mysql_num_rows($result)>0)){ + runDebug( __FILE__, __FUNCTION__, __LINE__, "FOUND: '".mysql_num_rows($result)."' potential AIML matches",2); + //loop through results + while($row=mysql_fetch_array($result)) { + $allrows[$i]['aiml_id'] = $row['id']; + $allrows[$i]['bot_id'] = $row['bot_id']; + $allrows[$i]['pattern'] = $row['pattern']; + $allrows[$i]['thatpattern'] = $row['thatpattern']; + $allrows[$i]['template'] = $row['template']; + $allrows[$i]['topic'] = $row['topic']; + $allrows[$i]['aiml_to_php'] = $row['php_code']; + $i++; + } + } + else + { + runDebug( __FILE__, __FUNCTION__, __LINE__, "Error: FOUND NO AIML matches in DB",1); + $allrows[$i]['aiml_id'] = "-1"; + $allrows[$i]['bot_id'] = "-1"; + $allrows[$i]['pattern'] = "no results"; + $allrows[$i]['thatpattern'] = ""; + $allrows[$i]['template'] = "$error_response"; + $allrows[$i]['topic'] = ""; + $allrows[$i]['aiml_to_php'] = "\$botsay=\"$error_response\""; + } + return $allrows; +} ?> \ No newline at end of file diff --git a/chatbot/core/aiml/load_aimlfunctions.php b/chatbot/core/aiml/load_aimlfunctions.php index 73cec36..99811f0 100644 --- a/chatbot/core/aiml/load_aimlfunctions.php +++ b/chatbot/core/aiml/load_aimlfunctions.php @@ -15,5 +15,5 @@ include_once("buildingphp_code_functions.php"); include_once("replace_tomakesafe.php"); -runDebug( __FILE__, __FUNCTION__, __LINE__, "Aimlfunction include files loaded",1); +runDebug( __FILE__, __FUNCTION__, __LINE__, "Aimlfunction include files loaded",4); ?> \ No newline at end of file diff --git a/chatbot/core/aiml/make_aiml_to_php_code.php b/chatbot/core/aiml/make_aiml_to_php_code.php index 3237119..b74a273 100644 --- a/chatbot/core/aiml/make_aiml_to_php_code.php +++ b/chatbot/core/aiml/make_aiml_to_php_code.php @@ -1,312 +1,327 @@ -#ie', "\$convoArr['bot_properties']['$1']", $template); - runDebug( __FILE__, __FUNCTION__, __LINE__, "Made initial bot property replacements",3); - - - $find[$i]='##i'; - $replace[$i]='\';'; - - $i++; - $find[$i]='##i'; - $replace[$i]='$tmp_botsay .= \''; - - $i++; - $find[$i]='##i'; - $replace[$i]='\'.call_user_func(\'get_convo_var\',$convoArr,\'bot_properties\',\'$1\').\''; - - - $i++; - $find[$i]='##i'; - $replace[$i]='\'.call_user_func(\'get_formatted_date\',\'$1\').\''; - - $i++; - $find[$i]='##i'; - $replace[$i]='; $tmp_botsay = ""; '; - - $i++; - $find[$i]='#(.*)([^<]*)#'; - $replace[$i]='\'.call_user_func(\'push_stack\',&$convoArr,\'$1\').\''; - - $i++; - $find[$i]='#PUSH\s?<([^>]*)>#'; - $replace[$i]='\'.call_user_func(\'push_stack\',&$convoArr,\'<$1>\').\''; - - $i++; - $find[$i]='#POP\s?<([^>]*)>#'; - $replace[$i]='\'.call_user_func(\'pop_stack\',&$convoArr).\''; - - $i++; - $find[$i]='##'; - $replace[$i]='\'.call_user_func(\'pop_stack\',&$convoArr).\''; - - $i++; - $find[$i]='##i'; - $replace[$i]='\'.call_user_func(\'url_encode_star\',$convoArr).\''; - - $i++; - $find[$i]='##i'; - $replace[$i]='\'.call_user_func(\'set_topicname\',$convoArr,\'$1\').\''; - - $i++; - $find[$i]='##i'; - $replace[$i]='\'.call_user_func(\'get_convo_var\',$convoArr,\'star\',\'\',\'$1\').\''; - - - - $i++; - $find[$i]='##i'; - $replace[$i]='\'.call_user_func(\'get_convo_var\',$convoArr,\'that\',\'\',\'$1\',\'$2\').\''; - - $i++; - $find[$i]='##i'; - $replace[$i]='\'.call_user_func(\'get_convo_var\',$convoArr,\'input\',\'\',\'$1\').\''; - - $i++; - $find[$i]='##i'; - $replace[$i]='\'.call_user_func(\'get_convo_var\',$convoArr,\'that_star\',\'\',\'$1\').\''; - - $i++; - $find[$i]='##i'; - $replace[$i]='\'.call_user_func(\'get_convo_var\',$convoArr,\'topic_star\',\'\',\'$1\').\''; - - - $i++; - $find[$i]='##i'; - $replace[$i]='\'.call_user_func(\'get_convo_var\',$convoArr,\'topic\').\''; - - $i++; - $find[$i]='##i'; - $replace[$i]='\'.call_user_func(\'get_convo_var\',$convoArr,\'client_properties\',\'$1\').\''; - - - - - - - $i++; - $find[$i]='##i'; - $replace[$i]='\'.call_user_func(\'get_convo_var\',$convoArr,\'client_properties\',\'id\').\''; - - $i++; - $find[$i]='#([^<]*)#i'; - $replace[$i]='\'.call_user_func(\'format\',\'uppercase\',\'$1\').\''; - - $i++; - $find[$i]='#([^<]*)#i'; - $replace[$i]='\'.call_user_func(\'format\',\'lowercase\',\'$1\').\''; - - $i++; - $find[$i]='#([^<]*)#i'; - $replace[$i]='\'.call_user_func(\'format\',\'formal\',\'$1\').\''; - - $i++; - $find[$i]='#([^<]*)#i'; - $replace[$i]='\'.call_user_func(\'format\',\'sentence\',\'$1\').\''; - - $i++; - $find[$i]='##i'; - $replace[$i]='\'.call_user_func(\'run_srai\',&$convoArr,\''; - - $i++; - $find[$i]='##i'; - $replace[$i]='\').\''; - - $i++; - $find[$i]='##i'; - $replace[$i]='\'.call_user_func(\'make_null\',\''; - - $i++; - $find[$i]='##i'; - $replace[$i]='\').\''; - - $i++; - $find[$i]='#([^<]*)#i'; - $replace[$i]='\'.call_user_func(\'transform_prounoun\',$convoArr,\'3\',\'$1\').\''; - - $i++; - $find[$i]='#([^<]*)#i'; - $replace[$i]='\'.call_user_func(\'transform_prounoun\',$convoArr,\'2\',\'$1\').\''; - - $i++; - $find[$i]='#[\s]?
  • #i'; - $replace[$i]="';\r\n".' if( ((isset($convoArr[\'$1\'])) && (strtoupper($convoArr[\'$1\']) === strtoupper(\'$2\'))) || ((isset($convoArr[\'client_properties\'][\'$1\'])) && (strtoupper($convoArr[\'client_properties\'][\'$1\']) === strtoupper(\'$2\'))) )'."\r\n".' { $tmp_botsay .= \''; - - $i++; - $find[$i]='##i'; - $replace[$i]="\r\n".'; $condition = call_user_func(\'clean_condition\',\'$1\'); '; - - $i++; - $find[$i]='#
  • #i'; - $replace[$i]="\r\n".' elseif( ((isset($convoArr[\'$1\'])) && (strtoupper($convoArr[\'$1\']) === strtoupper(\'$2\'))) || ((isset($convoArr[\'client_properties\'][\'$1\'])) && (strtoupper($convoArr[\'client_properties\'][\'$1\']) === strtoupper(\'$2\'))) )'."\r\n".' { $tmp_botsay .= \''; - - $i++; - $find[$i]='#
  • #i'; - $replace[$i]="\r\n".' elseif( ((isset($convoArr[$condition])) && (strtoupper($convoArr[$condition]) === strtoupper(\'$1\'))) || ((isset($convoArr[\'client_properties\'][$condition])) && (strtoupper($convoArr[\'client_properties\'][$condition]) === strtoupper(\'$1\'))) )'."\r\n".' { $tmp_botsay .= \''; - - $i++; - $find[$i]='#;(\s|\s+)?elseif#i'; - $replace[$i]=";\r\nif"; - - //this has to be be evalutated immeditately as nothing will change we are just collecting a random value - $i++; - $find[$i]="#(.*?)#e"; - $replace[$i]='call_user_func(\'select_random\',\'$1\')'; - - $i++; - $find[$i]='#
  • (.*)
  • #i'; - $replace[$i]="\r\n".'else { $tmp_botsay .=\'$1\'; } '; - - $i++; - $find[$i]='##i'; - $replace[$i]='\'; } '; - - $i++; - $find[$i]='#
    #i'; - $replace[$i]="\r\n".'$condition = ""; '; - - //TODO WORK OUT WHY THIS OCCURES - $i++; - $find[$i]='##ie', "\$convoArr['bot_properties']['$1']", $template); + runDebug( __FILE__, __FUNCTION__, __LINE__, "Made initial bot property replacements",4); + + + $find[$i]='#
    #i'; + $replace[$i]='\';'; + + $i++; + $find[$i]='##i'; + $replace[$i]='$tmp_botsay .= \''; + + $i++; + $find[$i]='##i'; + $replace[$i]='\'.call_user_func(\'get_convo_var\',$convoArr,\'bot_properties\',\'$1\').\''; + + + $i++; + $find[$i]='##i'; + $replace[$i]='\'.call_user_func(\'get_formatted_date\',\'$1\').\''; + + $i++; + $find[$i]='##i'; + $replace[$i]='; $tmp_botsay = ""; '; + + $i++; + $find[$i]='#(.*)([^<]*)#'; + $replace[$i]='\'.call_user_func(\'push_stack\',&$convoArr,\'$1\').\''; + + $i++; + $find[$i]='#PUSH\s?<([^>]*)>#'; + $replace[$i]='\'.call_user_func(\'push_stack\',&$convoArr,\'<$1>\').\''; + + $i++; + $find[$i]='#POP\s?<([^>]*)>#'; + $replace[$i]='\'.call_user_func(\'pop_stack\',&$convoArr).\''; + + $i++; + $find[$i]='##'; + $replace[$i]='\'.call_user_func(\'pop_stack\',&$convoArr).\''; + + $i++; + $find[$i]='##i'; + $replace[$i]='\'.call_user_func(\'url_encode_star\',$convoArr).\''; + + $i++; + $find[$i]='##i'; + $replace[$i]='\'.call_user_func(\'set_topicname\',$convoArr,\'$1\').\''; + + $i++; + $find[$i]='##i'; + $replace[$i]='\'.call_user_func(\'get_convo_var\',$convoArr,\'star\',\'\',\'$1\').\''; + + + + $i++; + $find[$i]='##i'; + $replace[$i]='\'.call_user_func(\'get_convo_var\',$convoArr,\'that\',\'\',\'$1\',\'$2\').\''; + + $i++; + $find[$i]='##i'; + $replace[$i]='\'.call_user_func(\'get_convo_var\',$convoArr,\'input\',\'\',\'$1\').\''; + + $i++; + $find[$i]='##i'; + $replace[$i]='\'.call_user_func(\'get_convo_var\',$convoArr,\'that_star\',\'\',\'$1\').\''; + + $i++; + $find[$i]='##i'; + $replace[$i]='\'.call_user_func(\'get_convo_var\',$convoArr,\'topic_star\',\'\',\'$1\').\''; + + + $i++; + $find[$i]='##i'; + $replace[$i]='\'.call_user_func(\'get_convo_var\',$convoArr,\'topic\').\''; + + $i++; + $find[$i]='##i'; + $replace[$i]='\'.call_user_func(\'get_convo_var\',$convoArr,\'client_properties\',\'$1\').\''; + + + + + + + $i++; + $find[$i]='##i'; + $replace[$i]='\'.call_user_func(\'get_convo_var\',$convoArr,\'client_properties\',\'id\').\''; + + $i++; + $find[$i]='#([^<]*)#i'; + $replace[$i]='\'.call_user_func(\'format\',\'uppercase\',\'$1\').\''; + + $i++; + $find[$i]='#([^<]*)#i'; + $replace[$i]='\'.call_user_func(\'format\',\'lowercase\',\'$1\').\''; + + $i++; + $find[$i]='#([^<]*)#i'; + $replace[$i]='\'.call_user_func(\'format\',\'formal\',\'$1\').\''; + + $i++; + $find[$i]='#([^<]*)#i'; + $replace[$i]='\'.call_user_func(\'format\',\'sentence\',\'$1\').\''; + + $i++; + $find[$i]='##i'; + $replace[$i]='\'.call_user_func(\'run_srai\',&$convoArr,\''; + + $i++; + $find[$i]='##i'; + $replace[$i]='\').\''; + + $i++; + $find[$i]='##i'; + $replace[$i]='\'.call_user_func(\'make_null\',\''; + + $i++; + $find[$i]='##i'; + $replace[$i]='\').\''; + + $i++; + $find[$i]='#([^<]*)#i'; + $replace[$i]='\'.call_user_func(\'transform_prounoun\',$convoArr,\'3\',\'$1\').\''; + + $i++; + $find[$i]='#([^<]*)#i'; + $replace[$i]='\'.call_user_func(\'transform_prounoun\',$convoArr,\'2\',\'$1\').\''; + + $i++; + $find[$i]='#[\s]?
  • #i'; + $replace[$i]="';\r\n".' if( ((isset($convoArr[\'$1\'])) && (strtoupper($convoArr[\'$1\']) === strtoupper(\'$2\'))) || ((isset($convoArr[\'client_properties\'][\'$1\'])) && (strtoupper($convoArr[\'client_properties\'][\'$1\']) === strtoupper(\'$2\'))) )'."\r\n".' { $tmp_botsay .= \''; + + $i++; + $find[$i]='##i'; + $replace[$i]="\r\n".'; $condition = call_user_func(\'clean_condition\',\'$1\'); '; + + $i++; + $find[$i]='#
  • #i'; + $replace[$i]="\r\n".' elseif( ((isset($convoArr[\'$1\'])) && (strtoupper($convoArr[\'$1\']) === strtoupper(\'$2\'))) || ((isset($convoArr[\'client_properties\'][\'$1\'])) && (strtoupper($convoArr[\'client_properties\'][\'$1\']) === strtoupper(\'$2\'))) )'."\r\n".' { $tmp_botsay .= \''; + + $i++; + $find[$i]='#
  • #i'; + $replace[$i]="\r\n".' elseif( ((isset($convoArr[$condition])) && (strtoupper($convoArr[$condition]) === strtoupper(\'$1\'))) || ((isset($convoArr[\'client_properties\'][$condition])) && (strtoupper($convoArr[\'client_properties\'][$condition]) === strtoupper(\'$1\'))) )'."\r\n".' { $tmp_botsay .= \''; + + $i++; + $find[$i]='#;(\s|\s+)?elseif#i'; + $replace[$i]=";\r\nif"; + + + $i++; + $find[$i]="#<\random>(\?|\.|\!\s)?
  • #eis"; + $replace[$i]='$1\';}'; + + + //this has to be be evalutated immeditately as nothing will change we are just collecting a random value + $i++; + $find[$i]="#([^<]*)#eis"; + $replace[$i]='call_user_func(\'select_random\',\'$1\')'; + + + // this needs a second attempt before i work out a proper fix + // the first removes the out random but if there is a nested random it wont work + $i++; + $find[$i]="#(.*)#eis"; + $replace[$i]='call_user_func(\'select_random\',\'$1\')'; + + + $i++; + $find[$i]='#
  • (.*)
  • #i'; + $replace[$i]="\r\n".'else { $tmp_botsay .=\'$1\'; } '; + + $i++; + $find[$i]='##i'; + $replace[$i]='\'; } '; + + $i++; + $find[$i]='#
    #i'; + $replace[$i]="\r\n".'$condition = ""; '; + + //TODO WORK OUT WHY THIS OCCURES + $i++; + $find[$i]='# \ No newline at end of file diff --git a/chatbot/core/aiml/parse_aiml.php b/chatbot/core/aiml/parse_aiml.php index fd30453..6f66f7b 100644 --- a/chatbot/core/aiml/parse_aiml.php +++ b/chatbot/core/aiml/parse_aiml.php @@ -1,928 +1,942 @@ -Unable to open tr"); - - sort($verbList); - // fill the arrays - foreach ($verbList as $line) { - $line = rtrim($line, "\r\n"); - #print "line = |$line|
    \n"; - if (empty($line)) continue; - $firstChar = substr($line, 0, 1); - if ($firstChar === '#') continue; - if ($firstChar === '$') { - $words = str_replace('$ ', '', $line); - list ($first, $third) = explode(', ', $words); - $second = $first; - } - elseif ($firstChar === '~') { - $words = str_replace('~ ', '', $line); - $first = $words; - $second = $first; - $third = $first; - } - else { - list ($first, $second, $third) = explode(', ', $line); - } - // build first patterns to second (both keyed and non) patterns and replacements, and first patterns to third replacements - $firstPersonPatterns[] = str_replace('[word]', $first, $firstPersonSearchTemplate); - $secondPersonKeyedPatterns[] = str_replace('[word]', $second, $secondPersonKeyedSearchTemplate); - #$secondPersonKeyedPatterns[] = str_replace('[word]', $second, $secondPersonSearchTemplateReversed); - $secondPersonReplacements[] = str_replace('[word]', $first, $secondPersonReplaceTemplate); - #$secondPersonReplacements[] = str_replace('[word]', $first, $firstPersonReplaceTemplateReversed); - $thirdPersonReplacements[] = str_replace('[word]', $tpWord, $thirdPersonReplaceTemplate); - // build second patterns to first replacements - reversed (e.g. "would I") first - $secondPersonPatterns[] = str_replace('[word]', $second, $secondPersonSearchTemplate); - $firstPersonReplacements[] = str_replace('[word]', $first, $firstPersonReplaceTemplate); - // build first patterns to third replacements - } - $_SESSION['verbList'] = true; -// debugging - Let's see what the contents of the files are! - $transformList = array( - 'firstPersonPatterns'=> $firstPersonPatterns, - 'secondPersonKeyedPatterns'=> $secondPersonKeyedPatterns, - 'secondPersonReplacements'=> $secondPersonReplacements, - 'thirdPersonReplacements'=> $thirdPersonReplacements, - 'secondPersonPatterns'=> $secondPersonPatterns, - 'firstPersonReplacements'=> $firstPersonReplacements - ); - foreach ($transformList as $transform_index => $transform_value) { - $_SESSION['transform_list'][$transform_index] = $transform_value; - } - } - - -/** - * function swapPerson() - * @param array $convoArr - * @param int $person - * @param string $in - * @return the tranformed string -**/ -function swapPerson($convoArr,$person =2, $in) { //2 = swap first with second poerson // otherwise swap with third person - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Person:$person In:$in",1); - - $name = get_convo_var($convoArr,'client_properties','name'); - $gender = get_convo_var($convoArr,'client_properties','gender'); - - $tmp = trim($in); - - if ((!isset($_SESSION['transform_list']))|| ($_SESSION['transform_list'] == NULL ) ){ - buildVerbList($name,$gender); - } - - switch ($gender) { - case "male": - $g1 = "his"; - $g2 = "him"; - $g3 = "he"; - break; - case "female"; - $g1 = "hers"; - $g2 = "her"; - $g3 = "she"; - break; - default: - $g1 = "theirs"; - $g2 = "them"; - $g3 = "they"; - } - -// the "simple" transform arrays - more for exceptions to the above rules than for anything "simple" :) - $simpleFirstPersonPatterns = array( - '/(\bi am\b)/i', - '/(\bam i\b)/i', - '/(\bi\b)/i', - '/(\bmy\b)/i', - '/(\bmine\b)/i', - '/(\bmyself\b)/i', - '/(\bcan i\b)/i' - ); - $simpleSecondPersonKeyedReplacements = array( - 'you are', - 'are you', - 'you', - 'your', - 'yours', - 'yourself', - 'can you' - ); - $simpleFirstToThirdPersonPatterns = array( - '/(\bi am\b)/i', - '/(\bam i\b)/i', - '/(\bi\b)/i', - '/(\bmy\b)/i', - '/(\bmine\b)/i', - '/(\bmyself\b)/i', - '/(\bwill i\b)/i', - '/(\bshall i\b)/i', - '/(\bmay i\b)/i', - '/(\bmight i\b)/i', - '/(\bcan i\b)/i', - '/(\bcould i\b)/i', - '/(\bmust i\b)/i', - '/(\bshould i\b)/i', - '/(\bwould i\b)/i', - '/(\bneed i\b)/i', - '/(\bam i\b)/i', - '/(\bwas i\b)/i', - ); - $simpleThirdPersonReplacements = array( - "$g3 is", - "is $g3", - "$g3", - "$g1", - "$g1", - "$g2".'self', - 'will '.$g3, - 'shall '.$g3, - 'may '.$g3, - 'might '.$g3, - 'can '.$g3, - 'could '.$g3, - 'must '.$g3, - 'should '.$g3, - 'would '.$g3, - 'need '.$g3, - 'is '.$g3, - 'was '.$g3, - ); - $simpleSecondPersonPatterns = array( - '/(\bhelp you\b)/i', - '/(\bwill you\b)/i', - '/(\bshall you\b)/i', - '/(\bmay you\b)/i', - '/(\bmight you\b)/i', - '/(\bcan you\b)/i', - '/(\bcould you\b)/i', - '/(\bmust you\b)/i', - '/(\bshould you\b)/i', - '/(\bwould you\b)/i', - '/(\bneed you\b)/i', - '/(\bare you\b)/i', - '/(\bwere you\b)/i', - '/(\byour\b)/i', - '/(\byours\b)/i', - '/(\byourself\b)/i', - '/(\bthy\b)/i' - ); -# will, shall, may, might, can, could, must, should, would, need - $simpleFirstPersonReplacements = array( - 'help m e', - 'will @II', - 'shall @II', - 'may @II', - 'might @II', - 'can @II', - 'could @II', - 'must @II', - 'should @II', - 'would @II', - 'need @II', - 'am @II', - 'was @II', - 'my', - 'mine', - 'myself', - 'my' - ); - - if ($person == 2) { - $tmp = preg_replace('/\bare you\b/i', 'am @II', $tmp); // simple second to first transform - $tmp = preg_replace('/\byou and i\b/i', 'y ou and @II', $tmp); // fix the "Me and you" glitch - $tmp = preg_replace($simpleSecondPersonPatterns, $simpleFirstPersonReplacements, $tmp); // "simple" second to keyed first transform - $tmp = preg_replace($simpleFirstPersonPatterns, $simpleSecondPersonKeyedReplacements, $tmp); // simple first to keyed second transform - $tmp = preg_replace($_SESSION['transform_list']['secondPersonPatterns'], $_SESSION['transform_list']['firstPersonReplacements'], $tmp); // second to first transform - $tmp = preg_replace('/\bme\b/i', 'you', $tmp); // simple second to first transform (me) - #$tmp = preg_replace('/\bi\b/i', 'y ou', $tmp); // simple second to first transform (I) - $tmp = preg_replace('/\byou\b/i', 'me', $tmp); // simple second to first transform - $tmp = str_replace('you', 'you', $tmp); // replace second person key (y ou) with non-keyed value (you) - $tmp = str_replace(' me', ' me', $tmp); // replace first person key (m e) with non-keyed value (me) - $tmp = str_replace(' my', ' my', $tmp); // replace first person key (m e) with non-keyed value (me) - $tmp = str_replace('my ', 'my ', $tmp); // replace first person key (m e) with non-keyed value (me) - $tmp = str_replace(' mine', ' mine', $tmp); // replace first person key (m e) with non-keyed value (me) - $tmp = str_replace('mine ', 'mine ', $tmp); // replace first person key (m e) with non-keyed value (me) - $tmp = str_replace(' @II ', ' I ', $tmp); // replace first person key (@I) with non-keyed value (I) - $tmp = str_replace('@II ', 'I ', $tmp); // replace first person key (@I) with non-keyed value (I) - $tmp = str_replace(' @II', ' I', $tmp); // replace first person key (@I) with non-keyed value (I) - $tmp = str_replace('@you', 'I', $tmp); // replace first person key (@I) with non-keyed value (I) - #$tmp = ucfirst( $tmp); - } -elseif ($person == 3) { - - - $tmp = preg_replace($_SESSION['transform_list']['firstPersonPatterns'], $_SESSION['transform_list']['thirdPersonReplacements'], $tmp); // first to third transform, but only when specifically needed - $tmp = preg_replace('/(\byour gender\b)/i',$g3,$tmp); - $tmp = preg_replace('/(\bthey\b)/i',$g3,$tmp); - $tmp = preg_replace('/(\bi\b)/i',$g3,$tmp); - $tmp = preg_replace('/(\bme\b)/i',$g3,$tmp); - - } - - - //debug - // if (RUN_DEBUG) runDebug(4, __FILE__, __FUNCTION__, __LINE__,"
    \nTransformation complete. was: $in, is: $tmp"); - return $tmp; - //return - -} - -/** - * function parse_matched_aiml() - * This function controls and triggers all the functions to parse the matched aiml - * @param array $convoArr - the conversation array - * @param string $type - normal or srai - * @return array $convoArr - the updated conversation array -**/ -function parse_matched_aiml($convoArr,$type="normal") -{ - runDebug( __FILE__, __FUNCTION__, __LINE__, "Run the aiml parse in $type mode (normal or srai)",1); - $convoArr = expand_shorthand_to_longhand($convoArr); - $convoArr = set_wildcards($convoArr); - $convoArr = aiml_to_phpfunctions($convoArr); - - if($type!="srai"){ - runDebug( __FILE__, __FUNCTION__, __LINE__, "$type - Saving for next turn",1); - $convoArr = save_for_nextturn($convoArr); - } else { - runDebug( __FILE__, __FUNCTION__, __LINE__, "$type - Not saving for next turn",1); - } - return $convoArr; -} - -/** - * function clean_that() - * This function cleans the 'that' of html and other bits and bobs - * @param string $that - the string to clean - * @return string $that - the cleaned string -**/ -function clean_that($that) -{ - $in = $that; - $that = str_replace("
    ",".",$that); - $that = strip_tags($that); - $that= remove_allpuncutation($that); - $that= whitespace_clean($that); - $that= captialise($that); - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Cleaning the that - that: $in cleanthat:$that",3); - - return $that; -} - -/** - * function save_for_nextturn() - * This function puts the bot results of an srai search into the main convoArr - * @param array $convoArr - the conversation array - * @return array $convoArr - the updated conversation array -**/ -function save_for_nextturn($convoArr) -{ - runDebug( __FILE__, __FUNCTION__, __LINE__, "Saving that and that_raw for next turn",3); - - $savethis = get_convo_var($convoArr,'aiml','parsed_template'); - - $convoArr = push_on_front_convoArr('that_raw',$savethis,$convoArr); - $convoArr = push_on_front_convoArr('that',$savethis,$convoArr); - return $convoArr; -} - -/** - * function set_wildcards() - * This function extracts wildcards from a patterns and puts their values in their associated array - * @param array $convoArr - the conversation array - * @return array $convoArr - the updated conversation array -**/ -function set_wildcards($convoArr) -{ - runDebug( __FILE__, __FUNCTION__, __LINE__, "Setting Wildcards",3); - - $aiml_pattern = get_convo_var($convoArr,'aiml','pattern'); - - - $ap = $aiml_pattern; - $ap = str_replace("+","\+",$ap); - $ap = str_replace("*","(.*)",$ap); - $ap = str_replace("_","(.*)",$ap); - - $wildcards = str_replace("_","(.*)?",str_replace("*","(.*)?",$aiml_pattern)); - - if($wildcards!=$aiml_pattern) - { - if(!isset($convoArr['aiml']['user_raw'])) { - $checkagainst = $convoArr['aiml']['lookingfor']; - } else { - $checkagainst = $convoArr['aiml']['user_raw']; - } - - if (preg_match('/'.$ap.'/si', $checkagainst, $matches,PREG_OFFSET_CAPTURE)) { - - $totalStars = count($matches)-1; - for($i = $totalStars; $i>=1; $i--){ - $convoArr = push_on_front_convoArr('star',$matches[$i][0],$convoArr); - } - } - } - return $convoArr; -} - -/** - * function expand_shorthand_to_longhand() - * This function replaces the annotated aiml with the long hand version - * and also makes some changes to the original aiml to make it program o friendly - * @param array $convoArr - the conversation array - * @return array $convoArr - the updated conversation array -**/ -function expand_shorthand_to_longhand($convoArr) -{ - global $allowed_html_tags; - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Expanding shorthand to longhand",3); - - $template = $convoArr['aiml']['template']; - $convoArr['aiml']['shorthand_template']=htmlentities($template); - - $template = trim($template); - - $i=0; - $find[$i]='#index="([0-9]*),(\*)"#'; - $replace[$i]='index="$1,all"'; - - $i++; - $find[$i]='#PUSH(.*)#'; - $replace[$i]='$1'; - - $i++; - $find[$i]='#\s|\s+#'; - $replace[$i]=' '; - - $i++; - $find[$i]='##'; - $replace[$i]=' '; - - - $template = preg_replace($find, $replace, $template); - runDebug( __FILE__, __FUNCTION__, __LINE__, "Completed preg_replace expansion: ".htmlentities($template),3); - - - //TODO not sure if this is correct implementation of star if lots of problems will see about making changes in future releases - $template = str_replace("\"",$template); - $template = str_replace(" />","/>",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - - $template = str_replace("","",$template); - $template = str_replace('',' ',$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("","",$template); - $template = str_replace("POP","",$template); - $template = str_replace("",$template); - $template = str_replace("]]>","",$template); - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Completed str_replace expansion: ".htmlentities($template),3); - - //this might actually have no aiml in it all so lets check. - if(substr($template, 0, 1)!="<") - { - $template = "$template"; - } - else - { - $htmls = implode("|",$allowed_html_tags); - //$matches = preg_match_all("# ?(([^\.\?!]*)+(?:[\.\?!]|(?:
    0) - { - $chktag = trim($tags[0][0]); - $len = strlen($chktag); - if(substr(trim($template), 0, $len)==$chktag) - { - $template = "$template"; - } - } - } - $template = trim($template); - $ex = explode("<",$template); - $ex = remove_nulls_from_array($ex); - - if((trim($ex[0])=="think>") && (trim($ex[count($ex)-1])=="/think>")) - { - $template = rtrim($template,""); - $template = ltrim($template,""); - $template .= ""; - } - - if(trim($ex[0])=="think>") - { - $template = str_replace_once("" , "" , $template); - $template = str_replace_once("" , " " , $template); - $template .= ""; - $template = str_replace_once(" " , " " , $template); - } - - //$template = preg_replace("#([^<]*)<#si","$1<",$template); - $template = str_replace("","
    ",$template); - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Completed Program O specific: ".htmlentities($template),3); - - $convoArr['aiml']['template']=$template; - $convoArr['aiml']['longhand_template']=htmlentities($template); - return $convoArr; -} - - -/** - * function str_replace_once() - * This function replaces the first occurence of $needle in $haystack - * @param string $needle - look for this - * @param $replace - replace with this - * @param $haystack - look in this - * @return string - the replaced string -**/ -function str_replace_once($needle , $replace , $haystack){ - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Replacing $needle in $haystack with $replace",3); - - $pos = strpos($haystack, $needle); - if ($pos === false) { - // Nothing found - return $haystack; - } - return substr_replace($haystack, $replace, $pos, strlen($needle)); -} - - -/** - * function make_null() - * This function removes a string - * @param string $c - the string to remove - * @return "" - to overwrite the sent string -**/ -function make_null($c) -{ - runDebug( __FILE__, __FUNCTION__, __LINE__, "Deleting $c from string",3); - return ""; -} - -/** - * function set_simple() - * This function sets simple client properties - * @param array &$convoArr - a reference to the existing conversation array - * @param string $index - the array index to set - * @param string $value - the value of the array index - * @return string $value - the value of the array index -**/ -function set_simple(&$convoArr,$index,$value) -{ - runDebug( __FILE__, __FUNCTION__, __LINE__, "Setting simple value $index to equal $value",3); - - if($index=="topic") - { - $convoArr = push_on_front_convoArr('topic',$value,$convoArr); - } - else { - $convoArr['client_properties'][$index]=$value; - } - return $value." "; -} - -/** - * function select_random() - * This function is given a string of
  • 's it is then converted into PHP code so that a random option can be selected - * @param string $random_options - a string of
  • this
  • that
  • options to be made into an array and PHP code - * @return array $array - the clean array -**/ -function select_random($random_options) -{ - //initialise php code string - $str = ""; - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Building an array to select random option from",3); - - $arrayname = "$".get_random_str(); - - //split up the
  • 's - $random_options = preg_split('#
  • |
  • #',$random_options); - //remove any blanks from array - $random_options = remove_nulls_from_array($random_options); - //count the total options - $mx= count($random_options)-1; - //add to php code string - $str .= "'; \r\n\r\n$arrayname = rand(0,$mx);\r\n\r\n"; - - //build big if else so that we can select a random options later - foreach($random_options as $index => $value) - { - if($index==0) - { - $str .= "if(".$arrayname."=='".$index."'){\r\n\t\$tmp_botsay.= '".$value."'; }\r\n"; - } - elseif((count($random_options)-1)==$index) - { - $str .= "else{\r\n\t\$tmp_botsay.= '". $value ."'; }\r\n\r\n"; - $str .= "\$tmp_botsay .= '"; - } - else - { - $str .= "elseif(".$arrayname."=='".$index."'){\r\n\t\$tmp_botsay.= '".$value."'; }\r\n"; - } - } - //return the php code string - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Built PHP code string so that we can select option later: $str",3); - - return $str; -} - -/** - * function remove_nulls_from_array() - * This function is just a little cleaning function to remove nulls from a given array - * @param array $array - the array to clean - * @return array $array - the clean array -**/ -function remove_nulls_from_array($array) -{ - runDebug( __FILE__, __FUNCTION__, __LINE__, "Removing nulls from array",3); - $newArray=array(); - foreach($array as $index => $value) - { - if(($value != NULL) && (trim($value)!="")) - { - $newArray[]=$value; - } - } - - return $newArray; -} - -/** - * function run_srai() - * This function controls the SRAI recursion calls - * @param array &$convoArr - a reference to the existing conversation array - * @param string $now_look_for_this - the text to search for - * @return string $srai_parsed_template - the result of the search -**/ -function run_srai(&$convoArr,$now_look_for_this) -{ - global $srai_iterations, $offset,$error_response; - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Running SRAI $srai_iterations on $now_look_for_this",1); - runDebug( __FILE__, __FUNCTION__, __LINE__, $convoArr['aiml']['html_template'],3); - //number of srai iterations - will stop recursion if it is over 10 - $srai_iterations++; - runDebug( __FILE__, __FUNCTION__, __LINE__, "Incrementing srai iterations to $srai_iterations",3); - if($srai_iterations>10){ - runDebug( __FILE__, __FUNCTION__, __LINE__, "ERROR - Too much recursion breaking out",1); - $convoArr['aiml']['parsed_template']=$error_response; - return $error_response; - } - - $tmp_convoArr = array(); - $tmp_convoArr = $convoArr; - - $tmp_convoArr['aiml']=array(); - $tmp_convoArr['that'][$offset][$offset]=""; //added - $tmp_convoArr['aiml']['parsed_template']=""; - $tmp_convoArr['aiml']['lookingfor']=$now_look_for_this; - - $tmp_convoArr = get_aiml_to_parse($tmp_convoArr); - $tmp_convoArr = parse_matched_aiml($tmp_convoArr,"srai"); - - $srai_parsed_template = $tmp_convoArr['aiml']['parsed_template']; - - runDebug( __FILE__, __FUNCTION__, __LINE__, "SRAI Found: '$srai_parsed_template'",1); - - $convoArr['client_properties'] = $tmp_convoArr['client_properties']; - $convoArr['topic'] = $tmp_convoArr['topic']; - $convoArr['stack'] = $tmp_convoArr['stack']; - - - $srai_iterations--; - runDebug( __FILE__, __FUNCTION__, __LINE__, "Decrementing srai iterations to $srai_iterations",3); - return $srai_parsed_template." "; - -} - - - -/** - * function format() - * This function formats a given text - * @param string $format - the format - * @param string $texttoformat - the text to format - * @return string $texttoformat - the formated text -**/ -function format($format,$texttoformat) -{ - switch($format) - { - case "uppercase": - $texttoformat = strtoupper($texttoformat); - break; - case "lowercase": - $texttoformat = strtolower($texttoformat); - break; - case "formal": - $texttoformat = ucwords($texttoformat); - break; - case "sentence": - $texttoformat = ucfirst($texttoformat); - break; - } - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Formated the text to: $texttoformat",3); - - return $texttoformat; -} - -/** - * function url_encode_star() - * This function encode the star value to make it safe for web addresses - * @param array $convoArr - conversation array - * @return string $encoded_star - the encoded string -**/ -function url_encode_star($convoArr) -{ - - $encoded_star = urlencode(get_convo_var($convoArr,'star')); - runDebug( __FILE__, __FUNCTION__, __LINE__, "Urlencoded the string to: $encoded_star",3); - return $encoded_star; -} - - -/** - * function push_stack() - * This function pushes an item on to the stack - * @param array $convoArr - conversation array - * @param string $item - the item to push - * @return string $item - the pushed item -**/ -function push_stack(&$convoArr,$item) -{ - if((trim($item))!=(trim($convoArr['stack']['top']))){ - runDebug( __FILE__, __FUNCTION__, __LINE__, "Pushing $item onto to the stack",3); - $convoArr['stack']['last'] = $convoArr['stack']['seventh']; - $convoArr['stack']['seventh'] = $convoArr['stack']['sixth']; - $convoArr['stack']['sixth'] = $convoArr['stack']['fifth']; - $convoArr['stack']['fifth'] = $convoArr['stack']['fourth']; - $convoArr['stack']['fourth'] = $convoArr['stack']['third']; - $convoArr['stack']['third'] = $convoArr['stack']['second']; - $convoArr['stack']['second'] = $convoArr['stack']['top']; - $convoArr['stack']['top'] = $item; - }else{ - runDebug( __FILE__, __FUNCTION__, __LINE__, "Could not push empty item onto to the stack",3); - } - - return $item; -} - - -/** - * function pop_stack() - * This function pops an item off the stack - * @param array $convoArr - conversation array - * @return string $item - the popped item -**/ -function pop_stack(&$convoArr) -{ - $item = trim($convoArr['stack']['top']); - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Popped $item off the stack",3); - - $convoArr['stack']['top'] = $convoArr['stack']['second']; - $convoArr['stack']['second'] = $convoArr['stack']['third']; - $convoArr['stack']['third'] = $convoArr['stack']['fourth']; - $convoArr['stack']['fourth'] = $convoArr['stack']['fifth']; - $convoArr['stack']['fifth'] = $convoArr['stack']['sixth']; - $convoArr['stack']['sixth'] = $convoArr['stack']['seventh']; - $convoArr['stack']['seventh'] = $convoArr['stack']['last']; - $convoArr['stack']['last'] = "om"; - return $item; -} - -/** - * function make_learn() - * This function builds the sql insert a learnt aiml cateogry in to the db - * @param array $convoArr - conversation array - * @param string $pattern - the pattern we will insert - * @param string $template - the template to insert -**/ -function make_learn($convoArr, $pattern, $template) -{ - global $con,$dbn; - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Making learn",1); - runDebug( __FILE__, __FUNCTION__, __LINE__, "Pattern: $pattern",1); - runDebug( __FILE__, __FUNCTION__, __LINE__, "Template: $template",1); - - $pattern = clean_for_aiml_match($pattern); - $aiml = " $pattern "; - $aiml = mysql_escape_string($aiml); - $pattern = mysql_escape_string($pattern." "); - $template = mysql_escape_string($template." "); - $u_id = $convoArr['conversation']['user_id']; - $bot_id = $convoArr['conversation']['bot_id']; - - $sql = "INSERT INTO `$dbn`.`aiml_userdefined` - VALUES - (NULL, '$aiml','$pattern','$template','$u_id','$bot_id',NOW())"; - runDebug( __FILE__, __FUNCTION__, __LINE__, "Make learn SQL: $sql",2); - $res = mysql_query($sql,$con); -} - -/** - * function run_system() - * This function runs the system math operations - * @param char $operator - maths operator - * @param int $num_1 - the first number - * @param int $num_2 - the second number - * @param int $output - the result of the math operation -**/ -function run_system($operator,$num_1,$num_2="") -{ - runDebug( __FILE__, __FUNCTION__, __LINE__, "Running system tag math $num_1 $operator $num_2",1); - - switch (strtolower($operator)) { - case "add": - $output = $num_1 + $num_2; - break; - case "subtract": - $output = $num_1 - $num_2; - break; - case "multiply": - $output = $num_1 * $num_2; - break; - case "divide": - if ($num_2 == 0) { - $output = "You can't divide by 0!"; - } else { - $output = $num_1 / $num_2; - } - break; - case "sqrt": - $output = sqrt($num_1); - break; - case "power": - $output = pow($num_1, $num_2); - break; - default: - $output = $operator."?"; - } - - return $output; - -} +Unable to open tr"); + + sort($verbList); + // fill the arrays + foreach ($verbList as $line) { + $line = rtrim($line, "\r\n"); + #print "line = |$line|
    \n"; + if (empty($line)) continue; + $firstChar = substr($line, 0, 1); + if ($firstChar === '#') continue; + if ($firstChar === '$') { + $words = str_replace('$ ', '', $line); + list ($first, $third) = explode(', ', $words); + $second = $first; + } + elseif ($firstChar === '~') { + $words = str_replace('~ ', '', $line); + $first = $words; + $second = $first; + $third = $first; + } + else { + list ($first, $second, $third) = explode(', ', $line); + } + // build first patterns to second (both keyed and non) patterns and replacements, and first patterns to third replacements + $firstPersonPatterns[] = str_replace('[word]', $first, $firstPersonSearchTemplate); + $secondPersonKeyedPatterns[] = str_replace('[word]', $second, $secondPersonKeyedSearchTemplate); + #$secondPersonKeyedPatterns[] = str_replace('[word]', $second, $secondPersonSearchTemplateReversed); + $secondPersonReplacements[] = str_replace('[word]', $first, $secondPersonReplaceTemplate); + #$secondPersonReplacements[] = str_replace('[word]', $first, $firstPersonReplaceTemplateReversed); + $thirdPersonReplacements[] = str_replace('[word]', $tpWord, $thirdPersonReplaceTemplate); + // build second patterns to first replacements - reversed (e.g. "would I") first + $secondPersonPatterns[] = str_replace('[word]', $second, $secondPersonSearchTemplate); + $firstPersonReplacements[] = str_replace('[word]', $first, $firstPersonReplaceTemplate); + // build first patterns to third replacements + } + $_SESSION['verbList'] = true; +// debugging - Let's see what the contents of the files are! + $transformList = array( + 'firstPersonPatterns'=> $firstPersonPatterns, + 'secondPersonKeyedPatterns'=> $secondPersonKeyedPatterns, + 'secondPersonReplacements'=> $secondPersonReplacements, + 'thirdPersonReplacements'=> $thirdPersonReplacements, + 'secondPersonPatterns'=> $secondPersonPatterns, + 'firstPersonReplacements'=> $firstPersonReplacements + ); + foreach ($transformList as $transform_index => $transform_value) { + $_SESSION['transform_list'][$transform_index] = $transform_value; + } + } + + +/** + * function swapPerson() + * @param array $convoArr + * @param int $person + * @param string $in + * @return the tranformed string +**/ +function swapPerson($convoArr,$person =2, $in) { //2 = swap first with second poerson // otherwise swap with third person + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Person:$person In:$in",4); + + $name = get_convo_var($convoArr,'client_properties','name'); + $gender = get_convo_var($convoArr,'client_properties','gender'); + + $tmp = trim($in); + + if ((!isset($_SESSION['transform_list']))|| ($_SESSION['transform_list'] == NULL ) ){ + buildVerbList($name,$gender); + } + + switch ($gender) { + case "male": + $g1 = "his"; + $g2 = "him"; + $g3 = "he"; + break; + case "female"; + $g1 = "hers"; + $g2 = "her"; + $g3 = "she"; + break; + default: + $g1 = "theirs"; + $g2 = "them"; + $g3 = "they"; + } + +// the "simple" transform arrays - more for exceptions to the above rules than for anything "simple" :) + $simpleFirstPersonPatterns = array( + '/(\bi am\b)/i', + '/(\bam i\b)/i', + '/(\bi\b)/i', + '/(\bmy\b)/i', + '/(\bmine\b)/i', + '/(\bmyself\b)/i', + '/(\bcan i\b)/i' + ); + $simpleSecondPersonKeyedReplacements = array( + 'you are', + 'are you', + 'you', + 'your', + 'yours', + 'yourself', + 'can you' + ); + $simpleFirstToThirdPersonPatterns = array( + '/(\bi am\b)/i', + '/(\bam i\b)/i', + '/(\bi\b)/i', + '/(\bmy\b)/i', + '/(\bmine\b)/i', + '/(\bmyself\b)/i', + '/(\bwill i\b)/i', + '/(\bshall i\b)/i', + '/(\bmay i\b)/i', + '/(\bmight i\b)/i', + '/(\bcan i\b)/i', + '/(\bcould i\b)/i', + '/(\bmust i\b)/i', + '/(\bshould i\b)/i', + '/(\bwould i\b)/i', + '/(\bneed i\b)/i', + '/(\bam i\b)/i', + '/(\bwas i\b)/i', + ); + $simpleThirdPersonReplacements = array( + "$g3 is", + "is $g3", + "$g3", + "$g1", + "$g1", + "$g2".'self', + 'will '.$g3, + 'shall '.$g3, + 'may '.$g3, + 'might '.$g3, + 'can '.$g3, + 'could '.$g3, + 'must '.$g3, + 'should '.$g3, + 'would '.$g3, + 'need '.$g3, + 'is '.$g3, + 'was '.$g3, + ); + $simpleSecondPersonPatterns = array( + '/(\bhelp you\b)/i', + '/(\bwill you\b)/i', + '/(\bshall you\b)/i', + '/(\bmay you\b)/i', + '/(\bmight you\b)/i', + '/(\bcan you\b)/i', + '/(\bcould you\b)/i', + '/(\bmust you\b)/i', + '/(\bshould you\b)/i', + '/(\bwould you\b)/i', + '/(\bneed you\b)/i', + '/(\bare you\b)/i', + '/(\bwere you\b)/i', + '/(\byour\b)/i', + '/(\byours\b)/i', + '/(\byourself\b)/i', + '/(\bthy\b)/i' + ); +# will, shall, may, might, can, could, must, should, would, need + $simpleFirstPersonReplacements = array( + 'help m e', + 'will @II', + 'shall @II', + 'may @II', + 'might @II', + 'can @II', + 'could @II', + 'must @II', + 'should @II', + 'would @II', + 'need @II', + 'am @II', + 'was @II', + 'my', + 'mine', + 'myself', + 'my' + ); + + if ($person == 2) { + $tmp = preg_replace('/\bare you\b/i', 'am @II', $tmp); // simple second to first transform + $tmp = preg_replace('/\byou and i\b/i', 'y ou and @II', $tmp); // fix the "Me and you" glitch + $tmp = preg_replace($simpleSecondPersonPatterns, $simpleFirstPersonReplacements, $tmp); // "simple" second to keyed first transform + $tmp = preg_replace($simpleFirstPersonPatterns, $simpleSecondPersonKeyedReplacements, $tmp); // simple first to keyed second transform + $tmp = preg_replace($_SESSION['transform_list']['secondPersonPatterns'], $_SESSION['transform_list']['firstPersonReplacements'], $tmp); // second to first transform + $tmp = preg_replace('/\bme\b/i', 'you', $tmp); // simple second to first transform (me) + #$tmp = preg_replace('/\bi\b/i', 'y ou', $tmp); // simple second to first transform (I) + $tmp = preg_replace('/\byou\b/i', 'me', $tmp); // simple second to first transform + $tmp = str_replace('you', 'you', $tmp); // replace second person key (y ou) with non-keyed value (you) + $tmp = str_replace(' me', ' me', $tmp); // replace first person key (m e) with non-keyed value (me) + $tmp = str_replace(' my', ' my', $tmp); // replace first person key (m e) with non-keyed value (me) + $tmp = str_replace('my ', 'my ', $tmp); // replace first person key (m e) with non-keyed value (me) + $tmp = str_replace(' mine', ' mine', $tmp); // replace first person key (m e) with non-keyed value (me) + $tmp = str_replace('mine ', 'mine ', $tmp); // replace first person key (m e) with non-keyed value (me) + $tmp = str_replace(' @II ', ' I ', $tmp); // replace first person key (@I) with non-keyed value (I) + $tmp = str_replace('@II ', 'I ', $tmp); // replace first person key (@I) with non-keyed value (I) + $tmp = str_replace(' @II', ' I', $tmp); // replace first person key (@I) with non-keyed value (I) + $tmp = str_replace('@you', 'I', $tmp); // replace first person key (@I) with non-keyed value (I) + #$tmp = ucfirst( $tmp); + } +elseif ($person == 3) { + + + $tmp = preg_replace($_SESSION['transform_list']['firstPersonPatterns'], $_SESSION['transform_list']['thirdPersonReplacements'], $tmp); // first to third transform, but only when specifically needed + $tmp = preg_replace('/(\byour gender\b)/i',$g3,$tmp); + $tmp = preg_replace('/(\bthey\b)/i',$g3,$tmp); + $tmp = preg_replace('/(\bi\b)/i',$g3,$tmp); + $tmp = preg_replace('/(\bme\b)/i',$g3,$tmp); + + } + + + //debug + // if (RUN_DEBUG) runDebug(4, __FILE__, __FUNCTION__, __LINE__,"
    \nTransformation complete. was: $in, is: $tmp"); + return $tmp; + //return + +} + +/** + * function parse_matched_aiml() + * This function controls and triggers all the functions to parse the matched aiml + * @param array $convoArr - the conversation array + * @param string $type - normal or srai + * @return array $convoArr - the updated conversation array +**/ +function parse_matched_aiml($convoArr,$type="normal") +{ + //which debug mode? + runDebug( __FILE__, __FUNCTION__, __LINE__, "Run the aiml parse in $type mode (normal or srai)",3); + $convoArr = expand_shorthand_to_longhand($convoArr); + $convoArr = set_wildcards($convoArr); + $convoArr = aiml_to_phpfunctions($convoArr); + + if($type!="srai"){ + runDebug( __FILE__, __FUNCTION__, __LINE__, "$type - Saving for next turn",4); + $convoArr = save_for_nextturn($convoArr); + } else { + runDebug( __FILE__, __FUNCTION__, __LINE__, "$type - Not saving for next turn",4); + } + return $convoArr; +} + +/** + * function clean_that() + * This function cleans the 'that' of html and other bits and bobs + * @param string $that - the string to clean + * @return string $that - the cleaned string +**/ +function clean_that($that) +{ + $in = $that; + $that = str_replace("
    ",".",$that); + $that = strip_tags($that); + $that= remove_allpuncutation($that); + $that= whitespace_clean($that); + $that= captialise($that); + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Cleaning the that - that: $in cleanthat:$that",4); + + return $that; +} + +/** + * function save_for_nextturn() + * This function puts the bot results of an srai search into the main convoArr + * @param array $convoArr - the conversation array + * @return array $convoArr - the updated conversation array +**/ +function save_for_nextturn($convoArr) +{ + runDebug( __FILE__, __FUNCTION__, __LINE__, "Saving that and that_raw for next turn",4); + + $savethis = get_convo_var($convoArr,'aiml','parsed_template'); + + $convoArr = push_on_front_convoArr('that_raw',$savethis,$convoArr); + $convoArr = push_on_front_convoArr('that',$savethis,$convoArr); + return $convoArr; +} + +/** + * function set_wildcards() + * This function extracts wildcards from a patterns and puts their values in their associated array + * @param array $convoArr - the conversation array + * @return array $convoArr - the updated conversation array +**/ +function set_wildcards($convoArr) +{ + runDebug( __FILE__, __FUNCTION__, __LINE__, "Setting Wildcards",4); + + $aiml_pattern = get_convo_var($convoArr,'aiml','pattern'); + + + $ap = $aiml_pattern; + $ap = str_replace("+","\+",$ap); + $ap = str_replace("*","(.*)",$ap); + $ap = str_replace("_","(.*)",$ap); + + $wildcards = str_replace("_","(.*)?",str_replace("*","(.*)?",$aiml_pattern)); + + if($wildcards!=$aiml_pattern) + { + if(!isset($convoArr['aiml']['user_raw'])) { + $checkagainst = $convoArr['aiml']['lookingfor']; + } else { + $checkagainst = $convoArr['aiml']['user_raw']; + } + + if (preg_match('/'.$ap.'/si', $checkagainst, $matches,PREG_OFFSET_CAPTURE)) { + + $totalStars = count($matches)-1; + for($i = $totalStars; $i>=1; $i--){ + $convoArr = push_on_front_convoArr('star',$matches[$i][0],$convoArr); + } + } + } + return $convoArr; +} + +/** + * function expand_shorthand_to_longhand() + * This function replaces the annotated aiml with the long hand version + * and also makes some changes to the original aiml to make it program o friendly + * @param array $convoArr - the conversation array + * @return array $convoArr - the updated conversation array +**/ +function expand_shorthand_to_longhand($convoArr) +{ + global $allowed_html_tags; + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Expanding shorthand to longhand",4); + + $template = $convoArr['aiml']['template']; + $convoArr['aiml']['shorthand_template']=htmlentities($template); + + $template = trim($template); + + $i=0; + $find[$i]='#index="([0-9]*),(\*)"#'; + $replace[$i]='index="$1,all"'; + + $i++; + $find[$i]='#PUSH(.*)#'; + $replace[$i]='$1'; + + $i++; + $find[$i]='#\s|\s+#'; + $replace[$i]=' '; + + $i++; + $find[$i]='##'; + $replace[$i]=' '; + + + $template = preg_replace($find, $replace, $template); + runDebug( __FILE__, __FUNCTION__, __LINE__, "Completed preg_replace expansion: ".htmlentities($template),4); + + + //TODO not sure if this is correct implementation of star if lots of problems will see about making changes in future releases + $template = str_replace("\"",$template); + $template = str_replace(" />","/>",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + + $template = str_replace("","",$template); + $template = str_replace('',' ',$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("","",$template); + $template = str_replace("POP","",$template); + $template = str_replace("",$template); + $template = str_replace("]]>","",$template); + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Completed str_replace expansion: ".htmlentities($template),4); + + //this might actually have no aiml in it all so lets check. + if(substr($template, 0, 1)!="<") + { + $template = "$template"; + } + else + { + $htmls = implode("|",$allowed_html_tags); + //$matches = preg_match_all("# ?(([^\.\?!]*)+(?:[\.\?!]|(?:
    0) + { + $chktag = trim($tags[0][0]); + $len = strlen($chktag); + if(substr(trim($template), 0, $len)==$chktag) + { + $template = "$template"; + } + } + } + $template = trim($template); + $ex = explode("<",$template); + $ex = remove_nulls_from_array($ex); + + if((trim($ex[0])=="think>") && (trim($ex[count($ex)-1])=="/think>")) + { + $template = rtrim($template,""); + $template = ltrim($template,""); + $template .= ""; + } + + if(trim($ex[0])=="think>") + { + $template = str_replace_once("" , "" , $template); + $template = str_replace_once("" , " " , $template); + $template .= ""; + $template = str_replace_once(" " , " " , $template); + } + + //$template = preg_replace("#([^<]*)<#si","$1<",$template); + $template = str_replace("","
    ",$template); + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Completed Program O specific: ".htmlentities($template),4); + + $convoArr['aiml']['template']=$template; + $convoArr['aiml']['longhand_template']=htmlentities($template); + return $convoArr; +} + + +/** + * function str_replace_once() + * This function replaces the first occurence of $needle in $haystack + * @param string $needle - look for this + * @param $replace - replace with this + * @param $haystack - look in this + * @return string - the replaced string +**/ +function str_replace_once($needle , $replace , $haystack){ + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Replacing $needle in $haystack with $replace",4); + + $pos = strpos($haystack, $needle); + if ($pos === false) { + // Nothing found + return $haystack; + } + return substr_replace($haystack, $replace, $pos, strlen($needle)); +} + + +/** + * function make_null() + * This function removes a string + * @param string $c - the string to remove + * @return "" - to overwrite the sent string +**/ +function make_null($c) +{ + runDebug( __FILE__, __FUNCTION__, __LINE__, "Deleting $c from string",4); + return ""; +} + +/** + * function set_simple() + * This function sets simple client properties + * @param array &$convoArr - a reference to the existing conversation array + * @param string $index - the array index to set + * @param string $value - the value of the array index + * @return string $value - the value of the array index +**/ +function set_simple(&$convoArr,$index,$value) +{ + runDebug( __FILE__, __FUNCTION__, __LINE__, "Setting simple value $index to equal $value",4); + + if($index=="topic") + { + $convoArr = push_on_front_convoArr('topic',$value,$convoArr); + } + else { + $convoArr['client_properties'][$index]=$value; + + } + return $value." "; +} + +/** + * function select_random() + * This function is given a string of
  • 's it is then converted into PHP code so that a random option can be selected + * @param string $random_options - a string of
  • this
  • that
  • options to be made into an array and PHP code + * @return array $array - the clean array +**/ +function select_random($random_options) +{ + //echo "HERE WITH ".htmlentities($random_options); + //initialise php code string + $str = ""; + //check if there is another random in here + $find="#(.*)#is"; + + if(preg_match($find, $random_options, $matches)) + { + //found a match and recall this function + $replace = select_random($matches[1]); + $find="#(.*)#is"; + + $random_options = preg_replace($find,$replace,$random_options); + } + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Building an array to select random option from",4); + + $arrayname = "$".get_random_str(); + + //split up the
  • 's + $random_options = preg_split('#
  • |
  • #',$random_options); + //remove any blanks from array + $random_options = remove_nulls_from_array($random_options); + //count the total options + $mx= count($random_options)-1; + //add to php code string + $str .= "'; \r\n\r\n$arrayname = rand(0,$mx);\r\n\r\n"; + + //build big if else so that we can select a random options later + foreach($random_options as $index => $value) + { + if($index==0) + { + $str .= "if(".$arrayname."=='".$index."'){\r\n\t\$tmp_botsay.= '".$value."'; }\r\n"; + } + elseif((count($random_options)-1)==$index) + { + $str .= "else{\r\n\t\$tmp_botsay.= '". $value ."'; }\r\n\r\n"; + $str .= "\$tmp_botsay .= '"; + } + else + { + $str .= "elseif(".$arrayname."=='".$index."'){\r\n\t\$tmp_botsay.= '".$value."'; }\r\n"; + } + } + //return the php code string + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Built PHP code string so that we can select option later: $str",4); + + return $str; +} + +/** + * function remove_nulls_from_array() + * This function is just a little cleaning function to remove nulls from a given array + * @param array $array - the array to clean + * @return array $array - the clean array +**/ +function remove_nulls_from_array($array) +{ + runDebug( __FILE__, __FUNCTION__, __LINE__, "Removing nulls from array",4); + $newArray=array(); + foreach($array as $index => $value) + { + if(($value != NULL) && (trim($value)!="")) + { + $newArray[]=$value; + } + } + + return $newArray; +} + +/** + * function run_srai() + * This function controls the SRAI recursion calls + * @param array &$convoArr - a reference to the existing conversation array + * @param string $now_look_for_this - the text to search for + * @return string $srai_parsed_template - the result of the search +**/ +function run_srai(&$convoArr,$now_look_for_this) +{ + global $srai_iterations, $offset,$error_response; + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Running SRAI $srai_iterations on $now_look_for_this",3); + runDebug( __FILE__, __FUNCTION__, __LINE__, $convoArr['aiml']['html_template'],4); + //number of srai iterations - will stop recursion if it is over 10 + $srai_iterations++; + runDebug( __FILE__, __FUNCTION__, __LINE__, "Incrementing srai iterations to $srai_iterations",4); + if($srai_iterations>10){ + runDebug( __FILE__, __FUNCTION__, __LINE__, "ERROR - Too much recursion breaking out",1); + $convoArr['aiml']['parsed_template']=$error_response; + return $error_response; + } + + $tmp_convoArr = array(); + $tmp_convoArr = $convoArr; + + $tmp_convoArr['aiml']=array(); + $tmp_convoArr['that'][$offset][$offset]=""; //added + $tmp_convoArr['aiml']['parsed_template']=""; + $tmp_convoArr['aiml']['lookingfor']=$now_look_for_this; + + $tmp_convoArr = get_aiml_to_parse($tmp_convoArr); + $tmp_convoArr = parse_matched_aiml($tmp_convoArr,"srai"); + + $srai_parsed_template = $tmp_convoArr['aiml']['parsed_template']; + + runDebug( __FILE__, __FUNCTION__, __LINE__, "SRAI Found: '$srai_parsed_template'",2); + + $convoArr['client_properties'] = $tmp_convoArr['client_properties']; + $convoArr['topic'] = $tmp_convoArr['topic']; + $convoArr['stack'] = $tmp_convoArr['stack']; + + + $srai_iterations--; + runDebug( __FILE__, __FUNCTION__, __LINE__, "Decrementing srai iterations to $srai_iterations",4); + return $srai_parsed_template." "; + +} + + + +/** + * function format() + * This function formats a given text + * @param string $format - the format + * @param string $texttoformat - the text to format + * @return string $texttoformat - the formated text +**/ +function format($format,$texttoformat) +{ + switch($format) + { + case "uppercase": + $texttoformat = strtoupper($texttoformat); + break; + case "lowercase": + $texttoformat = strtolower($texttoformat); + break; + case "formal": + $texttoformat = ucwords($texttoformat); + break; + case "sentence": + $texttoformat = ucfirst($texttoformat); + break; + } + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Formated the text to: $texttoformat",4); + + return $texttoformat; +} + +/** + * function url_encode_star() + * This function encode the star value to make it safe for web addresses + * @param array $convoArr - conversation array + * @return string $encoded_star - the encoded string +**/ +function url_encode_star($convoArr) +{ + + $encoded_star = urlencode(get_convo_var($convoArr,'star')); + runDebug( __FILE__, __FUNCTION__, __LINE__, "Urlencoded the string to: $encoded_star",4); + return $encoded_star; +} + + +/** + * function push_stack() + * This function pushes an item on to the stack + * @param array $convoArr - conversation array + * @param string $item - the item to push + * @return string $item - the pushed item +**/ +function push_stack(&$convoArr,$item) +{ + if((trim($item))!=(trim($convoArr['stack']['top']))){ + runDebug( __FILE__, __FUNCTION__, __LINE__, "Pushing $item onto to the stack",4); + $convoArr['stack']['last'] = $convoArr['stack']['seventh']; + $convoArr['stack']['seventh'] = $convoArr['stack']['sixth']; + $convoArr['stack']['sixth'] = $convoArr['stack']['fifth']; + $convoArr['stack']['fifth'] = $convoArr['stack']['fourth']; + $convoArr['stack']['fourth'] = $convoArr['stack']['third']; + $convoArr['stack']['third'] = $convoArr['stack']['second']; + $convoArr['stack']['second'] = $convoArr['stack']['top']; + $convoArr['stack']['top'] = $item; + }else{ + runDebug( __FILE__, __FUNCTION__, __LINE__, "Could not push empty item onto to the stack",1); + } + + return $item; +} + + +/** + * function pop_stack() + * This function pops an item off the stack + * @param array $convoArr - conversation array + * @return string $item - the popped item +**/ +function pop_stack(&$convoArr) +{ + $item = trim($convoArr['stack']['top']); + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Popped $item off the stack",4); + + $convoArr['stack']['top'] = $convoArr['stack']['second']; + $convoArr['stack']['second'] = $convoArr['stack']['third']; + $convoArr['stack']['third'] = $convoArr['stack']['fourth']; + $convoArr['stack']['fourth'] = $convoArr['stack']['fifth']; + $convoArr['stack']['fifth'] = $convoArr['stack']['sixth']; + $convoArr['stack']['sixth'] = $convoArr['stack']['seventh']; + $convoArr['stack']['seventh'] = $convoArr['stack']['last']; + $convoArr['stack']['last'] = "om"; + return $item; +} + +/** + * function make_learn() + * This function builds the sql insert a learnt aiml cateogry in to the db + * @param array $convoArr - conversation array + * @param string $pattern - the pattern we will insert + * @param string $template - the template to insert +**/ +function make_learn($convoArr, $pattern, $template) +{ + global $con,$dbn; + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Making learn",2); + runDebug( __FILE__, __FUNCTION__, __LINE__, "Pattern: $pattern",2); + runDebug( __FILE__, __FUNCTION__, __LINE__, "Template: $template",2); + + $pattern = clean_for_aiml_match($pattern); + $aiml = " $pattern "; + $aiml = mysql_escape_string($aiml); + $pattern = mysql_escape_string($pattern." "); + $template = mysql_escape_string($template." "); + $u_id = $convoArr['conversation']['user_id']; + $bot_id = $convoArr['conversation']['bot_id']; + + $sql = "INSERT INTO `$dbn`.`aiml_userdefined` + VALUES + (NULL, '$aiml','$pattern','$template','$u_id','$bot_id',NOW())"; + runDebug( __FILE__, __FUNCTION__, __LINE__, "Make learn SQL: $sql",3); + $res = mysql_query($sql,$con); +} + +/** + * function run_system() + * This function runs the system math operations + * @param char $operator - maths operator + * @param int $num_1 - the first number + * @param int $num_2 - the second number + * @param int $output - the result of the math operation +**/ +function run_system($operator,$num_1,$num_2="") +{ + runDebug( __FILE__, __FUNCTION__, __LINE__, "Running system tag math $num_1 $operator $num_2",4); + + switch (strtolower($operator)) { + case "add": + $output = $num_1 + $num_2; + break; + case "subtract": + $output = $num_1 - $num_2; + break; + case "multiply": + $output = $num_1 * $num_2; + break; + case "divide": + if ($num_2 == 0) { + $output = "You can't divide by 0!"; + } else { + $output = $num_1 / $num_2; + } + break; + case "sqrt": + $output = sqrt($num_1); + break; + case "power": + $output = pow($num_1, $num_2); + break; + default: + $output = $operator."?"; + } + + return $output; + +} ?> \ No newline at end of file diff --git a/chatbot/core/aiml/replace_tomakesafe.php b/chatbot/core/aiml/replace_tomakesafe.php index 43f65f1..37e3faa 100644 --- a/chatbot/core/aiml/replace_tomakesafe.php +++ b/chatbot/core/aiml/replace_tomakesafe.php @@ -19,7 +19,7 @@ * @return the en/decoded string **/ function entity_replace($whichway,$text){ - runDebug( __FILE__, __FUNCTION__, __LINE__, "$whichway",3); + runDebug( __FILE__, __FUNCTION__, __LINE__, "$whichway",4); //array of the symbols to be encoded decoded $html_entity_symbols =array( @@ -207,7 +207,7 @@ function entity_replace($whichway,$text){ * @return the en/decoded string **/ function foreignchar_replace($whichway,$text){ - runDebug( __FILE__, __FUNCTION__, __LINE__, "$whichway",3); + runDebug( __FILE__, __FUNCTION__, __LINE__, "$whichway",4); $foreign_char_array = array( 'Š'=>'htmlentity_foreign_big_S_caron', diff --git a/chatbot/core/conversation/display_conversation.php b/chatbot/core/conversation/display_conversation.php index 8312561..7338334 100644 --- a/chatbot/core/conversation/display_conversation.php +++ b/chatbot/core/conversation/display_conversation.php @@ -1,188 +1,188 @@ -0){ - while($row=mysql_fetch_array($result,MYSQL_ASSOC)){ - $allrows[]=$row; - } - $orderedRows = array_reverse($allrows,false); - } - else - { - $orderedRows =array('id'=>NULL, 'input'=>"", 'response'=>"", 'userid'=>$convoArr['conversation']['user_id'], 'bot_id'=>$convoArr['conversation']['bot_id'], 'timestamp'=>""); - - } - - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Found '".db_res_count($result)."' lines of conversation",1); - - return $orderedRows; -} - -/** - * function get_conversation() - * This function gets the conversation format - * @param array $convoArr - the conversation array - * @return array $convoArr -**/ -function get_conversation($convoArr) -{ - $conversation = get_conversation_to_display($convoArr); - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Processing conversation as ".$convoArr['conversation']['format'],1); - - switch($convoArr['conversation']['format']) - { - case "html": - $convoArr = get_html($convoArr,$conversation); - break; - case "json": - $convoArr = get_json($convoArr,$conversation); - break; - case "xml": - $convoArr = get_xml($convoArr,$conversation); - break; - } - return $convoArr; -} - -/** - * function get_html() - * This function formats the response as html - * @param array $convoArr - the conversation array - * @param array $conversation - the conversation lines to format - * @return array $convoArr -**/ -function get_html($convoArr,$conversation) -{ - $conversation_lines = $convoArr['conversation']['conversation_lines']; - $show= ""; - $user_name = $convoArr['conversation']['user_name']; - $bot_name = $convoArr['conversation']['bot_name']; - foreach($conversation as $index => $conversation_subarray){ - $show .= "
    $user_name: ".stripslashes($conversation_subarray['input'])."
    "; - $show .= "
    $bot_name: ".stripslashes($conversation_subarray['response'])."
    "; - - - - - } - - $convoArr['send_to_user']=$show; - runDebug( __FILE__, __FUNCTION__, __LINE__, "Returning HTML",3); - return $convoArr; -} - - -/** - * function get_json() - * This function formats the response as json - * @param array $convoArr - the conversation array - * @param array $conversation - the conversation lines to format - * @return array $convoArr -**/ -function get_json($convoArr,$conversation) -{ - $conversation_lines = $convoArr['conversation']['conversation_lines']; - $show_json = array(); - $i=0; - - foreach($conversation as $index => $conversation_subarray){ - - $show_json['convo_id'] = $convoArr['conversation']['convo_id']; - $show_json['usersay'] = stripslashes($conversation_subarray['input']); - $show_json['botsay'] = stripslashes($conversation_subarray['response']); - - - - $i++; - - } - - - - $convoArr['send_to_user']= json_encode($show_json); - runDebug( __FILE__, __FUNCTION__, __LINE__, "Returning JSON",3); - return $convoArr; -} - - -/** - * function get_xml() - * This function formats the response as xml - * @param array $convoArr - the conversation array - * @param array $conversation - the conversation lines to format - * @return array $convoArr -**/ -function get_xml($convoArr,$conversation) -{ - $user_name = $convoArr['conversation']['user_name']; - $user_id = $convoArr['conversation']['user_id']; - $bot_name = $convoArr['conversation']['bot_name']; - - $conversation_lines = $convoArr['conversation']['conversation_lines']; - $convo_xml = "\n \n"; - $convo_xml .= " $bot_name\n"; - $convo_xml .= " $user_name\n"; - $convo_xml .= " \n"; -/* - $convo_xml .= " \n"; -*/ - foreach($conversation as $index => $conversation_subarray) { - $convo_xml .= " ".stripslashes($conversation_subarray['input'])."\n ".stripslashes($conversation_subarray['response'])."\n"; - } - $convo_xml .= " \n"; - #$convo_xml .= " \n \n"; - $convoArr['send_to_user']=$convo_xml; - runDebug( __FILE__, __FUNCTION__, __LINE__, "Returning XML",3); - return $convoArr; -} +0){ + while($row=mysql_fetch_array($result,MYSQL_ASSOC)){ + $allrows[]=$row; + } + $orderedRows = array_reverse($allrows,false); + } + else + { + $orderedRows =array('id'=>NULL, 'input'=>"", 'response'=>"", 'userid'=>$convoArr['conversation']['user_id'], 'bot_id'=>$convoArr['conversation']['bot_id'], 'timestamp'=>""); + + } + + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Found '".db_res_count($result)."' lines of conversation",2); + + return $orderedRows; +} + +/** + * function get_conversation() + * This function gets the conversation format + * @param array $convoArr - the conversation array + * @return array $convoArr +**/ +function get_conversation($convoArr) +{ + $conversation = get_conversation_to_display($convoArr); + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Processing conversation as ".$convoArr['conversation']['format'],4); + + switch($convoArr['conversation']['format']) + { + case "html": + $convoArr = get_html($convoArr,$conversation); + break; + case "json": + $convoArr = get_json($convoArr,$conversation); + break; + case "xml": + $convoArr = get_xml($convoArr,$conversation); + break; + } + return $convoArr; +} + +/** + * function get_html() + * This function formats the response as html + * @param array $convoArr - the conversation array + * @param array $conversation - the conversation lines to format + * @return array $convoArr +**/ +function get_html($convoArr,$conversation) +{ + $conversation_lines = $convoArr['conversation']['conversation_lines']; + $show= ""; + $user_name = $convoArr['conversation']['user_name']; + $bot_name = $convoArr['conversation']['bot_name']; + foreach($conversation as $index => $conversation_subarray){ + $show .= "
    $user_name: ".stripslashes($conversation_subarray['input'])."
    "; + $show .= "
    $bot_name: ".stripslashes($conversation_subarray['response'])."
    "; + + + + + } + + $convoArr['send_to_user']=$show; + runDebug( __FILE__, __FUNCTION__, __LINE__, "Returning HTML",4); + return $convoArr; +} + + +/** + * function get_json() + * This function formats the response as json + * @param array $convoArr - the conversation array + * @param array $conversation - the conversation lines to format + * @return array $convoArr +**/ +function get_json($convoArr,$conversation) +{ + $conversation_lines = $convoArr['conversation']['conversation_lines']; + $show_json = array(); + $i=0; + + foreach($conversation as $index => $conversation_subarray){ + + $show_json['convo_id'] = $convoArr['conversation']['convo_id']; + $show_json['usersay'] = stripslashes($conversation_subarray['input']); + $show_json['botsay'] = stripslashes($conversation_subarray['response']); + + + + $i++; + + } + + + + $convoArr['send_to_user']= json_encode($show_json); + runDebug( __FILE__, __FUNCTION__, __LINE__, "Returning JSON",4); + return $convoArr; +} + + +/** + * function get_xml() + * This function formats the response as xml + * @param array $convoArr - the conversation array + * @param array $conversation - the conversation lines to format + * @return array $convoArr +**/ +function get_xml($convoArr,$conversation) +{ + $user_name = $convoArr['conversation']['user_name']; + $user_id = $convoArr['conversation']['user_id']; + $bot_name = $convoArr['conversation']['bot_name']; + + $conversation_lines = $convoArr['conversation']['conversation_lines']; + $convo_xml = "\n \n"; + $convo_xml .= " $bot_name\n"; + $convo_xml .= " $user_name\n"; + $convo_xml .= " \n"; +/* + $convo_xml .= " \n"; +*/ + foreach($conversation as $index => $conversation_subarray) { + $convo_xml .= " ".stripslashes($conversation_subarray['input'])."\n ".stripslashes($conversation_subarray['response'])."\n"; + } + $convo_xml .= " \n"; + #$convo_xml .= " \n \n"; + $convoArr['send_to_user']=$convo_xml; + runDebug( __FILE__, __FUNCTION__, __LINE__, "Returning XML",4); + return $convoArr; +} ?> \ No newline at end of file diff --git a/chatbot/core/conversation/intialise_conversation.php b/chatbot/core/conversation/intialise_conversation.php index 5f63c8f..d78aec2 100644 --- a/chatbot/core/conversation/intialise_conversation.php +++ b/chatbot/core/conversation/intialise_conversation.php @@ -1,569 +1,583 @@ -))*)#ui",$value,$sentances); - $cmatch = 0; - - //do another check to make sure the array is not just full of blanks - foreach($sentances as $temp){ - foreach($temp as $chk){ - if(trim($chk)!=""){ - $cmatch++; - } - } - } - - //if there definately is something in the sentance array build the temp sentance array - if(($cmatch>0)&&($matches!==FALSE)){ - foreach($sentances[1] as $index => $value){ - if($arrayIndex=="that"){ - $t = clean_that($value); - if($t!=""){ - $tmp_sentance[] = $t; - } - } - else{ - $tmp_sentance[] = $value; - } - } - - //reverse the array and store - $sentances = array(); - $sentances = array_reverse($tmp_sentance); - } - else{ - $sentances = array(); - if($arrayIndex=="that"){ - $sentances[0] = clean_that($value); - } - else{ - $sentances[0] = $value; - } - } - - //make a space so that [0] is null (in accordance with the AIML array offset) - array_unshift($sentances,NULL); - unset($sentances[0]); - //push this onto the subarray and then clear [0] element (in accordance with the AIML array offset) - array_unshift($convoArr[$arrayIndex],$sentances); - array_unshift($convoArr[$arrayIndex],null); - unset($convoArr[$arrayIndex][0]); - - }else{ - array_unshift($convoArr[$arrayIndex],$value); - array_unshift($convoArr[$arrayIndex],NULL); - } - - for($i=$remember_up_to+1;$i<=count($convoArr[$arrayIndex]);$i++){ - if(isset($convoArr[$arrayIndex][$i])){ - unset($convoArr[$arrayIndex][$i]); - } - } - unset($convoArr[$arrayIndex][0]); - - if($arrayIndex=="topic"){ - push_stack($convoArr,$value); - } - - return $convoArr; -} - -/** - * function load_bot_config() - * A function to get the bot/convo configuration values out of the database - * @param array $convoArr - current state of the conversation - * @return $convoArr (updated) -**/ -function load_bot_config($convoArr){ - global $con,$dbn, $default_format,$default_pattern,$default_update_aiml_code,$default_conversation_lines,$default_remember_up_to,$default_debugemail,$default_debugshow,$default_debugmode,$default_save_state, $error_response; - - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Getting bot config from DB",1); - - //get the values from the db - $sql = "SELECT * FROM `$dbn`.`bots` WHERE bot_id = '".$convoArr['conversation']['bot_id']."'"; - - runDebug( __FILE__, __FUNCTION__, __LINE__, "load bot config SQL: $sql",2); - - $result = db_query($sql,$con) or die('You have a SQL error on line '. __LINE__ . ' of ' . __FILE__ . '. Error message is: ' . mysql_error() . ".
    \nSQL = $sql
    \n"); - - #if(($result)&&(db_res_count($result)>0)){ - if($result !== false and (mysql_num_rows($result) > 0)){ - while($row=mysql_fetch_array($result)){ - //$convoArr['conversation']['format']=$row['format']; //set in the form - $convoArr['conversation']['use_aiml_code']=$row['use_aiml_code']; - $convoArr['conversation']['update_aiml_code']=$row['update_aiml_code']; - $convoArr['conversation']['conversation_lines']=$row['conversation_lines']; - $convoArr['conversation']['remember_up_to']=$row['remember_up_to']; - $convoArr['conversation']['debugemail']=$row['debugemail']; - $convoArr['conversation']['debugshow']=$row['debugshow']; - $convoArr['conversation']['debugmode']=$row['debugmode']; - $convoArr['conversation']['save_state']=$row['save_state']; - $convoArr['conversation']['default_aiml_pattern']=$row['default_aiml_pattern']; - $convoArr['conversation']['bot_parent_id']=$row['bot_parent_id']; - $error_response = $row['error_response']; - } - }else{ - //$convoArr['conversation']['format']=$default_format; //set in the form - $convoArr['conversation']['use_aiml_code']=$default_use_aiml_code; - $convoArr['conversation']['update_aiml_code']=$default_update_aiml_code; - $convoArr['conversation']['conversation_lines']=$default_conversation_lines; - $convoArr['conversation']['remember_up_to']=$default_remember_up_to; - $convoArr['conversation']['debugemail']=$default_debugemail; - $convoArr['conversation']['debugshow']=$default_debugshow; - $convoArr['conversation']['debugmode']=$default_debugmode; - $convoArr['conversation']['save_state']=$default_save_state; - $convoArr['conversation']['default_aiml_pattern']=$default_pattern; - $convoArr['conversation']['bot_parent_id']=0; - } - - //if return format is not html overide the debug type - - if($convoArr['conversation']['format']!="html") - { - $convoArr['conversation']['debugmode']=1; - } - - - - return $convoArr; -} - - -/** - * function log_conversation(() - * A function to log the conversation - * @param array $convoArr - the current state of the conversation array - * @return $convoArr (updated) -**/ -function log_conversation($convoArr){ - - //db globals - global $con,$dbn; - - runDebug( __FILE__, __FUNCTION__, __LINE__, "logging the conversation turn in the db",3); - - //clean and set - $usersay = mysql_escape_string($convoArr['aiml']['user_raw']); - $botsay = mysql_escape_string($convoArr['aiml']['parsed_template']); - $user_id = $convoArr['conversation']['user_id']; - $bot_id = $convoArr['conversation']['bot_id']; - - $sql = "INSERT INTO `$dbn`.`conversation_log` ( - `id` , - `input` , - `response` , - `userid` , - `bot_id` , - `timestamp` - ) - VALUES ( - NULL , '$usersay', '$botsay', '$user_id', '$bot_id', - CURRENT_TIMESTAMP - )"; - - runDebug( __FILE__, __FUNCTION__, __LINE__, "Saving conservation SQL: $sql",2); - db_query($sql,$con); - - return $convoArr; -} - - -/** - * function log_conversation_state(() - * A function to log the conversation - * @param array $convoArr - the current state of the conversation array - * @return $convoArr (updated) -**/ -function log_conversation_state($convoArr){ - - global $con,$dbn; - //get undefined defaults from the db - - runDebug( __FILE__, __FUNCTION__, __LINE__, "logging state",3); - - $serialise_convo = mysql_escape_String(serialize($convoArr)); - $user_id = $convoArr['conversation']['user_id']; - $bot_id = $convoArr['conversation']['bot_id']; - - $sql = "UPDATE `$dbn`.`users` - SET - `state` = '$serialise_convo', - `last_update` = NOW(), - `chatlines` = `chatlines`+1 - WHERE `id` = '$user_id' LIMIT 1"; - - runDebug( __FILE__, __FUNCTION__, __LINE__, "updating conversation state SQL: $sql",2); - db_query($sql,$con); - - return $convoArr; -} - -/** - * function get_conversation_state(() - * A function to log the conversation - * @param array $convoArr - the current state of the conversation array - * @return $convoArr (updated) -**/ -function get_conversation_state($convoArr){ - - global $con,$dbn; - runDebug( __FILE__, __FUNCTION__, __LINE__, "getting state",3); - //get converstation state from the db - $serialise_convo = mysql_escape_string(serialize($convoArr)); - $user_id = $convoArr['conversation']['user_id']; - - $sql = "SELECT * FROM `$dbn`.`users` WHERE `id` = '$user_id' LIMIT 1"; - runDebug( __FILE__, __FUNCTION__, __LINE__, "Getting conversation state SQL: $sql",2); - $result = db_query($sql,$con); - - if(($result)&&(mysql_num_rows($result)>0)){ - $row=mysql_fetch_array($result); - $convoArr = unserialize($row['state']); - } - - return $convoArr; -} - -/** - * function check_set_bot(() - * A function to check and set the bot id - * @param array $convoArr - the current state of the conversation array - * @return $convoArr (updated) -**/ -function check_set_bot($convoArr) -{ - global $con,$dbn, $default_bot_id, $error_response; - //check to see if bot_id has been passed if not load default - if((isset($_REQUEST['bot_id'])) && (trim($_REQUEST['bot_id'])!="")) { - $bot_id=trim($_REQUEST['bot_id']); - } - elseif(isset($convoArr['conversation']['bot_id'])) { - $bot_id = $convoArr['conversation']['bot_id']; - }else{ - $bot_id = $default_bot_id; - } - - //get the values from the db - $sql = "SELECT * FROM `$dbn`.`bots` WHERE bot_id = '$bot_id' and `bot_active`='1'"; - runDebug( __FILE__, __FUNCTION__, __LINE__, "Checking bot exists SQL: $sql",2); - $result = db_query($sql,$con); - if(($result)&&(db_res_count($result)>0)){ - $row = mysql_fetch_assoc($result); - $bot_name = $row['bot_name']; - $error_response = $row['error_response']; - $convoArr['conversation']['bot_name']=$bot_name; - $convoArr['conversation']['bot_id']=$bot_id; - runDebug( __FILE__, __FUNCTION__, __LINE__, "BOT ID: $bot_id",1); - } - else{ - $convoArr['debug']['intialisation_error']="Bot ID: $bot_id does not exist"; - $convoArr['conversation']['bot_id']=$bot_id; - runDebug( __FILE__, __FUNCTION__, __LINE__, "ERROR - Cannot find bot id: $bot_id",1); - } - return $convoArr; -} - -/** - * function check_set_convo_id(() - * A function to check and set the convo id - * @param array $convoArr - the current state of the conversation array - * @return $convoArr (updated) -**/ -function check_set_convo_id($convoArr) -{ - global $default_convo_id; - //check to see if convo_id has been passed if not load default - if((isset($_REQUEST['convo_id'])) && (trim($_REQUEST['convo_id'])!="")) { - $convo_id=trim($_REQUEST['convo_id']); - runDebug( __FILE__, __FUNCTION__, __LINE__, "CONVO ID: $convo_id",1); - }else { - $convo_id = $default_convo_id; - runDebug( __FILE__, __FUNCTION__, __LINE__, "ERROR - Cannot find CONVO ID using default: $convo_id",1); - } - $convoArr['conversation']['convo_id']=$convo_id; - return $convoArr; -} - -/** - * function check_set_convo_id(() - * A function to check and set the convo id - * @param array $convoArr - the current state of the conversation array - * @return $convoArr (updated) -**/ -function check_set_user($convoArr) -{ - global $default_convo_id,$con,$dbn, $unknown_user; - //check to see if user_name has been set if not set as default - $convo_id = (isset($convoArr['conversation']['convo_id'])) ? $convoArr['conversation']['convo_id'] : session_id(); - $bot_id = $convoArr['conversation']['bot_id']; - $ip = $_SERVER['REMOTE_ADDR']; - $sql = "select `name`, `id` from `users` where `session_id` = '$convo_id' limit 1;"; - $result = mysql_query($sql, $con) or $msg = SQL_error(mysql_errno(), __FILE__, __FUNCTION__, __LINE__); - $numRows = mysql_num_rows($result); - if ($numRows == 0) { - $user_id = intisaliseUser($convo_id); - } - else { - $row = mysql_fetch_assoc($result); - $user_id = (!empty($row['id'])) ? $row['id'] : 0; - $user_name = (!empty($row['name'])) ? $row['name'] : 'User'; - } - $convoArr['conversation']['user_name'] = (!empty($user_name)) ? $user_name : $unknown_user; - #die("User name = $user_name
    \n"); - return $convoArr; -} - - -/** - * function check_set_format(() - * A function to check and set the conversation return type - * @param array $convoArr - the current state of the conversation array - * @return $convoArr (updated) -**/ -function check_set_format($convoArr){ - - global $default_format; - - $formatsArr = array('html','xml','json'); - - //at thsi point we can overwrite the conversation format. - if((isset($_REQUEST['format'])) && (trim($_REQUEST['format'])!="")) - { - $format = trim($_REQUEST['format']); - } - else - { - $format = $default_format; - } - - $convoArr['conversation']['format'] = strtolower($format); - - if(!in_array($convoArr['conversation']['format'],$formatsArr)) - { - $convoArr['debug']['intialisation_error']="Incompatible return type: $format"; - runDebug( __FILE__, __FUNCTION__, __LINE__, "ERROR - bad return type: $format",1); - } - else - { - runDebug( __FILE__, __FUNCTION__, __LINE__, "Using format: $format",1); - } - - - return $convoArr; -} - - +))*)#ui",$value,$sentances); + $cmatch = 0; + + //do another check to make sure the array is not just full of blanks + foreach($sentances as $temp){ + foreach($temp as $chk){ + if(trim($chk)!=""){ + $cmatch++; + } + } + } + + //if there definately is something in the sentance array build the temp sentance array + if(($cmatch>0)&&($matches!==FALSE)){ + foreach($sentances[1] as $index => $value){ + if($arrayIndex=="that"){ + $t = clean_that($value); + if($t!=""){ + $tmp_sentance[] = $t; + } + } + else{ + $tmp_sentance[] = $value; + } + } + + //reverse the array and store + $sentances = array(); + $sentances = array_reverse($tmp_sentance); + } + else{ + $sentances = array(); + if($arrayIndex=="that"){ + $sentances[0] = clean_that($value); + } + else{ + $sentances[0] = $value; + } + } + + //make a space so that [0] is null (in accordance with the AIML array offset) + array_unshift($sentances,NULL); + unset($sentances[0]); + //push this onto the subarray and then clear [0] element (in accordance with the AIML array offset) + array_unshift($convoArr[$arrayIndex],$sentances); + array_unshift($convoArr[$arrayIndex],null); + unset($convoArr[$arrayIndex][0]); + + }else{ + array_unshift($convoArr[$arrayIndex],$value); + array_unshift($convoArr[$arrayIndex],NULL); + } + + for($i=$remember_up_to+1;$i<=count($convoArr[$arrayIndex]);$i++){ + if(isset($convoArr[$arrayIndex][$i])){ + unset($convoArr[$arrayIndex][$i]); + } + } + unset($convoArr[$arrayIndex][0]); + + if($arrayIndex=="topic"){ + push_stack($convoArr,$value); + } + + return $convoArr; +} + +/** + * function load_bot_config() + * A function to get the bot/convo configuration values out of the database + * @param array $convoArr - current state of the conversation + * @return $convoArr (updated) +**/ +function load_bot_config($convoArr){ + global $con,$dbn, $default_format,$default_pattern,$default_update_aiml_code,$default_conversation_lines,$default_remember_up_to,$default_debugemail,$default_debugshow,$default_debugmode,$default_save_state, $error_response; + + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Getting bot config from DB",1); + + //get the values from the db + $sql = "SELECT * FROM `$dbn`.`bots` WHERE bot_id = '".$convoArr['conversation']['bot_id']."'"; + + runDebug( __FILE__, __FUNCTION__, __LINE__, "load bot config SQL: $sql",3); + + $result = db_query($sql,$con) or die('You have a SQL error on line '. __LINE__ . ' of ' . __FILE__ . '. Error message is: ' . mysql_error() . ".
    \nSQL = $sql
    \n"); + + #if(($result)&&(db_res_count($result)>0)){ + if($result !== false and (mysql_num_rows($result) > 0)){ + while($row=mysql_fetch_array($result)){ + //$convoArr['conversation']['format']=$row['format']; //set in the form + $convoArr['conversation']['use_aiml_code']=$row['use_aiml_code']; + $convoArr['conversation']['update_aiml_code']=$row['update_aiml_code']; + $convoArr['conversation']['conversation_lines']=$row['conversation_lines']; + $convoArr['conversation']['remember_up_to']=$row['remember_up_to']; + $convoArr['conversation']['debugemail']=$row['debugemail']; + $convoArr['conversation']['debugshow']=$row['debugshow']; + $convoArr['conversation']['debugmode']=$row['debugmode']; + $convoArr['conversation']['save_state']=$row['save_state']; + $convoArr['conversation']['default_aiml_pattern']=$row['default_aiml_pattern']; + $convoArr['conversation']['bot_parent_id']=$row['bot_parent_id']; + $error_response = $row['error_response']; + } + }else{ + //$convoArr['conversation']['format']=$default_format; //set in the form + $convoArr['conversation']['use_aiml_code']=$default_use_aiml_code; + $convoArr['conversation']['update_aiml_code']=$default_update_aiml_code; + $convoArr['conversation']['conversation_lines']=$default_conversation_lines; + $convoArr['conversation']['remember_up_to']=$default_remember_up_to; + $convoArr['conversation']['debugemail']=$default_debugemail; + $convoArr['conversation']['debugshow']=$default_debugshow; + $convoArr['conversation']['debugmode']=$default_debugmode; + $convoArr['conversation']['save_state']=$default_save_state; + $convoArr['conversation']['default_aiml_pattern']=$default_pattern; + $convoArr['conversation']['bot_parent_id']=0; + } + + //if return format is not html overide the debug type + + if($convoArr['conversation']['format']!="html") + { + $convoArr['conversation']['debugmode']=1; + } + + + + return $convoArr; +} + + +/** + * function log_conversation(() + * A function to log the conversation + * @param array $convoArr - the current state of the conversation array + * @return $convoArr (updated) +**/ +function log_conversation($convoArr){ + + //db globals + global $con,$dbn; + + runDebug( __FILE__, __FUNCTION__, __LINE__, "logging the conversation turn in the db",4); + + //clean and set + $usersay = mysql_escape_string($convoArr['aiml']['user_raw']); + $botsay = mysql_escape_string($convoArr['aiml']['parsed_template']); + $user_id = $convoArr['conversation']['user_id']; + $bot_id = $convoArr['conversation']['bot_id']; + + $sql = "INSERT INTO `$dbn`.`conversation_log` ( + `id` , + `input` , + `response` , + `userid` , + `bot_id` , + `timestamp` + ) + VALUES ( + NULL , '$usersay', '$botsay', '$user_id', '$bot_id', + CURRENT_TIMESTAMP + )"; + + runDebug( __FILE__, __FUNCTION__, __LINE__, "Saving conservation SQL: $sql",4); + db_query($sql,$con); + + return $convoArr; +} + + +/** + * function log_conversation_state(() + * A function to log the conversation + * @param array $convoArr - the current state of the conversation array + * @return $convoArr (updated) +**/ +function log_conversation_state($convoArr){ + + global $con,$dbn; + //get undefined defaults from the db + + runDebug( __FILE__, __FUNCTION__, __LINE__, "logging state",4); + + $serialise_convo = mysql_escape_String(serialize($convoArr)); + $user_id = $convoArr['conversation']['user_id']; + $bot_id = $convoArr['conversation']['bot_id']; + + $client_name = get_convo_var($convoArr,'client_properties','name'); + if(isset($client_name) && ($client_name!="")) + { + $sql_addon = "`name` = '". mysql_escape_string($client_name)."', "; + } + else + { + $sql_addon = ""; + } + + + $sql = "UPDATE `$dbn`.`users` + SET + `state` = '$serialise_convo', + `last_update` = NOW(), + $sql_addon + `chatlines` = `chatlines`+1 + WHERE `id` = '$user_id' LIMIT 1"; + + + + runDebug( __FILE__, __FUNCTION__, __LINE__, "updating conversation state SQL: $sql",3); + db_query($sql,$con); + + return $convoArr; +} + +/** + * function get_conversation_state(() + * A function to log the conversation + * @param array $convoArr - the current state of the conversation array + * @return $convoArr (updated) +**/ +function get_conversation_state($convoArr){ + + global $con,$dbn; + runDebug( __FILE__, __FUNCTION__, __LINE__, "getting state",4); + //get converstation state from the db + $serialise_convo = mysql_escape_string(serialize($convoArr)); + $user_id = $convoArr['conversation']['user_id']; + + $sql = "SELECT * FROM `$dbn`.`users` WHERE `id` = '$user_id' LIMIT 1"; + runDebug( __FILE__, __FUNCTION__, __LINE__, "Getting conversation state SQL: $sql",3); + $result = db_query($sql,$con); + + if(($result)&&(mysql_num_rows($result)>0)){ + $row=mysql_fetch_array($result); + $convoArr = unserialize($row['state']); + } + + return $convoArr; +} + +/** + * function check_set_bot(() + * A function to check and set the bot id + * @param array $convoArr - the current state of the conversation array + * @return $convoArr (updated) +**/ +function check_set_bot($convoArr) +{ + global $con,$dbn, $default_bot_id, $error_response; + //check to see if bot_id has been passed if not load default + if((isset($_REQUEST['bot_id'])) && (trim($_REQUEST['bot_id'])!="")) { + $bot_id=trim($_REQUEST['bot_id']); + } + elseif(isset($convoArr['conversation']['bot_id'])) { + $bot_id = $convoArr['conversation']['bot_id']; + }else{ + $bot_id = $default_bot_id; + } + + //get the values from the db + $sql = "SELECT * FROM `$dbn`.`bots` WHERE bot_id = '$bot_id' and `bot_active`='1'"; + runDebug( __FILE__, __FUNCTION__, __LINE__, "Checking bot exists SQL: $sql",3); + $result = db_query($sql,$con); + if(($result)&&(db_res_count($result)>0)){ + $row = mysql_fetch_assoc($result); + $bot_name = $row['bot_name']; + $error_response = $row['error_response']; + $convoArr['conversation']['bot_name']=$bot_name; + $convoArr['conversation']['bot_id']=$bot_id; + runDebug( __FILE__, __FUNCTION__, __LINE__, "BOT ID: $bot_id",2); + } + else{ + $convoArr['debug']['intialisation_error']="Bot ID: $bot_id does not exist"; + $convoArr['conversation']['bot_id']=$bot_id; + runDebug( __FILE__, __FUNCTION__, __LINE__, "ERROR - Cannot find bot id: $bot_id",1); + } + return $convoArr; +} + +/** + * function check_set_convo_id(() + * A function to check and set the convo id + * @param array $convoArr - the current state of the conversation array + * @return $convoArr (updated) +**/ +function check_set_convo_id($convoArr) +{ + global $default_convo_id; + //check to see if convo_id has been passed if not load default + if((isset($_REQUEST['convo_id'])) && (trim($_REQUEST['convo_id'])!="")) { + $convo_id=trim($_REQUEST['convo_id']); + runDebug( __FILE__, __FUNCTION__, __LINE__, "CONVO ID: $convo_id",2); + }else { + $convo_id = $default_convo_id; + runDebug( __FILE__, __FUNCTION__, __LINE__, "ERROR - Cannot find CONVO ID using default: $convo_id",1); + } + $convoArr['conversation']['convo_id']=$convo_id; + return $convoArr; +} + +/** + * function check_set_convo_id(() + * A function to check and set the convo id + * @param array $convoArr - the current state of the conversation array + * @return $convoArr (updated) +**/ +function check_set_user($convoArr) +{ + global $default_convo_id,$con,$dbn, $unknown_user; + //check to see if user_name has been set if not set as default + $convo_id = (isset($convoArr['conversation']['convo_id'])) ? $convoArr['conversation']['convo_id'] : session_id(); + $bot_id = $convoArr['conversation']['bot_id']; + $ip = $_SERVER['REMOTE_ADDR']; + $sql = "select `name`, `id` from `users` where `session_id` = '$convo_id' limit 1;"; + $result = mysql_query($sql, $con) or $msg = SQL_error(mysql_errno(), __FILE__, __FUNCTION__, __LINE__); + $numRows = mysql_num_rows($result); + if ($numRows == 0) { + $user_id = intisaliseUser($convo_id); + } + else { + $row = mysql_fetch_assoc($result); + $user_id = (!empty($row['id'])) ? $row['id'] : 0; + $user_name = (!empty($row['name'])) ? $row['name'] : 'User'; + } + $convoArr['conversation']['user_name'] = (!empty($user_name)) ? $user_name : $unknown_user; + #die("User name = $user_name
    \n"); + return $convoArr; +} + + +/** + * function check_set_format(() + * A function to check and set the conversation return type + * @param array $convoArr - the current state of the conversation array + * @return $convoArr (updated) +**/ +function check_set_format($convoArr){ + + global $default_format; + + $formatsArr = array('html','xml','json'); + + //at thsi point we can overwrite the conversation format. + if((isset($_REQUEST['format'])) && (trim($_REQUEST['format'])!="")) + { + $format = trim($_REQUEST['format']); + } + else + { + $format = $default_format; + } + + $convoArr['conversation']['format'] = strtolower($format); + + if(!in_array($convoArr['conversation']['format'],$formatsArr)) + { + $convoArr['debug']['intialisation_error']="Incompatible return type: $format"; + runDebug( __FILE__, __FUNCTION__, __LINE__, "ERROR - bad return type: $format",1); + } + else + { + runDebug( __FILE__, __FUNCTION__, __LINE__, "Using format: $format",2); + } + + + return $convoArr; +} + + ?> \ No newline at end of file diff --git a/chatbot/core/conversation/load_convofunctions.php b/chatbot/core/conversation/load_convofunctions.php index 790ccea..5ed84fb 100644 --- a/chatbot/core/conversation/load_convofunctions.php +++ b/chatbot/core/conversation/load_convofunctions.php @@ -13,5 +13,5 @@ include_once("display_conversation.php"); include_once("make_conversation.php"); -runDebug( __FILE__, __FUNCTION__, __LINE__, "Convofunction include files loaded",1); +runDebug( __FILE__, __FUNCTION__, __LINE__, "Convofunction include files loaded",4); ?> \ No newline at end of file diff --git a/chatbot/core/conversation/make_conversation.php b/chatbot/core/conversation/make_conversation.php index 518624b..7f7ce8d 100644 --- a/chatbot/core/conversation/make_conversation.php +++ b/chatbot/core/conversation/make_conversation.php @@ -17,7 +17,7 @@ **/ function make_conversation($convoArr){ - runDebug( __FILE__, __FUNCTION__, __LINE__, "Making conversation",1); + runDebug( __FILE__, __FUNCTION__, __LINE__, "Making conversation",4); global $offset; //get the user input and clean it //$convoArr = clean_for_aiml_match('user_say','lookingfor',$convoArr); @@ -43,11 +43,11 @@ function make_conversation($convoArr){ **/ function add_aiml_to_php($convoArr){ - runDebug( __FILE__, __FUNCTION__, __LINE__, "Adding PHP to table",1); + runDebug( __FILE__, __FUNCTION__, __LINE__, "Adding PHP to table",4); global $dbn,$con; $evalthis = mysql_escape_string($convoArr['aiml']['aiml_to_php']); $sql = "UPDATE `$dbn`.`aiml` SET `php_code` = \"$evalthis\" WHERE `id` = '".$convoArr['aiml']['aiml_id']."' LIMIT 1"; - runDebug( __FILE__, __FUNCTION__, __LINE__, "Adding new PHP to aiml table SQL: $sql",2); + runDebug( __FILE__, __FUNCTION__, __LINE__, "Adding new PHP to aiml table SQL: $sql",3); $result = db_query($sql,$con); return $convoArr; } @@ -60,7 +60,7 @@ function add_aiml_to_php($convoArr){ **/ function make_safe_to_eval($evalthis){ - runDebug( __FILE__, __FUNCTION__, __LINE__, "Making it safe to eval",1); + runDebug( __FILE__, __FUNCTION__, __LINE__, "Making it safe to eval",4); $evalthis = str_replace('"','\"',$evalthis); $evalthis = str_replace('$','\$',$evalthis); return $evalthis; @@ -74,7 +74,7 @@ function make_safe_to_eval($evalthis){ **/ function eval_aiml_to_php_code(&$convoArr,$evalthis){ - runDebug( __FILE__, __FUNCTION__, __LINE__, "",1); + runDebug( __FILE__, __FUNCTION__, __LINE__, "",4); $botsay = @run_aiml_to_php($convoArr,$evalthis); //if run correctly $botsay should be re valued return $botsay; @@ -90,14 +90,14 @@ function eval_aiml_to_php_code(&$convoArr,$evalthis){ **/ function run_aiml_to_php(&$convoArr,$evalthis){ - runDebug( __FILE__, __FUNCTION__, __LINE__, "Evaluating Stored PHP Code from the Database",1); + runDebug( __FILE__, __FUNCTION__, __LINE__, "Evaluating Stored PHP Code from the Database",4); global $botsay; global $error_response; //this must be NULL if it is FALSE then its failed but if its NULL its a success $error_flag = eval($evalthis); if($error_flag===NULL){ //success - runDebug( __FILE__, __FUNCTION__, __LINE__, "EVALUATED: $evalthis ",1); + runDebug( __FILE__, __FUNCTION__, __LINE__, "EVALUATED: $evalthis ",4); $result = $botsay; } else { //error runDebug( __FILE__, __FUNCTION__, __LINE__, "ERROR TRYING TO EVAL: $evalthis ",1); diff --git a/chatbot/core/user/handle_user.php b/chatbot/core/user/handle_user.php index 5ac1120..7f92f39 100644 --- a/chatbot/core/user/handle_user.php +++ b/chatbot/core/user/handle_user.php @@ -18,8 +18,10 @@ **/ function load_new_client_defaults($convoArr) { + //to do could put this in an array - runDebug( __FILE__, __FUNCTION__, __LINE__, "Loading client defaults","3"); + //todo check this out + runDebug( __FILE__, __FUNCTION__, __LINE__, "Loading client defaults",1); $convoArr['client_properties']['name'] = "my friend"; $convoArr['client_properties']['id'] = $_SERVER['REMOTE_ADDR']; @@ -56,8 +58,8 @@ function get_user_id($convoArr) $msg = "new"; } - runDebug( __FILE__, __FUNCTION__, __LINE__, "Getting $msg user id:".$convoArr['conversation']['user_id'],"1"); - runDebug( __FILE__, __FUNCTION__, __LINE__, "get_user_id SQL: $sql","2"); + runDebug( __FILE__, __FUNCTION__, __LINE__, "Getting $msg user id:".$convoArr['conversation']['user_id'],4); + runDebug( __FILE__, __FUNCTION__, __LINE__, "get_user_id SQL: $sql",3); return $convoArr; } @@ -95,7 +97,7 @@ function intisaliseUser($convo_id) mysql_query($sql,$con); $user_id = mysql_insert_id($con); - runDebug( __FILE__, __FUNCTION__, __LINE__, "intisaliseUser #$user_id SQL: $sql","2"); + runDebug( __FILE__, __FUNCTION__, __LINE__, "intisaliseUser #$user_id SQL: $sql",3); return $user_id; } diff --git a/chatbot/core/user/load_userfunctions.php b/chatbot/core/user/load_userfunctions.php index 338c371..7bd5c2c 100644 --- a/chatbot/core/user/load_userfunctions.php +++ b/chatbot/core/user/load_userfunctions.php @@ -13,5 +13,5 @@ include_once("user_spellcheck.php"); include_once("handle_user.php"); -runDebug( __FILE__, __FUNCTION__, __LINE__, "userfunctions include files loaded",1); +runDebug( __FILE__, __FUNCTION__, __LINE__, "userfunctions include files loaded",4); ?> \ No newline at end of file diff --git a/chatbot/core/user/user_input_clean.php b/chatbot/core/user/user_input_clean.php index 41b1b62..c191dde 100644 --- a/chatbot/core/user/user_input_clean.php +++ b/chatbot/core/user/user_input_clean.php @@ -1,95 +1,95 @@ - \ No newline at end of file diff --git a/chatbot/core/user/user_spellcheck.php b/chatbot/core/user/user_spellcheck.php index 4416877..3ad68df 100644 --- a/chatbot/core/user/user_spellcheck.php +++ b/chatbot/core/user/user_spellcheck.php @@ -45,7 +45,7 @@ function spellcheck($user_say) $replacement = $row['correction']; if($user_say = preg_replace($pattern, $replacement, $user_say)) { - runDebug( __FILE__, __FUNCTION__, __LINE__, "Replacing ".$row['missspelling']." with ".$row['correction'],3); + runDebug( __FILE__, __FUNCTION__, __LINE__, "Replacing ".$row['missspelling']." with ".$row['correction'],4); } } diff --git a/gui/xml/index.php b/gui/xml/index.php index f558dd4..c7397a0 100644 --- a/gui/xml/index.php +++ b/gui/xml/index.php @@ -1,136 +1,146 @@ - ", $responseXML); - $responseXML = str_replace("\n", "
    ", $responseXML); - - $xml = new SimpleXMLElement($sXML); - $count = 0; - foreach ($xml->children() as $child) { - $childName = $child->getName(); - switch ($childName) { - case 'user_name': - $user_name = $child; - break; - case 'bot_name': - $bot_name = $child; - break; - case 'usersay': - $response .= "$user_name: " . $child . "
    \n"; - break; - case 'botsay': - $response .= "$bot_name: " . $child . "
    \n"; - default: - } - } - - - -function get_response($path){ - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL,$path); - curl_setopt($ch, CURLOPT_FAILONERROR,1); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION,1); - curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); - curl_setopt($ch, CURLOPT_TIMEOUT, 15); - $retValue = curl_exec($ch); - curl_close($ch); - return $retValue; -} - - - -?> - - - - - - - - Program O AIML Chatbot - - - - -
    -
    -

    - - - - - - -

    -
    - - - - + ", $responseXML); + $responseXML = str_replace("\n", "
    ", $responseXML); + + + + + $xml = new SimpleXMLElement($sXML); + $count = 0; + foreach ($xml->children() as $child) { + $childName = $child->getName(); + switch ($childName) { + case 'user_name': + $user_name = $child; + break; + case 'bot_name': + $bot_name = $child; + break; + case 'usersay': + $response .= "$user_name: " . $child . "
    \n"; + break; + case 'botsay': + $response .= "$bot_name: " . $child . "
    \n"; + default: + } + } + + + +function get_response($path){ + + + $strCookie = 'PHPSESSID=' . $_COOKIE['PHPSESSID'] . '; path=/'; + + session_write_close(); + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL,$path); + curl_setopt($ch, CURLOPT_FAILONERROR,1); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION,1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); + curl_setopt($ch, CURLOPT_TIMEOUT, 15); + curl_setopt( $ch, CURLOPT_COOKIE, $strCookie ); + $retValue = curl_exec($ch); + curl_close($ch); + return $retValue; +} + + + +?> + + + + + + + + Program O AIML Chatbot + + + + +
    +
    +

    + + + + + + +

    +
    + + + + \ No newline at end of file diff --git a/library/error_functions.php b/library/error_functions.php index b61fd3b..8fad081 100644 --- a/library/error_functions.php +++ b/library/error_functions.php @@ -1,295 +1,360 @@ - $subArray){ - $log .= "[NEWLINE]".$time.": "; - foreach($subArray as $index=>$value){ - $log .= $index."=".$value.",\t"; - } - } - - //echo ">>>>".$convoArr['conversation']['debugmode']; - - switch($convoArr['conversation']['debugmode']){ - case 0: //show in source code - $log = str_replace("[NEWLINE]","\r\n",$log); - display_on_page(0,$log); - break; - case 1: //write to log file - $log = str_replace("[NEWLINE]","\r\n",$log); - writefile_debug($convoArr); - break; - case 2: //show in webpage - $log = str_replace("[NEWLINE]","
    ",$log); - display_on_page(1,$log); - break; - case 3: //email to user - $log = str_replace("[NEWLINE]","\r\n",$log); - email_debug($convoArr['conversation']['debugemail'],$log); - break; - } - - - return $convoArr; -} - -/** - * function writefile_debug() - * Handles the debug when written to a file - * @param string $filename - the name of the file which is also the convo id - * @param string $log - the data to write -**/ -function writefile_debug($array) -{ - $myFile = _DEBUG_PATH_.session_id().".txt"; - - $mode = "w"; - $file = ""; - $tabs = ""; - - - $file = print_r($array,true); - if (DIRECTORY_SEPARATOR == '\\') { - $file = str_replace("\n", "\r\n", $file); - } - - if(isset($array['conversation'])) - { - - $file .= "\r\n----------------------------------------\r\n"; - - $tmp_array = $array; - unset($tmp_array['debug']); - $file .= str_replace("'","\'",serialize($tmp_array)); - } - file_put_contents($myFile, $file); -} - - - -/** - * function display_on_page() - * Handles the debug when it is displayed on the webpage either in the source or on the page - * @param int $show_on_page - 0=show in source 1=output to user - * @param string $log - the data to show -**/ -function display_on_page($show_on_page,$log) -{ - if($show_on_page==0){ - echo ""; - }else{ - echo "
    ";
    -		print_r($log);
    -		echo "
    "; - } -} - - -/** - * function email_debug() - * Handles the debug when it is emailed to the botmaster - * @param string $email - email address - * @param string $log - the data to send -**/ -function email_debug($email,$log) -{ - $to = $email; - $subject = 'Debug Data'; - $message = $log; - $headers = 'From: '.$email . "\r\n" . - 'Reply-To: '.$email . "\r\n" . - 'X-Mailer: PHP/' . phpversion(); - - mail($to, $subject, $message, $headers); -} - - - - -/** - * function outputDebug() - * Used in the install/upgrade files will display it straightaway - * @param string $fileName - the file the error came from - * @param string $functionName - the function that triggered the error - * @param string $line - the line of code - * @param string $info - the message to display -**/ -function outputDebug($fileName, $functionName, $line, $info) { - - global $srai_iterations; - list($usec, $sec) = explode(" ",microtime()); - - //build timestamp index for the debug array - $string = ((float)$usec + (float)$sec); - $string2 = explode(".", $string); - $index = date("d-m-Y H:i:s", $string2[0]).":".$string2[1]; - - if($srai_iterations<1){ - $sr_it = 0;} - else { - $sr_it = $srai_iterations;} - - //add to array - print "
    ----------------------------------------------------"; - print "
    ".$index.": ".$fileName; - print "
    ".$index.": ".$functionName; - print "
    ".$index.": ".$line; - print "
    ".$index.": ".$info; - print "
    ".$index.": srai:".$sr_it; - print "
    ----------------------------------------------------"; -} - - function SQL_Error($errNum, $file = 'unknown', $function = 'unknown', $line = 'unknown') { - $msg = "There's a problem with your Program O installation. Please run the install script to correct the problem.
    \n"; - switch ($errNum) { - case '1146': - $msg .= "The database and/or table used in the config file doesn't exist.
    \n"; - break; - default: - $msg = "Error number $errNum!
    \n"; - } - return $msg; - } - - function save_file($file, $content, $append = false) { - if (function_exists('file_put_contents')) { - $x = file_put_contents($file, $content, $append); - } - else { - $fileMode = ($append === true) ? "a" : "w"; - $fh = fopen($file, $fileMode)or die("Can't open the file!"); - $cLen = strlen($content); - fwrite($fh, $content, $cLen); - fclose($fh); - } - return 1; - } - + $subArray){ + $log .= $time."[NEWLINE]"; + foreach($subArray as $index=>$value){ + + + + + if(($index == "fileName") || ($index == "functionName") || ($index == "line")) + { + $log .= "[".$value."]"; + } + elseif($index == "info") + { + $log .= "[NEWLINE]".$value."[NEWLINE]-----------------------[NEWLINE]"; + } + + + + } + } + + + $log .= "[NEWLINE]-----------------------[NEWLINE]"; + $log .= "CONVERSATION ARRAY"; + $log .= "[NEWLINE]-----------------------[NEWLINE]"; + + $debuglevel = get_convo_var($convoArr, 'conversation', 'debugshow', '', ''); + + if($debuglevel == 3 ) + { + //show the full array + $showArr = $convoArr; + } + else + { + //show a reduced array + $showArr = reduceConvoArr($convoArr); + } + + + $log .= print_r($showArr,true); + + + + + switch($convoArr['conversation']['debugmode']){ + case 0: //show in source code + $log = str_replace("[NEWLINE]","\r\n",$log); + display_on_page(0,$log); + break; + case 1: //write to log file + $log = str_replace("[NEWLINE]","\r\n",$log); + writefile_debug($log); + break; + case 2: //show in webpage + $log = str_replace("[NEWLINE]","
    ",$log); + display_on_page(1,$log); + break; + + case 3: //email to user + $log = str_replace("[NEWLINE]","\r\n",$log); + email_debug($convoArr['conversation']['debugemail'],$log); + break; + + } + + + return $convoArr; +} + +/** reduceConvoArr() + * A small function to create a smaller convoArr just for debuggin! + * @param array $convoArr - the big array to be reduced + */ +function reduceConvoArr($convoArr) +{ + $showconvoArr = array(); + + $showconvoArr['conversation'] = $convoArr['conversation']; + $showconvoArr['topic']['1'] = $convoArr['topic']['1']; + $showconvoArr['that']['1'] = $convoArr['that']['1']; + $showconvoArr['star']['1'] = $convoArr['star']['1']; + $showconvoArr['input']['1'] = $convoArr['input']['1']; + $showconvoArr['stack']['top'] = $convoArr['stack']['top']; + $showconvoArr['stack']['last'] = $convoArr['stack']['last']; + $showconvoArr['client_properties'] = $convoArr['client_properties']; + $showconvoArr['aiml']['user_raw'] = $convoArr['aiml']['user_raw']; + $showconvoArr['aiml']['lookingfor'] = $convoArr['aiml']['lookingfor']; + $showconvoArr['aiml']['pattern'] = $convoArr['aiml']['pattern']; + $showconvoArr['aiml']['thatpattern'] = $convoArr['aiml']['thatpattern']; + $showconvoArr['aiml']['topic'] = $convoArr['aiml']['topic']; + $showconvoArr['aiml']['score'] = $convoArr['aiml']['score']; + $showconvoArr['aiml']['aiml_to_php'] = $convoArr['aiml']['aiml_to_php']; + $showconvoArr['aiml']['aiml_id'] = $convoArr['aiml']['aiml_id']; + $showconvoArr['aiml']['parsed_template'] = $convoArr['aiml']['parsed_template']; + $showconvoArr['user_say']['1'] = $convoArr['user_say']['1']; + $showconvoArr['that_raw']['1'] = $convoArr['that_raw']['1']; + $showconvoArr['parsed_template']['1'] = $convoArr['parsed_template']['1']; + + return $showconvoArr; +} + + +/** + * function writefile_debug() + * Handles the debug when written to a file + * @param string $filename - the name of the file which is also the convo id + * @param string $log - the data to write +**/ +function writefile_debug($log) +{ + $myFile = _DEBUG_PATH_.session_id().".txt"; + + $mode = "w"; + $file = ""; + $tabs = ""; + + + if (DIRECTORY_SEPARATOR == '\\') { + $file = str_replace("\n", "\r\n", $log); + } + + + file_put_contents($myFile, $file); +} + + + +/** + * function display_on_page() + * Handles the debug when it is displayed on the webpage either in the source or on the page + * @param int $show_on_page - 0=show in source 1=output to user + * @param string $log - the data to show +**/ +function display_on_page($show_on_page,$log) +{ + if($show_on_page==0){ + echo ""; + }else{ + echo "
    ";
    +		print_r($log);
    +		echo "
    "; + } +} + + +/** + * function email_debug() + * Handles the debug when it is emailed to the botmaster + * @param string $email - email address + * @param string $log - the data to send +**/ +function email_debug($email,$log) +{ + $to = $email; + $subject = 'Debug Data'; + $message = $log; + $headers = 'From: '.$email . "\r\n" . + 'Reply-To: '.$email . "\r\n" . + 'X-Mailer: PHP/' . phpversion(); + + mail($to, $subject, $message, $headers); +} + + + + +/** + * function outputDebug() + * Used in the install/upgrade files will display it straightaway + * @param string $fileName - the file the error came from + * @param string $functionName - the function that triggered the error + * @param string $line - the line of code + * @param string $info - the message to display +**/ +function outputDebug($fileName, $functionName, $line, $info) { + + global $srai_iterations; + list($usec, $sec) = explode(" ",microtime()); + + //build timestamp index for the debug array + $string = ((float)$usec + (float)$sec); + $string2 = explode(".", $string); + $index = date("d-m-Y H:i:s", $string2[0]).":".$string2[1]; + + if($srai_iterations<1){ + $sr_it = 0;} + else { + $sr_it = $srai_iterations;} + + //add to array + print "
    ----------------------------------------------------"; + print "
    ".$index.": ".$fileName; + print "
    ".$index.": ".$functionName; + print "
    ".$index.": ".$line; + print "
    ".$index.": ".$info; + print "
    ".$index.": srai:".$sr_it; + print "
    ----------------------------------------------------"; +} + + function SQL_Error($errNum, $file = 'unknown', $function = 'unknown', $line = 'unknown') { + $msg = "There's a problem with your Program O installation. Please run the install script to correct the problem.
    \n"; + switch ($errNum) { + case '1146': + $msg .= "The database and/or table used in the config file doesn't exist.
    \n"; + break; + default: + $msg = "Error number $errNum!
    \n"; + } + return $msg; + } + + function save_file($file, $content, $append = false) { + if (function_exists('file_put_contents')) { + $x = file_put_contents($file, $content, $append); + } + else { + $fileMode = ($append === true) ? "a" : "w"; + $fh = fopen($file, $fileMode)or die("Can't open the file!"); + $cLen = strlen($content); + fwrite($fh, $content, $cLen); + fclose($fh); + } + return 1; + } + ?> \ No newline at end of file From cc942e12a73104a827a8554b48d6ade34f331200 Mon Sep 17 00:00:00 2001 From: Program-O Date: Thu, 28 Jun 2012 22:09:28 +0100 Subject: [PATCH 069/123] Fixed - learn.aiml will now work correctly --- chatbot/core/aiml/buildingphp_code_functions.php | 6 +++--- chatbot/core/aiml/make_aiml_to_php_code.php | 10 ++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/chatbot/core/aiml/buildingphp_code_functions.php b/chatbot/core/aiml/buildingphp_code_functions.php index d201992..56a94ee 100644 --- a/chatbot/core/aiml/buildingphp_code_functions.php +++ b/chatbot/core/aiml/buildingphp_code_functions.php @@ -66,9 +66,9 @@ function clean_for_eval($parsed_template,$count=0) $parsed_template= str_replace("close_bracket';", "close_bracket;", $parsed_template); $parsed_template= str_replace("close_bracket.\\\"';","close_bracket.'\\\"';", $parsed_template); - //$parsed_template= preg_replace("/elseif\(\$[a-z]=='[0-9]'\){ \$tmp_botsay.= ''; }/is","", $parsed_template); - - + //TODO BE CAREFULL this might break things ... needed for learn.aiml + $parsed_template= str_replace("close_bracket.'close_bracket;","close_bracket close_bracket;", $parsed_template); + $linebyline = explode("\r\n",$parsed_template); diff --git a/chatbot/core/aiml/make_aiml_to_php_code.php b/chatbot/core/aiml/make_aiml_to_php_code.php index b74a273..44b6f7b 100644 --- a/chatbot/core/aiml/make_aiml_to_php_code.php +++ b/chatbot/core/aiml/make_aiml_to_php_code.php @@ -75,6 +75,16 @@ function aiml_to_phpfunctions($convoArr) $find[$i]='#
    #i'; $replace[$i]='\';'; + + $i++; + $find[$i]='##i'; + $replace[$i]=''; + + $i++; + $find[$i]='#