pchat/server.lua

by hugeblank
520 days agolua
COPY
1
-- pchat by hugeblank, Feb 2023
2
-- Requires computer have a wireless modem, 
3
-- and be licensed with a chatbox.
4
-- Usage:
5
-- ^pcjoin <channel [1-65535]> <passkey> - Join a channel.
6
-- ^pchat <message> - Send a message to joined channel.
7
-- ^pclist - List current users in joined channel.
8
-- ^pcleave - Disconnect from joined channel.
9
-- All command names can be modified, see commandNames table. Ex, changing ^pchat to ^p
10
sleep(0.5)
11
local aes = require("aes")
12
local sha = require("sha")
13
local aliases = require("aliases")
14
assert(chatbox, "Missing a chatbox! Run `/chatbox license` in chat to get started.")
15

16
local modem
17
do
18
    local modems = {peripheral.find("modem")}
19
    local i = 1
20
    while i <= #modems and not modem do
21
        if modems[i].isWireless() then
22
            modem = modems[i]
23
        end
24
        i = i+1
25
    end
26
    if not modem then
27
        printError("Wireless Modem not found")
28
    end
29
end
30

31
local owner = chatbox.getLicenseOwner()
32
local commands = {}
33
local key, channel
34
local users = {}
35

36
local function packet(type, message)
37
    local data = {
38
        type = type,
39
        message = message,
40
    }
41
    local encData = aes.encrypt_str(textutils.serialise(data), key)
42
    modem.transmit(channel, channel, encData)
43
end
44

45
local function unpacket(encData)
46
    local data
47
    pcall(function() 
48
        data = textutils.unserialise(aes.decrypt_str(encData, key))
49
    end)
50
    if type(data) == "table" then
51
        return data.type, data.message
52
    end
53
end
54

55
local function printError(...)
56
    chatbox.tell(owner, "&c"..table.concat(table.pack(...), " "), "pchat", nil, "format")
57
end
58

59
local function print(...)
60
    chatbox.tell(owner, table.concat(table.pack(...), " "), "pchat")
61
end
62

63
local function status(...)
64
    chatbox.tell(owner, "&e"..table.concat(table.pack(...), " "), "pchat", nil, "format")
65
end
66

67
local function isJoined()
68
    if not key then
69
        printError("Not joined to any channel!")
70
        return false
71
    end
72
    return true
73
end
74

75
-- pchat
76
commands[aliases.pchat] = function(args)
77
    if isJoined() then
78
        packet("message", {
79
            user = owner,
80
            message = table.concat(args, " ")
81
        })
82
        print("<"..owner.."> "..table.concat(args, " "))
83
    end
84
end
85

86
-- pcleave
87
commands[aliases.pcleave] = function(args)
88
    if isJoined() then
89
        packet("leave", owner)
90
        status("Left channel "..channel)
91
        key, channel = nil, nil
92
        users = {}
93
    end
94
end
95

96
-- pcjoin
97
commands[aliases.pcjoin] = function(args)
98
    local nchannel, nkey = tonumber(args[1]), args[2]
99
    if not nchannel then
100
        printError("Expected channel number, got "..(args[1] or "nil"))
101
        return
102
    elseif not nkey then
103
        printError("Missing passcode.")
104
        return
105
    end
106
    if key then
107
        packet("leave", owner)
108
        modem.close(channel)
109
        status("Switched channels from", channel, "to", nchannel, ".")
110
    else
111
        status("Joined channel "..nchannel)
112
    end
113
    key = aes.strToBlocks(sha.digestStr("000-PCHAT-"..nkey))[1]
114
    channel = nchannel
115
    users = {
116
        [owner] = true
117
    }
118
    modem.open(channel)
119
    packet("join", owner)
120
    packet("getList")
121
end
122

123
-- pclist
124
commands[aliases.pclist] = function()
125
    local out = {
126
        "Current users in channel:",
127
    }
128
    for user in pairs(users) do
129
        out[#out+1] = user
130
    end
131
    local str = table.concat(out, "\n"):gsub("&", "")
132
    print(str)
133
end
134

135
local args = {...}
136
if #args == 2 then
137
    commands[aliases.pcjoin](args)
138
else
139
    _G.print("PChat is now running! Usage:")
140
    _G.print("^pcjoin <channel> <passcode>")
141
    _G.print("^pcleave")
142
    _G.print("^pclist")
143
    _G.print("^pchat <message>")
144
end
145

146
while true do
147
    local e, user, command, args, data = os.pullEventRaw()
148
    if e == "command" then
149
        if data.ownerOnly and user == owner then
150
            for key, alias in pairs(aliases) do
151
                for i = 1, #alias do
152
                    if command == alias[i] then
153
                        commands[aliases[key]](args)
154
                        break
155
                    end
156
                end
157
            end
158
        elseif user == owner and commands[command] then
159
            printError("pchat command leaked! When using pchat commands *always* use ^ instead of \\!")
160
        end
161
    elseif e == "modem_message" then
162
        local m_type, message = unpacket(data)
163
        if m_type == "message" and type(message) == "table" and message.owner and message.message then
164
            print("<"..tostring(message.owner)..">"..tostring(message.message))
165
        elseif m_type == "join" and type(message) == "string" then
166
            status(message:gsub("&", "").." has joined the channel.")
167
            users[message] = true
168
        elseif m_type == "leave" and type(message) == "string" then
169
            status(message:gsub("&", "").." has left the channel.")
170
            users[message] = nil
171
        elseif m_type == "getList" then
172
            packet("list", owner)
173
        elseif m_type == "list" and type(message) == "string" then
174
            users[message] = true
175
        end
176
    end
177
end