This commit is contained in:
hshub
2024-04-16 22:26:30 +00:00
commit fce566a8b9
94 changed files with 52437 additions and 0 deletions

14
etc/avatar.php Normal file
View File

@@ -0,0 +1,14 @@
<?php
include "includes.php";
$id = $_GET["id"];
$avatarFile = $GLOBALS["path"]."/etc/avatars/".$id;
$domainInfo = domainForID($id);
if (@$domainInfo && @$domainInfo["avatar"] && file_exists($avatarFile)) {
$image = file_get_contents($avatarFile);
$type = mime_content_type($avatarFile);
header("Content-Type: ".$type);
die($image);
}
?>

6
etc/config.php Normal file
View File

@@ -0,0 +1,6 @@
<?php
$json = json_decode(file_get_contents(__DIR__."/config.json"), true);
foreach ($json as $key => $value) {
$GLOBALS[$key] = $value;
}
?>

12
etc/config.sample.json Normal file
View File

@@ -0,0 +1,12 @@
{
"path": "/var/www/html/hnschat",
"sqlHost": "",
"sqlUser": "",
"sqlPass": "",
"sqlDatabase": "",
"typingDelay": 2000,
"tenorKey": ""
}

59
etc/cron.php Normal file
View File

@@ -0,0 +1,59 @@
<?php
include "includes.php";
// ACTIVATE NEW CHANNELS
$getChannels = sql("SELECT * FROM `channels` WHERE `tx` IS NOT NULL AND `activated` = 0 AND `hidden` = 1");
if ($getChannels) {
foreach ($getChannels as $key => $data) {
$verify = verifyTransaction($data["tx"], $data["fee"]);
if ($verify) {
sql("UPDATE `channels` SET `activated` = 1, `hidden` = 0 WHERE `id` = ?", [$data["id"]]);
}
}
}
// FIND REGISTRY FOR SLD GATED COMMUNITIES
$getChannels = sql("SELECT * FROM `channels` WHERE `public` = 0 AND `hidden` = 0 AND `registry` IS NULL");
foreach ($getChannels as $key => $data) {
$tld = $data["name"];
$staked = isNameStaked($tld);
if ($staked) {
sql("UPDATE `channels` SET `registry` = ? WHERE `ai` = ?", [$staked, $data["ai"]]);
}
}
// FETCH AVATARS
$getUsers = sql("SELECT * FROM `domains` WHERE `claimed` = 1 AND `locked` = 0 AND `deleted` = 0 ORDER BY `ai` DESC");
foreach ($getUsers as $key => $data) {
$avatar = fetchAvatar($data["domain"]);
$avatarFile = $GLOBALS["path"]."/etc/avatars/".$data["id"];
$tld = tldForDomain($data["domain"]);
if ($tld && in_array($tld, getStakedNames())) {
if ($data["avatar"]) {
$avatar = $data["avatar"];
}
}
if ($avatar) {
$response = getContentsWithCode($avatar);
if (validImageWithoutFetch($response["data"])) {
if ($response["code"] == 200) {
sql("UPDATE `domains` SET `avatar` = ? WHERE `id` = ?", [$avatar, $data["id"]]);
$newSize = strlen($response["data"]);
if (file_exists($avatarFile)) {
$currentSize = filesize($avatarFile);
}
if (!@$currentSize || (int)$newSize !== (int)$currentSize) {
file_put_contents($avatarFile, $response["data"]);
}
}
}
}
}
?>

486
etc/functions.php Normal file
View File

@@ -0,0 +1,486 @@
<?php
function error($message) {
$output = [
"success" => false,
"message" => $message
];
die(json_encode($output));
}
function generateID($length) {
$alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
$pass = array();
$alphaLength = strlen($alphabet) - 1;
for ($i = 0; $i < $length; $i++) {
$n = rand(0, $alphaLength);
$pass[] = $alphabet[$n];
}
return implode($pass);
}
function generateNumber($length) {
$alphabet = '123456789';
$pass = array();
$alphaLength = strlen($alphabet) - 1;
for ($i = 0; $i < $length; $i++) {
$n = rand(0, $alphaLength);
$pass[] = $alphabet[$n];
}
return implode($pass);
}
function generateCode($type) {
switch ($type) {
case "session":
$db = "sessions";
$param = "id";
$length = 32;
$prefix = "V2-";
break;
case "domain":
$db = "domains";
$param = "id";
$length = 16;
break;
case "preview":
$db = "previews";
$param = "id";
$length = 16;
break;
case "upload":
$db = "uploads";
$param = "id";
$length = 32;
break;
default:
return;
}
tryAgain:
$id = generateID($length);
$checkExists = sql("SELECT * FROM `".$db."` WHERE `".$param."` = ?", [@$prefix.$id]);
if ($checkExists) {
goto tryAgain;
}
return $id;
}
function guidv4($data) {
assert(strlen($data) == 16);
$data[6] = chr(ord($data[6]) & 0x0f | 0x40); // set version to 0100
$data[8] = chr(ord($data[8]) & 0x3f | 0x80); // set bits 6-7 to 10
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}
function verifyTransaction($tx, $amount) {
$amount = preg_replace("/[^0-9]/", "", $amount);
$address = "hs1qf0cxy6ukhgjlmqfhe0tpw800t2tcul4s0szwqa";
$response = queryHSW("/wallet/hnschat-hip-2/tx/".$tx);
foreach (@$response["outputs"] as $key => $output) {
if (@$response["confirmations"] >= 2 && $output["value"] == $amount && $output["address"] = $address) {
return true;
}
}
return false;
}
function tldForDomain($domain) {
$split = explode(".", $domain);
$tld = end($split);
return $tld;
}
function domainForID($id) {
$getDomain = @sql("SELECT * FROM `domains` WHERE `id` = ?", [$id])[0];
if ($getDomain) {
return $getDomain;
}
return false;
}
function domainForName($name) {
$getDomain = @sql("SELECT * FROM `domains` WHERE `domain` = ?", [$name])[0];
if ($getDomain) {
return $getDomain;
}
return false;
}
function activeDomainForName($name) {
$getDomain = @sql("SELECT * FROM `domains` WHERE `domain` = ? AND `claimed` = 1 AND `locked` = 0 AND `deleted` = 0", [$name])[0];
if ($getDomain) {
return $getDomain;
}
return false;
}
function channelForID($id) {
$getChannel = @sql("SELECT * FROM `channels` WHERE `id` = ?", [$id])[0];
if ($getChannel) {
return $getChannel;
}
return false;
}
function getStakedNames() {
$getNames = sql("SELECT `name` FROM `channels` WHERE `slds` = 1 AND `hidden` = 0 ORDER BY `name` ASC");
$names = [];
foreach ($getNames as $key => $value) {
$names[] = $value["name"];
}
return $names;
}
function getStakedHIP2Names() {
$getNames = sql("SELECT `name` FROM `channels` WHERE `slds` = 1 AND `hip2` = 1 AND `hidden` = 0 ORDER BY `name` ASC");
$names = [];
foreach ($getNames as $key => $value) {
$names[] = $value["name"];
}
return $names;
}
function isNameStaked($tld) {
if ($tld === "eth") {
return "ens";
}
$data = [
"method" => "getnameresource",
"params" => [$tld],
];
$response = queryHSD($data);
if (@$response["records"]) {
foreach ($response["records"] as $key => $value) {
if ($value["type"] == "NS") {
if (stripos($value["ns"], ".nameserver.io.") !== false || stripos($value["ns"], ".registry.namebase.io.") !== false) {
return "namebase";
}
if ($value["ns"] == "0x06081C6B2B876EABDC41DFD3345e8Fa59588C02e._eth.") {
return "impervious";
}
}
}
}
$hshub = checkVaro($tld);
if ($hshub) {
return "varo";
}
return false;
}
function checkVaro($tld) {
$url = "https://hshub.io/tld/".$tld;
$html = getContents($url);
$canPurchase = preg_match("/<div class=\"title\">Buy \./", $html);
if ($canPurchase) {
return true;
}
return false;
}
function avatarFromTXT($content) {
if ((substr($content, 0, 7) === "avatar=" || substr($content, 0, 15) === "profile avatar=")) {
if (substr($content, 0, 15) === "profile avatar=") {
$avatar = substr($content, 15);
}
else {
$avatar = substr($content, 7);
}
}
if (!filter_var(@$avatar, FILTER_VALIDATE_URL) === false) {
return $avatar;
}
return false;
}
function fetchAvatar($domain) {
if ($domain) {
$getRecords = shell_exec("dig @127.0.0.44 +noall +answer +noidnin +noidnout ".escapeshellarg($domain)." TXT");
preg_match_all("/(?<domain>.+)\..+TXT\s\"(?<value>.+)\"/", $getRecords, $matches);
if ($matches) {
foreach ($matches["domain"] as $key => $data) {
if ($data === $domain) {
$value = $matches["value"][$key];
$avatar = avatarFromTXT($value);
if ($avatar) {
return $avatar;
}
}
}
}
if (strpos($domain, ".") == false) {
$data = [
"method" => "getnameresource",
"params" => [$domain],
];
$response = queryHSD($data);
if ($response) {
$records = @$response["records"];
if ($records) {
foreach ($records as $key => $record) {
if (@$record["txt"]) {
$content = @$record["txt"][0];
$avatar = avatarFromTXT($content);
if ($avatar) {
return $avatar;
}
}
}
}
}
}
}
return false;
}
function getContents($url) {
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_PROXY, "127.0.0.1:8080");
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, TRUE);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($curl, CURLOPT_TIMEOUT, 5);
$c = curl_exec($curl);
curl_close($curl);
return $c;
}
function getContentsWithCode($url) {
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_PROXY, "127.0.0.1:8080");
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, TRUE);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($curl, CURLOPT_TIMEOUT, 5);
$data = curl_exec($curl);
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
return [
"data" => $data,
"code" => $code
];
}
function getContentsWithSpoof($url) {
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_PROXY, "127.0.0.1:8080");
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, TRUE);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($curl, CURLOPT_TIMEOUT, 5);
curl_setopt($curl, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; Googlebot/2.1; +http://google.com/bot.html)");
$c = curl_exec($curl);
curl_close($curl);
return $c;
}
function post($url, $data) {
$ch = curl_init($url);
$payload = json_encode($data);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type:application/json'));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
function fetchMetaTags($url) {
$output = [];
if (filter_var($url, FILTER_VALIDATE_URL) !== false) {
libxml_use_internal_errors(true);
$c = getContentsWithSpoof($url);
if ($c) {
$d = new DomDocument();
$d->loadHTML($c);
$xp = new domxpath($d);
foreach ($xp->query("//title") as $el) {
$output["title"] = $el->textContent;
}
foreach ($xp->query("//meta[@property='og:title']") as $el) {
$output["title"] = $el->getAttribute("content");
}
foreach ($xp->query("//meta[@name='og:title']") as $el) {
$output["title"] = $el->getAttribute("content");
}
foreach ($xp->query("//meta[@property='description']") as $el) {
$output["description"] = $el->getAttribute("content");
}
foreach ($xp->query("//meta[@name='description']") as $el) {
$output["description"] = $el->getAttribute("content");
}
foreach ($xp->query("//meta[@property='og:description']") as $el) {
$output["description"] = $el->getAttribute("content");
}
foreach ($xp->query("//meta[@name='og:description']") as $el) {
$output["description"] = $el->getAttribute("content");
}
foreach ($xp->query("//meta[@property='og:image']") as $el) {
$image = $el->getAttribute("content");
if (validImage($image)) {
$output["image"] = $image;
}
}
foreach ($xp->query("//meta[@name='og:image']") as $el) {
$image = $el->getAttribute("content");
if (validImage($image)) {
$output["image"] = $image;
}
}
foreach ($xp->query("//meta[@property='og:video:secure_url']") as $el) {
$output["video"] = $el->getAttribute("content");
}
foreach ($xp->query("//meta[@name='og:video:secure_url']") as $el) {
$output["video"] = $el->getAttribute("content");
}
}
}
if (@$output["title"]) {
$id = generateCode("preview");
$insert = sql("INSERT INTO `previews` (id, link, title, description, image, video) VALUES (?,?,?,?,?,?)", [$id, $url, @$output["title"], @$output["description"], @$output["image"], @$output["video"]]);
if ($insert) {
$output["id"] = $id;
}
}
return $output;
}
function validImage($url) {
$string = getContents($url);
if ($string) {
$id = generateID(16);
$file = "/tmp/".$id;
$f = fopen($file, 'wb');
fputs($f, $string);
fclose($f);
$size = getimagesize($file);
unlink($file);
}
return (strtolower(substr(@$size['mime'], 0, 5)) == 'image' ? true : false);
}
function validImageWithoutFetch($string) {
if ($string) {
$id = generateID(16);
$file = "/tmp/".$id;
$f = fopen($file, 'wb');
fputs($f, $string);
fclose($f);
$size = getimagesize($file);
unlink($file);
}
return (strtolower(substr(@$size['mime'], 0, 5)) == 'image' ? true : false);
}
function validateAddress($address) {
$data = [
"method" => "validateaddress",
"params" => [$address],
];
$response = queryHSD($data);
if (@$response["isvalid"] && @$response["isspendable"]) {
return true;
}
return false;
}
function queryHSD($data) {
if (@$data["params"]) {
foreach ($data["params"] as $key => $value) {
$data["params"][$key] = trim($value);
}
}
$curl = curl_init();
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($curl, CURLOPT_HTTPHEADER, ["Content-Type:application/json"]);
curl_setopt($curl, CURLOPT_URL,"http://x:a831d3c59ce474d8e13a7cea3a3935d3d5a55b84698abe38f2eea2329327e2c50@127.0.0.1:12037");
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($curl);
curl_close ($curl);
if ($response) {
$info = @json_decode($response, true);
if (@$info["result"]) {
return $info["result"];
}
}
return false;
}
function queryHSW($endpoint) {
$endpoint = trim($endpoint);
$curl = curl_init();
curl_setopt($curl, CURLOPT_HTTPHEADER, ["Content-Type:application/json"]);
curl_setopt($curl, CURLOPT_URL,"http://x:a831d3c59ce474d8e13a7cea3a3935d3d5a55b84698abe38f2eea2329327e2c50@127.0.0.1:12039".$endpoint);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($curl);
curl_close ($curl);
if ($response) {
$info = @json_decode($response, true);
if (@$info) {
return $info;
}
}
return false;
}
?>

45
etc/head.php Normal file
View File

@@ -0,0 +1,45 @@
<?php
header("Access-Control-Allow-Origin: *");
$revision = trim(file_get_contents(".git/refs/heads/main"));
?>
<title>HNSChat</title>
<meta charset="utf-8">
<meta name="google" content="notranslate">
<meta name="darkreader" content="noplz">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="format-detection" content="telephone=no">
<meta name="title" content="HNSChat">
<meta name="description" content="HNSChat is a free end-to-end encrypted messaging platform where you chat using your Handshake names.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://hns.chat/">
<meta property="og:title" content="HNSChat">
<meta property="og:description" content="HNSChat is a free end-to-end encrypted messaging platform where you chat using your Handshake names.">
<meta property="og:image" content="https://hns.chat/assets/img/cover">
<meta property="twitter:card" content="summary_large_image">
<meta property="twitter:url" content="https://hns.chat/">
<meta property="twitter:title" content="HNSChat">
<meta property="twitter:description" content="HNSChat is a free end-to-end encrypted messaging platform where you chat using your Handshake names.">
<meta property="twitter:image" content="https://hns.chat/assets/img/cover">
<link rel="manifest" href="/manifest.json">
<link href="https://fonts.googleapis.com/css2?family=Rubik&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/assets/css/style?r=<?php echo $revision; ?>">
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script type="text/javascript" src="https://auth.varo.domains/v1"></script>
<script type="text/javascript" src="/assets/js/qr?r=<?php echo $revision; ?>"></script>
<script type="text/javascript" src="/assets/js/qr2?r=<?php echo $revision; ?>"></script>
<script type="text/javascript" src="/assets/js/he?r=<?php echo $revision; ?>"></script>
<script type="text/javascript" src="/assets/js/zwj?r=<?php echo $revision; ?>"></script>
<script type="text/javascript" src="/assets/js/date?r=<?php echo $revision; ?>"></script>
<script type="text/javascript" src="/assets/js/emojis?r=<?php echo $revision; ?>"></script>
<script type="text/javascript" src="/assets/js/anchorme?r=<?php echo $revision; ?>"></script>
<script type="text/javascript" src="/assets/js/mask?r=<?php echo $revision; ?>"></script>
<script type="text/javascript" src="/assets/js/confetti?r=<?php echo $revision; ?>"></script>
<script type="text/javascript" src="/assets/js/dish?r=<?php echo $revision; ?>"></script>
<script type="text/javascript" src="/assets/js/janus?r=<?php echo $revision; ?>"></script>
<script type="text/javascript" src="/assets/js/adapter?r=<?php echo $revision; ?>"></script>
<script type="module" src="/assets/js/script?r=<?php echo $revision; ?>"></script>
<script type="text/javascript">
var revision = "<?php echo $revision; ?>";
</script>

5
etc/includes.php Normal file
View File

@@ -0,0 +1,5 @@
<?php
include "config.php";
include "sql.php";
include "functions.php";
?>

385
etc/page/chat.php Normal file
View File

@@ -0,0 +1,385 @@
<body data-page="chat" data-version="<?php echo $revision; ?>">
<div class="connecting">
<div class="lds-facebook"><div></div><div></div><div></div></div>
</div>
<div id="blackout"></div>
<div class="popover" data-name="update">
<div class="head">
<div class="title">Update</div>
<div class="icon action close" data-action="close"></div>
</div>
<div class="body">
<div class="subtitle">An update is available. Please reload for the best possible experience.</div>
<div class="button" data-action="reload">Reload</div>
</div>
<div class="response error"></div>
</div>
<div class="popover" data-name="newConversation">
<div class="head">
<div class="title">New Conversation</div>
<div class="icon action close" data-action="close"></div>
</div>
<div class="body">
<input class="tab" type="text" name="domain" placeholder="hnschat/">
<input type="text" name="message" placeholder="Message">
<div class="button" data-action="startConversation">Start Conversation</div>
</div>
<div class="response error"></div>
</div>
<div class="popover" data-name="syncSession">
<div class="head">
<div class="title">Sync Session</div>
<div class="icon action close" data-action="close"></div>
</div>
<div class="body">
<div class="subtitle">Use this QR code or link to sync your session to another browser.</div>
<div id="qrcode"></div>
<div class="group">
<input readonly="readonly" class="copyable" type="text" name="syncLink">
<div class="icon action clipboard" data-action="clipboard"></div>
</div>
</div>
</div>
<div class="popover" data-name="donate">
<div class="head">
<div class="title">Donate</div>
<div class="icon action close" data-action="close"></div>
</div>
<div class="body">
<div class="subtitle">If you enjoy using this free service, please consider donating.</div>
<div class="group">
<input readonly="readonly" class="copyable" type="text" name="donateAddress" value="hs1qf0cxy6ukhgjlmqfhe0tpw800t2tcul4s0szwqa">
<div class="icon action clipboard" data-action="clipboard"></div>
</div>
<div class="center">&copy; <?php echo date("Y"); ?>&nbsp;<a href="https://eskimo.software" target="_blank">Eskimo Software</a></div>
</div>
</div>
<div class="popover" data-name="pay">
<div class="head">
<div class="title">Send HNS</div>
<div class="icon action close" data-action="close"></div>
</div>
<div class="body">
<div class="loading flex shown">
<div class="lds-facebook"><div></div><div></div><div></div></div>
</div>
<div class="content">
<input type="hidden" name="address">
<input type="text" name="hns" placeholder="0 HNS">
<div class="button" data-action="sendPayment">Send with Bob Extension</div>
</div>
<div class="response error"></div>
</div>
</div>
<div class="popover" data-name="settings">
<div class="head">
<div class="title">Settings</div>
<div class="icon action close" data-action="close"></div>
</div>
<div class="body">
<div class="setting">
<div class="subtitle">Avatar URL</div>
<input class="remote tab" type="text" name="avatar" placeholder="">
</div>
<div class="setting">
<div class="subtitle">HNS Wallet Address</div>
<input class="remote tab" type="text" name="address" placeholder="">
</div>
<div class="setting">
<div class="subtitle">Chat Bubble Color</div>
<input class="local color tab" type="color" name="bubbleBackground">
</div>
<div class="setting">
<div class="subtitle">Self Chat Bubble Color</div>
<input class="local color tab" type="color" name="bubbleSelfBackground">
</div>
<div class="setting">
<div class="subtitle">Mention Chat Bubble Color</div>
<input class="local color" type="color" name="bubbleMentionBackground">
</div>
<div class="setting">
<div class="subtitle">Chat Display Mode</div>
<select class="local" name="chatDisplayMode">
<option value="normal">Normal</option>
<option value="compact">Compact</option>
</select>
</div>
<div class="setting">
<div class="subtitle">Sync Session</div>
<div class="center action link" data-action="syncSession">Show QR + Link</div>
</div>
<div class="button" data-action="saveSettings">Save</div>
</div>
<div class="response error"></div>
</div>
<div class="popover contextMenu" data-name="userContext">
<div class="actions">
<div class="action icon edit" data-action="editProfile"></div>
<div class="action icon save" data-action="saveProfile"></div>
<div class="action icon close" data-action="undoProfile"></div>
</div>
<div class="body">
<ul>
<li>
<div class="pic"></div>
<span class="user subtitle"></span>
<div class="icon type"></div>
</li>
<li class="bio">
<div class="title small">Bio</div>
<div class="bioHolder">
<div class="bio subtitle"></div>
<div class="limit"></div>
</div>
</li>
<li>
<div class="title small">Joined</div>
<span class="joined subtitle"></span>
</li>
</ul>
<div class="separator"></div>
<ul class="contextActions">
<li class="action" data-action="newConversationWith">
<div class="icon message"></div>
<span>Message</span>
</li>
<li class="action" data-action="mentionUser">
<div class="icon mention"></div>
<span>Mention</span>
</li>
<li class="action" data-action="slapUser">
<div class="icon fish"></div>
<span>Slap</span>
</li>
<li class="action speaker" data-action="inviteVideo">
<div class="icon voice"></div>
<span>Speaker</span>
</li>
</ul>
</div>
</div>
<div class="popover contextMenu" data-name="channelContext">
<div class="body">
<ul>
<li>
<span class="channel subtitle"></span>
</li>
</ul>
<div class="separator"></div>
<ul>
<li class="action" data-action="switchConversation">
<div class="icon view"></div>
<span>View</span>
</li>
</ul>
</div>
</div>
<div class="popover contextMenu" data-name="messageContext">
<div class="body">
<ul>
<li class="action reply" data-action="reply">
<div class="icon reply"></div>
<span>Reply</span>
</li>
<li class="action emoji" data-action="emojis">
<div class="icon emoji"></div>
<span>React</span>
</li>
<li class="action pin" data-action="pinMessage">
<div class="icon pin"></div>
<span>Pin</span>
</li>
<li class="action delete error" data-action="deleteMessage">
<div class="icon delete"></div>
<span>Delete</span>
</li>
</ul>
</div>
</div>
<div id="holder">
<div class="header">
<div class="left">
<div class="icon menu"></div>
</div>
<div class="center">
<div class="logo">
<img draggable="false" src="/assets/img/handshake">
</div>
<div class="messageHeader">
<table></table>
<div class="pinnedMessage flex">
<div class="icon pin"></div>
<div class="message"></div>
<div class="action icon delete" data-action="pinMessage"></div>
</div>
</div>
<div class="end">
<div id="me"></div>
<div class="domains">
<select></select>
</div>
</div>
</div>
<div class="right">
<div class="icon users"></div>
</div>
</div>
<div id="chats">
<div id="conversations" class="sidebar">
<div class="title">
<div class="tabs">
<div class="tab" data-tab="channels">Channels</div>
<div class="tab" data-tab="pms">Private</div>
</div>
<div class="actionHolder">
<div class="action icon compose" data-action="newConversation"></div>
</div>
</div>
<div class="sections">
<div class="section channels">
<table></table>
</div>
<div class="section pms">
<table></table>
</div>
</div>
<div class="footer">
<div class="action link" data-action="settings">Settings</div>
<div class="action link" data-action="docs">Docs</div>
<div class="action link" data-action="donate">Donate</div>
</div>
</div>
<div class="content">
<div class="pinnedMessage flex">
<div class="icon pin"></div>
<div class="message"></div>
<div class="action icon delete" data-action="pinMessage"></div>
</div>
<div id="closeMenu"></div>
<div id="videoInfo" class="flex">
<div class="info">
<div class="users"></div>
<div class="title flex">
<span>LIVE</span>
<div class="icon audio"></div>
</div>
<div class="watching flex">
<div class="watchers"></div>
</div>
</div>
<div class="actions">
<div class="link" data-action="viewVideo">Watch</div>
<div class="link" data-action="startVideo">Stream</div>
<div class="link" data-action="joinVideo">Join</div>
<div class="link destructive" data-action="leaveVideo">Leave</div>
<div class="link destructive" data-action="endVideo">End</div>
</div>
</div>
<div id="videoContainer" class="flex">
<div class="controls">
<div class="button outline muted" data-action="toggleScreen">
<div class="icon screen"></div>
</div>
<div class="button outline muted" data-action="toggleAudio">
<div class="icon voice"></div>
</div>
<div class="button outline muted" data-action="toggleVideo">
<div class="icon video"></div>
</div>
<div class="button outline muted" data-action="leaveVideo">
<div class="icon leave"></div>
</div>
</div>
</div>
<div id="messageHolder">
<div class="popover" id="completions" data-name="completions">
<div class="head">
<div class="title"></div>
<div class="icon action close" data-action="close"></div>
</div>
<div class="body">
<table class="list"></table>
</div>
</div>
<div class="popover" id="react" data-name="react">
<div class="head">
<div class="title">
<div class="tabs">
<div class="tab" data-name="gifs">Gifs</div>
<div class="tab" data-name="emojis">Emojis</div>
</div>
</div>
<div class="icon action close" data-action="close"></div>
</div>
<div class="body">
<div class="search">
<input type="text" name="searchGifs" placeholder="Search Tenor">
<input type="text" class="shown" name="searchEmojis" placeholder="Search Emojis">
</div>
<div class="grids">
<div class="grid" data-type="gifs">
<div class="section" data-type="categories"></div>
<div class="section flex" data-type="gifs">
<div class="column" data-column="0"></div>
<div class="column" data-column="1"></div>
</div>
</div>
<div class="grid shown" data-type="emojis"></div>
</div>
</div>
</div>
<div id="messages"></div>
<div id="jumpToPresent" class="hidden">
<div class="action" data-action="jumpToPresent">Jump To Present</div>
</div>
<div class="loading flex">
<div class="lds-facebook"><div></div><div></div><div></div></div>
</div>
</div>
<div class="inputContainer">
<div id="typing" class="flex">
<div class="message"></div>
</div>
<div id="replying" class="flex">
<div class="message">Replying to <span class="name"></span></div>
<div class="action icon remove" data-action="removeReply"></div>
</div>
<div id="attachments" class="flex"></div>
<div class="inputHolder">
<div class="input">
<div class="action icon plus" data-action="file">
<input id="file" type="file" name="file">
</div>
<div class="action icon pay" data-action="pay"></div>
<div class="inputs">
<textarea id="message" placeholder="Message"></textarea>
</div>
<div class="action icon gif big" data-action="gifs"></div>
<div class="action icon emoji big" data-action="emojis"></div>
</div>
<div class="locked"></div>
</div>
</div>
</div>
<div id="users" class="sidebar">
<div class="title">
<div class="group normal">
<div class="action icon search" data-action="searchUsers"></div>
<div>Users</div>
</div>
<div class="group flex searching">
<input type="text" name="search">
<div class="action icon close" data-action="searchUsers"></div>
</div>
<div id="count"></div>
</div>
<div class="sections">
<div class="section users">
<table></table>
</div>
</div>
</div>
</div>
</div>
<div id="avatars"></div>
</body>

26
etc/preview.php Normal file
View File

@@ -0,0 +1,26 @@
<?php
include "includes.php";
$id = $_GET["id"];
$previewFile = $GLOBALS["path"]."/etc/previews/".$id;
$getImage = @sql("SELECT `image` FROM `previews` WHERE `id` = ? AND `image` IS NOT NULL", [$id])[0];
if ($getImage) {
if (file_exists($previewFile)) {
$image = file_get_contents($previewFile);
$type = mime_content_type($previewFile);
}
else {
$getImage["image"] = html_entity_decode(html_entity_decode($getImage["image"]));
$image = getContents($getImage["image"]);
if (validImageWithoutFetch($image)) {
file_put_contents($previewFile, $image);
$type = mime_content_type($previewFile);
}
}
header("Content-Type: ".$type);
die($image);
}
?>

68
etc/sql.php Normal file
View File

@@ -0,0 +1,68 @@
<?php
$GLOBALS["sqlInfo"] = [
"host" => $GLOBALS["sqlHost"],
"user" => $GLOBALS["sqlUser"],
"pass" => $GLOBALS["sqlPass"],
"db" => $GLOBALS["sqlDatabase"],
"options" => [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]
];
$GLOBALS["sqlDSN"] = "mysql:host=".$GLOBALS["sqlInfo"]["host"].";dbname=".$GLOBALS["sqlInfo"]["db"].";charset=utf8mb4";
function initSQL() {
retry:
try {
$GLOBALS["remoteSQL"] = new PDO($GLOBALS["sqlDSN"], $GLOBALS["sqlInfo"]["user"], $GLOBALS["sqlInfo"]["pass"], $GLOBALS["sqlInfo"]["options"]);
}
catch (\PDOException $e) {
$message = $e->getMessage();
if (strpos($message, "Connection refused") !== false) {
goto retry;
}
}
}
function sql($query, $values = []) {
if (!@$GLOBALS["remoteSQL"]) {
initSQL();
}
retry:
try {
$statement = $GLOBALS["remoteSQL"]->prepare($query);
$success = $statement->execute($values);
$result = $statement->fetchAll();
if (count($result) > 1) {
return $result;
}
else if (count($result) == 1) {
return [$result[0]];
}
else if ($success && (substr($query, 0, 12) === "INSERT INTO " || substr($query, 0, 12) === "DELETE FROM " || (substr($query, 0, 7) === "UPDATE "))) {
return true;
}
return false;
}
catch (\PDOException $e) {
$message = $e->getMessage();
if (strpos($message, "MySQL server has gone away") !== false || strpos($message, "Communication link failure") !== false) {
initSQL();
goto retry;
}
else {
//var_dump($message);
//error
}
}
}
?>