SyntaxGameServer/RCCService2018/content/LuaPackages/PurchasePrompt/ClickScamDetector.lua

106 lines
2.8 KiB
Lua

local UserInputService = game:GetService("UserInputService")
local join = require(script.Parent.join)
--[[
CLILUACORE-318: Revisit this approach and evaluate if it adequately
addresses existing or future scams
]]
local ClickScamDetector = {}
ClickScamDetector.__index = ClickScamDetector
--[[
Create a new scam detector with the specified options
This object will track any clicks or confirm button presses that occur
during its lifetime and attempt to determine whether the player is
being asked to spam clicks or button presses. When processing an action,
users of this object can abort their behavior if calling isClickValid
returns false.
]]
function ClickScamDetector.new(options)
--[[
Overwrite default values with provided options
]]
options = join({
-- The number of clicks within the window that is interpreted as a scam
clickSpeedThreshold = 3,
-- The window in which to measure clicks
clickTimeWindow = 1,
-- A delay to allow clicks to stack up and prevent immediate clicks
initialDelay = 1,
-- Allow a button input to be associated with this click detection
buttonInput = nil,
}, options)
local self = {
_inputConnection = nil,
_clickCount = 0,
_startTime = tick(),
_options = options,
}
setmetatable(self, ClickScamDetector)
self._inputConnection = UserInputService.InputBegan:Connect(function(input)
self:_onInput(input)
end)
return self
end
--[[
Track mouse inputs, counting the number that occurred in the last
CLICK_TIME_WINDOW duration
Includes touch inputs and pressing the A button on a gamepad
]]
function ClickScamDetector:_onInput(input)
local inputType = input.UserInputType
local isGamepad = self._options.buttonInput ~= nil and input.KeyCode == self._options.buttonInput
local isMouseOrTouch = inputType == Enum.UserInputType.MouseButton1
or inputType == Enum.UserInputType.Touch
if isGamepad or isMouseOrTouch then
self._clickCount = self._clickCount + 1
delay(self._options.clickTimeWindow, function()
self._clickCount = self._clickCount - 1
end)
end
end
--[[
Determine whether or not there's a possibility of a scam occurring
and return whether or not we believe the click to be valid
]]
function ClickScamDetector:isClickValid()
--[[
If the mouse behavior is locked by dev-facing APIs, clicks are not valid
]]
if UserInputService.MouseBehavior == Enum.MouseBehavior.LockCurrentPosition then
return false
end
--[[
Don't allow any clicks until the initial delay has passed;
]]
if tick() - self._startTime < self._options.initialDelay then
return false
end
return self._clickCount / self._options.clickTimeWindow < self._options.clickSpeedThreshold
end
--[[
Cleanup connection to InputService; should be called when
the UI element using this object is destroyed
]]
function ClickScamDetector:destroy()
self._inputConnection:Disconnect()
end
return ClickScamDetector