pchat/server.lua

by hugeblank
661 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
    local ok = pcall(function() 
48
        data = textutils.unserialise(aes.decrypt_str(encData, key))
49
    end)
50
    if ok 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
        local msg = table.concat(args, " ")
79
        packet("message", {
80
            user = owner,
81
            message = msg
82
        })
83
        print("<"..owner.."> "..msg)
84
    end
85
end
86

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

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

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

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

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