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