/* * CSB MOTD News * Copyright (C) 2026 counter-strike-boost.com * * Shows a news / changelog MOTD to players, but only when it actually changed. * The news file's first line carries a version number; that number is stored per * SteamID in an nVault, so a returning player is not shown the same news twice. * The MOTD itself is rendered from a local file (or a URL, if one is set). * Players can re-open the latest news any time with /news. * * 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 new const PLUGIN[] = "CSB MOTD News" new const VERSION[] = "1.0.0" new const AUTHOR[] = "counter-strike-boost.com" new g_pEnabled, g_pFile, g_pUrl, g_pTitle new g_iVault new g_iNewsVersion public plugin_init() { register_plugin(PLUGIN, VERSION, AUTHOR) g_pEnabled = register_cvar("csb_news_enabled", "1") g_pFile = register_cvar("csb_news_file", "csb_news.txt") g_pUrl = register_cvar("csb_news_url", "") g_pTitle = register_cvar("csb_news_title", "Server News") register_clcmd("say /news", "cmdNews") register_clcmd("say_team /news", "cmdNews") g_iVault = nvault_open("csb_news") if (g_iVault == INVALID_HANDLE) set_fail_state("Could not open csb_news nvault") g_iNewsVersion = readNewsVersion() } public plugin_end() { if (g_iVault != INVALID_HANDLE) nvault_close(g_iVault) } readNewsVersion() { new file[64] get_pcvar_string(g_pFile, file, charsmax(file)) if (!file[0] || !file_exists(file)) return 0 new f = fopen(file, "rt") if (!f) return 0 new line[64] fgets(f, line, charsmax(line)) fclose(f) trim(line) /* accept "version 7" or a bare "7" on the first line */ new digits[16], j = 0 for (new i = 0; line[i] && j < charsmax(digits); i++) { if (line[i] >= '0' && line[i] <= '9') digits[j++] = line[i] } digits[j] = 0 return str_to_num(digits) } public client_authorized(id) { if (!get_pcvar_num(g_pEnabled) || is_user_bot(id)) return if (g_iNewsVersion <= 0) return new authid[35] get_user_authid(id, authid, charsmax(authid)) new seen = getSeen(authid) if (g_iNewsVersion > seen) { set_task(3.0, "taskShow", id) setSeen(authid, g_iNewsVersion) } } getSeen(const authid[]) { new data[16], timestamp if (nvault_lookup(g_iVault, authid, data, charsmax(data), timestamp)) return str_to_num(data) return 0 } setSeen(const authid[], version) { new data[16] num_to_str(version, data, charsmax(data)) nvault_set(g_iVault, authid, data) } public taskShow(id) { if (is_user_connected(id)) showNews(id) } public cmdNews(id) { showNews(id) return PLUGIN_HANDLED } showNews(id) { new title[48] get_pcvar_string(g_pTitle, title, charsmax(title)) new url[128] get_pcvar_string(g_pUrl, url, charsmax(url)) if (url[0]) { show_motd(id, url, title) return } new file[64] get_pcvar_string(g_pFile, file, charsmax(file)) if (file[0] && file_exists(file)) { show_motd(id, file, title) } else { client_print(id, print_chat, "[CSB] No news file is configured.") } }