This commit is contained in:
lifestorm
2024-08-05 18:40:29 +03:00
parent 9f505a0646
commit c6d9b6f580
8044 changed files with 1853472 additions and 21 deletions

View File

@@ -0,0 +1,174 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local f = string.format
VyHub = VyHub or {}
VyHub.Config = VyHub.Config or {}
VyHub.Lib = VyHub.Lib or {}
VyHub.ready = false
local vyhub_root = "vyhub"
local color_warn = Color(211, 120, 0)
local color_err = Color(255, 0, 0)
local color_green = Color(0, 255, 0)
function VyHub:msg(message, type)
type = type or "neutral"
// Remove color tags
message = string.gsub(message, "<([%l]+)>([^<]+)</%1>", "%2")
if type == "success" then
MsgC("[VyHub] ", color_green, message .. "\n")
elseif type == "error" then
MsgC("[VyHub] [ERROR] ", color_err, message .. "\n")
elseif type == "neutral" then
MsgC("[VyHub] ", color_white, message .. "\n")
elseif type == "warning" then
MsgC("[VyHub] [WARN] ", color_warn, message .. "\n")
elseif type == "debug" and VyHub.Config.debug then
MsgC("[VyHub] [Debug] ", color_white, message .. "\n")
end
end
VyHub:msg("Initializing...")
if SERVER then
addon_incomplete = false
if file.Exists( vyhub_root .. '/lang/en.json', "LUA") then
if file.Exists( vyhub_root .. '/config/sv_config.lua', "LUA") then
hook.Run("vyhub_loading_start")
-- libs
VyHub:msg("Loading lib files...")
local files = file.Find( vyhub_root .."/lib/*.lua", "LUA" )
for _, file in ipairs( files ) do
AddCSLuaFile( vyhub_root .. "/lib/" .. file )
include( vyhub_root .. "/lib/" .. file )
end
-- Shared Config
include( vyhub_root .. '/config/sh_config.lua' )
AddCSLuaFile( vyhub_root .. "/config/sh_config.lua" )
-- Language
VyHub:msg('Loading ' .. VyHub.Config.lang .. ' language...')
include( vyhub_root .. '/shared/sh_lang.lua' )
-- Config Files
VyHub:msg("Loading config files...")
include( vyhub_root .. '/config/sv_config.lua' )
-- Shared Files
VyHub:msg("Loading shared files...")
local files = file.Find( vyhub_root .."/shared/*.lua", "LUA" )
for _, file in ipairs( files ) do
AddCSLuaFile( vyhub_root .. "/shared/" .. file )
include( vyhub_root .. "/shared/" .. file )
end
-- Client Files
VyHub:msg("Loading client files...")
local files = file.Find( vyhub_root .."/client/*.lua", "LUA" )
for _, file in ipairs( files ) do
AddCSLuaFile( vyhub_root .."/client/" .. file )
end
-- Server Files
VyHub:msg("Loading server files...")
local files = file.Find( vyhub_root .. "/server/*.lua", "LUA" )
for _, file in ipairs( files ) do
include( vyhub_root .. "/server/" .. file )
end
game.ConsoleCommand("sv_hibernate_think 1\n")
file.CreateDir("vyhub")
VyHub.Config:load_cache_config()
timer.Simple(2, function()
hook.Run("vyhub_loading_finish")
end)
VyHub:msg("Finished loading!")
else
VyHub:msg("Could not find lua/vyhub/config/sv_config.lua. Please make sure it exists.", "error")
end
else
VyHub:msg("!!!", "error")
VyHub:msg("!!!", "error")
VyHub:msg("!!!", "error")
VyHub:msg("Could not find language files!!! Please make sure to download a correct vyhub-gmod release here: https://github.com/matbyte-com/vyhub-gmod/releases", "error")
VyHub:msg("Cannot proceed with initialization.", "error")
VyHub:msg("!!!", "error")
VyHub:msg("!!!", "error")
VyHub:msg("!!!", "error")
end
end
if CLIENT then
if file.Exists( vyhub_root .. '/shared/sh_lang.lua', "LUA") then
hook.Run("vyhub_loading_start")
-- libs
VyHub:msg("Loading lib files...")
local files = file.Find( vyhub_root .."/lib/*.lua", "LUA" )
for _, file in ipairs( files ) do
include( vyhub_root .. "/lib/" .. file )
end
-- Language
VyHub:msg('Loading language...')
include( vyhub_root .. '/shared/sh_lang.lua' )
-- Config Files
VyHub:msg("Loading config files...")
local files = file.Find( vyhub_root .."/config/*.lua", "LUA" )
for _, file in ipairs( files ) do
if not string.StartWith(file, 'sv_') then
include( vyhub_root .. "/config/" .. file )
end
end
-- Shared Files
VyHub:msg("Loading shared files...")
local files = file.Find( vyhub_root .."/shared/*.lua", "LUA" )
for _, file in ipairs( files ) do
include( vyhub_root .. "/shared/" .. file )
end
-- Client Files
VyHub:msg("Loading client files...")
local files = file.Find( vyhub_root .."/client/*.lua", "LUA" )
for _, file in ipairs( files ) do
include( vyhub_root .."/client/" .. file )
end
timer.Simple(2, function()
hook.Run("vyhub_loading_finish")
end)
VyHub:msg("Finished loading!")
else
VyHub:msg("!!!", "error")
VyHub:msg("!!!", "error")
VyHub:msg("!!!", "error")
VyHub:msg("VyHub not correctly loaded. Please check the server log.", "error")
VyHub:msg("!!!", "error")
VyHub:msg("!!!", "error")
VyHub:msg("!!!", "error")
end
end

View File

@@ -0,0 +1,34 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local f = string.format
local color_red = Color(255, 0, 0)
if VyHub.Config.chat_tags and not DarkRP then
hook.Add("OnPlayerChat", "vyhub_chattag_OnPlayerChat", function(ply, msg)
if IsValid(ply) then
local group = VyHub.Group:get(ply:GetUserGroup())
if group then
local teamcolor = team.GetColor(ply:Team())
local deadTag = ""
if not ply:Alive() then
deadTag = f("*%s* ", VyHub.lang.other.dead)
end
chat.AddText(VyHub.Util:hex2rgb(group.color), "[", group.name, "]", " ", color_red, deadTag, teamcolor, ply:Nick(), color_white, ": ", msg)
return true
end
end
end)
end

View File

@@ -0,0 +1,532 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local f = string.format
VyHub.Dashboard = VyHub.Dashboard or {}
VyHub.Dashboard.ui = VyHub.Dashboard.ui or nil
VyHub.Dashboard.html_ready = false
VyHub.Dashboard.html_generated = VyHub.Dashboard.html_generated or false
local dashboard_html = dashboard_html or "Loading, please try again. If this does not help, please ask the server owner to check for errors in the server console. In most cases, vyhub-gmod has not been downloaded correctly and is missing files."
function VyHub.Dashboard:create_ui()
VyHub.Dashboard.html_ready = false
local xsize = ScrW() - ScrW()/4
local ysize = ScrH() - ScrH()/4
local xpos = ScrW()/2 - xsize/2
local ypos = ScrH()/2 - ysize/2
local title = "VyHub Server-Dashboard"
local box_color = Color(94, 0, 0, 255)
VyHub.Dashboard.ui = vgui.Create("DFrame")
VyHub.Dashboard.ui:SetSize(xsize, ysize)
VyHub.Dashboard.ui:SetPos(xpos, ypos)
VyHub.Dashboard.ui:SetDraggable(true)
VyHub.Dashboard.ui:SetTitle(title)
VyHub.Dashboard.ui:SetDeleteOnClose(false)
function VyHub.Dashboard.ui.Paint(self, w, h)
surface.SetDrawColor(box_color)
surface.DrawRect(0, 0, w, 24)
end
VyHub.Dashboard.ui_html = vgui.Create("DHTML", VyHub.Dashboard.ui)
VyHub.Dashboard.ui_html:SetSize(xsize, ysize - 24)
VyHub.Dashboard.ui_html:SetPos(0, 24)
VyHub.Dashboard.ui_html:SetHTML(dashboard_html)
function VyHub.Dashboard.ui_html:OnDocumentReady()
MsgN("VyHub Dashboard: HTML Loaded")
VyHub.Dashboard.html_ready = true
VyHub.Dashboard.ui_html:RunJavascript('local_steamid64 = ' .. LocalPlayer():SteamID64())
end
VyHub.Dashboard.ui_html:AddFunction("vyhub", "warning_toggle", function (warning_id)
LocalPlayer():ConCommand(f("vh_warning_toggle %s", warning_id))
end)
VyHub.Dashboard.ui_html:AddFunction("vyhub", "warning_delete", function (warning_id)
LocalPlayer():ConCommand(f("vh_warning_delete %s", warning_id))
end)
VyHub.Dashboard.ui_html:AddFunction("vyhub", "ban_set_status", function (ban_id, status)
LocalPlayer():ConCommand(f("vh_ban_set_status %s %s", ban_id, status))
end)
VyHub.Dashboard.ui_html:AddFunction("vyhub", "warning_create", function (steamid, reason)
LocalPlayer():ConCommand(f('vh_warn %s "%s"', steamid, VyHub.Util:escape_concommand_str(reason)))
end)
VyHub.Dashboard.ui_html:AddFunction("vyhub", "ban_create", function (steamid, minutes, reason)
LocalPlayer():ConCommand(f('vh_ban %s "%s" "%s"', steamid, minutes, VyHub.Util:escape_concommand_str(reason)))
end)
end
function VyHub.Dashboard:load_html()
VyHub.Dashboard.html_generated = true
dashboard_html = [[
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://cdn.vyhub.net/assets/roboto-v30-latin/roboto-v30-latin.css">
<link rel="stylesheet" href="http://cdn.vyhub.net/assets/font-awesome-4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="http://cdn.vyhub.net/assets/bootswatch-darkly.min.css">
<style>
::selection {
background: #b5b5b5; /* WebKit/Blink Browsers */
}
body{
overflow-x: hidden;
overflow-y: scroll;
font-family: Roboto !important;
}
.vh-input {
background-color: #303030;
color: white;
height: 30px;
}
.nav-pills .active {
background-color: #303030;
font-weight: bold;
margin-left: 4px;
}
.user-tab {
padding: 3px 6px 3px 6px;
border-radius: 8px;
text-overflow: ellipsis;
overflow:hidden;
}
#user_name {
width: 50%;
text-overflow: ellipsis;
overflow: hidden;
}
</style>
</head>
<body>
<div class="row" style="margin: 10px">
<div class="col-xs-4 col-lg-3">
<div class="input-group">
<div class="input-group-addon"><i class="fa fa-search"></i></div>
<input style="height: 40px;" id="user_search" type="text" class="form-control vh-input" onclick="$('#user_search').val(''); generate_user_list();" onkeyup="generate_user_list()" >
</div>
<br/>
<ul class="nav nav-pills nav-stacked" id="user_list">
</ul>
</div>
<div class="col-xs-8 col-lg-9">
<div id="user_content_empty">
]] .. VyHub.lang.dashboard.select_user .. [[
</div>
<div class="tab-content" id="user_content" style="display: none;">
<h3 style="margin: 5px 0px 0px 0;">
<div class="row">
<div class="col-xs-9">
<span id="user_name">
<span class="label label-default" style="background-color: #5E0000; border-radius: .25em 0 0 .25em;">
<i class="fa fa-user"></i> &nbsp;<span id="user_content_name"></span>
</span>
<span class="label label-default" style="border-radius: 0 .25em .25em 0;">
<span id="user_content_username"></span>
</span>
</span>
</div>
<div class="col-xs-3">
<span id="user_memberships" class="pull-right">
</span>
</div>
</div>
</h3>
<hr/>
<h4><span class="label label-default"><i class="fa fa-exclamation-triangle"></i> &nbsp;]] .. VyHub.lang.other.warnings .. [[</span></h3>
<div class="row perm-warning_edit">
<div class="col-xs-10">
<input id="user_warn" type="text" class="form-control vh-input" onclick="$('#user_warn').val('');" placeholder="]] .. VyHub.lang.other.reason .. [[" />
</div>
<div class="col-xs-2" style="padding-left: 0;">
<button style="height: 30px;" onclick="create_warning()" class="btn btn-warning btn-xs btn-block"><i class="fa fa-exclamation-triangle"></i> &nbsp; ]] .. VyHub.lang.dashboard.action_warn .. [[</button>
</div>
</div>
<br/>
<table class="table table-condensed table-hover">
<tr>
<th width="10px"></th>
<th>]] .. VyHub.lang.other.reason .. [[</th>
<th>]] .. VyHub.lang.other.admin .. [[</th>
<th>]] .. VyHub.lang.other.date .. [[</th>
<th class="text-right">]] .. VyHub.lang.other.actions .. [[</th>
</tr>
<tbody id="user_content_warnings">
</tbody>
</table>
<div>
<span class="label label-success"><i class="fa fa-check"></i>&nbsp; ]] .. VyHub.lang.other.active .. [[</span>
<span class="label label-warning"><i class="fa fa-hourglass"></i>&nbsp; ]] .. VyHub.lang.other.inactive .. [[</span>
<span class="label label-default"><i class="fa fa-times"></i>&nbsp; ]] .. VyHub.lang.other.disabled .. [[</span>
</div>
<hr />
<h4><span class="label label-default"><i class="fa fa-gavel"></i> &nbsp;]] .. VyHub.lang.other.bans .. [[</span></h3>
<div class="row perm-ban_edit">
<div class="col-xs-8">
<input id="user_ban_reason" type="text" class="form-control vh-input" onclick="$('#user_ban_reason').val('');" placeholder="]] .. VyHub.lang.other.reason .. [[" />
</div>
<div class="col-xs-2" style="padding-left: 0;">
<input id="user_ban_minutes" type="text" class="form-control vh-input" onclick="$('#user_ban_minutes').val('');" placeholder="]] .. VyHub.lang.other.minutes .. [[" />
</div>
<div class="col-xs-2" style="padding-left: 0;">
<button style="height: 30px;" onclick="create_ban()" class="btn btn-danger btn-xs btn-block"><i class="fa fa-gavel"></i> &nbsp; ]] .. VyHub.lang.dashboard.action_ban .. [[</button>
</div>
</div>
<br/>
<table class="table table-condensed table-hover">
<tr>
<th width="10px"></th>
<th>]] .. VyHub.lang.other.reason .. [[</th>
<th>]] .. VyHub.lang.other.admin .. [[</th>
<th>]] .. VyHub.lang.other.date .. [[</th>
<th>]] .. VyHub.lang.other.minutes .. [[</th>
<th class="text-right">]] .. VyHub.lang.other.actions .. [[</th>
</tr>
<tbody id="user_content_bans">
</tbody>
</table>
<div>
<span class="label label-success"><i class="fa fa-check"></i>&nbsp; ]] .. VyHub.lang.other.active .. [[</span>
<span class="label label-info"><i class="fa fa-globe"></i>&nbsp; ]] .. VyHub.lang.other.active_global .. [[</span>
<span class="label label-warning"><i class="fa fa-times"></i>&nbsp; ]] .. VyHub.lang.other.unbanned .. [[</span>
<span class="label label-danger"><i class="fa fa-hourglass"></i>&nbsp; ]] .. VyHub.lang.other.inactive .. [[</span>
</div>
</div>
</div>
</div>
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.30.1/moment.min.js"></script>
<script>
var perms = null;
var users = [];
var users_by_id = {};
var current_user = null;
var local_steamid64 = null;
function escape(str) {
return $("<div>").text(str).html();
}
function format_date(iso_str) {
return moment(iso_str).format('YYYY-MM-DD HH:mm');
}
function load_data(new_data) {
users = new_data;
users_by_id = {};
new_data.forEach(function(user) {
users_by_id[user.id] = user;
});
generate_user_list()
}
function load_perms(new_perms) {
perms = new_perms;
}
function enforce_perms() {
if (perms == null) { return; }
Object.keys(perms).forEach(function(perm) {
var has_perm = perms[perm];
if (has_perm) {
$('.perm-' + perm).show();
} else {
$('.perm-' + perm).hide();
}
});
}
function generate_user_list() {
$('#user_list').html('');
var filter = null;
if ($('#user_search').val()) {
filter = $('#user_search').val().toLowerCase();
}
var ids = [];
var only_local_user = perms == null || (!perms.warning_show && !perms.ban_show);
users.forEach(function(user) {
var activity = user.activities[0];
if (activity == null) { return; }
if (only_local_user && user.identifier !== local_steamid64) { return; }
if (filter != null) {
if (activity.extra.Nickname.toLowerCase().indexOf(filter) == -1 && user.username.toLowerCase().indexOf(filter) == -1) {
return;
}
}
var color = 'white';
if (user.memberships.length > 0) {
color = user.memberships[0].group.color;
}
var warn_badge_color = ((user.warnings.length == 0) ? '#444' : "#f0ad4e");
var ban_badge_color = ((user.bans.length == 0) ? '#444' : "#d9534f");
$('#user_list').append(' \
<li class="user-tab" id="user_tab_' + user.id + '" onclick="generate_user_overview(\'' + user.id + '\')" style="cursor:pointer; color: ' + color + ';"> \
' + escape(activity.extra.Nickname) + ' \
<span class="badge pull-right" style="background-color: ' + ban_badge_color + ';">' + user.bans.length + ' <i class="fa fa-gavel"></i></span> \
<span class="badge pull-right" style="background-color: ' + warn_badge_color + '; margin-left: 3px; margin-right: 3px;">' + user.warnings.length + ' <i class="fa fa-exclamation-triangle"></i></span> \
</li> \
');
ids.push(user.id);
});
if (ids.length == 1) {
generate_user_overview(ids[0]);
} else if (ids.length == 0) {
$('#user_content_empty').show();
$('#user_content').hide();
}
}
function generate_user_overview(user_id) {
current_user = null;
$('#user_content_empty').hide();
$('#user_content').hide();
var user = users_by_id[user_id];
if (user == null) { return; }
var activity = user.activities[0];
if (activity == null) { return; }
current_user = user;
$('#user_content_name').text(activity.extra.Nickname);
$('#user_content_username').text(user.username);
if (activity.extra.Nickname === user.username) {
$('#user_content_username').hide();
} else {
$('#user_content_username').show();
}
$('.user-tab').removeClass("active");
$('#user_tab_' + user_id).addClass("active");
$('#user_content_warnings').html('');
user.warnings.forEach(function(warning) {
var row_class = "success";
if (warning.disabled) {
row_class = "active";
} else if (!warning.active) {
row_class = "warning";
}
$('#user_content_warnings').append(' \
<tr> \
<td class="' + row_class + '"></td> \
<td>' + escape(warning.reason) + '</td> \
<td>' + escape(warning.creator.username) + '</td> \
<td>' + format_date(warning.created_on) + '</td> \
<td class="text-right"> \
<button class="btn btn-default btn-xs perm-warning_edit" onclick="vyhub.warning_toggle(\'' + warning.id + '\')"><i class="fa fa-play"></i><i class="fa fa-pause"></i></button> \
<button class="btn btn-default btn-xs perm-warning_delete" onclick="vyhub.warning_delete(\'' + warning.id + '\')"><i class="fa fa-trash"></i></button> \
</td> \
</tr> \
');
});
$('#user_content_bans').html('');
user.bans.forEach(function(ban) {
var minutes = '∞';
if (ban.length != null) {
minutes = Math.round(ban.length/60);
}
var row_class = "success";
if (ban.status == "UNBANNED") {
row_class = "warning";
} else if (!ban.active) {
row_class = "danger";
} else if (ban.serverbundle == null) {
row_class = "info";
}
var actions = "";
if (ban.status == "ACTIVE") {
actions += '<button class="btn btn-default btn-xs perm-ban_edit" onclick="vyhub.ban_set_status(\'' + ban.id + '\', \'UNBANNED\')"><i class="fa fa-check"></i> &nbsp;]] .. VyHub.lang.other.unban .. [[</button>';
} else if (ban.status == "UNBANNED") {
actions += '<button class="btn btn-default btn-xs perm-ban_edit" onclick="vyhub.ban_set_status(\'' + ban.id + '\', \'ACTIVE\')"><i class="fa fa-gavel"></i> &nbsp;]] .. VyHub.lang.other.reban .. [[</button>';
}
$('#user_content_bans').append(' \
<tr> \
<td class="' + row_class + '"></td> \
<td>' + escape(ban.reason) + '</td> \
<td>' + escape(ban.creator.username) + '</td> \
<td>' + format_date(ban.created_on) + '</td> \
<td>' + minutes + '</td> \
<td class="text-right">' + actions + '</td> \
</tr> \
');
});
$('#user_memberships').html('');
user.memberships.forEach(function(membership) {
$('#user_memberships').append('<span class="label label-default" style="background-color: ' + membership.group.color + ';">' + membership.group.name + '</span>');
});
$('#user_content').show();
enforce_perms();
}
function reload_current_user() {
if (current_user != null) {
generate_user_overview(current_user.id);
}
}
function create_warning() {
if (current_user == null) {
return;
}
var reason = $('#user_warn').val();
vyhub.warning_create(current_user.identifier, reason);
$('#user_warn').val('');
}
function create_ban() {
if (current_user == null) {
return;
}
var reason = $('#user_ban_reason').val();
var minutes = $('#user_ban_minutes').val();
vyhub.ban_create(current_user.identifier, minutes, reason);
$('#user_ban_reason').val('');
$('#user_ban_minutes').val('');
}
</script>
</html>
]]
end
function VyHub.Dashboard:load_users(users_json)
VyHub.Dashboard.ui_html:RunJavascript("load_data(" .. users_json .. ");")
VyHub.Dashboard.ui_html:RunJavascript("reload_current_user();")
end
function VyHub.Dashboard:load_perms(perms_json)
VyHub.Dashboard.ui_html:RunJavascript("load_perms(" .. perms_json .. ");")
end
concommand.Add("vh_dashboard", function ()
if VyHub.Dashboard.ui == nil or not VyHub.Dashboard.ui:IsValid() then
VyHub.Dashboard:create_ui()
VyHub.Dashboard.ui:Show()
VyHub.Dashboard.ui:MakePopup()
else
-- VyHub.Dashboard:create_ui()
-- if VyHub.Dashboard.ui != nil and VyHub.Dashboard.ui:IsValid() and VyHub.Dashboard.ui:IsVisible() then
-- VyHub.Dashboard.ui:Hide()
-- else
VyHub.Dashboard.ui:Show()
VyHub.Dashboard.ui:MakePopup()
-- end
end
net.Start("vyhub_dashboard")
net.SendToServer()
end)
net.Receive("vyhub_dashboard", function()
local data_length = net.ReadUInt(16)
local data_raw = net.ReadData(data_length)
local perms_json = net.ReadString()
local users_json = util.Decompress(data_raw)
timer.Create("vyhub_dashboard_html_ready", 0.3, 20, function ()
if not VyHub.Dashboard.html_ready then
MsgN("VyHub Dashboard: Waiting for HTML to load.")
return
end
timer.Remove("vyhub_dashboard_html_ready")
VyHub.Dashboard:load_perms(perms_json)
VyHub.Dashboard:load_users(users_json)
end)
end)
net.Receive("vyhub_dashboard_reload", function()
if VyHub.Dashboard.ui and VyHub.Dashboard.ui:IsVisible() then
MsgN("Reloading dashboard data, because server told us.")
net.Start("vyhub_dashboard")
net.SendToServer()
end
end)
hook.Add("vyhub_lang_loaded", "vyhub_dashboard_vyhub_lang_loaded", function ()
VyHub.Dashboard:load_html()
end)
if VyHub.Dashboard.html_generated then
VyHub.Dashboard:load_html()
end

View File

@@ -0,0 +1,30 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
VyHub.groups_mapped = VyHub.groups_mapped or nil
net.Receive("vyhub_group_data", function()
local num = net.ReadUInt(8)
local groups_mapped_new = {}
for i=1, num do
-- Currently only the name and color of the group is transferred
local name_game = net.ReadString()
local name = net.ReadString()
local color = net.ReadString()
groups_mapped_new[name_game] = {
name = name,
color = color,
}
end
VyHub.groups_mapped = groups_mapped_new
end)

View File

@@ -0,0 +1,27 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local meta_ply = FindMetaTable("Player")
VyHub.user_id = VyHub.user_id or nil
function meta_ply:VyHubID()
if IsValid(self) then
if self == LocalPlayer() then
return VyHub.user_id
else
MsgN("ERROR: Cannot get VyHubID of other users on the client side.")
end
end
end
net.Receive("vyhub_user_id", function ()
VyHub.user_id = net.ReadString()
end)

View File

@@ -0,0 +1,26 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
-- Language used. Currently only languages in the "lang" directory are supported.
VyHub.Config.lang = "en"
-- Enable chat tags, not working together with DarkRP
VyHub.Config.chat_tags = true
-- Date format to use (for example in ban message)
-- See https://tieske.github.io/date/#dateObject.fmt
VyHub.Config.date_format = "%Y-%m-%d %H:%M:%S %z"
-- Time offset from UTC (+00:00)
-- Uses the timezone of the GMOD server when set to nil
-- Example: 2 for CEST, -5 for EST
VyHub.Config.time_offset = nil
-- Debug mode. Enable to see all API requests and responses
VyHub.Config.debug = false

View File

@@ -0,0 +1,70 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
-- VyHub Server Config
-- BEWARE: Additional config values can be set in data/vyhub/config.json with the `vh_config <key> <value>` console command.
-- The configuration in this file is overwritten by the configuration in data/vyhub/config.json
-- ONLY SET THE 3 FOLLOWING OPTIONS IF YOU KNOW WHAT YOU ARE DOING!
-- PLEASE FOLLOW THE INSTALLATION INSTRUCTIONS HERE: https://docs.vyhub.net/latest/game/gmod/#installation
VyHub.Config.api_url = "" -- https://api.vyhub.app/<name>/v1
VyHub.Config.api_key = "" -- Admin -> Settings -> Server -> Setup
VyHub.Config.server_id = "" -- Admin -> Settings -> Server -> Setup
-- Prevent script execution as reward
-- Rewards that want to execute a script will not work if this is enabled.
VyHub.Config.reward_disable_scripts = false
-- Whitelsit for executed reward commands
-- If this table has entries, only commands matching the given patterns are executed
-- Patterns: https://wiki.facepunch.com/gmod/Patterns
-- Example: { "^ulx adduser %l+ %l+$" } -> Allows a command like "ulx adduser username groupname"
VyHub.Config.reward_command_whitelist = {}
-- Player groups are checked every X seconds
VyHub.Config.player_refresh_time = 120
-- Groups are refreshed every X seconds
VyHub.Config.group_refresh_time = 300
-- Every X seconds, an advert message is shown.
VyHub.Config.advert_interval = 180
-- Printed before every advert line
VyHub.Config.advert_prefix = "[★] "
-- Disable group sync
VyHub.Config.group_disable_sync = false
-- Disable override of admin mod bans (ULX, SAM, ServerGuard, xAdmin, ...)
VyHub.Config.ban_disable_sync = false
-- Replace ULib ban list with VyHub bans
VyHub.Config.replace_ulib_bans = false
-- Commands that open the shop page
VyHub.Config.commands_shop = { '!shop' }
-- Commands that open the bans page
VyHub.Config.commands_bans = { '!bans' }
-- Commands that open the warnings page
VyHub.Config.commands_warnings = { '!warnings' }
-- Commands that open the news page
VyHub.Config.commands_news = { '!news' }
-- Commands that open the profile page of a user (Usage: !user <user>)
VyHub.Config.commands_profile = { '!user' }
-- Commands to warn a user (Usage: !warn <user> <reason>)
VyHub.Config.commands_warn = { '!warn' }
-- Commands to open the dashboard
VyHub.Config.commands_dashboard = { '!dashboard' }
-- Customize the ban message that banned players see when trying to connect
VyHub.Config.ban_message = ">>> Ban Message <<<" .. "\n\n"
.. VyHub.lang.other.reason .. ": %reason%" .. "\n"
.. VyHub.lang.other.ban_date .. ": %ban_date%" .. "\n"
.. VyHub.lang.other.unban_date .. ": %unban_date%" .. "\n"
.. VyHub.lang.other.admin .. ": %admin%" .. "\n"
.. VyHub.lang.other.id .. ": %id%" .. "\n\n"
.. VyHub.lang.other.unban_url .. ": %unban_url%" .. "\n\n"

View File

@@ -0,0 +1 @@
AA==

View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [2022] [Matbyte UG]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,14 @@
[![Translation Percentage](http://translate.matbyte.com/widgets/vyhub/-/vyhub-lang/svg-badge.svg)](http://translate.matbyte.com/engage/vyhub/)
# vyhub-lang
Translations for VyHub GMOD
Matbyte Translation Tool (MTT): https://translate.matbyte.com/projects/vyhub/vyhub-lang/
## Contribute
1. Create a MTT account (see above)
2. Contact us with your username and the language you wish to translate
3. After your account has been unlocked, translate as much as you want
4. Regularily check for new strings that need to be translated

View File

@@ -0,0 +1,63 @@
{
"ply": {
"no_permissions": "Du hast nicht die nötigen Berechtigungen dafür.",
"banned_self": "Du wurdest vom Server gebannt",
"group_changed": "Deine Gruppe wurde zu <green>%s</green> geändert.",
"error_get": "Spieler konnte nicht gefunden werden. Bitte probiere es später erneut.",
"banned": "Spieler <green>%s</green> wurde <red>%s gebannt</red>. Grund: <green>%s</green>",
"welcome": "Willkommen auf dem Server, <green>%s</green>"
},
"warning": {
"toggled": "Status der Verwarnung wurde geändert.",
"cmd_help": "<red>Ungültiger Spieler!</red> Verwendung: <green>!warn <nick> <reason></green>",
"create_error": "Fehler beim hinzufügen einer Verwarnung für <green>%s</green>: <red>%s</red>",
"deleted": "Verwarnung gelöscht.",
"received": "Du wurdest von <green>%s</green> verwarnt: <red>%s</red>",
"toggled_self": "Der Status einer deiner Verwarnungen wurde geändert. Du kannst deiner Verwarnungen unter !dashboard sehen.",
"user_warned": "<red>%s</red> wurde von <green>%s</green> verwarnt. Grund: <green>%s</green>",
"deleted_self": "Eine deiner Verwarnungen wurde gelöscht. Du kannst deiner Verwarnungen unter !dashboard sehen."
},
"ban": {
"status_changed": "Ban Status von %s erfolgreich auf %s gesetzt.",
"user_banned": "<red>%s</red> wurde von <green>%s</green> für <red>%s</red> vom Server gebannt. Grund: <green>%s</green>"
},
"rslots": {
"full": "Der Server ist voll",
"full_no_slot": "Der Server ist voll und du hast keinen Zugriff auf einen reservierten Slot",
"kick": "Ein Spieler mit einem reservierten Slot ist dem Server beigetreten und du wurdest gekickt um Platz zu schaffen"
},
"other": {
"id": "ID",
"reason": "Grund",
"dead": "TOT",
"ban_date": "Ban Datum",
"unban_date": "Ban Ende",
"admin": "Admin",
"unknown": "Unbekannt",
"minutes": "Minuten",
"x_minutes": "%i Minuten",
"permanently": "Permanent",
"unban_url": "Entbannungs URL",
"error_api": "Fehler bei der Kommunikation mit der API: %s",
"never": "Nie",
"active_global": "Aktiv (Global)",
"unban": "Entbannen",
"actions": "Aktionen",
"active": "Aktiv",
"inactive": "Inaktiv",
"disabled": "Deaktiviert",
"unbanned": "Entbannt",
"reban": "Bannen",
"warnings": "Verwarnungen",
"date": "Datum",
"warn": "Verwarnung",
"ban": "Bann",
"bans": "Banns",
"welcome": "Willkommen"
},
"dashboard": {
"select_user": "Bitte wähle einen Spieler aus.",
"action_ban": "Bannen",
"action_warn": "Verwarnen"
}
}

View File

@@ -0,0 +1,63 @@
{
"ply": {
"banned": "Player <green>%s</green> has been <red>banned for %s</red>. Reason: <green>%s</green>",
"no_permissions": "You don't have all required permissions.",
"banned_self": "You are banned from this server",
"group_changed": "Your group has been changed to <green>%s</green>.",
"error_get": "Could find player. Please try again.",
"welcome": "Welcome on the server, <green>%s</green>"
},
"warning": {
"user_warned": "<red>%s</red> has been warned by <green>%s</green>. Reason: <green>%s</green>",
"cmd_help": "<red>Invlid player!</red> Usage: <green>!warn <nick> <reason></green>",
"toggled_self": "The status of one of your warnings has been changed. Check your warnings with !dashboard.",
"create_error": "Error while adding warning for player <green>%s</green>: <red>%s</red>",
"toggled": "Warning status changed.",
"deleted": "Warning deleted.",
"deleted_self": "One of your warnings has been deleted. Check your warnings with !dashboard.",
"received": "You have received a warning by <green>%s</green>: <red>%s</red>"
},
"ban": {
"status_changed": "Successfully set ban status of %s to %s.",
"user_banned": "<red>%s</red> has been banned by <green>%s</green> for <red>%s</red>. Reason: <green>%s</green>"
},
"rslots": {
"kick": "A player with a reserved slot connected to the server and you got kicked to free up space",
"full": "The server is full",
"full_no_slot": "The server is full and you do not have access to a reserved slot"
},
"dashboard": {
"select_user": "Please select an user.",
"action_ban": "Ban",
"action_warn": "Warn"
},
"other": {
"active": "Active",
"active_global": "Active (Global)",
"inactive": "Inactive",
"disabled": "Disabled",
"unbanned": "Unbanned",
"id": "ID",
"reason": "Reason",
"dead": "DEAD",
"ban_date": "Ban date",
"unban_date": "Unban date",
"unban": "Unban",
"reban": "Reban",
"admin": "Admin",
"welcome": "Welcome",
"actions": "Actions",
"never": "Never",
"warnings": "Warnings",
"date": "Date",
"ban": "Ban",
"warn": "Warn",
"bans": "Bans",
"unknown": "Unknown",
"minutes": "Minutes",
"x_minutes": "%i Minutes",
"permanently": "Permanently",
"unban_url": "Unban URL",
"error_api": "Error while communicating with the API: %s"
}
}

View File

@@ -0,0 +1,63 @@
{
"ply": {
"banned": "Le joueur <green>%s</green> a été <red>banni pendant %s</red>. Raison : <green>%s</green>",
"no_permissions": "Vous ne disposez pas de toutes les permissions requises.",
"banned_self": "Vous êtes banni de ce serveur",
"error_get": "Pourrait trouver un joueur. Veuillez réessayer.",
"group_changed": "Votre groupe a été changé par <green>%s</green>.",
"welcome": "Bienvenue sur le serveur, <green>%s</green>"
},
"warning": {
"user_warned": "<red>%s</red> a été averti par <green>%s</green>. Raison : <green>%s</green>",
"toggled_self": "L'état de l'un de vos avertissements a été modifié. Vérifiez vos avertissements avec !dashboard.",
"create_error": "Erreur lors de l'ajout d'un avertissement pour le joueur <green>%s</green> : <red>%s</red>",
"deleted": "Avertissement supprimé.",
"deleted_self": "L'un de vos avertissements a été supprimé. Vérifiez vos avertissements avec !dashboard.",
"toggled": "L'état d'avertissement a changé.",
"cmd_help": "<red>Joueur invalide !</red> Utilisation : <green>!warn <joueur> <raison></green>",
"received": "Vous avez reçu un avertissement de <green>%s</green> : <red>%s</red>"
},
"ban": {
"status_changed": "Le statut de bannissement de %s a été défini avec succès sur %s.",
"user_banned": "<red>%s</red> a été banni par <green>%s</green> pour <red>%s</red>. Raison : <green>%s</green>"
},
"rslots": {
"full": "Le serveur est plein",
"full_no_slot": "Le serveur est plein et vous n'avez pas accès à un créneau réservé",
"kick": "Un joueur avec un emplacement réservé connecté au serveur et vous avez été expulsé pour libérer de l'espace"
},
"dashboard": {
"select_user": "Veuillez sélectionner un utilisateur.",
"action_warn": "Avertir",
"action_ban": "Bannir"
},
"other": {
"active": "Actif",
"active_global": "Actif (Global)",
"inactive": "Inactif",
"disabled": "Désactivé",
"unbanned": "Débannir",
"id": "ID",
"reason": "Raison",
"dead": "Mort",
"ban_date": "Date de bannissement",
"unban": "Débannir",
"unban_date": "Date de débannissement",
"reban": "Rebannir",
"admin": "Administrateur",
"actions": "Actions",
"never": "Jamais",
"warnings": "Avertissements",
"date": "Date",
"ban": "Bannir",
"warn": "Avertir",
"bans": "Bannissements",
"minutes": "Minutes",
"unknown": "Inconnu",
"x_minutes": "%i Minutes",
"unban_url": "URL de débannissement",
"permanently": "Permanent",
"error_api": "Erreur lors de la communication avec l'API : %s",
"welcome": "Bienvenue"
}
}

View File

@@ -0,0 +1,766 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
VyHub.Lib.date = VyHub.Lib.date or {}
---------------------------------------------------------------------------------------
-- Module for date and time calculations
--
-- Version 2.2
-- Copyright (C) 2005-2006, by Jas Latrix (jastejada@yahoo.com)
-- Copyright (C) 2013-2021, by Thijs Schreijer
-- Licensed under MIT, http://opensource.org/licenses/MIT
-- https://github.com/Tieske/date
-- Changes by Matbyte: New function setbias
--[[ CONSTANTS ]]--
local HOURPERDAY = 24
local MINPERHOUR = 60
local MINPERDAY = 1440 -- 24*60
local SECPERMIN = 60
local SECPERHOUR = 3600 -- 60*60
local SECPERDAY = 86400 -- 24*60*60
local TICKSPERSEC = 1000000
local TICKSPERDAY = 86400000000
local TICKSPERHOUR = 3600000000
local TICKSPERMIN = 60000000
local DAYNUM_MAX = 365242500 -- Sat Jan 01 1000000 00:00:00
local DAYNUM_MIN = -365242500 -- Mon Jan 01 1000000 BCE 00:00:00
local DAYNUM_DEF = 0 -- Mon Jan 01 0001 00:00:00
local _;
--[[ GLOBAL SETTINGS ]]--
local centuryflip = 0 -- year >= centuryflip == 1900, < centuryflip == 2000
--[[ LOCAL ARE FASTER ]]--
local type = type
local pairs = pairs
local error = error
local assert = assert
local tonumber = tonumber
local tostring = tostring
local string = string
local math = math
local os = os
local unpack = unpack or table.unpack
local setmetatable = setmetatable
local getmetatable = getmetatable
--[[ EXTRA FUNCTIONS ]]--
local fmt = string.format
local lwr = string.lower
local rep = string.rep
local len = string.len -- luacheck: ignore
local sub = string.sub
local gsub = string.gsub
local gmatch = string.gmatch or string.gfind
local find = string.find
local ostime = os.time
local osdate = os.date
local floor = math.floor
local ceil = math.ceil
local abs = math.abs
-- removes the decimal part of a number
local function fix(n) n = tonumber(n) return n and ((n > 0 and floor or ceil)(n)) end
-- returns the modulo n % d;
local function mod(n,d) return n - d*floor(n/d) end
-- is `str` in string list `tbl`, `ml` is the minimun len
local function inlist(str, tbl, ml, tn)
local sl = len(str)
if sl < (ml or 0) then return nil end
str = lwr(str)
for k, v in pairs(tbl) do
if str == lwr(sub(v, 1, sl)) then
if tn then tn[0] = k end
return k
end
end
end
local function fnil() end
--[[ DATE FUNCTIONS ]]--
local DATE_EPOCH -- to be set later
local sl_weekdays = {
[0]="Sunday",[1]="Monday",[2]="Tuesday",[3]="Wednesday",[4]="Thursday",[5]="Friday",[6]="Saturday",
[7]="Sun",[8]="Mon",[9]="Tue",[10]="Wed",[11]="Thu",[12]="Fri",[13]="Sat",
}
local sl_meridian = {[-1]="AM", [1]="PM"}
local sl_months = {
[00]="January", [01]="February", [02]="March",
[03]="April", [04]="May", [05]="June",
[06]="July", [07]="August", [08]="September",
[09]="October", [10]="November", [11]="December",
[12]="Jan", [13]="Feb", [14]="Mar",
[15]="Apr", [16]="May", [17]="Jun",
[18]="Jul", [19]="Aug", [20]="Sep",
[21]="Oct", [22]="Nov", [23]="Dec",
}
-- added the '.2' to avoid collision, use `fix` to remove
local sl_timezone = {
[000]="utc", [0.2]="gmt",
[300]="est", [240]="edt",
[360]="cst", [300.2]="cdt",
[420]="mst", [360.2]="mdt",
[480]="pst", [420.2]="pdt",
}
-- set the day fraction resolution
local function setticks(t)
TICKSPERSEC = t;
TICKSPERDAY = SECPERDAY*TICKSPERSEC
TICKSPERHOUR= SECPERHOUR*TICKSPERSEC
TICKSPERMIN = SECPERMIN*TICKSPERSEC
end
-- is year y leap year?
local function isleapyear(y) -- y must be int!
return (mod(y, 4) == 0 and (mod(y, 100) ~= 0 or mod(y, 400) == 0))
end
-- day since year 0
local function dayfromyear(y) -- y must be int!
return 365*y + floor(y/4) - floor(y/100) + floor(y/400)
end
-- day number from date, month is zero base
local function makedaynum(y, m, d)
local mm = mod(mod(m,12) + 10, 12)
return dayfromyear(y + floor(m/12) - floor(mm/10)) + floor((mm*306 + 5)/10) + d - 307
--local yy = y + floor(m/12) - floor(mm/10)
--return dayfromyear(yy) + floor((mm*306 + 5)/10) + (d - 1)
end
-- date from day number, month is zero base
local function breakdaynum(g)
local g = g + 306
local y = floor((10000*g + 14780)/3652425)
local d = g - dayfromyear(y)
if d < 0 then y = y - 1; d = g - dayfromyear(y) end
local mi = floor((100*d + 52)/3060)
return (floor((mi + 2)/12) + y), mod(mi + 2,12), (d - floor((mi*306 + 5)/10) + 1)
end
--[[ for floats or int32 Lua Number data type
local function breakdaynum2(g)
local g, n = g + 306;
local n400 = floor(g/DI400Y);n = mod(g,DI400Y);
local n100 = floor(n/DI100Y);n = mod(n,DI100Y);
local n004 = floor(n/DI4Y); n = mod(n,DI4Y);
local n001 = floor(n/365); n = mod(n,365);
local y = (n400*400) + (n100*100) + (n004*4) + n001 - ((n001 == 4 or n100 == 4) and 1 or 0)
local d = g - dayfromyear(y)
local mi = floor((100*d + 52)/3060)
return (floor((mi + 2)/12) + y), mod(mi + 2,12), (d - floor((mi*306 + 5)/10) + 1)
end
]]
-- day fraction from time
local function makedayfrc(h,r,s,t)
return ((h*60 + r)*60 + s)*TICKSPERSEC + t
end
-- time from day fraction
local function breakdayfrc(df)
return
mod(floor(df/TICKSPERHOUR),HOURPERDAY),
mod(floor(df/TICKSPERMIN ),MINPERHOUR),
mod(floor(df/TICKSPERSEC ),SECPERMIN),
mod(df,TICKSPERSEC)
end
-- weekday sunday = 0, monday = 1 ...
local function weekday(dn) return mod(dn + 1, 7) end
-- yearday 0 based ...
local function yearday(dn)
return dn - dayfromyear((breakdaynum(dn))-1)
end
-- parse v as a month
local function getmontharg(v)
local m = tonumber(v);
return (m and fix(m - 1)) or inlist(tostring(v) or "", sl_months, 2)
end
-- get daynum of isoweek one of year y
local function isow1(y)
local f = makedaynum(y, 0, 4) -- get the date for the 4-Jan of year `y`
local d = weekday(f)
d = d == 0 and 7 or d -- get the ISO day number, 1 == Monday, 7 == Sunday
return f + (1 - d)
end
local function isowy(dn)
local w1;
local y = (breakdaynum(dn))
if dn >= makedaynum(y, 11, 29) then
w1 = isow1(y + 1);
if dn < w1 then
w1 = isow1(y);
else
y = y + 1;
end
else
w1 = isow1(y);
if dn < w1 then
w1 = isow1(y-1)
y = y - 1
end
end
return floor((dn-w1)/7)+1, y
end
local function isoy(dn)
local y = (breakdaynum(dn))
return y + (((dn >= makedaynum(y, 11, 29)) and (dn >= isow1(y + 1))) and 1 or (dn < isow1(y) and -1 or 0))
end
local function makedaynum_isoywd(y,w,d)
return isow1(y) + 7*w + d - 8 -- simplified: isow1(y) + ((w-1)*7) + (d-1)
end
--[[ THE DATE MODULE ]]--
local fmtstr = "%x %X";
--#if not DATE_OBJECT_AFX then
local date = {}
setmetatable(date, date)
-- Version: VMMMRRRR; V-Major, M-Minor, R-Revision; e.g. 5.45.321 == 50450321
do
local major = 2
local minor = 2
local revision = 0
date.version = major * 10000000 + minor * 10000 + revision
end
--#end -- not DATE_OBJECT_AFX
--[[ THE DATE OBJECT ]]--
local dobj = {}
dobj.__index = dobj
dobj.__metatable = dobj
-- shout invalid arg
local function date_error_arg() return error("invalid argument(s)",0) end
-- create new date object
local function date_new(dn, df)
return setmetatable({daynum=dn, dayfrc=df}, dobj)
end
--#if not NO_LOCAL_TIME_SUPPORT then
-- magic year table
local date_epoch, yt;
local function getequivyear(y)
assert(not yt)
yt = {}
local de = date_epoch:copy()
local dw, dy
for _ = 0, 3000 do
de:setyear(de:getyear() + 1, 1, 1)
dy = de:getyear()
dw = de:getweekday() * (isleapyear(dy) and -1 or 1)
if not yt[dw] then yt[dw] = dy end --print(de)
if yt[1] and yt[2] and yt[3] and yt[4] and yt[5] and yt[6] and yt[7] and yt[-1] and yt[-2] and yt[-3] and yt[-4] and yt[-5] and yt[-6] and yt[-7] then
getequivyear = function(y) return yt[ (weekday(makedaynum(y, 0, 1)) + 1) * (isleapyear(y) and -1 or 1) ] end
return getequivyear(y)
end
end
end
-- TimeValue from date and time
local function totv(y,m,d,h,r,s)
return (makedaynum(y, m, d) - DATE_EPOCH) * SECPERDAY + ((h*60 + r)*60 + s)
end
-- TimeValue from TimeTable
local function tmtotv(tm)
return tm and totv(tm.year, tm.month - 1, tm.day, tm.hour, tm.min, tm.sec)
end
-- Returns the bias in seconds of utc time daynum and dayfrc
local function getbiasutc2(self)
local y,m,d = breakdaynum(self.daynum)
local h,r,s = breakdayfrc(self.dayfrc)
local tvu = totv(y,m,d,h,r,s) -- get the utc TimeValue of date and time
local tml = osdate("*t", tvu) -- get the local TimeTable of tvu
if (not tml) or (tml.year > (y+1) or tml.year < (y-1)) then -- failed try the magic
y = getequivyear(y)
tvu = totv(y,m,d,h,r,s)
tml = osdate("*t", tvu)
end
local tvl = tmtotv(tml)
if tvu and tvl then
return tvu - tvl, tvu, tvl
else
return error("failed to get bias from utc time")
end
end
-- Returns the bias in seconds of local time daynum and dayfrc
local function getbiasloc2(daynum, dayfrc)
local tvu
-- extract date and time
local y,m,d = breakdaynum(daynum)
local h,r,s = breakdayfrc(dayfrc)
-- get equivalent TimeTable
local tml = {year=y, month=m+1, day=d, hour=h, min=r, sec=s}
-- get equivalent TimeValue
local tvl = tmtotv(tml)
local function chkutc()
tml.isdst = nil; local tvug = ostime(tml) if tvug and (tvl == tmtotv(osdate("*t", tvug))) then tvu = tvug return end
tml.isdst = true; local tvud = ostime(tml) if tvud and (tvl == tmtotv(osdate("*t", tvud))) then tvu = tvud return end
tvu = tvud or tvug
end
chkutc()
if not tvu then
tml.year = getequivyear(y)
tvl = tmtotv(tml)
chkutc()
end
return ((tvu and tvl) and (tvu - tvl)) or error("failed to get bias from local time"), tvu, tvl
end
--#end -- not NO_LOCAL_TIME_SUPPORT
--#if not DATE_OBJECT_AFX then
-- the date parser
local strwalker = {} -- ^Lua regular expression is not as powerful as Perl$
strwalker.__index = strwalker
local function newstrwalker(s)return setmetatable({s=s, i=1, e=1, c=len(s)}, strwalker) end
function strwalker:aimchr() return "\n" .. self.s .. "\n" .. rep(".",self.e-1) .. "^" end
function strwalker:finish() return self.i > self.c end
function strwalker:back() self.i = self.e return self end
function strwalker:restart() self.i, self.e = 1, 1 return self end
function strwalker:match(s) return (find(self.s, s, self.i)) end
function strwalker:__call(s, f)-- print("strwalker:__call "..s..self:aimchr())
local is, ie; is, ie, self[1], self[2], self[3], self[4], self[5] = find(self.s, s, self.i)
if is then self.e, self.i = self.i, 1+ie; if f then f(unpack(self)) end return self end
end
local function date_parse(str)
local y,m,d, h,r,s, z, w,u, j, e, x,c, dn,df
local sw = newstrwalker(gsub(gsub(str, "(%b())", ""),"^(%s*)","")) -- remove comment, trim leading space
--local function error_out() print(y,m,d,h,r,s) end
local function error_dup(q) --[[error_out()]] error("duplicate value: " .. (q or "") .. sw:aimchr()) end
local function error_syn(q) --[[error_out()]] error("syntax error: " .. (q or "") .. sw:aimchr()) end
local function error_inv(q) --[[error_out()]] error("invalid date: " .. (q or "") .. sw:aimchr()) end
local function sety(q) y = y and error_dup() or tonumber(q); end
local function setm(q) m = (m or w or j) and error_dup(m or w or j) or tonumber(q) end
local function setd(q) d = d and error_dup() or tonumber(q) end
local function seth(q) h = h and error_dup() or tonumber(q) end
local function setr(q) r = r and error_dup() or tonumber(q) end
local function sets(q) s = s and error_dup() or tonumber(q) end
local function adds(q) s = s + tonumber(q) end
local function setj(q) j = (m or w or j) and error_dup() or tonumber(q); end
local function setz(q) z = (z ~= 0 and z) and error_dup() or q end
local function setzn(zs,zn) zn = tonumber(zn); setz( ((zn<24) and (zn*60) or (mod(zn,100) + floor(zn/100) * 60))*( zs=='+' and -1 or 1) ) end
local function setzc(zs,zh,zm) setz( ((tonumber(zh)*60) + tonumber(zm))*( zs=='+' and -1 or 1) ) end
if not (sw("^(%d%d%d%d)",sety) and (sw("^(%-?)(%d%d)%1(%d%d)",function(_,a,b) setm(tonumber(a)); setd(tonumber(b)) end) or sw("^(%-?)[Ww](%d%d)%1(%d?)",function(_,a,b) w, u = tonumber(a), tonumber(b or 1) end) or sw("^%-?(%d%d%d)",setj) or sw("^%-?(%d%d)",function(a) setm(a);setd(1) end))
and ((sw("^%s*[Tt]?(%d%d):?",seth) and sw("^(%d%d):?",setr) and sw("^(%d%d)",sets) and sw("^(%.%d+)",adds))
or sw:finish() or (sw"^%s*$" or sw"^%s*[Zz]%s*$" or sw("^%s-([%+%-])(%d%d):?(%d%d)%s*$",setzc) or sw("^%s*([%+%-])(%d%d)%s*$",setzn))
) )
then --print(y,m,d,h,r,s,z,w,u,j)
sw:restart(); y,m,d,h,r,s,z,w,u,j = nil,nil,nil,nil,nil,nil,nil,nil,nil,nil
repeat -- print(sw:aimchr())
if sw("^[tT:]?%s*(%d%d?):",seth) then --print("$Time")
_ = sw("^%s*(%d%d?)",setr) and sw("^%s*:%s*(%d%d?)",sets) and sw("^(%.%d+)",adds)
elseif sw("^(%d+)[/\\%s,-]?%s*") then --print("$Digits")
x, c = tonumber(sw[1]), len(sw[1])
if (x >= 70) or (m and d and (not y)) or (c > 3) then
sety( x + ((x >= 100 or c>3) and 0 or x<centuryflip and 2000 or 1900) )
else
if m then setd(x) else m = x end
end
elseif sw("^(%a+)[/\\%s,-]?%s*") then --print("$Words")
x = sw[1]
if inlist(x, sl_months, 2, sw) then
if m and (not d) and (not y) then d, m = m, false end
setm(mod(sw[0],12)+1)
elseif inlist(x, sl_timezone, 2, sw) then
c = fix(sw[0]) -- ignore gmt and utc
if c ~= 0 then setz(c, x) end
elseif not inlist(x, sl_weekdays, 2, sw) then
sw:back()
-- am pm bce ad ce bc
if sw("^([bB])%s*(%.?)%s*[Cc]%s*(%2)%s*[Ee]%s*(%2)%s*") or sw("^([bB])%s*(%.?)%s*[Cc]%s*(%2)%s*") then
e = e and error_dup() or -1
elseif sw("^([aA])%s*(%.?)%s*[Dd]%s*(%2)%s*") or sw("^([cC])%s*(%.?)%s*[Ee]%s*(%2)%s*") then
e = e and error_dup() or 1
elseif sw("^([PApa])%s*(%.?)%s*[Mm]?%s*(%2)%s*") then
x = lwr(sw[1]) -- there should be hour and it must be correct
if (not h) or (h > 12) or (h < 0) then return error_inv() end
if x == 'a' and h == 12 then h = 0 end -- am
if x == 'p' and h ~= 12 then h = h + 12 end -- pm
else error_syn() end
end
elseif not(sw("^([+-])(%d%d?):(%d%d)",setzc) or sw("^([+-])(%d+)",setzn) or sw("^[Zz]%s*$")) then -- sw{"([+-])",{"(%d%d?):(%d%d)","(%d+)"}}
error_syn("?")
end
sw("^%s*") until sw:finish()
--else print("$Iso(Date|Time|Zone)")
end
-- if date is given, it must be complete year, month & day
if (not y and not h) or ((m and not d) or (d and not m)) or ((m and w) or (m and j) or (j and w)) then return error_inv("!") end
-- fix month
if m then m = m - 1 end
-- fix year if we are on BCE
if e and e < 0 and y > 0 then y = 1 - y end
-- create date object
dn = (y and ((w and makedaynum_isoywd(y,w,u)) or (j and makedaynum(y, 0, j)) or makedaynum(y, m, d))) or DAYNUM_DEF
df = makedayfrc(h or 0, r or 0, s or 0, 0) + ((z or 0)*TICKSPERMIN)
--print("Zone",h,r,s,z,m,d,y,df)
return date_new(dn, df) -- no need to :normalize();
end
local function date_fromtable(v)
local y, m, d = fix(v.year), getmontharg(v.month), fix(v.day)
local h, r, s, t = tonumber(v.hour), tonumber(v.min), tonumber(v.sec), tonumber(v.ticks)
-- atleast there is time or complete date
if (y or m or d) and (not(y and m and d)) then return error("incomplete table") end
return (y or h or r or s or t) and date_new(y and makedaynum(y, m, d) or DAYNUM_DEF, makedayfrc(h or 0, r or 0, s or 0, t or 0))
end
local tmap = {
['number'] = function(v) return date_epoch:copy():addseconds(v) end,
['string'] = function(v) return date_parse(v) end,
['boolean']= function(v) return date_fromtable(osdate(v and "!*t" or "*t")) end,
['table'] = function(v) local ref = getmetatable(v) == dobj; return ref and v or date_fromtable(v), ref end
}
local function date_getdobj(v)
local o, r = (tmap[type(v)] or fnil)(v);
return (o and o:normalize() or error"invalid date time value"), r -- if r is true then o is a reference to a date obj
end
--#end -- not DATE_OBJECT_AFX
local function date_from(arg1, arg2, arg3, arg4, arg5, arg6, arg7)
local y, m, d = fix(arg1), getmontharg(arg2), fix(arg3)
local h, r, s, t = tonumber(arg4 or 0), tonumber(arg5 or 0), tonumber(arg6 or 0), tonumber(arg7 or 0)
if y and m and d and h and r and s and t then
return date_new(makedaynum(y, m, d), makedayfrc(h, r, s, t)):normalize()
else
return date_error_arg()
end
end
--[[ THE DATE OBJECT METHODS ]]--
function dobj:normalize()
local dn, df = fix(self.daynum), self.dayfrc
self.daynum, self.dayfrc = dn + floor(df/TICKSPERDAY), mod(df, TICKSPERDAY)
return (dn >= DAYNUM_MIN and dn <= DAYNUM_MAX) and self or error("date beyond imposed limits:"..self)
end
function dobj:getdate() local y, m, d = breakdaynum(self.daynum) return y, m+1, d end
function dobj:gettime() return breakdayfrc(self.dayfrc) end
function dobj:getclockhour() local h = self:gethours() return h>12 and mod(h,12) or (h==0 and 12 or h) end
function dobj:getyearday() return yearday(self.daynum) + 1 end
function dobj:getweekday() return weekday(self.daynum) + 1 end -- in lua weekday is sunday = 1, monday = 2 ...
function dobj:getyear() local r,_,_ = breakdaynum(self.daynum) return r end
function dobj:getmonth() local _,r,_ = breakdaynum(self.daynum) return r+1 end-- in lua month is 1 base
function dobj:getday() local _,_,r = breakdaynum(self.daynum) return r end
function dobj:gethours() return mod(floor(self.dayfrc/TICKSPERHOUR),HOURPERDAY) end
function dobj:getminutes() return mod(floor(self.dayfrc/TICKSPERMIN), MINPERHOUR) end
function dobj:getseconds() return mod(floor(self.dayfrc/TICKSPERSEC ),SECPERMIN) end
function dobj:getfracsec() return mod(floor(self.dayfrc/TICKSPERSEC ),SECPERMIN)+(mod(self.dayfrc,TICKSPERSEC)/TICKSPERSEC) end
function dobj:getticks(u) local x = mod(self.dayfrc,TICKSPERSEC) return u and ((x*u)/TICKSPERSEC) or x end
function dobj:getweeknumber(wdb)
local wd, yd = weekday(self.daynum), yearday(self.daynum)
if wdb then
wdb = tonumber(wdb)
if wdb then
wd = mod(wd-(wdb-1),7)-- shift the week day base
else
return date_error_arg()
end
end
return (yd < wd and 0) or (floor(yd/7) + ((mod(yd, 7)>=wd) and 1 or 0))
end
function dobj:getisoweekday() return mod(weekday(self.daynum)-1,7)+1 end -- sunday = 7, monday = 1 ...
function dobj:getisoweeknumber() return (isowy(self.daynum)) end
function dobj:getisoyear() return isoy(self.daynum) end
function dobj:getisodate()
local w, y = isowy(self.daynum)
return y, w, self:getisoweekday()
end
function dobj:setisoyear(y, w, d)
local cy, cw, cd = self:getisodate()
if y then cy = fix(tonumber(y))end
if w then cw = fix(tonumber(w))end
if d then cd = fix(tonumber(d))end
if cy and cw and cd then
self.daynum = makedaynum_isoywd(cy, cw, cd)
return self:normalize()
else
return date_error_arg()
end
end
function dobj:setisoweekday(d) return self:setisoyear(nil, nil, d) end
function dobj:setisoweeknumber(w,d) return self:setisoyear(nil, w, d) end
function dobj:setyear(y, m, d)
local cy, cm, cd = breakdaynum(self.daynum)
if y then cy = fix(tonumber(y))end
if m then cm = getmontharg(m) end
if d then cd = fix(tonumber(d))end
if cy and cm and cd then
self.daynum = makedaynum(cy, cm, cd)
return self:normalize()
else
return date_error_arg()
end
end
function dobj:setmonth(m, d)return self:setyear(nil, m, d) end
function dobj:setday(d) return self:setyear(nil, nil, d) end
function dobj:sethours(h, m, s, t)
local ch,cm,cs,ck = breakdayfrc(self.dayfrc)
ch, cm, cs, ck = tonumber(h or ch), tonumber(m or cm), tonumber(s or cs), tonumber(t or ck)
if ch and cm and cs and ck then
self.dayfrc = makedayfrc(ch, cm, cs, ck)
return self:normalize()
else
return date_error_arg()
end
end
function dobj:setminutes(m,s,t) return self:sethours(nil, m, s, t) end
function dobj:setseconds(s, t) return self:sethours(nil, nil, s, t) end
function dobj:setticks(t) return self:sethours(nil, nil, nil, t) end
function dobj:spanticks() return (self.daynum*TICKSPERDAY + self.dayfrc) end
function dobj:spanseconds() return (self.daynum*TICKSPERDAY + self.dayfrc)/TICKSPERSEC end
function dobj:spanminutes() return (self.daynum*TICKSPERDAY + self.dayfrc)/TICKSPERMIN end
function dobj:spanhours() return (self.daynum*TICKSPERDAY + self.dayfrc)/TICKSPERHOUR end
function dobj:spandays() return (self.daynum*TICKSPERDAY + self.dayfrc)/TICKSPERDAY end
function dobj:addyears(y, m, d)
local cy, cm, cd = breakdaynum(self.daynum)
if y then y = fix(tonumber(y))else y = 0 end
if m then m = fix(tonumber(m))else m = 0 end
if d then d = fix(tonumber(d))else d = 0 end
if y and m and d then
self.daynum = makedaynum(cy+y, cm+m, cd+d)
return self:normalize()
else
return date_error_arg()
end
end
function dobj:addmonths(m, d)
return self:addyears(nil, m, d)
end
local function dobj_adddayfrc(self,n,pt,pd)
n = tonumber(n)
if n then
local x = floor(n/pd);
self.daynum = self.daynum + x;
self.dayfrc = self.dayfrc + (n-x*pd)*pt;
return self:normalize()
else
return date_error_arg()
end
end
function dobj:adddays(n) return dobj_adddayfrc(self,n,TICKSPERDAY,1) end
function dobj:addhours(n) return dobj_adddayfrc(self,n,TICKSPERHOUR,HOURPERDAY) end
function dobj:addminutes(n) return dobj_adddayfrc(self,n,TICKSPERMIN,MINPERDAY) end
function dobj:addseconds(n) return dobj_adddayfrc(self,n,TICKSPERSEC,SECPERDAY) end
function dobj:addticks(n) return dobj_adddayfrc(self,n,1,TICKSPERDAY) end
local tvspec = {
-- Abbreviated weekday name (Sun)
['%a']=function(self) return sl_weekdays[weekday(self.daynum) + 7] end,
-- Full weekday name (Sunday)
['%A']=function(self) return sl_weekdays[weekday(self.daynum)] end,
-- Abbreviated month name (Dec)
['%b']=function(self) return sl_months[self:getmonth() - 1 + 12] end,
-- Full month name (December)
['%B']=function(self) return sl_months[self:getmonth() - 1] end,
-- Year/100 (19, 20, 30)
['%C']=function(self) return fmt("%.2d", fix(self:getyear()/100)) end,
-- The day of the month as a number (range 1 - 31)
['%d']=function(self) return fmt("%.2d", self:getday()) end,
-- year for ISO 8601 week, from 00 (79)
['%g']=function(self) return fmt("%.2d", mod(self:getisoyear() ,100)) end,
-- year for ISO 8601 week, from 0000 (1979)
['%G']=function(self) return fmt("%.4d", self:getisoyear()) end,
-- same as %b
['%h']=function(self) return self:fmt0("%b") end,
-- hour of the 24-hour day, from 00 (06)
['%H']=function(self) return fmt("%.2d", self:gethours()) end,
-- The hour as a number using a 12-hour clock (01 - 12)
['%I']=function(self) return fmt("%.2d", self:getclockhour()) end,
-- The day of the year as a number (001 - 366)
['%j']=function(self) return fmt("%.3d", self:getyearday()) end,
-- Month of the year, from 01 to 12
['%m']=function(self) return fmt("%.2d", self:getmonth()) end,
-- Minutes after the hour 55
['%M']=function(self) return fmt("%.2d", self:getminutes())end,
-- AM/PM indicator (AM)
['%p']=function(self) return sl_meridian[self:gethours() > 11 and 1 or -1] end, --AM/PM indicator (AM)
-- The second as a number (59, 20 , 01)
['%S']=function(self) return fmt("%.2d", self:getseconds()) end,
-- ISO 8601 day of the week, to 7 for Sunday (7, 1)
['%u']=function(self) return self:getisoweekday() end,
-- Sunday week of the year, from 00 (48)
['%U']=function(self) return fmt("%.2d", self:getweeknumber()) end,
-- ISO 8601 week of the year, from 01 (48)
['%V']=function(self) return fmt("%.2d", self:getisoweeknumber()) end,
-- The day of the week as a decimal, Sunday being 0
['%w']=function(self) return self:getweekday() - 1 end,
-- Monday week of the year, from 00 (48)
['%W']=function(self) return fmt("%.2d", self:getweeknumber(2)) end,
-- The year as a number without a century (range 00 to 99)
['%y']=function(self) return fmt("%.2d", mod(self:getyear() ,100)) end,
-- Year with century (2000, 1914, 0325, 0001)
['%Y']=function(self) return fmt("%.4d", self:getyear()) end,
-- Time zone offset, the date object is assumed local time (+1000, -0230)
['%z']=function(self) local b = -self:getbias(); local x = abs(b); return fmt("%s%.4d", b < 0 and "-" or "+", fix(x/60)*100 + floor(mod(x,60))) end,
-- Time zone name, the date object is assumed local time
['%Z']=function(self) return self:gettzname() end,
-- Misc --
-- Year, if year is in BCE, prints the BCE Year representation, otherwise result is similar to "%Y" (1 BCE, 40 BCE)
['%\b']=function(self) local x = self:getyear() return fmt("%.4d%s", x>0 and x or (-x+1), x>0 and "" or " BCE") end,
-- Seconds including fraction (59.998, 01.123)
['%\f']=function(self) local x = self:getfracsec() return fmt("%s%.9f",x >= 10 and "" or "0", x) end,
-- percent character %
['%%']=function(self) return "%" end,
-- Group Spec --
-- 12-hour time, from 01:00:00 AM (06:55:15 AM); same as "%I:%M:%S %p"
['%r']=function(self) return self:fmt0("%I:%M:%S %p") end,
-- hour:minute, from 01:00 (06:55); same as "%I:%M"
['%R']=function(self) return self:fmt0("%I:%M") end,
-- 24-hour time, from 00:00:00 (06:55:15); same as "%H:%M:%S"
['%T']=function(self) return self:fmt0("%H:%M:%S") end,
-- month/day/year from 01/01/00 (12/02/79); same as "%m/%d/%y"
['%D']=function(self) return self:fmt0("%m/%d/%y") end,
-- year-month-day (1979-12-02); same as "%Y-%m-%d"
['%F']=function(self) return self:fmt0("%Y-%m-%d") end,
-- The preferred date and time representation; same as "%x %X"
['%c']=function(self) return self:fmt0("%x %X") end,
-- The preferred date representation, same as "%a %b %d %\b"
['%x']=function(self) return self:fmt0("%a %b %d %\b") end,
-- The preferred time representation, same as "%H:%M:%\f"
['%X']=function(self) return self:fmt0("%H:%M:%\f") end,
-- GroupSpec --
-- Iso format, same as "%Y-%m-%dT%T"
['${iso}'] = function(self) return self:fmt0("%Y-%m-%dT%T") end,
-- http format, same as "%a, %d %b %Y %T GMT"
['${http}'] = function(self) return self:fmt0("%a, %d %b %Y %T GMT") end,
-- ctime format, same as "%a %b %d %T GMT %Y"
['${ctime}'] = function(self) return self:fmt0("%a %b %d %T GMT %Y") end,
-- RFC850 format, same as "%A, %d-%b-%y %T GMT"
['${rfc850}'] = function(self) return self:fmt0("%A, %d-%b-%y %T GMT") end,
-- RFC1123 format, same as "%a, %d %b %Y %T GMT"
['${rfc1123}'] = function(self) return self:fmt0("%a, %d %b %Y %T GMT") end,
-- asctime format, same as "%a %b %d %T %Y"
['${asctime}'] = function(self) return self:fmt0("%a %b %d %T %Y") end,
}
function dobj:fmt0(str) return (gsub(str, "%%[%a%%\b\f]", function(x) local f = tvspec[x];return (f and f(self)) or x end)) end
function dobj:fmt(str)
str = str or self.fmtstr or fmtstr
return self:fmt0((gmatch(str, "${%w+}")) and (gsub(str, "${%w+}", function(x)local f=tvspec[x];return (f and f(self)) or x end)) or str)
end
function dobj.__lt(a, b) if (a.daynum == b.daynum) then return (a.dayfrc < b.dayfrc) else return (a.daynum < b.daynum) end end
function dobj.__le(a, b) if (a.daynum == b.daynum) then return (a.dayfrc <= b.dayfrc) else return (a.daynum <= b.daynum) end end
function dobj.__eq(a, b)return (a.daynum == b.daynum) and (a.dayfrc == b.dayfrc) end
function dobj.__sub(a,b)
local d1, d2 = date_getdobj(a), date_getdobj(b)
local d0 = d1 and d2 and date_new(d1.daynum - d2.daynum, d1.dayfrc - d2.dayfrc)
return d0 and d0:normalize()
end
function dobj.__add(a,b)
local d1, d2 = date_getdobj(a), date_getdobj(b)
local d0 = d1 and d2 and date_new(d1.daynum + d2.daynum, d1.dayfrc + d2.dayfrc)
return d0 and d0:normalize()
end
function dobj.__concat(a, b) return tostring(a) .. tostring(b) end
function dobj:__tostring() return self:fmt() end
function dobj:copy() return date_new(self.daynum, self.dayfrc) end
function dobj:setbias(bias)
self.bias = bias
return self
end
--[[ THE LOCAL DATE OBJECT METHODS ]]--
function dobj:tolocal()
local dn,df = self.daynum, self.dayfrc
bias = self.bias or getbiasutc2(self)
if bias != nil then
-- utc = local + bias; local = utc - bias
self.daynum = dn
self.dayfrc = df - bias*TICKSPERSEC
return self:normalize()
else
return nil
end
end
function dobj:toutc()
local dn,df = self.daynum, self.dayfrc
local bias = self.bias or getbiasloc2(dn, df)
if bias then
-- utc = local + bias;
self.daynum = dn
self.dayfrc = df + bias*TICKSPERSEC
return self:normalize()
else
return nil
end
end
function dobj:getbias() return self.bias != nil and self.bias/SECPERMIN or (getbiasloc2(self.daynum, self.dayfrc))/SECPERMIN end
function dobj:gettzname()
if self.bias != nil then return "" end
local _, tvu, _ = getbiasloc2(self.daynum, self.dayfrc)
return tvu and osdate("%Z",tvu) or ""
end
--#if not DATE_OBJECT_AFX then
function date.time(h, r, s, t)
h, r, s, t = tonumber(h or 0), tonumber(r or 0), tonumber(s or 0), tonumber(t or 0)
if h and r and s and t then
return date_new(DAYNUM_DEF, makedayfrc(h, r, s, t))
else
return date_error_arg()
end
end
function date:__call(arg1, ...)
local arg_count = select("#", ...) + (arg1 == nil and 0 or 1)
if arg_count > 1 then return (date_from(arg1, ...))
elseif arg_count == 0 then return (date_getdobj(false))
else local o, r = date_getdobj(arg1); return r and o:copy() or o end
end
date.diff = dobj.__sub
function date.isleapyear(v)
local y = fix(v);
if not y then
y = date_getdobj(v)
y = y and y:getyear()
end
return isleapyear(y+0)
end
function date.epoch() return date_epoch:copy() end
function date.isodate(y,w,d) return date_new(makedaynum_isoywd(y + 0, w and (w+0) or 1, d and (d+0) or 1), 0) end
function date.setcenturyflip(y)
if y ~= floor(y) or y < 0 or y > 100 then date_error_arg() end
centuryflip = y
end
function date.getcenturyflip() return centuryflip end
-- Internal functions
function date.fmt(str) if str then fmtstr = str end; return fmtstr end
function date.daynummin(n) DAYNUM_MIN = (n and n < DAYNUM_MAX) and n or DAYNUM_MIN return n and DAYNUM_MIN or date_new(DAYNUM_MIN, 0):normalize()end
function date.daynummax(n) DAYNUM_MAX = (n and n > DAYNUM_MIN) and n or DAYNUM_MAX return n and DAYNUM_MAX or date_new(DAYNUM_MAX, 0):normalize()end
function date.ticks(t) if t then setticks(t) end return TICKSPERSEC end
--#end -- not DATE_OBJECT_AFX
local tm = osdate("!*t", 0);
if tm then
date_epoch = date_new(makedaynum(tm.year, tm.month - 1, tm.day), makedayfrc(tm.hour, tm.min, tm.sec, 0))
-- the distance from our epoch to os epoch in daynum
DATE_EPOCH = date_epoch and date_epoch:spandays()
else -- error will be raise only if called!
date_epoch = setmetatable({},{__index = function() error("failed to get the epoch date") end})
end
--#if not DATE_OBJECT_AFX then
VyHub.Lib.date = date
--#else
--$return date_from
--#end

View File

@@ -0,0 +1,399 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
VyHub.Lib.json = VyHub.Lib.json or {}
--
-- json.lua
--
-- Copyright (c) 2020 rxi
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
-- this software and associated documentation files (the "Software"), to deal in
-- the Software without restriction, including without limitation the rights to
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-- of the Software, and to permit persons to whom the Software is furnished to do
-- so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in all
-- copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.
--
local json = { _version = "0.1.2" }
-------------------------------------------------------------------------------
-- Encode
-------------------------------------------------------------------------------
local encode
local escape_char_map = {
[ "\\" ] = "\\",
[ "\"" ] = "\"",
[ "\b" ] = "b",
[ "\f" ] = "f",
[ "\n" ] = "n",
[ "\r" ] = "r",
[ "\t" ] = "t",
}
local escape_char_map_inv = { [ "/" ] = "/" }
for k, v in pairs(escape_char_map) do
escape_char_map_inv[v] = k
end
local function escape_char(c)
return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
end
local function encode_nil(val)
return "null"
end
local function encode_table(val, stack)
local res = {}
stack = stack or {}
-- Circular reference?
if stack[val] then error("circular reference") end
stack[val] = true
if rawget(val, 1) ~= nil or next(val) == nil then
-- Treat as array -- check keys are valid and it is not sparse
local n = 0
for k in pairs(val) do
if type(k) ~= "number" then
error("invalid table: mixed or invalid key types")
end
n = n + 1
end
if n ~= #val then
error("invalid table: sparse array")
end
-- Encode
for i, v in ipairs(val) do
table.insert(res, encode(v, stack))
end
stack[val] = nil
return "[" .. table.concat(res, ",") .. "]"
else
-- Treat as an object
for k, v in pairs(val) do
if type(k) ~= "string" then
error("invalid table: mixed or invalid key types")
end
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
end
stack[val] = nil
return "{" .. table.concat(res, ",") .. "}"
end
end
local function encode_string(val)
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
end
local function encode_number(val)
-- Check for NaN, -inf and inf
if val ~= val or val <= -math.huge or val >= math.huge then
error("unexpected number value '" .. tostring(val) .. "'")
end
return string.format("%.14g", val)
end
local type_func_map = {
[ "nil" ] = encode_nil,
[ "table" ] = encode_table,
[ "string" ] = encode_string,
[ "number" ] = encode_number,
[ "boolean" ] = tostring,
}
encode = function(val, stack)
local t = type(val)
local f = type_func_map[t]
if f then
return f(val, stack)
end
error("unexpected type '" .. t .. "'")
end
function json.encode(val)
return ( encode(val) )
end
-------------------------------------------------------------------------------
-- Decode
-------------------------------------------------------------------------------
local parse
local function create_set(...)
local res = {}
for i = 1, select("#", ...) do
res[ select(i, ...) ] = true
end
return res
end
local space_chars = create_set(" ", "\t", "\r", "\n")
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
local literals = create_set("true", "false", "null")
local literal_map = {
[ "true" ] = true,
[ "false" ] = false,
[ "null" ] = nil,
}
local function next_char(str, idx, set, negate)
for i = idx, #str do
if set[str:sub(i, i)] ~= negate then
return i
end
end
return #str + 1
end
local function decode_error(str, idx, msg)
local line_count = 1
local col_count = 1
for i = 1, idx - 1 do
col_count = col_count + 1
if str:sub(i, i) == "\n" then
line_count = line_count + 1
col_count = 1
end
end
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
end
local function codepoint_to_utf8(n)
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
local f = math.floor
if n <= 0x7f then
return string.char(n)
elseif n <= 0x7ff then
return string.char(f(n / 64) + 192, n % 64 + 128)
elseif n <= 0xffff then
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
elseif n <= 0x10ffff then
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
f(n % 4096 / 64) + 128, n % 64 + 128)
end
error( string.format("invalid unicode codepoint '%x'", n) )
end
local function parse_unicode_escape(s)
local n1 = tonumber( s:sub(1, 4), 16 )
local n2 = tonumber( s:sub(7, 10), 16 )
-- Surrogate pair?
if n2 then
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
else
return codepoint_to_utf8(n1)
end
end
local function parse_string(str, i)
local res = ""
local j = i + 1
local k = j
while j <= #str do
local x = str:byte(j)
if x < 32 then
decode_error(str, j, "control character in string")
elseif x == 92 then -- `\`: Escape
res = res .. str:sub(k, j - 1)
j = j + 1
local c = str:sub(j, j)
if c == "u" then
local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1)
or str:match("^%x%x%x%x", j + 1)
or decode_error(str, j - 1, "invalid unicode escape in string")
res = res .. parse_unicode_escape(hex)
j = j + #hex
else
if not escape_chars[c] then
decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
end
res = res .. escape_char_map_inv[c]
end
k = j + 1
elseif x == 34 then -- `"`: End of string
res = res .. str:sub(k, j - 1)
return res, j + 1
end
j = j + 1
end
decode_error(str, i, "expected closing quote for string")
end
local function parse_number(str, i)
local x = next_char(str, i, delim_chars)
local s = str:sub(i, x - 1)
local n = tonumber(s)
if not n then
decode_error(str, i, "invalid number '" .. s .. "'")
end
return n, x
end
local function parse_literal(str, i)
local x = next_char(str, i, delim_chars)
local word = str:sub(i, x - 1)
if not literals[word] then
decode_error(str, i, "invalid literal '" .. word .. "'")
end
return literal_map[word], x
end
local function parse_array(str, i)
local res = {}
local n = 1
i = i + 1
while 1 do
local x
i = next_char(str, i, space_chars, true)
-- Empty / end of array?
if str:sub(i, i) == "]" then
i = i + 1
break
end
-- Read token
x, i = parse(str, i)
res[n] = x
n = n + 1
-- Next token
i = next_char(str, i, space_chars, true)
local chr = str:sub(i, i)
i = i + 1
if chr == "]" then break end
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
end
return res, i
end
local function parse_object(str, i)
local res = {}
i = i + 1
while 1 do
local key, val
i = next_char(str, i, space_chars, true)
-- Empty / end of object?
if str:sub(i, i) == "}" then
i = i + 1
break
end
-- Read key
if str:sub(i, i) ~= '"' then
decode_error(str, i, "expected string for key")
end
key, i = parse(str, i)
-- Read ':' delimiter
i = next_char(str, i, space_chars, true)
if str:sub(i, i) ~= ":" then
decode_error(str, i, "expected ':' after key")
end
i = next_char(str, i + 1, space_chars, true)
-- Read value
val, i = parse(str, i)
-- Set
res[key] = val
-- Next token
i = next_char(str, i, space_chars, true)
local chr = str:sub(i, i)
i = i + 1
if chr == "}" then break end
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
end
return res, i
end
local char_func_map = {
[ '"' ] = parse_string,
[ "0" ] = parse_number,
[ "1" ] = parse_number,
[ "2" ] = parse_number,
[ "3" ] = parse_number,
[ "4" ] = parse_number,
[ "5" ] = parse_number,
[ "6" ] = parse_number,
[ "7" ] = parse_number,
[ "8" ] = parse_number,
[ "9" ] = parse_number,
[ "-" ] = parse_number,
[ "t" ] = parse_literal,
[ "f" ] = parse_literal,
[ "n" ] = parse_literal,
[ "[" ] = parse_array,
[ "{" ] = parse_object,
}
parse = function(str, idx)
local chr = str:sub(idx, idx)
local f = char_func_map[chr]
if f then
return f(str, idx)
end
decode_error(str, idx, "unexpected character '" .. chr .. "'")
end
function json.decode(str)
if type(str) ~= "string" then
error("expected argument of type string, got " .. type(str))
end
local res, idx = parse(str, next_char(str, 1, space_chars, true))
idx = next_char(str, idx, space_chars, true)
if idx <= #str then
decode_error(str, idx, "trailing garbage")
end
return res
end
VyHub.Lib.json = json

View File

@@ -0,0 +1,57 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
VyHub.Advert = VyHub.Advert or {}
VyHub.adverts = VyHub.adverts or {}
local current_advert = 0
function VyHub.Advert:refresh()
VyHub.API:get("/advert/", nil, { active = "true", serverbundle_id = VyHub.server.serverbundle.id }, function(code, result)
VyHub.adverts = result
end)
end
function VyHub.Advert:show(advert)
if advert then
local lines = string.Explode('\n', advert.content)
local color = VyHub.Util:hex2rgb(advert.color)
local prefix = VyHub.Config.advert_prefix
for _, line in ipairs(lines) do
VyHub.Util:print_chat_all(line, prefix, color)
end
end
end
function VyHub.Advert:next()
current_advert = current_advert + 1;
local advert = VyHub.adverts[current_advert];
if advert then
VyHub.Advert:show(advert)
else
current_advert = 0
end
end
hook.Add("vyhub_ready", "vyhub_advert_vyhub_ready", function ()
VyHub.Advert:refresh()
timer.Create("vyhub_advert_next", VyHub.Config.advert_interval, 0, function()
VyHub.Advert:next()
end)
timer.Create("vyhub_advert_refresh", 300, 0, function ()
VyHub.Advert:refresh()
end)
end)

View File

@@ -0,0 +1,170 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local f = string.format
VyHub.API = VyHub.API or {}
local content_type = "application/json; charset=utf-8"
local json = VyHub.Lib.json
function VyHub.API:request(method, url, path_params, query, headers, request_body, type, success, failed, no_error_for)
no_error_for = no_error_for or {}
if path_params != nil then
url = f(url, unpack(path_params))
end
if istable(request_body) then
request_body = json.encode(request_body)
end
success_func = function(code, body, headers)
local result = body
if headers["Content-Type"] and headers["Content-Type"] == 'application/json' then
result = json.decode(body)
end
if code >= 200 and code < 300 then
VyHub:msg(f("HTTP %s %s (%s): %s", method, url, json.encode(query), code), "debug")
if success != nil then
-- VyHub:msg(f("Response: %s", body), "debug")
success(code, result, headers)
end
else
if not table.HasValue(no_error_for, code) then
local err_msg = f("HTTP %s %s: %s", method, url, code)
if query then
err_msg = err_msg .. f("\nQuery: %s", json.encode(query))
end
if request_body then
err_msg = err_msg .. f("\nBody: %s", request_body)
end
VyHub:msg(err_msg, "error")
if code < 500 and string.find( body:lower(), "html>" ) == nil then
VyHub:msg(f("Response: %s", body), "error")
end
end
if failed != nil then
local err_text = json.encode(result)
if istable(result) and result.detail != nil and result.detail.msg != nil then
err_text = f("%s (%s)", result.detail.msg, result.detail.code)
end
failed(code, result, headers, err_text)
end
end
end
failed_func = function(reason)
local err_msg = f("HTTP %s %s: %s", method, url, code)
if query then
err_msg = err_msg .. f("\nQuery: %s", json.encode(query))
end
if request_body then
err_msg = err_msg .. f("\nBody: %s", request_body)
end
VyHub:msg(err_msg, "error")
if failed != nil then
failed(0, reason, {})
end
end
HTTP({
method = method,
url = url,
parameters = query,
headers = headers,
body = request_body,
type = type,
success = success_func,
failed = failed_func,
})
end
function VyHub.API:get(endpoint, path_params, query, success, failed, no_error_for)
local url = f("%s%s", VyHub.API.url, endpoint)
VyHub.API:request("GET", url, path_params, query, self.headers, nil, content_type, success, failed, no_error_for)
end
function VyHub.API:delete(endpoint, path_params, success, failed)
local url = f("%s%s", VyHub.API.url, endpoint)
self:request("DELETE", url, path_params, nil, self.headers, nil, content_type, success, failed)
end
function VyHub.API:post(endpoint, path_params, body, success, failed, query)
local url = f("%s%s", VyHub.API.url, endpoint)
self:request("POST", url, path_params, query, self.headers, body, content_type, success, failed)
end
function VyHub.API:patch(endpoint, path_params, body, success, failed)
local url = f("%s%s", VyHub.API.url, endpoint)
self:request("PATCH", url, path_params, nil, self.headers, body, content_type, success, failed)
end
function VyHub.API:put(endpoint, path_params, body, success, failed)
local url = f("%s%s", VyHub.API.url, endpoint)
self:request("PUT", url, path_params, nil, self.headers, body, content_type, success, failed)
end
hook.Add("vyhub_loading_finish", "vyhub_api_vyhub_loading_finish", function()
if VyHub.Util:invalid_str({VyHub.Config.api_url, VyHub.Config.api_key, VyHub.Config.server_id}) then
VyHub:msg("API URL, Server ID or API Key not set! Please follow the manual.", "error")
timer.Simple(60, function ()
hook.Run("vyhub_loading_finish")
end)
return
end
VyHub.API.url = VyHub.Config.api_url
VyHub.API.headers = {
Authorization = f("Bearer %s", VyHub.Config.api_key)
}
if string.EndsWith(VyHub.API.url, "/") then
VyHub.API.url = string.sub(VyHub.API.url, 1, -2)
end
VyHub:msg(f("API URL is %s", VyHub.API.url))
VyHub.API:get("/openapi.json", nil, nil, function(code, result, headers)
VyHub:msg(f("Connection to API %s version %s successful!", result.info.title, result.info.version), "success")
hook.Run("vyhub_api_ready")
end, function()
VyHub:msg("Connection to API failed! Trying to use cache.", "error")
hook.Run("vyhub_api_failed")
end)
end)
concommand.Add("vh_reinit", function (ply)
if not VyHub.Util:is_server(ply) then return end
hook.Run("vyhub_loading_finish")
end)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,58 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local f = string.format
local json = VyHub.Lib.json
VyHub.Cache = VyHub.Cache or {}
function VyHub.Cache:save(key, value)
local data = {
timestamp = os.time(),
data = value
}
local filename = f("vyhub/%s.json", key)
local json = json.encode(data)
VyHub:msg("Write " .. filename .. ": " .. json, "debuga")
file.Write(filename, json)
end
function VyHub.Cache:get(key, max_age)
local path = f("vyhub/%s.json", key)
if not file.Exists(path, "data") then
return nil
end
local data_str = file.Read(path, "data")
if not string.Trim(data_str) then
return nil
end
local success, data = pcall(json.decode, data_str)
if not success then
return nil
end
if istable(data) and data.timestamp and data.data then
if max_age != nil and os.time() - data.timestamp > max_age then
return nil
end
return data.data
end
return nil
end

View File

@@ -0,0 +1,75 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local f = string.format
hook.Add("vyhub_ready", "vyhub_commands_vyhub_ready", function ()
VyHub:get_frontend_url(function (url)
-- !shop
local function open_shop(ply, args)
if IsValid(ply) then
VyHub.Util:open_url(ply, f('%s/shop', url))
end
end
for _, cmd in ipairs(VyHub.Config.commands_shop) do
VyHub.Util:register_chat_command(cmd, open_shop)
end
-- !bans
local function open_bans(ply, args)
if IsValid(ply) then
VyHub.Util:open_url(ply, f('%s/bans', url))
end
end
for _, cmd in ipairs(VyHub.Config.commands_bans) do
VyHub.Util:register_chat_command(cmd, open_bans)
end
-- !warnings
local function open_warnings(ply, args)
if IsValid(ply) then
VyHub.Util:open_url(ply, f('%s/warnings', url))
end
end
for _, cmd in ipairs(VyHub.Config.commands_warnings) do
VyHub.Util:register_chat_command(cmd, open_warnings)
end
-- !news
local function open_news(ply, args)
if IsValid(ply) then
VyHub.Util:open_url(ply, f('%s/', url))
end
end
for _, cmd in ipairs(VyHub.Config.commands_news) do
VyHub.Util:register_chat_command(cmd, open_news)
end
-- !user
local function open_profile(ply, args)
if IsValid(ply) and args[1] then
other_ply = VyHub.Util:get_player_by_nick(args[1])
if IsValid(other_ply) then
VyHub.Util:open_url(ply, f('%s/profile/steam/%s', url, other_ply:SteamID64()))
end
end
end
for _, cmd in ipairs(VyHub.Config.commands_profile) do
VyHub.Util:register_chat_command(cmd, open_profile)
end
end)
end)

View File

@@ -0,0 +1,88 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local f = string.format
function VyHub.Config:load_cache_config()
local ccfg = VyHub.Cache:get("config")
if ccfg != nil and #table.GetKeys(ccfg) > 0 then
VyHub.Config = table.Merge(VyHub.Config, ccfg)
VyHub:msg(f("Loaded cache config values: %s", table.concat(table.GetKeys(ccfg), ', ')))
end
end
concommand.Add("vh_setup", function (ply, _, args)
if not VyHub.Util:is_server(ply) then return end
if not args[1] or not args[2] or not args[3] then return end
local ccfg = VyHub.Cache:get("config")
if not istable(ccfg) then
ccfg = {}
end
ccfg["api_key"] = args[1]
ccfg["api_url"] = args[2]
ccfg["server_id"] = args[3]
VyHub.Cache:save("config", ccfg)
for key, value in pairs(ccfg) do
VyHub.Config[key] = value
end
VyHub:msg(f("Successfully set initial config, please wait up to one minute for VyHub to initialize (%s, %s, %s)", args[1], args[2], args[3]))
end)
concommand.Add("vh_config", function (ply, _, args)
if not VyHub.Util:is_server(ply) then return end
local ccfg = VyHub.Cache:get("config")
if not args[1] or not args[2] then
if istable(ccfg) then
VyHub:msg("Additional config options:")
PrintTable(ccfg)
else
VyHub:msg("No additional config options set.")
end
return
end
local key = args[1]
local value = args[2]
if not istable(ccfg) then
ccfg = {}
end
if value == "false" then
value = false
elseif value == "true" then
value = true
end
ccfg[key] = value
VyHub.Cache:save("config", ccfg)
VyHub.Config[key] = value
VyHub:msg(f("Successfully set config value %s.", key))
end)
concommand.Add("vh_config_reset", function (ply)
if not VyHub.Util:is_server(ply) then return end
VyHub.Cache:save("config", {})
VyHub:msg(f("Successfully cleared additional config.", key))
end)

View File

@@ -0,0 +1,95 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local f = string.format
local json = VyHub.Lib.json
VyHub.Dashboard = VyHub.Dashboard or {}
VyHub.Dashboard.last_update = VyHub.Dashboard.last_update or {}
VyHub.Dashboard.data = VyHub.Dashboard.data or {}
util.AddNetworkString("vyhub_dashboard")
util.AddNetworkString("vyhub_dashboard_reload")
function VyHub.Dashboard:reset()
VyHub.Dashboard.data = {}
VyHub.Dashboard.last_update = {}
net.Start("vyhub_dashboard_reload")
net.Broadcast()
end
function VyHub.Dashboard:fetch_data(user_id, callback)
VyHub.API:get("/server/%s/user-activity?morph_user_id=%s", {VyHub.server.id, user_id}, nil, function(code, result)
callback(result)
end)
end
function VyHub.Dashboard:get_data(steamid64, callback)
if VyHub.Dashboard.data[steamid64] == nil or VyHub.Dashboard.last_update[steamid64] == nil or os.time() - VyHub.Dashboard.last_update[steamid64] > 30 then
VyHub.Player:get(steamid64, function (user)
if user then
VyHub.Dashboard:fetch_data(user.id, function (data)
VyHub.Dashboard.data[steamid64] = data
VyHub.Dashboard.last_update[steamid64] = os.time()
callback(VyHub.Dashboard.data[steamid64])
end)
end
end)
else
callback(VyHub.Dashboard.data[steamid64])
end
end
function VyHub.Dashboard:get_permissions(ply)
return {
warning_show = VyHub.Player:check_property(ply, 'warning_show'),
warning_edit = VyHub.Player:check_property(ply, 'warning_edit'),
warning_delete = VyHub.Player:check_property(ply, 'warning_delete'),
ban_show = VyHub.Player:check_property(ply, 'ban_show'),
ban_edit = VyHub.Player:check_property(ply, 'ban_edit'),
}
end
net.Receive("vyhub_dashboard", function(_, ply)
if not IsValid(ply) then return end
VyHub.Dashboard:get_data(ply:SteamID64(), function (users)
local users_json = json.encode(users)
local users_json_compressed = util.Compress(users_json)
local users_json_compressed_len = #users_json_compressed
local perms = VyHub.Dashboard:get_permissions(ply)
local perms_json = json.encode(perms)
net.Start("vyhub_dashboard")
net.WriteUInt(users_json_compressed_len, 16)
net.WriteData(users_json_compressed, users_json_compressed_len)
net.WriteString(perms_json)
net.Send(ply)
end)
end)
local function open_dashboard(ply, args)
if ply and IsValid(ply) then
ply:ConCommand("vh_dashboard")
end
end
hook.Add("vyhub_ready", "vyhub_dashboard_vyhub_ready", function ()
for _, cmd in ipairs(VyHub.Config.commands_dashboard) do
VyHub.Util:register_chat_command(cmd, open_dashboard)
end
end)
hook.Add("vyhub_dashboard_data_changed", "vyhub_dahboard_vyhub_dashboard_data_changed", function ()
VyHub.Dashboard:reset()
end)

View File

@@ -0,0 +1,375 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local f = string.format
local json = VyHub.Lib.json
VyHub.Group = VyHub.Group or {}
VyHub.groups = VyHub.groups or nil
VyHub.groups_mapped = VyHub.groups_mapped or nil
VyHub.Group.group_changes = VyHub.Group.group_changes or {} -- dict(steamid64, groupname) of the last in-game group change (VyHub -> GMOD). Used to prevent loop.
util.AddNetworkString("vyhub_group_data")
local meta_ply = FindMetaTable("Player")
function VyHub.Group:refresh()
VyHub.API:get("/group/", nil, nil, function(code, result)
if result != VyHub.groups then
VyHub.groups = result
VyHub:msg(f("Found groups: %s", json.encode(result)), "debug")
VyHub.groups_mapped = {}
for _, group in ipairs(VyHub.groups) do
for _, mapping in ipairs(group.mappings) do
if mapping.serverbundle_id == nil or mapping.serverbundle_id == VyHub.server.serverbundle.id then
VyHub.groups_mapped[mapping.name] = group
break
end
end
end
VyHub.Group:send_groups()
end
end, function (code, reason)
VyHub:msg("Could not refresh groups.", "error")
end)
end
function VyHub.Group:send_groups(ply)
if not VyHub.groups_mapped then return end
local groups_to_send = VyHub.groups_mapped
net.Start("vyhub_group_data")
net.WriteUInt(table.Count(groups_to_send), 8)
for name_game, group in pairs(groups_to_send) do
net.WriteString(name_game)
net.WriteString(group.name)
net.WriteString(group.color)
end
if ply != nil and IsValid(ply) then
net.Send(ply)
else
net.Broadcast()
end
end
function VyHub.Group:set(steamid, groupname, seconds, processor_id, callback)
if seconds != nil and seconds == 0 then
seconds = nil
end
if VyHub.groups_mapped == nil then
VyHub:msg("Groups not initialized yet. Please try again later.", "error")
if callback then
callback(!VyHub.Config.strict_group_sync)
end
return
end
local group = VyHub.groups_mapped[groupname]
if group == nil then
VyHub:msg(f("Could not find VyHub group with name %s", groupname), "debug")
if callback then
callback(!VyHub.Config.strict_group_sync)
end
return
end
VyHub.Player:get(steamid, function (user)
if user == nil then
if callback then
callback(false)
return
end
end
local end_date = nil
if seconds != nil then
end_date = VyHub.Util:format_datetime(os.time() + seconds)
end
local url = '/user/%s/membership'
if processor_id != nil then
url = url .. '?morph_user_id=' .. processor_id
end
local ply = player.GetBySteamID64(steamid)
VyHub.API:post(url, {user.id}, {
begin = VyHub.Util.format_datetime(),
["end"] = end_date,
group_id = group.id,
serverbundle_id = VyHub.server.serverbundle.id
}, function (code, result)
VyHub:msg(f("Added membership in group %s for user %s.", groupname, steamid), "success")
if IsValid(ply) then
timer.Simple(5, function()
VyHub.Player:refresh(ply)
end)
end
if callback then
callback(true)
end
end, function (code, reason)
VyHub:msg(f("Could not add membership in group %s for user %s.", groupname, steamid), "error")
if callback then
callback(false)
end
if IsValid(ply) then
timer.Simple(2, function()
VyHub.Player:refresh(ply)
end)
end
end)
end)
end
function VyHub.Group:remove(steamid, processor_id, callback)
VyHub.Player:get(steamid, function (user)
if user == nil then
if callback then
callback(false)
return
end
end
local url = f('/user/%%s/membership?serverbundle_id=%s', VyHub.server.serverbundle.id)
if processor_id != nil then
url = url .. '&morph_user_id=' .. processor_id
end
VyHub.API:delete(url, {user.id}, function (code, result)
VyHub:msg(f("Removed %s from all groups.", steamid), "success")
local ply = player.GetBySteamID64(steamid)
if IsValid(ply) then
VyHub.Player:refresh(ply)
end
if callback then
callback(true)
end
end, function (code, reason)
VyHub:msg(f("Could not remove %s from all groups.", steamid), "error")
if callback then
callback(false)
end
end)
end)
end
function VyHub.Group:override_admin_mods()
if VyHub.Config.group_disable_sync then return end
local _setusergroup = meta_ply.SetUserGroup
if not ULib and not serverguard and not sam and not (xAdmin and xAdmin.Admin.RegisterBan) and not sAdmin then
hook.Remove("PlayerInitialSpawn", "PlayerAuthSpawn")
meta_ply.SetUserGroup = function(ply, name, ignore_vh)
if ply:GetUserGroup() == name then
VyHub:msg(ply:SteamID64() .. " already in group " .. name .. ". Ignoring change...")
return
end
local steamid = ply:SteamID64()
if not ignore_vh and VyHub.Group.group_changes[steamid] != name then
if VyHub.Group:set(steamid, name) or VyHub.Config.disable_group_check then
_setusergroup(ply, name)
end
else
_setusergroup(ply, name)
end
end
end
if xAdmin and xAdmin.Admin.RegisterBan then
local xadmin_setgroup = xAdmin.SetGroup
xAdmin.SetGroup = function(ply, group, ignore_vh)
local steamid32 = isstring(ply) and ply or ply:SteamID()
local steamid64 = util.SteamIDTo64(steamid32)
if not ignore_vh then
VyHub.Group:set(steamid64, group, nil, nil, function(success)
if success then
xadmin_setgroup( ply, group )
end
end)
else
xadmin_setgroup( ply, group )
end
end
end
if ULib then
local ulx_adduser = ULib.ucl.addUser
local ulx_removeuser = ULib.ucl.removeUser
ULib.ucl.addUser = function(steamid32, allow, deny, groupname, ignore_vh)
if not ignore_vh then
local steamid64 = util.SteamIDTo64(steamid32)
VyHub.Group:set(steamid64, groupname, nil, nil, function(success)
if success then
ulx_adduser( steamid32, allow, deny, groupname )
end
end)
else
ulx_adduser( steamid32, allow, deny, groupname )
end
end
ULib.ucl.removeUser = function(id)
local steamid64 = nil
if string.find(id, ":") then
steamid64 = util.SteamIDTo64(id)
else
local ply = player.GetByUniqueID(id)
if IsValid(ply) then
steamid64 = ply:SteamID64()
end
end
if steamid64 then
VyHub.Group:remove(steamid64, nil, function (success)
if success then
ulx_removeuser( id )
end
end)
end
end
end
if serverguard then
local servergaurd_setrank = serverguard.player["SetRank"]
function serverguard.player:SetRank(target, rank, length, ignore_vh)
if not ignore_vh then
if target then
if type(target) == "Player" and IsValid(target) then
VyHub.Group:set(target:SteamID64(), rank, nil, nil, function(success)
if success then
servergaurd_setrank(self, target, rank, length)
end
end)
elseif type(target) == "string" and string.match(target, "STEAM_%d:%d:%d+") then
local steamid = util.SteamIDTo64(target)
VyHub.Group:set(steamid, rank, nil, nil, function(success)
if success then
servergaurd_setrank(self, target, rank, length)
end
end)
end
end
else
servergaurd_setrank(self, target, rank, length)
end
end
end
if sam then
local sam_setrank = sam.player.set_rank
function sam.player.set_rank(ply, rank, length, ignore_vh)
if not ignore_vh then
if not sam.isnumber(length) or length < 0 then
length = nil
end
local seconds = nil
if length != nil then
seconds = math.Round(length * 60, 0)
end
VyHub.Group:set(ply:SteamID64(), rank, seconds, nil, function(success)
if success then
sam_setrank(ply, rank, length)
end
end)
else
sam_setrank(ply, rank, length)
end
end
end
if sAdmin then
local sadmin_setrank = sAdmin.setRank
sAdmin.setRank = function(ply, rank, expire, noupdate, ignore_vh)
rank = rank or "user"
if not ignore_vh and not noupdate then
local seconds = nil
if isnumber(expire) and expire > 0 then
seconds = math.max(expire, 0)
end
VyHub.Group:set(ply:SteamID64(), rank, seconds, nil, function(success)
if success then
sadmin_setrank(ply, rank, nil, noupdate)
end
end)
else
sadmin_setrank(ply, rank, expire, noupdate)
end
end
end
end
hook.Add("vyhub_ready", "vyhub_group_vyhub_ready", function ()
VyHub.Group:refresh()
timer.Create("vyhub_group_refresh", VyHub.Config.group_refresh_time, 0, function ()
VyHub.Group:refresh()
end)
hook.Add("vyhub_ply_connected", "vyhub_group_vyhub_ply_connected", function(ply)
VyHub.Group:send_groups(ply)
end)
concommand.Add("vh_setgroup", function(ply, _, args)
if VyHub.Util:is_server(ply) then
local steamid = args[1]
local group = args[2]
local bundle = args[3]
if steamid and group then
VyHub.Group:set(steamid, group)
end
end
end)
VyHub.Group:override_admin_mods()
end)

View File

@@ -0,0 +1,92 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local f = string.format
VyHub.frontend_url = VyHub.frontend_url or nil
function VyHub:server_data_ready()
VyHub:msg(f("I am server %s in bundle %s.", VyHub.server.name, VyHub.server.serverbundle.name))
VyHub.ready = true
hook.Run("vyhub_ready")
end
function VyHub:get_frontend_url(callback)
if VyHub.frontend_url != nil then
if callback then
callback(VyHub.frontend_url)
end
return VyHub.frontend_url
end
VyHub.API:get('/general/frontend-url', nil, nil, function (code, result)
VyHub.frontend_url = result.frontend_url
VyHub.Cache:save('frontend_url', VyHub.frontend_url)
if callback then
callback(VyHub.frontend_url)
end
end, function ()
local frontend_url = VyHub.Cache:get('frontend_url')
if frontend_url == nil then
VyHub:msg("Could not get frontend_url!", "error")
end
if callback then
callback(frontend_url)
end
end)
end
hook.Add("vyhub_api_ready", "vyhub_main_vyhub_api_ready", function ()
VyHub.API:get("/server/%s", { VyHub.Config.server_id }, nil, function(code, result)
VyHub.server = result
VyHub:server_data_ready()
VyHub.Cache:save("server", VyHub.server)
end, function (code, result)
VyHub:msg(f("Could not find server with id %s", VyHub.Config.server_id), "error")
timer.Simple(60, function ()
hook.Run("vyhub_loading_finish")
end)
end)
VyHub:get_frontend_url()
end)
hook.Add("vyhub_api_failed", "vyhub_main_vyhub_api_failed", function ()
local server = VyHub.Cache:get("server", 604800)
if server != nil then
VyHub.server = server
VyHub:server_data_ready()
else
VyHub:msg("Could not find cached server data or cached data is too old. Please make sure that the server is able to reach the VyHub API.", "error")
timer.Simple(60, function ()
hook.Run("vyhub_loading_finish")
end)
end
end)
timer.Create("vyhub_not_ready_msg", 30, 0, function ()
if VyHub.ready then
timer.Remove("vyhub_not_ready_msg")
else
VyHub.Util:print_chat_all("<green>VyHub</green> is not ready. Please check the server log/console for errors.")
end
end)

View File

@@ -0,0 +1,350 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local f = string.format
VyHub.Player = VyHub.Player or {}
VyHub.Player.connect_queue = VyHub.Player.connect_queue or {}
VyHub.Player.table = VyHub.Player.table or {}
util.AddNetworkString("vyhub_user_id")
local meta_ply = FindMetaTable("Player")
function VyHub.Player:initialize(ply, retry)
if not IsValid(ply) then return end
local steamid = ply:SteamID64()
VyHub:msg(f("Initializing user %s, %s", ply:Nick(), steamid))
VyHub.API:get("/user/%s", {steamid}, {type = "STEAM"}, function(code, result)
VyHub:msg(f("Found existing user %s for steam id %s (%s).", result.id, steamid, ply:Nick()), "success")
VyHub.Player.table[steamid] = result
VyHub.Player:refresh(ply)
VyHub.Player:send_user_id(ply)
hook.Run("vyhub_ply_initialized", ply)
local ply_timer_name = "vyhub_player_" .. steamid
timer.Create(ply_timer_name, VyHub.Config.player_refresh_time, 0, function()
if IsValid(ply) then
VyHub.Player:refresh(ply)
else
timer.Remove(ply_timer_name)
end
end)
end, function(code, reason)
if code != 404 then
VyHub:msg(f("Could not check if users %s exists. Retrying in a minute..", steamid), "error")
timer.Simple(60, function ()
VyHub.Player:initialize(ply)
end)
return
end
if retry then
VyHub:msg(f("Could not create user %s. Retrying in a minute..", steamid), "error")
timer.Simple(60, function()
VyHub.Player:initialize(ply)
end)
return
end
VyHub.Player:create(steamid, function()
VyHub.Player:initialize(ply, true)
end, function ()
VyHub.Player:initialize(ply, true)
end)
end, { 404 })
end
function VyHub.Player:send_user_id(ply)
if not IsValid(ply) then return end
ply:VyHubID(function (user_id)
net.Start("vyhub_user_id")
net.WriteString(user_id)
net.Send(ply)
end)
end
local creation_began = {}
local creation_success = {}
local creation_err = {}
function VyHub.Player:create(steamid, success, err)
-- Creation can take longer. If multiple creation requests occur, merge them to one.
if not istable(creation_success[steamid]) then creation_success[steamid] = {} end
if not istable(creation_err[steamid]) then creation_err[steamid] = {} end
table.insert(creation_success[steamid], success)
table.insert(creation_err[steamid], err)
if creation_began[steamid] and os.time() - creation_began[steamid] < 10 then
VyHub:msg(f("Queued creation request for steamid %s", steamid), "debug")
return
end
VyHub:msg(f("No existing user found for steam id %s. Creating..", steamid))
creation_began[steamid] = os.time()
local function reset_queue()
creation_began[steamid] = 0
creation_success[steamid] = {}
creation_err[steamid] = {}
end
VyHub.API:post('/user/', nil, { identifier = steamid, type = 'STEAM' }, function()
for _, success_callback in pairs(creation_success[steamid]) do
if success_callback then
success_callback()
end
end
reset_queue()
end, function()
for _, err_callback in pairs(creation_err[steamid]) do
if err_callback then
err_callback()
end
end
reset_queue()
end)
end
-- Return nil if steamid is nil or API error
-- Return false if steamid is false or could not create user
function VyHub.Player:get(steamid, callback, retry)
if steamid == nil then
callback(nil)
return
end
if steamid == false then
callback(false)
return
end
if VyHub.Player.table[steamid] != nil then
callback(VyHub.Player.table[steamid])
else
VyHub.API:get("/user/%s", {steamid}, {type = "STEAM"}, function(code, result)
VyHub:msg(f("Received user %s for steam id %s.", result.id, steamid), "debug")
VyHub.Player.table[steamid] = result
callback(result)
end, function(code)
VyHub:msg(f("Could not receive user %s.", steamid), "error")
if code == 404 and retry == nil then
VyHub.Player:create(steamid, function ()
VyHub.Player:get(steamid, callback, true)
end, function ()
callback(false)
end)
else
callback(nil)
end
end, {404})
end
end
function VyHub.Player:change_game_group(ply, group)
if not IsValid(ply) then return end
local steamid64 = ply:SteamID64()
local nick = ply:Nick()
VyHub.Group.group_changes[steamid64] = group
if serverguard then
serverguard.player:SetRank(ply, group, false, true)
elseif ulx then
ULib.ucl.addUser( ply:SteamID(), {}, {}, group, true )
elseif sam then
sam.player.set_rank(ply, group, 0, true)
elseif xAdmin and xAdmin.Admin.RegisterBan then
xAdmin.SetGroup(ply, group, true)
elseif sAdmin then
sAdmin.setRank(ply, group, 0, false, true)
else
ply:SetUserGroup(group, true)
end
VyHub:msg("Added " .. nick .. " to group " .. group, "success")
VyHub.Util:print_chat(ply, f(VyHub.lang.ply.group_changed, group))
end
function VyHub.Player:check_group(ply, callback)
if VyHub.Config.group_disable_sync then return end
if ply:VyHubID() == nil then
VyHub:msg(f("Could not check groups for user %s, because no VyHub id is available.", ply:SteamID64()), "debug")
return
end
VyHub.API:get("/user/%s/group", {ply:VyHubID()}, { serverbundle_id = VyHub.server.serverbundle_id }, function(code, result)
if not IsValid(ply) then return end
local steamid64 = ply:SteamID64()
local nick = ply:Nick()
local highest = nil
for _, group in ipairs(result) do
if highest == nil or highest.permission_level < group.permission_level then
highest = group
end
end
if highest == nil then
VyHub:msg(f("Could not find any active group for %s (%s)", nick, steamid64), "debug")
return
end
local group = nil
for _, mapping in ipairs(highest.mappings) do
if mapping.serverbundle_id == nil or mapping.serverbundle_id == VyHub.server.serverbundle.id then
group = mapping.name
break
end
end
if group == nil then
VyHub:msg(f("Could not find group name mapping for group %s.", highest.name), "debug")
return
end
local delay = 0
if sAdmin then
delay = 3
end
timer.Simple(delay, function ()
local curr_group = ply:GetUserGroup()
if curr_group != group then
VyHub.Player:change_game_group(ply, group)
end
end)
end, function()
end)
end
function VyHub.Player:refresh(ply, callback)
VyHub.Player:check_group(ply)
end
function VyHub.Player:get_group(ply)
if not IsValid(ply) then
return nil
end
local group = VyHub.groups_mapped[ply:GetUserGroup()]
if group == nil then
return nil
end
return group
end
function VyHub.Player:check_property(ply, property)
if not IsValid(ply) then return false end
local group = VyHub.Player:get_group(ply)
if group != nil then
local prop = group.properties[property]
if prop != nil and prop.granted then
return true
end
end
local steamid64 = ply:SteamID64()
if VyHub.Player.table[steamid64] then
return VyHub.Player.table[steamid64].admin
end
return false
end
function meta_ply:VyHubID(callback)
if IsValid(self) then
local user = VyHub.Player.table[self:SteamID64()]
local id = nil
if user != nil then
id = user.id
end
if id == nil or id == "" then
VyHub.Player:get(self:SteamID64(), function(user)
if user then
if callback then
callback(user.id)
end
else
if callback then
callback(nil)
end
end
end)
else
if callback then
callback(id)
end
end
return id
end
end
hook.Add("vyhub_ply_connected", "vyhub_ply_vyhub_ply_connected", function(ply)
VyHub.Player:initialize(ply)
end)
hook.Add("PlayerInitialSpawn","vyhub_ply_PlayerInitialSpawn", function(ply)
if IsValid(ply) and not ply:IsBot() then
if VyHub.ready then
hook.Run("vyhub_ply_connected", ply)
else
VyHub.Player.connect_queue[#VyHub.Player.connect_queue+1] = ply
end
end
end)
hook.Add("vyhub_ready", "vyhub_ply_vyhub_ready", function ()
timer.Simple(5, function()
for _, ply in ipairs(VyHub.Player.connect_queue) do
if IsValid(ply) then
hook.Run("vyhub_ply_connected", ply)
end
end
VyHub.Player.connect_queue = {}
end)
end)

View File

@@ -0,0 +1,266 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local f = string.format
local json = VyHub.Lib.json
VyHub.Reward = VyHub.Reward or {}
VyHub.Reward.executed_rewards_queue = VyHub.Reward.executed_rewards_queue or {}
VyHub.Reward.executed_rewards = VyHub.Reward.executed_rewards or {}
VyHub.rewards = VyHub.rewards or {}
local RewardEvent = {
DIRECT = "DIRECT",
CONNECT = "CONNECT",
SPAWN = "SPAWN",
DEATH = "DEATH",
DISCONNECT = "DISCONNECT",
DISABLE = "DISABLE",
}
local RewardType = {
COMMAND = "COMMAND",
SCRIPT = "SCRIPT",
CREDITS = "CREDITS",
MEMBERSHIP = "MEMBERSHIP",
}
function VyHub.Reward:refresh(callback, limit_players, err)
local user_ids = ""
local players = limit_players or player.GetHumans()
for _, ply in ipairs(players) do
if IsValid(ply) then
local id = ply:VyHubID()
if id and string.len(id) == 36 then
local glue = '&'
if user_ids == "" then
glue = '?'
end
user_ids = user_ids .. glue .. 'user_id=' .. id
end
end
end
if user_ids == "" then
VyHub.rewards = {}
else
local query = f("%s&active=true&serverbundle_id=%s&status=OPEN&for_server_id=%s&foreign_ids=true", user_ids, VyHub.server.serverbundle.id, VyHub.server.id)
VyHub.API:get('/packet/reward/applied/user' .. query, nil, nil,
function(code, result)
if limit_players == nil then
VyHub.rewards = result
VyHub:msg(f("Found %i users with open rewards.", table.Count(result)), "debug")
else
for steamid, arewards in pairs(result) do
VyHub.rewards[steamid] = arewards
end
end
if callback then
callback()
end
end, function (code, reason)
if err then
err()
end
end)
end
end
function VyHub.Reward:set_executed(areward_id)
VyHub.Reward.executed_rewards_queue[areward_id] = true
table.insert(VyHub.Reward.executed_rewards, areward_id)
VyHub.Reward:save_executed()
end
function VyHub.Reward:save_executed()
VyHub.Cache:save("executed_rewards_queue", VyHub.Reward.executed_rewards_queue)
end
function VyHub.Reward:send_executed()
for areward_id, val in pairs(VyHub.Reward.executed_rewards_queue) do
if val != nil then
VyHub.API:patch('/packet/reward/applied/%s', { areward_id }, { executed_on = { VyHub.server.id } }, function (code, result)
VyHub.Reward.executed_rewards_queue[areward_id] = nil
VyHub.Reward:save_executed()
end, function (code, reason)
if code >= 400 and code < 500 then
VyHub:msg(f("Could not mark reward %s as executed. Aborting.", areward_id), "error")
VyHub.Reward.executed_rewards_queue[areward_id] = nil
VyHub.Reward:save_executed()
end
end)
end
end
end
function VyHub.Reward:exec_rewards(event, steamid)
steamid = steamid or nil
local allowed_events = { event }
local rewards_by_player = VyHub.rewards
if steamid != nil then
rewards_by_player = {}
rewards_by_player[steamid] = VyHub.rewards[steamid]
else
if event != RewardEvent.DIRECT then
return
end
end
if event == RewardEvent.DIRECT then
table.insert(allowed_events, RewardEvent.DISABLE)
end
for steamid, arewards in pairs(rewards_by_player) do
local ply = player.GetBySteamID64(steamid)
if not IsValid(ply) then
VyHub:msg(f("Player %s not valid, skipping.", steamid), "debug")
continue
end
for _, areward in ipairs(arewards) do
local se = true
local reward = areward.reward
if not table.HasValue(allowed_events, reward.on_event) then
continue
end
if table.HasValue(VyHub.Reward.executed_rewards, areward.id) then
VyHub:msg(f("Skipped reward %s, because it already has been executed.", areward.id), "debug")
continue
end
local data = reward.data
if reward.type == RewardType.COMMAND then
if data.command != nil then
local cmd = VyHub.Reward:do_string_replacements(data.command, ply, areward)
if VyHub.Config.reward_command_whitelist and #VyHub.Config.reward_command_whitelist > 0 then
local matched = false
for _, cmd_pattern in ipairs(VyHub.Config.reward_command_whitelist) do
if string.match(cmd, cmd_pattern) != nil then
matched = true
break
end
end
if not matched then
VyHub:msg(f("Failed to execute reward '%s': Command '%s' does not match a command on the whitelist.", reward.name, cmd), "error")
continue
end
end
game.ConsoleCommand(cmd.. "\n")
end
elseif reward.type == RewardType.SCRIPT then
if VyHub.Config.reward_disable_scripts then
VyHub:msg(f("Failed to execute reward '%s': Scripts are not allowed on this server. You can enable scripts in sv_config.lua or by entering 'vh_config reward_disable_scripts false' in the server console.", reward.name), "error")
continue
end
local lua_str = data.script
if lua_str != nil then
lua_str = VyHub.Reward:do_string_replacements(lua_str, ply, areward)
RunString("local PLAYER = player.GetBySteamID64(\"" .. steamid .. "\") " .. lua_str, "vyhub_reward_script")
end
else
VyHub:msg(f("No implementation for reward type %s", reward.type) "warning")
end
VyHub:msg(f("Executed reward %s for user %s (%s): %s", reward.type, ply:Nick(), ply:SteamID64(), json.encode(data)))
if se and reward.once then
VyHub.Reward:set_executed(areward.id)
end
end
end
VyHub.Reward:send_executed()
end
function VyHub.Reward:do_string_replacements(inp_str, ply, areward)
local purchase_amount = "-"
if areward.applied_packet.purchase != nil then
purchase_amount = areward.applied_packet.purchase.amount_text
end
local replacements = {
["user_id"] = ply:VyHubID(),
["nick"] = ply:Nick(),
["steamid64"] = ply:SteamID64(),
["steamid32"] = ply:SteamID(),
["uniqueid"] = ply:UniqueID(),
["applied_packet_id"] = areward.applied_packet_id,
["packet_title"] = areward.applied_packet.packet.title,
["purchase_amount"] = purchase_amount,
}
for k, v in pairs(replacements) do
inp_str = string.Replace(tostring(inp_str), "%" .. tostring(k) .. "%", tostring(v))
end
return inp_str
end
hook.Add("vyhub_ready", "vyhub_reward_vyhub_ready", function ()
VyHub.Reward.executed_rewards_queue = VyHub.Cache:get("executed_rewards_queue") or {}
VyHub.Reward:refresh(function ()
VyHub.Reward:exec_rewards(RewardEvent.DIRECT)
end)
timer.Create("vyhub_reward_refresh", 60, 0, function ()
VyHub.Reward:refresh(function ()
VyHub.Reward:exec_rewards(RewardEvent.DIRECT)
end)
end)
hook.Add("vyhub_ply_initialized", "vyhub_reward_vyhub_ply_initialized", function(ply)
local function exec_ply_rewards()
VyHub.Reward:exec_rewards(RewardEvent.CONNECT, tostring(ply:SteamID64()))
hook.Run("vyhub_reward_post_connect", ply)
end
VyHub.Reward:refresh(exec_ply_rewards, { ply }, exec_ply_rewards)
end)
hook.Add("PlayerSpawn", "vyhub_reward_PlayerSpawn", function(ply)
if ply:Alive() then
VyHub.Reward:exec_rewards(RewardEvent.SPAWN, tostring(ply:SteamID64()))
end
end)
hook.Add("PostPlayerDeath", "vyhub_reward_PostPlayerDeath", function(ply)
VyHub.Reward:exec_rewards(RewardEvent.DEATH, tostring(ply:SteamID64()))
end)
-- Does not work
hook.Add("PlayerDisconnect", "vyhub_reward_PlayerDisconnect", function(ply)
if IsValid(ply) then
VyHub.Reward:exec_rewards(RewardEvent.Disconnect, tostring(ply:SteamID64()))
end
end)
end)

View File

@@ -0,0 +1,157 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local f = string.format
local json = VyHub.Lib.json
VyHub.Server = VyHub.Server or {}
VyHub.Server.extra_defaults = {
res_slots = 0,
res_slots_keep_free = false,
res_slots_hide = false,
}
VyHub.Server.reserved_slot_plys = VyHub.Server.reserved_slot_plys or {}
function VyHub.Server:get_extra(key)
if VyHub.server.extra != nil and VyHub.server.extra[key] != nil then
return VyHub.server.extra[key]
end
return VyHub.Server.extra_defaults[key]
end
function VyHub.Server:update_status()
local user_activities = {}
for _, ply in ipairs(player.GetHumans()) do
local id = ply:VyHubID()
if id and string.len(id) == 36 then
local tt = string.FormattedTime( ply:TimeConnected() )
table.insert(user_activities, { user_id = id, extra = {
Score = ply:Frags(),
Deaths = ply:Deaths(),
Nickname = ply:Nick(),
Playtime = f('%02d:%02d:%02d', tt.h, tt.m, tt.s),
Ping = f('%i ms', ply:Ping()),
}})
end
end
local data = {
users_max = VyHub.Server.max_slots_visible,
users_current = #player.GetAll(),
map = game.GetMap(),
is_alive = true,
user_activities = user_activities,
}
VyHub:msg(f("Updating status: %s", json.encode(data)), "debug")
VyHub.API:patch(
'/server/%s',
{VyHub.server.id},
data,
function ()
hook.Run("vyhub_dashboard_data_changed")
end,
function ()
VyHub:msg("Could not update server status.", "error")
end
)
end
function VyHub.Server:update_max_slots()
RunConsoleCommand("sv_visiblemaxplayers", VyHub.Server.max_slots_visible)
end
function VyHub.Server:init_slots()
VyHub.Server.max_slots = game.MaxPlayers() - VyHub.Server:get_extra("res_slots")
VyHub.Server.max_slots_visible = VyHub.Server.max_slots
if VyHub.Server:get_extra("res_slots_hide") then
VyHub.Server:update_max_slots()
hook.Add("PlayerDisconnected", "vyhub_server_PlayerDisconnected", function(ply)
timer.Create("vyhub_slots", 0.5, 20, function()
if not IsValid(ply) then
timer.Remove("vyhub_slots")
VyHub.Server:update_max_slots()
end
end)
end)
else
VyHub.Server.max_slots_visible = game.MaxPlayers()
end
end
function VyHub.Server:can_use_rslot(ply)
if not IsValid(ply) or ply:IsBot() then
return false
end
if table.HasValue(VyHub.Server.reserved_slot_plys, ply:SteamID64()) then
return true
end
local group = VyHub.Player:get_group(ply)
if group != nil then
if group.properties.reserved_slot_use != nil then
return group.properties.reserved_slot_use.granted
end
end
return false
end
function VyHub.Server:handle_ply_connect(ply)
if IsValid(ply) then
if #player.GetHumans() > VyHub.Server.max_slots then
if VyHub.Server:can_use_rslot(ply) then
if VyHub.Server:get_extra("res_slots_keep_free") then
local tokick = nil
for _, v in ipairs(player.GetHumans()) do
if v:SteamID64() != ply:SteamID64() and not VyHub.Server:can_use_rslot(v) then
if tokick == nil or (IsValid(tokick) and v:TimeConnected() < tokick:TimeConnected()) then
tokick = v
end
end
end
if tokick and IsValid(tokick) then
tokick:Kick(VyHub.lang.rslots.kick)
else
ply:Kick(VyHub.lang.rslots.full)
end
end
else
ply:Kick(VyHub.lang.rslots.full_no_slot)
end
end
end
end
hook.Add("vyhub_ready", "vyhub_server_vyhub_ready", function ()
VyHub.Server:init_slots()
VyHub.Server:update_status()
timer.Create("vyhub_status_update", 60, 0, function ()
VyHub.Server:update_status()
end)
hook.Add("vyhub_reward_post_connect", "vyhub_server_vyhub_reward_post_connect", function (ply)
VyHub.Server:handle_ply_connect(ply)
end)
end)

View File

@@ -0,0 +1,143 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local f = string.format
local json = VyHub.Lib.json
VyHub.Statistic = VyHub.Statistic or {}
VyHub.Statistic.playtime = VyHub.Statistic.playtime or {}
VyHub.Statistic.attr_def = VyHub.Statistic.attr_def or nil
function VyHub.Statistic:save_playtime()
VyHub:msg(f("Saved playtime statistics: %s", json.encode(VyHub.Statistic.playtime)), "debug")
VyHub.Cache:save("playtime", VyHub.Statistic.playtime)
end
function VyHub.Statistic:add_one_minute()
for _, ply in ipairs(player.GetHumans()) do
local steamid = ply:SteamID64()
ply:VyHubID(function (user_id)
if user_id == nil or string.len(user_id) < 10 then
VyHub:msg(f("Could not add playtime for user %s", steamid))
return
end
VyHub.Statistic.playtime[user_id] = VyHub.Statistic.playtime[user_id] or 0
VyHub.Statistic.playtime[user_id] = VyHub.Statistic.playtime[user_id] + 60
end)
end
VyHub.Statistic:save_playtime()
end
function VyHub.Statistic:send_playtime()
VyHub.Statistic:get_or_create_attr_definition(function (attr_def)
if attr_def == nil then
VyHub:msg("Could not send playtime statistics to API.", "warning")
return
end
local user_ids = table.GetKeys(VyHub.Statistic.playtime)
timer.Create("vyhub_send_stats", 0.3, table.Count(user_ids), function ()
local i = table.Count(user_ids)
local user_id = user_ids[i]
if user_id != nil then
local seconds = VyHub.Statistic.playtime[user_id]
table.remove(user_ids, i)
if seconds != nil and seconds > 0 then
local hours = math.Round(seconds / 60 / 60, 2)
if hours > 0 then
if string.len(user_id) < 10 then
VyHub.Statistic.playtime[user_id] = nil
return
end
VyHub.API:post("/user/attribute/", nil, {
definition_id = attr_def.id,
user_id = user_id,
serverbundle_id = VyHub.server.serverbundle.id,
value = tostring(hours),
}, function (code, result)
VyHub.Statistic.playtime[user_id] = nil
VyHub.Statistic:save_playtime()
end, function (code, reason)
if code == 404 then
VyHub.Statistic.playtime[user_id] = nil
VyHub.Statistic:save_playtime()
end
VyHub:msg(f("Could not send %s seconds playtime of %s to API.", seconds, user_id), "warning")
end)
end
else
VyHub.Statistic.playtime[user_id] = nil
end
end
end)
end)
end
function VyHub.Statistic:get_or_create_attr_definition(callback)
local function cb_wrapper(attr_def)
VyHub.Statistic.attr_def = attr_def
callback(attr_def)
end
if VyHub.Statistic.attr_def != nil then
callback(VyHub.Statistic.attr_def)
return
end
VyHub.API:get("/user/attribute/definition/%s", { "playtime" }, nil, function (code, result)
VyHub.Cache:save("playtime_attr_def", result)
cb_wrapper(result)
end, function (code, reason)
if code != 404 then
local attr_def = VyHub.Cache:get("playtime_attr_def")
cb_wrapper(attr_def)
else
VyHub.API:post("/user/attribute/definition/", nil, {
name = "playtime",
title = "Play Time",
unit = "Hours",
type = "ACCUMULATED",
accumulation_interval = "day",
unspecific = "true",
}, function (code, result)
VyHub.Cache:save("playtime_attr_def", result)
cb_wrapper(result)
end, function (code, reason)
cb_wrapper(nil)
end)
end
end)
end
hook.Add("vyhub_ready", "vyhub_statistic_vyhub_ready", function ()
VyHub.Statistic.playtime = VyHub.Cache:get("playtime") or {}
VyHub.Statistic:send_playtime()
timer.Create("vyhub_statistic_playtime_tick", 60, 0, function ()
VyHub.Statistic:add_one_minute()
end)
timer.Create("vyhub_statistic_send_playtime", 3600, 0, function ()
VyHub.Statistic:send_playtime()
end)
end)

View File

@@ -0,0 +1,183 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local f = string.format
VyHub.Warning = VyHub.Warning or {}
function VyHub.Warning:create(steamid, reason, processor_steamid)
processor_steamid = processor_steamid or nil
VyHub.Player:get(steamid, function (user)
if user == nil then
VyHub.Util:print_chat_steamid(processor_steamid, f("<red>Cannot find VyHub user with SteamID %s.</red>", steamid))
return
end
VyHub.Player:get(processor_steamid, function (processor)
if processor_steamid != nil and processor == nil then
return
end
local url = '/warning/'
if processor != nil then
url = url .. f('?morph_user_id=%s', processor.id)
end
VyHub.API:post(url, nil, {
reason = reason,
serverbundle_id = VyHub.server.serverbundle.id,
user_id = user.id
}, function (code, result)
VyHub.Ban:refresh()
VyHub:msg(f("Added warning for player %s: %s", user.username, reason))
VyHub.Util:print_chat_all(f(VyHub.lang.warning.user_warned, user.username, processor.username, reason))
VyHub.Util:print_chat_steamid(steamid, f(VyHub.lang.warning.received, processor.username, reason))
VyHub.Util:play_sound_steamid(steamid, "https://cdn.vyhub.net/sound/negativebeep.wav")
hook.Run("vyhub_dashboard_data_changed")
end, function (code, err_reason, _, err_text)
VyHub:msg(f("Error while adding warning for player %s: %s", user.username, err_text), "error")
VyHub.Util:print_chat_steamid(processor_steamid, f(VyHub.lang.warning.create_error, user.username, err_text))
end)
end)
end)
end
function VyHub.Warning:delete(warning_id, processor_steamid)
processor_steamid = processor_steamid or nil
VyHub.Player:get(processor_steamid, function (processor)
if not processor then return end
local url = '/warning/%s'
if processor != nil then
url = url .. f('?morph_user_id=%s', processor.id)
end
VyHub.API:delete(url, { warning_id }, function (code, result)
VyHub:msg(f("%s deleted warning %s.", processor.username, warning_id))
VyHub.Util:print_chat_steamid(processor_steamid, f(VyHub.lang.warning.deleted))
VyHub.Util:print_chat_steamid(steamid, VyHub.lang.warning.deleted_self)
hook.Run("vyhub_dashboard_data_changed")
end, function (code, err_reason, _, err_text)
VyHub:msg(f("Error while deleteing warning %s: %s", warning_id, err_text), "error")
VyHub.Util:print_chat_steamid(processor_steamid, f(VyHub.lang.other.error_api, err_text))
end)
end)
end
function VyHub.Warning:toggle(warning_id, processor_steamid)
processor_steamid = processor_steamid or nil
VyHub.Player:get(processor_steamid, function (processor)
if not processor then return end
local url = '/warning/%s/toggle'
if processor != nil then
url = url .. f('?morph_user_id=%s', processor.id)
end
VyHub.API:patch(url, { warning_id }, nil, function (code, result)
VyHub:msg(f("%s toggled warning %s.", processor.username, warning_id))
VyHub.Util:print_chat_steamid(processor_steamid, f(VyHub.lang.warning.toggled))
VyHub.Util:print_chat_steamid(steamid, VyHub.lang.warning.toggled_self)
hook.Run("vyhub_dashboard_data_changed")
end, function (code, err_reason, _, err_text)
VyHub:msg(f("Error while toggling status of warning %s: %s", warning_id, err_text), "error")
VyHub.Util:print_chat_steamid(processor_steamid, f(VyHub.lang.other.error_api, err_text))
end)
end)
end
local function warn_command(ply, args)
if not VyHub.Player:check_property(ply, "warning_edit") then
VyHub.Util:print_chat(ply, VyHub.lang.ply.no_permissions)
return
end
if args[1] and args[2] then
local reason = VyHub.Util:concat_args(args, 2)
local target = VyHub.Util:get_player_by_nick(args[1])
if target and IsValid(target) then
local nickparts = string.Explode(' ', target:Nick())
if #nickparts > 1 then
nickparts = VyHub.Util:concat_args(nickparts, 2) .. ' '
reason = string.Replace(reason, nickparts, '')
end
VyHub.Warning:create(target:SteamID64(), reason, ply:SteamID64())
end
end
if IsValid(ply) then
VyHub.Util:print_chat(ply, VyHub.lang.warning.cmd_help)
end
return false;
end
hook.Add("vyhub_ready", "vyhub_warning_vyhub_ready", function ()
concommand.Add("vh_warn", function(ply, _, args)
if not args[1] or not args[2] then return end
if VyHub.Util:is_server(ply) then
VyHub.Warning:create(args[1], args[2])
elseif IsValid(ply) then
if VyHub.Player:check_property(ply, "warning_edit") then
VyHub.Warning:create(args[1], args[2], ply:SteamID64())
else
VyHub.Util:print_chat(ply, VyHub.lang.ply.no_permissions)
end
end
end)
concommand.Add("vh_warning_toggle", function(ply, _, args)
if not args[1] then return end
local warning_id = args[1]
if VyHub.Util:is_server(ply) then
VyHub.Warning:toggle(warning_id)
elseif IsValid(ply) then
if VyHub.Player:check_property(ply, "warning_edit") then
VyHub.Warning:toggle(warning_id, ply:SteamID64())
else
VyHub.Util:print_chat(ply, VyHub.lang.ply.no_permissions)
end
end
end)
concommand.Add("vh_warning_delete", function(ply, _, args)
if not args[1] then return end
local warning_id = args[1]
if VyHub.Util:is_server(ply) then
VyHub.Warning:delete(warning_id)
elseif IsValid(ply) then
if VyHub.Player:check_property(ply, "warning_delete") then
VyHub.Warning:delete(warning_id, ply:SteamID64())
else
VyHub.Util:print_chat(ply, VyHub.lang.ply.no_permissions)
end
end
end)
for _, cmd in ipairs(VyHub.Config.commands_warn) do
VyHub.Util:register_chat_command(cmd, warn_command)
end
end)

View File

@@ -0,0 +1,43 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
-- DEFAULTS
VyHub.Config.date_format = VyHub.Config.date_format or "%Y-%m-%d %H:%M:%S %z"
if SERVER then
VyHub.Config.advert_interval = VyHub.Config.advert_interval or 180
VyHub.Config.advert_prefix = VyHub.Config.advert_prefix or "[★] "
-- Do not allow too small refresh intervals
if VyHub.Config.player_refresh_time < 5 then
VyHub.Config.player_refresh_time = 5
end
if VyHub.Config.group_refresh_time < 5 then
VyHub.Config.group_refresh_time = 5
end
VyHub.Config.ban_message = VyHub.Config.ban_message or ">>> Ban Message <<<" .. "\n\n"
.. VyHub.lang.other.reason .. ": %reason%" .. "\n"
.. VyHub.lang.other.ban_date .. ": %ban_date%" .. "\n"
.. VyHub.lang.other.unban_date .. ": %unban_date%" .. "\n"
.. VyHub.lang.other.admin .. ": %admin%" .. "\n"
.. VyHub.lang.other.id .. ": %id%" .. "\n\n"
.. VyHub.lang.other.unban_url .. ": %unban_url%" .. "\n\n"
VyHub.Config.commands_shop = VyHub.Config.commands_shop or { '!shop' }
VyHub.Config.commands_bans = VyHub.Config.commands_bans or { '!bans' }
VyHub.Config.commands_warnings = VyHub.Config.commands_warnings or { '!warnings' }
VyHub.Config.commands_news = VyHub.Config.commands_news or { '!news' }
VyHub.Config.commands_profile = VyHub.Config.commands_profile or { '!user' }
VyHub.Config.commands_warn = VyHub.Config.commands_warn or { '!warn' }
VyHub.Config.commands_dashboard = VyHub.Config.commands_dashboard or { '!dashboard' }
VyHub.Config.strict_group_sync = VyHub.Config.strict_group_sync or false
end

View File

@@ -0,0 +1,19 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
VyHub.Group = VyHub.Group or {}
function VyHub.Group:get(groupname)
if VyHub.groups_mapped == nil then
return nil
end
return VyHub.groups_mapped[groupname]
end

View File

@@ -0,0 +1,110 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local f = string.format
local json = VyHub.Lib.json
VyHub.Lang = VyHub.Lang or {}
VyHub.lang = VyHub.lang or nil
if SERVER then
util.AddNetworkString("vyhub_lang")
VyHub.Lang.compressed = VyHub.Lang.compressed or nil
function VyHub.Lang:load()
local f_en = file.Open("vyhub/lang/en.json", "r", "LUA")
if f_en == nil then
VyHub:msg("Missing language file en.json!!! PLEASE MAKE SURE TO DOWNLOAD VYHUB-GMOD ON THE GITHUB RELESES PAGE! https://github.com/matbyte-com/vyhub-gmod/releases", "error")
return
end
local en = json.decode(f_en:Read())
f_en:Close()
if not istable(en) then
VyHub:msg("Could not load language file en.json!", "error")
return
end
VyHub.lang = en
VyHub:msg("Loaded language en.")
if VyHub.Config.lang != 'en' then
local f_custom = file.Open(f("vyhub/lang/%s.json", VyHub.Config.lang), "r", "LUA")
if f_custom != nil then
local custom = json.decode(f_custom:Read())
f_custom:Close()
if istable(custom) then
table.Merge(VyHub.lang, custom)
VyHub:msg(f("Loaded language %s.", VyHub.Config.lang))
else
VyHub:msg(f("Could not load language file %s.json!", VyHub.Config.lang), "warning")
end
else
VyHub:msg(f("Missing language file %s.json.", VyHub.Config.lang), "warning")
end
end
VyHub.Lang.compressed = util.Compress(json.encode(VyHub.lang))
end
if VyHub.lang == nil then
VyHub.Lang:load()
end
net.Receive("vyhub_lang", function(_, ply)
if not IsValid(ply) then return end
if not VyHub.Lang.compressed then return end
local len = #VyHub.Lang.compressed
net.Start("vyhub_lang")
net.WriteUInt(len, 16)
net.WriteData(VyHub.Lang.compressed, len)
net.Send(ply)
end)
end
if CLIENT then
function VyHub.Lang:load()
net.Start("vyhub_lang")
net.SendToServer()
end
net.Receive("vyhub_lang", function()
timer.Remove("vyhub_lang_load")
local len = net.ReadUInt(16)
local lang_compr = net.ReadData(len)
VyHub.lang = json.decode(util.Decompress(lang_compr))
VyHub:msg("Loaded language.")
hook.Run("vyhub_lang_loaded")
end)
hook.Add("Initialize", "vyhub_lang_Initialize", function ()
VyHub.Lang:load()
timer.Create("vyhub_lang_load", 5, 5, function ()
if VyHub.lang == nil then
VyHub.Lang:load()
else
timer.Remove("vyhub_lang_load")
end
end)
end)
end

View File

@@ -0,0 +1,322 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local f = string.format
local date = VyHub.Lib.date
VyHub.Util = VyHub.Util or {}
VyHub.Util.chat_commands = VyHub.Util.chat_commands or {}
if SERVER then
util.AddNetworkString("vyhub_print_chat")
util.AddNetworkString("vyhub_play_sound")
util.AddNetworkString("vyhub_open_url")
end
function VyHub.Util:format_datetime(unix_timestamp)
unix_timestamp = unix_timestamp or os.time()
local tz_wrong = os.date("%z", unix_timestamp)
local timezone = f("%s:%s", string.sub(tz_wrong, 1, 3), string.sub(tz_wrong, 4, 5))
return os.date("%Y-%m-%dT%H:%M:%S" .. timezone, unix_timestamp)
end
function VyHub.Util:is_server(obj)
if type(obj) == "Entity" and (obj.EntIndex and obj:EntIndex() == 0) and !IsValid(obj) then
return true
else
return false
end
end
function VyHub.Util:iso_to_unix_timestamp(datetime)
if datetime == nil then return nil end
local pd = date(datetime)
if pd == nil then return nil end
local time = os.time(
{
year = pd:getyear(),
month = pd:getmonth(),
day = pd:getday(),
hour = pd:gethours(),
minute = pd:getminutes(),
second = pd:getseconds(),
}
)
return time
end
function VyHub.Util:get_ply_by_nick(nick)
nick = string.lower(nick);
for _, v in ipairs(player.GetHumans()) do
if(string.find(string.lower(v:Name()), nick, 1, true) != nil)
then return v;
end
end
end
function VyHub.Util:register_chat_command(strCommand, Func)
if !strCommand || !Func then return end
for k, v in pairs( VyHub.Util.chat_commands ) do
if( strCommand == k ) then
return
end
end
VyHub.Util.chat_commands[ tostring( strCommand ) ] = Func;
end
function VyHub.Util:concat_args(args, pos)
local toconcat = {}
if pos > 1 then
for i = pos, #args, 1 do
toconcat[#toconcat+1] = args[i]
end
end
return string.Implode(" ", toconcat)
end
if SERVER then
hook.Add("PlayerSay", "vyhub_util_PlayerSay", function(ply, message)
if VyHub.ready then
local chat_string = string.Explode(" ", message)
local ret = nil
for k, v in pairs( VyHub.Util.chat_commands ) do
if( string.lower(chat_string[1]) == string.lower(k) ) then
table.remove(chat_string, 1)
ret = v(ply, chat_string)
break
end
end
if ret != nil then
return ret
end
end
end)
end
local colors = {
red = Color(255, 24, 35),
green = Color(45, 170, 0),
blue = Color(0, 115, 204),
yellow = Color(229, 221, 0),
pink = Color(229, 0, 218),
}
-- Takes a str message with colors and returns a table
function VyHub.Util:replace_colors(message, no_color)
local resultTable = {}
local currentIndex = 1
local function getColor(colorName)
if colors[colorName] then
return colors[colorName]
else
return no_color
end
end
local function addStringToTable(str, color)
table.insert(resultTable, color)
table.insert(resultTable, str)
end
local tags = {}
-- Extract all color tags and their corresponding content
for tag, content in string.gmatch(message, "<([%l]+)>([^<]+)</%1>") do
table.insert(tags, {tag = tag, content = content})
end
-- Process the string, splitting it based on the color tags
for _, tagData in ipairs(tags) do
local startIndex, endIndex = string.find(message, f("<(%s)>[^<]+</%s>", string.PatternSafe(tagData.tag), string.PatternSafe(tagData.tag)), currentIndex, false)
if startIndex then
local str = string.sub(message, currentIndex, startIndex - 1)
addStringToTable(str, no_color)
addStringToTable(tagData.content, getColor(tagData.tag))
currentIndex = endIndex + 1
end
end
-- Append any remaining part of the string
local str = string.sub(message, currentIndex)
if str != "" then
addStringToTable(str, no_color)
end
return resultTable
end
local color_tag = Color(0, 187, 255)
function VyHub.Util:print_chat(ply, message, tag, color)
color = color or color_white
if SERVER then
if IsValid(ply) then
if not VyHub.Config.chat_tag then
VyHub.Config.chat_tag = "[VyHub] "
end
if not tag then
tag = VyHub.Config.chat_tag
end
message = string.Replace(message, '\r', '')
message = string.Replace(message, '\n', '')
net.Start("vyhub_print_chat")
net.WriteString(message)
net.WriteString(tag)
net.WriteColor(color)
net.Send(ply)
end
elseif CLIENT then
msg_table = VyHub.Util:replace_colors(message, color)
chat.AddText(color_tag, tag, color_white, unpack(msg_table))
end
end
function VyHub.Util:print_chat_steamid(steamid, message, tag, color)
if steamid != nil and steamid != false then
local ply = player.GetBySteamID64(steamid)
if IsValid(ply) then
VyHub.Util:print_chat(ply, message, tag, color)
end
end
end
function VyHub.Util:play_sound_steamid(steamid, url)
if steamid then
local ply = player.GetBySteamID64(steamid)
if IsValid(ply) then
return VyHub.Util:play_sound(ply, url)
end
end
end
function VyHub.Util:play_sound(ply, url)
if SERVER then
if IsValid(ply) then
net.Start("vyhub_play_sound")
net.WriteString(url)
net.Send(ply)
end
elseif CLIENT then
sound.PlayURL ( url, "", function() end)
end
end
function VyHub.Util:open_url(ply, url)
if SERVER then
if IsValid(ply) then
net.Start("vyhub_open_url")
net.WriteString(url)
net.Send(ply)
end
elseif CLIENT then
gui.OpenURL(url)
end
end
function VyHub.Util:print_chat_all(message, tag, color)
for _, ply in ipairs(player.GetHumans()) do
VyHub.Util:print_chat(ply, message, tag, color)
end
end
function VyHub.Util:get_player_by_nick(nick)
nick = string.lower(nick);
for _,v in ipairs(player.GetHumans()) do
if(string.find(string.lower(v:Name()), nick, 1, true) != nil)
then return v;
end
end
end
function VyHub.Util:hex2rgb(hex)
hex = hex:gsub("#","")
if(string.len(hex) == 3) then
return Color(tonumber("0x"..hex:sub(1,1)) * 17, tonumber("0x"..hex:sub(2,2)) * 17, tonumber("0x"..hex:sub(3,3)) * 17)
elseif(string.len(hex) == 6) then
return Color(tonumber("0x"..hex:sub(1,2)), tonumber("0x"..hex:sub(3,4)), tonumber("0x"..hex:sub(5,6)))
else
return color_white
end
end
function VyHub.Util:iso_ts_to_local_str(iso_ts)
local bias = VyHub.Config.time_offset != nil and -math.Round(VyHub.Config.time_offset * 60 * 60) or nil
return date(iso_ts):setbias(bias):tolocal():fmt(VyHub.Config.date_format)
end
function VyHub.Util:invalid_str(str_list)
for _, str in ipairs(str_list) do
if str == nil or string.Trim(str) == "" then
return true
end
end
return false
end
function VyHub.Util:escape_concommand_str(str)
str = string.Replace(str, '"', "'")
return str
end
if CLIENT then
net.Receive("vyhub_print_chat", function ()
local message = net.ReadString()
local tag = net.ReadString()
local color = net.ReadColor()
VyHub.Util:print_chat(nil, message, tag, color)
end)
net.Receive("vyhub_play_sound", function ()
local url = net.ReadString()
VyHub.Util:play_sound(nil, url)
end)
net.Receive("vyhub_open_url", function ()
local url = net.ReadString()
VyHub.Util:open_url(nil, url)
end)
end