mirror of
https://github.com/Shadowss/TravianZ.git
synced 2026-09-10 20:47:14 +00:00
Added Chatbox
Added Chatbox
This commit is contained in:
+17
-26
@@ -1,20 +1,23 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
#################################################################################
|
#################################################################################
|
||||||
## ##
|
## -= YOU MAY NOT REMOVE OR CHANGE THIS NOTICE =- ##
|
||||||
## -= YOU MUST NOT REMOVE OR CHANGE THIS NOTICE =- ##
|
## --------------------------------------------------------------------------- ##
|
||||||
## ##
|
## Filename : admin.php ##
|
||||||
|
## Type : Admin Main Page (engine) ##
|
||||||
|
## --------------------------------------------------------------------------- ##
|
||||||
|
## Developed by : Dzoki ##
|
||||||
|
## Refactored by : Shadow ##
|
||||||
|
## Redesign by : Shadow ##
|
||||||
|
## --------------------------------------------------------------------------- ##
|
||||||
|
## Contact : cata7007@gmail.com ##
|
||||||
|
## Project : TravianZ ##
|
||||||
|
## URLs: : https://travianz.org ##
|
||||||
|
## GitHub : https://github.com/Shadowss/TravianZ ##
|
||||||
|
## --------------------------------------------------------------------------- ##
|
||||||
|
## License : TravianZ Project ##
|
||||||
|
## Copyright : TravianZ (c) 2010-2026. All rights reserved. ##
|
||||||
## --------------------------------------------------------------------------- ##
|
## --------------------------------------------------------------------------- ##
|
||||||
## ##
|
|
||||||
## Project: TravianZ ##
|
|
||||||
## Version: 05.03.2026 ##
|
|
||||||
## Filename: Admin/admin.php ##
|
|
||||||
## Developed by: Dzoki ##
|
|
||||||
## Refactored by: Shadow ##
|
|
||||||
## License: TravianZ Project ##
|
|
||||||
## Copyright: TravianZ (c) 2010-2026. All rights reserved. ##
|
|
||||||
## URLs: https://travianz.org ##
|
|
||||||
## https://github.com/Shadowss/TravianZ ##
|
|
||||||
## ##
|
|
||||||
#################################################################################
|
#################################################################################
|
||||||
|
|
||||||
// ─── SESSION ─────────────────────────────────────────────────────────────────
|
// ─── SESSION ─────────────────────────────────────────────────────────────────
|
||||||
@@ -176,11 +179,6 @@ if ($page !== '') {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'message':
|
case 'message':
|
||||||
// NOTE: original code had this case duplicated (second occurrence
|
|
||||||
// overrode with 'Search IGMs/Reports'). The first definition
|
|
||||||
// ('Players Message') is intentional for the ?p=message route.
|
|
||||||
// The 'Search IGMs/Reports' label belongs to ?p=search sub-section
|
|
||||||
// which is already covered by the search template include logic.
|
|
||||||
$subpage = ADMIN_PLAYERS_MESSAGE;
|
$subpage = ADMIN_PLAYERS_MESSAGE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -444,8 +442,6 @@ if ($page !== '') {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'userlogin':
|
case 'userlogin':
|
||||||
// SECURITY FIX: was raw mysqli_query with direct $_GET interpolation.
|
|
||||||
// Now uses admin_get_user_by_id() which internally uses a prepared statement.
|
|
||||||
$uid = admin_input_id($_GET, 'uid');
|
$uid = admin_input_id($_GET, 'uid');
|
||||||
if ($uid !== null) {
|
if ($uid !== null) {
|
||||||
$player = admin_get_user_by_id($uid);
|
$player = admin_get_user_by_id($uid);
|
||||||
@@ -458,7 +454,6 @@ if ($page !== '') {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'userillegallog':
|
case 'userillegallog':
|
||||||
// SECURITY FIX: same as userlogin above.
|
|
||||||
$uid = admin_input_id($_GET, 'uid');
|
$uid = admin_input_id($_GET, 'uid');
|
||||||
if ($uid !== null) {
|
if ($uid !== null) {
|
||||||
$player = admin_get_user_by_id($uid);
|
$player = admin_get_user_by_id($uid);
|
||||||
@@ -500,7 +495,6 @@ if ($page !== '') {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// ── Village-context pages (require a valid ?did=) ────────────────────
|
|
||||||
case 'village':
|
case 'village':
|
||||||
$did = admin_input_id($_GET, 'did');
|
$did = admin_input_id($_GET, 'did');
|
||||||
if ($did !== null) {
|
if ($did !== null) {
|
||||||
@@ -525,8 +519,6 @@ if ($page !== '') {
|
|||||||
$user = $database->getUserArray($village['owner'], 1);
|
$user = $database->getUserArray($village['owner'], 1);
|
||||||
$subpage = ADMIN_EDIT_RESOURCES . ' (' . e($village['name']) . ' » ' . e($user['username']) . ')';
|
$subpage = ADMIN_EDIT_RESOURCES . ' (' . e($village['name']) . ' » ' . e($user['username']) . ')';
|
||||||
} else {
|
} else {
|
||||||
// BUGFIX: original used $did which was only set in 'village' case,
|
|
||||||
// causing an undefined variable notice here. Now always defined above.
|
|
||||||
$subpage = ADMIN_EDIT_RESOURCES . $did . ' not found)';
|
$subpage = ADMIN_EDIT_RESOURCES . $did . ' not found)';
|
||||||
$village = null;
|
$village = null;
|
||||||
}
|
}
|
||||||
@@ -568,7 +560,6 @@ if ($page !== '') {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// ── Alliance-context pages (require a valid ?aid=) ───────────────────
|
|
||||||
case 'alliance':
|
case 'alliance':
|
||||||
$aid = admin_input_id($_GET, 'aid');
|
$aid = admin_input_id($_GET, 'aid');
|
||||||
if ($aid !== null) {
|
if ($aid !== null) {
|
||||||
|
|||||||
@@ -1227,7 +1227,13 @@ trait AutomationBattleResolution {
|
|||||||
*/
|
*/
|
||||||
private function applyOwnDefenceCasualties($data, $targettribe, $battlepart) {
|
private function applyOwnDefenceCasualties($data, $targettribe, $battlepart) {
|
||||||
global $database;
|
global $database;
|
||||||
|
|
||||||
|
$targettribe = (int)$targettribe;
|
||||||
|
// FIX: daca satul a fost sters / owner 0 / oaza fara trib, nu avem ce sterge
|
||||||
|
if($targettribe < 1 || $targettribe > 9) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
$owndead = [];
|
$owndead = [];
|
||||||
$unitlist = $database->getUnit($data['to'], false);
|
$unitlist = $database->getUnit($data['to'], false);
|
||||||
$start = ($targettribe - 1) * 10 + 1;
|
$start = ($targettribe - 1) * 10 + 1;
|
||||||
@@ -1319,7 +1325,7 @@ trait AutomationBattleResolution {
|
|||||||
} else {
|
} else {
|
||||||
$tribe = 4;
|
$tribe = 4;
|
||||||
}
|
}
|
||||||
|
if($tribe < 1 || $tribe > 9) $tribe = 4; // fallback la natura
|
||||||
$start = ($tribe - 1) * 10 + 1;
|
$start = ($tribe - 1) * 10 + 1;
|
||||||
$end = ($tribe * 10);
|
$end = ($tribe * 10);
|
||||||
unset($dead);
|
unset($dead);
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ include_once __DIR__ . '/Database/DatabaseHeroQueries.php';
|
|||||||
include_once __DIR__ . '/Database/DatabaseStatisticsQueries.php';
|
include_once __DIR__ . '/Database/DatabaseStatisticsQueries.php';
|
||||||
include_once __DIR__ . '/Database/DatabaseArtefactQueries.php';
|
include_once __DIR__ . '/Database/DatabaseArtefactQueries.php';
|
||||||
include_once __DIR__ . '/Database/DatabaseSystemQueries.php';
|
include_once __DIR__ . '/Database/DatabaseSystemQueries.php';
|
||||||
|
// Chat general (server-wide), cerut de Catalin 07.09.2026 - separat de alliance chat (chat.php)
|
||||||
|
include_once __DIR__ . '/Database/DatabaseGlobalChatQueries.php';
|
||||||
|
|
||||||
use App\Database\IDbConnection;
|
use App\Database\IDbConnection;
|
||||||
use App\Utils\Math;
|
use App\Utils\Math;
|
||||||
@@ -80,6 +82,7 @@ class MYSQLi_DB implements IDbConnection {
|
|||||||
use DatabaseStatisticsQueries;
|
use DatabaseStatisticsQueries;
|
||||||
use DatabaseArtefactQueries;
|
use DatabaseArtefactQueries;
|
||||||
use DatabaseSystemQueries;
|
use DatabaseSystemQueries;
|
||||||
|
use DatabaseGlobalChatQueries;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,176 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
#################################################################################
|
||||||
|
## -= YOU MAY NOT REMOVE OR CHANGE THIS NOTICE =- ##
|
||||||
|
## --------------------------------------------------------------------------- ##
|
||||||
|
## Project: TravianZ ##
|
||||||
|
## Filename: DatabaseGlobalChatQueries.php ##
|
||||||
|
## Purpose: Server-wide ("World Chat") messages + moderation ##
|
||||||
|
## (mute/block), separate from the existing alliance chat ##
|
||||||
|
## (chat table, see GameEngine/Chat.php). ##
|
||||||
|
## ##
|
||||||
|
## Cerut de Catalin, 07.09.2026: chat general vizibil/scris de orice ##
|
||||||
|
## jucator logat, cu moderare pentru Admin (access 9) si MH (access 8). ##
|
||||||
|
## ##
|
||||||
|
## License: TravianZ Project ##
|
||||||
|
## Copyright: TravianZ (c) 2010-2026. All rights reserved. ##
|
||||||
|
## URLs: https://travianz.org ##
|
||||||
|
## https://github.com/Shadowss/TravianZ ##
|
||||||
|
#################################################################################
|
||||||
|
|
||||||
|
trait DatabaseGlobalChatQueries {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Posteaza un mesaj in chat-ul general, daca userul nu e mutat si a
|
||||||
|
* trecut rate-limit-ul minim intre 2 mesaje (anti-spam simplu).
|
||||||
|
*
|
||||||
|
* @return array ['ok'=>bool, 'reason'=>string|null, 'mutedUntil'=>int|null]
|
||||||
|
*/
|
||||||
|
function postGlobalChatMessage($uid, $msg) {
|
||||||
|
$uid = (int) $uid;
|
||||||
|
$msg = trim((string) $msg);
|
||||||
|
|
||||||
|
if ($uid <= 0) {
|
||||||
|
return ['ok' => false, 'reason' => 'notloggedin'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($msg === '') {
|
||||||
|
return ['ok' => false, 'reason' => 'empty'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// acelasi plafon ca la chat-ul de alianta existent (chat.msg e varchar(255);
|
||||||
|
// 250 lasa loc de siguranta pentru escaping/multi-byte)
|
||||||
|
$msg = function_exists('mb_substr') ? mb_substr($msg, 0, 250) : substr($msg, 0, 250);
|
||||||
|
|
||||||
|
$mutedUntil = $this->getGlobalChatMuteStatus($uid);
|
||||||
|
if ($mutedUntil !== null) {
|
||||||
|
return ['ok' => false, 'reason' => 'muted', 'mutedUntil' => $mutedUntil];
|
||||||
|
}
|
||||||
|
|
||||||
|
list($euid) = $this->escape_input($uid);
|
||||||
|
|
||||||
|
$lastRow = mysqli_fetch_assoc($this->query(
|
||||||
|
"SELECT date FROM " . TB_PREFIX . "chat_global WHERE id_user = $euid ORDER BY id DESC LIMIT 1"
|
||||||
|
));
|
||||||
|
|
||||||
|
$now = time();
|
||||||
|
// rate-limit: minim 3 secunde intre 2 mesaje ale aceluiasi user
|
||||||
|
if ($lastRow && ($now - (int) $lastRow['date']) < 3) {
|
||||||
|
return ['ok' => false, 'reason' => 'ratelimit'];
|
||||||
|
}
|
||||||
|
|
||||||
|
list($emsg) = $this->escape_input($msg);
|
||||||
|
|
||||||
|
$this->query(
|
||||||
|
"INSERT INTO " . TB_PREFIX . "chat_global (id_user, date, msg) VALUES ($euid, $now, '$emsg')"
|
||||||
|
);
|
||||||
|
|
||||||
|
return ['ok' => true];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mesajele din chat-ul general, gata de afisat (ordine cronologica).
|
||||||
|
*
|
||||||
|
* Mod 1 - incarcare initiala: ultimele $limit mesaje ($sinceId = 0).
|
||||||
|
* Mod 2 - poll incremental: doar mesajele noi, mai mari decat $sinceId
|
||||||
|
* (evita re-trimiterea ferestrei intregi la fiecare interogare din JS).
|
||||||
|
*
|
||||||
|
* Tag-ul de alianta si nivelul de access sunt rezolvate live, printr-un
|
||||||
|
* singur JOIN (nu o interogare per mesaj) - reflecta mereu alianta
|
||||||
|
* curenta a jucatorului, nu una "inghetata" la momentul postarii.
|
||||||
|
*/
|
||||||
|
function getGlobalChatMessages($limit = 30, $sinceId = 0) {
|
||||||
|
$limit = (int) $limit;
|
||||||
|
$sinceId = (int) $sinceId;
|
||||||
|
|
||||||
|
$where = $sinceId > 0 ? "WHERE c.id > $sinceId" : "";
|
||||||
|
$order = $sinceId > 0 ? "ORDER BY c.id ASC" : "ORDER BY c.id DESC";
|
||||||
|
// plafon si pe polling-ul incremental (nu doar la incarcarea initiala) -
|
||||||
|
// daca un tab a stat minimizat/in background ore intregi (throttling de
|
||||||
|
// browser pe mobil), la revenire nu vrem un SELECT nemarginit
|
||||||
|
$limitSql = "LIMIT " . ($sinceId > 0 ? 200 : $limit);
|
||||||
|
|
||||||
|
$q = "SELECT c.id, c.id_user, c.date, c.msg, u.username, u.access, a.tag AS ally_tag, a.id AS ally_id
|
||||||
|
FROM " . TB_PREFIX . "chat_global c
|
||||||
|
LEFT JOIN " . TB_PREFIX . "users u ON u.id = c.id_user
|
||||||
|
LEFT JOIN " . TB_PREFIX . "alidata a ON a.id = u.alliance AND u.alliance > 0
|
||||||
|
$where
|
||||||
|
$order
|
||||||
|
$limitSql";
|
||||||
|
|
||||||
|
$rows = $this->mysqli_fetch_all($this->query($q));
|
||||||
|
|
||||||
|
// la incarcarea initiala am luat descrescator (ca sa prindem exact
|
||||||
|
// ultimele $limit), le intoarcem in ordine cronologica normala
|
||||||
|
if ($sinceId <= 0) {
|
||||||
|
$rows = array_reverse($rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timestamp-ul (unix) pana la care userul e mutat din chat-ul general,
|
||||||
|
* sau null daca nu e mutat (inclusiv daca o mutare veche a expirat).
|
||||||
|
*/
|
||||||
|
function getGlobalChatMuteStatus($uid) {
|
||||||
|
$uid = (int) $uid;
|
||||||
|
|
||||||
|
$row = mysqli_fetch_assoc($this->query(
|
||||||
|
"SELECT muted_until FROM " . TB_PREFIX . "chat_mutes WHERE id_user = $uid"
|
||||||
|
));
|
||||||
|
|
||||||
|
if (!$row) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$until = (int) $row['muted_until'];
|
||||||
|
return $until > time() ? $until : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Info despre viewer-ul curent, folosita de client ca sa stie daca
|
||||||
|
* afiseaza controalele de moderare si daca dezactiveaza inputul (mutat).
|
||||||
|
*/
|
||||||
|
function getGlobalChatViewerInfo($uid) {
|
||||||
|
$uid = (int) $uid;
|
||||||
|
|
||||||
|
$access = (int) $this->getUserField($uid, 'access', 0);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'uid' => $uid,
|
||||||
|
'access' => $access,
|
||||||
|
'isMod' => $access >= MULTIHUNTER,
|
||||||
|
'mutedUntil' => $this->getGlobalChatMuteStatus($uid),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Muteaza (temporar sau permanent, dupa $untilTimestamp) un user din
|
||||||
|
* chat-ul general. Ierarhia de rang (cine pe cine poate muta) se
|
||||||
|
* verifica in ajax.php inainte de a apela asta - metoda doar scrie
|
||||||
|
* starea, ca sa ramana usor de testat separat.
|
||||||
|
*/
|
||||||
|
function muteGlobalChatUser($targetUid, $mutedBy, $untilTimestamp, $reason = '') {
|
||||||
|
$targetUid = (int) $targetUid;
|
||||||
|
$mutedBy = (int) $mutedBy;
|
||||||
|
$untilTimestamp = (int) $untilTimestamp;
|
||||||
|
|
||||||
|
list($ereason) = $this->escape_input((string) $reason);
|
||||||
|
$now = time();
|
||||||
|
|
||||||
|
$q = "INSERT INTO " . TB_PREFIX . "chat_mutes (id_user, muted_until, muted_by, reason, created)
|
||||||
|
VALUES ($targetUid, $untilTimestamp, $mutedBy, '$ereason', $now)
|
||||||
|
ON DUPLICATE KEY UPDATE muted_until = $untilTimestamp, muted_by = $mutedBy, reason = '$ereason', created = $now";
|
||||||
|
|
||||||
|
return $this->query($q) ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function unmuteGlobalChatUser($targetUid) {
|
||||||
|
$targetUid = (int) $targetUid;
|
||||||
|
|
||||||
|
return $this->query(
|
||||||
|
"DELETE FROM " . TB_PREFIX . "chat_mutes WHERE id_user = $targetUid"
|
||||||
|
) ? true : false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4404,3 +4404,22 @@ tz_def('TRAIN_BONUS_ARTIFACT', 'Artifact bonus');
|
|||||||
tz_def('TRAIN_BONUS_HERO', 'Hero bonus');
|
tz_def('TRAIN_BONUS_HERO', 'Hero bonus');
|
||||||
tz_def('TRAIN_BONUS_ALLIANCE', 'Alliance bonus');
|
tz_def('TRAIN_BONUS_ALLIANCE', 'Alliance bonus');
|
||||||
tz_def('TRAIN_BONUS_FINAL', 'Training time');
|
tz_def('TRAIN_BONUS_FINAL', 'Training time');
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// GENERAL (SERVER-WIDE) CHAT - floating widget, footer.tpl + Templates/GlobalChat/widget.tpl
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
tz_def('GCHAT_TITLE', 'World Chat');
|
||||||
|
tz_def('GCHAT_PLACEHOLDER', 'Message...');
|
||||||
|
tz_def('GCHAT_SEND', 'Send');
|
||||||
|
tz_def('GCHAT_EMPTY', 'No messages yet.');
|
||||||
|
tz_def('GCHAT_MUTED', 'You are muted from chat.');
|
||||||
|
tz_def('GCHAT_RATELIMIT', 'You are sending messages too fast.');
|
||||||
|
tz_def('GCHAT_CONFIRM_BLOCK', 'Block this player from chat permanently?');
|
||||||
|
tz_def('GCHAT_MUTE_5M', 'Mute 5m');
|
||||||
|
tz_def('GCHAT_MUTE_30M', 'Mute 30m');
|
||||||
|
tz_def('GCHAT_MUTE_1H', 'Mute 1h');
|
||||||
|
tz_def('GCHAT_MUTE_24H', 'Mute 24h');
|
||||||
|
tz_def('GCHAT_BLOCK', 'Block');
|
||||||
|
tz_def('GCHAT_UNMUTE', 'Unmute');
|
||||||
|
tz_def('GCHAT_ADMIN_BADGE', 'Admin');
|
||||||
|
tz_def('GCHAT_MH_BADGE', 'MH');
|
||||||
|
|||||||
+19
-1
@@ -4141,4 +4141,22 @@ tz_def('SITTER_P_NOT_SITTING', 'Nu esti sitter pe niciun cont.');
|
|||||||
tz_def('TRAIN_BONUS_ARTIFACT', 'Bonus artefact');
|
tz_def('TRAIN_BONUS_ARTIFACT', 'Bonus artefact');
|
||||||
tz_def('TRAIN_BONUS_HERO', 'Bonus erou');
|
tz_def('TRAIN_BONUS_HERO', 'Bonus erou');
|
||||||
tz_def('TRAIN_BONUS_ALLIANCE', 'Bonus alianta');
|
tz_def('TRAIN_BONUS_ALLIANCE', 'Bonus alianta');
|
||||||
tz_def('TRAIN_BONUS_FINAL', 'Timp instruire');
|
tz_def('TRAIN_BONUS_FINAL', 'Timp instruire');
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CHAT GENERAL (server-wide) - widget plutitor, footer.tpl + Templates/GlobalChat/widget.tpl
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
tz_def('GCHAT_TITLE', 'Chat general');
|
||||||
|
tz_def('GCHAT_PLACEHOLDER', 'Mesaj...');
|
||||||
|
tz_def('GCHAT_SEND', 'Trimite');
|
||||||
|
tz_def('GCHAT_EMPTY', 'Niciun mesaj inca.');
|
||||||
|
tz_def('GCHAT_MUTED', 'Esti mutat din chat.');
|
||||||
|
tz_def('GCHAT_RATELIMIT', 'Trimiti mesaje prea repede.');
|
||||||
|
tz_def('GCHAT_CONFIRM_BLOCK', 'Blochezi permanent acest jucator din chat?');
|
||||||
|
tz_def('GCHAT_MUTE_5M', 'Mute 5m');
|
||||||
|
tz_def('GCHAT_MUTE_30M', 'Mute 30m');
|
||||||
|
tz_def('GCHAT_MUTE_1H', 'Mute 1h');
|
||||||
|
tz_def('GCHAT_MUTE_24H', 'Mute 24h');
|
||||||
|
tz_def('GCHAT_BLOCK', 'Blocheaza');
|
||||||
|
tz_def('GCHAT_UNMUTE', 'Anuleaza mute');
|
||||||
|
tz_def('GCHAT_ADMIN_BADGE', 'Admin');
|
||||||
|
tz_def('GCHAT_MH_BADGE', 'MH');
|
||||||
|
|||||||
@@ -0,0 +1,459 @@
|
|||||||
|
<?php
|
||||||
|
#################################################################################
|
||||||
|
## -= YOU MAY NOT REMOVE OR CHANGE THIS NOTICE =- ##
|
||||||
|
## --------------------------------------------------------------------------- ##
|
||||||
|
## Project: TravianZ ##
|
||||||
|
## Filename: Templates/GlobalChat/widget.tpl ##
|
||||||
|
## Purpose: Floating server-wide chat widget (bubble + panel), included ##
|
||||||
|
## from Templates/footer.tpl for any logged-in user. ##
|
||||||
|
## ##
|
||||||
|
## Cerut de Catalin, 07.09.2026: ##
|
||||||
|
## - chat general, vazut/scris de orice jucator logat ##
|
||||||
|
## - MH (access 8) si Admin (access 9) pot muta/bloca jucatori care ##
|
||||||
|
## vorbesc urat ##
|
||||||
|
## - MH si Admin evidentiati fata de restul jucatorilor ##
|
||||||
|
## - format afisare: [TAG] Nume, sau doar Nume daca nu are alianta ##
|
||||||
|
## ##
|
||||||
|
## Nu depinde de MooTools/jQuery (evita coliziuni cu $ / $$ din unx.js / ##
|
||||||
|
## mt-full.js) - JS vanilla, prefixat "gchat_" peste tot. ##
|
||||||
|
## ##
|
||||||
|
## License: TravianZ Project ##
|
||||||
|
## Copyright: TravianZ (c) 2010-2026. All rights reserved. ##
|
||||||
|
#################################################################################
|
||||||
|
?>
|
||||||
|
<div id="gchat_root" data-uid="<?php echo (int) $session->uid; ?>">
|
||||||
|
<button id="gchat_bubble" type="button" title="<?php echo GCHAT_TITLE; ?>">
|
||||||
|
💬<span id="gchat_badge" style="display:none">0</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div id="gchat_panel" style="display:none">
|
||||||
|
<div id="gchat_head">
|
||||||
|
<span><?php echo GCHAT_TITLE; ?></span>
|
||||||
|
<button id="gchat_close" type="button" title="×">×</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="gchat_messages"></div>
|
||||||
|
|
||||||
|
<div id="gchat_notice" style="display:none"></div>
|
||||||
|
|
||||||
|
<form id="gchat_form" autocomplete="off">
|
||||||
|
<input id="gchat_input" type="text" maxlength="250" placeholder="<?php echo GCHAT_PLACEHOLDER; ?>"/>
|
||||||
|
<button id="gchat_send_btn" type="submit" title="<?php echo GCHAT_SEND; ?>">►</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#gchat_root, #gchat_root * { box-sizing: border-box; }
|
||||||
|
|
||||||
|
#gchat_bubble {
|
||||||
|
position: fixed;
|
||||||
|
right: 18px;
|
||||||
|
bottom: 18px;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #4a7c2f;
|
||||||
|
border: 2px solid #d9e8c8;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,.35);
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 9998;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gchat_badge {
|
||||||
|
position: absolute;
|
||||||
|
top: -4px;
|
||||||
|
right: -4px;
|
||||||
|
background: #c0392b;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: 9px;
|
||||||
|
min-width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
line-height: 18px;
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gchat_panel {
|
||||||
|
position: fixed;
|
||||||
|
right: 18px;
|
||||||
|
bottom: 76px;
|
||||||
|
width: 300px;
|
||||||
|
max-width: calc(100vw - 36px);
|
||||||
|
height: 380px;
|
||||||
|
max-height: calc(100vh - 110px);
|
||||||
|
background: #fdfcf7;
|
||||||
|
border: 1px solid #ab9770;
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 0 4px 18px rgba(0,0,0,.4);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: 9999;
|
||||||
|
font-size: 12px;
|
||||||
|
font-family: Verdana, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gchat_head {
|
||||||
|
background: #6b8f47;
|
||||||
|
color: #fff;
|
||||||
|
padding: 6px 8px;
|
||||||
|
font-weight: bold;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gchat_head button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gchat_messages {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 6px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gchat_msg { margin-bottom: 6px; word-wrap: break-word; }
|
||||||
|
.gchat_time { color: #999; font-size: 10px; margin-right: 3px; }
|
||||||
|
.gchat_name { font-weight: bold; text-decoration: none; }
|
||||||
|
.gchat_name:hover { text-decoration: underline; }
|
||||||
|
.gchat_tag { font-weight: bold; text-decoration: none; }
|
||||||
|
.gchat_tag:hover { text-decoration: underline; }
|
||||||
|
.gchat_text { color: #222; }
|
||||||
|
|
||||||
|
/* evidentiere MH / Admin fata de restul jucatorilor */
|
||||||
|
.gchat_name_admin, .gchat_tag_admin { color: #b8290a; }
|
||||||
|
.gchat_name_mh, .gchat_tag_mh { color: #1a5aa8; }
|
||||||
|
.gchat_badge_role {
|
||||||
|
font-size: 9px;
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 0 3px;
|
||||||
|
margin-right: 3px;
|
||||||
|
color: #fff;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.gchat_badge_role.admin { background: #b8290a; }
|
||||||
|
.gchat_badge_role.mh { background: #1a5aa8; }
|
||||||
|
|
||||||
|
.gchat_mod_actions { margin-left: 4px; }
|
||||||
|
.gchat_mod_actions a {
|
||||||
|
font-size: 10px;
|
||||||
|
color: #888;
|
||||||
|
text-decoration: none;
|
||||||
|
border-bottom: 1px dotted #aaa;
|
||||||
|
margin-right: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.gchat_mod_actions a:hover { color: #b8290a; }
|
||||||
|
|
||||||
|
#gchat_notice {
|
||||||
|
padding: 4px 8px;
|
||||||
|
background: #fff3cd;
|
||||||
|
color: #7a5c00;
|
||||||
|
border-top: 1px solid #e0d29a;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gchat_form {
|
||||||
|
display: flex;
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gchat_input {
|
||||||
|
flex: 1;
|
||||||
|
border: none;
|
||||||
|
padding: 6px 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gchat_send_btn {
|
||||||
|
border: none;
|
||||||
|
background: #6b8f47;
|
||||||
|
color: #fff;
|
||||||
|
width: 36px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gchat_input:disabled { background: #eee; color: #999; }
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
#gchat_panel { right: 8px; bottom: 66px; }
|
||||||
|
#gchat_bubble { right: 8px; bottom: 8px; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var GCHAT_TXT = {
|
||||||
|
muted: <?php echo json_encode(GCHAT_MUTED); ?>,
|
||||||
|
ratelimit: <?php echo json_encode(GCHAT_RATELIMIT); ?>,
|
||||||
|
empty: <?php echo json_encode(GCHAT_EMPTY); ?>,
|
||||||
|
confirmBlock: <?php echo json_encode(GCHAT_CONFIRM_BLOCK); ?>,
|
||||||
|
mute5: <?php echo json_encode(GCHAT_MUTE_5M); ?>,
|
||||||
|
mute30: <?php echo json_encode(GCHAT_MUTE_30M); ?>,
|
||||||
|
mute60: <?php echo json_encode(GCHAT_MUTE_1H); ?>,
|
||||||
|
mute1440: <?php echo json_encode(GCHAT_MUTE_24H); ?>,
|
||||||
|
block: <?php echo json_encode(GCHAT_BLOCK); ?>,
|
||||||
|
unmute: <?php echo json_encode(GCHAT_UNMUTE); ?>,
|
||||||
|
adminBadge: <?php echo json_encode(GCHAT_ADMIN_BADGE); ?>,
|
||||||
|
mhBadge: <?php echo json_encode(GCHAT_MH_BADGE); ?>
|
||||||
|
};
|
||||||
|
|
||||||
|
var ACCESS_ADMIN = 9, ACCESS_MH = 8;
|
||||||
|
var AJAX_URL = 'ajax.php';
|
||||||
|
|
||||||
|
var root = document.getElementById('gchat_root');
|
||||||
|
if (!root) { return; }
|
||||||
|
|
||||||
|
var bubble = document.getElementById('gchat_bubble');
|
||||||
|
var badge = document.getElementById('gchat_badge');
|
||||||
|
var panel = document.getElementById('gchat_panel');
|
||||||
|
var closeBtn = document.getElementById('gchat_close');
|
||||||
|
var messagesBox = document.getElementById('gchat_messages');
|
||||||
|
var notice = document.getElementById('gchat_notice');
|
||||||
|
var form = document.getElementById('gchat_form');
|
||||||
|
var input = document.getElementById('gchat_input');
|
||||||
|
|
||||||
|
var myUid = parseInt(root.getAttribute('data-uid'), 10) || 0;
|
||||||
|
var lastId = 0;
|
||||||
|
var unread = 0;
|
||||||
|
var isOpen = false;
|
||||||
|
var viewerIsMod = false;
|
||||||
|
var pollTimer = null;
|
||||||
|
|
||||||
|
function fmtTime(unixTs) {
|
||||||
|
var d = new Date(unixTs * 1000);
|
||||||
|
function pad(n) { return (n < 10 ? '0' : '') + n; }
|
||||||
|
return pad(d.getHours()) + ':' + pad(d.getMinutes());
|
||||||
|
}
|
||||||
|
|
||||||
|
function showNotice(text) {
|
||||||
|
notice.textContent = text;
|
||||||
|
notice.style.display = text ? 'block' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeForLog(s) { return s; } // (folosim textContent peste tot mai jos - nu innerHTML pe input de utilizator)
|
||||||
|
|
||||||
|
function renderModActions(row) {
|
||||||
|
if (!viewerIsMod || row.id_user === myUid || (row.access || 0) >= ACCESS_MH) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var span = document.createElement('span');
|
||||||
|
span.className = 'gchat_mod_actions';
|
||||||
|
|
||||||
|
function actionLink(label, handler) {
|
||||||
|
var a = document.createElement('a');
|
||||||
|
a.textContent = label;
|
||||||
|
a.addEventListener('click', handler);
|
||||||
|
span.appendChild(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
actionLink(GCHAT_TXT.mute5, function () { doModerate('gchat_mute', row.id_user, 5); });
|
||||||
|
actionLink(GCHAT_TXT.mute30, function () { doModerate('gchat_mute', row.id_user, 30); });
|
||||||
|
actionLink(GCHAT_TXT.mute60, function () { doModerate('gchat_mute', row.id_user, 60); });
|
||||||
|
actionLink(GCHAT_TXT.mute1440, function () { doModerate('gchat_mute', row.id_user, 1440); });
|
||||||
|
actionLink(GCHAT_TXT.block, function () {
|
||||||
|
if (window.confirm(GCHAT_TXT.confirmBlock)) {
|
||||||
|
doModerate('gchat_block', row.id_user, 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
actionLink(GCHAT_TXT.unmute, function () { doModerate('gchat_unmute', row.id_user, 0); });
|
||||||
|
|
||||||
|
return span;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderMessage(row) {
|
||||||
|
var line = document.createElement('div');
|
||||||
|
line.className = 'gchat_msg';
|
||||||
|
|
||||||
|
var time = document.createElement('span');
|
||||||
|
time.className = 'gchat_time';
|
||||||
|
time.textContent = fmtTime(row.date);
|
||||||
|
line.appendChild(time);
|
||||||
|
|
||||||
|
var access = row.access || 0;
|
||||||
|
var roleClass = access >= ACCESS_ADMIN ? 'admin' : (access >= ACCESS_MH ? 'mh' : '');
|
||||||
|
|
||||||
|
if (roleClass) {
|
||||||
|
var roleBadge = document.createElement('span');
|
||||||
|
roleBadge.className = 'gchat_badge_role ' + roleClass;
|
||||||
|
roleBadge.textContent = roleClass === 'admin' ? GCHAT_TXT.adminBadge : GCHAT_TXT.mhBadge;
|
||||||
|
line.appendChild(roleBadge);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row.ally_tag) {
|
||||||
|
var tagLink = document.createElement('a');
|
||||||
|
tagLink.className = 'gchat_tag' + (roleClass ? ' gchat_tag_' + roleClass : '');
|
||||||
|
tagLink.href = 'allianz.php?aid=' + encodeURIComponent(row.ally_id);
|
||||||
|
tagLink.textContent = '[' + row.ally_tag + ']';
|
||||||
|
line.appendChild(tagLink);
|
||||||
|
line.appendChild(document.createTextNode(' '));
|
||||||
|
}
|
||||||
|
|
||||||
|
var nameLink = document.createElement('a');
|
||||||
|
nameLink.className = 'gchat_name' + (roleClass ? ' gchat_name_' + roleClass : '');
|
||||||
|
nameLink.href = 'spieler.php?uid=' + encodeURIComponent(row.id_user);
|
||||||
|
nameLink.textContent = row.username || ('#' + row.id_user);
|
||||||
|
line.appendChild(nameLink);
|
||||||
|
|
||||||
|
line.appendChild(document.createTextNode(': '));
|
||||||
|
|
||||||
|
var text = document.createElement('span');
|
||||||
|
text.className = 'gchat_text';
|
||||||
|
text.textContent = row.msg;
|
||||||
|
line.appendChild(text);
|
||||||
|
|
||||||
|
var mod = renderModActions(row);
|
||||||
|
if (mod) { line.appendChild(mod); }
|
||||||
|
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendMessages(rows) {
|
||||||
|
var wasAtBottom = messagesBox.scrollTop + messagesBox.clientHeight >= messagesBox.scrollHeight - 4;
|
||||||
|
|
||||||
|
rows.forEach(function (row) {
|
||||||
|
messagesBox.appendChild(renderMessage(row));
|
||||||
|
lastId = Math.max(lastId, row.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (rows.length && (wasAtBottom || !isOpen)) {
|
||||||
|
messagesBox.scrollTop = messagesBox.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rows.length && !isOpen) {
|
||||||
|
unread += rows.length;
|
||||||
|
badge.textContent = unread > 99 ? '99+' : String(unread);
|
||||||
|
badge.style.display = 'inline-block';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyViewerState(viewer) {
|
||||||
|
viewerIsMod = !!viewer.isMod;
|
||||||
|
|
||||||
|
if (viewer.mutedUntil) {
|
||||||
|
input.disabled = true;
|
||||||
|
var mins = Math.max(1, Math.ceil((viewer.mutedUntil - Date.now() / 1000) / 60));
|
||||||
|
showNotice(GCHAT_TXT.muted + ' (' + mins + 'm)');
|
||||||
|
} else {
|
||||||
|
input.disabled = false;
|
||||||
|
if (notice.textContent && notice.textContent.indexOf(GCHAT_TXT.muted) === 0) {
|
||||||
|
showNotice('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function poll() {
|
||||||
|
fetch(AJAX_URL + '?f=gchat_poll&sinceId=' + lastId, { credentials: 'same-origin' })
|
||||||
|
.then(function (r) { return r.json(); })
|
||||||
|
.then(function (data) {
|
||||||
|
if (!data || !data.ok) { return; }
|
||||||
|
appendMessages(data.messages || []);
|
||||||
|
applyViewerState(data.viewer || {});
|
||||||
|
})
|
||||||
|
.catch(function () { /* hiccup de retea - reincercam la urmatorul tick */ });
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadInitial() {
|
||||||
|
fetch(AJAX_URL + '?f=gchat_poll&sinceId=0', { credentials: 'same-origin' })
|
||||||
|
.then(function (r) { return r.json(); })
|
||||||
|
.then(function (data) {
|
||||||
|
if (!data || !data.ok) { return; }
|
||||||
|
messagesBox.innerHTML = '';
|
||||||
|
if (!data.messages || !data.messages.length) {
|
||||||
|
var empty = document.createElement('div');
|
||||||
|
empty.style.color = '#999';
|
||||||
|
empty.textContent = GCHAT_TXT.empty;
|
||||||
|
messagesBox.appendChild(empty);
|
||||||
|
}
|
||||||
|
appendMessages(data.messages || []);
|
||||||
|
applyViewerState(data.viewer || {});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function doModerate(action, targetUid, minutes) {
|
||||||
|
var body = 'target=' + encodeURIComponent(targetUid);
|
||||||
|
if (minutes) { body += '&minutes=' + encodeURIComponent(minutes); }
|
||||||
|
|
||||||
|
fetch(AJAX_URL + '?f=' + action, {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'same-origin',
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||||
|
body: body
|
||||||
|
}).then(function (r) { return r.json(); })
|
||||||
|
.then(function (data) {
|
||||||
|
if (data && data.ok) {
|
||||||
|
poll();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function openPanel() {
|
||||||
|
isOpen = true;
|
||||||
|
panel.style.display = 'flex';
|
||||||
|
unread = 0;
|
||||||
|
badge.style.display = 'none';
|
||||||
|
messagesBox.scrollTop = messagesBox.scrollHeight;
|
||||||
|
input.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function closePanel() {
|
||||||
|
isOpen = false;
|
||||||
|
panel.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
bubble.addEventListener('click', function () {
|
||||||
|
if (isOpen) { closePanel(); } else { openPanel(); }
|
||||||
|
});
|
||||||
|
|
||||||
|
closeBtn.addEventListener('click', closePanel);
|
||||||
|
|
||||||
|
form.addEventListener('submit', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var msg = input.value.trim();
|
||||||
|
if (!msg || input.disabled) { return; }
|
||||||
|
|
||||||
|
fetch(AJAX_URL + '?f=gchat_send', {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'same-origin',
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||||
|
body: 'msg=' + encodeURIComponent(msg)
|
||||||
|
}).then(function (r) { return r.json(); })
|
||||||
|
.then(function (data) {
|
||||||
|
if (data && data.ok) {
|
||||||
|
input.value = '';
|
||||||
|
showNotice('');
|
||||||
|
poll();
|
||||||
|
} else if (data && data.reason === 'muted') {
|
||||||
|
applyViewerState({ isMod: viewerIsMod, mutedUntil: data.mutedUntil });
|
||||||
|
} else if (data && data.reason === 'ratelimit') {
|
||||||
|
showNotice(GCHAT_TXT.ratelimit);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
loadInitial();
|
||||||
|
pollTimer = window.setInterval(poll, 2500);
|
||||||
|
|
||||||
|
window.addEventListener('beforeunload', function () {
|
||||||
|
if (pollTimer) { window.clearInterval(pollTimer); }
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
+14
-1
@@ -91,4 +91,17 @@ $serverVersion = 'v.11 Full Refactor';
|
|||||||
<!-- Footer extra content -->
|
<!-- Footer extra content -->
|
||||||
<div id="cfoot"></div>
|
<div id="cfoot"></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Chat general (server-wide) - cerut de Catalin, 07.09.2026.
|
||||||
|
* footer.tpl e inclus si de pagini fara user logat (login/anmelden/logout/
|
||||||
|
* banned/maintenance) - widget-ul apare DOAR daca exista o sesiune valida
|
||||||
|
* si contul nu e blocat (access == BANNED inseamna cont blocat, nu "vizitator
|
||||||
|
* neautentificat" - ambele cazuri trebuie excluse aici).
|
||||||
|
*/
|
||||||
|
if (isset($session) && !empty($session->uid) && (!isset($session->access) || $session->access != BANNED)) {
|
||||||
|
include __DIR__ . '/GlobalChat/widget.tpl';
|
||||||
|
}
|
||||||
|
?>
|
||||||
@@ -107,5 +107,86 @@ switch(isset($_GET['f']) ? $_GET['f'] : '') {
|
|||||||
$ok = $database->setMovementMarker($_POST['moveid'] ?? 0, $_POST['marker'] ?? 0, $uid);
|
$ok = $database->setMovementMarker($_POST['moveid'] ?? 0, $_POST['marker'] ?? 0, $uid);
|
||||||
echo json_encode(['ok' => $ok ? 1 : 0]);
|
echo json_encode(['ok' => $ok ? 1 : 0]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// Chat general (server-wide), cerut de Catalin 07.09.2026. Separat de
|
||||||
|
// alliance chat (Chat.php, SAJAX) - vazut/scris de orice user logat,
|
||||||
|
// moderat de MH (access 8) si Admin (access 9).
|
||||||
|
case 'gchat_poll':
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
if (!isset($_SESSION)) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
include_once($autoprefix.'GameEngine/Database.php');
|
||||||
|
$uid = (int) ($_SESSION['id_user'] ?? 0);
|
||||||
|
if (!$uid) {
|
||||||
|
http_response_code(403);
|
||||||
|
echo json_encode(['ok' => 0, 'reason' => 'notloggedin']);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$sinceId = (int) ($_GET['sinceId'] ?? 0);
|
||||||
|
echo json_encode([
|
||||||
|
'ok' => 1,
|
||||||
|
'messages' => $database->getGlobalChatMessages(30, $sinceId),
|
||||||
|
'viewer' => $database->getGlobalChatViewerInfo($uid),
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'gchat_send':
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
if (!isset($_SESSION)) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
include_once($autoprefix.'GameEngine/Database.php');
|
||||||
|
$uid = (int) ($_SESSION['id_user'] ?? 0);
|
||||||
|
if (!$uid) {
|
||||||
|
http_response_code(403);
|
||||||
|
echo json_encode(['ok' => 0, 'reason' => 'notloggedin']);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
echo json_encode($database->postGlobalChatMessage($uid, $_POST['msg'] ?? ''));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'gchat_mute':
|
||||||
|
case 'gchat_block':
|
||||||
|
case 'gchat_unmute':
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
if (!isset($_SESSION)) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
include_once($autoprefix.'GameEngine/Database.php');
|
||||||
|
$modUid = (int) ($_SESSION['id_user'] ?? 0);
|
||||||
|
$targetUid = (int) ($_POST['target'] ?? 0);
|
||||||
|
|
||||||
|
if (!$modUid || !$targetUid) {
|
||||||
|
http_response_code(403);
|
||||||
|
echo json_encode(['ok' => 0, 'reason' => 'notloggedin']);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$modAccess = (int) $database->getUserField($modUid, 'access', 0);
|
||||||
|
$targetAccess = (int) $database->getUserField($targetUid, 'access', 0);
|
||||||
|
|
||||||
|
// doar MH (8) si Admin (9) modereaza chat-ul general, si nimeni nu
|
||||||
|
// poate modera pe cineva de rang egal sau mai mare - deci MH nu
|
||||||
|
// poate muta alt MH/Admin, iar Admin nu poate muta alt Admin
|
||||||
|
if ($modAccess < MULTIHUNTER || $targetAccess >= $modAccess) {
|
||||||
|
http_response_code(403);
|
||||||
|
echo json_encode(['ok' => 0, 'reason' => 'forbidden']);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_GET['f'] == 'gchat_unmute') {
|
||||||
|
$ok = $database->unmuteGlobalChatUser($targetUid);
|
||||||
|
} else {
|
||||||
|
$reason = substr((string) ($_POST['reason'] ?? ''), 0, 255);
|
||||||
|
// 'gchat_block' = permanent (~100 de ani); 'gchat_mute' = temporar, in minute (POST['minutes'])
|
||||||
|
$until = ($_GET['f'] == 'gchat_block')
|
||||||
|
? (time() + 3600 * 24 * 365 * 100)
|
||||||
|
: (time() + max(1, (int) ($_POST['minutes'] ?? 0)) * 60);
|
||||||
|
$ok = $database->muteGlobalChatUser($targetUid, $modUid, $until, $reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode(['ok' => $ok ? 1 : 0]);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -575,6 +575,41 @@ CREATE TABLE IF NOT EXISTS `%PREFIX%chat` (
|
|||||||
-- Dumping data for table `%prefix%chat`
|
-- Dumping data for table `%prefix%chat`
|
||||||
--
|
--
|
||||||
|
|
||||||
|
-- --------------------------------------------------------
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Chat general (server-wide), cerut de Catalin 07.09.2026 - separat de
|
||||||
|
-- chat-ul de alianta de mai sus (acela e filtrat pe `alli`, asta e vazut
|
||||||
|
-- de toti jucatorii de pe server, indiferent de alianta)
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `%PREFIX%chat_global` (
|
||||||
|
`id` int(20) NOT NULL AUTO_INCREMENT,
|
||||||
|
`id_user` int(11) NOT NULL,
|
||||||
|
`date` int(11) NOT NULL,
|
||||||
|
`msg` varchar(250) NOT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `id_user_date` (`id_user`,`date`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
|
-- --------------------------------------------------------
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Stare activa de mute/block pe chat-ul general - un singur rand per user
|
||||||
|
-- (id_user e PRIMARY KEY, deci un nou mute suprascrie direct pe cel vechi
|
||||||
|
-- via ON DUPLICATE KEY UPDATE, fara sa acumuleze istoric).
|
||||||
|
-- muted_until in viitorul indepartat (~100 ani) = block permanent.
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `%PREFIX%chat_mutes` (
|
||||||
|
`id_user` int(11) NOT NULL,
|
||||||
|
`muted_until` int(11) NOT NULL,
|
||||||
|
`muted_by` int(11) NOT NULL,
|
||||||
|
`reason` varchar(255) NULL,
|
||||||
|
`created` int(11) NOT NULL,
|
||||||
|
PRIMARY KEY (`id_user`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
-- --------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user