/*
* CSB Admin Tag
* Copyright (C) 2026 counter-strike-boost.com
*
* Prints a configurable coloured prefix (e.g. [ADMIN], [OWNER]) before an
* admin's chat line based on their access flags. Tags are read from
* configs/csb_tags.ini. The say/say_team message is intercepted and re-sent via
* the SayText user message so the *DEAD* marker and team colouring stay correct.
*
* Inspired by Admin Prefix by Alka. Independent GPL re-implementation; no
* original code is reused.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version. It is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See for details.
*/
#include
#include
#define MAX_TAGS 24
new const PLUGIN[] = "CSB Admin Tag"
new const VERSION[] = "1.0.0"
new const AUTHOR[] = "counter-strike-boost.com"
new g_pEnabled
new g_msgSayText
new g_iTagFlags[MAX_TAGS]
new g_szTagText[MAX_TAGS][24]
new g_iTagCount = 0
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
g_pEnabled = register_cvar("csb_tag_enabled", "1")
register_clcmd("say", "hookSay")
register_clcmd("say_team", "hookSayTeam")
g_msgSayText = get_user_msgid("SayText")
loadTags()
}
loadTags()
{
new dir[128]
get_localinfo("amxx_configsdir", dir, charsmax(dir))
new path[160]
formatex(path, charsmax(path), "%s/csb_tags.ini", dir)
if (!file_exists(path))
{
/* seed a sane default so admins have something to edit */
write_file(path, "; CSB Admin Tag - one rule per line: ")
write_file(path, "; flags use AMXX access letters; first matching line wins.")
write_file(path, "z [OWNER]")
write_file(path, "b [ADMIN]")
write_file(path, "c [MOD]")
}
new f = fopen(path, "rt")
if (!f)
{
log_amx("[CSB Tag] could not open %s", path)
return
}
new line[128], flagStr[32], tag[24]
while (!feof(f) && g_iTagCount < MAX_TAGS)
{
fgets(f, line, charsmax(line))
trim(line)
if (!line[0] || line[0] == ';' || line[0] == '#')
continue
strtok(line, flagStr, charsmax(flagStr), tag, charsmax(tag), ' ')
trim(tag)
if (!flagStr[0] || !tag[0])
continue
g_iTagFlags[g_iTagCount] = read_flags(flagStr)
copy(g_szTagText[g_iTagCount], charsmax(g_szTagText[]), tag)
g_iTagCount++
}
fclose(f)
log_amx("[CSB Tag] loaded %d tag rule(s).", g_iTagCount)
}
tagFor(id, out[], len)
{
out[0] = 0
new flags = get_user_flags(id)
for (new i = 0; i < g_iTagCount; i++)
{
if (flags & g_iTagFlags[i])
{
copy(out, len, g_szTagText[i])
return
}
}
}
public hookSay(id)
{
return handleChat(id, false)
}
public hookSayTeam(id)
{
return handleChat(id, true)
}
handleChat(id, bool:teamOnly)
{
if (!get_pcvar_num(g_pEnabled) || !is_user_connected(id))
return PLUGIN_CONTINUE
new tag[24]
tagFor(id, tag, charsmax(tag))
if (!tag[0])
return PLUGIN_CONTINUE
new msg[160]
read_args(msg, charsmax(msg))
remove_quotes(msg)
trim(msg)
if (!msg[0])
return PLUGIN_HANDLED
/* leave command-style lines to the plugins that own them */
if (msg[0] == '/' || msg[0] == '!')
return PLUGIN_CONTINUE
new name[32]
get_user_name(id, name, charsmax(name))
new bool:dead = !is_user_alive(id)
new senderTeam = get_user_team(id)
new out[192]
formatex(out, charsmax(out), "%s^x04 %s ^x03%s^x01 : %s",
dead ? "*DEAD* " : "", tag, name, msg)
new players[32], num, pid
get_players(players, num, "ch")
for (new i = 0; i < num; i++)
{
pid = players[i]
if (teamOnly)
{
/* team chat: same team, or dead players see dead team chat */
if (get_user_team(pid) != senderTeam)
continue
}
message_begin(MSG_ONE, g_msgSayText, _, pid)
write_byte(id)
write_string(out)
message_end()
}
return PLUGIN_HANDLED
}