-- created by Gerkiz for ComfyFactorio local Global = require 'utils.global' local Server = require 'utils.server' local Event = require 'utils.event' local Utils = require 'utils.core' local Gui = require 'utils.gui' local Commands = require 'utils.commands' local Token = require 'utils.token' local Discord = require 'utils.discord' local DiscordHandler = require 'utils.discord_handler' local module_name = '[Warning handler] ' local warning_data_set = 'warnings' local warnings = {} local set_data = Server.set_data local try_get_data = Server.try_get_data local warning_frame_name = Gui.uid_name() local ok_button_name = Gui.uid_name() Global.register( { warnings = warnings }, function (t) warnings = t.warnings end ) local Public = {} local function generate_warning_id() return game.tick .. '_' .. math.random(1000, 9999) end local function set_character_state(player, state) if not player or not player.valid then return false end if player.character ~= nil then player.character.active = state end return true end local function draw_warning_frame(player, warning_data) local main_frame, inside_table = Gui.add_main_frame_with_toolbar(player, 'screen', warning_frame_name, nil, nil, 'Warning', true, 2) if not main_frame or not inside_table then return end main_frame.style.width = 500 main_frame.auto_center = true local content_flow = inside_table.add { type = 'flow', direction = 'vertical' } content_flow.style.top_padding = 16 content_flow.style.bottom_padding = 16 content_flow.style.left_padding = 24 content_flow.style.right_padding = 24 content_flow.style.vertical_spacing = 12 local top_row = content_flow.add { type = 'flow', direction = 'horizontal' } local sprite_flow = top_row.add { type = 'flow' } sprite_flow.style.vertical_align = 'center' sprite_flow.add { type = 'sprite', sprite = 'utility/warning_icon' } local label_flow = top_row.add { type = 'flow' } label_flow.style.left_padding = 24 label_flow.style.top_padding = 6 local warning_message = '[font=heading-2]You have received a warning[/font]\n' .. (warning_data.reason or 'No reason provided.') local label = label_flow.add { type = 'label', caption = warning_message } label.style.single_line = false label.style.width = 400 local notice_message = '[font=heading-2]Breaking our rules multiple times will result in a ban.[/font]' local notice_label = content_flow.add { type = 'label', caption = notice_message } notice_label.style.single_line = false notice_label.style.width = 400 local bottom_flow = main_frame.add { type = 'flow', direction = 'horizontal' } local right_flow = bottom_flow.add { type = 'flow' } right_flow.style.horizontally_stretchable = true right_flow.style.horizontal_align = 'right' set_character_state(player, false) local ok_button = right_flow.add { type = 'button', name = ok_button_name, caption = 'OK', style = 'confirm_button' } Gui.set_data(ok_button, { warning_id = warning_data.id }) player.opened = main_frame end local function show_next_warning(player) if not warnings[player.name] then return end local player_warnings = warnings[player.name] for _, warning in ipairs(player_warnings) do if not warning.accepted then draw_warning_frame(player, warning) return end end end local function send_warning_discord_message(offender_name, admin_name, reason, accepted) local data = Server.build_embed_data() data.username = offender_name data.admin = admin_name data.reason = reason data.accepted = accepted or false local message = offender_name .. ' has ' .. (accepted and 'accepted' or 'received') .. ' a warning from ' .. admin_name .. '. Reason: ' .. reason DiscordHandler.send_notification( { title = 'Warning', description = message, color = 'warning' }) end local function assign_warning(offender_name, admin_name, reason) if not offender_name or not reason then return false end local offender = game.get_player(offender_name) local date = Server.get_current_date_with_time() local warning_id = generate_warning_id() local warning_data = { id = warning_id, reason = reason, admin = admin_name, date = date, accepted = false } if not warnings[offender_name] then warnings[offender_name] = {} end table.insert(warnings[offender_name], warning_data) set_data(warning_data_set, offender_name, warnings[offender_name]) send_warning_discord_message(offender_name, admin_name, reason, false) if offender and offender.valid then local frame = offender.gui.screen[warning_frame_name] if not frame or not frame.valid then draw_warning_frame(offender, warning_data) end end return true end local function accept_warning(player_name, warning_id) if not warnings[player_name] then return false end local player_warnings = warnings[player_name] local warning_data = nil for _, warning in ipairs(player_warnings) do if warning.id == warning_id then warning_data = warning break end end if not warning_data then return false end warning_data.accepted = true warning_data.accepted_date = Server.get_current_date_with_time() set_data(warning_data_set, player_name, player_warnings) warnings[player_name] = player_warnings send_warning_discord_message(player_name, warning_data.admin, warning_data.reason, true) local p = game.get_player(player_name) if p and p.valid then set_character_state(p, true) end return true end local load_warning_token = Token.register( function (data) local key = data.key local value = data.value if not key or not value then return end local player = game.get_player(key) if not player or not player.valid then return end if type(value) ~= 'table' then return end if not value[1] and value.id then value = { value } end if not value[1] then return end local needs_save = false for _, warning in ipairs(value) do if not warning.id then warning.id = generate_warning_id() needs_save = true end end warnings[key] = value if needs_save then set_data(warning_data_set, key, value) end show_next_warning(player) end ) function Public.try_dl_data(key) key = tostring(key) local secs = Server.get_current_time() if not secs then return else try_get_data(warning_data_set, key, load_warning_token) end end Commands.new('warn', 'Assigns a warning to a player.') :add_parameter('offender', false, 'player') :add_parameter('reason', false, 'string') :require_backend() :callback(function (player, offender, reason) if not offender then Utils.print_to(player, module_name .. 'No valid player given.') return false end if not reason or string.len(reason) <= 0 then Utils.print_to(player, module_name .. 'No valid reason was given.') return false end if string.len(reason) < 10 then Utils.print_to(player, module_name .. 'Reason is too short.') return false end if assign_warning(offender.name, player.name, reason) then Utils.print_to(player, module_name .. 'Warning assigned to ' .. offender.name .. '.') return true else Utils.print_to(player, module_name .. 'Failed to assign warning.') return false end end) Event.add( defines.events.on_player_joined_game, function (event) local player = game.get_player(event.player_index) if not player or not player.valid then return end Public.try_dl_data(player.name) end ) Gui.on_click( ok_button_name, function (event) local player = event.player if not player or not player.valid then return end local screen = player.gui.screen local frame = screen[warning_frame_name] if not frame or not frame.valid then return end local data = Gui.get_data(event.element) if not data or not data.warning_id then return end accept_warning(player.name, data.warning_id) frame.destroy() show_next_warning(player) end ) Server.on_data_set_changed( warning_data_set, function (data) if not data then return end local key = data.key local value = data.value if not key or not value then return end if type(value) ~= 'table' then warnings[key] = nil return end if not value[1] and value.id then value = { value } end if not value[1] then warnings[key] = nil return end for _, warning in ipairs(value) do if not warning.id then warning.id = generate_warning_id() end end warnings[key] = value local player = game.get_player(key) if player and player.valid then local frame = player.gui.screen[warning_frame_name] if not frame or not frame.valid then show_next_warning(player) end end end ) Public.assign_warning = assign_warning Public.accept_warning = accept_warning Public.get_warnings = function () return warnings end return Public