Initial import.
This commit is contained in:
commit
1839991457
19 changed files with 3033 additions and 0 deletions
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
* text=auto
|
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[Tt]humbs.db
|
||||||
|
[Dd]esktop.ini
|
||||||
|
.DS_Store
|
||||||
|
/.idea
|
||||||
|
/vendor
|
||||||
|
/config.php
|
||||||
|
/composer.phar
|
5
composer.json
Normal file
5
composer.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"require": {
|
||||||
|
"cboden/ratchet": "0.3.*"
|
||||||
|
}
|
||||||
|
}
|
1062
composer.lock
generated
Normal file
1062
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
45
daemon
Normal file
45
daemon
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
function spawn () {
|
||||||
|
nohup php server.php > daemon.out 2>&1 &
|
||||||
|
PID=$!
|
||||||
|
echo $PID > daemon.pid
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -z $1 ]
|
||||||
|
then
|
||||||
|
if [ ! -e daemon.pid ]
|
||||||
|
then
|
||||||
|
nohup bash $0 start > /dev/null 2>&1 &
|
||||||
|
echo "Sock Chat Daemon started."
|
||||||
|
else
|
||||||
|
echo "ERROR: Sock Chat Daemon is already running!"
|
||||||
|
echo "To stop the daemon, use daemon stop"
|
||||||
|
fi
|
||||||
|
elif [ $1 = "start" ]
|
||||||
|
then
|
||||||
|
if [ ! -e daemon.pid ]
|
||||||
|
then
|
||||||
|
spawn
|
||||||
|
|
||||||
|
while [ -e daemon.pid ]
|
||||||
|
do
|
||||||
|
if ! ps -p $PID > /dev/null
|
||||||
|
then
|
||||||
|
echo "Server died unexpectedly! Restarting..."
|
||||||
|
spawn
|
||||||
|
fi
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
|
||||||
|
/bin/kill -s SIGKILL $PID
|
||||||
|
fi
|
||||||
|
elif [ $1 = "stop" ]
|
||||||
|
then
|
||||||
|
rm daemon.pid
|
||||||
|
echo "Sock Chat Daemon stopped."
|
||||||
|
else
|
||||||
|
echo "SOCK CHAT SERVER DAEMON"
|
||||||
|
echo
|
||||||
|
echo "USAGE: daemon [stop]"
|
||||||
|
fi
|
10
daemon.out
Normal file
10
daemon.out
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
Could not connect to the database! Details: SQLSTATE[HY000] [1045] Access denied for user 'aroltd'@'localhost' (using password: YES)
|
||||||
|
PHP Fatal error: Uncaught PDOException: SQLSTATE[HY000] [1045] Access denied for user 'aroltd'@'localhost' (using password: YES) in /home/flash/nabucco/lib/db.php:121
|
||||||
|
Stack trace:
|
||||||
|
#0 /home/flash/nabucco/lib/db.php(121): PDO->__construct('mysql:host=loca...', 'aroltd', 'buddyman5', Array)
|
||||||
|
#1 /home/flash/nabucco/lib/db.php(106): sockchat\Database::SpawnConnection()
|
||||||
|
#2 /home/flash/nabucco/lib/db.php(207): sockchat\Database::Execute('clrusers')
|
||||||
|
#3 /home/flash/nabucco/server.php(32): sockchat\Database::TruncateUserList()
|
||||||
|
#4 /home/flash/nabucco/server.php(149): sockchat\Chat->__construct()
|
||||||
|
#5 {main}
|
||||||
|
thrown in /home/flash/nabucco/lib/db.php on line 121
|
65
lib/channel.php
Normal file
65
lib/channel.php
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
<?php
|
||||||
|
namespace sockchat;
|
||||||
|
use \sockchat\User;
|
||||||
|
|
||||||
|
class Backlog {
|
||||||
|
public static $loglen = 10;
|
||||||
|
public $logs = array();
|
||||||
|
|
||||||
|
public function Log($user, $msg, $msgid, $time = null, $flags = "10010") {
|
||||||
|
array_push($this->logs, [$time == null ? gmdate("U") : $time, clone $user, $msg, $msgid, $flags]);
|
||||||
|
if(count($this->logs) > Backlog::$loglen)
|
||||||
|
$this->logs = array_slice($this->logs, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetAllLogStrings() {
|
||||||
|
$retval = array();
|
||||||
|
foreach($this->logs as $msg)
|
||||||
|
array_push($retval, join(Utils::$separator, array($msg[0], $msg[1], $msg[2], $msg[3], "0", $msg[4])));
|
||||||
|
//$retval = array_reverse($retval);
|
||||||
|
return $retval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Channel {
|
||||||
|
public $name;
|
||||||
|
public $permissionLevel = 0;
|
||||||
|
|
||||||
|
public $password = "";
|
||||||
|
public $users = [];
|
||||||
|
public $channelMods = []; // id list
|
||||||
|
|
||||||
|
public $channelOwner = null;
|
||||||
|
public $channelType = CHANNEL_PERM;
|
||||||
|
|
||||||
|
public $log;
|
||||||
|
|
||||||
|
public function __construct($name, $password = "", $permissionLevel = 0, $channelOwner = null, $channelType = CHANNEL_PERM, $backlog = null) {
|
||||||
|
$this->name = $name;
|
||||||
|
$this->permissionLevel = $permissionLevel;
|
||||||
|
$this->password = $password;
|
||||||
|
$this->channelOwner = $channelOwner;
|
||||||
|
$this->channelType = $channelType;
|
||||||
|
$this->log = $backlog == null ? new Backlog() : $backlog;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetOwner() {
|
||||||
|
if($this->channelOwner != null) return $this->channelOwner;
|
||||||
|
else return new User(-1, "", "", "", "", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetAllUsers() {
|
||||||
|
$arr = [];
|
||||||
|
|
||||||
|
foreach(Context::$invisibleUsers as $user)
|
||||||
|
array_push($arr, $user . Utils::$separator . "0");
|
||||||
|
foreach($this->users as $user)
|
||||||
|
array_push($arr, $user . Utils::$separator . "1");
|
||||||
|
|
||||||
|
return (count($this->users) + count(Context::$invisibleUsers)) . Utils::$separator . join(Utils::$separator, $arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString() {
|
||||||
|
return join(Utils::$separator, [$this->name, $this->password != "" ? "1" : "0", $this->channelType == CHANNEL_TEMP ? "1" : "0"]);
|
||||||
|
}
|
||||||
|
}
|
39
lib/constants.php
Normal file
39
lib/constants.php
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
define("AUTH_CUSTOM", 0);
|
||||||
|
define("AUTH_PHPBB", 1);
|
||||||
|
|
||||||
|
$auth_method = array("", "phpbb.php");
|
||||||
|
|
||||||
|
define("MSG_NORMAL", 0);
|
||||||
|
define("MSG_ERROR", 1);
|
||||||
|
|
||||||
|
define("CHANNEL_TEMP", 0);
|
||||||
|
define("CHANNEL_PERM", 1);
|
||||||
|
|
||||||
|
define("ALL_CHANNELS", "@all");
|
||||||
|
define("LOCAL_CHANNEL", "@local");
|
||||||
|
define("DEFAULT_CHANNEL", "@default");
|
||||||
|
|
||||||
|
define("CLEAR_MSGS", "0");
|
||||||
|
define("CLEAR_USERS", "1");
|
||||||
|
define("CLEAR_CHANNELS", "2");
|
||||||
|
define("CLEAR_MSGNUSERS", "3");
|
||||||
|
define("CLEAR_ALL", "4");
|
||||||
|
|
||||||
|
define("CTX_USER", "0");
|
||||||
|
define("CTX_MSG", "1");
|
||||||
|
define("CTX_CHANNEL", "2");
|
||||||
|
|
||||||
|
define("P_USER_JOIN", 1);
|
||||||
|
define("P_SEND_MESSAGE", 2);
|
||||||
|
define("P_USER_LEAVE", 3);
|
||||||
|
define("P_CHANNEL_INFO", 4);
|
||||||
|
define("P_CHANGE_CHANNEL", 5);
|
||||||
|
define("P_MSG_DEL", 6);
|
||||||
|
define("P_CTX_DATA", 7);
|
||||||
|
define("P_CTX_CLR", 8);
|
||||||
|
define("P_BAKA", 9);
|
||||||
|
define("P_USER_CHANGE", 10);
|
||||||
|
|
||||||
|
define("LEAVE_NORMAL", "leave");
|
||||||
|
define("LEAVE_KICK", "kick");
|
291
lib/context.php
Normal file
291
lib/context.php
Normal file
|
@ -0,0 +1,291 @@
|
||||||
|
<?php
|
||||||
|
namespace sockchat;
|
||||||
|
use \sockchat\User;
|
||||||
|
use \sockchat\Channel;
|
||||||
|
|
||||||
|
class Ban {
|
||||||
|
public $id = null;
|
||||||
|
public $ip = null;
|
||||||
|
public $username = null;
|
||||||
|
public $expire;
|
||||||
|
|
||||||
|
public function __construct($ip, $id, $username, $expire) {
|
||||||
|
$this->id = Utils::$chat["AUTOID"] ? null : $id;
|
||||||
|
$this->ip = $ip;
|
||||||
|
$this->username = $username;
|
||||||
|
$this->expire = $expire;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Check($id, $ip, $username) {
|
||||||
|
if($GLOBALS["chat"]["AUTOID"]) $id = null;
|
||||||
|
return (($this->id == null ? false : $id == $this->id) ||
|
||||||
|
($this->ip == null ? false : Utils::CheckIPAddresses($ip, $this->ip)) ||
|
||||||
|
($this->username == null ? false : $username == $this->username)) &&
|
||||||
|
($this->expire > time() || $this->expire == -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Context {
|
||||||
|
public static $onlineUsers = [];
|
||||||
|
public static $channelList = [];
|
||||||
|
public static $bannedUsers = [];
|
||||||
|
public static $invisibleUsers = [];
|
||||||
|
|
||||||
|
public static function ForceChannelSwitch($user, $to) {
|
||||||
|
if(Context::ChannelExists($to)) {
|
||||||
|
$oldchan = $user->channel;
|
||||||
|
|
||||||
|
if(!Modules::ExecuteRoutine("OnChannelDelete", [$user, Context::GetChannel($to), Context::GetChannel($oldchan)])) return;
|
||||||
|
Message::HandleChannelSwitch($user, $to, $user->channel);
|
||||||
|
unset(Context::GetChannel($user->channel)->users[$user->id]);
|
||||||
|
Context::GetChannel($to)->users[$user->id] = Context::$onlineUsers[$user->id];
|
||||||
|
Context::$onlineUsers[$user->id]->channel = $to;
|
||||||
|
|
||||||
|
if(Context::GetChannel($oldchan)->channelType == CHANNEL_TEMP && Context::GetChannel($oldchan)->GetOwner()->id == $user->id)
|
||||||
|
Context::DeleteChannel($oldchan);
|
||||||
|
|
||||||
|
Modules::ExecuteRoutine("AfterChannelSwitch", [$user, Context::GetChannel($to), Context::GetChannel($oldchan)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function SwitchChannel($user, $to, $pwd = "") {
|
||||||
|
if($user->channel != $to) {
|
||||||
|
if(Context::ChannelExists($to)) {
|
||||||
|
if($pwd == Context::GetChannel($to)->password || $user->canModerate() || Context::GetChannel($to)->GetOwner()->id == $user->id) {
|
||||||
|
if(Context::GetChannel($to)->permissionLevel <= $user->getRank()) {
|
||||||
|
Context::ForceChannelSwitch($user, $to);
|
||||||
|
return;
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "ipchan", array($to), $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "ipwchan", array($to), $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "nochan", array($to), $user);
|
||||||
|
} // else Message::PrivateBotMessage(MSG_ERROR, "samechan", array($to), $user); // kind of extraneous
|
||||||
|
$user->sock->send(Utils::PackMessage(5, ["2", $user->channel]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function IsLobby($channel) {
|
||||||
|
if(is_string($channel)) $channel = Context::GetChannel($channel);
|
||||||
|
return $channel->name == Context::GetChannel(Utils::$chat["DEFAULT_CHANNEL"])->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function AddInvisibleUser($name, $color) {
|
||||||
|
for($id = -2;;$id--) {
|
||||||
|
if(!array_key_exists($id, Context::$onlineUsers) && !array_key_exists($id, Context::$invisibleUsers)) break;
|
||||||
|
}
|
||||||
|
Context::$invisibleUsers[$id] = new User($id, "", $name, $color, "6770\f1\f1\f1\f1\f1", null, false);
|
||||||
|
foreach(Context::$onlineUsers as $user) {
|
||||||
|
$user->sock->send(Utils::PackMessage(7, ["1", $id, $name, $color, "6770\f1\f1\f1\f1\f1", ]));
|
||||||
|
}
|
||||||
|
return Context::$invisibleUsers[$id];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function GetUserByID($id) {
|
||||||
|
if(array_key_exists($id, Context::$onlineUsers)) return Context::$onlineUsers[$id];
|
||||||
|
//return Context::$onlineUsers[array_rand(Context::$onlineUsers)];
|
||||||
|
else return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function GetUserByName($name) {
|
||||||
|
foreach(Context::$onlineUsers as $user) {
|
||||||
|
if ($user->username == $name) return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
//return GetUserByID(0);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function GetUserBySock($sock) {
|
||||||
|
foreach(Context::$onlineUsers as $user) {
|
||||||
|
if($user->sock == $sock) return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function GetAllChannels() {
|
||||||
|
return join(Utils::$separator, Context::$channelList);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function GetChannel($name) {
|
||||||
|
if(array_key_exists($name, Context::$channelList)) return Context::$channelList[$name];
|
||||||
|
else if($name == "@default") return Context::$channelList[Utils::$chat["DEFAULT_CHANNEL"]];
|
||||||
|
else return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function ChannelExists($name) {
|
||||||
|
return array_key_exists($name, Context::$channelList);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function CreateChannel($channel) {
|
||||||
|
if(is_string($channel)) $channel = new Channel($channel);
|
||||||
|
|
||||||
|
if(!Context::ChannelExists($channel->name)) {
|
||||||
|
if($channel->name[0] != "@" && $channel->name[0] != "*") {
|
||||||
|
if(!Modules::ExecuteRoutine("OnChannelCreate", [$channel])) return Utils::FormatBotMessage(MSG_ERROR, "generr", []);
|
||||||
|
Context::$channelList[$channel->name] = $channel;
|
||||||
|
Message::HandleChannelCreation($channel);
|
||||||
|
Database::CreateChannel($channel->name, $channel->password, $channel->permissionLevel);
|
||||||
|
Modules::ExecuteRoutine("AfterChannelCreate", [$channel]);
|
||||||
|
return "OK";
|
||||||
|
} else return Utils::FormatBotMessage(MSG_ERROR, "inchan", []);
|
||||||
|
} else return Utils::FormatBotMessage(MSG_ERROR, "nischan", [$channel->name]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function RenameChannel($oldname, $newname) {
|
||||||
|
if(Context::ChannelExists($oldname) && !Context::ChannelExists($newname)) {
|
||||||
|
Context::$channelList[$newname] = clone Context::GetChannel($oldname);
|
||||||
|
Context::$channelList[$newname]->name = $newname;
|
||||||
|
if(!Modules::ExecuteRoutine("OnChannelModify", [Context::$channelList[$oldname], Context::$channelList[$newname]])) {
|
||||||
|
unset(Context::$channelList[$newname]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Message::HandleChannelModification($newname, $oldname);
|
||||||
|
Modules::ExecuteRoutine("AfterChannelModify", [Context::$channelList[$oldname], Context::$channelList[$newname]]);
|
||||||
|
unset(Context::$channelList[$oldname]);
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function ChangeChannelPassword($channel, $pwd) {
|
||||||
|
if(is_string($channel)) $channel = Context::GetChannel($channel);
|
||||||
|
$tmp = [clone $channel, clone $channel];
|
||||||
|
$tmp[0]->password = trim($pwd) == "" ? "" : Utils::Hash(trim($pwd));
|
||||||
|
if(!Modules::ExecuteRoutine("OnChannelModify", [$channel, $tmp[0]])) return;
|
||||||
|
Context::$channelList[$channel->name] = $tmp[0];
|
||||||
|
Message::HandleChannelModification(Context::$channelList[$channel->name]);
|
||||||
|
Modules::ExecuteRoutine("AfterChannelModify", [$tmp[1], $channel]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function ChangeChannelPermission($channel, $perm) {
|
||||||
|
if(is_string($channel)) $channel = Context::GetChannel($channel);
|
||||||
|
$tmp = [clone $channel, clone $channel];
|
||||||
|
$tmp[0]->permissionLevel = $perm;
|
||||||
|
if(!Modules::ExecuteRoutine("OnChannelModify", [$channel, $tmp[0]])) return;
|
||||||
|
Context::$channelList[$channel->name] = $tmp[0];
|
||||||
|
Message::HandleChannelModification(Context::$channelList[$channel->name]);
|
||||||
|
Modules::ExecuteRoutine("AfterChannelModify", [$tmp[1], $channel]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function DeleteChannel($channel) {
|
||||||
|
if(is_string($channel)) $channel = Context::GetChannel($channel);
|
||||||
|
if(!Modules::ExecuteRoutine("OnChannelDelete", [$channel]));
|
||||||
|
foreach($channel->users as $user) Context::SwitchChannel($user, Utils::$chat["DEFAULT_CHANNEL"]);
|
||||||
|
Message::HandleChannelDeletion($channel);
|
||||||
|
Database::RemoveChannel($channel->name);
|
||||||
|
unset(Context::$channelList[$channel->name]);
|
||||||
|
Modules::ExecuteRoutine("AfterChannelDelete", [$channel]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Join($user) {
|
||||||
|
if(!Modules::ExecuteRoutine("OnUserJoin", [$user])) return;
|
||||||
|
Message::HandleJoin($user);
|
||||||
|
Context::$onlineUsers[$user->id] = $user;
|
||||||
|
Context::$channelList[Utils::$chat["DEFAULT_CHANNEL"]]->users[$user->id] = Context::$onlineUsers[$user->id];
|
||||||
|
Database::Login($user);
|
||||||
|
Modules::ExecuteRoutine("AfterUserJoin", [$user]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function AllowUser($username, $sock) {
|
||||||
|
foreach(Context::$onlineUsers as $user) {
|
||||||
|
if($user->GetOriginalUsername() != $username) {
|
||||||
|
if($sock == $user->sock) {
|
||||||
|
return "sockfail";
|
||||||
|
}
|
||||||
|
} else return "userfail";
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function CheckBan($id, $ip, $name) {
|
||||||
|
foreach(Context::$bannedUsers as $ban) {
|
||||||
|
if($ban->Check($id, $ip, $name)) return $ban->expire;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Unban($id, $ip, $name, $by = null) {
|
||||||
|
if($by == null) $by = Message::$bot;
|
||||||
|
if(!Modules::ExecuteRoutine("OnUnban", [&$id, &$ip, &$name, $by])) return false;
|
||||||
|
$banned = false;
|
||||||
|
foreach(Context::$bannedUsers as $bid => $ban) {
|
||||||
|
if($ban->Check($id, $ip, $name)) {
|
||||||
|
unset(Context::$bannedUsers[$bid]);
|
||||||
|
$banned = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if($ip != null) $ip = str_replace("*", "%", $ip);
|
||||||
|
Database::Unban($ip, $id, $name);
|
||||||
|
Modules::ExecuteRoutine("AfterUnban", [$id, $ip, $name, $by]);
|
||||||
|
return $banned;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function ModifyUser($newuser) {
|
||||||
|
$u = Context::GetUserByID($newuser->id);
|
||||||
|
$u->Copy($newuser);
|
||||||
|
if(!Modules::ExecuteRoutine("OnUserModify", [$u])) return;
|
||||||
|
Message::HandleUserModification($u);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function KickUser($user, $by = null, $time = 0, $banip = false, $type = LEAVE_KICK) {
|
||||||
|
if(!Modules::ExecuteRoutine("OnUserKick", [$user, $by == null ? Message::$bot : $by, &$time, &$banip])) return;
|
||||||
|
Message::HandleKick($user, $time);
|
||||||
|
if($time != 0) {
|
||||||
|
$exp = $time < 0 ? -1 : (int)gmdate("U") + $time;
|
||||||
|
Database::Ban($banip ? $user->sock->remoteAddress : null , $user->id, $user->GetOriginalUsername(), $exp);
|
||||||
|
array_push(Context::$bannedUsers, new Ban($banip ? $user->sock->remoteAddress : null , $user->id, $user->GetOriginalUsername(), $exp));
|
||||||
|
}
|
||||||
|
$ip = $user->sock->remoteAddress;
|
||||||
|
$user->sock->close();
|
||||||
|
Context::Leave($user, $type);
|
||||||
|
if($banip) Context::BanIP($ip, $time, $by, true);
|
||||||
|
Modules::ExecuteRoutine("AfterUserKick", [$user, $by == null ? Message::$bot : $by, $time, $banip]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function BanIP($ip, $time = -1, $by = null, $alreadybanned = false) {
|
||||||
|
if(!$alreadybanned) {
|
||||||
|
if(!Modules::ExecuteRoutine("OnBanIP", [&$ip, &$time, $by == null ? Message::$bot : $by])) return;
|
||||||
|
$exp = $time < 0 ? -1 : (int)gmdate("U") + $time;
|
||||||
|
array_push(Context::$bannedUsers, new Ban($ip, null, null, $exp));
|
||||||
|
Database::Ban($ip, null, null, $exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(Context::$onlineUsers as $user) {
|
||||||
|
if(Utils::CheckIPAddresses($user->sock->remoteAddress, $ip)) {
|
||||||
|
Message::HandleKick($user, $time);
|
||||||
|
$user->sock->close();
|
||||||
|
Context::Leave($user, LEAVE_KICK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$alreadybanned)
|
||||||
|
Modules::ExecuteRoutine("AfterBanIP", [$ip, $time, $by == null ? Message::$bot : $by]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function CheckPings() {
|
||||||
|
foreach(Context::$onlineUsers as $user) {
|
||||||
|
if(gmdate("U") - $user->ping > Utils::$chat["MAX_IDLE_TIME"]) {
|
||||||
|
$user->sock->close();
|
||||||
|
Context::Leave($user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function DoesSockExist($sock) {
|
||||||
|
foreach(Context::$onlineUsers as $u) {
|
||||||
|
if($u->sock == $sock) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Leave($user, $type = LEAVE_NORMAL) {
|
||||||
|
if(Context::GetChannel($user->channel)->channelType == CHANNEL_TEMP && Context::GetChannel($user->channel)->GetOwner()->id == $user->id)
|
||||||
|
Context::DeleteChannel($user->channel);
|
||||||
|
|
||||||
|
Database::Logout($user);
|
||||||
|
Message::HandleLeave($user, $type);
|
||||||
|
unset(Context::GetChannel($user->channel)->users[$user->id]);
|
||||||
|
unset(Context::$onlineUsers[$user->id]);
|
||||||
|
}
|
||||||
|
}
|
333
lib/db.php
Normal file
333
lib/db.php
Normal file
|
@ -0,0 +1,333 @@
|
||||||
|
<?php
|
||||||
|
namespace sockchat;
|
||||||
|
use \PDO;
|
||||||
|
|
||||||
|
class FFDB {
|
||||||
|
protected static function CreateFile($dir) {
|
||||||
|
try {
|
||||||
|
while(file_exists($fname = "$dir/". md5(microtime())));
|
||||||
|
} catch(\Exception $e) {
|
||||||
|
while(file_exists($fname = "$dir/". md5(time() + rand(0, 100))));
|
||||||
|
}
|
||||||
|
return $fname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Init() {
|
||||||
|
if(!file_exists("./ffdb")) mkdir("./ffdb");
|
||||||
|
if(!file_exists("./ffdb/chans")) mkdir("./ffdb/chans");
|
||||||
|
if(!file_exists("./ffdb/bans")) mkdir("./ffdb/bans");
|
||||||
|
if(!file_exists("./ffdb/logs.txt")) file_put_contents("./ffdb/logs.txt", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Log($str) {
|
||||||
|
file_put_contents("./ffdb/logs.txt", "{$str}\n", FILE_APPEND);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Ban($ip, $id, $username, $expire) {
|
||||||
|
$fname = FFDB::CreateFile("./ffdb/bans");
|
||||||
|
file_put_contents($fname, implode("\f", [$ip, $id, $username, $expire]));
|
||||||
|
echo $fname ."\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Unban($ip, $id, $username) {
|
||||||
|
$files = glob("./ffdb/bans/*");
|
||||||
|
foreach($files as $file) {
|
||||||
|
$data = explode("\f", file_get_contents($file));
|
||||||
|
if(($data[0] == $ip && $data[0] != null) || ($data[1] == $id && $data[1] != null) || ($data[2] == $username && $data[2] != null))
|
||||||
|
unlink($file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function GetAllBans() {
|
||||||
|
$bans = [];
|
||||||
|
|
||||||
|
$files = glob("./ffdb/bans/*");
|
||||||
|
foreach($files as $file) {
|
||||||
|
$data = explode("\f", file_get_contents($file));
|
||||||
|
if($data[3] <= time() && $data[3] != "-1") {
|
||||||
|
unlink($file);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
array_push($bans, new Ban($data[0], $data[1], $data[2], $data[3]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $bans;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function CreateChannel($name, $pwd, $priv) {
|
||||||
|
$fname = FFDB::CreateFile("./ffdb/chans");
|
||||||
|
echo $fname ."\n";
|
||||||
|
file_put_contents($fname, implode("\f", [$name, $pwd, $priv]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function ModifyChannel($oldname, $newname, $pwd, $priv) {
|
||||||
|
$files = glob("./ffdb/chans/*");
|
||||||
|
foreach($files as $file) {
|
||||||
|
$data = explode("\f", file_get_contents($file));
|
||||||
|
if($data[0] == $oldname) {
|
||||||
|
file_put_contents($file, implode("\f", [$newname, $pwd, $priv]));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function RemoveChannel($name) {
|
||||||
|
$files = glob("./ffdb/chans/*");
|
||||||
|
foreach($files as $file) {
|
||||||
|
$data = explode("\f", file_get_contents($file));
|
||||||
|
if($data[0] == $name) {
|
||||||
|
unlink($file);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function GetAllChannels() {
|
||||||
|
$chans = [];
|
||||||
|
|
||||||
|
$files = glob("./ffdb/chans/*");
|
||||||
|
foreach($files as $file) {
|
||||||
|
$data = explode("\f", file_get_contents($file));
|
||||||
|
$chans[$data[0]] = new Channel($data[0], $data[1], $data[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $chans;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Database {
|
||||||
|
protected static $conn = null;
|
||||||
|
protected static $persist = true;
|
||||||
|
protected static $statements;
|
||||||
|
protected static $useFlatFile;
|
||||||
|
|
||||||
|
protected static function Execute($stmt, $fetch = false) {
|
||||||
|
Database::SpawnConnection();
|
||||||
|
$tmp = Database::$conn->prepare(Database::$statements[$stmt]["query"]);
|
||||||
|
foreach(Database::$statements[$stmt] as $param => $value) {
|
||||||
|
if($param != "query") $tmp->bindValue(":{$param}", $value);
|
||||||
|
}
|
||||||
|
$tmp->execute();
|
||||||
|
$ret = [];
|
||||||
|
if ($fetch) $ret = $tmp->fetchAll(PDO::FETCH_BOTH);
|
||||||
|
$tmp = null;
|
||||||
|
Database::$conn = null;
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function SpawnConnection() {
|
||||||
|
$chat = $GLOBALS["chat"];
|
||||||
|
Database::$conn = new PDO($chat["DB_DSN"], $chat["DB_USER"], $chat["DB_PASS"], Database::$persist ? [PDO::ATTR_PERSISTENT => true] : []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Init() {
|
||||||
|
$chat = $GLOBALS["chat"];
|
||||||
|
Database::$useFlatFile = !$chat["DB_ENABLE"];
|
||||||
|
$persist = $chat["DB_PERSIST"];
|
||||||
|
if($chat["DB_ENABLE"]) {
|
||||||
|
try {
|
||||||
|
self::$persist = $persist;
|
||||||
|
Database::SpawnConnection();
|
||||||
|
$pre = $chat["DB_TABLE_PREFIX"];
|
||||||
|
|
||||||
|
Database::$statements = [
|
||||||
|
"logstore" => [
|
||||||
|
"query" => "INSERT INTO {$pre}_logs (epoch, userid, username, color, channel, chrank, message, flags) VALUES (:epoch, :uid, :uname, :color, :chan, :chrank, :msg, :flags)",
|
||||||
|
"epoch" => "","uid" => "", "uname" => "", "color" => "", "chan" => "", "chrank" => "", "msg" => "", "flags" => ""
|
||||||
|
],
|
||||||
|
"fetchbacklog" => [
|
||||||
|
"query" => "SELECT * FROM {$pre}_logs WHERE channel = :chan OR channel = '@all' ORDER BY epoch DESC LIMIT 0, ". Backlog::$loglen,
|
||||||
|
"chan" => ""
|
||||||
|
],
|
||||||
|
"login" => [
|
||||||
|
"query" => "INSERT INTO {$pre}_online_users (userid, username, color, perms) VALUES (:uid, :uname, :col, :perms)",
|
||||||
|
"uid" => "", "uname" => "", "col" => "", "perms" => ""
|
||||||
|
],
|
||||||
|
"logout" => [
|
||||||
|
"query" => "DELETE FROM {$pre}_online_users WHERE userid = :uid",
|
||||||
|
"uid" => ""
|
||||||
|
],
|
||||||
|
"clrusers" => [
|
||||||
|
"query" => "TRUNCATE TABLE {$pre}_online_users"
|
||||||
|
],
|
||||||
|
"crchan" => [
|
||||||
|
"query" => "INSERT INTO {$pre}_channels (chname, pwd, priv) VALUES (:chn, :pwd, :priv)",
|
||||||
|
"chn" => "", "pwd" => "", "priv" => ""
|
||||||
|
],
|
||||||
|
"modchan" => [
|
||||||
|
"query" => "UPDATE {$pre}_channels SET chname = :chn, pwd = :pwd, priv = :priv WHERE chname = :chon",
|
||||||
|
"chn" => "", "pwd" => "", "priv" => "", "chon" => ""
|
||||||
|
],
|
||||||
|
"delchan" => [
|
||||||
|
"query" => "DELETE FROM {$pre}_channels WHERE chname = :chn",
|
||||||
|
"chn" => ""
|
||||||
|
],
|
||||||
|
"fetchchan" => [
|
||||||
|
"query" => "SELECT * FROM {$pre}_channels"
|
||||||
|
],
|
||||||
|
"banuser" => [
|
||||||
|
"query" => "INSERT INTO {$pre}_banned_users (ip, uid, username, expiration) VALUES (:ip, :id, :uname, :exp)",
|
||||||
|
"ip" => "", "id" => "", "uname" => "", "exp" => ""
|
||||||
|
],
|
||||||
|
"unban" => [
|
||||||
|
"query" => "DELETE FROM {$pre}_banned_users WHERE (ip IS NOT NULL AND ip LIKE :ip) OR (uid IS NOT NULL AND uid = :id) OR (username IS NOT NULL AND username = :uname)",
|
||||||
|
"ip" => "", "id" => "", "uname" => ""
|
||||||
|
],
|
||||||
|
"fetchbans" => [
|
||||||
|
"query" => "SELECT * FROM {$pre}_banned_users"
|
||||||
|
],
|
||||||
|
"updatebans" => [
|
||||||
|
"query" => "DELETE FROM {$pre}_banned_users WHERE expiration <= :epoch AND expiration != -1",
|
||||||
|
"epoch" => ""
|
||||||
|
]
|
||||||
|
];
|
||||||
|
} catch(\Exception $err) {
|
||||||
|
echo "Could not connect to the database! Details: ". $err->getMessage() ."\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else FFDB::Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function FetchBacklog($chan) {
|
||||||
|
if(!Database::$useFlatFile) {
|
||||||
|
$ret = new Backlog();
|
||||||
|
|
||||||
|
Database::$statements["fetchbacklog"]["chan"] = $chan;
|
||||||
|
$logs = Database::Execute("fetchbacklog", true);
|
||||||
|
foreach($logs as $log)
|
||||||
|
$ret->Log(new User($log["userid"], "", $log["username"], $log["color"], "", null), $log["message"], "rlbl", $log["epoch"], $log["flags"]);
|
||||||
|
$ret->logs = array_reverse($ret->logs);
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
} else return new Backlog();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function TruncateUserList() {
|
||||||
|
if(!Database::$useFlatFile) Database::Execute("clrusers");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Login($user) {
|
||||||
|
if(!Database::$useFlatFile) {
|
||||||
|
Database::$statements["login"]["uid"] = $user->id;
|
||||||
|
Database::$statements["login"]["uname"] = $user->username;
|
||||||
|
Database::$statements["login"]["col"] = $user->color;
|
||||||
|
Database::$statements["login"]["perms"] = $user->permstr;
|
||||||
|
Database::Execute("login");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Logout($user) {
|
||||||
|
if(!Database::$useFlatFile) {
|
||||||
|
Database::$statements["logout"]["uid"] = $user->id;
|
||||||
|
Database::Execute("logout");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Log($time, $user, $msg, $chan = null, $flags = "10010") {
|
||||||
|
$chan == null ? $user->channel : $chan;
|
||||||
|
if($chan == $GLOBALS["chat"]["DEFAULT_CHANNEL"]) $chan = "@default";
|
||||||
|
|
||||||
|
if(Database::$useFlatFile)
|
||||||
|
FFDB::Log("(". date("m/d/Y H:i:s") . ") ". $user->username ." to ". ($chan == null ? $user->channel : $chan) .": ". $msg);
|
||||||
|
else {
|
||||||
|
Database::$statements["logstore"]["epoch"] = $time;
|
||||||
|
Database::$statements["logstore"]["uid"] = $user->id;
|
||||||
|
Database::$statements["logstore"]["uname"] = $user->username;
|
||||||
|
Database::$statements["logstore"]["color"] = $user->color;
|
||||||
|
Database::$statements["logstore"]["chan"] = $chan == null ? $user->channel : $chan;
|
||||||
|
Database::$statements["logstore"]["chrank"] = $chan[0] == "@" ? 0 : Context::GetChannel($user->channel)->permissionLevel;
|
||||||
|
Database::$statements["logstore"]["msg"] = $msg;
|
||||||
|
Database::$statements["logstore"]["flags"] = substr($flags, 0, 4) ."0";
|
||||||
|
Database::Execute("logstore");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Ban($ip, $id, $username, $expire) {
|
||||||
|
if(Database::$useFlatFile)
|
||||||
|
FFDB::Ban($ip, $id, $username, $expire);
|
||||||
|
else {
|
||||||
|
Database::$statements["banuser"]["ip"] = $ip;
|
||||||
|
Database::$statements["banuser"]["id"] = $id;
|
||||||
|
Database::$statements["banuser"]["uname"] = $username;
|
||||||
|
Database::$statements["banuser"]["exp"] = $expire;
|
||||||
|
Database::Execute("banuser");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// you've got no bans
|
||||||
|
// you've got no drans
|
||||||
|
public static function GetAllBans() {
|
||||||
|
if(Database::$useFlatFile)
|
||||||
|
return FFDB::GetAllBans();
|
||||||
|
else {
|
||||||
|
$time = time();
|
||||||
|
Database::$statements["updatebans"]["epoch"] = $time;
|
||||||
|
Database::Execute("updatebans");
|
||||||
|
|
||||||
|
$blist = [];
|
||||||
|
$bans = Database::Execute("fetchbans", true);
|
||||||
|
foreach($bans as $ban) {
|
||||||
|
if($ban["expiration"] > $time || $ban["expiration"] == "-1") array_push($blist, new Ban($ban["ip"], $ban["uid"], $ban["username"], $ban["expiration"]));
|
||||||
|
}
|
||||||
|
return $blist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// you want some
|
||||||
|
// i'll give it ya
|
||||||
|
public static function Unban($ip, $id, $username) {
|
||||||
|
if(Database::$useFlatFile)
|
||||||
|
FFDB::Unban($ip, $id, $username);
|
||||||
|
else {
|
||||||
|
Database::$statements["unban"]["ip"] = $ip;
|
||||||
|
Database::$statements["unban"]["id"] = $id;
|
||||||
|
Database::$statements["unban"]["uname"] = $username;
|
||||||
|
Database::Execute("unban");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function CreateChannel($name, $pwd, $priv = 0) {
|
||||||
|
if(Database::$useFlatFile)
|
||||||
|
FFDB::CreateChannel($name, $pwd, $priv);
|
||||||
|
else {
|
||||||
|
Database::$statements["crchan"]["chn"] = $name;
|
||||||
|
Database::$statements["crchan"]["pwd"] = $pwd;
|
||||||
|
Database::$statements["crchan"]["priv"] = $priv;
|
||||||
|
Database::Execute("crchan");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function RemoveChannel($name) {
|
||||||
|
if(Database::$useFlatFile)
|
||||||
|
FFDB::RemoveChannel($name);
|
||||||
|
else {
|
||||||
|
Database::$statements["delchan"]["chn"] = $name;
|
||||||
|
Database::Execute("delchan");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function ModifyChannel($oldname, $newname, $pwd, $priv) {
|
||||||
|
if(Database::$useFlatFile)
|
||||||
|
FFDB::ModifyChannel($oldname, $newname, $pwd, $priv);
|
||||||
|
else {
|
||||||
|
Database::$statements["modchan"]["chon"] = $oldname;
|
||||||
|
Database::$statements["modchan"]["chn"] = $newname;
|
||||||
|
Database::$statements["modchan"]["pwd"] = $pwd;
|
||||||
|
Database::$statements["modchan"]["priv"] = $priv;
|
||||||
|
Database::Execute("modchan");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function GetAllChannels() {
|
||||||
|
if(Database::$useFlatFile)
|
||||||
|
return FFDB::GetAllChannels();
|
||||||
|
else {
|
||||||
|
$clist = [];
|
||||||
|
$chans = Database::Execute("fetchchan", true);
|
||||||
|
foreach($chans as $chan)
|
||||||
|
$clist[$chan["chname"]] = new Channel($chan["chname"], $chan["pwd"], $chan["priv"], null, CHANNEL_PERM, Database::FetchBacklog($chan["chname"]));
|
||||||
|
return $clist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
56
lib/mods.php
Normal file
56
lib/mods.php
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
namespace sockchat;
|
||||||
|
|
||||||
|
class Modules {
|
||||||
|
protected static $mods = [];
|
||||||
|
protected static $cmds = [];
|
||||||
|
|
||||||
|
public static function Load() {
|
||||||
|
require_once("./mods/generic_mod.php");
|
||||||
|
$mods = glob("./mods/*", GLOB_ONLYDIR);
|
||||||
|
foreach($mods as $mod) {
|
||||||
|
$name = substr($mod, strrpos($mod, "/")+1);
|
||||||
|
if(file_exists("{$mod}/{$name}.php")) {
|
||||||
|
include("{$mod}/{$name}.php");
|
||||||
|
if(class_exists("\\sockchat\\mods\\{$name}\\Main")) {
|
||||||
|
Modules::$mods[$name] = $name;
|
||||||
|
|
||||||
|
$cmds = call_user_func_array("\\sockchat\\mods\\{$name}\\Main::Init", []);
|
||||||
|
$cmds = call_user_func_array("\\sockchat\\mods\\{$name}\\Main::GetCommands", []);
|
||||||
|
foreach($cmds as $cmd) {
|
||||||
|
if(array_key_exists($cmd, self::$cmds))
|
||||||
|
echo "Error loading module $name: Command $cmd has already been defined by module ". self::$cmds[$cmd] ."!\n";
|
||||||
|
else self::$cmds[$cmd] = $name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//var_dump(self::$cmds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function DoesModExist($mod) {
|
||||||
|
return array_key_exists($mod, self::$mods);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function DoesCommandExist($cmd) {
|
||||||
|
return array_key_exists($cmd, self::$cmds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function ExecuteRoutine($routine, $args, $exitAfterFirstReturn = false) {
|
||||||
|
$ret = true;
|
||||||
|
foreach(Modules::$mods as $mod) {
|
||||||
|
$ret &= call_user_func_array("\\sockchat\\mods\\{$mod}\\Main::{$routine}", $args) === null;
|
||||||
|
if(!$ret && $exitAfterFirstReturn) break;
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function ExecuteCommand($cmd, $user, $args) {
|
||||||
|
if(!array_key_exists($cmd, self::$cmds)) return false;
|
||||||
|
else {
|
||||||
|
call_user_func_array("\\sockchat\\mods\\". self::$cmds[$cmd] ."\\Main::ExecuteCommand", [$cmd, $args, $user, "\\sockchat\\mods\\". self::$cmds[$cmd] ."\\Main"]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
219
lib/msg.php
Normal file
219
lib/msg.php
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
<?php
|
||||||
|
namespace sockchat;
|
||||||
|
|
||||||
|
class Message {
|
||||||
|
public static $msgId = 0;
|
||||||
|
public static $bot;
|
||||||
|
|
||||||
|
protected static function SendToAll($msg) {
|
||||||
|
foreach(Context::$onlineUsers as $user)
|
||||||
|
$user->sock->send($msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function LogToAll($user, $msg, $flags) {
|
||||||
|
if(!Modules::ExecuteRoutine("OnMessageLog", [$user, &$msg, "@all", &$flags])) return;
|
||||||
|
foreach(Context::$channelList as $channel)
|
||||||
|
$channel->log->Log($user, $msg, Message::$msgId, null, $flags);
|
||||||
|
Database::Log(gmdate("U"), $user, $msg, "@all", $flags);
|
||||||
|
Modules::ExecuteRoutine("OnMessageLog", [$user, $msg, "@all", $flags]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function SendToChannel($msg, $channel) {
|
||||||
|
if(is_string($channel)) {
|
||||||
|
if(Context::ChannelExists($channel)) {
|
||||||
|
$channel = Context::GetChannel($channel);
|
||||||
|
} else return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($channel->users as $user)
|
||||||
|
$user->sock->send($msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function LogToChannel($user, $msg, $channel, $flags) {
|
||||||
|
if(is_string($channel)) {
|
||||||
|
if($channel[0] != "@") {
|
||||||
|
if (Context::ChannelExists($channel)) {
|
||||||
|
$channel = Context::GetChannel($channel);
|
||||||
|
} else return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Modules::ExecuteRoutine("OnMessageLog", [$user, &$msg, $channel, &$flags]);
|
||||||
|
if(!is_string($channel)) {
|
||||||
|
Database::Log(gmdate("U"), $user, $msg, $channel->name, $flags);
|
||||||
|
$channel->log->Log($user, $msg, Message::$msgId, null, $flags);
|
||||||
|
} else Database::Log(gmdate("U"), $user, $msg, $channel, $flags);
|
||||||
|
Modules::ExecuteRoutine("AfterMessageLog", [$user, $msg, $channel, $flags]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function BroadcastSilentMessage($user, $msg, $channel = ALL_CHANNELS, $msgid = null, $time = null, $alert = false, $flags = "1001") {
|
||||||
|
if(!is_string($channel)) $channel = $channel->name;
|
||||||
|
$msgid = $msgid == null ? Message::$msgId : $msgid;
|
||||||
|
$flags = substr($flags, 0, 4) ."0";
|
||||||
|
if($channel == ALL_CHANNELS)
|
||||||
|
Message::SendToAll(Utils::PackMessage(P_CTX_DATA, ["1", $time == null ? gmdate("U") : $time, $user, $msg, $msgid, $alert == true ? "1": "0", $flags]));
|
||||||
|
else
|
||||||
|
Message::SendToChannel(Utils::PackMessage(P_CTX_DATA, ["1", $time == null ? gmdate("U") : $time, $user, $msg, $msgid, $alert == true ? "1": "0", $flags]), $channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function BroadcastSilentBotMessage($type, $langid, $params, $channel = ALL_CHANNELS, $msgid = null, $time = null, $alert = false) {
|
||||||
|
Message::BroadcastSilentMessage(Message::$bot, Utils::FormatBotMessage($type, $langid, $params), $channel, $msgid, $time, $alert);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function PrivateSilentMessage($user, $msg, $to, $msgid = null, $time = null, $alert = false, $flags = "1001", $pm = true) {
|
||||||
|
$msgid = $msgid == null ? Message::$msgId : $msgid;
|
||||||
|
$flags = substr($flags, 0, 4) . ($pm ? "1" : "0");
|
||||||
|
$to->sock->send(Utils::PackMessage(P_CTX_DATA, ["1", $time == null ? gmdate("U") : $time, $user, $msg, $msgid, $alert == true ? "1": "0", $flags]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function PrivateSilentBotMessage($type, $langid, $params, $to, $msgid = null, $time = null, $alert = false) {
|
||||||
|
Message::PrivateSilentMessage(Message::$bot, Utils::FormatBotMessage($type, $langid, $params), $to, $msgid, $time, $alert, "1001", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function ClearUserContext($user, $type = CLEAR_ALL) {
|
||||||
|
$user->sock->send(Utils::PackMessage(P_CTX_CLR, array($type)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function ClearUserContexts($channel = ALL_CHANNELS, $type = CLEAR_ALL) {
|
||||||
|
$out = Utils::PackMessage(P_CTX_CLR, array($type));
|
||||||
|
|
||||||
|
if($channel == ALL_CHANNELS) Message::SendToAll($out);
|
||||||
|
else Message::SendToChannel($out, ($channel == LOCAL_CHANNEL) ? Utils::$chat["DEFAULT_CHANNEL"] : $channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: DOES NOT SANITIZE INPUT MESSAGE !! DO THIS ELSEWHERE
|
||||||
|
public static function BroadcastUserMessage($user, $msg, $channel = LOCAL_CHANNEL, $flags = "1001") {
|
||||||
|
if(!is_string($channel)) $channel = $channel->name;
|
||||||
|
$flags = substr($flags, 0, 4) ."0";
|
||||||
|
$out = Utils::PackMessage(P_SEND_MESSAGE, array(gmdate("U"), $user->id, $msg, Message::$msgId, $flags));
|
||||||
|
|
||||||
|
if($channel == ALL_CHANNELS) {
|
||||||
|
Message::SendToAll($out);
|
||||||
|
Message::LogToAll($user, $msg, $flags);
|
||||||
|
Message::$msgId++;
|
||||||
|
} else {
|
||||||
|
$channel = ($channel == LOCAL_CHANNEL) ? $user->channel : $channel;
|
||||||
|
|
||||||
|
if(Context::ChannelExists($channel)) {
|
||||||
|
Message::SendToChannel($out, Context::GetChannel($channel));
|
||||||
|
Message::LogToChannel($user, $msg, $channel, $flags);
|
||||||
|
Message::$msgId++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function BroadcastBotMessage($type, $langid, $params, $channel = ALL_CHANNELS) {
|
||||||
|
$msg = Utils::FormatBotMessage($type, $langid, $params);
|
||||||
|
$channel = ($channel == LOCAL_CHANNEL) ? Utils::$chat["DEFAULT_CHANNEL"] : $channel;
|
||||||
|
Message::BroadcastUserMessage(Message::$bot, $msg, $channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function PrivateUserMessage($user, $to, $msg, $flags = "1001", $pm = true) {
|
||||||
|
$flags = substr($flags, 0, 4) . ($pm ? "1" : "0");
|
||||||
|
$out = Utils::PackMessage(P_SEND_MESSAGE, array(gmdate("U"), $user->id, $msg, Message::$msgId, $flags));
|
||||||
|
$to->sock->send($out);
|
||||||
|
if($user->id != $to->id)
|
||||||
|
Message::LogToChannel($user, "(@". $to->username .") ". $msg, "@priv", $flags);
|
||||||
|
Message::$msgId++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function PrivateBotMessage($type, $langid, $params, $to) {
|
||||||
|
$msg = Utils::FormatBotMessage($type, $langid, $params);
|
||||||
|
Message::PrivateUserMessage(Message::$bot, $to, $msg, "1001", false);
|
||||||
|
Message::$msgId++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function SendChannelToUser($user, $channel) {
|
||||||
|
if(is_string($channel)) $channel = Context::GetChannel($channel);
|
||||||
|
if($user->getRank() >= $channel->permissionLevel) $user->sock->send(Utils::PackMessage(P_CHANNEL_INFO, ["0", $channel]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function SendAllChannelsToUser($user) {
|
||||||
|
$arr = [];
|
||||||
|
foreach(Context::$channelList as $channel) {
|
||||||
|
if($user->getRank() >= $channel->permissionLevel) array_push($arr, $channel);
|
||||||
|
}
|
||||||
|
$user->sock->send(Utils::PackMessage(P_CTX_DATA, ["2", count($arr), join(Utils::$separator, $arr)]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function HandleKick($user, $length = 0) {
|
||||||
|
if($length == 0)
|
||||||
|
$user->sock->send(Utils::PackMessage(P_BAKA, ["kick"]));
|
||||||
|
else
|
||||||
|
$user->sock->send(Utils::PackMessage(P_BAKA, ["ban", date("U") + $length]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function HandleUserModification($user) {
|
||||||
|
Message::SendToChannel(Utils::PackMessage(P_USER_CHANGE, [$user]), $user->channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function HandleJoin($user) {
|
||||||
|
Message::SendToChannel(Utils::PackMessage(P_USER_JOIN, array(gmdate("U"), $user, Message::$msgId)), Utils::$chat["DEFAULT_CHANNEL"]);
|
||||||
|
|
||||||
|
$user->sock->send(Utils::PackMessage(P_USER_JOIN, array("y", $user, Utils::$chat["DEFAULT_CHANNEL"])));
|
||||||
|
Message::LogToChannel(Message::$bot, Utils::FormatBotMessage(MSG_NORMAL, "join", array($user->username)), Utils::$chat["DEFAULT_CHANNEL"], "10010");
|
||||||
|
$user->sock->send(Utils::PackMessage(P_CTX_DATA, array("0", Context::GetChannel(Utils::$chat["DEFAULT_CHANNEL"])->GetAllUsers())));
|
||||||
|
|
||||||
|
$msgs = Context::GetChannel(Utils::$chat["DEFAULT_CHANNEL"])->log->GetAllLogStrings();
|
||||||
|
foreach($msgs as $msg)
|
||||||
|
$user->sock->send(Utils::PackMessage(P_CTX_DATA, array("1", $msg)));
|
||||||
|
|
||||||
|
Message::SendAllChannelsToUser($user);
|
||||||
|
|
||||||
|
Message::$msgId++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function HandleChannelCreation($channel) {
|
||||||
|
foreach(Context::$onlineUsers as $user)
|
||||||
|
Message::SendChannelToUser($user, $channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function HandleChannelDeletion($channel) {
|
||||||
|
if(is_string($channel)) $channel = Context::GetChannel($channel);
|
||||||
|
|
||||||
|
foreach(Context::$onlineUsers as $user) {
|
||||||
|
if($user->getRank() >= $channel->permissionLevel) $user->sock->send(Utils::PackMessage(4, ["2", $channel->name]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function HandleChannelModification($channel, $oldname = "") {
|
||||||
|
if(is_string($channel)) $channel = Context::GetChannel($channel);
|
||||||
|
Database::ModifyChannel($oldname == "" ? $channel->name : $oldname, $channel->name, $channel->password, $channel->permissionLevel);
|
||||||
|
foreach(Context::$onlineUsers as $user) {
|
||||||
|
if($user->getRank() >= $channel->permissionLevel) {
|
||||||
|
$user->sock->send(Utils::PackMessage(4, ["1", $oldname == "" ? $channel->name : $oldname, $channel]));
|
||||||
|
if($user->channel == $oldname && $oldname != "") {
|
||||||
|
$user->sock->send(Utils::PackMessage(5, ["2", $channel->name]));
|
||||||
|
$user->channel = $channel->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function HandleChannelSwitch($user, $to, $from) {
|
||||||
|
Message::SendToChannel(Utils::PackMessage(P_CHANGE_CHANNEL, array("1", $user->id, Message::$msgId)), $from);
|
||||||
|
Message::LogToChannel(Message::$bot, Utils::FormatBotMessage(MSG_NORMAL, "lchan", array($user->username)), $from, "10010");
|
||||||
|
Message::SendToChannel(Utils::PackMessage(P_CHANGE_CHANNEL, array("0", $user, Message::$msgId)), $to);
|
||||||
|
Message::LogToChannel(Message::$bot, Utils::FormatBotMessage(MSG_NORMAL, "jchan", array($user->username)), $to, "10010");
|
||||||
|
$user->sock->send(Utils::PackMessage(P_CTX_CLR, array(CLEAR_MSGNUSERS)));
|
||||||
|
$user->sock->send(Utils::PackMessage(P_CTX_DATA, array("0", Context::GetChannel($to)->GetAllUsers(), Message::$msgId)));
|
||||||
|
|
||||||
|
$msgs = Context::GetChannel($to)->log->GetAllLogStrings();
|
||||||
|
foreach($msgs as $msg)
|
||||||
|
$user->sock->send(Utils::PackMessage(P_CTX_DATA, array("1", $msg)));
|
||||||
|
|
||||||
|
$user->sock->send(Utils::PackMessage(P_CHANGE_CHANNEL, array("2", $to)));
|
||||||
|
|
||||||
|
Message::$msgId++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function HandleLeave($user, $method = LEAVE_NORMAL) {
|
||||||
|
Message::SendToChannel(Utils::PackMessage(P_USER_LEAVE, array($user->id, $user->username, $method, gmdate("U"), Message::$msgId)), $user->channel);
|
||||||
|
Message::LogToChannel(Message::$bot, Utils::FormatBotMessage(MSG_NORMAL, $method, array($user->username)), $user->channel, "10010");
|
||||||
|
Message::$msgId++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function DeleteMessage($id) {
|
||||||
|
Message::SendToAll(Utils::PackMessage(P_MSG_DEL, [$id]));
|
||||||
|
}
|
||||||
|
}
|
79
lib/user.php
Normal file
79
lib/user.php
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
<?php
|
||||||
|
namespace sockchat;
|
||||||
|
|
||||||
|
class User {
|
||||||
|
public $id;
|
||||||
|
public $channel;
|
||||||
|
public $originalData = [];
|
||||||
|
public $username;
|
||||||
|
public $color;
|
||||||
|
public $permissions;
|
||||||
|
public $permstr;
|
||||||
|
public $sock;
|
||||||
|
public $ping;
|
||||||
|
protected $customParams = [];
|
||||||
|
|
||||||
|
public function __construct($id, $channel, $username, $color, $permissions, $sock) {
|
||||||
|
$this->id = $id;
|
||||||
|
$this->channel = $channel;
|
||||||
|
$this->originalData = [$username, $color, $permissions];
|
||||||
|
$this->username = $username;
|
||||||
|
$this->color = $color;
|
||||||
|
$this->permstr = $permissions;
|
||||||
|
$this->permissions = explode("\f", $permissions);
|
||||||
|
$this->sock = $sock;
|
||||||
|
$this->ping = gmdate("U");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function SetParameter($key, $value) {
|
||||||
|
$this->customParams[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetParameter($key) {
|
||||||
|
if(array_key_exists($key, $this->customParams)) return $this->customParams[$key];
|
||||||
|
else return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetOriginalUsername() {
|
||||||
|
return $this->originalData[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetOriginalColor() {
|
||||||
|
return $this->originalData[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetOriginalPermissionString() {
|
||||||
|
return $this->originalData[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Copy($user) {
|
||||||
|
$this->username = $user->username;
|
||||||
|
$this->color = $user->color;
|
||||||
|
$this->permstr = $user->permstr;
|
||||||
|
$this->permissions = $user->permissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetRank() {
|
||||||
|
return $this->permissions[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function CanModerate() {
|
||||||
|
return $this->permissions[1] == "1";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function CanViewLogs() {
|
||||||
|
return $this->permissions[2] == "1";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function CanChangeNick() {
|
||||||
|
return $this->permissions[3] == "1";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ChannelCreationPermission() {
|
||||||
|
return $this->permissions[4];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString() {
|
||||||
|
return join(Utils::$separator, array($this->id, $this->username, $this->color, $this->permstr));
|
||||||
|
}
|
||||||
|
}
|
92
lib/utils.php
Normal file
92
lib/utils.php
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
<?php
|
||||||
|
namespace sockchat;
|
||||||
|
|
||||||
|
class Utils {
|
||||||
|
public static $separator = "\t";
|
||||||
|
public static $chat;
|
||||||
|
|
||||||
|
public static function PackMessage($id, $params) {
|
||||||
|
$ret = $id . Utils::$separator . join(Utils::$separator, $params);
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function FormatBotMessage($type, $id, $params) {
|
||||||
|
return $type ."\f". $id ."\f". implode("\f", $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Sanitize($str) {
|
||||||
|
return str_replace(["<", ">", "\n"], ["<", ">", " <br/> "], $str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function SanitizeName($name) {
|
||||||
|
return str_replace([" ","\n","\t","\f"], ["_","","",""], htmlspecialchars($name, ENT_QUOTES));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function GetHeader($sock, $name) {
|
||||||
|
try {
|
||||||
|
return (string)$sock->WebSocket->request->getHeader($name, true);
|
||||||
|
} catch(\Exception $e) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function DoesModExist($name) {
|
||||||
|
return file_exists("./mods/". $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function DoesCommandExist($name) {
|
||||||
|
return file_exists("./commands/". $name .".php");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Hash($in) {
|
||||||
|
return hash("sha256", $in);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function IsValidIPAddress($addr) {
|
||||||
|
$addr = explode(".", $addr);
|
||||||
|
if(count($addr) != 4) return false;
|
||||||
|
foreach($addr as $subaddr) {
|
||||||
|
if(!is_numeric($subaddr) && $subaddr != "*") return false;
|
||||||
|
if(($subaddr > 255 || $subaddr < 0) && $subaddr != "*") return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function CheckIPAddresses($addr1, $addr2) {
|
||||||
|
$addr1 = explode(".", $addr1);
|
||||||
|
$addr2 = explode(".", $addr2);
|
||||||
|
|
||||||
|
for($i = 0; $i < 4; $i++) {
|
||||||
|
if($addr1[$i] != $addr2[$i] && $addr1[$i] != "*" && $addr2[$i] != "*") return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function CreateUniqueFile($dir) {
|
||||||
|
try {
|
||||||
|
while(file_exists($fname = "$dir/". md5(microtime())));
|
||||||
|
} catch(\Exception $e) {
|
||||||
|
while(file_exists($fname = "$dir/". md5(time() + rand(0, 100))));
|
||||||
|
}
|
||||||
|
return $fname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Romanize($num) {
|
||||||
|
$lultima_romano = array("M" => 1000, "CM" => 900, "D" => 500, "CD" => 400, "C" => 100, "XC" => 90,
|
||||||
|
"L" => 50, "XL" => 40, "X" => 10, "IX" => 9, "V" => 5, "IV" => 4, "I" => 1);
|
||||||
|
|
||||||
|
$piangera = "";
|
||||||
|
while($num > 0) {
|
||||||
|
foreach($lultima_romano as $romano => $italia) {
|
||||||
|
if($num >= $italia) {
|
||||||
|
$num -= $italia;
|
||||||
|
$piangera .= $romano;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $piangera;
|
||||||
|
}
|
||||||
|
}
|
57
mods/Greeter/Greeter.php
Normal file
57
mods/Greeter/Greeter.php
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* GREETER: A SAMPLE SERVER MOD
|
||||||
|
*
|
||||||
|
* This will send a chatbot message to a user first thing as they join the server, greeting them. This is meant as a
|
||||||
|
* simple example of how to write mods for the Sock Chat server engine.
|
||||||
|
*
|
||||||
|
* For a reference guide to the Sock Chat engine interface, refer to the following page:
|
||||||
|
* TODO put link to engine reference here
|
||||||
|
*/
|
||||||
|
|
||||||
|
// the filename containing the main class must have the same name as the folder
|
||||||
|
|
||||||
|
namespace sockchat\mods\Greeter; // the third portion of the namespace must be the same as the folder name
|
||||||
|
use sockchat\cmds\join;
|
||||||
|
use \sockchat\mods\GenericMod; // necessary for the implementation (detailed below)
|
||||||
|
|
||||||
|
// some common static classes you may deal with
|
||||||
|
use \sockchat\Context;
|
||||||
|
use sockchat\User;
|
||||||
|
use \sockchat\Utils;
|
||||||
|
use \sockchat\Message;
|
||||||
|
use \sockchat\Database;
|
||||||
|
|
||||||
|
// the main class must be called Main and must extend \sockchat\mods\GenericMod.
|
||||||
|
class Main extends GenericMod {
|
||||||
|
protected static $bot;
|
||||||
|
|
||||||
|
public static function Init() {
|
||||||
|
self::$bot = Context::AddInvisibleUser("negrophobe", "#FF69B4");
|
||||||
|
}
|
||||||
|
|
||||||
|
// we want to do things when a user joins
|
||||||
|
public static function OnUserJoin($user) {
|
||||||
|
// send the bot message to the user that has just entered the chat
|
||||||
|
Message::PrivateSilentBotMessage(MSG_NORMAL, "say", ["Welcome to the chat, ". $user->username ."!"], $user, "welcome");
|
||||||
|
Message::PrivateSilentBotMessage(MSG_NORMAL, "say", ["To change your user color, use /color"], $user, "welcome");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
public static function AfterChannelCreate($channel) {
|
||||||
|
Message::BroadcastUserMessage(self::$bot, "channel ". $channel->name ." created", ALL_CHANNELS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function OnCommandReceive($user, &$cmd, &$args) {
|
||||||
|
//Message::BroadcastUserMessage(self::$bot, $user->username ." sent command ". $cmd ." with args ". implode(" ", $args), $user->channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function AfterMessageReceived($user, $msg) {
|
||||||
|
if(strstr($msg, "nigger")) Message::BroadcastUserMessage(self::$bot, "the only nigger here is [i]you[/i]", $user->channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function OnUserLeave($user) {
|
||||||
|
Message::BroadcastUserMessage(self::$bot, "this ". $user->username ." guy is a faggot", $user->channel);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
426
mods/core/core.php
Normal file
426
mods/core/core.php
Normal file
|
@ -0,0 +1,426 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace sockchat\mods\core;
|
||||||
|
use \sockchat\mods\GenericMod;
|
||||||
|
|
||||||
|
use \sockchat\Context;
|
||||||
|
use \sockchat\User;
|
||||||
|
use \sockchat\Utils;
|
||||||
|
use \sockchat\Message;
|
||||||
|
use \sockchat\Channel;
|
||||||
|
|
||||||
|
class Stack {
|
||||||
|
protected $size;
|
||||||
|
protected $stack;
|
||||||
|
|
||||||
|
public function __construct($size, $copy = null) {
|
||||||
|
$this->size = $size;
|
||||||
|
if($copy != null) {
|
||||||
|
if($copy->Size() <= $this->size) $this->stack = $copy->Raw();
|
||||||
|
else $this->stack = array_slice($copy->Raw(), $copy->Size()-$this->size);
|
||||||
|
} else $this->stack = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Push($val, $id = -1) {
|
||||||
|
if($id == -1)
|
||||||
|
array_push($this->stack, $val);
|
||||||
|
else
|
||||||
|
$this->stack[$id] = $val;
|
||||||
|
if(count($this->stack) > $this->size)
|
||||||
|
$this->stack = array_slice($this->stack, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Pop() {
|
||||||
|
return array_pop($this->stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Top() {
|
||||||
|
return ($this->stack == []) ? null : $this->stack[count($this->stack)-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Bottom() {
|
||||||
|
return ($this->stack == []) ? null : $this->stack[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function MaxSize() {
|
||||||
|
return $this->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Size() {
|
||||||
|
return count($this->stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Full() {
|
||||||
|
return $this->size == count($this->stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Raw() {
|
||||||
|
return $this->stack;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Main extends GenericMod {
|
||||||
|
protected static $silencedUsers;
|
||||||
|
protected static $allowedCmds = ["join", "whois", "afk"];
|
||||||
|
|
||||||
|
protected static $maxAfkTagLength = 5;
|
||||||
|
protected static $floodFilterSize = 30;
|
||||||
|
protected static $floodFilerDuration = 10;
|
||||||
|
|
||||||
|
protected static $folder;
|
||||||
|
|
||||||
|
protected static $logLength = 300;
|
||||||
|
protected static $messageLog;
|
||||||
|
|
||||||
|
public static function Silence($user, $expires) {
|
||||||
|
self::$silencedUsers[Utils::$chat["AUTOID"] ? $user->GetOriginalUsername() : $user->id] = $expires;
|
||||||
|
$user->SetParameter("silence", $expires);
|
||||||
|
$fn = Utils::CreateUniqueFile(self::$folder ."/ffdb/silences");
|
||||||
|
file_put_contents($fn, implode("\f", [$expires, $user->id, $user->GetOriginalUsername()]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function RemoveSilence($user) {
|
||||||
|
$search = Utils::$chat["AUTOID"] ? [$user->GetOriginalUsername(), 2] : [$user->id, 1];
|
||||||
|
if(array_key_exists($search[0], self::$silencedUsers)) unset(self::$silencedUsers[$search[0]]);
|
||||||
|
$user->SetParameter("silence", null);
|
||||||
|
|
||||||
|
$files = glob(self::$folder ."/ffdb/silences/*");
|
||||||
|
foreach($files as $file) {
|
||||||
|
$parts = explode("\f", file_get_contents($file));
|
||||||
|
if($parts[$search[1]] == $search[0]) {
|
||||||
|
unlink($file);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function IsSilencedFromList($user) {
|
||||||
|
$key = Utils::$chat["AUTOID"] ? $user->GetOriginalUsername() : $user->id;
|
||||||
|
if(array_key_exists($key, self::$silencedUsers)) {
|
||||||
|
if(self::$silencedUsers[$key] > gmdate("U") || self::$silencedUsers[$key] == "-1") return self::$silencedUsers[$key];
|
||||||
|
else unset(self::$silencedUsers[$key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function IsSilenced($user) {
|
||||||
|
$tmp = $user->GetParameter("silence");
|
||||||
|
if($tmp == null) return false;
|
||||||
|
else {
|
||||||
|
if($tmp > gmdate("U") || $tmp == "-1") return true;
|
||||||
|
else {
|
||||||
|
self::RemoveSilence($user);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Init() {
|
||||||
|
$dir = self::$folder = self::GetModFolder(__NAMESPACE__);
|
||||||
|
|
||||||
|
if(!file_exists("$dir/ffdb/silences")) mkdir("$dir/ffdb/silences", 0777, true);
|
||||||
|
self::$silencedUsers = [];
|
||||||
|
|
||||||
|
$files = glob("$dir/ffdb/silences/*");
|
||||||
|
foreach($files as $file) {
|
||||||
|
$parts = explode("\f", file_get_contents($file));
|
||||||
|
|
||||||
|
if(gmdate("U") >= $parts[0])
|
||||||
|
unlink($file);
|
||||||
|
else
|
||||||
|
self::$silencedUsers[Utils::$chat["AUTOID"] ? $parts[2] : $parts[1]] = $parts[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$messageLog = new Stack(self::$logLength);
|
||||||
|
|
||||||
|
self::AddCommandHook(["join", "create", "del", "pwd", "password", "priv", "privilege", "rank"], "handleChannelCommands");
|
||||||
|
self::AddCommandHook(["kick", "ban", "pardon", "unban", "silence", "unsilence", "say", "whois", "ip", "delete", "unbanip", "pardonip", "bans", "banned"], "handleModeratorCommands");
|
||||||
|
self::AddCommandHook(["whisper", "msg", "nick", "afk", "me", "action", "who", "color"], "handleUserCommands");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function OnUserJoin($user) {
|
||||||
|
if(($time = self::IsSilencedFromList($user)) != 0) $user->SetParameter("silence", $time);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function OnMessageReceive($user, &$msg) {
|
||||||
|
if(($val = $user->GetParameter("afk")) != null) {
|
||||||
|
$user->username = mb_substr($user->username, mb_strlen("<$val>_"));
|
||||||
|
$user->SetParameter("afk", null);
|
||||||
|
Context::ModifyUser($user);
|
||||||
|
}
|
||||||
|
if(self::IsSilenced($user)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function OnPacketReceive($conn, &$pid, &$data) {
|
||||||
|
if(($target = Context::GetUserBySock($conn)) != null) {
|
||||||
|
if($target->GetParameter("packetlog") == null) $target->SetParameter("packetlog", new Stack(self::$floodFilterSize));
|
||||||
|
$target->GetParameter("packetlog")->Push(gmdate("U"));
|
||||||
|
$stack = $target->GetParameter("packetlog");
|
||||||
|
//var_dump($stack);
|
||||||
|
$tmp = new Stack(self::$floodFilterSize-5, $stack);
|
||||||
|
if($tmp->Full()) {
|
||||||
|
if($stack->Top() - $stack->Bottom() <= self::$floodFilerDuration)
|
||||||
|
Message::PrivateBotMessage(MSG_NORMAL, "flwarn", [], $target);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($stack->Full()) {
|
||||||
|
if($stack->Top() - $stack->Bottom() <= self::$floodFilerDuration)
|
||||||
|
Context::KickUser($target, null, 0, false, "flood");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function AfterMessageLog($user, $msg, $channel, $flags) {
|
||||||
|
self::$messageLog->Push($user->id == -1 ? 999999999 : $user->GetRank(), Message::$msgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function handleChannelCommands($cmd, $user, $args) {
|
||||||
|
switch($cmd) {
|
||||||
|
case "join":
|
||||||
|
if(isset($args[0]) && $args[0] != "") {
|
||||||
|
$pwd = isset($args[1]) ? Utils::Hash(implode(" ", array_slice($args, 1))) : "";
|
||||||
|
Context::SwitchChannel($user, $args[0], $pwd);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "create":
|
||||||
|
if($user->channelCreationPermission() != "0") {
|
||||||
|
if(isset($args[0]) && $args[0] != "") {
|
||||||
|
$channel = null;
|
||||||
|
if(is_numeric($args[0]) && isset($args[1]) && $args[1] != "") {
|
||||||
|
$args[0] = ($args[0] > $user->getRank()) ? $user->getRank() : $args[0];
|
||||||
|
$channel = new Channel(implode("_", array_slice($args, 1)), "", $args[0], $user);
|
||||||
|
} else {
|
||||||
|
$channel = new Channel(implode("_", $args));
|
||||||
|
$channel->channelOwner = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
$channel->channelType = ($user->channelCreationPermission() == 1) ? CHANNEL_TEMP : CHANNEL_PERM;
|
||||||
|
|
||||||
|
if(($ret = Context::CreateChannel($channel)) == "OK") {
|
||||||
|
if($channel->channelType == CHANNEL_TEMP) Context::SwitchChannel($user, $channel->name, $channel->password);
|
||||||
|
Message::PrivateBotMessage(MSG_NORMAL, "crchan", [$channel->name], $user);
|
||||||
|
} else
|
||||||
|
Message::PrivateUserMessage(Message::$bot, $user, $ret, "1001", false);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "cmderr", [], $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "cmdna", ["/create"], $user);
|
||||||
|
break;
|
||||||
|
case "del":
|
||||||
|
if(isset($args[0]) && $args[0] != "") {
|
||||||
|
$name = implode($args, "_");
|
||||||
|
if(($channel = Context::GetChannel($name)) != null) {
|
||||||
|
if($user->canModerate() || $channel->GetOwner()->id == $user->id) {
|
||||||
|
Context::DeleteChannel($channel);
|
||||||
|
Message::PrivateBotMessage(MSG_NORMAL, "delchan", [$name], $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "ndchan", [$name], $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "nochan", [$name], $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "cmderr", [], $user);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "password":
|
||||||
|
case "pwd":
|
||||||
|
if($user->canModerate() || Context::GetChannel($user->channel)->GetOwner()->id == $user->id) {
|
||||||
|
Context::ChangeChannelPassword($user->channel, isset($args[0]) ? implode(" ", $args) : "");
|
||||||
|
Message::PrivateBotMessage(MSG_NORMAL, "cpwdchan", [], $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "cmdna", ["/pwd"], $user);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "privilege":
|
||||||
|
case "rank":
|
||||||
|
case "priv":
|
||||||
|
if($user->canModerate() || Context::GetChannel($user->channel)->GetOwner()->id == $user->id) {
|
||||||
|
if(!isset($args[0]) || $args[0] <= $user->getRank()) {
|
||||||
|
Context::ChangeChannelPermission($user->channel, isset($args[0]) ? $args[0] : 0);
|
||||||
|
Message::PrivateBotMessage(MSG_NORMAL, "cprivchan", [], $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "rankerr", [], $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "cmdna", ["/priv"], $user);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function handleModeratorCommands($cmd, $user, $args) {
|
||||||
|
if($user->canModerate()) {
|
||||||
|
switch ($cmd) {
|
||||||
|
case "kick":
|
||||||
|
if(($target = Context::GetUserByName($args[0])) != null) {
|
||||||
|
if($target->getRank() < $user->getRank() && strtolower($args[0]) != strtolower($user->username)) {
|
||||||
|
$length = (!isset($args[1]) || !is_numeric($args[1])) ? 0 : ($args[1] > 0 ? $args[1]*60 : -1);
|
||||||
|
Context::KickUser($target, $user, $length);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "kickna", [$args[0]], $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "usernf", [$args[0]], $user);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "ban":
|
||||||
|
if(($target = Context::GetUserByName($args[0])) != null) {
|
||||||
|
if($target->getRank() < $user->getRank() && strtolower($args[0]) != strtolower($user->username)) {
|
||||||
|
$length = (!isset($args[1]) || !is_numeric($args[1])) ? -1 : ($args[1] > 0 ? $args[1]*60 : -1);
|
||||||
|
Context::KickUser($target, $user, $length, true);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "kickna", [$args[0]], $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "usernf", [$args[0]], $user);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "pardon":
|
||||||
|
case "unban":
|
||||||
|
if(Context::Unban(null, null, $args[0], $user)) {
|
||||||
|
Message::PrivateBotMessage(MSG_NORMAL, "unban", [$args[0]], $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "notban", [$args[0]], $user);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "pardonip":
|
||||||
|
case "unbanip":
|
||||||
|
if(Context::Unban(null, $args[0], null, $user)) {
|
||||||
|
Message::PrivateBotMessage(MSG_NORMAL, "unban", [$args[0]], $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "notban", [$args[0]], $user);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "bans":
|
||||||
|
case "banned":
|
||||||
|
$list = [];
|
||||||
|
foreach(Context::$bannedUsers as $bid => $ban) {
|
||||||
|
if($ban->username == null)
|
||||||
|
$push = "<a href=\"javascript:void(0);\" onclick=\"Chat.SendMessageWrapper('/unbanip '+ this.innerHTML);\">". $ban->ip ."</a>";
|
||||||
|
else
|
||||||
|
$push = "<a href=\"javascript:void(0);\" onclick=\"Chat.SendMessageWrapper('/unban '+ this.innerHTML);\">". $ban->username ."</a>";
|
||||||
|
array_push($list, $push);
|
||||||
|
}
|
||||||
|
Message::PrivateBotMessage(MSG_NORMAL, "banlist", [implode(", ", $list)], $user);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "silence":
|
||||||
|
if(($target = Context::GetUserByName($args[0])) != null) {
|
||||||
|
if($target->id != $user->id) {
|
||||||
|
if($target->getRank() < $user->getRank()) {
|
||||||
|
if (!self::IsSilenced($target)) {
|
||||||
|
$exp = isset($args[1]) ? (int)gmdate("U") + abs($args[1]*60) : -1;
|
||||||
|
self::Silence($target, $exp);
|
||||||
|
Message::PrivateBotMessage(MSG_NORMAL, "silence", [], $target);
|
||||||
|
Message::PrivateBotMessage(MSG_NORMAL, "silok", [$target->username], $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "silerr", [], $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "silperr", [], $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "silself", [], $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "usernf", [$args[0]], $user);
|
||||||
|
break;
|
||||||
|
case "unsilence":
|
||||||
|
if(($target = Context::GetUserByName($args[0])) != null) {
|
||||||
|
if($target->getRank() < $user->getRank()) {
|
||||||
|
if (self::IsSilenced($target)) {
|
||||||
|
self::RemoveSilence($target);
|
||||||
|
Message::PrivateBotMessage(MSG_NORMAL, "unsil", [], $target);
|
||||||
|
Message::PrivateBotMessage(MSG_NORMAL, "usilok", [$target->username], $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "usilerr", [], $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "usilperr", [], $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "usernf", [$args[0]], $user);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "say":
|
||||||
|
Message::BroadcastBotMessage(MSG_NORMAL, "say", [implode(" ", $args)]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "ip":
|
||||||
|
case "whois":
|
||||||
|
if(($tgt = Context::GetUserByName($args[0])) != null) {
|
||||||
|
Message::PrivateBotMessage(MSG_NORMAL, "ipaddr", [$args[0], $tgt->sock->remoteAddress], $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "usernf", [$args[0]], $user);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "delete":
|
||||||
|
if(isset($args[0])) {
|
||||||
|
if(array_key_exists($args[0], self::$messageLog->Raw())) {
|
||||||
|
if(self::$messageLog->Raw()[$args[0]] <= $user->GetRank()) {
|
||||||
|
Message::DeleteMessage($args[0]);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "delerr", [], $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "delerr", [], $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "cmderr", [], $user);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "cmdna", ["/{$cmd}"], $user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function handleUserCommands($cmd, $user, $args) {
|
||||||
|
switch($cmd) {
|
||||||
|
case "nick":
|
||||||
|
if($user->canChangeNick()) {
|
||||||
|
$name = "~". trim(Utils::SanitizeName(mb_substr(join("_", $args), 0, Utils::$chat["MAX_USERNAME_LEN"]-1)));
|
||||||
|
if(!isset($args[0])) $name = $user->GetOriginalUsername();
|
||||||
|
if(Context::GetUserByName($name) == null) {
|
||||||
|
Message::BroadcastBotMessage(MSG_NORMAL, "nick", [$user->username, $name], $user->channel);
|
||||||
|
$user->username = $name;
|
||||||
|
Context::ModifyUser($user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "nameinuse", [$name], $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "cmdna", ["/nick"], $user);
|
||||||
|
break;
|
||||||
|
case "whisper":
|
||||||
|
case "msg":
|
||||||
|
if(isset($args[0]) && isset($args[1])) {
|
||||||
|
if(($target = Context::GetUserByName($args[0])) != null) {
|
||||||
|
if($target->id != $user->id) {
|
||||||
|
$msg = implode(" ", array_slice($args, 1));
|
||||||
|
Message::PrivateUserMessage($user, $target, $msg);
|
||||||
|
Message::PrivateUserMessage($user, $user, $target->username . " " . $msg);
|
||||||
|
}
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "usernf", [$args[0]], $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "cmderr", [], $user);
|
||||||
|
break;
|
||||||
|
case "afk":
|
||||||
|
$val = isset($args[0]) ? strtoupper(mb_substr($args[0], 0, self::$maxAfkTagLength)) : "AFK";
|
||||||
|
if($user->GetParameter("afk") == null && $val != "") {
|
||||||
|
$user->SetParameter("afk", $val);
|
||||||
|
$user->username = "<$val>_". $user->username;
|
||||||
|
Context::ModifyUser($user);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "action":
|
||||||
|
case "me":
|
||||||
|
$msg = join(" ", $args);
|
||||||
|
if(trim($msg) != "")
|
||||||
|
Message::BroadcastUserMessage($user, "<i>". $msg ."</i>", LOCAL_CHANNEL, "1100");
|
||||||
|
break;
|
||||||
|
case "who":
|
||||||
|
if(isset($args[0])) {
|
||||||
|
if(($chan = Context::GetChannel($args[0])) != null) {
|
||||||
|
if($chan->permissionLevel <= $user->GetRank() && ($chan->password != "" || $user->CanModerate())) {
|
||||||
|
$arr = [];
|
||||||
|
foreach ($chan->users as $u)
|
||||||
|
array_push($arr, "<a href=\"javascript:void(0);\" onclick=\"UI.InsertChatText(this.innerHTML);\"". ($u->id == $user->id ? " style='font-weight: bold;'" : "") .">" . $u->username . "</a>");
|
||||||
|
Message::PrivateBotMessage(MSG_NORMAL, "whochan", [$args[0], implode(", ", $arr)], $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "whoerr", [$args[0]], $user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "nochan", [$args[0]], $user);
|
||||||
|
} else {
|
||||||
|
$arr = [];
|
||||||
|
foreach(Context::$onlineUsers as $u)
|
||||||
|
array_push($arr, "<a href=\"javascript:void(0);\" onclick=\"UI.InsertChatText(this.innerHTML);\"". ($u->id == $user->id ? " style='font-weight: bold;'" : "") .">". $u->username ."</a>");
|
||||||
|
Message::PrivateBotMessage(MSG_NORMAL, "who", [implode(", ", $arr)], $user);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "color":
|
||||||
|
if(isset($args[0])) {
|
||||||
|
$user->color = $args[0];//str_replace(["&",";","\"","'"], ["","","",""], $args[0]);
|
||||||
|
Context::ModifyUser($user);
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "cmderr", [], $user);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function OnCommandReceive($user, &$cmd, &$args) {
|
||||||
|
/*if(!self::IsSilenced($user) || (self::IsSilenced($user) && in_array($cmd, self::$allowedCmds))) {
|
||||||
|
if($cmd == "afk") {
|
||||||
|
$val = isset($args[0]) ? strtoupper(mb_substr($args[0], 0, self::$maxAfkTagLength)) : "AFK";
|
||||||
|
if($user->GetParameter("afk") == null && $val != "") {
|
||||||
|
$user->SetParameter("afk", $val);
|
||||||
|
$user->username = "<$val>_". $user->username;
|
||||||
|
Context::ModifyUser($user);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else if($cmd == "silence") {
|
||||||
|
if($user->canModerate()) {
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "cmdna", ["/silence"], $user);
|
||||||
|
return false;
|
||||||
|
} else if($cmd == "unsilence") {
|
||||||
|
if($user->canModerate()) {
|
||||||
|
} else Message::PrivateBotMessage(MSG_ERROR, "cmdna", ["/unsilence"], $user);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else return false;*/
|
||||||
|
}
|
||||||
|
}
|
80
mods/generic_mod.php
Normal file
80
mods/generic_mod.php
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
<?php
|
||||||
|
namespace sockchat\mods;
|
||||||
|
|
||||||
|
// TODO finish generic mod interface
|
||||||
|
|
||||||
|
abstract class GenericMod {
|
||||||
|
public static function Init() {} // called on server startup, do initialization work here
|
||||||
|
|
||||||
|
public static function OnUserJoin($user) {} // called when a user attempts to join the chat
|
||||||
|
public static function AfterUserJoin($user) {} // called after a user successfully joins the chat
|
||||||
|
|
||||||
|
public static function OnUserLeave($user) {} // called when a user leaves the chat
|
||||||
|
public static function OnUserModify($user) {} // called when a user is modified
|
||||||
|
|
||||||
|
public static function OnUserKick($user, $by, &$duration, &$banip) {} // called when a user is about to be kicked or banned (delimited in type)
|
||||||
|
public static function AfterUserKick($user, $by, $duration, $banip) {} // called when a user
|
||||||
|
|
||||||
|
public static function OnBanIP(&$ip, &$duration, $by) {} // called when an ip is banned
|
||||||
|
public static function AfterBanIP($ip, $duration, $by) {} // called after an ip is successfully banned
|
||||||
|
|
||||||
|
public static function OnUnban(&$ip, &$id, &$name, $by) {}
|
||||||
|
public static function AfterUnban($ip, $id, $name, $by) {}
|
||||||
|
|
||||||
|
public static function OnChannelSwitch($user, $to, $from) {} // called when a user switches channels
|
||||||
|
public static function AfterChannelSwitch($user, $to, $from) {} // called after a successful channel switch
|
||||||
|
|
||||||
|
public static function OnMessageReceive($user, &$msg) {} // called when a message is received by the server
|
||||||
|
public static function AfterMessageReceived($user, $msg) {} // called after a message is successfully processed by the server
|
||||||
|
|
||||||
|
public static function OnCommandReceive($user, $cmd, $args) {} // called when a command is received by the server
|
||||||
|
// !!! DO NOT !!! USE OnCommandReceived OR AfterCommandReceived TO HANDLE
|
||||||
|
// PARSING COMMANDS IN YOUR MOD. INSTEAD USE THE AddCommandHook FUNCTION;
|
||||||
|
// THESE FUNCTIONS SHOULD ONLY BE USED IN CIRCUMSTANCES WHERE YOU NEED TO
|
||||||
|
// OVERRIDE THE DEFAULT BEHAVIOUR OF THE COMMAND PARSING ENGINE.
|
||||||
|
// For more info on AddCommandHook refer to TODO wiki link
|
||||||
|
public static function AfterCommandReceived($user, $cmd, $args) {} // called after a command is successfully executed
|
||||||
|
|
||||||
|
public static function OnPacketReceive($conn, &$pid, &$data) {} // called when a packet is received by the server
|
||||||
|
public static function AfterPacketReceived($conn, $pid, $data) {} // called after a packet is successfully processed by the server
|
||||||
|
|
||||||
|
public static function OnChannelCreate($channel) {} // called when a channel is created
|
||||||
|
public static function AfterChannelCreate($channel) {} // called after a channel is successfully create
|
||||||
|
|
||||||
|
public static function OnChannelModify($old, $new) {} // called when a channel is modified
|
||||||
|
public static function AfterChannelModify($old, $new) {} // called after a channel is successfully modified
|
||||||
|
|
||||||
|
public static function OnChannelDelete($channel) {} // called when a channel is deleted
|
||||||
|
public static function AfterChannelDelete($channel) {} // called after a channel is successfully deleted
|
||||||
|
|
||||||
|
public static function OnMessageLog($user, &$msg, $channel, &$flags) {}
|
||||||
|
public static function AfterMessageLog($user, $msg, $channel, $flags) {}
|
||||||
|
|
||||||
|
protected static $cmdHooks = [];
|
||||||
|
|
||||||
|
public static function GetModFolder($namespace) {
|
||||||
|
return "./mods/". substr($namespace, strrpos($namespace, "\\")+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function AddCommandHook($cmd, $functionName) {
|
||||||
|
if(!is_array($cmd)) $cmd = [$cmd];
|
||||||
|
foreach($cmd as $str)
|
||||||
|
self::$cmdHooks[$str] = $functionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function ExecuteCommand($cmd, $args, $user, $namespace) {
|
||||||
|
if(array_key_exists($cmd, self::$cmdHooks)) {
|
||||||
|
call_user_func_array("{$namespace}::". self::$cmdHooks[$cmd], [$cmd, $user, $args]);
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function GetCommands() {
|
||||||
|
$retval = [];
|
||||||
|
|
||||||
|
foreach(self::$cmdHooks as $cmd => $func)
|
||||||
|
array_push($retval, $cmd);
|
||||||
|
|
||||||
|
return $retval;
|
||||||
|
}
|
||||||
|
}
|
7
phpstan.neon
Normal file
7
phpstan.neon
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
parameters:
|
||||||
|
level: 5
|
||||||
|
paths:
|
||||||
|
- lib
|
||||||
|
- mods
|
||||||
|
bootstrapFiles:
|
||||||
|
- server.php
|
159
server.php
Normal file
159
server.php
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
<?php
|
||||||
|
namespace sockchat;
|
||||||
|
require __DIR__ ."/vendor/autoload.php";
|
||||||
|
use \Ratchet\MessageComponentInterface;
|
||||||
|
use \Ratchet\ConnectionInterface;
|
||||||
|
use \Ratchet\Server\IoServer;
|
||||||
|
use \Ratchet\Http\HttpServer;
|
||||||
|
use \Ratchet\WebSocket\WsServer;
|
||||||
|
use React\Stream\Util;
|
||||||
|
|
||||||
|
mb_internal_encoding("UTF-8");
|
||||||
|
error_reporting(E_ERROR);
|
||||||
|
|
||||||
|
require_once("lib/constants.php");
|
||||||
|
require_once("config.php");
|
||||||
|
require_once("lib/utils.php");
|
||||||
|
require_once("lib/db.php");
|
||||||
|
require_once("lib/user.php");
|
||||||
|
require_once("lib/context.php");
|
||||||
|
require_once("lib/channel.php");
|
||||||
|
require_once("lib/msg.php");
|
||||||
|
require_once("lib/mods.php");
|
||||||
|
|
||||||
|
Modules::Load();
|
||||||
|
|
||||||
|
class Chat implements MessageComponentInterface {
|
||||||
|
public function __construct() {
|
||||||
|
Utils::$chat = $GLOBALS["chat"];
|
||||||
|
Database::Init();
|
||||||
|
Message::$bot = new User("-1", "", "ChatBot", "inherit", "", null);
|
||||||
|
|
||||||
|
Database::TruncateUserList();
|
||||||
|
Context::$channelList = array_merge([Utils::$chat["DEFAULT_CHANNEL"] => new Channel(Utils::SanitizeName(Utils::$chat["DEFAULT_CHANNEL"]), "", 0, null, CHANNEL_PERM, Database::FetchBacklog(DEFAULT_CHANNEL))], Database::GetAllChannels());
|
||||||
|
Context::$bannedUsers = Database::GetAllBans();
|
||||||
|
|
||||||
|
echo "Server started.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onOpen(ConnectionInterface $conn) {
|
||||||
|
$conn->remoteAddress = $conn->WebSocket->request->getHeader('X-Real-IP') ?? $conn->remoteAddress;
|
||||||
|
Context::CheckPings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onMessage(ConnectionInterface $conn, $msg) {
|
||||||
|
$conn->remoteAddress = $conn->WebSocket->request->getHeader('X-Real-IP') ?? $conn->remoteAddress;
|
||||||
|
Context::CheckPings();
|
||||||
|
if(true) {
|
||||||
|
$parts = explode(Utils::$separator, $msg);
|
||||||
|
$id = $parts[0];
|
||||||
|
$parts = array_slice($parts, 1);
|
||||||
|
|
||||||
|
if(!Modules::ExecuteRoutine("OnPacketReceive", [$conn, &$id, &$parts])) return;
|
||||||
|
|
||||||
|
switch($id) {
|
||||||
|
case 0:
|
||||||
|
if(($u = Context::GetUserByID($parts[0])) != null) {
|
||||||
|
$u->ping = gmdate("U");
|
||||||
|
$conn->send(Utils::PackMessage(0, array("pong")));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if(!Context::DoesSockExist($conn)) {
|
||||||
|
$arglist = "";
|
||||||
|
for($i = 0; $i < count($parts); $i++)
|
||||||
|
$arglist .= "&arg". ($i+1) ."=". urlencode($parts[$i]);
|
||||||
|
$aparts = file_get_contents(Utils::$chat['CHATROOT'] ."/?view=auth". $arglist);
|
||||||
|
|
||||||
|
if(substr($aparts, 0, 3) == "yes") {
|
||||||
|
$aparts = explode("\n", mb_substr($aparts, 3));
|
||||||
|
$reason = Context::AllowUser($aparts[1], $conn);
|
||||||
|
if($reason === 0 || $reason === "userfail") {
|
||||||
|
if(($length = Context::CheckBan(Utils::$chat["AUTOID"] ? null : $aparts[0], $conn->remoteAddress, Utils::SanitizeName($aparts[1]))) === false) {
|
||||||
|
$id = 0;
|
||||||
|
if(Utils::$chat["AUTOID"]) {
|
||||||
|
for($i = 1;; $i++) {
|
||||||
|
if(Context::GetUserByID($i) == null) {
|
||||||
|
$id = "".$i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else $id = $aparts[0];
|
||||||
|
|
||||||
|
if($reason === "userfail")
|
||||||
|
$aparts[1] = $aparts[1] ."_". Utils::Romanize(rand(100, 1999));
|
||||||
|
|
||||||
|
Context::Join(new User($id, Utils::$chat["DEFAULT_CHANNEL"], Utils::SanitizeName($aparts[1]), $aparts[2], $aparts[3], $conn));
|
||||||
|
} else $conn->send(Utils::PackMessage(1, array("n", "joinfail", $length)));
|
||||||
|
} else $conn->send(Utils::PackMessage(1, array("n", $reason)));
|
||||||
|
} else $conn->send(Utils::PackMessage(1, array("n", "authfail")));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if(($user = Context::GetUserByID($parts[0])) != null) {
|
||||||
|
if($user->sock == $conn) {
|
||||||
|
if(trim($parts[1]) != "") {
|
||||||
|
$parts[1] = mb_substr(trim($parts[1]), 0, Utils::$chat["MAX_MSG_LEN"]);
|
||||||
|
//$parts[1] = str_replace("feel", "grip", $parts[1]);
|
||||||
|
if(trim($parts[1])[0] != "/") {
|
||||||
|
$out = Utils::Sanitize($parts[1]);
|
||||||
|
if(!Modules::ExecuteRoutine("OnMessageReceive", [$user, &$out])) return;
|
||||||
|
Message::BroadcastUserMessage($user, $out);
|
||||||
|
Modules::ExecuteRoutine("AfterMessageReceived", [$user, $out]);
|
||||||
|
} else {
|
||||||
|
//Database::Log(gmdate("U"), $user, Utils::Sanitize(trim($parts[1])));
|
||||||
|
|
||||||
|
$parts[1] = mb_substr(trim($parts[1]), 1);
|
||||||
|
$cmdparts = explode(" ", $parts[1]);
|
||||||
|
$cmd = strtolower(str_replace(".","",$cmdparts[0]));
|
||||||
|
$cmdparts = array_slice($cmdparts, 1);
|
||||||
|
for($i = 0; $i < count($cmdparts); $i++)
|
||||||
|
$cmdparts[$i] = Utils::Sanitize(trim($cmdparts[$i]));
|
||||||
|
|
||||||
|
if(!Modules::ExecuteRoutine("OnCommandReceive", [$user, &$cmd, &$cmdparts])) return;
|
||||||
|
if(Modules::ExecuteCommand($cmd, $user, $cmdparts))
|
||||||
|
Modules::ExecuteRoutine("AfterCommandReceived", [$user, $cmd, $cmdparts]);
|
||||||
|
else
|
||||||
|
Message::PrivateBotMessage(MSG_ERROR, "nocmd", [strtolower($cmd)], $user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Modules::ExecuteRoutine("AfterPacketReceived", [$conn, $id, $parts]);
|
||||||
|
} else
|
||||||
|
$conn->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onClose(ConnectionInterface $conn) {
|
||||||
|
$conn->remoteAddress = $conn->WebSocket->request->getHeader('X-Real-IP') ?? $conn->remoteAddress;
|
||||||
|
echo $conn->remoteAddress ." has disconnected\n";
|
||||||
|
foreach(Context::$onlineUsers as $user) {
|
||||||
|
if($user->sock == $conn) {
|
||||||
|
echo "found user ". $user->username .", dropped\n";
|
||||||
|
Context::Leave($user);
|
||||||
|
Modules::ExecuteRoutine("OnUserLeave", [$user]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Context::CheckPings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $err) {
|
||||||
|
$conn->remoteAddress = $conn->WebSocket->request->getHeader('X-Real-IP') ?? $conn->remoteAddress;
|
||||||
|
Context::CheckPings();
|
||||||
|
echo "Error on ". $conn->remoteAddress .": ". $err ."\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$server = IoServer::factory(
|
||||||
|
new HttpServer(
|
||||||
|
new WsServer(
|
||||||
|
new Chat()
|
||||||
|
)
|
||||||
|
),
|
||||||
|
$GLOBALS["chat"]["PORT"]
|
||||||
|
);
|
||||||
|
|
||||||
|
$server->run();
|
Loading…
Reference in a new issue