Compare commits

..

3 Commits

7 changed files with 612 additions and 321 deletions

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2019 Alessandro Copyright (c) 2020 Alessandro
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -30,6 +30,7 @@ interface Client {
interface PollingClient extends Client { interface PollingClient extends Client {
lastPing: number, lastPing: number,
queue: Array<any>, queue: Array<any>,
token: string,
} }
interface Channels { interface Channels {
@ -428,6 +429,7 @@ pollingRouter.get("/connect", (req, res, next) => {
client.client = req; client.client = req;
client.queue = []; client.queue = [];
client.token = sessionToken;
client.send = function (data: string | object) { client.send = function (data: string | object) {
if (typeof data === "string") data = JSON.parse(data); if (typeof data === "string") data = JSON.parse(data);
@ -457,11 +459,6 @@ pollingRouter.use("*", function (req, res, next) {
}) })
} }
let client = clients[pollingTokens[sessionToken]] as PollingClient;
client.lastPing = Date.now();
clients[pollingTokens[sessionToken]] = client; // you're never 100% sure
next(); next();
}) })
@ -547,6 +544,7 @@ pollingRouter.post("/update", (req, res, next) => {
queue: client.queue, queue: client.queue,
}); });
client.lastPing = Date.now();
client.queue = []; client.queue = [];
}) })
@ -577,7 +575,10 @@ app.listen(config.port, () => {
let v = clients[id] as PollingClient; let v = clients[id] as PollingClient;
if ((Date.now() - v.lastPing) > 60000) { if ((Date.now() - v.lastPing) > 60000) {
console.log("[POL]", `Client connected: ${id}`);
let clientToken = (clients[id] as PollingClient).token;
delete clients[id]; delete clients[id];
delete pollingTokens[clientToken]
} }
}) })
}, 60000) }, 60000)

View File

@ -1,95 +0,0 @@
local component = require("component")
local modem = component.modem
if not modem then
error("Missing network card", 2)
end
local serial = require("serialization")
local event = require("event")
local soqet = {
uuid = nil,
running = false,
}
local lastid
local function open()
modem.open(1010)
end
local function receive()
if not modem.isOpen(1010) then
open()
end
while true do
local ev = {event.pull(nil, "modem_message")}
local ch = ev[4]
local message = serial.unserialize(ev[6])
--print(serial.serialize(message))
soqet.uuid = message.uuid
if lastid ~= message.mid then
if message.message and message.channel and message.meta then
lastid = message.mid
return message.channel, message.message, message.meta
end
end
end
end
local function send(data)
if not modem.isOpen(1010) then
open()
end
modem.broadcast(1010, serial.serialize(data))
end
function soqet.open(channel)
send({
type = "open",
channel = channel,
})
end
function soqet.close(channel)
send({
type = "close",
channel = channel,
})
end
function soqet.auth(token)
send({
type = "auth",
token = token,
})
end
function soqet.send(channel, message, meta)
send({
type = "send",
channel = channel,
message = message,
meta = meta,
})
end
function soqet.listen()
open()
soqet.running = true
while soqet.running do
event.push("soqet_message", receive())
end
end
function soqet.receive()
open()
return receive()
end
function soqet.unlisten()
soqet.running = false
end
return soqet

View File

@ -1,46 +0,0 @@
local modem = peripheral.find("modem")
modem.open(1010)
local soqet = require("soqet")
local function action(data)
if data.type == "open" and data.channel then
print("Opening " .. data.channel)
soqet.open(data.channel)
elseif data.type == "close" and data.channel then
print("Closing " .. data.channel)
soqet.close(data.channel)
elseif data.type == "send" then
print("Sending message...")
soqet.send(data.channel, data.message, data.meta)
elseif data.type == "auth" then
print("Authenticating...")
soqet.auth(data.token)
end
end
local function main()
while true do
local ev = {os.pullEvent()}
--for k,v in pairs(ev) do
--print(k, v)
--end
if ev[1] == "soqet_message" then
modem.transmit(1010, 0, textutils.serialise({
channel = ev[2],
message = ev[3],
meta = ev[4],
uuid = soqet.uuid,
mid = math.random(0,99999)
}))
elseif ev[1] == "modem_message" and ev[3] == 1010 then
local data = textutils.unserialize(ev[5])
action(data)
end
end
end
parallel.waitForAny(soqet.listen, main)

290
oc_soqet.lua Normal file
View File

@ -0,0 +1,290 @@
--[[
-- OC_Soqet.lua --
https://github.com/Ale32bit/Soqet/
MIT License
Copyright (c) 2020 Alessandro
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.
]] --
--[[
-- json.lua --
https://github.com/rxi/json.lua
Copyright (c) 2019 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 http = require("internet")
local fs = require("filesystem")
if not http then
error("Missing internet card", 2)
end
local function _request(...)
local handle = http.request(...)
handle.finishConnect()
local content = ""
for chunk in handle do
content = content .. chunk
end
return content
end
local function get(url)
return _request(url)
end
local function post(url, data, headers)
return _request(url, data, headers, "POST")
end
if not fs.exists("/lib/json.lua") then
local con = get("https://raw.githubusercontent.com/rxi/json.lua/master/json.lua")
local f = io.open("/lib/json.lua", "w")
f:write(con)
f:close()
end
local function expect(index, value, ...)
local types = {...}
local valueType = type(value)
local valid = false
for _, v in ipairs(types) do
if valueType == v then
valid = true
break
end
end
if not valid then
error(("bad argument #%d (expected %s, got %s)"):format(index, table.concat(types, ", "), valueType), 3)
end
return value
end
local soqet = {
ENDPOINT = "soqet.alexdevs.pw",
ssl = false,
json = require("json"),
credits = "OC_Soqet.lua by AlexDevs"
}
local function postJson(url, data)
return _request(
url,
soqet.json.encode(data),
{
["Content-Type"] = "application/json"
}
)
end
function soqet.new()
error("WebSocket client is not supported. Use long polling instead.", 2)
end
function soqet.poll()
local client = {
channels = {},
uuid = nil,
sessionId = math.random(0xffffff),
sessionToken = nil,
ssl = soqet.ssl,
connected = false,
listening = true,
updateInterval = 1
}
if ssl then
client.ENDPOINT = "https://" .. soqet.ENDPOINT
else
client.ENDPOINT = "http://" .. soqet.ENDPOINT
end
local function send(path, body)
return postJson(client.ENDPOINT .. "/api/" .. path, body)
end
local function rawreceive()
if not client.connected then
client.connect()
end
while true do
local h =
send(
"update",
{
token = client.sessionToken
}
)
local data = soqet.json.decode(h)
local queue = {}
for i, v in ipairs(data.queue) do
if v.type == "message" then
table.insert(
queue,
{
channel = v.channel,
message = v.message,
meta = v.meta
}
)
end
end
if #queue > 0 then
return queue
else
os.sleep(client.updateInterval)
end
end
end
function client.connect(token)
if nil and type(token) ~= "string" then
error("bad argument #1", 2)
end
local h = get(client.ENDPOINT .. "/api/connect?token=" .. (token or ""))
local data = soqet.json.decode(h)
client.uuid = data.uuid
client.sessionToken = data.token
client.motd = data.motd
client.connected = true
for i, v in pairs(client.channels) do
client.open(v)
end
return true
end
function client.open(channel)
expect(1, channel, "string", "number")
client.channels[#client.channels + 1] = channel
send(
"open",
{
token = client.sessionToken,
channel = channel
}
)
return true
end
function client.close(channel)
expect(1, channel, "string", "number")
for i, v in pairs(client.channels) do
if v == channel then
client.channels[i] = nil
end
end
send(
"close",
{
token = client.sessionToken,
channel = channel
}
)
return true
end
function client.send(channel, message, meta)
expect(1, channel, "string", "number")
expect(3, meta, "nil", "table")
meta = meta or {}
meta.library = meta.library or soqet.credits
send(
"send",
{
token = client.sessionToken,
channel = channel,
message = message,
meta = meta
}
)
end
function client.receive()
return rawreceive()
end
function client.listen()
client.listening = true
while client.listening do
local queue = rawreceive()
for i, v in ipairs(queue) do
computer.pushSignal("soqet_message", v.channel, v.message, v.meta, client.sessionId)
end
sleep(client.updateInterval)
end
end
function client.unlisten()
client.listening = false
return true
end
return client
end
return soqet

View File

@ -297,7 +297,7 @@
<code>token</code> field.</p> <code>token</code> field.</p>
<p>Field <code>motd</code> is also supplied upon connection.</p> <p>Field <code>motd</code> is also supplied upon connection.</p>
<p><b>Once connected you need to request at least once every 60 seconds to keep the session token alive!</b> <p><b>Once connected you need to request <code>/api/update</code> at least once every 60 seconds to keep the session token alive!</b>
</p> </p>
</div> </div>
@ -487,7 +487,7 @@
<tr> <tr>
<td>motd</td> <td>motd</td>
<td>string</td> <td>string</td>
<td><i>Any message the </i></td> <td><i>Inspiring quotes to help the developer and the user get over problems and easily achieve life goals.</i></td>
</tr> </tr>
</table> </table>

437
soqet.lua
View File

@ -4,7 +4,7 @@ https://github.com/Ale32bit/Soqet/
MIT License MIT License
Copyright (c) 2019 Alessandro Copyright (c) 2020 Alessandro
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
@ -23,7 +23,7 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
]]-- ]] --
--[[ --[[
-- json.lua -- -- json.lua --
@ -50,220 +50,361 @@ 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
]]-- ]] --
local h = http.get("https://raw.githubusercontent.com/rxi/json.lua/master/json.lua") local expect = dofile("rom/modules/main/cc/expect.lua").expect
local f = fs.open("json.lua", "w")
f.write(h.readAll())
f.close()
h.close()
local json = require("json")
local soqet = { local soqet = {
ENDPOINT = "wss://soqet.alexdevs.pw", ENDPOINT = "soqet.alexdevs.pw",
channels = {}, ssl = true,
socket = nil, json = json,
running = false, credits = "Soqet.lua v2 by AlexDevs"
uuid = nil,
sessionId = nil,
} }
local function send(data) if not soqet.json then
if not soqet.socket then if not fs.exists("json.lua") then
soqet.connect() local h = http.get("https://raw.githubusercontent.com/rxi/json.lua/master/json.lua")
local f = fs.open("json.lua", "w")
f.write(h.readAll())
f.close()
h.close()
end end
return soqet.socket.send(json.encode(data)) soqet.json = require("json")
end end
local function receive() function soqet.new()
if not soqet.socket then if not http then
soqet.connect() return false, "HTTP is not enabled!"
end
if not http.websocket then
return false, "HTTP WebSocket feature is not enabled!"
end
local client = {
channels = {},
uuid = nil,
socket = nil,
sessionId = math.random(0xffffff),
ssl = soqet.ssl
}
local function rawsend(data)
if not client.socket then
return false
end
client.socket.send(soqet.json.encode(data))
return true
end
local function rawreceive()
if not client.socket then
client.connect()
end end
while true do while true do
local data = soqet.socket.receive() local data = client.socket.receive()
data = json.decode(data) data = soqet.json.decode(data)
soqet.uuid = data.uuid
if data.type == "message" then
local message = data.message
local channel = data.channel
local meta = data.meta
return channel, message, meta client.uuid = data.uuid
elseif data.type == "ping" then
send({ if data.type == "ping" then
rawsend(
{
type = "ping", type = "ping",
id = 5, id = 99
}) }
)
elseif data.type == "motd" then
client.motd = data.motd
elseif data.type == "message" then
return data.channel, data.message, data.meta
end
end end
end end
end
function soqet.connect() if ssl then
assert(http.websocket, "WebSocket not enabled or not compatible with this ComputerCraft version.") client.ENDPOINT = "wss://" .. soqet.ENDPOINT .. "/" .. client.sessionId
soqet.sessionId = tostring(math.random(0xffffff)) else
local socket, err = http.websocket(soqet.ENDPOINT .. "/" .. soqet.sessionId) client.ENDPOINT = "ws://" .. soqet.ENDPOINT .. "/" .. client.sessionId
end
function client.connect()
if client.socket then
pcall(client.socket.close)
end
local socket, err = http.websocket(client.ENDPOINT)
if not socket then if not socket then
error(err, 1); return false, err
end end
soqet.socket = socket;
end
function soqet.open(channel) client.socket = socket
send({
for i, v in pairs(client.channels) do
client.open(v)
end
return true
end
function client.open(channel)
expect(1, channel, "string", "number")
client.channels[#client.channels + 1] = channel
return rawsend(
{
type = "open", type = "open",
channel = channel, channel = channel
id = 2, }
}) )
end end
function soqet.close(channel) function client.close(channel)
send({ expect(1, channel, "string", "number")
for i, v in pairs(client.channels) do
if v == channel then
client.channels[i] = nil
end
end
return rawsend(
{
type = "close", type = "close",
channel = channel, channel = channel
id = 3, }
}) )
end end
function soqet.auth(token) function client.send(channel, message, meta)
send({ expect(1, channel, "string", "number")
type = "auth", expect(3, meta, "nil", "table")
token = token,
id = 4,
})
end
function soqet.send(channel, message, meta) meta = meta or {}
send({ meta.library = meta.library or soqet.credits
return rawsend(
{
type = "send", type = "send",
channel = channel, channel = channel,
message = message, message = message,
meta = meta or {}, meta = meta
id = 1, }
}) )
end
function soqet.receive()
return receive()
end
function soqet.listen()
soqet.running = true
while soqet.running do
local channel, message, meta = receive()
os.queueEvent("soqet_message", channel, message, meta)
end end
function client.auth(token)
expect(1, token, "string")
return rawsend(
{
type = "auth",
token = token
}
)
end
function client.receive()
return rawreceive()
end
function client.listen()
client.listening = true
while client.listening do
local channel, message, meta = rawreceive()
os.queueEvent("soqet_message", channel, message, meta, client.sessionId)
end
return true
end
function client.unlisten()
client.listening = false
return true
end
return client, client.sessionId
end end
function soqet.unlisten() function soqet.poll(token)
soqet.running = false local client = {
end channels = {},
soqet.polling = {
host = "https://soqet.alexdevs.pw",
token = nil,
uuid = nil, uuid = nil,
motd = "soqet", sessionId = math.random(0xffffff),
sessionToken = nil,
ssl = soqet.ssl,
connected = false, connected = false,
}; listening = true,
updateInterval = 1
}
function soqet.polling.connect(token) if ssl then
local h, err = http.get(soqet.polling.host .. "/api/connect?token=" .. textutils.urlEncode(token)); client.ENDPOINT = "https://" .. soqet.ENDPOINT
if not h then else
return false, err client.ENDPOINT = "http://" .. soqet.ENDPOINT
end end
local result = json.decode(h.readAll()) local function postJson(path, body)
return http.post(
if not result.ok then client.ENDPOINT .. "/api/" .. path,
return false, result.error soqet.json.encode(body),
{
["Content-Type"] = "application/json"
}
)
end end
soqet.polling.token = result.token local function rawreceive()
soqet.polling.motd = result.motd if not client.connected then
soqet.polling.connected = true client.connect()
end
return true while true do
end local h, err =
postJson(
function soqet.polling.update() "update",
local h, err = http.post(soqet.polling.host .. "/api/update", textutils.serialiseJSON({ {
token = soqet.polling.token, token = client.sessionToken
})) }
)
if not h then if not h then
return false, err error(err)
end end
local result = json.decode(h.readAll()); local data = soqet.json.decode(h.readAll())
h.close()
if not result.ok then local queue = {}
return false, result.err
for i, v in ipairs(data.queue) do
if v.type == "message" then
table.insert(
queue,
{
channel = v.channel,
message = v.message,
meta = v.meta
}
)
end
end end
return result.queue if #queue > 0 then
end return queue
else
sleep(client.updateInterval)
end
end
end
function soqet.polling.open(channel) function client.connect(token)
local h, err = http.post(soqet.polling.host .. "/api/open", textutils.serialiseJSON({ expect(1, token, "nil", "string")
token = soqet.polling.token,
channel = channel, local h, err, eh = http.get(client.ENDPOINT .. "/api/connect?token=" .. textutils.urlEncode(token or ""))
}))
if not h then if not h then
return false, err return false, err, eh
end end
local result = json.decode(h.readAll()); local data = soqet.json.decode(h.readAll())
if not result.ok then h.close()
return false, result.err
client.uuid = data.uuid
client.sessionToken = data.token
client.motd = data.motd
client.connected = true
for i, v in pairs(client.channels) do
client.open(v)
end end
return true return true
end
function soqet.polling.close(channel)
local h, err = http.post(soqet.polling.host .. "/api/close", textutils.serialiseJSON({
token = soqet.polling.token,
channel = channel,
}))
if not h then
return false, err
end end
local result = json.decode(h.readAll()); function client.open(channel)
expect(1, channel, "string", "number")
if not result.ok then client.channels[#client.channels + 1] = channel
return false, result.err
end postJson(
"open",
{
token = client.sessionToken,
channel = channel
}
)
return true return true
end end
function soqet.polling.send(channel, message, meta) function client.close(channel)
local h, err = http.post(soqet.polling.host .. "/api/send", textutils.serialiseJSON({ expect(1, channel, "string", "number")
token = soqet.polling.token,
for i, v in pairs(client.channels) do
if v == channel then
client.channels[i] = nil
end
end
postJson(
"close",
{
token = client.sessionToken,
channel = channel
}
)
return true
end
function client.send(channel, message, meta)
expect(1, channel, "string", "number")
expect(3, meta, "nil", "table")
meta = meta or {}
meta.library = meta.library or soqet.credits
postJson(
"send",
{
token = client.sessionToken,
channel = channel, channel = channel,
message = message, message = message,
meta = meta, meta = meta
})) }
)
if not h then
return false, err
end end
local result = json.decode(h.readAll()); function client.receive()
return rawreceive()
if not result.ok then
return false, result.err
end end
function client.listen()
client.listening = true
while client.listening do
local queue = rawreceive()
for i, v in ipairs(queue) do
os.queueEvent("soqet_message", v.channel, v.message, v.meta, client.sessionId)
end
sleep(client.updateInterval)
end
end
function client.unlisten()
client.listening = false
return true return true
end
return client
end end
return soqet return soqet