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