PHP based GroupMe bot with sqlite and control panel
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
0
down vote
favorite
I made a lot of updates to my GroupMe bot, now it uses sqlite and has a web panel. I'm wondering if there are any bad coding techniques used and more importantly if there are any security issues with it.
https://github.com/desultory/GroupMe-Bot
bot.php:
<?php
//Includes all functions and parses the post data into appropriate variables
include 'functions.php';
include 'lights.php';
$callback = json_decode(file_get_contents('php://input'));
$attachments = $callback->attachments;
$avatar = $callback->avatar_url;
$name = $callback->name;
$type = $callback->sender_type;
$text = $callback->text;
$userid = $callback->user_id;
$admins = get_admins();
$ignored = get_ignored();
$settings = get_settings();
//If logging is enabled in the config, this logs the chat to the database
logging($userid, $name, $text);
//Only handles messages from users to prevent infinite loops
if ($type == 'user' && !in_array($userid, $ignored) && $text[0] != '/')
//Basic response is a simple response to a found phrase
basic_response($text, $name, $userid);
//If the Weather Underground API token and location are set and weather has been enabled, this will return a forecast if someone says "weather"
if ($settings['weather'])
weather_response($text);
//If anyone says "bitcoin" and the bitcoin setting is enabled, this will return the price in USD
if ($settings['bitcoin'])
btc_response($text);
//If anyone says "ethereum" and the ethereum setting is enabled, this will return the price in USD and BTC
if ($settings['ethereum'])
eth_response($text);
//If anyone says "litecoin" and the litecoin setting is enabled, this will return the price in USD and BTC
if ($settings['litecoin'])
ltc_response($text);
if ($settings['lights'])
blink($ip, $pins, "50", "20");
if (in_array($userid, $admins) && $type == 'user' && $text == '/config')
functions.php:
<?php
//Writes the contents of a variable to a text file for debugging purposes
function debugvar($variable)
file_put_contents('debug.txt', print_r($variable, true));
//Initialize the database
function initdb()
$db = new PDO('sqlite:db.sqlite');
$dbcmds = ['CREATE TABLE IF NOT EXISTS config(
name TEXT NOT NULL,
value TEXT NOT NULL
)',
'CREATE TABLE IF NOT EXISTS settings(
name TEXT NOT NULL,
value INTEGER NOT NULL
)',
'CREATE TABLE IF NOT EXISTS responses(
find TEXT NOT NULL,
respond TEXT NOT NULL
)',
'CREATE TABLE IF NOT EXISTS users(
name TEXT NOT NULL,
userid TEXT NOT NULL,
admin INTEGER,
ignored INTEGER
)',
'CREATE TABLE IF NOT EXISTS auth(
username TEXT NOT NULL,
password TEXT NOT NULL
)',
'CREATE TABLE IF NOT EXISTS log(
entry TEXT NOT NULL,
timestamp INTEGER NOT NULL
)',
];
foreach ($dbcmds as $cmd)
$db->exec($cmd);
$clean = 1;
foreach ($db->errorInfo() as $error)
if ($error != 0)
$clean = $error;
return $clean;
//Gets the specified config variable value from the database
function get_config_var($parameter)
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT value FROM config WHERE name=:name');
$query->bindValue(':name', $parameter, PDO::PARAM_STR);
$query->execute();
$result = $query->fetch(PDO::FETCH_ASSOC);
return $result['value'];
//Returns panel admins as an array
function get_panel_admins()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT username FROM auth');
$query->execute();
$result = $query->fetchAll(PDO::FETCH_COLUMN, 0);
return $result;
//Adds admins listed in array
function add_admin($user, $pass)
$db = new PDO('sqlite:db.sqlite');
$admins = get_panel_admins();
$username = strtolower($user);
$password = password_hash($pass, PASSWORD_DEFAULT);
if (!in_array($username, $admins))
$query = $db->prepare('INSERT INTO auth (username, password) VALUES (:username, :password)');
$query->bindValue(':username', $username, PDO::PARAM_STR);
$query->bindValue(':password', $password, PDO::PARAM_STR);
$query->execute();
else
echo "Admin already exists";
//Changes an admin password to the specified password
function change_admin_pass($users)
$db = new PDO('sqlite:db.sqlite');
foreach ($users as $name=>$pass)
if (!empty($pass))
$username = strtolower($name);
$password = password_hash($pass, PASSWORD_DEFAULT);
$query = $db->prepare('UPDATE auth set password=:password WHERE username=:username');
$query->bindValue(':username', $username, PDO::PARAM_STR);
$query->bindValue(':password', $password, PDO::PARAM_STR);
$query->execute();
//Deletes admins listed in array
function delete_admin($delete)
$db = new PDO('sqlite:db.sqlite');
foreach ($delete as $name)
$query = $db->prepare('SELECT count(username) FROM auth');
$query->execute();
$count = $query->fetch();
$count = $count[0];
if ($count > '1')
$username = strtolower($name);
$query = $db->prepare('DELETE FROM auth WHERE username=:name');
$query->bindValue(':name', $username, PDO::PARAM_STR);
$query->execute();
else
echo "Cannot delete last admin";
//Returns the responses as an array
function get_responses()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT find,respond FROM responses');
$query->execute();
return $query->fetchAll();
//Returns the config as an array
function get_config()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT name,value FROM config');
$query->execute();
return $query->fetchAll();
//Returns the chat log
function get_log()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT entry,timestamp FROM log');
$query->execute();
return $query->fetchAll();
//Returns the admins as an array
function get_admins()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT userid FROM users WHERE admin=1');
$query->execute();
return $query->fetchAll(PDO::FETCH_COLUMN, 0);
//Returns the ignored users as an array
function get_ignored()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT userid FROM users WHERE ignored=1');
$query->execute();
return $query->fetchAll(PDO::FETCH_COLUMN, 0);
//Returns the settings as an array
function get_settings()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT name,value FROM settings');
$query->execute();
$result = $query->fetchAll();
foreach ($result as $setting)
$settings[$setting[0]] = $setting[1];
return $settings;
//Logs all chat to the database
function logging($userid, $name, $text)
$db = new PDO('sqlite:db.sqlite');
if (get_config_var('log'))
$entry = "$name($userid): $text";
$statement = $db->prepare('INSERT INTO log (entry, timestamp) VALUES (:entry, :timestamp)');
$statement->bindValue(':entry', $entry, PDO::PARAM_STR);
$statement->bindValue(':timestamp', time(), PDO::PARAM_STR);
$statement->execute();
//Basic response (no images)
function basic_response($text, $name, $userid)
$responses = get_responses();
foreach ($responses as $element)
if (stripos($text, $element[0]) !== FALSE)
$message = $element[1];
$message = str_replace('%u', $userid, $message);
if (stripos($message, '%n') !== FALSE)
$message = str_replace('%n', $name, $message);
mention($message, $name);
else
send($message);
//WUnderground response
function weather_response($text)
$wutoken = get_config_var('wutoken');
$wuloc = get_config_var('wuloc');
if (stripos($text, 'weather') !== FALSE)
if (isset($wutoken) && isset($wuloc))
$rawweather = json_decode(curl_get("https://api.wunderground.com/api/$wutoken/conditions/q/$wuloc.json"));
$temperature = $rawweather->current_observation->feelslike_string;
$weather = $rawweather->current_observation->weather;
$icon = $rawweather->current_observation->icon_url;
$forecast = "The weather is $weather with a temperature of $temperature";
send_img($forecast, $icon);
else
send('WUnderground token and location are not set');
//Bitcoin value response
function btc_response($text)
if (stripos($text, 'bitcoin') !== FALSE)
$pricedata = json_decode(curl_get("https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=USD"));
$usdprice = $pricedata->USD;
$message = "Bitcoin is worth $$usdprice";
$btclogo = 'https://files.coinmarketcap.com/static/img/coins/32x32/bitcoin.png';
send_img($message, $btclogo);
//Ethereum value response
function eth_response($text)
if (stripos($text, 'ethereum') !== FALSE)
$pricedata = json_decode(curl_get("https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=BTC,USD"));
$usdprice = $pricedata->USD;
$btcprice = $pricedata->BTC;
$message = "Ethereum is worth $$usdprice and $btcprice Bitcoin";
$ethlogo = 'https://files.coinmarketcap.com/static/img/coins/32x32/ethereum.png';
send_img($message, $ethlogo);
//Litecoin value response
function ltc_response($text)
if (stripos($text, 'litecoin') !== FALSE)
$pricedata = json_decode(curl_get("https://min-api.cryptocompare.com/data/price?fsym=LTC&tsyms=BTC,USD"));
$usdprice = $pricedata->USD;
$btcprice = $pricedata->BTC;
$message = "Litecoin is worth $$usdprice and $btcprice Bitcoin";
$ltclogo = 'https://files.coinmarketcap.com/static/img/coins/32x32/litecoin.png';
send_img($message, $ltclogo);
//Curl get function, takes url and returns the get response
function curl_get($url)
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$url");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$get = curl_exec($ch);
curl_close($ch);
return $get;
//Curl post to groupme, takes the postfields and posts to the groupme bot api
function curl_post($postfields)
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.groupme.com/v3/bots/post');
curl_setopt($ch, CURLOPT_POSTFIELDS, $postfields);
curl_exec($ch);
curl_close($ch);
//Send message function, takes a message as input and posts to GroupMe
function send($message)
$bottoken = get_config_var('bottoken');
$postdata = [
'bot_id' => $bottoken,
'text' => $message
];
curl_post(http_build_query($postdata));
//Send image function, takes message and img url as inputs and posts to GroupMe
function send_img($message, $image)
$bottoken = get_config_var('bottoken');
$attachments = [
'type' => 'image',
'url' => $image
];
$postdata = [
'bot_id' => $bottoken,
'text' => $message,
'attachments' => [$attachments]
];
curl_post(json_encode($postdata));
//Mention function, takes a message and name as inputs and posts to GroupMe
function mention($message, $name)
$bottoken = get_config_var('bottoken');
$loci = [
stripos($message, $name),
strlen($name)
];
$attachments = [
'loci' => [$loci],
'type' => 'mentions',
'user_ids' => [get_user_id($name)]
];
$postdata = [
'bot_id' => $bottoken,
'text' => $message,
'attachments' => [$attachments]
];
curl_post(json_encode($postdata));
//Get bot group function, returns the group id of the bot
function get_bot_group()
$apitoken = get_config_var('apitoken');
$bottoken = get_config_var('bottoken');
$bots = json_decode(curl_get("https://api.groupme.com/v3/bots?token=$apitoken"));
foreach($bots->response as $element)
if ($element->bot_id == $bottoken)
return $element->group_id;
//Get user id function, takes a name as input and returns the user id
function get_user_id($name)
$apitoken = get_config_var('apitoken');
$user_id = 'No member with that name found';
$groupid = get_bot_group();
$groups = json_decode(curl_get("https://api.groupme.com/v3/groups?token=$apitoken"));
foreach($groups->response as $element)
if ($element->id == $groupid)
foreach($element->members as $member)
if (stripos($member->nickname, $name) !== FALSE)
$user_id = $member->user_id;
return $user_id;
//Get name function, takes a user id as input and returns the name associated with that user id
function get_name($userid)
$apitoken = get_config_var('apitoken');
$name = 'Invalid userid';
$groupid = get_bot_group();
$groups = json_decode(curl_get("https://api.groupme.com/v3/groups?token=$apitoken"));
foreach($groups->response as $element)
if ($element->id == $groupid)
foreach($element->members as $member)
if ($userid == $member->user_id)
$name = $member->nickname;
return $name;
//Get users function, gets user information from the groupme api, adds it to the database, and returns it as an array
function get_users()
$apitoken = get_config_var('apitoken');
$groupid = get_bot_group();
$groups = json_decode(curl_get("https://api.groupme.com/v3/groups?token=$apitoken"));
$index = 0;
$db = new PDO('sqlite:db.sqlite');
foreach($groups->response as $element)
if ($element->id == $groupid)
foreach($element->members as $member)
$userid = $member->user_id;
$name = $member->nickname;
$avatar = $member->image_url;
$query = $db->prepare('SELECT userid FROM users WHERE userid=:userid');
$query->bindValue('userid', $userid, PDO::PARAM_STR);
$query->execute();
$result = $query->fetch(PDO::FETCH_ASSOC);
if (isset($result['userid']))
$query = $db->prepare('UPDATE users SET name=:name WHERE userid=:userid');
$query->bindValue(':name', $name, PDO::PARAM_STR);
$query->bindValue(':userid', $userid, PDO::PARAM_STR);
$query->execute();
else
$query = $db->prepare('INSERT INTO users (name, userid) VALUES (:name, :userid)');
$query->bindValue(':name', $name, PDO::PARAM_STR);
$query->bindValue(':userid', $userid, PDO::PARAM_STR);
$query->execute();
$members[$index] = [
"userid" => $userid,
"name" => $name,
"avatar" => $avatar
];
$index++;
return $members;
//Adds a response to the database, uses input find and respond where find is the text that is searched for and respond is the text that is retrned
function add_response($find, $respond)
$responses = get_responses();
$exists = 0;
foreach ($responses as $element)
if (stripos($element[0], $find) !== FALSE
if (!$exists)
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('INSERT INTO responses (find, respond) VALUES (:find, :respond)');
$query->bindValue(':find', $find, PDO::PARAM_STR);
$query->bindValue(':respond', $respond, PDO::PARAM_STR);
$query->execute();
else
echo "Similar find already exists<br>";
//Deletes responses from the database, takes the "find" string as input
function del_responses($delete)
$db = new PDO('sqlite:db.sqlite');
foreach ($delete as $find)
$query = $db->prepare('DELETE FROM responses WHERE find=:find');
$query->bindValue(':find', $find, PDO::PARAM_STR);
$query->execute();
//Deletes all admins from the database
function delete_admins()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('UPDATE users SET admin = 0');
$query->execute();
//Updates the admins by deleting all of them and then adding the specified userids
function update_admins($admins)
delete_admins();
$db = new PDO('sqlite:db.sqlite');
foreach ($admins as $element)
$query = $db->prepare('UPDATE users SET admin=:admin WHERE userid=:userid');
$query->bindValue(':userid', $element, PDO::PARAM_STR);
$query->bindValue(':admin', '1', PDO::PARAM_STR);
$query->execute();
//Deletes all ignored users from the database
function delete_ignored()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('UPDATE users SET ignored = 0');
$query->execute();
//Updates the users by deleting all of them and then adding the specified userids
function update_ignored($ignored)
delete_ignored();
$db = new PDO('sqlite:db.sqlite');
foreach ($ignored as $element)
$query = $db->prepare('UPDATE users SET ignored=:ignored WHERE userid=:userid');
$query->bindValue(':userid', $element, PDO::PARAM_STR);
$query->bindValue(':ignored', '1', PDO::PARAM_STR);
$query->execute();
//Resets all settings in the database
function reset_settings()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('UPDATE settings SET value = 0');
$query->execute();
//Updates the settings by restting all of the settings and then enabling the specified ones
function update_settings($settings)
reset_settings();
$db = new PDO('sqlite:db.sqlite');
foreach ($settings as $element)
$query = $db->prepare('UPDATE settings SET value=:value WHERE name=:name');
$query->bindValue(':name', $element, PDO::PARAM_STR);
$query->bindValue(':value', '1', PDO::PARAM_STR);
$query->execute();
//Adds the specified setting to the array if it doesn't already exist
function add_setting($setting)
$settings = get_settings();
$exists = 0;
foreach ($settings as $element=>$key)
if ($setting == $element)
$exists = 1;
if (!$exists)
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('INSERT INTO settings (name, value) VALUES (:name, :value)');
$query->bindValue(':name', $setting, PDO::PARAM_STR);
$query->bindValue(':value', '1', PDO::PARAM_STR);
$query->execute();
else
echo "Setting already exists<br>";
//Deletes responses from the database, takes the "find" string as input
function del_settings($delete)
$db = new PDO('sqlite:db.sqlite');
foreach ($delete as $setting)
$query = $db->prepare('DELETE FROM settings WHERE name=:setting');
$query->bindValue(':setting', $setting, PDO::PARAM_STR);
$query->execute();
//Updates the config, takes an array of config paramaters with the element name being the paramter and the value being the value
function update_config($config)
$db = new PDO('sqlite:db.sqlite');
foreach ($config as $name=>$value)
if ($value[0] != "*")
$query = $db->prepare('UPDATE config SET value=:value WHERE name=:name');
$query->bindValue(':name', $name, PDO::PARAM_STR);
$query->bindValue(':value', $value, PDO::PARAM_STR);
$query->execute();
//Display the setup form
function disp_setup()
$setup = <<<'EOSETUP'
<form name="setup" method="post" action="">
<table align="center" style="width: 50%;">
<tr>
<td>Panel username:</td>
<td><input type="text" style="width: 100%;" name="user" placeholder="Panel username" required></td>
</tr>
<tr>
<td>Panel password:</td>
<td><input type="password" style="width: 100%;" name="pass" placeholder="Panel password" required></td>
</tr>
<tr>
<td>GroupMe API token:</td>
<td><input type="text" style="width: 100%;" name="apitoken" placeholder="Your GroupMe API token" required></td>
</tr>
<tr>
<td>GroupMe Bot token:</td>
<td><input type="text" style="width: 100%;" name="bottoken" placeholder="Your GroupMe Bot token" required></td>
</tr>
<tr>
<td>WeatherUnderground API token:</td>
<td><input type="text" style="width: 100%;" name="wutoken" placeholder="Your WeatherUnderground API token" value=""></td>
</tr>
<tr>
<td>WeatherUnderground location code:</td>
<td><input type="text" style="width: 100%;" name="wuloc" placeholder="Your WeatherUnderground location code" value=""></td>
</tr>
<tr>
<td>Logging, check to enable</td>
<td><input type="checkbox" style="width: 100%;" name="log" value="1" checked required></td>
</tr>
<tr>
<td colspan="3"><input type="submit" value="Initialize"></td>
</tr>
</table>
</form>
EOSETUP;
echo $setup;
//Display the login
function disp_login()
$login = <<<'EOLOGIN'
<form name="login" method="post" action="">
<table align="center" style="width: 50%;">
<tr>
<td>Username:</td>
<td><input type="text" style="width: 100%;" name="username" placeholder="Panel username" required></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="password" style="width: 100%;" name="password" placeholder="Panel password" required></td>
</tr>
<tr>
<td colspan="3"><input type="submit" value="Login"></td>
</tr>
</table>
</form>
EOLOGIN;
echo $login;
index.php:
<!DOCTYPE html>
<html>
<head>
<style>
body
background: url("https://picload.org/image/dadcrgpl/background.png");
background-repeat: repeat-y;
background-size: cover;
color: white;
margin: 0;
padding: 0;
left: 0;
right: 0;
position: absolute;
font-size: 16px;
text-align: center;
font-family: "Lucida Console", Monaco, monospace;
form
border: 0;
margin: 0;
ul
list-style-type: none;
margin: 0;
padding: 0;
overflow: hidden;
li
float: left;
display: block;
summary
background: rgba(255, 0, 0, .1);
text-align: left;;
font-size: 18px;
table
max-width: 100%;
border-spacing: 0;
text-align: left;
font-size: 16px;
tr
max-width: 100%;
th, td
height: 100%;
padding: 10px;
overflow-x: hidden;
vertical-align: middle;
tr:nth-child(even)
background-color: rgba(255, 255, 255, 0.50);
tr:nth-child(odd)
background-color: rgba(255, 255, 255, 0.25);
input
border: 0;
box-sizing: border-box;
color: white;
text-indent: 0px;
font-size: 16px;
background: rgba(0, 0, 0, 0);
font-family: "Lucida Console", Monaco, monospace;
</style>
<title>PHP GroupMe Bot</title>
</head>
<body>
<?php
header('Content-type: text/html; charset=utf-8');
ini_set('display_errors', 1);
ini_set('session.save_path', getcwd());
error_reporting(-1);
include 'functions.php';
session_start();
if (file_exists('db.sqlite'))
if (isset($_SESSION['username']))
if (isset($_POST['logout']))
session_destroy();
header("Refresh:1");
if (!empty($_POST['add_admin_name']) && !empty($_POST['add_admin_pass']))
add_admin($_POST['add_admin_name'], $_POST['add_admin_pass']);
if (isset($_POST['delete_admin']))
delete_admin($_POST['delete_admin']);
if (isset($_POST['change_admin_pass']))
change_admin_pass($_POST['change_admin_pass']);
if (isset($_POST['config']))
update_config($_POST['config']);
if (isset($_POST['find']) && isset($_POST['respond']) && !empty($_POST['find']) && !empty($_POST['respond']))
add_response($_POST['find'], $_POST['respond']);
if (isset($_POST['delete']))
del_responses($_POST['delete']);
if (isset($_POST['users']))
if (isset($_POST['admins']))
update_admins($_POST['admins']);
else
delete_admins();
if (isset($_POST['ignored']))
update_ignored($_POST['ignored']);
else
delete_ignored();
if (isset($_POST['settings']))
update_settings($_POST['settings']);
if (isset($_POST['new_setting']) && !empty($_POST['new_setting']))
add_setting($_POST['new_setting']);
if (isset($_POST['del_settings']) && !empty($_POST['del_settings']))
del_settings($_POST['del_settings']);
if (isset($_POST['send']) && !empty($_POST['send']))
send($_POST['send']);
?>
<div style="align: right; height: 5vh; background: rgba(0, 0, 0, .5);">
<ul>
<?php
$username = $_SESSION['username'];
echo "<li>$username Logged in</li>";
?>
<form name="logout" method="post" action="">
<li><input type="hidden" name="logout" value="logout"></li>
<input style="float: right;" type="submit" value="Log Out">
</form>
</div>
<div style="overflow-y: scroll; height: 90vh">
<details>
<summary>Panel</summary>
<form name="panel" method="post" action="">
<table align="center">
<tr>
<th>Panel Admins</th>
<th>Delete</th>
<th>Change Password</th>
</tr>
<?php
$admins = get_panel_admins();
foreach ($admins as $element)
$name = $element;
echo "<tr>";
echo "<td>$name</td>";
echo "<td><input type="checkbox" name="delete_admin" value="$name"></td>";
echo "<td><input type="password" name="change_admin_pass[$name]" placeholder="Password"></td>";
echo "</tr>";
?>
<tr>
<td><input type="text" name="add_admin_name" placeholder="Username"></td>
<td colspan="2"><input type="password" name="add_admin_pass" placeholder="Password"></td>
<tr>
<th colspan="3"><input type="submit" value="Update"></th>
</tr>
</table>
</form>
</details>
<details>
<summary>Config</summary>
<form name="config" method="post" action="">
<table align="center">
<tr>
<th>Setting</th>
<th>Value</th>
<th>New Value</th>
</tr>
<?php
$config = get_config();
foreach ($config as $element)
$name = $element['name'];
$value = $element['value'];
echo "<tr>";
echo "<td>$name</td>";
if (stripos($name, 'token') !== FALSE)
$value = str_repeat('*', strlen($value) - 4) . substr($value, -4);
echo "<td>$value</td>";
echo "<td><input type="text" name="config[$name]" value="$value"></td>";
else
echo "<td>$value</td>";
echo "<td><input type="text" name="config[$name]" value="$value"></td>";
echo "</tr>";
?>
<tr>
<th colspan="3"><input type="submit" value="Update"></th>
</tr>
</table>
</form>
</details>
<details>
<summary>Add</summary>
<form name="add" method="post" action="">
<h3>%n can be used to mention someone in a response</h3>
<table align="center">
<tr>
<th><input type="text" name="find" placeholder="Text to find"></th>
<th><input type="text" name="respond" placeholder="Text to respond with"></th>
<th><input type="submit" value="Add"></th>
</tr>
</table>
</form>
</details>
<details>
<summary>Delete</summary>
<form name="delete" method="post" action="">
<table align="center">
<tr>
<th>Find</th>
<th>Respond</th>
<th>Delete</th>
</tr>
<?php
$responses = get_responses();
foreach ($responses as $element)
$find = $element['find'];
$respond = $element['respond'];
echo "<tr>";
echo "<td>$find</td>";
echo "<td>$respond</td>";
echo "<td><input type="checkbox" name="delete" value="$find"></td>";
echo "</tr>";
?>
<tr>
<th colspan="3"><input type="submit" value="Remove"></th>
</tr>
</table>
</form>
</details>
<details>
<summary>Users</summary>
<form name="Users" method="post" action="">
<table align="center">
<tr>
<th>Name</th>
<th>Admin</th>
<th>Ignored</th>
</tr>
<?php
$admins = get_admins();
$ignored = get_ignored();
$users = get_users();
$i = 0;
foreach ($users as $user)
$name = htmlspecialchars($user["name"]);
$userid = htmlspecialchars($user["userid"]);
$avatar = $user["avatar"];
echo "<tr>";
echo "<td style="text-align: left;"><img src="$avatar" style="width:50px; height:50px; vertical-align: middle;">$name ($userid)</td>";
if (in_array($users[$i]['userid'], $admins))
echo "<td><input type="checkbox" name="admins" value="$userid" checked></td>";
else
echo "<td><input type="checkbox" name="admins" value="$userid"></td>";
if (in_array($users[$i]['userid'], $ignored))
echo "<td><input type="checkbox" name="ignored" value="$userid" checked></td>";
else
echo "<td><input type="checkbox" name="ignored" value="$userid"></td>";
echo "</tr>";
$i++;
?>
<tr>
<th colspan="3"><input type="submit" value="Update"></th>
</tr>
</table>
<input type="hidden" name="users" value="1">
</form>
</details>
<details>
<summary>Settings</summary>
<form name="settings" method="post" action="">
<table align="center">
<tr>
<th>Name</th>
<th>State</th>
<th>Delete</th>
</tr>
<?php
$settings = get_settings();
foreach ($settings as $element=>$key)
$name = $element;
$value = $key;
echo "<tr>";
echo "<td>$name</td>";
if ($value)
echo "<td><input type="checkbox" name="settings" value="$name" checked></td>";
else
echo "<td><input type="checkbox" name="settings" value="$name"></td>";
echo "<td><input type="checkbox" name="del_settings" value="$name"></td>";
echo "</tr>";
?>
<tr>
<td>Add setting</td>
<td colspan="2"><input type="text" name="new_setting" placeholder="Name for new setting"></td>
</tr>
<tr>
<th colspan="3"><input type="submit" value="Update"></th>
</tr>
</table>
<input type="hidden" name="settings" value="1">
</form>
</details>
<details>
<summary>Log</summary>
<table style="width: 100%;">
<?php
$log = get_log();
foreach ($log as $element)
$timestamp = date("Y-m-d@H:i:s", $element['timestamp']);
$entry = htmlspecialchars($element['entry']);
echo "<tr>";
echo "<td>$timestamp</td>";
echo "<td>$entry</td>";
echo "</tr>";
?>
</table>
</details>
</div>
<form name="send" method="post" action="">
<table style="width: 100%; position: fixed; bottom: 0; height: 5vh">
<tr>
<th><input type="text" name="send" placeholder="Message to send"></th>
</tr>
</table>
<input type="submit" value="Send" style="display: none">
</form>
<?php
else
disp_login();
if (isset($_POST['username']) && isset($_POST['password']))
$db = new PDO('sqlite:db.sqlite');
$username = strtolower($_POST['username']);
$password = $_POST['password'];
$query = $db->prepare('SELECT password FROM auth WHERE username=:username');
$query->bindValue(':username', $username, PDO::PARAM_STR);
$query->execute();
$hashed = $query->fetch(PDO::FETCH_COLUMN, 0);
if (password_verify($password, $hashed))
echo "Logging in...";
$_SESSION['username'] = $username;
header("Refresh:1");
else
echo "Incorrect password!";
else if (is_writeable('./'))
if (!empty($_POST) && initdb())
$db = new PDO('sqlite:db.sqlite');
$config = ['apitoken', 'bottoken', 'wutoken', 'wuloc'];
$settings = ['litecoin', 'bitcoin', 'ethereum'];
foreach($config as $variable)
$statement = $db->prepare('INSERT INTO config (name, value) VALUES (:name, :value)');
$statement->bindValue(':name', $variable, PDO::PARAM_STR);
$statement->bindValue(':value', $_POST[$variable], PDO::PARAM_STR);
$statement->execute();
if ($_POST['log'])
$db->exec("INSERT INTO config (name, value) VALUES ('log', '1')");
else
$db->exec("INSERT INTO config (name, value) VALUES ('log', '1')");
if ((isset($_POST['wutoken'])) && isset($_POST['wuloc']))
$db->exec("INSERT INTO settings (name, value) VALUES ('weather', '1')");
else
$db->exec("INSERT INTO settings (name, value) VALUES ('weather', '0')");
$db->exec("INSERT INTO settings (name, value) VALUES ('lights', '0')");
$db->exec("INSERT INTO responses (find, respond) VALUES ('test', 'It works!')");
add_admin($_POST['user'], $_POST['pass']);
foreach($settings as $variable)
$statement = $db->prepare('INSERT INTO settings (name, value) VALUES (:name, :value)');
$statement->bindValue(':name', $variable, PDO::PARAM_STR);
$statement->bindValue(':value', '1', PDO::PARAM_STR);
$statement->execute();
file_put_contents('.htaccess', "<Files "db.sqlite">nDeny From Alln</Files>n<Files "sess*">nDeny From Alln</Files>");
header("Refresh:1");
disp_setup();
else
echo "Working directory is not writeable, either chown it to the webserver user and group or allow write permissions to everyone (insecure!)";
?>
</body>
</html>
php sqlite
add a comment |Â
up vote
0
down vote
favorite
I made a lot of updates to my GroupMe bot, now it uses sqlite and has a web panel. I'm wondering if there are any bad coding techniques used and more importantly if there are any security issues with it.
https://github.com/desultory/GroupMe-Bot
bot.php:
<?php
//Includes all functions and parses the post data into appropriate variables
include 'functions.php';
include 'lights.php';
$callback = json_decode(file_get_contents('php://input'));
$attachments = $callback->attachments;
$avatar = $callback->avatar_url;
$name = $callback->name;
$type = $callback->sender_type;
$text = $callback->text;
$userid = $callback->user_id;
$admins = get_admins();
$ignored = get_ignored();
$settings = get_settings();
//If logging is enabled in the config, this logs the chat to the database
logging($userid, $name, $text);
//Only handles messages from users to prevent infinite loops
if ($type == 'user' && !in_array($userid, $ignored) && $text[0] != '/')
//Basic response is a simple response to a found phrase
basic_response($text, $name, $userid);
//If the Weather Underground API token and location are set and weather has been enabled, this will return a forecast if someone says "weather"
if ($settings['weather'])
weather_response($text);
//If anyone says "bitcoin" and the bitcoin setting is enabled, this will return the price in USD
if ($settings['bitcoin'])
btc_response($text);
//If anyone says "ethereum" and the ethereum setting is enabled, this will return the price in USD and BTC
if ($settings['ethereum'])
eth_response($text);
//If anyone says "litecoin" and the litecoin setting is enabled, this will return the price in USD and BTC
if ($settings['litecoin'])
ltc_response($text);
if ($settings['lights'])
blink($ip, $pins, "50", "20");
if (in_array($userid, $admins) && $type == 'user' && $text == '/config')
functions.php:
<?php
//Writes the contents of a variable to a text file for debugging purposes
function debugvar($variable)
file_put_contents('debug.txt', print_r($variable, true));
//Initialize the database
function initdb()
$db = new PDO('sqlite:db.sqlite');
$dbcmds = ['CREATE TABLE IF NOT EXISTS config(
name TEXT NOT NULL,
value TEXT NOT NULL
)',
'CREATE TABLE IF NOT EXISTS settings(
name TEXT NOT NULL,
value INTEGER NOT NULL
)',
'CREATE TABLE IF NOT EXISTS responses(
find TEXT NOT NULL,
respond TEXT NOT NULL
)',
'CREATE TABLE IF NOT EXISTS users(
name TEXT NOT NULL,
userid TEXT NOT NULL,
admin INTEGER,
ignored INTEGER
)',
'CREATE TABLE IF NOT EXISTS auth(
username TEXT NOT NULL,
password TEXT NOT NULL
)',
'CREATE TABLE IF NOT EXISTS log(
entry TEXT NOT NULL,
timestamp INTEGER NOT NULL
)',
];
foreach ($dbcmds as $cmd)
$db->exec($cmd);
$clean = 1;
foreach ($db->errorInfo() as $error)
if ($error != 0)
$clean = $error;
return $clean;
//Gets the specified config variable value from the database
function get_config_var($parameter)
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT value FROM config WHERE name=:name');
$query->bindValue(':name', $parameter, PDO::PARAM_STR);
$query->execute();
$result = $query->fetch(PDO::FETCH_ASSOC);
return $result['value'];
//Returns panel admins as an array
function get_panel_admins()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT username FROM auth');
$query->execute();
$result = $query->fetchAll(PDO::FETCH_COLUMN, 0);
return $result;
//Adds admins listed in array
function add_admin($user, $pass)
$db = new PDO('sqlite:db.sqlite');
$admins = get_panel_admins();
$username = strtolower($user);
$password = password_hash($pass, PASSWORD_DEFAULT);
if (!in_array($username, $admins))
$query = $db->prepare('INSERT INTO auth (username, password) VALUES (:username, :password)');
$query->bindValue(':username', $username, PDO::PARAM_STR);
$query->bindValue(':password', $password, PDO::PARAM_STR);
$query->execute();
else
echo "Admin already exists";
//Changes an admin password to the specified password
function change_admin_pass($users)
$db = new PDO('sqlite:db.sqlite');
foreach ($users as $name=>$pass)
if (!empty($pass))
$username = strtolower($name);
$password = password_hash($pass, PASSWORD_DEFAULT);
$query = $db->prepare('UPDATE auth set password=:password WHERE username=:username');
$query->bindValue(':username', $username, PDO::PARAM_STR);
$query->bindValue(':password', $password, PDO::PARAM_STR);
$query->execute();
//Deletes admins listed in array
function delete_admin($delete)
$db = new PDO('sqlite:db.sqlite');
foreach ($delete as $name)
$query = $db->prepare('SELECT count(username) FROM auth');
$query->execute();
$count = $query->fetch();
$count = $count[0];
if ($count > '1')
$username = strtolower($name);
$query = $db->prepare('DELETE FROM auth WHERE username=:name');
$query->bindValue(':name', $username, PDO::PARAM_STR);
$query->execute();
else
echo "Cannot delete last admin";
//Returns the responses as an array
function get_responses()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT find,respond FROM responses');
$query->execute();
return $query->fetchAll();
//Returns the config as an array
function get_config()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT name,value FROM config');
$query->execute();
return $query->fetchAll();
//Returns the chat log
function get_log()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT entry,timestamp FROM log');
$query->execute();
return $query->fetchAll();
//Returns the admins as an array
function get_admins()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT userid FROM users WHERE admin=1');
$query->execute();
return $query->fetchAll(PDO::FETCH_COLUMN, 0);
//Returns the ignored users as an array
function get_ignored()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT userid FROM users WHERE ignored=1');
$query->execute();
return $query->fetchAll(PDO::FETCH_COLUMN, 0);
//Returns the settings as an array
function get_settings()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT name,value FROM settings');
$query->execute();
$result = $query->fetchAll();
foreach ($result as $setting)
$settings[$setting[0]] = $setting[1];
return $settings;
//Logs all chat to the database
function logging($userid, $name, $text)
$db = new PDO('sqlite:db.sqlite');
if (get_config_var('log'))
$entry = "$name($userid): $text";
$statement = $db->prepare('INSERT INTO log (entry, timestamp) VALUES (:entry, :timestamp)');
$statement->bindValue(':entry', $entry, PDO::PARAM_STR);
$statement->bindValue(':timestamp', time(), PDO::PARAM_STR);
$statement->execute();
//Basic response (no images)
function basic_response($text, $name, $userid)
$responses = get_responses();
foreach ($responses as $element)
if (stripos($text, $element[0]) !== FALSE)
$message = $element[1];
$message = str_replace('%u', $userid, $message);
if (stripos($message, '%n') !== FALSE)
$message = str_replace('%n', $name, $message);
mention($message, $name);
else
send($message);
//WUnderground response
function weather_response($text)
$wutoken = get_config_var('wutoken');
$wuloc = get_config_var('wuloc');
if (stripos($text, 'weather') !== FALSE)
if (isset($wutoken) && isset($wuloc))
$rawweather = json_decode(curl_get("https://api.wunderground.com/api/$wutoken/conditions/q/$wuloc.json"));
$temperature = $rawweather->current_observation->feelslike_string;
$weather = $rawweather->current_observation->weather;
$icon = $rawweather->current_observation->icon_url;
$forecast = "The weather is $weather with a temperature of $temperature";
send_img($forecast, $icon);
else
send('WUnderground token and location are not set');
//Bitcoin value response
function btc_response($text)
if (stripos($text, 'bitcoin') !== FALSE)
$pricedata = json_decode(curl_get("https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=USD"));
$usdprice = $pricedata->USD;
$message = "Bitcoin is worth $$usdprice";
$btclogo = 'https://files.coinmarketcap.com/static/img/coins/32x32/bitcoin.png';
send_img($message, $btclogo);
//Ethereum value response
function eth_response($text)
if (stripos($text, 'ethereum') !== FALSE)
$pricedata = json_decode(curl_get("https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=BTC,USD"));
$usdprice = $pricedata->USD;
$btcprice = $pricedata->BTC;
$message = "Ethereum is worth $$usdprice and $btcprice Bitcoin";
$ethlogo = 'https://files.coinmarketcap.com/static/img/coins/32x32/ethereum.png';
send_img($message, $ethlogo);
//Litecoin value response
function ltc_response($text)
if (stripos($text, 'litecoin') !== FALSE)
$pricedata = json_decode(curl_get("https://min-api.cryptocompare.com/data/price?fsym=LTC&tsyms=BTC,USD"));
$usdprice = $pricedata->USD;
$btcprice = $pricedata->BTC;
$message = "Litecoin is worth $$usdprice and $btcprice Bitcoin";
$ltclogo = 'https://files.coinmarketcap.com/static/img/coins/32x32/litecoin.png';
send_img($message, $ltclogo);
//Curl get function, takes url and returns the get response
function curl_get($url)
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$url");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$get = curl_exec($ch);
curl_close($ch);
return $get;
//Curl post to groupme, takes the postfields and posts to the groupme bot api
function curl_post($postfields)
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.groupme.com/v3/bots/post');
curl_setopt($ch, CURLOPT_POSTFIELDS, $postfields);
curl_exec($ch);
curl_close($ch);
//Send message function, takes a message as input and posts to GroupMe
function send($message)
$bottoken = get_config_var('bottoken');
$postdata = [
'bot_id' => $bottoken,
'text' => $message
];
curl_post(http_build_query($postdata));
//Send image function, takes message and img url as inputs and posts to GroupMe
function send_img($message, $image)
$bottoken = get_config_var('bottoken');
$attachments = [
'type' => 'image',
'url' => $image
];
$postdata = [
'bot_id' => $bottoken,
'text' => $message,
'attachments' => [$attachments]
];
curl_post(json_encode($postdata));
//Mention function, takes a message and name as inputs and posts to GroupMe
function mention($message, $name)
$bottoken = get_config_var('bottoken');
$loci = [
stripos($message, $name),
strlen($name)
];
$attachments = [
'loci' => [$loci],
'type' => 'mentions',
'user_ids' => [get_user_id($name)]
];
$postdata = [
'bot_id' => $bottoken,
'text' => $message,
'attachments' => [$attachments]
];
curl_post(json_encode($postdata));
//Get bot group function, returns the group id of the bot
function get_bot_group()
$apitoken = get_config_var('apitoken');
$bottoken = get_config_var('bottoken');
$bots = json_decode(curl_get("https://api.groupme.com/v3/bots?token=$apitoken"));
foreach($bots->response as $element)
if ($element->bot_id == $bottoken)
return $element->group_id;
//Get user id function, takes a name as input and returns the user id
function get_user_id($name)
$apitoken = get_config_var('apitoken');
$user_id = 'No member with that name found';
$groupid = get_bot_group();
$groups = json_decode(curl_get("https://api.groupme.com/v3/groups?token=$apitoken"));
foreach($groups->response as $element)
if ($element->id == $groupid)
foreach($element->members as $member)
if (stripos($member->nickname, $name) !== FALSE)
$user_id = $member->user_id;
return $user_id;
//Get name function, takes a user id as input and returns the name associated with that user id
function get_name($userid)
$apitoken = get_config_var('apitoken');
$name = 'Invalid userid';
$groupid = get_bot_group();
$groups = json_decode(curl_get("https://api.groupme.com/v3/groups?token=$apitoken"));
foreach($groups->response as $element)
if ($element->id == $groupid)
foreach($element->members as $member)
if ($userid == $member->user_id)
$name = $member->nickname;
return $name;
//Get users function, gets user information from the groupme api, adds it to the database, and returns it as an array
function get_users()
$apitoken = get_config_var('apitoken');
$groupid = get_bot_group();
$groups = json_decode(curl_get("https://api.groupme.com/v3/groups?token=$apitoken"));
$index = 0;
$db = new PDO('sqlite:db.sqlite');
foreach($groups->response as $element)
if ($element->id == $groupid)
foreach($element->members as $member)
$userid = $member->user_id;
$name = $member->nickname;
$avatar = $member->image_url;
$query = $db->prepare('SELECT userid FROM users WHERE userid=:userid');
$query->bindValue('userid', $userid, PDO::PARAM_STR);
$query->execute();
$result = $query->fetch(PDO::FETCH_ASSOC);
if (isset($result['userid']))
$query = $db->prepare('UPDATE users SET name=:name WHERE userid=:userid');
$query->bindValue(':name', $name, PDO::PARAM_STR);
$query->bindValue(':userid', $userid, PDO::PARAM_STR);
$query->execute();
else
$query = $db->prepare('INSERT INTO users (name, userid) VALUES (:name, :userid)');
$query->bindValue(':name', $name, PDO::PARAM_STR);
$query->bindValue(':userid', $userid, PDO::PARAM_STR);
$query->execute();
$members[$index] = [
"userid" => $userid,
"name" => $name,
"avatar" => $avatar
];
$index++;
return $members;
//Adds a response to the database, uses input find and respond where find is the text that is searched for and respond is the text that is retrned
function add_response($find, $respond)
$responses = get_responses();
$exists = 0;
foreach ($responses as $element)
if (stripos($element[0], $find) !== FALSE
if (!$exists)
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('INSERT INTO responses (find, respond) VALUES (:find, :respond)');
$query->bindValue(':find', $find, PDO::PARAM_STR);
$query->bindValue(':respond', $respond, PDO::PARAM_STR);
$query->execute();
else
echo "Similar find already exists<br>";
//Deletes responses from the database, takes the "find" string as input
function del_responses($delete)
$db = new PDO('sqlite:db.sqlite');
foreach ($delete as $find)
$query = $db->prepare('DELETE FROM responses WHERE find=:find');
$query->bindValue(':find', $find, PDO::PARAM_STR);
$query->execute();
//Deletes all admins from the database
function delete_admins()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('UPDATE users SET admin = 0');
$query->execute();
//Updates the admins by deleting all of them and then adding the specified userids
function update_admins($admins)
delete_admins();
$db = new PDO('sqlite:db.sqlite');
foreach ($admins as $element)
$query = $db->prepare('UPDATE users SET admin=:admin WHERE userid=:userid');
$query->bindValue(':userid', $element, PDO::PARAM_STR);
$query->bindValue(':admin', '1', PDO::PARAM_STR);
$query->execute();
//Deletes all ignored users from the database
function delete_ignored()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('UPDATE users SET ignored = 0');
$query->execute();
//Updates the users by deleting all of them and then adding the specified userids
function update_ignored($ignored)
delete_ignored();
$db = new PDO('sqlite:db.sqlite');
foreach ($ignored as $element)
$query = $db->prepare('UPDATE users SET ignored=:ignored WHERE userid=:userid');
$query->bindValue(':userid', $element, PDO::PARAM_STR);
$query->bindValue(':ignored', '1', PDO::PARAM_STR);
$query->execute();
//Resets all settings in the database
function reset_settings()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('UPDATE settings SET value = 0');
$query->execute();
//Updates the settings by restting all of the settings and then enabling the specified ones
function update_settings($settings)
reset_settings();
$db = new PDO('sqlite:db.sqlite');
foreach ($settings as $element)
$query = $db->prepare('UPDATE settings SET value=:value WHERE name=:name');
$query->bindValue(':name', $element, PDO::PARAM_STR);
$query->bindValue(':value', '1', PDO::PARAM_STR);
$query->execute();
//Adds the specified setting to the array if it doesn't already exist
function add_setting($setting)
$settings = get_settings();
$exists = 0;
foreach ($settings as $element=>$key)
if ($setting == $element)
$exists = 1;
if (!$exists)
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('INSERT INTO settings (name, value) VALUES (:name, :value)');
$query->bindValue(':name', $setting, PDO::PARAM_STR);
$query->bindValue(':value', '1', PDO::PARAM_STR);
$query->execute();
else
echo "Setting already exists<br>";
//Deletes responses from the database, takes the "find" string as input
function del_settings($delete)
$db = new PDO('sqlite:db.sqlite');
foreach ($delete as $setting)
$query = $db->prepare('DELETE FROM settings WHERE name=:setting');
$query->bindValue(':setting', $setting, PDO::PARAM_STR);
$query->execute();
//Updates the config, takes an array of config paramaters with the element name being the paramter and the value being the value
function update_config($config)
$db = new PDO('sqlite:db.sqlite');
foreach ($config as $name=>$value)
if ($value[0] != "*")
$query = $db->prepare('UPDATE config SET value=:value WHERE name=:name');
$query->bindValue(':name', $name, PDO::PARAM_STR);
$query->bindValue(':value', $value, PDO::PARAM_STR);
$query->execute();
//Display the setup form
function disp_setup()
$setup = <<<'EOSETUP'
<form name="setup" method="post" action="">
<table align="center" style="width: 50%;">
<tr>
<td>Panel username:</td>
<td><input type="text" style="width: 100%;" name="user" placeholder="Panel username" required></td>
</tr>
<tr>
<td>Panel password:</td>
<td><input type="password" style="width: 100%;" name="pass" placeholder="Panel password" required></td>
</tr>
<tr>
<td>GroupMe API token:</td>
<td><input type="text" style="width: 100%;" name="apitoken" placeholder="Your GroupMe API token" required></td>
</tr>
<tr>
<td>GroupMe Bot token:</td>
<td><input type="text" style="width: 100%;" name="bottoken" placeholder="Your GroupMe Bot token" required></td>
</tr>
<tr>
<td>WeatherUnderground API token:</td>
<td><input type="text" style="width: 100%;" name="wutoken" placeholder="Your WeatherUnderground API token" value=""></td>
</tr>
<tr>
<td>WeatherUnderground location code:</td>
<td><input type="text" style="width: 100%;" name="wuloc" placeholder="Your WeatherUnderground location code" value=""></td>
</tr>
<tr>
<td>Logging, check to enable</td>
<td><input type="checkbox" style="width: 100%;" name="log" value="1" checked required></td>
</tr>
<tr>
<td colspan="3"><input type="submit" value="Initialize"></td>
</tr>
</table>
</form>
EOSETUP;
echo $setup;
//Display the login
function disp_login()
$login = <<<'EOLOGIN'
<form name="login" method="post" action="">
<table align="center" style="width: 50%;">
<tr>
<td>Username:</td>
<td><input type="text" style="width: 100%;" name="username" placeholder="Panel username" required></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="password" style="width: 100%;" name="password" placeholder="Panel password" required></td>
</tr>
<tr>
<td colspan="3"><input type="submit" value="Login"></td>
</tr>
</table>
</form>
EOLOGIN;
echo $login;
index.php:
<!DOCTYPE html>
<html>
<head>
<style>
body
background: url("https://picload.org/image/dadcrgpl/background.png");
background-repeat: repeat-y;
background-size: cover;
color: white;
margin: 0;
padding: 0;
left: 0;
right: 0;
position: absolute;
font-size: 16px;
text-align: center;
font-family: "Lucida Console", Monaco, monospace;
form
border: 0;
margin: 0;
ul
list-style-type: none;
margin: 0;
padding: 0;
overflow: hidden;
li
float: left;
display: block;
summary
background: rgba(255, 0, 0, .1);
text-align: left;;
font-size: 18px;
table
max-width: 100%;
border-spacing: 0;
text-align: left;
font-size: 16px;
tr
max-width: 100%;
th, td
height: 100%;
padding: 10px;
overflow-x: hidden;
vertical-align: middle;
tr:nth-child(even)
background-color: rgba(255, 255, 255, 0.50);
tr:nth-child(odd)
background-color: rgba(255, 255, 255, 0.25);
input
border: 0;
box-sizing: border-box;
color: white;
text-indent: 0px;
font-size: 16px;
background: rgba(0, 0, 0, 0);
font-family: "Lucida Console", Monaco, monospace;
</style>
<title>PHP GroupMe Bot</title>
</head>
<body>
<?php
header('Content-type: text/html; charset=utf-8');
ini_set('display_errors', 1);
ini_set('session.save_path', getcwd());
error_reporting(-1);
include 'functions.php';
session_start();
if (file_exists('db.sqlite'))
if (isset($_SESSION['username']))
if (isset($_POST['logout']))
session_destroy();
header("Refresh:1");
if (!empty($_POST['add_admin_name']) && !empty($_POST['add_admin_pass']))
add_admin($_POST['add_admin_name'], $_POST['add_admin_pass']);
if (isset($_POST['delete_admin']))
delete_admin($_POST['delete_admin']);
if (isset($_POST['change_admin_pass']))
change_admin_pass($_POST['change_admin_pass']);
if (isset($_POST['config']))
update_config($_POST['config']);
if (isset($_POST['find']) && isset($_POST['respond']) && !empty($_POST['find']) && !empty($_POST['respond']))
add_response($_POST['find'], $_POST['respond']);
if (isset($_POST['delete']))
del_responses($_POST['delete']);
if (isset($_POST['users']))
if (isset($_POST['admins']))
update_admins($_POST['admins']);
else
delete_admins();
if (isset($_POST['ignored']))
update_ignored($_POST['ignored']);
else
delete_ignored();
if (isset($_POST['settings']))
update_settings($_POST['settings']);
if (isset($_POST['new_setting']) && !empty($_POST['new_setting']))
add_setting($_POST['new_setting']);
if (isset($_POST['del_settings']) && !empty($_POST['del_settings']))
del_settings($_POST['del_settings']);
if (isset($_POST['send']) && !empty($_POST['send']))
send($_POST['send']);
?>
<div style="align: right; height: 5vh; background: rgba(0, 0, 0, .5);">
<ul>
<?php
$username = $_SESSION['username'];
echo "<li>$username Logged in</li>";
?>
<form name="logout" method="post" action="">
<li><input type="hidden" name="logout" value="logout"></li>
<input style="float: right;" type="submit" value="Log Out">
</form>
</div>
<div style="overflow-y: scroll; height: 90vh">
<details>
<summary>Panel</summary>
<form name="panel" method="post" action="">
<table align="center">
<tr>
<th>Panel Admins</th>
<th>Delete</th>
<th>Change Password</th>
</tr>
<?php
$admins = get_panel_admins();
foreach ($admins as $element)
$name = $element;
echo "<tr>";
echo "<td>$name</td>";
echo "<td><input type="checkbox" name="delete_admin" value="$name"></td>";
echo "<td><input type="password" name="change_admin_pass[$name]" placeholder="Password"></td>";
echo "</tr>";
?>
<tr>
<td><input type="text" name="add_admin_name" placeholder="Username"></td>
<td colspan="2"><input type="password" name="add_admin_pass" placeholder="Password"></td>
<tr>
<th colspan="3"><input type="submit" value="Update"></th>
</tr>
</table>
</form>
</details>
<details>
<summary>Config</summary>
<form name="config" method="post" action="">
<table align="center">
<tr>
<th>Setting</th>
<th>Value</th>
<th>New Value</th>
</tr>
<?php
$config = get_config();
foreach ($config as $element)
$name = $element['name'];
$value = $element['value'];
echo "<tr>";
echo "<td>$name</td>";
if (stripos($name, 'token') !== FALSE)
$value = str_repeat('*', strlen($value) - 4) . substr($value, -4);
echo "<td>$value</td>";
echo "<td><input type="text" name="config[$name]" value="$value"></td>";
else
echo "<td>$value</td>";
echo "<td><input type="text" name="config[$name]" value="$value"></td>";
echo "</tr>";
?>
<tr>
<th colspan="3"><input type="submit" value="Update"></th>
</tr>
</table>
</form>
</details>
<details>
<summary>Add</summary>
<form name="add" method="post" action="">
<h3>%n can be used to mention someone in a response</h3>
<table align="center">
<tr>
<th><input type="text" name="find" placeholder="Text to find"></th>
<th><input type="text" name="respond" placeholder="Text to respond with"></th>
<th><input type="submit" value="Add"></th>
</tr>
</table>
</form>
</details>
<details>
<summary>Delete</summary>
<form name="delete" method="post" action="">
<table align="center">
<tr>
<th>Find</th>
<th>Respond</th>
<th>Delete</th>
</tr>
<?php
$responses = get_responses();
foreach ($responses as $element)
$find = $element['find'];
$respond = $element['respond'];
echo "<tr>";
echo "<td>$find</td>";
echo "<td>$respond</td>";
echo "<td><input type="checkbox" name="delete" value="$find"></td>";
echo "</tr>";
?>
<tr>
<th colspan="3"><input type="submit" value="Remove"></th>
</tr>
</table>
</form>
</details>
<details>
<summary>Users</summary>
<form name="Users" method="post" action="">
<table align="center">
<tr>
<th>Name</th>
<th>Admin</th>
<th>Ignored</th>
</tr>
<?php
$admins = get_admins();
$ignored = get_ignored();
$users = get_users();
$i = 0;
foreach ($users as $user)
$name = htmlspecialchars($user["name"]);
$userid = htmlspecialchars($user["userid"]);
$avatar = $user["avatar"];
echo "<tr>";
echo "<td style="text-align: left;"><img src="$avatar" style="width:50px; height:50px; vertical-align: middle;">$name ($userid)</td>";
if (in_array($users[$i]['userid'], $admins))
echo "<td><input type="checkbox" name="admins" value="$userid" checked></td>";
else
echo "<td><input type="checkbox" name="admins" value="$userid"></td>";
if (in_array($users[$i]['userid'], $ignored))
echo "<td><input type="checkbox" name="ignored" value="$userid" checked></td>";
else
echo "<td><input type="checkbox" name="ignored" value="$userid"></td>";
echo "</tr>";
$i++;
?>
<tr>
<th colspan="3"><input type="submit" value="Update"></th>
</tr>
</table>
<input type="hidden" name="users" value="1">
</form>
</details>
<details>
<summary>Settings</summary>
<form name="settings" method="post" action="">
<table align="center">
<tr>
<th>Name</th>
<th>State</th>
<th>Delete</th>
</tr>
<?php
$settings = get_settings();
foreach ($settings as $element=>$key)
$name = $element;
$value = $key;
echo "<tr>";
echo "<td>$name</td>";
if ($value)
echo "<td><input type="checkbox" name="settings" value="$name" checked></td>";
else
echo "<td><input type="checkbox" name="settings" value="$name"></td>";
echo "<td><input type="checkbox" name="del_settings" value="$name"></td>";
echo "</tr>";
?>
<tr>
<td>Add setting</td>
<td colspan="2"><input type="text" name="new_setting" placeholder="Name for new setting"></td>
</tr>
<tr>
<th colspan="3"><input type="submit" value="Update"></th>
</tr>
</table>
<input type="hidden" name="settings" value="1">
</form>
</details>
<details>
<summary>Log</summary>
<table style="width: 100%;">
<?php
$log = get_log();
foreach ($log as $element)
$timestamp = date("Y-m-d@H:i:s", $element['timestamp']);
$entry = htmlspecialchars($element['entry']);
echo "<tr>";
echo "<td>$timestamp</td>";
echo "<td>$entry</td>";
echo "</tr>";
?>
</table>
</details>
</div>
<form name="send" method="post" action="">
<table style="width: 100%; position: fixed; bottom: 0; height: 5vh">
<tr>
<th><input type="text" name="send" placeholder="Message to send"></th>
</tr>
</table>
<input type="submit" value="Send" style="display: none">
</form>
<?php
else
disp_login();
if (isset($_POST['username']) && isset($_POST['password']))
$db = new PDO('sqlite:db.sqlite');
$username = strtolower($_POST['username']);
$password = $_POST['password'];
$query = $db->prepare('SELECT password FROM auth WHERE username=:username');
$query->bindValue(':username', $username, PDO::PARAM_STR);
$query->execute();
$hashed = $query->fetch(PDO::FETCH_COLUMN, 0);
if (password_verify($password, $hashed))
echo "Logging in...";
$_SESSION['username'] = $username;
header("Refresh:1");
else
echo "Incorrect password!";
else if (is_writeable('./'))
if (!empty($_POST) && initdb())
$db = new PDO('sqlite:db.sqlite');
$config = ['apitoken', 'bottoken', 'wutoken', 'wuloc'];
$settings = ['litecoin', 'bitcoin', 'ethereum'];
foreach($config as $variable)
$statement = $db->prepare('INSERT INTO config (name, value) VALUES (:name, :value)');
$statement->bindValue(':name', $variable, PDO::PARAM_STR);
$statement->bindValue(':value', $_POST[$variable], PDO::PARAM_STR);
$statement->execute();
if ($_POST['log'])
$db->exec("INSERT INTO config (name, value) VALUES ('log', '1')");
else
$db->exec("INSERT INTO config (name, value) VALUES ('log', '1')");
if ((isset($_POST['wutoken'])) && isset($_POST['wuloc']))
$db->exec("INSERT INTO settings (name, value) VALUES ('weather', '1')");
else
$db->exec("INSERT INTO settings (name, value) VALUES ('weather', '0')");
$db->exec("INSERT INTO settings (name, value) VALUES ('lights', '0')");
$db->exec("INSERT INTO responses (find, respond) VALUES ('test', 'It works!')");
add_admin($_POST['user'], $_POST['pass']);
foreach($settings as $variable)
$statement = $db->prepare('INSERT INTO settings (name, value) VALUES (:name, :value)');
$statement->bindValue(':name', $variable, PDO::PARAM_STR);
$statement->bindValue(':value', '1', PDO::PARAM_STR);
$statement->execute();
file_put_contents('.htaccess', "<Files "db.sqlite">nDeny From Alln</Files>n<Files "sess*">nDeny From Alln</Files>");
header("Refresh:1");
disp_setup();
else
echo "Working directory is not writeable, either chown it to the webserver user and group or allow write permissions to everyone (insecure!)";
?>
</body>
</html>
php sqlite
add a comment |Â
up vote
0
down vote
favorite
up vote
0
down vote
favorite
I made a lot of updates to my GroupMe bot, now it uses sqlite and has a web panel. I'm wondering if there are any bad coding techniques used and more importantly if there are any security issues with it.
https://github.com/desultory/GroupMe-Bot
bot.php:
<?php
//Includes all functions and parses the post data into appropriate variables
include 'functions.php';
include 'lights.php';
$callback = json_decode(file_get_contents('php://input'));
$attachments = $callback->attachments;
$avatar = $callback->avatar_url;
$name = $callback->name;
$type = $callback->sender_type;
$text = $callback->text;
$userid = $callback->user_id;
$admins = get_admins();
$ignored = get_ignored();
$settings = get_settings();
//If logging is enabled in the config, this logs the chat to the database
logging($userid, $name, $text);
//Only handles messages from users to prevent infinite loops
if ($type == 'user' && !in_array($userid, $ignored) && $text[0] != '/')
//Basic response is a simple response to a found phrase
basic_response($text, $name, $userid);
//If the Weather Underground API token and location are set and weather has been enabled, this will return a forecast if someone says "weather"
if ($settings['weather'])
weather_response($text);
//If anyone says "bitcoin" and the bitcoin setting is enabled, this will return the price in USD
if ($settings['bitcoin'])
btc_response($text);
//If anyone says "ethereum" and the ethereum setting is enabled, this will return the price in USD and BTC
if ($settings['ethereum'])
eth_response($text);
//If anyone says "litecoin" and the litecoin setting is enabled, this will return the price in USD and BTC
if ($settings['litecoin'])
ltc_response($text);
if ($settings['lights'])
blink($ip, $pins, "50", "20");
if (in_array($userid, $admins) && $type == 'user' && $text == '/config')
functions.php:
<?php
//Writes the contents of a variable to a text file for debugging purposes
function debugvar($variable)
file_put_contents('debug.txt', print_r($variable, true));
//Initialize the database
function initdb()
$db = new PDO('sqlite:db.sqlite');
$dbcmds = ['CREATE TABLE IF NOT EXISTS config(
name TEXT NOT NULL,
value TEXT NOT NULL
)',
'CREATE TABLE IF NOT EXISTS settings(
name TEXT NOT NULL,
value INTEGER NOT NULL
)',
'CREATE TABLE IF NOT EXISTS responses(
find TEXT NOT NULL,
respond TEXT NOT NULL
)',
'CREATE TABLE IF NOT EXISTS users(
name TEXT NOT NULL,
userid TEXT NOT NULL,
admin INTEGER,
ignored INTEGER
)',
'CREATE TABLE IF NOT EXISTS auth(
username TEXT NOT NULL,
password TEXT NOT NULL
)',
'CREATE TABLE IF NOT EXISTS log(
entry TEXT NOT NULL,
timestamp INTEGER NOT NULL
)',
];
foreach ($dbcmds as $cmd)
$db->exec($cmd);
$clean = 1;
foreach ($db->errorInfo() as $error)
if ($error != 0)
$clean = $error;
return $clean;
//Gets the specified config variable value from the database
function get_config_var($parameter)
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT value FROM config WHERE name=:name');
$query->bindValue(':name', $parameter, PDO::PARAM_STR);
$query->execute();
$result = $query->fetch(PDO::FETCH_ASSOC);
return $result['value'];
//Returns panel admins as an array
function get_panel_admins()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT username FROM auth');
$query->execute();
$result = $query->fetchAll(PDO::FETCH_COLUMN, 0);
return $result;
//Adds admins listed in array
function add_admin($user, $pass)
$db = new PDO('sqlite:db.sqlite');
$admins = get_panel_admins();
$username = strtolower($user);
$password = password_hash($pass, PASSWORD_DEFAULT);
if (!in_array($username, $admins))
$query = $db->prepare('INSERT INTO auth (username, password) VALUES (:username, :password)');
$query->bindValue(':username', $username, PDO::PARAM_STR);
$query->bindValue(':password', $password, PDO::PARAM_STR);
$query->execute();
else
echo "Admin already exists";
//Changes an admin password to the specified password
function change_admin_pass($users)
$db = new PDO('sqlite:db.sqlite');
foreach ($users as $name=>$pass)
if (!empty($pass))
$username = strtolower($name);
$password = password_hash($pass, PASSWORD_DEFAULT);
$query = $db->prepare('UPDATE auth set password=:password WHERE username=:username');
$query->bindValue(':username', $username, PDO::PARAM_STR);
$query->bindValue(':password', $password, PDO::PARAM_STR);
$query->execute();
//Deletes admins listed in array
function delete_admin($delete)
$db = new PDO('sqlite:db.sqlite');
foreach ($delete as $name)
$query = $db->prepare('SELECT count(username) FROM auth');
$query->execute();
$count = $query->fetch();
$count = $count[0];
if ($count > '1')
$username = strtolower($name);
$query = $db->prepare('DELETE FROM auth WHERE username=:name');
$query->bindValue(':name', $username, PDO::PARAM_STR);
$query->execute();
else
echo "Cannot delete last admin";
//Returns the responses as an array
function get_responses()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT find,respond FROM responses');
$query->execute();
return $query->fetchAll();
//Returns the config as an array
function get_config()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT name,value FROM config');
$query->execute();
return $query->fetchAll();
//Returns the chat log
function get_log()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT entry,timestamp FROM log');
$query->execute();
return $query->fetchAll();
//Returns the admins as an array
function get_admins()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT userid FROM users WHERE admin=1');
$query->execute();
return $query->fetchAll(PDO::FETCH_COLUMN, 0);
//Returns the ignored users as an array
function get_ignored()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT userid FROM users WHERE ignored=1');
$query->execute();
return $query->fetchAll(PDO::FETCH_COLUMN, 0);
//Returns the settings as an array
function get_settings()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT name,value FROM settings');
$query->execute();
$result = $query->fetchAll();
foreach ($result as $setting)
$settings[$setting[0]] = $setting[1];
return $settings;
//Logs all chat to the database
function logging($userid, $name, $text)
$db = new PDO('sqlite:db.sqlite');
if (get_config_var('log'))
$entry = "$name($userid): $text";
$statement = $db->prepare('INSERT INTO log (entry, timestamp) VALUES (:entry, :timestamp)');
$statement->bindValue(':entry', $entry, PDO::PARAM_STR);
$statement->bindValue(':timestamp', time(), PDO::PARAM_STR);
$statement->execute();
//Basic response (no images)
function basic_response($text, $name, $userid)
$responses = get_responses();
foreach ($responses as $element)
if (stripos($text, $element[0]) !== FALSE)
$message = $element[1];
$message = str_replace('%u', $userid, $message);
if (stripos($message, '%n') !== FALSE)
$message = str_replace('%n', $name, $message);
mention($message, $name);
else
send($message);
//WUnderground response
function weather_response($text)
$wutoken = get_config_var('wutoken');
$wuloc = get_config_var('wuloc');
if (stripos($text, 'weather') !== FALSE)
if (isset($wutoken) && isset($wuloc))
$rawweather = json_decode(curl_get("https://api.wunderground.com/api/$wutoken/conditions/q/$wuloc.json"));
$temperature = $rawweather->current_observation->feelslike_string;
$weather = $rawweather->current_observation->weather;
$icon = $rawweather->current_observation->icon_url;
$forecast = "The weather is $weather with a temperature of $temperature";
send_img($forecast, $icon);
else
send('WUnderground token and location are not set');
//Bitcoin value response
function btc_response($text)
if (stripos($text, 'bitcoin') !== FALSE)
$pricedata = json_decode(curl_get("https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=USD"));
$usdprice = $pricedata->USD;
$message = "Bitcoin is worth $$usdprice";
$btclogo = 'https://files.coinmarketcap.com/static/img/coins/32x32/bitcoin.png';
send_img($message, $btclogo);
//Ethereum value response
function eth_response($text)
if (stripos($text, 'ethereum') !== FALSE)
$pricedata = json_decode(curl_get("https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=BTC,USD"));
$usdprice = $pricedata->USD;
$btcprice = $pricedata->BTC;
$message = "Ethereum is worth $$usdprice and $btcprice Bitcoin";
$ethlogo = 'https://files.coinmarketcap.com/static/img/coins/32x32/ethereum.png';
send_img($message, $ethlogo);
//Litecoin value response
function ltc_response($text)
if (stripos($text, 'litecoin') !== FALSE)
$pricedata = json_decode(curl_get("https://min-api.cryptocompare.com/data/price?fsym=LTC&tsyms=BTC,USD"));
$usdprice = $pricedata->USD;
$btcprice = $pricedata->BTC;
$message = "Litecoin is worth $$usdprice and $btcprice Bitcoin";
$ltclogo = 'https://files.coinmarketcap.com/static/img/coins/32x32/litecoin.png';
send_img($message, $ltclogo);
//Curl get function, takes url and returns the get response
function curl_get($url)
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$url");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$get = curl_exec($ch);
curl_close($ch);
return $get;
//Curl post to groupme, takes the postfields and posts to the groupme bot api
function curl_post($postfields)
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.groupme.com/v3/bots/post');
curl_setopt($ch, CURLOPT_POSTFIELDS, $postfields);
curl_exec($ch);
curl_close($ch);
//Send message function, takes a message as input and posts to GroupMe
function send($message)
$bottoken = get_config_var('bottoken');
$postdata = [
'bot_id' => $bottoken,
'text' => $message
];
curl_post(http_build_query($postdata));
//Send image function, takes message and img url as inputs and posts to GroupMe
function send_img($message, $image)
$bottoken = get_config_var('bottoken');
$attachments = [
'type' => 'image',
'url' => $image
];
$postdata = [
'bot_id' => $bottoken,
'text' => $message,
'attachments' => [$attachments]
];
curl_post(json_encode($postdata));
//Mention function, takes a message and name as inputs and posts to GroupMe
function mention($message, $name)
$bottoken = get_config_var('bottoken');
$loci = [
stripos($message, $name),
strlen($name)
];
$attachments = [
'loci' => [$loci],
'type' => 'mentions',
'user_ids' => [get_user_id($name)]
];
$postdata = [
'bot_id' => $bottoken,
'text' => $message,
'attachments' => [$attachments]
];
curl_post(json_encode($postdata));
//Get bot group function, returns the group id of the bot
function get_bot_group()
$apitoken = get_config_var('apitoken');
$bottoken = get_config_var('bottoken');
$bots = json_decode(curl_get("https://api.groupme.com/v3/bots?token=$apitoken"));
foreach($bots->response as $element)
if ($element->bot_id == $bottoken)
return $element->group_id;
//Get user id function, takes a name as input and returns the user id
function get_user_id($name)
$apitoken = get_config_var('apitoken');
$user_id = 'No member with that name found';
$groupid = get_bot_group();
$groups = json_decode(curl_get("https://api.groupme.com/v3/groups?token=$apitoken"));
foreach($groups->response as $element)
if ($element->id == $groupid)
foreach($element->members as $member)
if (stripos($member->nickname, $name) !== FALSE)
$user_id = $member->user_id;
return $user_id;
//Get name function, takes a user id as input and returns the name associated with that user id
function get_name($userid)
$apitoken = get_config_var('apitoken');
$name = 'Invalid userid';
$groupid = get_bot_group();
$groups = json_decode(curl_get("https://api.groupme.com/v3/groups?token=$apitoken"));
foreach($groups->response as $element)
if ($element->id == $groupid)
foreach($element->members as $member)
if ($userid == $member->user_id)
$name = $member->nickname;
return $name;
//Get users function, gets user information from the groupme api, adds it to the database, and returns it as an array
function get_users()
$apitoken = get_config_var('apitoken');
$groupid = get_bot_group();
$groups = json_decode(curl_get("https://api.groupme.com/v3/groups?token=$apitoken"));
$index = 0;
$db = new PDO('sqlite:db.sqlite');
foreach($groups->response as $element)
if ($element->id == $groupid)
foreach($element->members as $member)
$userid = $member->user_id;
$name = $member->nickname;
$avatar = $member->image_url;
$query = $db->prepare('SELECT userid FROM users WHERE userid=:userid');
$query->bindValue('userid', $userid, PDO::PARAM_STR);
$query->execute();
$result = $query->fetch(PDO::FETCH_ASSOC);
if (isset($result['userid']))
$query = $db->prepare('UPDATE users SET name=:name WHERE userid=:userid');
$query->bindValue(':name', $name, PDO::PARAM_STR);
$query->bindValue(':userid', $userid, PDO::PARAM_STR);
$query->execute();
else
$query = $db->prepare('INSERT INTO users (name, userid) VALUES (:name, :userid)');
$query->bindValue(':name', $name, PDO::PARAM_STR);
$query->bindValue(':userid', $userid, PDO::PARAM_STR);
$query->execute();
$members[$index] = [
"userid" => $userid,
"name" => $name,
"avatar" => $avatar
];
$index++;
return $members;
//Adds a response to the database, uses input find and respond where find is the text that is searched for and respond is the text that is retrned
function add_response($find, $respond)
$responses = get_responses();
$exists = 0;
foreach ($responses as $element)
if (stripos($element[0], $find) !== FALSE
if (!$exists)
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('INSERT INTO responses (find, respond) VALUES (:find, :respond)');
$query->bindValue(':find', $find, PDO::PARAM_STR);
$query->bindValue(':respond', $respond, PDO::PARAM_STR);
$query->execute();
else
echo "Similar find already exists<br>";
//Deletes responses from the database, takes the "find" string as input
function del_responses($delete)
$db = new PDO('sqlite:db.sqlite');
foreach ($delete as $find)
$query = $db->prepare('DELETE FROM responses WHERE find=:find');
$query->bindValue(':find', $find, PDO::PARAM_STR);
$query->execute();
//Deletes all admins from the database
function delete_admins()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('UPDATE users SET admin = 0');
$query->execute();
//Updates the admins by deleting all of them and then adding the specified userids
function update_admins($admins)
delete_admins();
$db = new PDO('sqlite:db.sqlite');
foreach ($admins as $element)
$query = $db->prepare('UPDATE users SET admin=:admin WHERE userid=:userid');
$query->bindValue(':userid', $element, PDO::PARAM_STR);
$query->bindValue(':admin', '1', PDO::PARAM_STR);
$query->execute();
//Deletes all ignored users from the database
function delete_ignored()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('UPDATE users SET ignored = 0');
$query->execute();
//Updates the users by deleting all of them and then adding the specified userids
function update_ignored($ignored)
delete_ignored();
$db = new PDO('sqlite:db.sqlite');
foreach ($ignored as $element)
$query = $db->prepare('UPDATE users SET ignored=:ignored WHERE userid=:userid');
$query->bindValue(':userid', $element, PDO::PARAM_STR);
$query->bindValue(':ignored', '1', PDO::PARAM_STR);
$query->execute();
//Resets all settings in the database
function reset_settings()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('UPDATE settings SET value = 0');
$query->execute();
//Updates the settings by restting all of the settings and then enabling the specified ones
function update_settings($settings)
reset_settings();
$db = new PDO('sqlite:db.sqlite');
foreach ($settings as $element)
$query = $db->prepare('UPDATE settings SET value=:value WHERE name=:name');
$query->bindValue(':name', $element, PDO::PARAM_STR);
$query->bindValue(':value', '1', PDO::PARAM_STR);
$query->execute();
//Adds the specified setting to the array if it doesn't already exist
function add_setting($setting)
$settings = get_settings();
$exists = 0;
foreach ($settings as $element=>$key)
if ($setting == $element)
$exists = 1;
if (!$exists)
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('INSERT INTO settings (name, value) VALUES (:name, :value)');
$query->bindValue(':name', $setting, PDO::PARAM_STR);
$query->bindValue(':value', '1', PDO::PARAM_STR);
$query->execute();
else
echo "Setting already exists<br>";
//Deletes responses from the database, takes the "find" string as input
function del_settings($delete)
$db = new PDO('sqlite:db.sqlite');
foreach ($delete as $setting)
$query = $db->prepare('DELETE FROM settings WHERE name=:setting');
$query->bindValue(':setting', $setting, PDO::PARAM_STR);
$query->execute();
//Updates the config, takes an array of config paramaters with the element name being the paramter and the value being the value
function update_config($config)
$db = new PDO('sqlite:db.sqlite');
foreach ($config as $name=>$value)
if ($value[0] != "*")
$query = $db->prepare('UPDATE config SET value=:value WHERE name=:name');
$query->bindValue(':name', $name, PDO::PARAM_STR);
$query->bindValue(':value', $value, PDO::PARAM_STR);
$query->execute();
//Display the setup form
function disp_setup()
$setup = <<<'EOSETUP'
<form name="setup" method="post" action="">
<table align="center" style="width: 50%;">
<tr>
<td>Panel username:</td>
<td><input type="text" style="width: 100%;" name="user" placeholder="Panel username" required></td>
</tr>
<tr>
<td>Panel password:</td>
<td><input type="password" style="width: 100%;" name="pass" placeholder="Panel password" required></td>
</tr>
<tr>
<td>GroupMe API token:</td>
<td><input type="text" style="width: 100%;" name="apitoken" placeholder="Your GroupMe API token" required></td>
</tr>
<tr>
<td>GroupMe Bot token:</td>
<td><input type="text" style="width: 100%;" name="bottoken" placeholder="Your GroupMe Bot token" required></td>
</tr>
<tr>
<td>WeatherUnderground API token:</td>
<td><input type="text" style="width: 100%;" name="wutoken" placeholder="Your WeatherUnderground API token" value=""></td>
</tr>
<tr>
<td>WeatherUnderground location code:</td>
<td><input type="text" style="width: 100%;" name="wuloc" placeholder="Your WeatherUnderground location code" value=""></td>
</tr>
<tr>
<td>Logging, check to enable</td>
<td><input type="checkbox" style="width: 100%;" name="log" value="1" checked required></td>
</tr>
<tr>
<td colspan="3"><input type="submit" value="Initialize"></td>
</tr>
</table>
</form>
EOSETUP;
echo $setup;
//Display the login
function disp_login()
$login = <<<'EOLOGIN'
<form name="login" method="post" action="">
<table align="center" style="width: 50%;">
<tr>
<td>Username:</td>
<td><input type="text" style="width: 100%;" name="username" placeholder="Panel username" required></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="password" style="width: 100%;" name="password" placeholder="Panel password" required></td>
</tr>
<tr>
<td colspan="3"><input type="submit" value="Login"></td>
</tr>
</table>
</form>
EOLOGIN;
echo $login;
index.php:
<!DOCTYPE html>
<html>
<head>
<style>
body
background: url("https://picload.org/image/dadcrgpl/background.png");
background-repeat: repeat-y;
background-size: cover;
color: white;
margin: 0;
padding: 0;
left: 0;
right: 0;
position: absolute;
font-size: 16px;
text-align: center;
font-family: "Lucida Console", Monaco, monospace;
form
border: 0;
margin: 0;
ul
list-style-type: none;
margin: 0;
padding: 0;
overflow: hidden;
li
float: left;
display: block;
summary
background: rgba(255, 0, 0, .1);
text-align: left;;
font-size: 18px;
table
max-width: 100%;
border-spacing: 0;
text-align: left;
font-size: 16px;
tr
max-width: 100%;
th, td
height: 100%;
padding: 10px;
overflow-x: hidden;
vertical-align: middle;
tr:nth-child(even)
background-color: rgba(255, 255, 255, 0.50);
tr:nth-child(odd)
background-color: rgba(255, 255, 255, 0.25);
input
border: 0;
box-sizing: border-box;
color: white;
text-indent: 0px;
font-size: 16px;
background: rgba(0, 0, 0, 0);
font-family: "Lucida Console", Monaco, monospace;
</style>
<title>PHP GroupMe Bot</title>
</head>
<body>
<?php
header('Content-type: text/html; charset=utf-8');
ini_set('display_errors', 1);
ini_set('session.save_path', getcwd());
error_reporting(-1);
include 'functions.php';
session_start();
if (file_exists('db.sqlite'))
if (isset($_SESSION['username']))
if (isset($_POST['logout']))
session_destroy();
header("Refresh:1");
if (!empty($_POST['add_admin_name']) && !empty($_POST['add_admin_pass']))
add_admin($_POST['add_admin_name'], $_POST['add_admin_pass']);
if (isset($_POST['delete_admin']))
delete_admin($_POST['delete_admin']);
if (isset($_POST['change_admin_pass']))
change_admin_pass($_POST['change_admin_pass']);
if (isset($_POST['config']))
update_config($_POST['config']);
if (isset($_POST['find']) && isset($_POST['respond']) && !empty($_POST['find']) && !empty($_POST['respond']))
add_response($_POST['find'], $_POST['respond']);
if (isset($_POST['delete']))
del_responses($_POST['delete']);
if (isset($_POST['users']))
if (isset($_POST['admins']))
update_admins($_POST['admins']);
else
delete_admins();
if (isset($_POST['ignored']))
update_ignored($_POST['ignored']);
else
delete_ignored();
if (isset($_POST['settings']))
update_settings($_POST['settings']);
if (isset($_POST['new_setting']) && !empty($_POST['new_setting']))
add_setting($_POST['new_setting']);
if (isset($_POST['del_settings']) && !empty($_POST['del_settings']))
del_settings($_POST['del_settings']);
if (isset($_POST['send']) && !empty($_POST['send']))
send($_POST['send']);
?>
<div style="align: right; height: 5vh; background: rgba(0, 0, 0, .5);">
<ul>
<?php
$username = $_SESSION['username'];
echo "<li>$username Logged in</li>";
?>
<form name="logout" method="post" action="">
<li><input type="hidden" name="logout" value="logout"></li>
<input style="float: right;" type="submit" value="Log Out">
</form>
</div>
<div style="overflow-y: scroll; height: 90vh">
<details>
<summary>Panel</summary>
<form name="panel" method="post" action="">
<table align="center">
<tr>
<th>Panel Admins</th>
<th>Delete</th>
<th>Change Password</th>
</tr>
<?php
$admins = get_panel_admins();
foreach ($admins as $element)
$name = $element;
echo "<tr>";
echo "<td>$name</td>";
echo "<td><input type="checkbox" name="delete_admin" value="$name"></td>";
echo "<td><input type="password" name="change_admin_pass[$name]" placeholder="Password"></td>";
echo "</tr>";
?>
<tr>
<td><input type="text" name="add_admin_name" placeholder="Username"></td>
<td colspan="2"><input type="password" name="add_admin_pass" placeholder="Password"></td>
<tr>
<th colspan="3"><input type="submit" value="Update"></th>
</tr>
</table>
</form>
</details>
<details>
<summary>Config</summary>
<form name="config" method="post" action="">
<table align="center">
<tr>
<th>Setting</th>
<th>Value</th>
<th>New Value</th>
</tr>
<?php
$config = get_config();
foreach ($config as $element)
$name = $element['name'];
$value = $element['value'];
echo "<tr>";
echo "<td>$name</td>";
if (stripos($name, 'token') !== FALSE)
$value = str_repeat('*', strlen($value) - 4) . substr($value, -4);
echo "<td>$value</td>";
echo "<td><input type="text" name="config[$name]" value="$value"></td>";
else
echo "<td>$value</td>";
echo "<td><input type="text" name="config[$name]" value="$value"></td>";
echo "</tr>";
?>
<tr>
<th colspan="3"><input type="submit" value="Update"></th>
</tr>
</table>
</form>
</details>
<details>
<summary>Add</summary>
<form name="add" method="post" action="">
<h3>%n can be used to mention someone in a response</h3>
<table align="center">
<tr>
<th><input type="text" name="find" placeholder="Text to find"></th>
<th><input type="text" name="respond" placeholder="Text to respond with"></th>
<th><input type="submit" value="Add"></th>
</tr>
</table>
</form>
</details>
<details>
<summary>Delete</summary>
<form name="delete" method="post" action="">
<table align="center">
<tr>
<th>Find</th>
<th>Respond</th>
<th>Delete</th>
</tr>
<?php
$responses = get_responses();
foreach ($responses as $element)
$find = $element['find'];
$respond = $element['respond'];
echo "<tr>";
echo "<td>$find</td>";
echo "<td>$respond</td>";
echo "<td><input type="checkbox" name="delete" value="$find"></td>";
echo "</tr>";
?>
<tr>
<th colspan="3"><input type="submit" value="Remove"></th>
</tr>
</table>
</form>
</details>
<details>
<summary>Users</summary>
<form name="Users" method="post" action="">
<table align="center">
<tr>
<th>Name</th>
<th>Admin</th>
<th>Ignored</th>
</tr>
<?php
$admins = get_admins();
$ignored = get_ignored();
$users = get_users();
$i = 0;
foreach ($users as $user)
$name = htmlspecialchars($user["name"]);
$userid = htmlspecialchars($user["userid"]);
$avatar = $user["avatar"];
echo "<tr>";
echo "<td style="text-align: left;"><img src="$avatar" style="width:50px; height:50px; vertical-align: middle;">$name ($userid)</td>";
if (in_array($users[$i]['userid'], $admins))
echo "<td><input type="checkbox" name="admins" value="$userid" checked></td>";
else
echo "<td><input type="checkbox" name="admins" value="$userid"></td>";
if (in_array($users[$i]['userid'], $ignored))
echo "<td><input type="checkbox" name="ignored" value="$userid" checked></td>";
else
echo "<td><input type="checkbox" name="ignored" value="$userid"></td>";
echo "</tr>";
$i++;
?>
<tr>
<th colspan="3"><input type="submit" value="Update"></th>
</tr>
</table>
<input type="hidden" name="users" value="1">
</form>
</details>
<details>
<summary>Settings</summary>
<form name="settings" method="post" action="">
<table align="center">
<tr>
<th>Name</th>
<th>State</th>
<th>Delete</th>
</tr>
<?php
$settings = get_settings();
foreach ($settings as $element=>$key)
$name = $element;
$value = $key;
echo "<tr>";
echo "<td>$name</td>";
if ($value)
echo "<td><input type="checkbox" name="settings" value="$name" checked></td>";
else
echo "<td><input type="checkbox" name="settings" value="$name"></td>";
echo "<td><input type="checkbox" name="del_settings" value="$name"></td>";
echo "</tr>";
?>
<tr>
<td>Add setting</td>
<td colspan="2"><input type="text" name="new_setting" placeholder="Name for new setting"></td>
</tr>
<tr>
<th colspan="3"><input type="submit" value="Update"></th>
</tr>
</table>
<input type="hidden" name="settings" value="1">
</form>
</details>
<details>
<summary>Log</summary>
<table style="width: 100%;">
<?php
$log = get_log();
foreach ($log as $element)
$timestamp = date("Y-m-d@H:i:s", $element['timestamp']);
$entry = htmlspecialchars($element['entry']);
echo "<tr>";
echo "<td>$timestamp</td>";
echo "<td>$entry</td>";
echo "</tr>";
?>
</table>
</details>
</div>
<form name="send" method="post" action="">
<table style="width: 100%; position: fixed; bottom: 0; height: 5vh">
<tr>
<th><input type="text" name="send" placeholder="Message to send"></th>
</tr>
</table>
<input type="submit" value="Send" style="display: none">
</form>
<?php
else
disp_login();
if (isset($_POST['username']) && isset($_POST['password']))
$db = new PDO('sqlite:db.sqlite');
$username = strtolower($_POST['username']);
$password = $_POST['password'];
$query = $db->prepare('SELECT password FROM auth WHERE username=:username');
$query->bindValue(':username', $username, PDO::PARAM_STR);
$query->execute();
$hashed = $query->fetch(PDO::FETCH_COLUMN, 0);
if (password_verify($password, $hashed))
echo "Logging in...";
$_SESSION['username'] = $username;
header("Refresh:1");
else
echo "Incorrect password!";
else if (is_writeable('./'))
if (!empty($_POST) && initdb())
$db = new PDO('sqlite:db.sqlite');
$config = ['apitoken', 'bottoken', 'wutoken', 'wuloc'];
$settings = ['litecoin', 'bitcoin', 'ethereum'];
foreach($config as $variable)
$statement = $db->prepare('INSERT INTO config (name, value) VALUES (:name, :value)');
$statement->bindValue(':name', $variable, PDO::PARAM_STR);
$statement->bindValue(':value', $_POST[$variable], PDO::PARAM_STR);
$statement->execute();
if ($_POST['log'])
$db->exec("INSERT INTO config (name, value) VALUES ('log', '1')");
else
$db->exec("INSERT INTO config (name, value) VALUES ('log', '1')");
if ((isset($_POST['wutoken'])) && isset($_POST['wuloc']))
$db->exec("INSERT INTO settings (name, value) VALUES ('weather', '1')");
else
$db->exec("INSERT INTO settings (name, value) VALUES ('weather', '0')");
$db->exec("INSERT INTO settings (name, value) VALUES ('lights', '0')");
$db->exec("INSERT INTO responses (find, respond) VALUES ('test', 'It works!')");
add_admin($_POST['user'], $_POST['pass']);
foreach($settings as $variable)
$statement = $db->prepare('INSERT INTO settings (name, value) VALUES (:name, :value)');
$statement->bindValue(':name', $variable, PDO::PARAM_STR);
$statement->bindValue(':value', '1', PDO::PARAM_STR);
$statement->execute();
file_put_contents('.htaccess', "<Files "db.sqlite">nDeny From Alln</Files>n<Files "sess*">nDeny From Alln</Files>");
header("Refresh:1");
disp_setup();
else
echo "Working directory is not writeable, either chown it to the webserver user and group or allow write permissions to everyone (insecure!)";
?>
</body>
</html>
php sqlite
I made a lot of updates to my GroupMe bot, now it uses sqlite and has a web panel. I'm wondering if there are any bad coding techniques used and more importantly if there are any security issues with it.
https://github.com/desultory/GroupMe-Bot
bot.php:
<?php
//Includes all functions and parses the post data into appropriate variables
include 'functions.php';
include 'lights.php';
$callback = json_decode(file_get_contents('php://input'));
$attachments = $callback->attachments;
$avatar = $callback->avatar_url;
$name = $callback->name;
$type = $callback->sender_type;
$text = $callback->text;
$userid = $callback->user_id;
$admins = get_admins();
$ignored = get_ignored();
$settings = get_settings();
//If logging is enabled in the config, this logs the chat to the database
logging($userid, $name, $text);
//Only handles messages from users to prevent infinite loops
if ($type == 'user' && !in_array($userid, $ignored) && $text[0] != '/')
//Basic response is a simple response to a found phrase
basic_response($text, $name, $userid);
//If the Weather Underground API token and location are set and weather has been enabled, this will return a forecast if someone says "weather"
if ($settings['weather'])
weather_response($text);
//If anyone says "bitcoin" and the bitcoin setting is enabled, this will return the price in USD
if ($settings['bitcoin'])
btc_response($text);
//If anyone says "ethereum" and the ethereum setting is enabled, this will return the price in USD and BTC
if ($settings['ethereum'])
eth_response($text);
//If anyone says "litecoin" and the litecoin setting is enabled, this will return the price in USD and BTC
if ($settings['litecoin'])
ltc_response($text);
if ($settings['lights'])
blink($ip, $pins, "50", "20");
if (in_array($userid, $admins) && $type == 'user' && $text == '/config')
functions.php:
<?php
//Writes the contents of a variable to a text file for debugging purposes
function debugvar($variable)
file_put_contents('debug.txt', print_r($variable, true));
//Initialize the database
function initdb()
$db = new PDO('sqlite:db.sqlite');
$dbcmds = ['CREATE TABLE IF NOT EXISTS config(
name TEXT NOT NULL,
value TEXT NOT NULL
)',
'CREATE TABLE IF NOT EXISTS settings(
name TEXT NOT NULL,
value INTEGER NOT NULL
)',
'CREATE TABLE IF NOT EXISTS responses(
find TEXT NOT NULL,
respond TEXT NOT NULL
)',
'CREATE TABLE IF NOT EXISTS users(
name TEXT NOT NULL,
userid TEXT NOT NULL,
admin INTEGER,
ignored INTEGER
)',
'CREATE TABLE IF NOT EXISTS auth(
username TEXT NOT NULL,
password TEXT NOT NULL
)',
'CREATE TABLE IF NOT EXISTS log(
entry TEXT NOT NULL,
timestamp INTEGER NOT NULL
)',
];
foreach ($dbcmds as $cmd)
$db->exec($cmd);
$clean = 1;
foreach ($db->errorInfo() as $error)
if ($error != 0)
$clean = $error;
return $clean;
//Gets the specified config variable value from the database
function get_config_var($parameter)
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT value FROM config WHERE name=:name');
$query->bindValue(':name', $parameter, PDO::PARAM_STR);
$query->execute();
$result = $query->fetch(PDO::FETCH_ASSOC);
return $result['value'];
//Returns panel admins as an array
function get_panel_admins()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT username FROM auth');
$query->execute();
$result = $query->fetchAll(PDO::FETCH_COLUMN, 0);
return $result;
//Adds admins listed in array
function add_admin($user, $pass)
$db = new PDO('sqlite:db.sqlite');
$admins = get_panel_admins();
$username = strtolower($user);
$password = password_hash($pass, PASSWORD_DEFAULT);
if (!in_array($username, $admins))
$query = $db->prepare('INSERT INTO auth (username, password) VALUES (:username, :password)');
$query->bindValue(':username', $username, PDO::PARAM_STR);
$query->bindValue(':password', $password, PDO::PARAM_STR);
$query->execute();
else
echo "Admin already exists";
//Changes an admin password to the specified password
function change_admin_pass($users)
$db = new PDO('sqlite:db.sqlite');
foreach ($users as $name=>$pass)
if (!empty($pass))
$username = strtolower($name);
$password = password_hash($pass, PASSWORD_DEFAULT);
$query = $db->prepare('UPDATE auth set password=:password WHERE username=:username');
$query->bindValue(':username', $username, PDO::PARAM_STR);
$query->bindValue(':password', $password, PDO::PARAM_STR);
$query->execute();
//Deletes admins listed in array
function delete_admin($delete)
$db = new PDO('sqlite:db.sqlite');
foreach ($delete as $name)
$query = $db->prepare('SELECT count(username) FROM auth');
$query->execute();
$count = $query->fetch();
$count = $count[0];
if ($count > '1')
$username = strtolower($name);
$query = $db->prepare('DELETE FROM auth WHERE username=:name');
$query->bindValue(':name', $username, PDO::PARAM_STR);
$query->execute();
else
echo "Cannot delete last admin";
//Returns the responses as an array
function get_responses()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT find,respond FROM responses');
$query->execute();
return $query->fetchAll();
//Returns the config as an array
function get_config()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT name,value FROM config');
$query->execute();
return $query->fetchAll();
//Returns the chat log
function get_log()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT entry,timestamp FROM log');
$query->execute();
return $query->fetchAll();
//Returns the admins as an array
function get_admins()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT userid FROM users WHERE admin=1');
$query->execute();
return $query->fetchAll(PDO::FETCH_COLUMN, 0);
//Returns the ignored users as an array
function get_ignored()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT userid FROM users WHERE ignored=1');
$query->execute();
return $query->fetchAll(PDO::FETCH_COLUMN, 0);
//Returns the settings as an array
function get_settings()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('SELECT name,value FROM settings');
$query->execute();
$result = $query->fetchAll();
foreach ($result as $setting)
$settings[$setting[0]] = $setting[1];
return $settings;
//Logs all chat to the database
function logging($userid, $name, $text)
$db = new PDO('sqlite:db.sqlite');
if (get_config_var('log'))
$entry = "$name($userid): $text";
$statement = $db->prepare('INSERT INTO log (entry, timestamp) VALUES (:entry, :timestamp)');
$statement->bindValue(':entry', $entry, PDO::PARAM_STR);
$statement->bindValue(':timestamp', time(), PDO::PARAM_STR);
$statement->execute();
//Basic response (no images)
function basic_response($text, $name, $userid)
$responses = get_responses();
foreach ($responses as $element)
if (stripos($text, $element[0]) !== FALSE)
$message = $element[1];
$message = str_replace('%u', $userid, $message);
if (stripos($message, '%n') !== FALSE)
$message = str_replace('%n', $name, $message);
mention($message, $name);
else
send($message);
//WUnderground response
function weather_response($text)
$wutoken = get_config_var('wutoken');
$wuloc = get_config_var('wuloc');
if (stripos($text, 'weather') !== FALSE)
if (isset($wutoken) && isset($wuloc))
$rawweather = json_decode(curl_get("https://api.wunderground.com/api/$wutoken/conditions/q/$wuloc.json"));
$temperature = $rawweather->current_observation->feelslike_string;
$weather = $rawweather->current_observation->weather;
$icon = $rawweather->current_observation->icon_url;
$forecast = "The weather is $weather with a temperature of $temperature";
send_img($forecast, $icon);
else
send('WUnderground token and location are not set');
//Bitcoin value response
function btc_response($text)
if (stripos($text, 'bitcoin') !== FALSE)
$pricedata = json_decode(curl_get("https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=USD"));
$usdprice = $pricedata->USD;
$message = "Bitcoin is worth $$usdprice";
$btclogo = 'https://files.coinmarketcap.com/static/img/coins/32x32/bitcoin.png';
send_img($message, $btclogo);
//Ethereum value response
function eth_response($text)
if (stripos($text, 'ethereum') !== FALSE)
$pricedata = json_decode(curl_get("https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=BTC,USD"));
$usdprice = $pricedata->USD;
$btcprice = $pricedata->BTC;
$message = "Ethereum is worth $$usdprice and $btcprice Bitcoin";
$ethlogo = 'https://files.coinmarketcap.com/static/img/coins/32x32/ethereum.png';
send_img($message, $ethlogo);
//Litecoin value response
function ltc_response($text)
if (stripos($text, 'litecoin') !== FALSE)
$pricedata = json_decode(curl_get("https://min-api.cryptocompare.com/data/price?fsym=LTC&tsyms=BTC,USD"));
$usdprice = $pricedata->USD;
$btcprice = $pricedata->BTC;
$message = "Litecoin is worth $$usdprice and $btcprice Bitcoin";
$ltclogo = 'https://files.coinmarketcap.com/static/img/coins/32x32/litecoin.png';
send_img($message, $ltclogo);
//Curl get function, takes url and returns the get response
function curl_get($url)
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "$url");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$get = curl_exec($ch);
curl_close($ch);
return $get;
//Curl post to groupme, takes the postfields and posts to the groupme bot api
function curl_post($postfields)
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.groupme.com/v3/bots/post');
curl_setopt($ch, CURLOPT_POSTFIELDS, $postfields);
curl_exec($ch);
curl_close($ch);
//Send message function, takes a message as input and posts to GroupMe
function send($message)
$bottoken = get_config_var('bottoken');
$postdata = [
'bot_id' => $bottoken,
'text' => $message
];
curl_post(http_build_query($postdata));
//Send image function, takes message and img url as inputs and posts to GroupMe
function send_img($message, $image)
$bottoken = get_config_var('bottoken');
$attachments = [
'type' => 'image',
'url' => $image
];
$postdata = [
'bot_id' => $bottoken,
'text' => $message,
'attachments' => [$attachments]
];
curl_post(json_encode($postdata));
//Mention function, takes a message and name as inputs and posts to GroupMe
function mention($message, $name)
$bottoken = get_config_var('bottoken');
$loci = [
stripos($message, $name),
strlen($name)
];
$attachments = [
'loci' => [$loci],
'type' => 'mentions',
'user_ids' => [get_user_id($name)]
];
$postdata = [
'bot_id' => $bottoken,
'text' => $message,
'attachments' => [$attachments]
];
curl_post(json_encode($postdata));
//Get bot group function, returns the group id of the bot
function get_bot_group()
$apitoken = get_config_var('apitoken');
$bottoken = get_config_var('bottoken');
$bots = json_decode(curl_get("https://api.groupme.com/v3/bots?token=$apitoken"));
foreach($bots->response as $element)
if ($element->bot_id == $bottoken)
return $element->group_id;
//Get user id function, takes a name as input and returns the user id
function get_user_id($name)
$apitoken = get_config_var('apitoken');
$user_id = 'No member with that name found';
$groupid = get_bot_group();
$groups = json_decode(curl_get("https://api.groupme.com/v3/groups?token=$apitoken"));
foreach($groups->response as $element)
if ($element->id == $groupid)
foreach($element->members as $member)
if (stripos($member->nickname, $name) !== FALSE)
$user_id = $member->user_id;
return $user_id;
//Get name function, takes a user id as input and returns the name associated with that user id
function get_name($userid)
$apitoken = get_config_var('apitoken');
$name = 'Invalid userid';
$groupid = get_bot_group();
$groups = json_decode(curl_get("https://api.groupme.com/v3/groups?token=$apitoken"));
foreach($groups->response as $element)
if ($element->id == $groupid)
foreach($element->members as $member)
if ($userid == $member->user_id)
$name = $member->nickname;
return $name;
//Get users function, gets user information from the groupme api, adds it to the database, and returns it as an array
function get_users()
$apitoken = get_config_var('apitoken');
$groupid = get_bot_group();
$groups = json_decode(curl_get("https://api.groupme.com/v3/groups?token=$apitoken"));
$index = 0;
$db = new PDO('sqlite:db.sqlite');
foreach($groups->response as $element)
if ($element->id == $groupid)
foreach($element->members as $member)
$userid = $member->user_id;
$name = $member->nickname;
$avatar = $member->image_url;
$query = $db->prepare('SELECT userid FROM users WHERE userid=:userid');
$query->bindValue('userid', $userid, PDO::PARAM_STR);
$query->execute();
$result = $query->fetch(PDO::FETCH_ASSOC);
if (isset($result['userid']))
$query = $db->prepare('UPDATE users SET name=:name WHERE userid=:userid');
$query->bindValue(':name', $name, PDO::PARAM_STR);
$query->bindValue(':userid', $userid, PDO::PARAM_STR);
$query->execute();
else
$query = $db->prepare('INSERT INTO users (name, userid) VALUES (:name, :userid)');
$query->bindValue(':name', $name, PDO::PARAM_STR);
$query->bindValue(':userid', $userid, PDO::PARAM_STR);
$query->execute();
$members[$index] = [
"userid" => $userid,
"name" => $name,
"avatar" => $avatar
];
$index++;
return $members;
//Adds a response to the database, uses input find and respond where find is the text that is searched for and respond is the text that is retrned
function add_response($find, $respond)
$responses = get_responses();
$exists = 0;
foreach ($responses as $element)
if (stripos($element[0], $find) !== FALSE
if (!$exists)
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('INSERT INTO responses (find, respond) VALUES (:find, :respond)');
$query->bindValue(':find', $find, PDO::PARAM_STR);
$query->bindValue(':respond', $respond, PDO::PARAM_STR);
$query->execute();
else
echo "Similar find already exists<br>";
//Deletes responses from the database, takes the "find" string as input
function del_responses($delete)
$db = new PDO('sqlite:db.sqlite');
foreach ($delete as $find)
$query = $db->prepare('DELETE FROM responses WHERE find=:find');
$query->bindValue(':find', $find, PDO::PARAM_STR);
$query->execute();
//Deletes all admins from the database
function delete_admins()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('UPDATE users SET admin = 0');
$query->execute();
//Updates the admins by deleting all of them and then adding the specified userids
function update_admins($admins)
delete_admins();
$db = new PDO('sqlite:db.sqlite');
foreach ($admins as $element)
$query = $db->prepare('UPDATE users SET admin=:admin WHERE userid=:userid');
$query->bindValue(':userid', $element, PDO::PARAM_STR);
$query->bindValue(':admin', '1', PDO::PARAM_STR);
$query->execute();
//Deletes all ignored users from the database
function delete_ignored()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('UPDATE users SET ignored = 0');
$query->execute();
//Updates the users by deleting all of them and then adding the specified userids
function update_ignored($ignored)
delete_ignored();
$db = new PDO('sqlite:db.sqlite');
foreach ($ignored as $element)
$query = $db->prepare('UPDATE users SET ignored=:ignored WHERE userid=:userid');
$query->bindValue(':userid', $element, PDO::PARAM_STR);
$query->bindValue(':ignored', '1', PDO::PARAM_STR);
$query->execute();
//Resets all settings in the database
function reset_settings()
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('UPDATE settings SET value = 0');
$query->execute();
//Updates the settings by restting all of the settings and then enabling the specified ones
function update_settings($settings)
reset_settings();
$db = new PDO('sqlite:db.sqlite');
foreach ($settings as $element)
$query = $db->prepare('UPDATE settings SET value=:value WHERE name=:name');
$query->bindValue(':name', $element, PDO::PARAM_STR);
$query->bindValue(':value', '1', PDO::PARAM_STR);
$query->execute();
//Adds the specified setting to the array if it doesn't already exist
function add_setting($setting)
$settings = get_settings();
$exists = 0;
foreach ($settings as $element=>$key)
if ($setting == $element)
$exists = 1;
if (!$exists)
$db = new PDO('sqlite:db.sqlite');
$query = $db->prepare('INSERT INTO settings (name, value) VALUES (:name, :value)');
$query->bindValue(':name', $setting, PDO::PARAM_STR);
$query->bindValue(':value', '1', PDO::PARAM_STR);
$query->execute();
else
echo "Setting already exists<br>";
//Deletes responses from the database, takes the "find" string as input
function del_settings($delete)
$db = new PDO('sqlite:db.sqlite');
foreach ($delete as $setting)
$query = $db->prepare('DELETE FROM settings WHERE name=:setting');
$query->bindValue(':setting', $setting, PDO::PARAM_STR);
$query->execute();
//Updates the config, takes an array of config paramaters with the element name being the paramter and the value being the value
function update_config($config)
$db = new PDO('sqlite:db.sqlite');
foreach ($config as $name=>$value)
if ($value[0] != "*")
$query = $db->prepare('UPDATE config SET value=:value WHERE name=:name');
$query->bindValue(':name', $name, PDO::PARAM_STR);
$query->bindValue(':value', $value, PDO::PARAM_STR);
$query->execute();
//Display the setup form
function disp_setup()
$setup = <<<'EOSETUP'
<form name="setup" method="post" action="">
<table align="center" style="width: 50%;">
<tr>
<td>Panel username:</td>
<td><input type="text" style="width: 100%;" name="user" placeholder="Panel username" required></td>
</tr>
<tr>
<td>Panel password:</td>
<td><input type="password" style="width: 100%;" name="pass" placeholder="Panel password" required></td>
</tr>
<tr>
<td>GroupMe API token:</td>
<td><input type="text" style="width: 100%;" name="apitoken" placeholder="Your GroupMe API token" required></td>
</tr>
<tr>
<td>GroupMe Bot token:</td>
<td><input type="text" style="width: 100%;" name="bottoken" placeholder="Your GroupMe Bot token" required></td>
</tr>
<tr>
<td>WeatherUnderground API token:</td>
<td><input type="text" style="width: 100%;" name="wutoken" placeholder="Your WeatherUnderground API token" value=""></td>
</tr>
<tr>
<td>WeatherUnderground location code:</td>
<td><input type="text" style="width: 100%;" name="wuloc" placeholder="Your WeatherUnderground location code" value=""></td>
</tr>
<tr>
<td>Logging, check to enable</td>
<td><input type="checkbox" style="width: 100%;" name="log" value="1" checked required></td>
</tr>
<tr>
<td colspan="3"><input type="submit" value="Initialize"></td>
</tr>
</table>
</form>
EOSETUP;
echo $setup;
//Display the login
function disp_login()
$login = <<<'EOLOGIN'
<form name="login" method="post" action="">
<table align="center" style="width: 50%;">
<tr>
<td>Username:</td>
<td><input type="text" style="width: 100%;" name="username" placeholder="Panel username" required></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="password" style="width: 100%;" name="password" placeholder="Panel password" required></td>
</tr>
<tr>
<td colspan="3"><input type="submit" value="Login"></td>
</tr>
</table>
</form>
EOLOGIN;
echo $login;
index.php:
<!DOCTYPE html>
<html>
<head>
<style>
body
background: url("https://picload.org/image/dadcrgpl/background.png");
background-repeat: repeat-y;
background-size: cover;
color: white;
margin: 0;
padding: 0;
left: 0;
right: 0;
position: absolute;
font-size: 16px;
text-align: center;
font-family: "Lucida Console", Monaco, monospace;
form
border: 0;
margin: 0;
ul
list-style-type: none;
margin: 0;
padding: 0;
overflow: hidden;
li
float: left;
display: block;
summary
background: rgba(255, 0, 0, .1);
text-align: left;;
font-size: 18px;
table
max-width: 100%;
border-spacing: 0;
text-align: left;
font-size: 16px;
tr
max-width: 100%;
th, td
height: 100%;
padding: 10px;
overflow-x: hidden;
vertical-align: middle;
tr:nth-child(even)
background-color: rgba(255, 255, 255, 0.50);
tr:nth-child(odd)
background-color: rgba(255, 255, 255, 0.25);
input
border: 0;
box-sizing: border-box;
color: white;
text-indent: 0px;
font-size: 16px;
background: rgba(0, 0, 0, 0);
font-family: "Lucida Console", Monaco, monospace;
</style>
<title>PHP GroupMe Bot</title>
</head>
<body>
<?php
header('Content-type: text/html; charset=utf-8');
ini_set('display_errors', 1);
ini_set('session.save_path', getcwd());
error_reporting(-1);
include 'functions.php';
session_start();
if (file_exists('db.sqlite'))
if (isset($_SESSION['username']))
if (isset($_POST['logout']))
session_destroy();
header("Refresh:1");
if (!empty($_POST['add_admin_name']) && !empty($_POST['add_admin_pass']))
add_admin($_POST['add_admin_name'], $_POST['add_admin_pass']);
if (isset($_POST['delete_admin']))
delete_admin($_POST['delete_admin']);
if (isset($_POST['change_admin_pass']))
change_admin_pass($_POST['change_admin_pass']);
if (isset($_POST['config']))
update_config($_POST['config']);
if (isset($_POST['find']) && isset($_POST['respond']) && !empty($_POST['find']) && !empty($_POST['respond']))
add_response($_POST['find'], $_POST['respond']);
if (isset($_POST['delete']))
del_responses($_POST['delete']);
if (isset($_POST['users']))
if (isset($_POST['admins']))
update_admins($_POST['admins']);
else
delete_admins();
if (isset($_POST['ignored']))
update_ignored($_POST['ignored']);
else
delete_ignored();
if (isset($_POST['settings']))
update_settings($_POST['settings']);
if (isset($_POST['new_setting']) && !empty($_POST['new_setting']))
add_setting($_POST['new_setting']);
if (isset($_POST['del_settings']) && !empty($_POST['del_settings']))
del_settings($_POST['del_settings']);
if (isset($_POST['send']) && !empty($_POST['send']))
send($_POST['send']);
?>
<div style="align: right; height: 5vh; background: rgba(0, 0, 0, .5);">
<ul>
<?php
$username = $_SESSION['username'];
echo "<li>$username Logged in</li>";
?>
<form name="logout" method="post" action="">
<li><input type="hidden" name="logout" value="logout"></li>
<input style="float: right;" type="submit" value="Log Out">
</form>
</div>
<div style="overflow-y: scroll; height: 90vh">
<details>
<summary>Panel</summary>
<form name="panel" method="post" action="">
<table align="center">
<tr>
<th>Panel Admins</th>
<th>Delete</th>
<th>Change Password</th>
</tr>
<?php
$admins = get_panel_admins();
foreach ($admins as $element)
$name = $element;
echo "<tr>";
echo "<td>$name</td>";
echo "<td><input type="checkbox" name="delete_admin" value="$name"></td>";
echo "<td><input type="password" name="change_admin_pass[$name]" placeholder="Password"></td>";
echo "</tr>";
?>
<tr>
<td><input type="text" name="add_admin_name" placeholder="Username"></td>
<td colspan="2"><input type="password" name="add_admin_pass" placeholder="Password"></td>
<tr>
<th colspan="3"><input type="submit" value="Update"></th>
</tr>
</table>
</form>
</details>
<details>
<summary>Config</summary>
<form name="config" method="post" action="">
<table align="center">
<tr>
<th>Setting</th>
<th>Value</th>
<th>New Value</th>
</tr>
<?php
$config = get_config();
foreach ($config as $element)
$name = $element['name'];
$value = $element['value'];
echo "<tr>";
echo "<td>$name</td>";
if (stripos($name, 'token') !== FALSE)
$value = str_repeat('*', strlen($value) - 4) . substr($value, -4);
echo "<td>$value</td>";
echo "<td><input type="text" name="config[$name]" value="$value"></td>";
else
echo "<td>$value</td>";
echo "<td><input type="text" name="config[$name]" value="$value"></td>";
echo "</tr>";
?>
<tr>
<th colspan="3"><input type="submit" value="Update"></th>
</tr>
</table>
</form>
</details>
<details>
<summary>Add</summary>
<form name="add" method="post" action="">
<h3>%n can be used to mention someone in a response</h3>
<table align="center">
<tr>
<th><input type="text" name="find" placeholder="Text to find"></th>
<th><input type="text" name="respond" placeholder="Text to respond with"></th>
<th><input type="submit" value="Add"></th>
</tr>
</table>
</form>
</details>
<details>
<summary>Delete</summary>
<form name="delete" method="post" action="">
<table align="center">
<tr>
<th>Find</th>
<th>Respond</th>
<th>Delete</th>
</tr>
<?php
$responses = get_responses();
foreach ($responses as $element)
$find = $element['find'];
$respond = $element['respond'];
echo "<tr>";
echo "<td>$find</td>";
echo "<td>$respond</td>";
echo "<td><input type="checkbox" name="delete" value="$find"></td>";
echo "</tr>";
?>
<tr>
<th colspan="3"><input type="submit" value="Remove"></th>
</tr>
</table>
</form>
</details>
<details>
<summary>Users</summary>
<form name="Users" method="post" action="">
<table align="center">
<tr>
<th>Name</th>
<th>Admin</th>
<th>Ignored</th>
</tr>
<?php
$admins = get_admins();
$ignored = get_ignored();
$users = get_users();
$i = 0;
foreach ($users as $user)
$name = htmlspecialchars($user["name"]);
$userid = htmlspecialchars($user["userid"]);
$avatar = $user["avatar"];
echo "<tr>";
echo "<td style="text-align: left;"><img src="$avatar" style="width:50px; height:50px; vertical-align: middle;">$name ($userid)</td>";
if (in_array($users[$i]['userid'], $admins))
echo "<td><input type="checkbox" name="admins" value="$userid" checked></td>";
else
echo "<td><input type="checkbox" name="admins" value="$userid"></td>";
if (in_array($users[$i]['userid'], $ignored))
echo "<td><input type="checkbox" name="ignored" value="$userid" checked></td>";
else
echo "<td><input type="checkbox" name="ignored" value="$userid"></td>";
echo "</tr>";
$i++;
?>
<tr>
<th colspan="3"><input type="submit" value="Update"></th>
</tr>
</table>
<input type="hidden" name="users" value="1">
</form>
</details>
<details>
<summary>Settings</summary>
<form name="settings" method="post" action="">
<table align="center">
<tr>
<th>Name</th>
<th>State</th>
<th>Delete</th>
</tr>
<?php
$settings = get_settings();
foreach ($settings as $element=>$key)
$name = $element;
$value = $key;
echo "<tr>";
echo "<td>$name</td>";
if ($value)
echo "<td><input type="checkbox" name="settings" value="$name" checked></td>";
else
echo "<td><input type="checkbox" name="settings" value="$name"></td>";
echo "<td><input type="checkbox" name="del_settings" value="$name"></td>";
echo "</tr>";
?>
<tr>
<td>Add setting</td>
<td colspan="2"><input type="text" name="new_setting" placeholder="Name for new setting"></td>
</tr>
<tr>
<th colspan="3"><input type="submit" value="Update"></th>
</tr>
</table>
<input type="hidden" name="settings" value="1">
</form>
</details>
<details>
<summary>Log</summary>
<table style="width: 100%;">
<?php
$log = get_log();
foreach ($log as $element)
$timestamp = date("Y-m-d@H:i:s", $element['timestamp']);
$entry = htmlspecialchars($element['entry']);
echo "<tr>";
echo "<td>$timestamp</td>";
echo "<td>$entry</td>";
echo "</tr>";
?>
</table>
</details>
</div>
<form name="send" method="post" action="">
<table style="width: 100%; position: fixed; bottom: 0; height: 5vh">
<tr>
<th><input type="text" name="send" placeholder="Message to send"></th>
</tr>
</table>
<input type="submit" value="Send" style="display: none">
</form>
<?php
else
disp_login();
if (isset($_POST['username']) && isset($_POST['password']))
$db = new PDO('sqlite:db.sqlite');
$username = strtolower($_POST['username']);
$password = $_POST['password'];
$query = $db->prepare('SELECT password FROM auth WHERE username=:username');
$query->bindValue(':username', $username, PDO::PARAM_STR);
$query->execute();
$hashed = $query->fetch(PDO::FETCH_COLUMN, 0);
if (password_verify($password, $hashed))
echo "Logging in...";
$_SESSION['username'] = $username;
header("Refresh:1");
else
echo "Incorrect password!";
else if (is_writeable('./'))
if (!empty($_POST) && initdb())
$db = new PDO('sqlite:db.sqlite');
$config = ['apitoken', 'bottoken', 'wutoken', 'wuloc'];
$settings = ['litecoin', 'bitcoin', 'ethereum'];
foreach($config as $variable)
$statement = $db->prepare('INSERT INTO config (name, value) VALUES (:name, :value)');
$statement->bindValue(':name', $variable, PDO::PARAM_STR);
$statement->bindValue(':value', $_POST[$variable], PDO::PARAM_STR);
$statement->execute();
if ($_POST['log'])
$db->exec("INSERT INTO config (name, value) VALUES ('log', '1')");
else
$db->exec("INSERT INTO config (name, value) VALUES ('log', '1')");
if ((isset($_POST['wutoken'])) && isset($_POST['wuloc']))
$db->exec("INSERT INTO settings (name, value) VALUES ('weather', '1')");
else
$db->exec("INSERT INTO settings (name, value) VALUES ('weather', '0')");
$db->exec("INSERT INTO settings (name, value) VALUES ('lights', '0')");
$db->exec("INSERT INTO responses (find, respond) VALUES ('test', 'It works!')");
add_admin($_POST['user'], $_POST['pass']);
foreach($settings as $variable)
$statement = $db->prepare('INSERT INTO settings (name, value) VALUES (:name, :value)');
$statement->bindValue(':name', $variable, PDO::PARAM_STR);
$statement->bindValue(':value', '1', PDO::PARAM_STR);
$statement->execute();
file_put_contents('.htaccess', "<Files "db.sqlite">nDeny From Alln</Files>n<Files "sess*">nDeny From Alln</Files>");
header("Refresh:1");
disp_setup();
else
echo "Working directory is not writeable, either chown it to the webserver user and group or allow write permissions to everyone (insecure!)";
?>
</body>
</html>
php sqlite
asked Feb 28 at 10:44
Desultory
335
335
add a comment |Â
add a comment |Â
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f188513%2fphp-based-groupme-bot-with-sqlite-and-control-panel%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password