feat: link implementation

This commit is contained in:
minish 2026-02-26 08:22:57 -05:00
parent f6c7ee2cad
commit 343aff3d3c
Signed by: min
SSH Key Fingerprint: SHA256:mf+pUTmK92Y57BuCjlkBdd82LqztTfDCQIUp0fCKABc
5 changed files with 186 additions and 38 deletions

View File

@ -1,5 +1,7 @@
return { return {
prefix = "!", prefix = "!",
linkToWS = nil,
sendToLink = false,
users = { users = {
["MinishCaps"] = { ["MinishCaps"] = {

View File

@ -2,15 +2,19 @@ local cfg = require("cfg")
local boxes = { peripheral.find("chat_box") } local boxes = { peripheral.find("chat_box") }
if #boxes == 0 then
error("no chat boxes :(")
end
local function ssleep(seconds) local function ssleep(seconds)
local fireAt = os.time() + (seconds / 50) local fireAt = os.time() + (seconds / 50)
local enqueued = {} local enqueued = {}
os.setAlarm(fireAt) local myId = os.setAlarm(fireAt)
while true do while true do
local ev = { os.pullEventRaw() } local ev = { os.pullEventRaw() }
if ev[1] == "alarm" then if ev[1] == "alarm" and ev[2] == myId then
break break
else else
table.insert(enqueued, ev) table.insert(enqueued, ev)
@ -44,6 +48,38 @@ local function dispatch(callback)
end end
end end
local deb = {
username = nil,
message = nil,
uuid = nil,
hidden = nil,
at = 0,
fired = 0,
}
local function checkDebounce(username, message, uuid, hidden)
if
username ~= deb.username
or message ~= deb.message
or uuid ~= deb.uuid
or hidden ~= deb.hidden
or os.clock() - deb.at > 0.1
or deb.fired > #boxes
then
deb.username = username
deb.message = message
deb.uuid = uuid
deb.hidden = hidden
deb.at = os.clock()
deb.fired = 0
return true
else
deb.fired = deb.fired + 1
return false
end
end
local function send(unm, msg, chan) local function send(unm, msg, chan)
local prefix = "&bSky" local prefix = "&bSky"
@ -94,9 +130,9 @@ local function sayas(plr, msg)
end) end)
end end
local function sayserver(msg) local function saysquare(as, msg)
dispatch(function(bx) dispatch(function(bx)
bx.sendMessage(msg, "Server", "[]") bx.sendMessage(msg, as, "[]")
end) end)
end end
@ -154,11 +190,12 @@ local function faketpa(tgtplr, sender, fakeplr, fakemsg, url)
end end
return { return {
checkDebounce = checkDebounce,
send = send, send = send,
userBc = userBc, userBc = userBc,
reportLog = reportLog, reportLog = reportLog,
say = say, say = say,
sayas = sayas, sayas = sayas,
sayserver = sayserver, saysquare = saysquare,
faketpa = faketpa, faketpa = faketpa,
} }

View File

@ -199,7 +199,7 @@ cmds.sayserver = {
}, },
allowLimitedUser = true, allowLimitedUser = true,
proc = function(unm, what) proc = function(unm, what)
ch.sayserver(what) ch.saysquare("Server", what)
end, end,
} }
@ -220,6 +220,19 @@ cmds.faketpa = {
end, end,
} }
cmds.link = {
alias = { "chatlink", "skylink", "bridge" },
desc = "manage chat link",
args = {
{ match = "boolean", desc = "on/off" },
},
allowAnybody = true,
proc = function(unm, state)
cfg.sendToLink = state
ch.send(unm, "updated link mode to " .. (state and "ON" or "OFF") .. "..", "link")
end
}
cmds.usage = { cmds.usage = {
alias = { "u", "cmd", "c", "describe", "what", "command" }, alias = { "u", "cmd", "c", "describe", "what", "command" },
desc = "how to use a command?", desc = "how to use a command?",

157
index.lua
View File

@ -1,4 +1,4 @@
-- Skynet v1.2 // server -- Skynet v1.3 // server
-- orig. written by minish - 2023-10 -- orig. written by minish - 2023-10
-- revived 2025-01 -- revived 2025-01
@ -13,6 +13,38 @@ local cmds = require("cmds")
settings.set("shell.allow_disk_startup", false) settings.set("shell.allow_disk_startup", false)
--#endregion --#endregion
--#region Listener States
local wsHandle
local termStreak = 0
local termStreakAt = 0
local lastPlrList
--#endregion
--#region Init/event Helpers
local function connectToLink()
http.websocketAsync(cfg.linkToWS)
end
local function scheduleScan()
os.startTimer(2)
end
--#endregion
--#region Helpers
local function dispatchLink(mtag, cont, data)
if not wsHandle then
return
end
local msg = {
Tag = mtag,
[cont] = data,
}
local jmsg = textutils.serialiseJSON(msg)
wsHandle.send(jmsg)
end
--#endregion
--#region Command Handling
local function nextWord(str) local function nextWord(str)
local first, last local first, last
@ -31,7 +63,18 @@ local function nextWord(str)
end end
local function processMessage(unm, msg, hidden) local function processMessage(unm, msg, hidden)
if not hidden and msg:sub(1, #cfg.prefix) ~= cfg.prefix then return end if not hidden and msg:sub(1, #cfg.prefix) ~= cfg.prefix then
-- it isn't smth we care about so let's broadcast?
if not cfg.sendToLink then
-- no let's not
return
end
dispatchLink(
"on_minecraft_chat", "OnMinecraftChat",
{ Sender = unm, Content = msg }
)
return
end
if not hidden then if not hidden then
msg = msg:sub(#cfg.prefix + 1) msg = msg:sub(#cfg.prefix + 1)
@ -40,12 +83,6 @@ local function processMessage(unm, msg, hidden)
local cmd local cmd
msg, cmd = nextWord(msg) msg, cmd = nextWord(msg)
local user = cfg.users[unm]
if user == nil then
ch.reportLog("foreign user " .. unm .. " tries: " .. cmd)
return
end
local xcmd local xcmd
for k, v in next, cmds do for k, v in next, cmds do
if k == cmd then if k == cmd then
@ -59,6 +96,18 @@ local function processMessage(unm, msg, hidden)
end end
end end
end end
local user = cfg.users[unm]
if user == nil then
if xcmd.allowAnybody then
-- prevent nil index w/ dummy user
user = {}
else
ch.reportLog("foreign user " .. unm .. " tries: " .. cmd)
return
end
end
if not xcmd then if not xcmd then
ch.reportLog("user " .. unm .. " tries invalid cmd: " .. cmd) ch.reportLog("user " .. unm .. " tries invalid cmd: " .. cmd)
ch.send(unm, "not a valid command", "cmd") ch.send(unm, "not a valid command", "cmd")
@ -113,6 +162,26 @@ local function processMessage(unm, msg, hidden)
return return
end end
end end
elseif x.match == "boolean" then
msg, arg = nextWord(msg)
if not arg and x.optional then
arg = "f"
else
arg = (arg or ""):lower()
if arg == "true" or arg == "t" or
arg == "yes" or arg == "y" or
arg == "on" or arg == "1" then
arg = true
elseif arg == "false" or arg == "f" or
arg == "no" or arg == "n" or
arg == "off" or arg == "0" then
arg = false
else
ch.send(unm, "invalid boolean for: " .. x.desc, "cmd")
return
end
end
else else
-- what?? -- what??
return true, error("invalid match type") return true, error("invalid match type")
@ -129,16 +198,13 @@ local function processMessage(unm, msg, hidden)
return true return true
end end
end end
--#endregion
-- local function scheduleScan()
-- os.startTimer(2)
-- end
local termStreak = 0
local termStreakAt = 0
-- local lastPlrList
local listeners = { local listeners = {
["chat"] = function(username, message, uuid, isHidden) ["chat"] = function(username, message, uuid, isHidden)
if not ch.checkDebounce(username, message, uuid, isHidden) then
return
end
return processMessage(username, message, isHidden) return processMessage(username, message, isHidden)
end, end,
["playerChangedDimension"] = function(username, fromDim, toDim) ["playerChangedDimension"] = function(username, fromDim, toDim)
@ -150,23 +216,48 @@ local listeners = {
["playerLeave"] = function(username, dimension) ["playerLeave"] = function(username, dimension)
tk.plrs[username] = nil tk.plrs[username] = nil
end, end,
-- ["timer"] = function() ["websocket_success"] = function(url, handle)
-- scheduleScan() wsHandle = handle
ch.reportLog("connected!", "link")
end,
["websocket_failure"] = function(url, err)
ch.reportLog("connection failed", "link")
end,
["websocket_message"] = function(url, data, isBinary)
local msg = textutils.unserialiseJSON(data)
if not msg then return end
-- local plrs = tk:scanRange(16) if msg.Tag == "on_discord_chat" then
-- table.sort(plrs) local p = msg.OnDiscordChat
if not p then return end
local sender, content = p.SenderNickname, p.Content
if not sender or not content then return end
-- local plrList = table.concat(plrs, ", ") ch.saysquare("@" .. sender, content)
-- if plrList ~= lastPlrList then end
-- if #plrs > 0 then end,
-- ch.reportLog("players in range: " .. plrList, "scan") ["websocket_closed"] = function(url, reason, code)
-- elseif lastPlrList ~= nil then ch.reportLog("disconnected", "link")
-- ch.reportLog("players left range", "scan") wsHandle = nil
-- end connectToLink()
end,
["timer"] = function()
scheduleScan()
-- lastPlrList = plrList local plrs = tk:scanRange(16)
-- end table.sort(plrs)
-- end,
local plrList = table.concat(plrs, ", ")
if plrList ~= lastPlrList then
if #plrs > 0 then
ch.reportLog("players in range: " .. plrList, "scan")
elseif lastPlrList ~= nil then
ch.reportLog("players left range", "scan")
end
lastPlrList = plrList
end
end,
["peripheral_detach"] = function() ["peripheral_detach"] = function()
end, end,
["terminate"] = function() ["terminate"] = function()
@ -203,8 +294,14 @@ local function evDispatcher()
end end
end end
tk:refresh()
ch.userBc("Loaded!") ch.userBc("Loaded!")
ch.reportLog("you are auto subbed to log, welcome!") ch.reportLog("you are auto subbed to log, welcome!")
tk:refresh() if cfg.linkToWS ~= nil then
connectToLink()
end
scheduleScan()
evDispatcher() evDispatcher()

View File

@ -3,8 +3,7 @@ local levenshtein = require("levenshtein")
local pd = peripheral.find("player_detector") local pd = peripheral.find("player_detector")
if not pd then if not pd then
print("no player detector :(") error("no player detector :(")
return
end end
local tk = {} local tk = {}